Testimi i Infrastrukturës si Kod me Pulumi. Pjesa 1

Mirëdita miq. Në pritje të fillimit të një fluksi të ri në normë "Praktikat dhe mjetet e DevOps" Po ndajmë me ju një përkthim të ri. Shkoni.

Testimi i Infrastrukturës si Kod me Pulumi. Pjesa 1

Përdorimi i Pulumi dhe gjuhëve të programimit për qëllime të përgjithshme për kodin e infrastrukturës (Infrastruktura si kod) ofron shumë përparësi: disponueshmërinë e aftësive dhe njohurive, eliminimin e bojlerplate në kod përmes abstraksionit, mjete të njohura për ekipin tuaj, të tilla si IDE dhe linter. Të gjitha këto mjete inxhinierike softuerike jo vetëm që na bëjnë më produktivë, por edhe përmirësojnë cilësinë e kodit tonë. Prandaj, është e natyrshme që përdorimi i gjuhëve programuese për qëllime të përgjithshme na lejon të prezantojmë një praktikë tjetër të rëndësishme të zhvillimit të softuerit - duke testuar.

Në këtë artikull, ne do të shohim se si Pulumi na ndihmon të testojmë infrastrukturën tonë si kod.

Testimi i Infrastrukturës si Kod me Pulumi. Pjesa 1

Pse të testoni infrastrukturën?

Para se të hyni në detaje, ia vlen të bëni pyetjen: "Pse të testohet fare infrastrukturë?" Ka shumë arsye për këtë dhe këtu janë disa prej tyre:

  • Testimi i njësive i funksioneve individuale ose fragmenteve të logjikës së programit tuaj
  • Verifikon gjendjen e dëshiruar të infrastrukturës kundrejt kufizimeve të caktuara.
  • Zbulimi i gabimeve të zakonshme, të tilla si mungesa e kriptimit të një kovë ruajtjeje ose aksesi i hapur dhe i pambrojtur nga interneti në makinat virtuale.
  • Kontrollimi i zbatimit të ofrimit të infrastrukturës.
  • Kryerja e testimit të kohës së ekzekutimit të logjikës së aplikacionit që funksionon brenda infrastrukturës tuaj të "programuar" për të kontrolluar funksionalitetin pas sigurimit.
  • Siç mund ta shohim, ekziston një gamë e gjerë opsionesh të testimit të infrastrukturës. Polumi ka mekanizma për testim në çdo pikë të këtij spektri. Le të fillojmë dhe të shohim se si funksionon.

Testimi i njësisë

Programet Pulumi janë shkruar në gjuhë programimi për qëllime të përgjithshme si JavaScript, Python, TypeScript ose Go. Prandaj, fuqia e plotë e këtyre gjuhëve, duke përfshirë mjetet dhe bibliotekat e tyre, duke përfshirë kornizat e testimit, është në dispozicion të tyre. Pulumi është me shumë re, që do të thotë se mund të përdoret për testim nga çdo ofrues i reve kompjuterike.

(Në këtë artikull, pavarësisht se jemi shumëgjuhësh dhe me shumë re, ne përdorim JavaScript dhe Mocha dhe fokusohemi në AWS. Ju mund të përdorni Python unittest, Shkoni në kuadrin e testimit ose ndonjë kornizë tjetër testimi që ju pëlqen. Dhe, sigurisht, Pulumi funksionon shkëlqyeshëm me Azure, Google Cloud, Kubernetes.)

Siç e kemi parë, ka disa arsye pse mund të dëshironi të testoni kodin tuaj të infrastrukturës. Një prej tyre është testimi i njësive konvencionale. Sepse kodi juaj mund të ketë funksione - për shembull, për të llogaritur CIDR, për të llogaritur në mënyrë dinamike emrat, etiketat, etj. - ndoshta do të dëshironi t'i provoni ato. Kjo është e njëjtë me shkrimin e testeve të rregullta të njësive për aplikacione në gjuhën tuaj të preferuar të programimit.
Për t'u bërë pak më i ndërlikuar, mund të kontrolloni se si programi juaj i shpërndan burimet. Për ta ilustruar, le të imagjinojmë se duhet të krijojmë një server të thjeshtë EC2 dhe duam të jemi të sigurt për sa vijon:

  • Rastet kanë një etiketë Name.
  • Instancat nuk duhet të përdorin skript inline userData - duhet të përdorim një AMI (imazh).
  • Nuk duhet të ketë asnjë SSH të ekspozuar në internet.

Ky shembull bazohet në shembulli im 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;

Ky është programi bazë Pulumi: ai thjesht cakton një grup sigurie EC2 dhe një shembull. Megjithatë, duhet theksuar se këtu ne po i thyejmë të tre rregullat e lartpërmendura. Le të shkruajmë teste!

Teste me shkrim

Struktura e përgjithshme e testeve tona do të duket si testet e rregullta Mocha:

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

Tani le të shkruajmë testin tonë të parë: sigurohuni që instancat të kenë etiketën Name. Për ta kontrolluar këtë ne thjesht marrim objektin e shembullit EC2 dhe kontrollojmë vetinë përkatëse 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();
                }
            });
        });

