Testado de Infrastrukturo kiel Kodo kun Pulumi. Parto 1

Bonan posttagmezon amikoj. En antaŭĝojo de la komenco de nova fluo kun la rapideco "DevOps-praktikoj kaj iloj" Ni dividas kun vi novan tradukon. Iru.

Testado de Infrastrukturo kiel Kodo kun Pulumi. Parto 1

Uzado de Pulumi kaj ĝeneraluzeblaj programlingvoj por infrastruktura kodo (Infrastrukturo kiel Kodo) provizas multajn avantaĝojn: la havebleco de kapabloj kaj scio, elimino de kodo en la kodo per abstraktado, iloj konataj al via teamo, kiel IDEoj kaj linters. Ĉiuj ĉi tiuj programaj inĝenieraj iloj ne nur igas nin pli produktivaj, sed ankaŭ plibonigas la kvaliton de nia kodo. Tial, estas nur nature, ke la uzo de ĝeneraluzeblaj programlingvoj ebligas al ni enkonduki alian gravan praktikan disvolviĝon de programaro - testado.

En ĉi tiu artikolo, ni rigardos kiel Pulumi helpas nin testi nian infrastrukturon-kiel-kodon.

Testado de Infrastrukturo kiel Kodo kun Pulumi. Parto 1

Kial testi infrastrukturon?

Antaŭ ol eniri en detalojn, indas demandi la demandon: "Kial testi infrastrukturon entute?" Estas multaj kialoj por ĉi tio kaj jen kelkaj el ili:

  • Unutestado de individuaj funkcioj aŭ fragmentoj de via programa logiko
  • Kontrolas la deziratan staton de la infrastrukturo kontraŭ certaj limoj.
  • Detekto de oftaj eraroj, kiel manko de ĉifrado de stoka sitelo aŭ senprotekta, malferma aliro de la Interreto al virtualaj maŝinoj.
  • Kontrolante la efektivigon de infrastruktura provizo.
  • Farante rultempan testadon de aplika logiko funkcianta en via "programita" infrastrukturo por kontroli funkciojn post provizo.
  • Kiel ni povas vidi, ekzistas vasta gamo de infrastrukturaj testaj opcioj. Polumi havas mekanismojn por testado ĉe ĉiu punkto sur ĉi tiu spektro. Ni komencu kaj vidu kiel ĝi funkcias.

Unuo-testado

Pulumi-programoj estas skribitaj en ĝeneraluzeblaj programlingvoj kiel JavaScript, Python, TypeScript aŭ Go. Tial, la plena potenco de ĉi tiuj lingvoj, inkluzive de iliaj iloj kaj bibliotekoj, inkluzive de testaj kadroj, estas disponebla por ili. Pulumi estas plurnuba, kio signifas, ke ĝi povas esti uzata por testado de iu ajn nuba provizanto.

(En ĉi tiu artikolo, malgraŭ esti plurlingva kaj plurnuba, ni uzas JavaScript kaj Mocha kaj koncentriĝas pri AWS. Vi povas uzi Python unittest, Iru testa kadro, aŭ ajna alia testa kadro vi ŝatas. Kaj, kompreneble, Pulumi funkcias bonege kun Azure, Google Cloud, Kubernetes.)

Kiel ni vidis, estas pluraj kialoj, kial vi eble volas testi vian infrastrukturan kodon. Unu el ili estas konvencia unuotestado. Ĉar via kodo povas havi funkciojn - ekzemple, por kalkuli CIDR, dinamike kalkuli nomojn, etikedojn, ktp. - vi verŝajne volos testi ilin. Ĉi tio samas kiel verki regulajn unutestojn por aplikoj en via plej ŝatata programlingvo.
Por iom pli kompliki, vi povas kontroli kiel via programo asignas rimedojn. Por ilustri, ni imagu, ke ni devas krei simplan EC2-servilon kaj ni volas certigi la jenon:

  • Kazoj havas etikedon Name.
  • Okazaĵoj ne devus uzi enlinian skripton userData - ni devas uzi AMI (bildon).
  • Ne devus esti SSH elmontrita al la Interreto.

Ĉi tiu ekzemplo baziĝas sur mia ekzemplo 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;

Ĉi tio estas la baza programo Pulumi: ĝi simple asignas sekurecgrupon kaj petskribon de EC2. Tamen, oni devas rimarki, ke ĉi tie ni malobeas ĉiujn tri regulojn menciitajn supre. Ni skribu testojn!

Skribado de provoj

La ĝenerala strukturo de niaj testoj aspektos kiel regulaj Mocha testoj:

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

Nun ni skribu nian unuan teston: certigu, ke la instancoj havas la etikedon Name. Por kontroli tion ni simple ricevas la EC2-instancan objekton kaj kontrolas la respondan posedaĵon 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();
                }
            });
        });

