Infrastruktuurin testaus koodina Pulumin kanssa. Osa 1

Hyvää iltapäivää ystävät. Odottaessa uuden virtauksen alkamista nopeudella "DevOps-käytännöt ja -työkalut" Jaamme kanssasi uuden käännöksen. Mennä.

Infrastruktuurin testaus koodina Pulumin kanssa. Osa 1

Pulumin ja yleiskäyttöisten ohjelmointikielten käyttäminen infrastruktuurikoodiin (Infrastructure as Code) tarjoaa monia etuja: osaamisen ja tiedon saatavuus, koodin kattilan poistaminen abstraktin avulla, tiimillesi tutut työkalut, kuten IDE:t ja linterit. Kaikki nämä ohjelmistosuunnittelutyökalut eivät ainoastaan ​​tee meistä tuottavampia, vaan myös parantavat koodimme laatua. Siksi on luonnollista, että yleiskäyttöisten ohjelmointikielten käyttö mahdollistaa toisen tärkeän ohjelmistokehityskäytännön - testaus.

Tässä artikkelissa tarkastellaan, kuinka Pulumi auttaa meitä testaamaan infrastruktuuriamme koodina.

Infrastruktuurin testaus koodina Pulumin kanssa. Osa 1

Miksi testata infrastruktuuria?

Ennen kuin mennään yksityiskohtiin, kannattaa kysyä: "Miksi testata infrastruktuuria ollenkaan?" Tähän on monia syitä, ja tässä on joitain niistä:

  • Yksittäisten toimintojen tai ohjelmalogiikan fragmenttien yksikkötestaus
  • Tarkistaa infrastruktuurin halutun tilan tiettyjä rajoituksia vastaan.
  • Yleisten virheiden, kuten tallennustilan salauksen puute tai suojaamaton, avoin pääsy Internetistä virtuaalikoneen, havaitseminen.
  • Infrastruktuurin käyttöönoton tarkistaminen.
  • Suorita "ohjelmoidun" infrastruktuurin sisällä ajettavan sovelluslogiikan ajonaikainen testaus toiminnan tarkistamiseksi asennuksen jälkeen.
  • Kuten näemme, infrastruktuurin testausvaihtoehtoja on laaja valikoima. Polumilla on mekanismeja tämän spektrin jokaisessa pisteessä testaamiseen. Aloitetaan ja katsotaan kuinka se toimii.

Yksikkötestaus

Pulumi-ohjelmat on kirjoitettu yleiskäyttöisillä ohjelmointikielillä, kuten JavaScript, Python, TypeScript tai Go. Siksi näiden kielten täysi teho, mukaan lukien niiden työkalut ja kirjastot, mukaan lukien testikehykset, ovat heidän käytettävissään. Pulumi on monipilvi, joten sitä voidaan käyttää testaukseen miltä tahansa pilvipalveluntarjoajalta.

(Tässä artikkelissa, vaikka se on monikielinen ja monipilvi, käytämme JavaScriptiä ja Mochaa ja keskitymme AWS:ään. Voit käyttää Pythonia unittest, Siirry testikehykseen tai muuhun haluamaasi testikehykseen. Ja tietysti Pulumi toimii loistavasti Azuren, Google Cloudin ja Kubernetesin kanssa.)

Kuten olemme nähneet, on useita syitä, miksi saatat haluta testata infrastruktuurikoodiasi. Yksi niistä on perinteinen yksikkötestaus. Koska koodissasi voi olla toimintoja - esimerkiksi laskea CIDR, laskea dynaamisesti nimiä, tunnisteita jne. - Haluat todennäköisesti testata niitä. Tämä on sama kuin säännöllisten yksikkötestien kirjoittaminen sovelluksille suosikkiohjelmointikielelläsi.
Jos haluat olla hieman monimutkaisempi, voit tarkistaa, kuinka ohjelmasi varaa resursseja. Esimerkkinä kuvitellaan, että meidän on luotava yksinkertainen EC2-palvelin ja haluamme olla varmoja seuraavista:

  • Esiintymillä on tunniste Name.
  • Ilmentymät eivät saa käyttää sisäistä komentosarjaa userData - meidän on käytettävä AMI-kuvaa (image).
  • Internetissä ei saa olla SSH:ta.

Tämä esimerkki perustuu esimerkkini 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;

Tämä on Pulumi-perusohjelma: se yksinkertaisesti varaa EC2-suojausryhmän ja ilmentymän. On kuitenkin huomattava, että tässä rikomme kaikkia kolmea edellä mainittua sääntöä. Kirjoitetaan testejä!

Kirjoituskokeet

Testimme yleinen rakenne näyttää tavallisilta Mokka-testeiltä:

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

Nyt kirjoitetaan ensimmäinen testimme: varmista, että esiintymissä on tunniste Name. Tämän tarkistamiseksi hankimme EC2-instanssiobjektin ja tarkistamme vastaavan ominaisuuden 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();
                }
            });
        });

