Testiranje infrastrukture kot kode s Pulumijem. 1. del

Dober dan prijatelji. V pričakovanju začetka novega toka po stopnji "Prakse in orodja DevOps" Z vami delimo nov prevod. pojdi

Testiranje infrastrukture kot kode s Pulumijem. 1. del

Uporaba Pulumi in programskih jezikov za splošne namene za infrastrukturno kodo (Infrastructure as Code) zagotavlja številne prednosti: razpoložljivost veščin in znanja, odpravo predloge v kodi z abstrakcijo, orodja, ki jih pozna vaša ekipa, kot so IDE in linterji. Vsa ta orodja za programsko inženirstvo ne samo, da postanejo bolj produktivni, ampak tudi izboljšajo kakovost naše kode. Zato je povsem naravno, da nam uporaba splošnih programskih jezikov omogoča uvedbo še ene pomembne prakse razvoja programske opreme - Testiranje.

V tem članku si bomo ogledali, kako nam Pulumi pomaga testirati našo infrastrukturo kot kodo.

Testiranje infrastrukture kot kode s Pulumijem. 1. del

Zakaj testirati infrastrukturo?

Preden gremo v podrobnosti, si velja zastaviti vprašanje: "Zakaj sploh testirati infrastrukturo?" Razlogov za to je veliko in tukaj je nekaj izmed njih:

  • Enotno testiranje posameznih funkcij ali fragmentov vaše programske logike
  • Preveri želeno stanje infrastrukture glede na določene omejitve.
  • Zaznavanje pogostih napak, kot je pomanjkanje šifriranja vedra za shranjevanje ali nezaščiten, odprt dostop iz interneta do virtualnih strojev.
  • Preverjanje izvajanja zagotavljanja infrastrukture.
  • Izvajanje izvajalnega testiranja logike aplikacije, ki se izvaja znotraj vaše "programirane" infrastrukture, da se preveri funkcionalnost po zagotavljanju.
  • Kot lahko vidimo, obstaja širok nabor možnosti testiranja infrastrukture. Polumi ima mehanizme za testiranje na vsaki točki tega spektra. Začnimo in poglejmo, kako deluje.

Testiranje enot

Programi Pulumi so napisani v splošnih programskih jezikih, kot so JavaScript, Python, TypeScript ali Go. Zato jim je na voljo vsa moč teh jezikov, vključno z njihovimi orodji in knjižnicami, vključno s testnimi ogrodji. Pulumi je multi-cloud, kar pomeni, da ga je mogoče uporabiti za testiranje pri katerem koli ponudniku oblaka.

(V tem članku kljub temu, da je večjezičen in uporabljamo več oblakov, uporabljamo JavaScript in Mocha ter se osredotočamo na AWS. Lahko uporabite Python unittest, testno ogrodje Go ali katero koli drugo testno ogrodje, ki vam je všeč. In seveda Pulumi odlično deluje z Azure, Google Cloud, Kubernetes.)

Kot smo videli, obstaja več razlogov, zakaj bi morda želeli preizkusiti svojo infrastrukturno kodo. Eden od njih je konvencionalno testiranje enot. Ker ima lahko vaša koda funkcije - na primer za izračun CIDR, dinamično izračun imen, oznak itd. - verjetno jih boste želeli preizkusiti. To je enako kot pisanje običajnih testov enot za aplikacije v vašem najljubšem programskem jeziku.
Če želite malo bolj zakomplicirati, lahko preverite, kako vaš program dodeljuje vire. Za ponazoritev si predstavljajmo, da moramo ustvariti preprost strežnik EC2 in želimo biti prepričani o naslednjem:

  • Primerki imajo oznako Name.
  • Primerki ne smejo uporabljati vgrajenega skripta userData - moramo uporabiti AMI (slika).
  • SSH ne sme biti izpostavljen internetu.

Ta primer temelji na moj primer 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;

To je osnovni program Pulumi: preprosto dodeli varnostno skupino EC2 in primerek. Vendar je treba opozoriti, da tukaj kršimo vsa tri zgoraj navedena pravila. Pišimo teste!

Pisanje testov

Splošna struktura naših testov bo videti kot običajni testi Mocha:

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

Zdaj pa napišimo naš prvi test: preverimo, ali imajo primerki oznako Name. Če želite to preveriti, preprosto pridobimo objekt instance EC2 in preverimo ustrezno lastnost 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();
                }
            });
        });

