Test af infrastruktur som kode med Pulumi. Del 1

God eftermiddag venner. I forventning om starten af ​​et nyt flow på hastigheden "DevOps-praksis og værktøjer" Vi deler en ny oversættelse med dig. Gå.

Test af infrastruktur som kode med Pulumi. Del 1

Brug af Pulumi og generelle programmeringssprog til infrastrukturkode (Infrastructure as Code) giver mange fordele: tilgængeligheden af ​​færdigheder og viden, eliminering af boilerplate i koden gennem abstraktion, værktøjer, dit team kender, såsom IDE'er og linters. Alle disse softwareingeniørværktøjer gør os ikke kun mere produktive, men forbedrer også kvaliteten af ​​vores kode. Derfor er det kun naturligt, at brugen af ​​generelle programmeringssprog giver os mulighed for at introducere en anden vigtig softwareudviklingspraksis - test.

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

Test af infrastruktur som kode med Pulumi. Del 1

Hvorfor teste infrastruktur?

Før du går i detaljer, er det værd at stille spørgsmålet: "Hvorfor teste infrastruktur overhovedet?" Der er mange grunde til dette, og her er nogle af dem:

  • Enhedstest af individuelle funktioner eller fragmenter af din programlogik
  • Verificerer den ønskede tilstand af infrastrukturen mod visse begrænsninger.
  • Registrering af almindelige fejl, såsom mangel på kryptering af en lagerbøtte eller ubeskyttet, åbner adgang fra internettet til virtuelle maskiner.
  • Kontrol af implementering af infrastrukturforsyning.
  • Udførelse af runtime-test af applikationslogik, der kører inde i din "programmerede" infrastruktur for at kontrollere funktionaliteten efter klargøring.
  • Som vi kan se, er der en bred vifte af muligheder for test af infrastruktur. Polumi har mekanismer til test på ethvert punkt på dette spektrum. Lad os komme i gang og se, hvordan det virker.

Enhedstest

Pulumi-programmer er skrevet i generelle programmeringssprog som JavaScript, Python, TypeScript eller Go. Derfor er den fulde kraft af disse sprog, inklusive deres værktøjer og biblioteker, inklusive testrammer, tilgængelig for dem. Pulumi er multi-cloud, hvilket betyder, at den kan bruges til test fra enhver cloud-udbyder.

(I denne artikel, på trods af at vi er flersprogede og multicloud, bruger vi JavaScript og Mocha og fokuserer på AWS. Du kan bruge Python unittest, Go test framework eller en hvilken som helst anden testramme du kan lide. Og selvfølgelig fungerer Pulumi fantastisk med Azure, Google Cloud, Kubernetes.)

Som vi har set, er der flere grunde til, at du måske vil teste din infrastrukturkode. En af dem er konventionel enhedstest. Fordi din kode kan have funktioner - for eksempel til at beregne CIDR, dynamisk beregne navne, tags mv. - du skal nok prøve dem. Dette er det samme som at skrive almindelige enhedstests for applikationer på dit foretrukne programmeringssprog.
For at blive lidt mere kompliceret kan du tjekke, hvordan dit program allokerer ressourcer. For at illustrere, lad os forestille os, at vi skal oprette en simpel EC2-server, og vi vil være sikre på følgende:

  • Forekomster har et tag Name.
  • Forekomster bør ikke bruge inline script userData - vi skal bruge en AMI (image).
  • Der bør ikke være nogen SSH udsat for internettet.

Dette eksempel er baseret på 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 det grundlæggende Pulumi-program: det tildeler simpelthen en EC2-sikkerhedsgruppe og en instans. Det skal dog bemærkes, at her bryder vi alle tre regler nævnt ovenfor. Lad os skrive prøver!

At skrive prøver

Den generelle struktur af vores tests vil ligne almindelige 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, открытого в Интернет.
    });
});

