Prova d'infraestructura com a codi amb Pulumi. Part 1

Bona tarda amics. En previsió de l'inici d'un nou flux al ritme "Pràctiques i eines de DevOps" Compartim amb vosaltres una nova traducció. Vés.

Prova d'infraestructura com a codi amb Pulumi. Part 1

L'ús de Pulumi i llenguatges de programació de propòsit general per al codi d'infraestructura (Infrastructure as Code) ofereix molts avantatges: la disponibilitat d'habilitats i coneixements, eliminació de la placa bàsica en el codi mitjançant l'abstracció, eines familiars al vostre equip, com ara IDE i linters. Totes aquestes eines d'enginyeria de programari no només ens fan més productius, sinó que també milloren la qualitat del nostre codi. Per tant, és natural que l'ús de llenguatges de programació de propòsit general ens permeti introduir una altra pràctica important de desenvolupament de programari: proves.

En aquest article, veurem com Pulumi ens ajuda a provar la nostra infraestructura com a codi.

Prova d'infraestructura com a codi amb Pulumi. Part 1

Per què provar la infraestructura?

Abans d'entrar en detall, val la pena fer la pregunta: "Per què provar la infraestructura?" Hi ha moltes raons per això i aquí n'hi ha algunes:

  • Proves unitàries de funcions individuals o fragments de la lògica del vostre programa
  • Verifica l'estat desitjat de la infraestructura davant determinades restriccions.
  • Detecció d'errors habituals, com ara la manca de xifratge d'un cub d'emmagatzematge o l'accés obert des d'Internet a màquines virtuals sense protecció.
  • Comprovació de la implantació de l'aprovisionament d'infraestructura.
  • Realització de proves en temps d'execució de la lògica de l'aplicació que s'executa dins de la vostra infraestructura "programada" per comprovar la funcionalitat després de l'aprovisionament.
  • Com podem veure, hi ha una àmplia gamma d'opcions de prova d'infraestructura. Polumi té mecanismes per provar en tots els punts d'aquest espectre. Comencem i veiem com funciona.

Prova unitària

Els programes Pulumi estan escrits en llenguatges de programació de propòsit general com JavaScript, Python, TypeScript o Go. Per tant, disposen de tot el poder d'aquests llenguatges, incloses les seves eines i biblioteques, inclosos els marcs de prova. Pulumi és multinúvol, el que significa que es pot utilitzar per fer proves des de qualsevol proveïdor de núvol.

(En aquest article, tot i ser multilingüe i multinúvol, fem servir JavaScript i Mocha i ens centrem en AWS. Podeu utilitzar Python unittest, marc de prova Go o qualsevol altre marc de prova que us agradi. I, per descomptat, Pulumi funciona molt bé amb Azure, Google Cloud, Kubernetes.)

Com hem vist, hi ha diversos motius pels quals potser voldreu provar el vostre codi d'infraestructura. Un d'ells és la prova d'unitat convencional. Com que el vostre codi pot tenir funcions, per exemple, calcular CIDR, calcular dinàmicament noms, etiquetes, etc. - Probablement voldreu provar-los. Això és el mateix que escriure proves unitàries habituals per a aplicacions en el vostre llenguatge de programació preferit.
Per complicar-vos una mica, podeu comprovar com el vostre programa assigna els recursos. Per il·lustrar-ho, imaginem que hem de crear un servidor EC2 senzill i volem estar segurs del següent:

  • Les instàncies tenen una etiqueta Name.
  • Les instàncies no haurien d'utilitzar script en línia userData - hem d'utilitzar una AMI (imatge).
  • No hi hauria d'haver SSH exposat a Internet.

Aquest exemple es basa en el meu exemple 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;

Aquest és el programa bàsic de Pulumi: simplement assigna un grup de seguretat EC2 i una instància. Tanmateix, cal tenir en compte que aquí estem incomplint les tres regles esmentades anteriorment. Escrivim proves!

Proves d'escriptura

L'estructura general de les nostres proves semblarà a les proves Mocha habituals:

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

