Testen Sie Ihre Infrastruktur als Code mit Pulumi. Teil 1

Guten Tag, Freunde. In Erwartung des Starts eines neuen Streams im Kurs "DevOps Practices and Tools" teilen wir Ihnen eine neue Übersetzung mit. Lass uns gehen.



Die Verwendung von Pulumi und allgemeinen Programmiersprachen für die Infrastruktur als Code bietet viele Vorteile: Kenntnisse und Kenntnisse, Eliminierung von Boilerplate im Code durch Abstraktion, Tools, die Ihrem Team vertraut sind, wie IDEs und Linters. All diese Software-Engineering-Tools machen uns nicht nur produktiver, sondern verbessern auch die Qualität des Codes. Daher ist es selbstverständlich, dass Sie durch die Verwendung allgemeiner Programmiersprachen eine weitere wichtige Praxis in der Softwareentwicklung einführen können - das Testen .

In diesem Artikel werden wir uns ansehen, wie Pulumi hilft, unsere "Infrastruktur als Code" zu testen.



Warum die Infrastruktur testen?


Bevor wir auf Details eingehen, lohnt es sich, die Frage zu stellen: „Warum müssen wir die Infrastruktur überhaupt testen?“ Dafür gibt es viele Gründe, und hier sind einige davon:

  • Unit-Test einzelner Funktionen oder logischer Teile in Ihrem Programm
  • Überprüfen Sie den gewünschten Status der Infrastruktur auf Einhaltung bestimmter Einschränkungen.
  • Erkennung häufiger Fehler, z. B. fehlende Verschlüsselung des Speicherbereichs oder unsicherer offener Zugriff des Internets auf virtuelle Maschinen.
  • Überprüfung der Bereitstellung der Infrastruktur.
  • Durchführen von Laufzeitprüfungen der Logik der Anwendung, die in Ihrer „programmierten“ Infrastruktur ausgeführt wird, um den Zustand nach der Bereitstellung zu überprüfen.
  • Wie wir sehen können, gibt es eine Vielzahl von Testoptionen für die Infrastruktur. Polumi verfügt an jedem Punkt in diesem Spektrum über Testmechanismen. Lassen Sie uns anfangen und sehen, wie es funktioniert.

Unit Testing


Pulumi-Programme werden in universellen Programmiersprachen wie JavaScript, Python, TypeScript oder Go erstellt. Daher steht ihnen die volle Leistungsfähigkeit dieser Sprachen zur Verfügung, einschließlich ihrer Tools und Bibliotheken, einschließlich Testframeworks. Pulumi ist eine Multi-Cloud, dh die Möglichkeit, beliebige Cloud-Anbieter zum Testen zu verwenden.

(In diesem Artikel verwenden wir JavaScript und Mocha, obwohl wir mehrsprachig und mehrwolkig sind, und konzentrieren uns auf AWS. Sie können Python unittest , das Go-Testframework oder ein beliebiges anderes Testframework verwenden. Und natürlich funktioniert Pulumi hervorragend mit Azure, Google Cloud, Kubernetes.)

Wie wir gesehen haben, gibt es mehrere Gründe, warum Sie möglicherweise Ihren Infrastrukturcode testen müssen. Eine davon ist die übliche Unit-Prüfung. Da Ihr Code möglicherweise Funktionen hat, z. B. zum Berechnen des CIDR, zum dynamischen Berechnen von Namen, Tags usw. - Sie möchten sie wahrscheinlich testen. Dies entspricht dem Schreiben regelmäßiger Komponententests für Anwendungen in Ihrer bevorzugten Programmiersprache.
Wenn Sie die Dinge etwas komplizieren, können Sie überprüfen, wie Ihr Programm Ressourcen zuweist. Stellen wir uns zur Veranschaulichung vor, wir müssen einen einfachen EC2-Server erstellen und möchten Folgendes sicherstellen:

  • Instanzen haben ein Name .
  • Instanzen sollten das Inline-Skript userData nicht verwenden - wir müssen das AMI (Bild) verwenden.
  • Im Internet sollte kein SSH geöffnet sein.

Dieses Beispiel basiert auf meinem aws-js-webserver-Beispiel :

