Bona tarda amics. En previsió de l'inici d'un nou flux al ritme
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.
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
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.all
per 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'entornPULUMI_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'entornPULUMI_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 piladev
, i AWS Regionus-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