Testovanie infraštruktúry ako kódu s Pulumi. Časť 1

Dobré popoludnie priatelia. V očakávaní začiatku nového toku rýchlosťou "Postupy a nástroje DevOps" Zdieľame s vami nový preklad. Choď.

Testovanie infraštruktúry ako kódu s Pulumi. Časť 1

Používanie Pulumi a všeobecných programovacích jazykov pre kód infraštruktúry (Infrastructure as Code) poskytuje mnoho výhod: dostupnosť zručností a znalostí, odstránenie štandardných kódov prostredníctvom abstrakcie, nástroje, ktoré váš tím pozná, ako sú IDE a linters. Všetky tieto nástroje softvérového inžinierstva nám nielen zvyšujú produktivitu, ale zlepšujú aj kvalitu nášho kódu. Preto je len prirodzené, že používanie všeobecných programovacích jazykov nám umožňuje zaviesť ďalšiu dôležitú prax vývoja softvéru - Testovanie.

V tomto článku sa pozrieme na to, ako nám Pulumi pomáha testovať našu infraštruktúru ako kód.

Testovanie infraštruktúry ako kódu s Pulumi. Časť 1

Prečo testovať infraštruktúru?

Predtým, ako pôjdeme do detailov, stojí za to položiť si otázku: „Prečo vôbec testovať infraštruktúru? Existuje na to veľa dôvodov a tu sú niektoré z nich:

  • Unit testovanie jednotlivých funkcií alebo fragmentov logiky vášho programu
  • Overuje požadovaný stav infraštruktúry voči určitým obmedzeniam.
  • Detekcia bežných chýb, ako je nedostatočné šifrovanie zásobníka alebo nechránený otvorený prístup z internetu k virtuálnym počítačom.
  • Kontrola implementácie poskytovania infraštruktúry.
  • Vykonávanie runtime testovania aplikačnej logiky spustenej vo vašej „naprogramovanej“ infraštruktúre na kontrolu funkčnosti po zriadení.
  • Ako vidíme, existuje široká škála možností testovania infraštruktúry. Polumi má mechanizmy na testovanie v každom bode tohto spektra. Začnime a uvidíme, ako to funguje.

Jednotkové testovanie

Programy Pulumi sú napísané vo všeobecných programovacích jazykoch, ako sú JavaScript, Python, TypeScript alebo Go. Preto je im k dispozícii plná sila týchto jazykov vrátane ich nástrojov a knižníc vrátane testovacích rámcov. Pulumi je multi-cloud, čo znamená, že ho možno použiť na testovanie od akéhokoľvek poskytovateľa cloudu.

(V tomto článku, napriek tomu, že je viacjazyčný a multicloudový, používame JavaScript a Mocha a zameriavame sa na AWS. Môžete použiť Python unittest, Go testovací rámec alebo akýkoľvek iný testovací rámec, ktorý sa vám páči. A samozrejme, Pulumi funguje skvele s Azure, Google Cloud, Kubernetes.)

Ako sme videli, existuje niekoľko dôvodov, prečo by ste mohli chcieť otestovať kód svojej infraštruktúry. Jedným z nich je konvenčné testovanie jednotiek. Pretože váš kód môže mať funkcie – napríklad na výpočet CIDR, dynamický výpočet mien, značiek atď. - Pravdepodobne ich budete chcieť otestovať. Je to rovnaké ako písanie bežných jednotkových testov pre aplikácie vo vašom obľúbenom programovacom jazyku.
Ak chcete byť trochu komplikovanejší, môžete skontrolovať, ako váš program prideľuje zdroje. Pre ilustráciu si predstavme, že potrebujeme vytvoriť jednoduchý EC2 server a chceme si byť istí nasledujúcim:

  • Inštancie majú značku Name.
  • Inštancie by nemali používať vložený skript userData - musíme použiť AMI (obrázok).
  • Žiadne SSH by nemalo byť vystavené internetu.

Tento príklad je založený na môj príklad 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;

Toto je základný program Pulumi: jednoducho prideľuje bezpečnostnú skupinu EC2 a inštanciu. Treba však poznamenať, že tu porušujeme všetky tri vyššie uvedené pravidlá. Poďme písať testy!

Písanie testov

Všeobecná štruktúra našich testov bude vyzerať ako bežné Mocha testy:

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

Teraz napíšme náš prvý test: uistite sa, že inštancie majú značku Name. Aby sme to skontrolovali, jednoducho získame objekt inštancie EC2 a skontrolujeme zodpovedajúcu vlastnosť 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();
                }
            });
        });

