Probando a infraestrutura como código con Pulumi. Parte 1

Boas tardes amigos. En previsión do inicio dun novo fluxo ao ritmo "Prácticas e ferramentas de DevOps" Compartimos contigo unha nova tradución. Vaia.

Probando a infraestrutura como código con Pulumi. Parte 1

O uso de Pulumi e linguaxes de programación de propósito xeral para o código de infraestrutura (Infraestrutura como Código) proporciona moitas vantaxes: a dispoñibilidade de habilidades e coñecementos, eliminación do boilerplate no código a través da abstracción, ferramentas familiares para o teu equipo, como IDEs e linters. Todas estas ferramentas de enxeñería de software non só nos fan máis produtivos, senón que tamén melloran a calidade do noso código. Polo tanto, é natural que o uso de linguaxes de programación de propósito xeral nos permita introducir outra práctica importante de desenvolvemento de software: proba.

Neste artigo, veremos como Pulumi nos axuda a probar a nosa infraestrutura como código.

Probando a infraestrutura como código con Pulumi. Parte 1

Por que probar a infraestrutura?

Antes de entrar en detalles, paga a pena facer a pregunta: "Por que probar a infraestrutura?" Hai moitas razóns para iso e aquí tes algunhas delas:

  • Proba unitaria de funcións individuais ou fragmentos da lóxica do seu programa
  • Verifica o estado desexado da infraestrutura fronte a certas restricións.
  • Detección de erros comúns, como a falta de cifrado dun depósito de almacenamento ou o acceso aberto desprotexido desde Internet a máquinas virtuais.
  • Comprobación da implantación do aprovisionamento da infraestrutura.
  • Realización de probas de tempo de execución da lóxica da aplicación en execución dentro da súa infraestrutura "programada" para comprobar a funcionalidade despois do aprovisionamento.
  • Como podemos ver, hai unha gran variedade de opcións de proba de infraestrutura. Polumi ten mecanismos para probar en todos os puntos deste espectro. Comecemos e vexamos como funciona.

Probas unitarias

Os programas Pulumi están escritos en linguaxes de programación de propósito xeral como JavaScript, Python, TypeScript ou Go. Polo tanto, todo o poder destas linguaxes, incluíndo as súas ferramentas e bibliotecas, incluíndo marcos de proba, está dispoñible para eles. Pulumi é multi-nube, o que significa que se pode usar para probar desde calquera provedor de nube.

(Neste artigo, a pesar de ser multilingüe e multinube, usamos JavaScript e Mocha e centrámonos en AWS. Podes usar Python unittest, marco de proba Go ou calquera outro marco de proba que che guste. E, por suposto, Pulumi funciona moi ben con Azure, Google Cloud, Kubernetes.)

Como vimos, hai varias razóns polas que pode querer probar o código de infraestrutura. Un deles son as probas unitarias convencionais. Porque o teu código pode ter funcións, por exemplo, calcular CIDR, calcular dinámicamente nomes, etiquetas, etc. - Probablemente quererás probalos. Isto é o mesmo que escribir probas unitarias habituais para aplicacións na súa linguaxe de programación favorita.
Para complicarte un pouco, podes comprobar como o teu programa asigna os recursos. Para ilustralo, imaxinemos que necesitamos crear un servidor EC2 sinxelo e queremos estar seguros do seguinte:

  • As instancias teñen unha etiqueta Name.
  • As instancias non deben usar script en liña userData - debemos utilizar unha AMI (imaxe).
  • Non debería haber ningún SSH exposto a Internet.

Este exemplo baséase en o meu exemplo 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;

Este é o programa Pulumi básico: simplemente asigna un grupo de seguridade EC2 e unha instancia. Non obstante, hai que ter en conta que aquí estamos incumprindo as tres regras indicadas anteriormente. Escribamos probas!

Probas de redacción

A estrutura xeral das nosas probas parecerase ás probas Mocha habituais:

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