Lad os nu skrive vores første test: sørg for, at forekomsterne har tagget Name. For at kontrollere dette henter vi blot EC2-instansobjektet og kontrollerer 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 ligner en almindelig test, men med et par funktioner, der er værd at bemærke:

  • Fordi vi forespørger om en ressources tilstand før implementering, køres vores test altid i "plan" (eller "preview") tilstand. Der er således mange ejendomme, hvis værdier simpelthen ikke vil blive hentet eller ikke vil blive defineret. Dette inkluderer alle output-egenskaber beregnet af din cloud-udbyder. Dette er normalt for vores tests - vi kontrollerer kun inputdata. Vi vender tilbage til dette spørgsmål senere, når det kommer til integrationstest.
  • Da alle Pulumi-ressourceegenskaber er output, og mange af dem evalueres asynkront, skal vi bruge appliceringsmetoden for at få adgang til værdierne. Dette minder meget om løfter og en funktion then .
  • Da vi bruger flere egenskaber til at vise ressource-URN'en i fejlmeddelelsen, skal vi bruge funktionen pulumi.allat kombinere dem.
  • Endelig, da disse værdier beregnes asynkront, skal vi bruge Mochas indbyggede asynkrone tilbagekaldsfunktion done eller returnere et løfte.

Når vi har sat alt op, har vi adgang til input som simple JavaScript-værdier. Ejendom tags er et kort (associativt array), så vi vil bare sørge for, at det (1) ikke er falsk, og (2) der er en nøgle til Name. Det er meget enkelt, og nu kan vi teste hvad som helst!

Lad os nu skrive vores anden check. Det er endnu nemmere:

 // 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 endelig, lad os skrive den tredje test. Dette bliver lidt mere kompliceret, fordi vi leder efter login-reglerne i forbindelse med sikkerhedsgruppen, som der kan være mange af, og CIDR-intervallerne i de regler, som der også kan være mange af. Men vi klarede:

    // 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 er alt. Lad os nu køre testene!

Løbende test

I de fleste tilfælde kan du køre test på den sædvanlige måde ved at bruge den testramme, du vælger. Men der er én funktion ved Pulumi, som er værd at være opmærksom på.
Til at køre Pulumi-programmer bruges typisk pulimi CLI (Command Line interface), som konfigurerer sprogets kørselstid, styrer lanceringen af ​​Pulumi-motoren, så operationer med ressourcer kan registreres og inkluderes i planen mv. Der er dog et problem. Når du kører under kontrol af din testramme, vil der ikke være nogen kommunikation mellem CLI'en og Pulumi-motoren.

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

  • Projektnavn, som er indeholdt i miljøvariablen PULUMI_NODEJS_PROJECT (eller mere generelt, PULUMI__PROJECT для других языков).
    Navnet på stakken, der er angivet i miljøvariablen PULUMI_NODEJS_STACK (eller mere generelt, PULUMI__ STACK).
    Dine stakkonfigurationsvariabler. De kan opnås ved hjælp af en miljøvariabel PULUMI_CONFIG og deres format er JSON-kort med nøgle/værdi-par.

    Programmet vil udsende advarsler, der indikerer, at forbindelsen til CLI/motoren ikke er tilgængelig under udførelsen. Dette er vigtigt, fordi dit program faktisk ikke vil implementere noget, og det kan komme som en overraskelse, hvis det ikke var det, du havde til hensigt at gøre! For at fortælle Pulumi, at det er præcis, hvad du har brug for, kan du installere PULUMI_TEST_MODE в true.

    Forestil dig, at vi skal angive projektnavnet i my-ws, stak navn dev, og AWS-regionen us-west-2. Kommandolinjen til at køre Mokka-test vil se sådan ud:

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

    Gør du dette, som forventet, vil det vise os, at vi har tre mislykkede tests!

    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

    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 kør derefter testene igen:

    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)

    Alt gik godt... Hurra! ✓✓✓

    Det var alt for i dag, men vi vil tale om implementeringstest i anden del af oversættelsen 😉

Kilde: www.habr.com

Tilføj en kommentar