Infrastruktūros kaip kodo bandymas su Pulumi. 1 dalis

Laba diena draugai. Laukiant naujo srauto pradžios greičiu „DevOps praktika ir įrankiai“ Dalinamės su jumis nauju vertimu. Eik.

Infrastruktūros kaip kodo bandymas su Pulumi. 1 dalis

„Pulumi“ ir bendrosios paskirties programavimo kalbų naudojimas infrastruktūros kodui (Infrastruktūra kaip kodas) suteikia daug privalumų: įgūdžių ir žinių prieinamumas, kodo pašalinimas per abstrakciją, jūsų komandai žinomi įrankiai, tokie kaip IDE ir linteriai. Visi šie programinės įrangos inžinerijos įrankiai ne tik daro mus produktyvesnius, bet ir pagerina mūsų kodo kokybę. Todėl natūralu, kad bendrosios paskirties programavimo kalbų naudojimas leidžia mums pristatyti dar vieną svarbią programinės įrangos kūrimo praktiką - Testavimas.

Šiame straipsnyje apžvelgsime, kaip „Pulumi“ padeda mums išbandyti infrastruktūrą kaip kodą.

Infrastruktūros kaip kodo bandymas su Pulumi. 1 dalis

Kodėl reikia išbandyti infrastruktūrą?

Prieš gilinantis į smulkmenas, verta užduoti klausimą: „Kam išvis išbandyti infrastruktūrą? Tam yra daug priežasčių ir štai keletas iš jų:

  • Atskirų funkcijų arba jūsų programos logikos fragmentų vienetinis testavimas
  • Patikrina norimą infrastruktūros būklę, nepaisant tam tikrų apribojimų.
  • Įprastų klaidų aptikimas, pvz., saugojimo segmento šifravimo trūkumas arba neapsaugota atvira prieiga iš interneto prie virtualių mašinų.
  • Infrastruktūros aprūpinimo įgyvendinimo tikrinimas.
  • Programos logikos, veikiančios jūsų „užprogramuotoje“ infrastruktūroje, vykdymo testavimas, siekiant patikrinti funkcionalumą po aprūpinimo.
  • Kaip matome, infrastruktūros testavimo galimybių yra labai daug. „Polumi“ turi mechanizmus, skirtus išbandyti kiekviename šio spektro taške. Pradėkime ir pažiūrėkime, kaip tai veikia.

Vieneto bandymas

Pulumi programos yra parašytos bendrosios paskirties programavimo kalbomis, tokiomis kaip JavaScript, Python, TypeScript arba Go. Todėl jie gali naudotis visa šių kalbų galia, įskaitant jų įrankius ir bibliotekas, įskaitant bandymų sistemas. „Pulumi“ yra daugiafunkcis debesis, o tai reiškia, kad jį galima naudoti bet kurio debesų tiekėjo bandymams.

(Šiame straipsnyje, nepaisant to, kad jis yra daugiakalbis ir daugiasluoksnis debesis, naudojame JavaScript ir Mocha ir daugiausia dėmesio skiriame AWS. Galite naudoti Python unittest, Go test framework arba bet kuri kita jums patinkanti testavimo sistema. Ir, žinoma, „Pulumi“ puikiai veikia su „Azure“, „Google Cloud“, „Kubernetes“.)

Kaip matėme, yra keletas priežasčių, kodėl galbūt norėsite išbandyti savo infrastruktūros kodą. Vienas iš jų – įprastiniai vienetiniai bandymai. Kadangi jūsų kodas gali turėti funkcijų, pavyzdžiui, apskaičiuoti CIDR, dinamiškai apskaičiuoti vardus, žymas ir pan. - tikriausiai norėsite juos išbandyti. Tai tas pats, kas rašyti įprastus vienetų testus programoms jūsų mėgstama programavimo kalba.
Kad būtų šiek tiek sudėtingiau, galite patikrinti, kaip programa paskirsto išteklius. Norėdami iliustruoti, įsivaizduokime, kad turime sukurti paprastą EC2 serverį ir norime būti tikri dėl šių dalykų:

  • Atvejai turi žymą Name.
  • Pavyzdžiai neturėtų naudoti įterpto scenarijaus userData - turime naudoti AMI (vaizdą).
  • Internete neturėtų būti SSH.

Šis pavyzdys pagrįstas mano pavyzdys 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;

Tai pagrindinė Pulumi programa: ji tiesiog paskiria EC2 saugos grupę ir egzempliorių. Tačiau reikia pažymėti, kad čia mes pažeidžiame visas tris aukščiau nurodytas taisykles. Rašykime testus!

Rašymo testai

Bendra mūsų testų struktūra atrodys kaip įprasti Mocha testai:

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

