Ittestjar Infrastruttura bħala Kodiċi ma Pulumi. Parti 1

Wara nofsinhar it-tajjeb ħbieb. B'antiċipazzjoni tal-bidu ta 'fluss ġdid bir-rata "Prattiċi u għodod DevOps" Qed naqsmu magħkom traduzzjoni ġdida. Mur.

Ittestjar Infrastruttura bħala Kodiċi ma Pulumi. Parti 1

L-użu ta 'Pulumi u lingwi ta' programmar għal skopijiet ġenerali għall-kodiċi tal-infrastruttura (Infrastructure as Code) jipprovdi ħafna vantaġġi: id-disponibbiltà ta 'ħiliet u għarfien, eliminazzjoni ta' boilerplate fil-kodiċi permezz ta 'estrazzjoni, għodod familjari mat-tim tiegħek, bħal IDEs u linters. Dawn l-għodod kollha tal-inġinerija tas-softwer mhux biss jagħmluna aktar produttivi, iżda wkoll itejbu l-kwalità tal-kodiċi tagħna. Għalhekk, huwa naturali li l-użu ta 'lingwi ta' programmar għal skopijiet ġenerali jippermettilna nintroduċu prattika oħra importanti ta 'żvilupp ta' softwer - ittestjar.

F'dan l-artikolu, se nħarsu lejn kif Pulumi jgħinna nittestjaw l-infrastruttura-as-code tagħna.

Ittestjar Infrastruttura bħala Kodiċi ma Pulumi. Parti 1

Għaliex tittestja l-infrastruttura?

Qabel ma nidħlu fid-dettall, ta 'min tistaqsi l-mistoqsija: "Għaliex tittestja l-infrastruttura?" Hemm ħafna raġunijiet għal dan u hawn xi wħud minnhom:

  • Ittestjar ta' unità ta' funzjonijiet individwali jew frammenti tal-loġika tal-programm tiegħek
  • Jivverifika l-istat mixtieq tal-infrastruttura kontra ċerti restrizzjonijiet.
  • Sejbien ta 'żbalji komuni, bħal nuqqas ta' encryption ta 'barmil tal-ħażna jew aċċess miftuħ mhux protett mill-Internet għal magni virtwali.
  • Iċċekkjar tal-implimentazzjoni tal-provvista tal-infrastruttura.
  • Twettiq ta' ttestjar tar-runtime tal-loġika tal-applikazzjoni li taħdem ġewwa l-infrastruttura "programmata" tiegħek biex tiċċekkja l-funzjonalità wara l-forniment.
  • Kif nistgħu naraw, hemm firxa wiesgħa ta 'għażliet ta' ttestjar tal-infrastruttura. Polumi għandu mekkaniżmi għall-ittestjar f'kull punt fuq dan l-ispettru. Ejja nibdew u naraw kif taħdem.

Ittestjar tal-unità

Il-programmi Pulumi huma miktuba f'lingwi ta' programmar għal skopijiet ġenerali bħal JavaScript, Python, TypeScript jew Go. Għalhekk, is-setgħa sħiħa ta 'dawn il-lingwi, inklużi l-għodod u l-libreriji tagħhom, inklużi l-oqfsa tat-test, hija disponibbli għalihom. Pulumi huwa multi-cloud, li jfisser li jista 'jintuża għall-ittestjar minn kwalunkwe fornitur tas-sħab.

(F'dan l-artikolu, minkejja li huma multilingwi u multicloud, nużaw JavaScript u Mocha u niffukaw fuq AWS. Tista' tuża Python unittest, Mur qafas tat-test, jew kwalunkwe qafas tat-test ieħor li tixtieq. U, ovvjament, Pulumi jaħdem tajjeb ma' Azure, Google Cloud, Kubernetes.)

Kif rajna, hemm diversi raġunijiet għalfejn tista' tkun trid tittestja l-kodiċi tal-infrastruttura tiegħek. Waħda minnhom hija l-ittestjar tal-unità konvenzjonali. Minħabba li l-kodiċi tiegħek jista 'jkollu funzjonijiet - pereżempju, biex tikkalkula CIDR, ikkalkula dinamikament ismijiet, tikketti, eċċ. - inti probabilment trid tittestjahom. Dan huwa l-istess bħall-kitba ta 'testijiet ta' unità regolari għall-applikazzjonijiet fil-lingwa ta 'programmar favorita tiegħek.
Biex tkun ftit aktar ikkumplikat, tista 'tiċċekkja kif il-programm tiegħek jalloka r-riżorsi. Biex nagħtu eżempju, ejja nimmaġinaw li għandna bżonn noħolqu server EC2 sempliċi u rridu nkunu ċerti minn dan li ġej:

  • L-istanzi għandhom tikketta Name.
  • L-istanzi m'għandhomx jużaw skript inline userData - irridu nużaw AMI (immaġini).
  • M'għandu jkun hemm l-ebda SSH espost għall-Internet.

Dan l-eżempju huwa bbażat fuq eżempju tiegħi 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;

Dan huwa l-programm Pulumi bażiku: sempliċement jalloka grupp tas-sigurtà EC2 u istanza. Madankollu, ta’ min jinnota li hawnhekk qed niksru t-tliet regoli ddikjarati hawn fuq. Ejja niktbu t-testijiet!

Testijiet tal-kitba

L-istruttura ġenerali tat-testijiet tagħna tidher bħal testijiet Mocha regolari:

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

Issa ejja niktbu l-ewwel test tagħna: kun żgur li l-istanzi għandhom it-tikketta Name. Biex niċċekkjaw dan aħna sempliċiment nikseb l-oġġett tal-istanza EC2 u niċċekkja l-proprjetà korrispondenti 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();
                }
            });
        });