Duket si një test i rregullt, por me disa veçori që ia vlen të përmenden:

  • Për shkak se ne kërkojmë gjendjen e një burimi përpara vendosjes, testet tona ekzekutohen gjithmonë në modalitetin "plan" (ose "parapamje"). Kështu, ka shumë prona, vlerat e të cilave thjesht nuk do të merren ose nuk do të përcaktohen. Kjo përfshin të gjitha vetitë e daljes të llogaritura nga ofruesi juaj i resë kompjuterike. Kjo është normale për testet tona - ne kontrollojmë vetëm të dhënat hyrëse. Ne do t'i kthehemi kësaj çështje më vonë, kur bëhet fjalë për testet e integrimit.
  • Meqenëse të gjitha vetitë e burimeve Pulumi janë rezultate dhe shumë prej tyre vlerësohen në mënyrë asinkrone, ne duhet të përdorim metodën e aplikimit për të aksesuar vlerat. Kjo është shumë e ngjashme me premtimet dhe funksionet then .
  • Meqenëse po përdorim disa veti për të treguar URN-në e burimit në mesazhin e gabimit, duhet të përdorim funksionin pulumi.allpër t'i kombinuar ato.
  • Së fundi, meqenëse këto vlera llogariten në mënyrë asinkrone, ne duhet të përdorim veçorinë e integruar të kthimit të thirrjes asinkronike të Mocha done ose kthimi i një premtimi.

Pasi të kemi vendosur gjithçka, do të kemi akses në hyrjet si vlera të thjeshta JavaScript. Prona tags është një hartë (grup shoqërues), kështu që ne thjesht do të sigurohemi që (1) të mos jetë false, dhe (2) ka një çelës për Name. Është shumë e thjeshtë dhe tani mund të testojmë gjithçka!

Tani le të shkruajmë kontrollin tonë të dytë. Është edhe më e thjeshtë:

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

Dhe së fundi, le të shkruajmë testin e tretë. Kjo do të jetë pak më e komplikuar sepse ne po kërkojmë rregullat e hyrjes që lidhen me grupin e sigurisë, nga të cilat mund të ketë shumë, dhe CIDR varion në ato rregulla, nga të cilat mund të ketë edhe shumë. Por ne ia dolëm:

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

Kjo eshte e gjitha. Tani le të bëjmë testet!

Vrapimi i testeve

Në shumicën e rasteve, ju mund të kryeni teste në mënyrën e zakonshme, duke përdorur kornizën e testimit të zgjedhjes suaj. Por ka një veçori të Pulumit që ia vlen t'i kushtohet vëmendje.
Në mënyrë tipike, për të ekzekutuar programet Pulumi, përdoret pulimi CLI (ndërfaqja e linjës së komandës), e cila konfiguron kohën e ekzekutimit të gjuhës, kontrollon nisjen e motorit Pulumi në mënyrë që operacionet me burime të mund të regjistrohen dhe përfshihen në plan, etj. Megjithatë, ka një problem. Kur punoni nën kontrollin e kornizës suaj të testimit, nuk do të ketë asnjë komunikim midis CLI dhe motorit Pulumi.

Për të kapërcyer këtë problem, ne vetëm duhet të specifikojmë sa vijon:

  • Emri i projektit, i cili gjendet në variablin e mjedisit PULUMI_NODEJS_PROJECT (ose, në përgjithësi, PULUMI__PROJECT для других языков).
    Emri i pirgut që është specifikuar në variablin e mjedisit PULUMI_NODEJS_STACK (ose, në përgjithësi, PULUMI__ STACK).
    Variablat tuaja të konfigurimit të pirgut. Ato mund të merren duke përdorur një variabël mjedisor PULUMI_CONFIG dhe formati i tyre është harta JSON me çifte çelës/vlerë.

    Programi do të lëshojë paralajmërime që tregojnë se lidhja me CLI/motorin nuk është e disponueshme gjatë ekzekutimit. Kjo është e rëndësishme sepse programi juaj në të vërtetë nuk do të vendosë asgjë dhe mund të jetë befasi nëse nuk është ajo çfarë keni menduar të bëni! Për t'i thënë Pulumit se kjo është pikërisht ajo që ju nevojitet, mund ta instaloni PULUMI_TEST_MODE в true.

    Imagjinoni se duhet të specifikojmë emrin e projektit my-ws, emri i stivës dev, dhe Rajoni AWS us-west-2. Linja e komandës për ekzekutimin e testeve Mocha do të duket si kjo:

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

    Bërja e kësaj, siç pritej, do të na tregojë se kemi tre teste të dështuara!

    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

    Le të rregullojmë programin tonë:

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

    Dhe pastaj kryeni përsëri testet:

    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)

    Gjithçka shkoi mirë... Urra! ✓✓✓

    Kjo është e gjitha për sot, por ne do të flasim për testimin e vendosjes në pjesën e dytë të përkthimit 😉

Burimi: www.habr.com

Shto një koment