Bună ziua, prieteni. Înainte de începerea noii sesiuni a cursului împărtășim cu voi un nou traducere. Să începem.

Utilizarea Pulumi și a limbajelor de programare generice pentru codul de infrastructură (Infrastructure as Code) aduce multe avantaje: abilități și cunoștințe disponibile, eliminarea codului boilerplate prin abstractizare, instrumente familiare echipei voastre, cum ar fi IDE-uri și lintere. Toate aceste unelte de inginerie software nu doar că ne fac mai productivi, dar îmbunătățesc și calitatea codului. Prin urmare, este foarte natural că utilizarea limbajelor de programare generice permite introducerea unei alte practici importante de dezvoltare software — testare.
În acest articol vom explora cum Pulumi ajută la testarea „infrastructurii ca cod”.

De ce să testăm infrastructura?
Înainte de a intra în detalii, merită să ne punem întrebarea: „De ce ar trebui să testăm infrastructura?” Există multe motive pentru asta și iată câteva dintre ele:
- Testarea modulară a funcțiilor individuale sau a fragmentelor de logică ale programului vostru
- Verificarea stării dorite a infrastructurii în conformitate cu anumite restricții.
- Identificarea erorilor comune, cum ar fi lipsa criptării bucket-ului de stocare sau accesul nesecurizat, deschis la internet, la mașinile virtuale.
- Verificarea îndeplinirii provisioning-ului infrastructurii.
- Executarea testării runtime a logicii aplicației, rulată în cadrul infrastructurii „programate” pentru a verifica funcționalitatea după provisioning.
- După cum vedem, există o gamă largă de opțiuni pentru testarea infrastructurii. În Pulumi există mecanisme pentru testarea în fiecare punct al acestui spectru. Să începem și să vedem cum funcționează.
Testarea modulară
Programele Pulumi sunt create în limbaje de programare generice, cum ar fi JavaScript, Python, TypeScript sau Go. Prin urmare, beneficiază de toată puterea acestor limbaje, inclusiv instrumentele și bibliotecile lor, inclusiv cadrele de testare. Pulumi este multi-cloud, ceea ce înseamnă că este posibilă utilizarea oricăror furnizori de cloud pentru teste.
(În acest articol, în ciuda multilingvismului și a multi-cloud-ului, folosim JavaScript și Mocha, având ca orientare AWS. Putem utiliza Python unittest, frameworkul de testare Go sau orice alt framework de testare preferat. Și, desigur, Pulumi funcționează perfect cu Azure, Google Cloud, Kubernetes.)
După cum am văzut, există câteva motive pentru care ar putea fi necesar să testați codul infrastructurii dvs. Unul dintre acestea este testarea modulară obișnuită. Deoarece codul dvs. poate avea funcții — de exemplu, pentru calcularea CIDR, calcularea dinamică a numelui, etichete etc. — probabil că doriți să le testați. Acesta este același lucru cu scrierea testelor de unitate obișnuite pentru aplicațiile din limbajul de programare preferat.
Dacă complicăm puțin lucrurile, putem verifica cum își alocă programul resursele. Pentru ilustrație, să ne imaginăm că trebuie să creăm un server EC2 simplu și dorim să ne asigurăm că următoarele sunt adevărate:
- Instanțele au o etichetă
Name. - Instanțele nu trebuie să utilizeze scripturi inline
userData— trebuie să folosim AMI (imagine). - Nu trebuie să existe SSH deschis la Internet.
Acest exemplu este bazat pe :
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 ], // referință la obiectul grup de mai sus
ami: "ami-c55673a0" // AMI pentru us-east-2 (Ohio),
userData: userData // porniți un server web simplu
});
exports.group = group;
exports.server = server;
exports.publicIp = server.publicIp;
exports.publicHostName = server.publicDns;
Aceasta este o programă de bază Pulumi: alocă pur și simplu un grup de securitate EC2 și o instanță. Cu toate acestea, trebuie menționat că aici încălcăm toate cele trei reguli enunțate mai sus. Să începem să scriem teste!
Scrieți teste
Structura generală a testelor noastre va arăta ca testele obișnuite 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): Trebuie să existe eticheta Name.
// TODO(check 2): Nu trebuie să existe script inline userData.
});
let group = infra.group;
describe("#group", function() {
// TODO(check 3): Nu trebuie să existe SSH deschis la Internet.
});
}); Acum să scriem primul nostru test: să ne asigurăm că instanțele au o etichetă Name. Pentru a verifica acest lucru, pur și simplu obținem obiectul instanței EC2 și verificăm proprietatea corespunzătoare. 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();
}
});
});Arată ca un test obișnuit, dar cu câteva particularități care merită menționate:
- Deoarece solicităm starea resursei înainte de implementare, testele noastre se execută întotdeauna în modul „plan” (sau „previzualizare”). Astfel, există multe proprietăți al căror valori pur și simplu nu vor fi obținute sau vor fi nedefinite. Acestea includ toate proprietățile de ieșire calculate de furnizorul dvs. de cloud. Acest lucru este acceptabil pentru testele noastre — verificăm doar datele de intrare. Vom reveni asupra acestui subiect mai târziu, când vom ajunge la testele de integrare.
- Deoarece toate proprietățile resurselor Pulumi sunt „ieșiri”, iar multe dintre ele sunt calculate asincron, trebuie să folosim metoda apply pentru a accesa valorile. Aceasta este foarte asemănătoare cu promisiunile și funcția async.
atunci. - Deoarece folosește mai multe proprietăți pentru a arăta URN-ul resursei în mesajul de eroare, trebuie să folosim funcția
pulumi.all, pentru a le combina. - În cele din urmă, deoarece aceste valori sunt calculate asincron, trebuie să folosim capacitatea încorporată asincronă a Mocha cu un callback
gatasau returnarea unei promisiuni.
După ce am configurat totul, vom avea acces la datele de intrare ca la valori simple JavaScript. Proprietatea tags este un map (un array asociativ), așa că ne vom asigura doar că aceasta (1) nu este false și (2) are o cheie pentru Name. Este foarte simplu și acum putem verifica orice vrem!
Acum să scriem al doilea nostru test. Acesta este și mai simplu:
// 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, în cele din urmă, să scriem al treilea test. Acesta va fi puțin mai complicat, deoarece căutăm reguli de intrare legate de grupuri de securitate, care pot fi multe, și intervalele CIDR din aceste reguli, care la fel pot fi numeroase. Dar ne-am descurcat:
// 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();
}
});
});Și asta e tot. Acum să rulăm testele!
Rularea testelor
Testele se pot rula, în majoritatea cazurilor, în mod obișnuit, folosind framework-ul de testare ales de dvs. Dar există o particularitate a Pulumi-ului la care merită să fiți atenți.
De obicei, pentru a rula programele Pulumi, se folosește interfața de linie de comandă pulumi CLI, care configurează runtime-ul limbajului, controlează execuțiile motorului Pulumi, astfel încât să poată înregistra operațiunile cu resursele și să le includă în plan etc. Cu toate acestea, există o problemă. Atunci când este rulat sub controlul cadrului dvs. de testare, nu va exista o legătură între CLI și motorul Pulumi.
Pentru a ocoli această problemă, trebuie să specificăm următoarele:
- Numele proiectului, care se află în variabila de mediu
PULUMI_NODEJS_PROJECT(sau, într-un caz mai general,PULUMI__PROJECT pentru alte limbi).
Numele stivei, care este specificat în variabila de mediuPULUMI_NODEJS_STACK(sau, într-un caz mai general,PULUMI__STACK).
Variabilele de configurare ale stivei. Acestea pot fi obținute prin variabila de mediuPULUMI_CONFIGiar formatul lor este o hartă JSON cu perechi cheie/valoare.Programul va emite avertismente care indică faptul că în timpul execuției nu există o conexiune cu CLI/motorul. Acest lucru este important, deoarece, de fapt, programul dumneavoastră nu va desfășura nimic și asta ar putea fi o surpriză, dacă nu este ceea ce ați dorit să faceți! Pentru a spune Pulumi că acesta este exact lucru pe care vi-l doriți, puteți seta
PULUMI_TEST_MODEîntrue.Imaginați-vă că trebuie să specificăm numele proiectului în
my-ws, numele stiveidev, și regiunea AWSus-west-2. Linia de comandă pentru a rula testele Mocha va arăta astfel:$ PULUMI_TEST_MODE=true PULUMI_NODEJS_STACK="my-ws" PULUMI_NODEJS_PROJECT="dev" PULUMI_CONFIG='{ "aws:region": "us-west-2" }' mocha tests.jsExecutarea acestuia, așa cum era de așteptat, ne va arăta că avem trei teste eșuate!
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 groupHaideți să corectăm programul nostru:
"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 ], // referință la obiectul grup de mai sus ami: "ami-c55673a0" // AMI pentru us-east-2 (Ohio), }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns;Și apoi vom relua testele:
Infrastructură #server ✓ trebuie să aibă un tag de nume ✓ nu trebuie să folosească userData (folosiți un AMI în schimb) #group ✓ nu trebuie să deschidă portul 22 (SSH) către Internet 3 treceri (16ms)Totul a mers bine… Ura! ✓✓✓
Asta este tot pentru astăzi, iar despre testarea desfășurării vom discuta în a doua parte a traducerii 😉
Sursa: habr.com
