Tester infrastruktur som kode med Pulumi. Del 1

God ettermiddag venner. I påvente av starten på en ny strømning på rate "DevOps-praksis og verktøy" Vi deler en ny oversettelse med deg. Gå.

Tester infrastruktur som kode med Pulumi. Del 1

Å bruke Pulumi og generelle programmeringsspråk for infrastrukturkode (Infrastructure as Code) gir mange fordeler: tilgjengeligheten av ferdigheter og kunnskaper, eliminering av boilerplate i koden gjennom abstraksjon, verktøy som er kjent for teamet ditt, for eksempel IDEer og linters. Alle disse programvareingeniørverktøyene gjør oss ikke bare mer produktive, men forbedrer også kvaliteten på koden vår. Derfor er det bare naturlig at bruken av generelle programmeringsspråk lar oss introdusere en annen viktig programvareutviklingspraksis - testing.

I denne artikkelen skal vi se på hvordan Pulumi hjelper oss med å teste vår infrastruktur-som-kode.

Tester infrastruktur som kode med Pulumi. Del 1

Hvorfor teste infrastruktur?

Før du går i detalj, er det verdt å stille spørsmålet: "Hvorfor teste infrastruktur i det hele tatt?" Det er mange grunner til dette, og her er noen av dem:

  • Enhetstesting av individuelle funksjoner eller fragmenter av programlogikken din
  • Verifiserer ønsket tilstand til infrastrukturen mot visse begrensninger.
  • Deteksjon av vanlige feil, for eksempel mangel på kryptering av en lagringsbøtte eller ubeskyttet, åpner tilgang fra Internett til virtuelle maskiner.
  • Kontrollere implementeringen av infrastrukturforsyning.
  • Utføre kjøretidstesting av applikasjonslogikk som kjører i din "programmerte" infrastruktur for å sjekke funksjonalitet etter klargjøring.
  • Som vi kan se, er det et bredt spekter av alternativer for testing av infrastruktur. Polumi har mekanismer for testing på hvert punkt på dette spekteret. La oss komme i gang og se hvordan det fungerer.

Enhetstesting

Pulumi-programmer er skrevet i generelle programmeringsspråk som JavaScript, Python, TypeScript eller Go. Derfor er den fulle kraften til disse språkene, inkludert deres verktøy og biblioteker, inkludert testrammeverk, tilgjengelig for dem. Pulumi er multi-sky, noe som betyr at den kan brukes til testing fra hvilken som helst skyleverandør.

(I denne artikkelen, til tross for at vi er flerspråklige og multicloud, bruker vi JavaScript og Mocha og fokuserer på AWS. Du kan bruke Python unittest, Go testrammeverk, eller et hvilket som helst annet testrammeverk du liker. Og selvfølgelig fungerer Pulumi utmerket med Azure, Google Cloud, Kubernetes.)

Som vi har sett, er det flere grunner til at du kanskje vil teste infrastrukturkoden din. En av dem er konvensjonell enhetstesting. Fordi koden din kan ha funksjoner - for eksempel å beregne CIDR, dynamisk kalkulere navn, tagger osv. - Du vil sannsynligvis prøve dem. Dette er det samme som å skrive vanlige enhetstester for applikasjoner på ditt favorittprogrammeringsspråk.
For å bli litt mer komplisert kan du sjekke hvordan programmet ditt tildeler ressurser. For å illustrere, la oss forestille oss at vi må lage en enkel EC2-server, og vi vil være sikre på følgende:

  • Forekomster har en tag Name.
  • Forekomster skal ikke bruke innebygde skript userData - vi må bruke en AMI (bilde).
  • Det skal ikke være noen SSH eksponert for Internett.

Dette eksemplet er basert på mitt 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 grunnleggende Pulumi-programmet: det tildeler ganske enkelt en EC2-sikkerhetsgruppe og en instans. Det skal imidlertid bemerkes at her bryter vi alle tre reglene nevnt ovenfor. La oss skrive tester!

Skrive prøver

Den generelle strukturen til testene våre vil se ut som vanlige Mokka-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, открытого в Интернет.
    });
});