Jidher qisu test regolari, iżda bi ftit karatteristiċi ta 'min jinnota:

  • Minħabba li nistaqsu l-istat ta’ riżorsa qabel l-iskjerament, it-testijiet tagħna dejjem isiru fil-modalità “pjan” (jew “previżjoni”). Għalhekk, hemm ħafna proprjetajiet li l-valuri tagħhom sempliċement mhux se jiġu rkuprati jew mhux se jiġu definiti. Dan jinkludi l-proprjetajiet kollha tal-output ikkalkulati mill-fornitur tal-cloud tiegħek. Dan huwa normali għat-testijiet tagħna - aħna niċċekkjaw biss id-dejta tal-input. Nirritornaw għal din il-kwistjoni aktar tard, fejn jidħlu testijiet ta 'integrazzjoni.
  • Peress li l-proprjetajiet tar-riżorsi Pulumi kollha huma outputs, u ħafna minnhom huma evalwati b'mod mhux sinkroniku, jeħtieġ li nużaw il-metodu ta 'applika biex jaċċessaw il-valuri. Dan huwa simili ħafna għall-wegħdiet u afunction then .
  • Peress li qed nużaw diversi proprjetajiet biex nuru r-riżorsa URN fil-messaġġ ta 'żball, għandna bżonn nużaw il-funzjoni pulumi.allbiex jgħaqqduhom.
  • Fl-aħħarnett, peress li dawn il-valuri huma kkalkulati b'mod asinkroniku, għandna bżonn nużaw il-karatteristika ta 'callback async inkorporata ta' Mocha done jew tirritorna wegħda.

Ladarba waqqafna kollox, ikollna aċċess għall-inputs bħala valuri JavaScript sempliċi. Proprjetà tags hija mappa (matriċi assoċjattiva), għalhekk aħna ser niżguraw li hija (1) mhux falza, u (2) hemm ċavetta għal Name. Huwa sempliċi ħafna u issa nistgħu nittestjaw xi ħaġa!

Issa ejja niktbu t-tieni kontroll tagħna. Huwa saħansitra aktar sempliċi:

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

U fl-aħħarnett, ejja niktbu t-tielet test. Dan se jkun ftit aktar ikkumplikat minħabba li qed infittxu r-regoli tal-login assoċjati mal-grupp tas-sigurtà, li jista 'jkun hemm ħafna, u l-firxiet tas-CIDR f'dawk ir-regoli, li minnhom jista' jkun hemm ukoll ħafna. Imma rnexxielna:

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

Dak kollox. Issa ejja nagħmel it-testijiet!

Tmexxija tat-testijiet

Fil-biċċa l-kbira tal-każijiet, tista 'tmexxi testijiet bil-mod tas-soltu, billi tuża l-qafas tat-test tal-għażla tiegħek. Iżda hemm karatteristika waħda ta 'Pulumi li ta' min tagħti attenzjoni għaliha.
Tipikament, biex imexxu programmi Pulumi, tintuża l-pulimi CLI (interface tal-Linja tal-Kmand), li tikkonfigura r-runtime tal-lingwa, tikkontrolla t-tnedija tal-magna Pulumi sabiex l-operazzjonijiet bir-riżorsi jkunu jistgħu jiġu rreġistrati u inklużi fil-pjan, eċċ. Madankollu, hemm problema waħda. Meta taħdem taħt il-kontroll tal-qafas tat-test tiegħek, ma jkun hemm l-ebda komunikazzjoni bejn is-CLI u l-magna Pulumi.

Biex nersqu din il-kwistjoni, irridu biss nispeċifikaw dan li ġej:

  • Isem tal-proġett, li jinsab fil-varjabbli ambjentali PULUMI_NODEJS_PROJECT (jew, b'mod aktar ġenerali, PULUMI__PROJECT для других языков).
    L-isem tal-munzell li huwa speċifikat fil-varjabbli ambjentali PULUMI_NODEJS_STACK (jew, b'mod aktar ġenerali, PULUMI__ STACK).
    Il-varjabbli tal-konfigurazzjoni tal-munzell tiegħek. Jistgħu jinkisbu permezz ta' varjabbli ambjentali PULUMI_CONFIG u l-format tagħhom huwa mappa JSON b'pari ewlenin/valur.

    Il-programm se joħroġ twissijiet li jindikaw li l-konnessjoni mal-CLI/magna mhix disponibbli waqt l-eżekuzzjoni. Dan huwa importanti għaliex il-programm tiegħek fil-fatt mhux se jkun qed juża xejn u jista 'jkun sorpriża jekk dan mhux dak li inti ħsibt li tagħmel! Biex tgħid lil Pulumi li dan huwa eżattament dak li għandek bżonn, tista 'tinstalla PULUMI_TEST_MODE в true.

    Immaġina li għandna bżonn nispeċifikaw l-isem tal-proġett my-ws, isem munzell dev, u Reġjun AWS us-west-2. Il-linja tal-kmand għat-tmexxija tat-testijiet Mocha se tidher bħal din:

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

    Jekk tagħmel dan, kif mistenni, se turina li għandna tliet testijiet falluti!

    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

    Ejja nirranġaw il-programm tagħna:

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

    U mbagħad erġa agħmel it-testijiet:

    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)

    Kollox mar tajjeb... Hurrah! ✓✓✓

    Dak kollu għal-lum, imma nitkellmu dwar l-ittestjar tal-iskjerament fit-tieni parti tat-traduzzjoni 😉

Sors: www.habr.com

Żid kumment