Infrastruktur-Tests als Code mit Pulumi. Teil 2

Hallo zusammen. Heute teilen wir mit Ihnen den abschließenden Teil des Artikels „Tests der Infrastruktur als Code mit Pulumi“, dessen Übersetzung speziell für die Studierenden des Kurses vorbereitet wurde „DevOps-Praktiken und -Werkzeuge“.

Infrastruktur-Tests als Code mit Pulumi. Teil 2

Deployment-Tests

Der betrachtete Teststil ist ein leistungsstarker Ansatz, der uns ermöglicht, White-Box-Tests durchzuführen, um das Innenleben unseres Infrastrukturcodes zu überprüfen. Allerdings schränkt er etwas ein, was wir überprüfen können. Die Tests basieren auf einem in-memory Deploy-Plan, der von Pulumi vor der tatsächlichen Bereitstellung erstellt wird, und daher kann die Bereitstellung selbst nicht getestet werden. Für solche Fälle gibt es in Pulumi ein Framework für Integrationstests. Und diese beiden Ansätze funktionieren hervorragend zusammen!

Das Integrationstest-Framework von Pulumi ist in Go geschrieben und genau damit testen wir den Großteil unseres internen Codes. Während der zuvor betrachtete Ansatz des Modultests eher wie ein White-Box-Test war, ist das Integrationstesting ein Black-Box-Test. (Es gibt auch Optionen für gründliche interne Tests.) Dieses Framework wurde entwickelt, um ein vollständiges Pulumi-Programm zu nehmen und verschiedene Lebenszyklusoperationen durchzuführen, wie das Bereitstellen eines neuen Stacks von Grund auf, das Aktualisieren mit Variationen und das Entfernen, möglicherweise mehrmals. Wir führen sie regelmäßig (z. B. nachts) und als Stresstests durch.

(Wir arbeiten daran, dass ähnliche Möglichkeiten für Integrationstests im nativen SDK der Sprachen verfügbar sind. Sie können das Go-Integrationstest-Framework unabhängig von der Sprache verwenden, in der Ihr Pulumi-Programm geschrieben ist.)

Nach dem Starten des Programms mit diesem Framework können Sie Folgendes überprüfen:

  • Der Code Ihres Projekts ist syntaktisch korrekt und funktioniert fehlerfrei.
  • Die Konfigurations- und Geheimnis-Einstellungen des Stacks funktionieren und werden korrekt interpretiert.
  • Ihr Projekt kann erfolgreich bei dem von Ihnen gewählten Cloud-Anbieter bereitgestellt werden.
  • Ihr Projekt kann erfolgreich vom Anfangszustand auf N andere Zustände aktualisiert werden.
  • Ihr Projekt kann erfolgreich gelöscht und aus Ihrem Cloud-Anbieter entfernt werden.

Wie wir bald sehen werden, kann dieses Framework auch zur Durchführung von Runtime-Validierungen verwendet werden.

Ein einfacher Integrationstest

Um dies in Aktion zu sehen, werfen wir einen Blick auf das Repository pulumi/examples, da unser Team und die Pulumi-Community es zur Prüfung ihrer eigenen Pull-Requests, Commits und nächtlichen Builds verwenden.

Im Folgenden finden Sie einen vereinfacht Test unseres Beispiels, das ein S3-Bucket und einige andere Objekte provisioniert:

example_test.go:

package test
 
import (
    "os"
    "path"
    "testing"
 
    "github.com/pulumi/pulumi/pkg/testing/integration"
)
 
func TestExamples(t *testing.T) {
    awsRegion := os.Getenv("AWS_REGION")
    if awsRegion == "" {
        awsRegion = "us-west-1"
    }
    cwd, _ := os.Getwd()
    integration.ProgramTest(t, &integration.ProgramTestOptions{
        Quick:       true,
        SkipRefresh: true,
        Dir:         path.Join(cwd, "..", "..", "aws-js-s3-folder"),
        Config: map[string]string{
            "aws:region": awsRegion,
        },
    })
}

Dieser Test durchläuft den grundlegenden Lebenszyklus der Erstellung, Änderung und Zerstörung eines Stacks für den Ordner aws-js-s3-folder. Es dauert etwa eine Minute, um den bestandenen Test zu melden:

$ go test .
PASS
ok      ... 43.993s

Es gibt viele Parameter, um das Verhalten dieser Tests anzupassen. Eine vollständige Liste der Optionen finden Sie in der Struktur ProgramTestOptions. Zum Beispiel können Sie den Jaeger-Endpunkt für das Tracing angeben (Tracing), angeben, dass Sie beim negativen Testen einen Testfehler erwarten (ExpectFailure), eine Reihe von "Änderungen" an der Anwendung anwenden, um aufeinanderfolgende Zustandsübergänge zu ermöglichen (EditDirs) und vieles mehr. Lassen Sie uns sehen, wie wir sie zur Überprüfung des Anwendungsdeployments verwenden können.

Überprüfung der Eigenschaften von Ressourcen

Die oben erwähnte Integration stellt sicher, dass unser Programm "funktioniert" — es stürzt nicht ab. Aber was ist, wenn wir die Eigenschaften des erhaltenen Stacks überprüfen möchten? Zum Beispiel, dass bestimmte Arten von Ressourcen erstellt wurden (oder nicht) und dass sie über bestimmte Attribute verfügen.

Parameter ExtraRuntimeValidation für ProgramTestOptions ermöglicht uns einen Blick auf den Zustand, den Pulumi nach der Bereitstellung festgehalten hat (post-deployment state), um zusätzliche Prüfungen vorzunehmen. Dies umfasst eine vollständige Momentaufnahme des Zustands des resultierenden Stacks, einschließlich Konfiguration, exportierte Ausgabewerte, alle Ressourcen und Werte ihrer Eigenschaften sowie alle Abhängigkeiten zwischen den Ressourcen.

Um ein einfaches Beispiel dafür zu sehen, überprüfen wir, dass unser Programm einen S3 Bucket erstellt.:

  integration.ProgramTest(t, &integration.ProgramTestOptions{
         wie zuvor...
        ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) {
            var foundBuckets int
            for _, res := range stack.Deployment.Resources {
                if res.Type == "aws:s3/bucket:Bucket" {
                    foundBuckets++
                }
            }
            assert.Equal(t, 1, foundBuckets, "Erwartet, dass ein einzelner AWS S3 Bucket gefunden wurde")
        },
    })

Jetzt, wo wir go test ausführen, wird es nicht nur durch eine Reihe von Lebenszyklustests gehen, sondern auch, nach erfolgreicher Bereitstellung des Stacks, eine zusätzliche Überprüfung des resultierenden Zustands durchführen.

Runtime-Tests

Bis jetzt waren alle Tests ausschließlich auf das Verhalten bei der Bereitstellung und auf das Ressourcenmodell von Pulumi ausgerichtet. Was tun, wenn Sie überprüfen möchten, ob Ihre vorbereitete Infrastruktur tatsächlich funktioniert? Zum Beispiel, dass die virtuelle Maschine läuft, der S3-Bucket das enthält, was wir erwarten, und so weiter.

Sie haben sich möglicherweise bereits gedacht, wie das geht: die Option ExtraRuntimeValidation für ProgramTestOptions ist eine großartige Gelegenheit dafür. An diesem Punkt führen Sie einen beliebigen Go-Test mit Zugriff auf den vollständigen Ressourcenstatus Ihres Programms aus. Dieser Status enthält Informationen wie die IP-Adressen der virtuellen Maschinen, URLs und alles, was erforderlich ist, um tatsächlich mit den bereitgestellten Cloud-Anwendungen und der Infrastruktur zu interagieren.