index.js:


 "use strict"; let aws = require("@pulumi/aws"); let group = new aws.ec2.SecurityGroup("web-secgrp", { ingress: [ { protocol: "tcp", fromPort: 22, toPort: 22, cidrBlocks: ["0.0.0.0/0"] }, { protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] }, ], }); let userData = `#!/bin/bash echo "Hello, World!" > index.html nohup python -m SimpleHTTPServer 80 &`; let server = new aws.ec2.Instance("web-server-www", { instanceType: "t2.micro", securityGroups: [ group.name ], // reference the group object above ami: "ami-c55673a0" // AMI for us-east-2 (Ohio), userData: userData // start a simple web server }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns; 

Dies ist das grundlegende Pulumi-Programm: Es ordnet einfach die EC2-Sicherheitsgruppe und -Instanz zu. Es ist jedoch zu beachten, dass wir hier gegen alle drei oben genannten Regeln verstoßen. Schreiben wir Tests!

Tests schreiben


Die allgemeine Struktur unserer Tests sieht aus wie normale Mokka-Tests:

ec2tests.js


 test.js: let assert = require("assert"); let mocha = require("mocha"); let pulumi = require("@pulumi/pulumi"); let infra = require("./index"); describe("Infrastructure", function() { let server = infra.server; describe("#server", function() { // TODO(check 1):    Name. // TODO(check 2):    inline- userData. }); let group = infra.group; describe("#group", function() { // TODO(check 3):    SSH,   . }); }); 

Schreiben wir nun unseren ersten Test: Stellen Sie sicher, dass die Instanzen ein Name . Um dies zu überprüfen, rufen wir einfach das EC2-Instanzobjekt ab und überprüfen die entsprechende tags Eigenschaft:

  // check 1:    Name. it("must have a name tag", function(done) { pulumi.all([server.urn, server.tags]).apply(([urn, tags]) => { if (!tags || !tags["Name"]) { done(new Error(`Missing a name tag on server ${urn}`)); } else { done(); } }); }); 

Es sieht aus wie ein regulärer Test, aber mit einigen Funktionen, die Aufmerksamkeit verdienen:

  • Da wir den Status der Ressource vor der Bereitstellung anfordern, werden unsere Tests immer im Modus "Plan" (oder "Vorschau") ausgeführt. Daher gibt es viele Eigenschaften, deren Werte einfach nicht empfangen oder nicht ermittelt werden. Dies schließt alle berechneten Ausgabeeigenschaften ein Ihr Cloud-Anbieter. Für unsere Tests ist dies normal. Wir überprüfen nur die Eingabedaten. Wir werden später auf dieses Problem zurückkommen, wenn es um Integrationstests geht.
  • Da alle Eigenschaften von Pulumi-Ressourcen "Ausgaben" sind und viele von ihnen asynchron berechnet werden, müssen wir die Methode apply verwenden, um auf die Werte zuzugreifen. Dies ist sehr ähnlich zu Versprechen und then .
  • Da wir mehrere Eigenschaften verwenden, um die Ressourcen-URN in der Fehlermeldung pulumi.all , müssen wir die Funktion pulumi.all verwenden, um sie zu kombinieren.
  • Da diese Werte asynchron berechnet werden, müssen wir schließlich die integrierte asynchrone Funktion von Mocha mit einem Rückruf oder einer Rückgabe eines Versprechens verwenden.

Nachdem wir alles konfiguriert haben, haben wir Zugriff auf die Eingabedaten als einfache JavaScript-Werte. Die Eigenschaft tags ist eine Zuordnung (assoziatives Array). Wir stellen daher nur sicher, dass sie (1) nicht falsch ist und (2) einen Schlüssel für Name . Es ist sehr einfach und jetzt können wir alles überprüfen!

Schreiben wir jetzt unseren zweiten Scheck. Das ist noch einfacher:

  // check 2:    inline- userData. it("must not use userData (use an AMI instead)", function(done) { pulumi.all([server.urn, server.userData]).apply(([urn, userData]) => { if (userData) { done(new Error(`Illegal use of userData on server ${urn}`)); } else { done(); } }); }); 


Und schließlich werden wir den dritten Test schreiben. Dies wird etwas komplizierter, da wir nach Anmelderegeln suchen, die einer Sicherheitsgruppe zugeordnet sind, die viele sein kann, und nach CIDR-Bereichen in diesen Regeln, die auch viele sein können. Aber wir haben es geschafft:

  // check 3:    SSH,   . it("must not open port 22 (SSH) to the Internet", function(done) { pulumi.all([ group.urn, group.ingress ]).apply(([ urn, ingress ]) => { if (ingress.find(rule => rule.fromPort == 22 && rule.cidrBlocks.find(block => block === "0.0.0.0/0"))) { done(new Error(`Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group ${urn}`)); } else { done(); } }); }); 

Das ist alles. Lassen Sie uns jetzt die Tests durchführen!

Ausführen von Tests


In den meisten Fällen können Sie Tests auf die übliche Weise mit dem Testframework Ihrer Wahl ausführen. Es gibt jedoch eine Pulumi-Funktion, auf die Sie achten sollten.
Normalerweise wird die Pulimi-CLI (Befehlszeilenschnittstelle, Befehlszeilenschnittstelle) zum Starten von Pulumi-Programmen verwendet, die die Laufzeit der Sprache festlegen, den Start der Pulumi-Engine steuern, sodass Vorgänge mit Ressourcen aufgezeichnet und in den Plan aufgenommen werden können usw. Es gibt jedoch ein Problem. Beim Start unter der Kontrolle Ihres Testframeworks besteht keine Verbindung zwischen der CLI und der Pulumi-Engine.

Um dieses Problem zu umgehen, müssen wir nur Folgendes angeben:

  • Der Name des Projekts, der in der Umgebungsvariablen PULUMI_NODEJS_PROJECT (oder allgemeiner PULUMI__PROJECT ).
    Der Name des Stapels, der in der Umgebungsvariablen PULUMI_NODEJS_STACK (oder allgemeiner PULUMI__ STACK).
    Ihre Stapelkonfigurationsvariablen. Sie können mit der Umgebungsvariablen PULUMI_CONFIG abgerufen werden. Ihr Format ist eine JSON-Zuordnung mit Schlüssel / Wert-Paaren.

    Das Programm gibt Warnungen aus, die darauf hinweisen, dass zur Laufzeit keine Verbindung zur CLI / Engine verfügbar ist. Dies ist wichtig, da Ihr Programm in Wirklichkeit nichts bereitstellt und dies eine Überraschung sein kann, wenn Sie dies nicht tun möchten! Um Pulumi mitzuteilen, dass dies genau das ist, was Sie benötigen, können Sie PULUMI_TEST_MODE auf true .

    Stellen Sie sich vor, wir müssen den Namen des Projekts in my-ws , den Namen des my-ws und die AWS us-west-2 angeben. Die Befehlszeile zum Ausführen von Mokka-Tests sieht folgendermaßen aus:

     $ PULUMI_TEST_MODE=true \ PULUMI_NODEJS_STACK="my-ws" \ PULUMI_NODEJS_PROJECT="dev" \ PULUMI_CONFIG='{ "aws:region": "us-west-2" }' \ mocha tests.js 

    Wenn wir dies wie erwartet tun, werden wir sehen, dass wir drei gefallene Tests haben!

     Infrastructure #server 1) must have a name tag 2) must not use userData (use an AMI instead) #group 3) must not open port 22 (SSH) to the Internet 0 passing (17ms) 3 failing 1) Infrastructure #server must have a name tag: Error: Missing a name tag on server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 2) Infrastructure #server must not use userData (use an AMI instead): Error: Illegal use of userData on server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 3) Infrastructure #group must not open port 22 (SSH) to the Internet: Error: Illegal SSH port 22 open to the Internet (CIDR 0.0.0.0/0) on group 

    Lassen Sie uns unser Programm reparieren:

     "use strict"; let aws = require("@pulumi/aws"); let group = new aws.ec2.SecurityGroup("web-secgrp", { ingress: [ { protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] }, ], }); let server = new aws.ec2.Instance("web-server-www", { tags: { "Name": "web-server-www" }, instanceType: "t2.micro", securityGroups: [ group.name ], // reference the group object above ami: "ami-c55673a0" // AMI for us-east-2 (Ohio), }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns; 

    Führen Sie dann die Tests erneut aus:

     Infrastructure #server ✓ must have a name tag ✓ must not use userData (use an AMI instead) #group ✓ must not open port 22 (SSH) to the Internet 3 passing (16ms) 

    Alles lief gut ... Hurra! ✓ ✓ ✓

    Das ist alles für heute, aber wir werden im zweiten Teil der Übersetzung über das Testen der Bereitstellung sprechen ;-)

Source: https://habr.com/ru/post/de463001/


All Articles