Dabar parašykime pirmąjį testą: įsitikinkite, kad egzemplioriai turi žymą Name. Norėdami tai patikrinti, tiesiog gauname EC2 egzemplioriaus objektą ir patikriname atitinkamą ypatybę 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();
                }
            });
        });

Tai atrodo kaip įprastas testas, tačiau verta atkreipti dėmesį į keletą funkcijų:

  • Kadangi prieš diegdami užklausiame išteklių būseną, mūsų testai visada vykdomi „planavimo“ (arba „peržiūros“) režimu. Taigi yra daug savybių, kurių reikšmės tiesiog nebus nuskaitytos arba neapibrėžtos. Tai apima visas debesies paslaugų teikėjo apskaičiuotas išvesties savybes. Tai normalu mūsų testams – tikriname tik įvesties duomenis. Prie šios problemos grįšime vėliau, kai kalbame apie integracijos testus.
  • Kadangi visos Pulumi išteklių savybės yra išvestis, o daugelis jų vertinamos asinchroniškai, turime naudoti taikymo metodą, kad pasiektume reikšmes. Tai labai panašu į pažadus ir funkciją then .
  • Kadangi mes naudojame kelias ypatybes, kad parodytume išteklių URN klaidos pranešime, turime naudoti funkciją pulumi.alljuos sujungti.
  • Galiausiai, kadangi šios vertės apskaičiuojamos asinchroniškai, turime naudoti „Mocha“ integruotą asinchroninio atgalinio skambinimo funkciją done arba grąžinti pažadą.

Kai viską nustatysime, turėsime prieigą prie įvesties kaip paprastos „JavaScript“ reikšmės. Nuosavybė tags yra žemėlapis (asociacinis masyvas), todėl mes tiesiog įsitikinsime, kad jis (1) nėra klaidingas ir (2) yra raktas Name. Tai labai paprasta ir dabar galime išbandyti bet ką!

Dabar parašykime antrąjį čekį. Tai dar paprasčiau:

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

Ir pagaliau parašykime trečią testą. Tai bus šiek tiek sudėtingiau, nes mes ieškome prisijungimo taisyklių, susijusių su saugos grupe, kurių gali būti daug, ir CIDR diapazonų šiose taisyklėse, kurių taip pat gali būti daug. Bet mums pavyko:

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

Tai viskas. Dabar atlikime testus!

Vykdomi bandymai

Daugeliu atvejų galite vykdyti testus įprastu būdu, naudodami pasirinktą testavimo sistemą. Tačiau yra viena Pulumi savybė, į kurią verta atkreipti dėmesį.
Paprastai Pulumi programoms paleisti naudojama pulimi CLI (Command Line interface), kuri sukonfigūruoja kalbos vykdymo laiką, kontroliuoja Pulumi variklio paleidimą, kad operacijos su ištekliais būtų įrašomos ir įtraukiamos į planą ir kt. Tačiau yra viena problema. Kai veikia jūsų bandomoji sistema, nebus ryšio tarp CLI ir „Pulumi“ variklio.

Norėdami išspręsti šią problemą, tereikia nurodyti šiuos dalykus:

  • Projekto pavadinimas, kuris yra aplinkos kintamajame PULUMI_NODEJS_PROJECT (arba apskritai PULUMI__PROJECT для других языков).
    Krūvos pavadinimas, nurodytas aplinkos kintamajame PULUMI_NODEJS_STACK (arba apskritai PULUMI__ STACK).
    Jūsų dėklo konfigūracijos kintamieji. Juos galima gauti naudojant aplinkos kintamąjį PULUMI_CONFIG ir jų formatas yra JSON žemėlapis su raktų/reikšmių poromis.

    Programa išduos įspėjimus, nurodančius, kad vykdant negalima prisijungti prie CLI / variklio. Tai svarbu, nes jūsų programa iš tikrųjų nieko neįdiegs ir gali būti netikėta, jei tai neketinote padaryti! Norėdami pasakyti „Pulumi“, kad tai yra būtent tai, ko jums reikia, galite įdiegti PULUMI_TEST_MODE в true.

    Įsivaizduokite, kad turime nurodyti projekto pavadinimą my-ws, krūvos pavadinimas devir AWS regionas us-west-2. Mocha testų vykdymo komandų eilutė atrodys taip:

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

    Tai darydami, kaip tikėtasi, parodysime, kad turime tris nesėkmingus testus!

    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

    Pataisykime savo 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;
    

    Ir tada dar kartą paleiskite testus:

    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)

    Viskas pavyko... Ura! ✓✓✓

    Šiandien tai viskas, bet apie diegimo testavimą kalbėsime antroje vertimo dalyje 😉

Šaltinis: www.habr.com

Добавить комментарий