Test af infrastruktur som kode med Pulumi. Del 1

Goddag, venner. I takt med at vi forbereder os på starten af det nye hold i kurset „DevOps praksis og værktøjer“ , deler vi en ny oversættelse med jer. Lad os komme i gang.

Test af infrastruktur som kode med Pulumi. Del 1

Brugen af Pulumi og generelle programmeringssprog til infrastrukturskode (Infrastructure as Code) giver mange fordele: evnen til at anvende færdigheder og viden, eliminering af boilerplate-kode gennem abstraktion, samt de værktøjer, som dit team er bekendt med, såsom IDE'er og linters. Alle disse programudviklingsværktøjer gør ikke kun os mere produktive, men forbedrer også kvaliteten af koden. Derfor er det ganske naturligt, at brugen af generelle programmeringssprog giver mulighed for at implementere endnu en vigtig praksis i softwareudvikling — testningen.

I denne artikel vil vi se på, hvordan Pulumi hjælper med at teste vores 'infrastruktur som kode'.

Test af infrastruktur som kode med Pulumi. Del 1

Hvorfor teste infrastrukturen?

Før vi går ind i detaljerne, er det værd at stille spørgsmålet: 'Hvorfor overhovedet teste infrastrukturen?' Der er mange grunde til dette, og her er nogle af dem:

  • Modultestning af individuelle funktioner eller logikfragmenter i dit program
  • Kontrol af den ønskede tilstand af infrastrukturen for at sikre overensstemmelse med bestemte restriktioner.
  • Opdagelse af almindelige fejl, såsom mangel på kryptering af storage bucket eller uautoriseret, offentlig adgang fra internettet til virtuelle maskiner.
  • Verifikation af, at provisioneringen af infrastrukturen gennemføres.
  • Udførelse af runtime-test af applikationslogik, der kører inden for din 'programmerede' infrastruktur for at kontrollere funktionaliteten efter provisioneringen.
  • Som vi kan se, er der et bredt spektrum af muligheder for at teste infrastrukturen. I Pulumi er der mekanismer til at teste i hver ende af dette spektrum. Lad os begynde og se, hvordan det fungerer.

Modultestning

Pulumi-programmer skabes på generelle programmeringssprog som JavaScript, Python, TypeScript eller Go. Derfor har de adgang til den fulde kraft af disse sprog, inklusive deres værktøjer og biblioteker, herunder testframeworks. Pulumi er multisky, hvilket betyder, at enhver cloud-leverandør kan bruges til testene.

(I denne artikel, på trods af flersprogethed og multisky, bruger vi JavaScript og Mocha og fokuserer på AWS. Python kan også anvendes unittest, testframework Go eller et andet testframework, du foretrækker. Og selvfølgelig fungerer Pulumi fremragende med Azure, Google Cloud, Kubernetes.)

Som vi har set, er der flere grunde til, at det kan være nødvendigt at teste din infrastrukturkode. En af dem er almindelig enhedstestning. Da din kode kan have funktioner — for eksempel til at beregne CIDR, dynamisk beregning af navne, tags osv. — vil du sandsynligvis gerne teste dem. Det er det samme som at skrive almindelige enhedstests for applikationer på dit foretrukne programmeringssprog.
Hvis vi vil gøre det lidt mere komplekst, kan vi tjekke, hvordan dit program distribuerer ressourcer. For at illustrere lad os forestille os, at vi skal oprette en simpel EC2-server, og vi vil sikre os, at følgende betingelser er opfyldt:

  • Instanserne har et tag Navn.
  • Instanserne må ikke bruge inline-script userData — vi skal bruge AMI (billede).
  • Der må ikke være SSH åbent på internettet.

Dette eksempel er skrevet inspireret af mit eksempel 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;

Dette er et grundlæggende Pulumi-program: det allokerer blot en EC2-sikkerhedsgruppe og en instans. Det skal dog bemærkes, at vi her overtræder alle tre regler, der er beskrevet ovenfor. Lad os begynde at skrive tests!

Lad os skrive tests

Den overordnede struktur for vores tests vil se ud som almindelige Mocha-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): Der skal være et Navn-tag.
        \/\/ TODO(check 2): Der må ikke være inline-script i userData.
    });
    let group = infra.group;
    describe("#group", function() {
        \/\/ TODO(check 3): Der må ikke være SSH åbent på internettet.
    });
});

Lad os nu skrive vores første test: vi skal sikre os, at instanserne har et tag Navn. For at kontrollere dette får vi blot EC2-instansobjektet og tjekker den tilsvarende egenskab 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();
                }
            });
        });

