Testen Infrastruktur als Code mat Pulumi. Deel 1

Gudde Mëtteg Frënn. An Erwaardung vum Ufank vun engem neie Flux um Taux "DevOps Praktiken an Tools" Mir deele mat Iech eng nei Iwwersetzung. Gitt.

Testen Infrastruktur als Code mat Pulumi. Deel 1

D'Benotzung vu Pulumi an allgemeng Zweck Programméierungssprooche fir Infrastrukturcode (Infrastruktur als Code) bitt vill Virdeeler: d'Disponibilitéit vu Fäegkeeten a Wëssen, Eliminatioun vu Kesselplat am Code duerch Abstraktioun, Tools déi Är Team vertraut sinn, sou wéi IDEs a Linters. All dës Software Engineering Tools maachen eis net nëmme méi produktiv, awer verbesseren och d'Qualitéit vun eisem Code. Dofir ass et nëmmen natierlech datt d'Benotzung vun allgemeng Zwecker Programméierungssproochen eis erlaabt eng aner wichteg Softwareentwécklungspraxis aféieren - Test.

An dësem Artikel wäerte mir kucken wéi Pulumi eis hëlleft eis Infrastruktur-als-Code ze testen.

Testen Infrastruktur als Code mat Pulumi. Deel 1

Firwat Test Infrastruktur?

Ier Dir an den Detail geet, ass et derwäert d'Fro ze stellen: "Firwat iwwerhaapt Infrastruktur testen?" Et gi vill Grënn dofir an hei sinn e puer vun hinnen:

  • Eenheetstest vun eenzelne Funktiounen oder Fragmenter vun Ärer Programmlogik
  • Verifizéiert de gewënschten Zoustand vun der Infrastruktur géint bestëmmte Contrainten.
  • Detektioun vu gemeinsame Feeler, wéi zum Beispill e Manktem u Verschlësselung vun engem Späicherbecher oder ongeschützt, oppen Zougang vum Internet op virtuelle Maschinnen.
  • Iwwerpréift d'Ëmsetzung vun der Infrastrukturbestëmmung.
  • Runtime Tester vun der Applikatiounslogik ausféieren, déi an Ärer "programméierter" Infrastruktur lafen fir d'Funktionalitéit no der Dispositioun ze kontrolléieren.
  • Wéi mir kënne gesinn, gëtt et eng breet Palette vun Infrastrukturtestoptiounen. Polumi huet Mechanismen fir op all Punkt op dësem Spektrum ze testen. Loosst eis ufänken a kucken wéi et funktionnéiert.

Eenheet Testen

Pulumi Programmer ginn an allgemeng Zwecker Programméierungssprooche geschriwwe wéi JavaScript, Python, TypeScript oder Go. Dofir ass déi voll Kraaft vun dëse Sproochen, inklusiv hir Tools a Bibliothéiken, dorënner Testframeworks, fir si verfügbar. Pulumi ass Multi-Cloud, dat heescht datt et fir Tester vun all Cloud Provider benotzt ka ginn.

(An dësem Artikel, obwuel mer méisproocheg a multicloud sinn, benotze mir JavaScript a Mocha a konzentréieren eis op AWS. Dir kënnt Python benotzen unittest, Go Test Kader, oder all aner Test Kader Dir gären. An, natierlech, Pulumi funktionnéiert super mat Azure, Google Cloud, Kubernetes.)

Wéi mir gesinn hunn, ginn et e puer Grënn firwat Dir Ären Infrastrukturcode wëllt testen. Ee vun hinnen ass konventionell Eenheetstest. Well Äre Code kann Funktiounen hunn - zum Beispill, fir CIDR ze berechnen, dynamesch Berechent Nimm, Tags, etc. - Dir wëllt se wahrscheinlech testen. Dëst ass d'selwecht wéi regelméisseg Eenheetstester fir Uwendungen an Ärer Liiblingsprogramméierungssprooch ze schreiwen.
Fir e bësse méi komplizéiert ze ginn, kënnt Dir kucken wéi Äre Programm Ressourcen allocéiert. Fir ze illustréieren, loosst eis virstellen datt mir en einfachen EC2 Server musse kreéieren a mir wëlle sécher sinn op déi folgend:

  • Instanzen hunn e Tag Name.
  • Instanzen sollen net Inline Skript benotzen userData - mir mussen en AMI (Bild) benotzen.
  • Et sollt keen SSH um Internet ausgesat sinn.

Dëst Beispill baséiert op mäi Beispill aws-js-webserver:

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;

Dëst ass de Basis Pulumi Programm: et verdeelt einfach eng EC2 Sécherheetsgrupp an eng Instanz. Wéi och ëmmer, et sollt bemierkt ginn datt mir hei all dräi Regelen briechen déi hei uewen ernimmt sinn. Loosst eis Tester schreiwen!

Schreiwen Tester

Déi allgemeng Struktur vun eisen Tester wäert ausgesinn wéi normal Mocha Tester:

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, открытого в Интернет.
    });
});

Loosst eis elo eisen éischten Test schreiwen: gitt sécher datt d'Instanzen den Tag hunn Name. Fir dëst z'iwwerpréiwen, kréien mir einfach den EC2 Instanzobjekt a kontrolléieren déi entspriechend Eegeschafte tags:

 // 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();
                }
            });
        });