Agora imos escribir a nosa primeira proba: asegúrate de que as instancias teñan a etiqueta Name. Para comprobar isto, simplemente obtemos o obxecto de instancia EC2 e comprobamos a propiedade correspondente 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();
                }
            });
        });

Parece unha proba normal, pero con algunhas características que vale a pena destacar:

  • Como consultamos o estado dun recurso antes da implantación, as nosas probas sempre se executan en modo "plan" (ou "vista previa"). Así, hai moitas propiedades cuxos valores simplemente non se recuperarán ou non se definirán. Isto inclúe todas as propiedades de saída calculadas polo teu provedor de nube. Isto é normal para as nosas probas: só comprobamos os datos de entrada. Volveremos sobre esta cuestión máis adiante, cando se trate de probas de integración.
  • Dado que todas as propiedades de recursos de Pulumi son saídas, e moitas delas avalíanse de forma asíncrona, necesitamos utilizar o método apply para acceder aos valores. Isto é moi semellante ás promesas e á función then .
  • Dado que estamos a usar varias propiedades para mostrar o URN do recurso na mensaxe de erro, necesitamos usar a función pulumi.allpara combinalos.
  • Finalmente, dado que estes valores se calculan de forma asíncrona, necesitamos usar a función de devolución de chamada asíncrona integrada de Mocha done ou devolver unha promesa.

Unha vez configurado todo, teremos acceso ás entradas como simples valores JavaScript. Propiedade tags é un mapa (matriz asociativa), así que só nos aseguraremos de que (1) non sexa falso e (2) hai unha clave para Name. É moi sinxelo e agora podemos probar calquera cousa!

Agora imos escribir o noso segundo cheque. É aínda máis sinxelo:

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

E para rematar, imos escribir a terceira proba. Isto será un pouco máis complicado porque estamos a buscar as regras de inicio de sesión asociadas ao grupo de seguridade, dos cales pode haber moitos, e os rangos CIDR nesas regras, dos que tamén pode haber moitos. Pero conseguimos:

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

Iso é todo. Agora imos facer as probas!

Execución de probas

Na maioría dos casos, pode realizar probas do xeito habitual, utilizando o marco de proba que elixa. Pero hai unha característica de Pulumi que paga a pena prestar atención.
Normalmente, para executar programas Pulumi utilízase a CLI (Command Line interface) de pulimi, que configura o tempo de execución da linguaxe, controla o lanzamento do motor Pulumi para que as operacións con recursos se poidan rexistrar e incluír no plan, etc. Non obstante, hai un problema. Cando se execute baixo o control do seu marco de proba, non haberá comunicación entre a CLI e o motor Pulumi.

Para evitar este problema, só precisamos especificar o seguinte:

  • Nome do proxecto, que está contido na variable de ambiente PULUMI_NODEJS_PROJECT (ou, de xeito máis xeral, PULUMI__PROJECT для других языков).
    O nome da pila que se especifica na variable de ambiente PULUMI_NODEJS_STACK (ou, de xeito máis xeral, PULUMI__ STACK).
    As súas variables de configuración da pila. Pódense obter mediante unha variable de ambiente PULUMI_CONFIG e o seu formato é un mapa JSON con pares clave/valor.

    O programa emitirá avisos indicando que a conexión á CLI/motor non está dispoñible durante a execución. Isto é importante porque o teu programa non vai implementar nada e pode ser unha sorpresa se non é o que pretendías facer. Para dicirlle a Pulumi que isto é exactamente o que necesitas, podes instalar PULUMI_TEST_MODE в true.

    Imaxina que necesitamos especificar o nome do proxecto my-ws, nome da pila dev, e a rexión de AWS us-west-2. A liña de comandos para executar probas de Mocha terá o seguinte aspecto:

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

    Facendo isto, como era de esperar, amosaranos que temos tres probas non superadas!

    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

    Imos arranxar o noso 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;
    

    E despois executa de novo as probas:

    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)

    Todo saíu ben... ¡Xa! ✓✓✓

    Iso é todo por hoxe, pero falaremos das probas de implantación na segunda parte da tradución 😉

Fonte: www.habr.com

Engadir un comentario