Vyzerá to ako bežný test, no s niekoľkými funkciami, ktoré stojí za zmienku:

  • Pretože sa pred nasadením pýtame na stav zdroja, naše testy sa vždy spúšťajú v režime „plán“ (alebo „ukážka“). Existuje teda veľa vlastností, ktorých hodnoty sa jednoducho nenačítajú alebo nebudú definované. To zahŕňa všetky výstupné vlastnosti vypočítané vaším poskytovateľom cloudu. Pri našich testoch je to normálne – kontrolujeme len vstupné údaje. K tejto problematike sa vrátime neskôr, keď príde reč na integračné testy.
  • Keďže všetky vlastnosti zdrojov Pulumi sú výstupy a mnohé z nich sú vyhodnocované asynchrónne, musíme na prístup k hodnotám použiť metódu Apply. Je to veľmi podobné sľubom a funkciám then .
  • Keďže na zobrazenie URN zdroja v chybovej správe používame niekoľko vlastností, musíme túto funkciu použiť pulumi.allich kombinovať.
  • Nakoniec, keďže tieto hodnoty sa počítajú asynchrónne, musíme použiť vstavanú funkciu spätného volania Mocha. done alebo vrátenie sľubu.

Keď všetko nastavíme, budeme mať prístup k vstupom ako jednoduchým hodnotám JavaScriptu. Nehnuteľnosť tags je mapa (asociatívne pole), takže sa uistíme, že (1) nie je nepravda a (2) existuje kľúč pre Name. Je to veľmi jednoduché a teraz môžeme testovať čokoľvek!

Teraz napíšeme našu druhú kontrolu. Je to ešte jednoduchšie:

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

A na záver si napíšeme tretí test. Bude to trochu komplikovanejšie, pretože hľadáme prihlasovacie pravidlá spojené s bezpečnostnou skupinou, ktorých môže byť veľa a CIDR sa v týchto pravidlách pohybuje, ktorých môže byť tiež veľa. Ale zvládli sme:

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

To je všetko. Teraz poďme na testy!

Beh testov

Vo väčšine prípadov môžete testy spustiť obvyklým spôsobom pomocou testovacieho rámca podľa vášho výberu. Ale je tu jedna vlastnosť Pulumi, ktorá stojí za pozornosť.
Na spustenie programov Pulumi sa zvyčajne používa pulimi CLI (rozhranie príkazového riadku), ktoré konfiguruje jazykový runtime, riadi spustenie motora Pulumi tak, aby bolo možné zaznamenávať operácie so zdrojmi a zahrnúť ich do plánu atď. Je tu však jeden problém. Pri spustení pod kontrolou vášho testovacieho rámca nebude existovať žiadna komunikácia medzi CLI a Pulumi engine.

Aby sme tento problém obišli, musíme špecifikovať nasledovné:

  • Názov projektu, ktorý je obsiahnutý v premennej prostredia PULUMI_NODEJS_PROJECT (alebo všeobecnejšie, PULUMI__PROJECT для других языков).
    Názov zásobníka, ktorý je zadaný v premennej prostredia PULUMI_NODEJS_STACK (alebo všeobecnejšie, PULUMI__ STACK).
    Premenné konfigurácie zásobníka. Môžu byť získané pomocou premennej prostredia PULUMI_CONFIG a ich formát je mapa JSON s pármi kľúč/hodnota.

    Program vydá varovania indikujúce, že pripojenie k CLI/engine nie je počas vykonávania dostupné. Je to dôležité, pretože váš program v skutočnosti nič nenasadzuje a môže byť prekvapením, ak to nie je to, čo ste zamýšľali urobiť! Ak chcete Pulumi povedať, že je to presne to, čo potrebujete, môžete nainštalovať PULUMI_TEST_MODE в true.

    Predstavte si, že musíme zadať názov projektu my-ws, názov zásobníka deva región AWS us-west-2. Príkazový riadok na spustenie testov Mocha bude vyzerať takto:

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

    Ak to urobíte, podľa očakávania nám ukáže, že máme tri neúspešné testy!

    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

    Opravme náš 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;
    

    A potom znova spustite testy:

    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)

    Všetko dobre dopadlo... Hurá! ✓✓✓

    To je na dnes všetko, ale o testovaní nasadenia si povieme v druhej časti prekladu 😉

Zdroj: hab.com

Pridať komentár