Videti je kot običajni test, vendar z nekaj značilnostmi, ki jih je vredno omeniti:

  • Ker pred uvedbo poizvedujemo o stanju vira, se naši testi vedno izvajajo v načinu »načrt« (ali »predogled«). Tako obstaja veliko lastnosti, katerih vrednosti preprosto ne bodo pridobljene ali ne bodo definirane. To vključuje vse izhodne lastnosti, ki jih izračuna vaš ponudnik oblaka. To je normalno za naše teste - preverjamo le vhodne podatke. K temu vprašanju se bomo vrnili pozneje, ko bo govora o integracijskih testih.
  • Ker so vse lastnosti virov Pulumi izhodi in so mnoge od njih ovrednotene asinhrono, moramo za dostop do vrednosti uporabiti metodo apply. To je zelo podobno obljubam in funkciji then .
  • Ker uporabljamo več lastnosti za prikaz URN vira v sporočilu o napaki, moramo uporabiti funkcijo pulumi.alljih združiti.
  • Nazadnje, ker so te vrednosti izračunane asinhrono, moramo uporabiti vgrajeno funkcijo asinhronega povratnega klica Mocha done ali vračanje obljube.

Ko bomo vse nastavili, bomo imeli dostop do vnosov kot preprostih vrednosti JavaScript. Lastnina tags je zemljevid (asociativno polje), zato se bomo samo prepričali, da (1) ni napačen in (2) obstaja ključ za Name. Zelo preprosto je in zdaj lahko preizkusimo karkoli!

Zdaj pa napišimo naš drugi ček. Še bolj preprosto je:

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

In za konec napišimo še tretji test. To bo nekoliko bolj zapleteno, ker iščemo pravila za prijavo, povezana z varnostno skupino, ki jih je lahko veliko, in obsege CIDR v teh pravilih, ki jih je lahko tudi veliko. Ampak uspelo nam je:

    // 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 vse. Zdaj pa zaženimo teste!

Izvajanje testov

V večini primerov lahko izvajate teste na običajen način z uporabo testnega ogrodja po vaši izbiri. Vendar obstaja ena lastnost Pulumija, na katero je vredno biti pozoren.
Običajno se za izvajanje programov Pulumi uporablja pulimi CLI (vmesnik ukazne vrstice), ki konfigurira jezikovno izvajalno okolje, nadzoruje zagon motorja Pulumi, tako da se operacije z viri lahko zabeležijo in vključijo v načrt itd. Vendar obstaja en problem. Ko deluje pod nadzorom vašega testnega ogrodja, ne bo komunikacije med CLI in motorjem Pulumi.

Da se izognemo tej težavi, moramo samo določiti naslednje:

  • Ime projekta, ki je vsebovano v spremenljivki okolja PULUMI_NODEJS_PROJECT (ali bolj na splošno, PULUMI__PROJECT для других языков).
    Ime sklada, ki je podano v spremenljivki okolja PULUMI_NODEJS_STACK (ali bolj na splošno, PULUMI__ STACK).
    Vaše spremenljivke konfiguracije sklada. Lahko jih pridobimo z uporabo spremenljivke okolja PULUMI_CONFIG njihov format pa je zemljevid JSON s pari ključ/vrednost.

    Program bo med izvajanjem izdal opozorila, ki kažejo, da povezava s CLI/motorjem ni na voljo. To je pomembno, ker vaš program dejansko ne bo uvajal ničesar in lahko bo presenečenje, če to ni tisto, kar ste nameravali storiti! Če želite povedati Pulumi, da je to točno tisto, kar potrebujete, lahko namestite PULUMI_TEST_MODE в true.

    Predstavljajte si, da moramo vnesti ime projekta my-ws, ime sklada dev, in regija AWS us-west-2. Ukazna vrstica za izvajanje testov Mocha bo videti takole:

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

    Če bomo to storili, kot je bilo pričakovano, bomo pokazali, da imamo tri neuspele teste!

    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

    Popravimo naš 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;
    

    In nato znova zaženite teste:

    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)

    Vse je šlo dobro ... Hura! ✓✓✓

    To je vse za danes, a o testiranju uvajanja bomo govorili v drugem delu prevoda 😉

Vir: www.habr.com

Dodaj komentar