Et gesäit aus wéi e reegelméissegen Test, awer mat e puer Features ze bemierken:

  • Well mir den Zoustand vun enger Ressource virum Ofbau froen, ginn eis Tester ëmmer am "Plan" (oder "Virschau") Modus lafen. Also ginn et vill Eegeschafte deenen hir Wäerter einfach net erëmfonnt ginn oder net definéiert ginn. Dëst beinhalt all Output Eegeschafte berechent vun Ärem Cloud Provider. Dëst ass normal fir eis Tester - mir kontrolléieren nëmmen d'Inputdaten. Mir komme spéider op dëst Thema zréck, wann et ëm Integratiounstester kënnt.
  • Well all Pulumi Ressourceeigenschaften Ausgänge sinn, a vill vun hinnen asynchron evaluéiert ginn, musse mir d'Applikatiounsmethod benotzen fir op d'Wäerter ze kommen. Dëst ass ganz ähnlech wéi Verspriechen an AFunktioun then .
  • Well mir e puer Eegeschafte benotze fir d'Ressource URN an der Fehlermeldung ze weisen, musse mir d'Funktioun benotzen pulumi.allhinnen ze kombinéieren.
  • Schlussendlech, well dës Wäerter asynchron berechent ginn, musse mir Mocha's agebaute asynchron Callback Feature benotzen done oder e Verspriechen zréck.

Wann mir alles ageriicht hunn, hu mir Zougang zu den Inputen als einfache JavaScript Wäerter. Immobilie tags ass eng Kaart (associativ Array), also wäerte mir just sécherstellen datt et (1) net falsch ass, an (2) et ass e Schlëssel fir Name. Et ass ganz einfach an elo kënne mir alles testen!

Loosst eis elo eisen zweete Scheck schreiwen. Et ass nach méi einfach:

 // 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();
                }
            });
        });

A schliisslech schreiwen mer den drëtten Test. Dëst wäert e bësse méi komplizéiert sinn, well mir sichen no de Loginregelen, déi mat der Sécherheetsgrupp verbonne sinn, vun deenen et vill kënne sinn, an d'CIDR rangéiert an deene Regelen, vun deenen et och vill kënne sinn. Awer mir hunn et fäerdeg bruecht:

    // 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();
                }
            });
        });

Dat ass alles. Loosst eis elo d'Tester lafen!

Lafen Tester

An deene meeschte Fäll kënnt Dir Tester op déi üblech Manéier lafen, andeems Dir den Testkader vun Ärer Wiel benotzt. Awer et gëtt eng Feature vu Pulumi déi et wäert ass opmierksam ze maachen.
Typesch, fir Pulumi Programmer ze lafen, gëtt de pulimi CLI (Command Line Interface) benotzt, deen d'Sprooch Runtime konfiguréiert, d'Start vun der Pulumi Motor kontrolléiert, sou datt Operatiounen mat Ressourcen opgeholl kënne ginn an am Plang abegraff sinn, etc. Allerdéngs gëtt et ee Problem. Wann Dir ënner der Kontroll vun Ärem Testkader leeft, gëtt et keng Kommunikatioun tëscht dem CLI an dem Pulumi-Motor.

Fir dëst Thema ëmzegoen, brauche mir just déi folgend ze spezifizéieren:

  • Projet Numm, deen an der Ëmfeld Variabel enthält PULUMI_NODEJS_PROJECT (oder, méi allgemeng, PULUMI__PROJECT для других языков).
    Den Numm vum Stack deen an der Ëmfeldvariabel uginn ass PULUMI_NODEJS_STACK (oder, méi allgemeng, PULUMI__ STACK).
    Är Stack Configuratioun Verännerlechen. Si kënne mat enger Ëmfeldvariabel kritt ginn PULUMI_CONFIG an hiert Format ass JSON Kaart mat Schlëssel / Wäertpairen.

    De Programm wäert Warnungen erausginn, déi uginn datt d'Verbindung zum CLI / Motor net während der Ausféierung verfügbar ass. Dëst ass wichteg well Äre Programm tatsächlech näischt wäert ofsetzen an et kann als Iwwerraschung kommen wann dat net ass wat Dir geduecht hutt ze maachen! Fir de Pulumi ze soen datt dat genau ass wat Dir braucht, kënnt Dir installéieren PULUMI_TEST_MODE в true.

    Stellt Iech vir, mir mussen den Numm vum Projet spezifizéieren my-ws, Stack Numm dev, an AWS Regioun us-west-2. D'Kommandozeil fir Mocha Tester ze lafen gesäit esou aus:

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

    Dëst ze maachen, wéi erwaart, wäert eis weisen datt mir dräi gescheitert Tester hunn!

    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

    Loosst eis eise Programm fixéieren:

    "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;
    

    An dann d'Tester nach eng Kéier lafen:

    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 goung gutt... Hurra! ✓✓✓

    Dat ass alles fir haut, awer mir schwätzen iwwer d'Deployment Testen am zweeten Deel vun der Iwwersetzung 😉

Source: will.com

Setzt e Commentaire