Se näyttää tavalliselta testiltä, ​​mutta siinä on muutamia huomionarvoisia ominaisuuksia:

  • Koska kysymme resurssin tilan ennen käyttöönottoa, testimme suoritetaan aina "suunnitelma" (tai "esikatselu") -tilassa. Siten on monia ominaisuuksia, joiden arvoja ei yksinkertaisesti noudeta tai niitä ei määritellä. Tämä sisältää kaikki pilvipalveluntarjoajasi laskemat tulosominaisuudet. Tämä on normaalia testeissämme - tarkistamme vain syötetiedot. Palaamme tähän asiaan myöhemmin integraatiotestien osalta.
  • Koska kaikki Pulumin resurssiominaisuudet ovat lähtöjä ja monet niistä arvioidaan asynkronisesti, meidän on käytettävä sovellusmenetelmää arvojen saamiseksi. Tämä muistuttaa hyvin paljon lupauksia ja toimintoja then .
  • Koska käytämme useita ominaisuuksia näyttääksemme resurssin URN:n virhesanomassa, meidän on käytettävä toimintoa pulumi.allyhdistämään niitä.
  • Lopuksi, koska nämä arvot lasketaan asynkronisesti, meidän on käytettävä Mochan sisäänrakennettua asynkronista takaisinsoittoominaisuutta done tai lupauksen palauttaminen.

Kun olemme määrittäneet kaiken, voimme käyttää syötteitä yksinkertaisina JavaScript-arvoina. Omaisuus tags on kartta (assosiatiivinen array), joten varmistamme vain, että se (1) ei ole epätosi ja (2) siinä on avain Name. Se on hyvin yksinkertaista ja nyt voimme testata mitä tahansa!

Nyt kirjoitetaan toinen tarkistus. Se on vielä yksinkertaisempaa:

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

Ja lopuksi kirjoitetaan kolmas testi. Tämä tulee olemaan hieman monimutkaisempi, koska etsimme suojausryhmään liittyviä kirjautumissääntöjä, joita voi olla useita, ja CIDR-alueita näissä säännöissä, joita voi myös olla monia. Mutta onnistuimme:

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

Siinä kaikki. Nyt tehdään testit!

Testien suorittaminen

Useimmissa tapauksissa voit suorittaa testejä tavalliseen tapaan käyttämällä valitsemaasi testikehystä. Mutta Pulumissa on yksi ominaisuus, johon kannattaa kiinnittää huomiota.
Tyypillisesti Pulumi-ohjelmien ajamiseen käytetään pulimi CLI:tä (Command Line interface), joka konfiguroi kielen ajoajan, ohjaa Pulumi-moottorin käynnistystä niin, että resursseja koskevat toiminnot voidaan tallentaa ja sisällyttää suunnitelmaan jne. Yksi ongelma kuitenkin on. Kun käytät testikehystäsi, CLI:n ja Pulumi-moottorin välillä ei ole yhteyttä.

Tämän ongelman kiertämiseksi meidän on vain määritettävä seuraavat tiedot:

  • Projektin nimi, joka sisältyy ympäristömuuttujaan PULUMI_NODEJS_PROJECT (tai yleisemmin PULUMI__PROJECT для других языков).
    Ympäristömuuttujassa määritetyn pinon nimi PULUMI_NODEJS_STACK (tai yleisemmin PULUMI__ STACK).
    Pinon määritysmuuttujat. Ne voidaan saada käyttämällä ympäristömuuttujaa PULUMI_CONFIG ja niiden muoto on JSON-kartta avain/arvo-parien kanssa.

    Ohjelma antaa varoituksia, jotka osoittavat, että yhteys CLI:hen/moottoriin ei ole käytettävissä suorituksen aikana. Tämä on tärkeää, koska ohjelmasi ei todellisuudessa ota käyttöön mitään ja voi tulla yllätyksenä, jos se ei ole sitä, mitä aioit tehdä! Voit kertoa Pulumille, että tämä on juuri sitä, mitä tarvitset, asentamalla PULUMI_TEST_MODE в true.

    Kuvittele, että meidän on määritettävä projektin nimi my-ws, pinon nimi devja AWS-alue us-west-2. Mocha-testien suorittamisen komentorivi näyttää tältä:

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

    Tämän tekeminen, kuten odotettiin, osoittaa meille, että meillä on kolme epäonnistunutta testiä!

    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

    Korjataan ohjelmamme:

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

    Ja tee sitten testit uudelleen:

    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)

    Kaikki meni hyvin... Hurraa! ✓✓✓

    Siinä kaikki tältä päivältä, mutta käyttöönottotestauksesta puhutaan käännöksen toisessa osassa 😉

Lähde: will.com

Lisää kommentti