Zum Beispiel exportiert unser Testprogramm die Eigenschaft webEndpoint des Buckets mit dem Namen websiteUrl, die die vollständige URL darstellt, über die wir das konfigurierte index document. Obwohl wir in der Zustandsdatei nachsehen und diese Eigenschaft direkt lesen könnten, exportieren unsere Stacks in vielen Fällen nützliche Eigenschaften wie diese, die wir gerne zur Überprüfung verwenden: Bucket integration.ProgramTest(t, &integration.ProgramTestOptions{  wie zuvor ... ExtraRuntimeValidation: func(t *testing.T, stack integration.RuntimeValidationStackInfo) { url := "http://" + stack.Outputs["websiteUrl"].(string) resp, err := http.Get(url) if !assert.NoError(t, err) { return } if !assert.Equal(t, 200, resp.StatusCode) { return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if !assert.NoError(t, err) { return } assert.Contains(t, string(body), "Hallo, Pulumi!") }, })

Wie bei unseren vorherigen Runtime-Überprüfungen wird diese Überprüfung unmittelbar nach der Bereitstellung des Stacks durchgeführt, und das alles als Antwort auf den einfachen Aufruf

go test go test. Und das ist nur die Spitze des Eisbergs – alle Testmöglichkeiten von Go stehen zur Verfügung, die Sie im Code schreiben können.

Kontinuierliche Integration der Infrastruktur

Es ist gut, die Möglichkeit zu haben, Tests auf einem Laptop auszuführen, wenn viele Änderungen an der Infrastruktur vorgenommen werden, um sie vor der Überprüfung des Codes zu überprüfen. Aber wir und viele unserer Kunden testen die Infrastruktur in verschiedenen Phasen des Entwicklungszyklus:

  • In jedem offenen Pull-Request zur Prüfung vor dem Mergen.
  • Als Antwort auf jeden Commit, um sicherzustellen, dass das Mergen korrekt durchgeführt wurde.
  • Periodisch, zum Beispiel nachts oder wöchentlich für zusätzliche Tests.
  • Im Rahmen von Leistungstests oder Stresstests, die normalerweise über einen längeren Zeitraum hinweg durchgeführt werden und Tests parallel ausführen und/oder dasselbe Programm mehrere Male bereitstellen.

Für jede dieser Möglichkeiten unterstützt Pulumi die Integration mit Ihrem bevorzugten kontinuierlichen Integrationssystem. Bei kontinuierlicher Integration erhalten Sie die gleiche Testabdeckung für Ihre Infrastruktur wie für Softwareanwendungen.

Pulumi unterstützt gängige CI-Systeme. Hier sind einige davon:

Für weitere Informationen konsultieren Sie die Dokumentation zu Continuous Delivery.

Ephemeral Environments

Eine sehr mächtige Möglichkeit, die sich eröffnet, ist die Möglichkeit, ephemere Umgebungen ausschließlich zu Testzwecken bereitzustellen. Das Konzept von Projekten und Stacks Pulumi wurde so konzipiert, dass vollständig isolierte und unabhängige Umgebungen einfach bereitgestellt und abgerissen werden können, alles in einer kurzen CLI-Befehlsreihe oder mit einem Test-Framework.

Wenn Sie GitHub verwenden, bietet Pulumi GitHub App, das Ihnen dabei hilft, Tests in Pull-Requests innerhalb Ihres CI-Pipelines zu integrieren. Installieren Sie einfach die App im GitHub-Repository und Pulumi in Ihrem CI, und in den Pull-Requests wird Informationen über die Infrastrukturvorschau, Aktualisierungen und Testergebnisse hinzugefügt:

Infrastruktur-Tests als Code mit Pulumi. Teil 2

Wenn Sie Pulumi für Ihre Hauptabnahmetests verwenden, eröffnen sich Ihnen neue Automatisierungsmöglichkeiten, die die Teamleistung verbessern und das Vertrauen in die Qualität der Änderungen stärken.

Fazit

In diesem Artikel haben wir gesehen, dass uns bei der Verwendung von Programmiersprachen allgemeiner Zweck viele Softwareentwicklungsmethoden zur Verfügung stehen, die bei der Entwicklung unserer Anwendungen nützlich waren. Dazu gehören Modultests, Integrationstests sowie deren Interaktion für umfangreiche Runtime-Tests. Tests können auf Anforderung oder in Ihrem CI-System einfach ausgeführt werden.

Pulumi — eine Open-Source-Software, die kostenlos genutzt werden kann und mit Ihren bevorzugten Programmiersprachen und Clouds funktioniert — probieren Sie es noch heute aus!

Erster Teil

Quelle: habr.com

60GB SSD 8Gb DDR4