Ara escrivim la nostra primera prova: assegureu-vos que les instàncies tinguin l'etiqueta Name. Per comprovar-ho, simplement obtenim l'objecte d'instància EC2 i comprovem la propietat corresponent 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();
                }
            });
        });

Sembla una prova normal, però amb algunes característiques que cal destacar:

  • Com que consultem l'estat d'un recurs abans del desplegament, les nostres proves sempre s'executen en mode "pla" (o "visualització prèvia"). Així, hi ha moltes propietats els valors de les quals simplement no es recuperaran o no es definiran. Això inclou totes les propietats de sortida calculades pel vostre proveïdor de núvol. Això és normal per a les nostres proves: només comprovem les dades d'entrada. Tornarem a aquest tema més endavant, quan es tracti de proves d'integració.
  • Com que totes les propietats dels recursos de Pulumi són sortides, i moltes d'elles s'avaluen de manera asíncrona, hem d'utilitzar el mètode apply per accedir als valors. Això és molt semblant a les promeses i una funció then .
  • Com que estem utilitzant diverses propietats per mostrar l'URN del recurs al missatge d'error, hem d'utilitzar la funció pulumi.allper combinar-los.
  • Finalment, com que aquests valors es calculen de manera asíncrona, hem d'utilitzar la funció de devolució de trucada asíncrona integrada de Mocha done o retornant una promesa.

Un cop ho hàgim configurat tot, tindrem accés a les entrades com a valors simples de JavaScript. Propietat tags és un mapa (matriu associatiu), així que ens assegurarem que (1) no sigui fals i (2) hi hagi una clau per Name. És molt senzill i ara podem provar qualsevol cosa!

Ara anem a escriure el nostre segon xec. És encara més senzill:

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

I finalment, escrivim la tercera prova. Això serà una mica més complicat perquè estem buscant les regles d'inici de sessió associades al grup de seguretat, del qual n'hi pot haver molts, i els intervals CIDR d'aquestes regles, de les quals també n'hi pot haver moltes. Però vam aconseguir:

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

Això és tot. Ara fem les proves!

Execució de proves

En la majoria dels casos, podeu executar proves de la manera habitual, utilitzant el marc de proves que trieu. Però hi ha una característica de Pulumi a la qual val la pena prestar atenció.
Normalment, per executar els programes Pulumi, s'utilitza la CLI (Command Line interface) de pulimi, que configura el temps d'execució de l'idioma, controla el llançament del motor Pulumi perquè les operacions amb recursos es puguin registrar i incloure al pla, etc. Tanmateix, hi ha un problema. Quan s'executa sota el control del vostre marc de prova, no hi haurà comunicació entre la CLI i el motor Pulumi.

Per solucionar aquest problema, només hem d'especificar el següent:

  • Nom del projecte, que es troba a la variable d'entorn PULUMI_NODEJS_PROJECT (o, de manera més general, PULUMI__PROJECT для других языков).
    El nom de la pila que s'especifica a la variable d'entorn PULUMI_NODEJS_STACK (o, de manera més general, PULUMI__ STACK).
    Les variables de configuració de la vostra pila. Es poden obtenir mitjançant una variable d'entorn PULUMI_CONFIG i el seu format és el mapa JSON amb parells clau/valor.

    El programa emetrà avisos indicant que la connexió a la CLI/motor no està disponible durant l'execució. Això és important perquè el vostre programa en realitat no desplegarà res i pot ser una sorpresa si això no és el que pretenieu fer! Per dir-li a Pulumi que això és exactament el que necessiteu, podeu instal·lar-lo PULUMI_TEST_MODE в true.

    Imagineu que hem d'especificar el nom del projecte my-ws, nom de pila dev, i AWS Region us-west-2. La línia d'ordres per executar les proves de Mocha serà així:

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

    Fer això, com era d'esperar, ens demostrarà que tenim tres proves fallides!

    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

    Arreglem el nostre programa:

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

    I després torna a executar les proves:

    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)

    Tot va anar bé... Hura! ✓✓✓

    Això és tot per avui, però parlarem de les proves de desplegament a la segona part de la traducció 😉

Font: www.habr.com

Afegeix comentari