Ĝi aspektas kiel regula testo, sed kun kelkaj trajtoj rimarkindaj:

  • Ĉar ni pridemandas la staton de rimedo antaŭ deplojo, niaj testoj ĉiam funkcias en "plana" (aŭ "antaŭrigardo") reĝimo. Tiel, ekzistas multaj propraĵoj, kies valoroj simple ne estos prenitaj aŭ ne estos difinitaj. Ĉi tio inkluzivas ĉiujn eligajn ecojn kalkulitajn de via nuba provizanto. Ĉi tio estas normala por niaj testoj - ni nur kontrolas la enigajn datumojn. Ni revenos al ĉi tiu afero poste, kiam temas pri integrigaj testoj.
  • Ĉar ĉiuj Pulumi-resursaj propraĵoj estas eliroj, kaj multaj el ili estas taksitaj nesinkrone, ni devas uzi la aplikan metodon por aliri la valorojn. Ĉi tio tre similas al promesoj kaj funkcio then .
  • Ĉar ni uzas plurajn ecojn por montri la rimedon URN en la erarmesaĝo, ni devas uzi la funkcion pulumi.allkombini ilin.
  • Fine, ĉar ĉi tiuj valoroj estas kalkulitaj nesinkrone, ni devas uzi la enkonstruitan nesinkronan revokan funkcion de Mocha. done aŭ resendi promeson.

Post kiam ni agordis ĉion, ni havos aliron al la enigaĵoj kiel simplaj JavaScript-valoroj. Proprieto tags estas mapo (asocieca tabelo), do ni nur certigos, ke ĝi estas (1) ne falsa, kaj (2) estas ŝlosilo por Name. Ĝi estas tre simpla kaj nun ni povas provi ion ajn!

Nun ni skribu nian duan kontrolon. Ĝi estas eĉ pli simpla:

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

Kaj fine, ni skribu la trian teston. Ĉi tio estos iom pli komplika ĉar ni serĉas la ensalutajn regulojn asociitajn kun la sekureca grupo, el kiuj povas esti multaj, kaj la CIDR-intervaloj en tiuj reguloj, el kiuj ankaŭ povas esti multaj. Sed ni sukcesis:

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

Tio estas ĉio. Nun ni faru la testojn!

Kurante provoj

Plejofte, vi povas ruli testojn laŭ la kutima maniero, uzante la testan kadron de via elekto. Sed estas unu trajto de Pulumi, pri kiu indas atenti.
Tipe, por ruli Pulumi-programojn, la pulimi CLI (Komandlinia interfaco) estas uzata, kiu agordas la lingvan rultempon, kontrolas la lanĉon de la Pulumi-motoro tiel ke operacioj kun resursoj povas esti registritaj kaj inkluditaj en la plano, ktp. Tamen, estas unu problemo. Kiam vi funkcias sub la kontrolo de via testa kadro, ne estos komunikado inter la CLI kaj la Pulumi-motoro.

Por ĉirkaŭiri ĉi tiun problemon, ni nur bezonas specifi la jenon:

  • Projektnomo, kiu estas enhavita en la mediovariablo PULUMI_NODEJS_PROJECT (aŭ, pli ĝenerale, PULUMI__PROJECT для других языков).
    La nomo de la stako kiu estas specifita en la mediovariablo PULUMI_NODEJS_STACK (aŭ, pli ĝenerale, PULUMI__ STACK).
    Viaj stakaj agordaj variabloj. Ili povas esti akiritaj uzante mediovariablon PULUMI_CONFIG kaj ilia formato estas JSON-mapo kun ŝlosilaj/valoraj paroj.

    La programo eligos avertojn indikante, ke la konekto al la CLI/motoro ne disponeblas dum ekzekuto. Ĉi tio gravas ĉar via programo efektive disfaldigos ion ajn kaj eble surprizos se vi ne intencis fari tion! Por diri al Pulumi, ke ĉi tio estas ĝuste tio, kion vi bezonas, vi povas instali PULUMI_TEST_MODE в true.

    Imagu, ke ni devas specifi la nomon de la projekto my-ws, staknomo dev, kaj AWS Regiono us-west-2. La komandlinio por ruli Mocha-testojn aspektos jene:

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

    Farante tion, kiel atendite, montros al ni, ke ni havas tri malsukcesajn provojn!

    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

    Ni riparu nian programon:

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

    Kaj poste rulu la testojn denove:

    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)

    Ĉio iris bone... Hura! ✓✓✓

    Tio estas ĉio por hodiaŭ, sed ni parolos pri deplojtestado en la dua parto de la traduko 😉

fonto: www.habr.com

Aldoni komenton