Det ser ud som en almindelig test, men med nogle egenskaber, der fortjener opmærksomhed:

  • Da vi anmoder om ressourcens tilstand inden distribution, udføres vores tests altid i "plan" (eller "preview"). Dermed findes der mange egenskaber, hvis værdier blot ikke vil blive opnået eller vil være ubestemte. Dette inkluderer alle output-ejendomme, der beregnes af din cloud-udbyder. For vores tests er det fint - vi tjekker kun input. Vi vil vende tilbage til dette spørgsmål senere, når vi når til integrationstests.
  • Da alle egenskaber for Pulumi-ressourcer er "outputs", og mange af dem beregnes asynkront, er vi nødt til at bruge metoden apply for at få adgang til værdierne. Det minder meget om promises og af-funktionen. så .
  • Da vi bruger flere egenskaber for at vise URN for ressourcen i fejlmeddelelsen, skal vi bruge funktionen pulumi.all, for at kombinere dem.
  • Endelig, da disse værdier beregnes asynkront, skal vi bruge den indbyggede asynkrone funktionalitet i Mocha med et callback done eller returnere et promise.

Når vi har konfigureret alt, vil vi have adgang til inputdataene som enkle JavaScript-værdier. Egenskaben tags er et map (associativt array), så vi skal bare sikre os, at det (1) ikke er false, og (2) har en nøgle for Navn. Det er meget enkelt, og nu kan vi tjekke hvad som helst!

Nu lad os skrive vores anden kontrol. Det er endnu lettere:

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

Og til slut, lad os skrive den tredje test. Det vil være lidt vanskeligere, fordi vi leder efter indgangsregler relateret til en sikkerhedsgruppe, som kan være mange, samt CIDR-områder i disse regler, som også kan være mange. Men vi klarede det:

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

Det var det. Lad os nu køre testene!

Kørsel af tests

Testene kan som regel køres på almindelig vis ved at bruge dit valgte test-framework. Men der er én egenskab ved Pulumi, som er værd at bemærke.
Normalt bruges pulimi CLI (Command Line Interface, kommandolinjegrænseflade) til at køre Pulumi-programmer, som konfigurerer sprogets runtime, styrer kørsel af Pulumi-motoren, så operationer med ressourcer kan registreres og inkluderes i planen osv. Men der er et problem. Når du kører under kontrol af dit testframework, vil der ikke være forbindelse mellem CLI og Pulumi-motoren.

For at omgå dette problem skal vi blot angive følgende:

  • Projektets navn, som findes i miljøvariablen PULUMI_NODEJS_PROJECT (eller, generelt set, PULUMI__PROJECT for andre sprog).
    Navnet på stakken, som er angivet i miljøvariablen PULUMI_NODEJS_STACK (eller, generelt set, PULUMI__STACK).
    Dine stak-konfigurationsvariabler. De kan hentes ved hjælp af miljøvariablen PULUMI_CONFIG og deres format er et JSON-kort med nøgle/værdi-par.

    Programmet vil give advarsler, der siger, at der ikke er forbindelse til CLI/motoren under kørsel. Dette er vigtigt, fordi, i virkeligheden, vil dit program ikke implementere noget, og det kan komme som en overraskelse, hvis det ikke var det, du ønskede at gøre! For at fortælle Pulumi, at det er præcist det, du har brug for, kan du sætte PULUMI_TEST_MODE i true.

    Forestil dig, at vi skal angive projektets navn som my-ws, stakkens navn dev, og AWS-regionen us-west-2. Kommandoen til at køre Mocha-tests vil se således ud:

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

    At køre dette, som forventet, vil vise os, at vi har tre fejlede tests!

    Infrastructure
        #server
          1) skal have et navn tag
     	 2) må ikke bruge userData (brug en AMI i stedet)
        #group
          3) må ikke åbne port 22 (SSH) for internettet
    
      0 passer (17ms)
      3 fejlede
     
     1) Infrastructure
           #server
             skal have et navn tag:
         Fejl: Mangler et navn tag på server
            urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www
    
     2) Infrastructure
           #server
             må ikke bruge userData (brug en AMI i stedet):
         Fejl: Ulovlig brug af userData på server
            urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www
    
     3) Infrastructure
           #group
             må ikke åbne port 22 (SSH) for internettet:
         Fejl: Ulovlig SSH-port 22 åbnet for internettet (CIDR 0.0.0.0/0) på gruppen

    Lad os rette vores program:

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

    Og så vil vi køre testene igen:

    Infrastruktur
        #server
          ✓ skal have en navnetag
          ✓ må ikke bruge userData (brug i stedet en AMI)
        #group
          ✓ må ikke åbne port 22 (SSH) til internettet
     
     
     3 bestået (16ms)

    Det gik hele vejen… Hurra! ✓✓✓

    Det var alt for i dag, og vi vil tale om test af deployment i den anden del af oversættelsen 😉

Kilde: habr.com

Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere 🔥 Køb pålidelig webhosting med DDoS-beskyttelse, VPS VDS-servere | ProHoster