La oss nå skrive vår første test: sørg for at forekomstene har taggen Name. For å sjekke dette får vi ganske enkelt EC2-forekomstobjektet og sjekker den tilsvarende egenskapen 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 ut som en vanlig test, men med noen få funksjoner som er verdt å merke seg:

  • Fordi vi spør om tilstanden til en ressurs før distribusjon, kjøres testene våre alltid i "plan" (eller "forhåndsvisning") modus. Dermed er det mange eiendommer hvis verdier rett og slett ikke vil bli hentet eller ikke vil bli definert. Dette inkluderer alle utdataegenskaper beregnet av nettskyleverandøren din. Dette er normalt for våre tester - vi sjekker kun inndataene. Vi kommer tilbake til denne problemstillingen senere, når det gjelder integrasjonstester.
  • Siden alle Pulumi-ressursegenskapene er utdata, og mange av dem blir evaluert asynkront, må vi bruke bruksmetoden for å få tilgang til verdiene. Dette er veldig likt løfter og afunction then .
  • Siden vi bruker flere egenskaper for å vise ressurs-URN i feilmeldingen, må vi bruke funksjonen pulumi.allå kombinere dem.
  • Til slutt, siden disse verdiene beregnes asynkront, må vi bruke Mochas innebygde funksjon for asynkron tilbakeringing done eller returnere et løfte.

Når vi har satt opp alt, vil vi ha tilgang til inngangene som enkle JavaScript-verdier. Eiendom tags er et kart (assosiativ array), så vi vil bare sørge for at det er (1) ikke usant, og (2) det er en nøkkel for Name. Det er veldig enkelt og nå kan vi teste hva som helst!

La oss nå skrive vår andre sjekk. Det er enda enklere:

 // 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 slutt, la oss skrive den tredje testen. Dette vil være litt mer komplisert fordi vi ser etter innloggingsreglene knyttet til sikkerhetsgruppen, som det kan være mange av, og CIDR-områdene i disse reglene, som det også kan være mange av. Men vi klarte:

    // 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. La oss nå kjøre testene!

Kjører tester

I de fleste tilfeller kan du kjøre tester på vanlig måte, ved å bruke testrammeverket du velger. Men det er en funksjon ved Pulumi som er verdt å være oppmerksom på.
Vanligvis, for å kjøre Pulumi-programmer, brukes pulimi CLI (Command Line-grensesnitt), som konfigurerer språkkjøringen, kontrollerer lanseringen av Pulumi-motoren slik at operasjoner med ressurser kan registreres og inkluderes i planen, etc. Det er imidlertid ett problem. Når du kjører under kontroll av testrammeverket ditt, vil det ikke være noen kommunikasjon mellom CLI og Pulumi-motoren.

For å komme rundt dette problemet trenger vi bare å spesifisere følgende:

  • Prosjektnavn, som er inneholdt i miljøvariabelen PULUMI_NODEJS_PROJECT (eller mer generelt, PULUMI__PROJECT для других языков).
    Navnet på stabelen som er spesifisert i miljøvariabelen PULUMI_NODEJS_STACK (eller mer generelt, PULUMI__ STACK).
    Dine stabelkonfigurasjonsvariabler. De kan oppnås ved å bruke en miljøvariabel PULUMI_CONFIG og formatet deres er JSON-kart med nøkkel/verdi-par.

    Programmet vil utstede advarsler som indikerer at tilkoblingen til CLI/motoren ikke er tilgjengelig under utførelse. Dette er viktig fordi programmet ditt faktisk ikke vil distribuere noe, og det kan komme som en overraskelse hvis det ikke var det du hadde tenkt å gjøre! For å fortelle Pulumi at dette er akkurat det du trenger, kan du installere PULUMI_TEST_MODE в true.

    Tenk deg at vi må spesifisere prosjektnavnet i my-ws, stabelnavn dev, og AWS-regionen us-west-2. Kommandolinjen for å kjøre Mokka-tester vil se slik ut:

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

    Å gjøre dette, som forventet, vil vise oss at vi har tre mislykkede tester!

    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

    La oss fikse programmet vårt:

    "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 kjør deretter testene på nytt:

    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 gikk bra... Hurra! ✓✓✓

    Det var alt for i dag, men vi skal snakke om distribusjonstesting i den andre delen av oversettelsen 😉

Kilde: www.habr.com

Legg til en kommentar