Pozdravljeni, prijatelji. Pred začetkom nove serije tečaja delimo z vami nov prevod. Gremo.

Uporaba Pulumi in jezikov programiranja splošne rabe za infrastrukturo kot kodo (Infrastructure as Code) prinaša številne prednosti: znanje in veščine, odpravo boilerplate kode z abstrakcijo, orodja, ki jih pozna vaša ekipa, kot so IDE in linters. Vsa ta orodja programske inženirije nas ne le naredijo bolj produktivne, ampak tudi izboljšajo kakovost kode. Zato ni presenetljivo, da uporaba jezikov splošne rabe omogoča vključitev še ene pomembne prakse v razvoju programske opreme — testiranje.
V tem članku bomo preučili, kako Pulumi pomaga testirati našo "infrastrukturo kot kodo".

Zakaj testirati infrastrukturo?
Preden se poglobimo v podrobnosti, si zastavimo vprašanje: "Zakaj sploh testirati infrastrukturo?" Razlogov je veliko, tukaj je nekaj njih:
- Enotno testiranje posameznih funkcij ali delov logike vašega programa
- Preverjanje, ali želi infrastrukturno stanje ustreza določenim omejitvam.
- Odkrivanje pogostih napak, kot so pomanjkanje šifriranja storage bucket ali nezaščiten, odprt dostop iz interneta do virtualnih strojev.
- Preverjanje izvedbe provizioniranja infrastrukture.
- Izvajanje runtime testiranja logike aplikacije, ki teče znotraj vaše "programirane" infrastrukture, da preverite delovanje po provizioniranju.
- Kot lahko vidimo, obstaja širok spekter možnosti za testiranje infrastrukture. V Polumi so mehanizmi za testiranje na vsaki točki tega spektra. Začnimo in poglejmo, kako to deluje.
Enotno testiranje
Pulumi programi so napisani v jezikih splošne rabe, kot so JavaScript, Python, TypeScript ali Go. Zato so jim na voljo vse zmogljivosti teh jezikov, vključno z njihovimi orodji in knjižnicami, tudi testnimi okviri. Pulumi je večoblačen, kar pomeni, da lahko za teste uporabljate katerikoli oblačni ponudnik.
(V tem članku, kljub večjezičnosti in večoblačnosti, uporabljamo JavaScript in Mocha ter se osredotočamo na AWS. Lahko pa uporabite Python unittest, testni okvir Go ili bilo koji drugi omiljeni testni okvir. I, naravno, Pulumi savršeno funkcioniše sa Azure, Google Cloud, Kubernetes.)
Kao što smo videli, postoji nekoliko razloga zbog kojih bi moglo biti potrebno testirati vaš infrastrukturni kod. Jedan od njih je obično kao testiranje modula. Pošto vaš kod može imati funkcije — na primer, za izračunavanje CIDR, dinamičko izračunavanje imena, oznaka itd. — verovatno ćete želeti da ih testirate. To je isto kao pisanje običnih modula za aplikacije na vašem omiljenom programskom jeziku.
Ako malo zakomplikujemo, možemo proveriti kako vaša aplikacija raspoređuje resurse. Da ilustrujemo, pretpostavimo da treba da kreiramo jednostavni EC2 server i želimo da budemo sigurni u sledeće:
- Instanci imaju oznaku
Ime. - Instanci ne bi trebali koristiti inline skriptu
userData— trebamo koristiti AMI (slika). - Ne sme biti SSH, otvorenog na Internetu.
Ovaj primer je napisan prema :
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 ], \/\/ referišite na objekat grupe iznad
ami: "ami-c55673a0" \/\/ AMI za us-east-2 (Ohio),
userData: userData \/\/ započnite jednostavan web server
});
exports.group = group;
exports.server = server;
exports.publicIp = server.publicIp;
exports.publicHostName = server.publicDns;
Ovo je osnovni Pulumi program: samo alocira grupu bezbednosti EC2 i instancu. Međutim, treba napomenuti da ovde kršimo sva tri pravila koja su postavljena ranije. Hajde da pišemo testove!
Pisanje testova
Opšta struktura naših testovaće izgledati kao obični Mocha testovi:
ec2tests.js
test.js:
let assert = require("assert");
let mocha = require("mocha");
let pulumi = require("@pulumi/pulumi");
let infra = require("./index");
describe("Infrastruktura", function() {
let server = infra.server;
describe("#server", function() {
\/\/ TODO(check 1): Treba da postoji oznaka Name.
\/\/ TODO(check 2): Ne bi smela biti inline skripta userData.
});
let group = infra.group;
describe("#group", function() {
\/\/ TODO(check 3): Ne bi smelo biti SSH, otvorenog na Internetu.
});
}); Sada hajde da napišemo naš prvi test: potvrdimo da instanci imaju oznaku Ime. Da bismo to proverili, jednostavno dobijamo objekat EC2 instance i proveravamo odgovarajuću osobinu. 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();
}
});
});Izgleda kao običan test, ali sa nekoliko osobina koje zaslužuju pažnju:
- Budući da zahtevamo stanje resursa pre postavljanja, naši testovi se uvek izvršavaju u "plan" (ili "preview") režimu. Na taj način, postoji mnogo svojstava čije vrednosti jednostavno neće biti dobijene ili će biti neodređene. Ovo uključuje sve izlazne osobine koje proračunava vaš cloud provajder. Za naše testove to je u redu — proveravamo samo ulaze. O ovoj temi ćemo se ponovo osvrnuti kasnije, kada dođemo do integracionih testova.
- Budući da su sva svojstva Pulumi resursa "izlazi", a mnoga od njih se izračunavaju asinhrono, potrebno je da koristimo metodu apply da bismo dobili pristup vrednostima. Ovo je veoma slično promenljivim i afunkciji.
then. - Budući da koristimo nekoliko svojstava da bismo prikazali URN resursa u poruci o grešci, potrebno je da koristimo funkciju
pulumi.all, kako bismo ih spojili. - Na kraju, budući da se ove vrednosti izračunavaju asinhrono, potrebno je da koristimo ugrađenu asinhronu mogućnost Mocha sa povratnim pozivom
doneili povratkom promenljive.
Nakon što sve postavimo, imat ćemo pristup ulaznim podacima kao običnim JavaScript vrednostima. Svojstvo tags je map (asocijativni niz), tako da ćemo se samo uveriti da je (1) ne false i (2) postoji ključ za Ime. Ovo je veoma jednostavno i sada možemo proveriti sve što želimo!
Sada hajde da napišemo našu drugu proveru. Ovo je još jednostavnije:
// 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, na kraju, napisat ćemo treći test. Ovo će biti malo komplikovanije, jer tražimo pravila ulaska vezana za sigurnosnu grupu, kojih može biti mnogo, i CIDR opsege u tim pravilima, kojih takođe može biti mnogo. Ali mi smo se snašli:
// 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 to. Sada hajde da pokrenemo testove!
Pokretanje testova
Testove se u većini slučajeva može pokrenuti na uobičajen način, koristeći izabrani testni okvir. Ali postoji jedna osobina Pulumi na koju treba obratiti pažnju.
Običajno za zagon Pulumi programov uporabljamo pulimi CLI (Command Line Interface), ki nastavi runtime jezika, nadzira zagon Pulumi motorja, da lahko sledimo operacijam z viri in jih vključimo v načrt itd. Vendar obstaja ena težava. Pri zagonu pod nadzorom vašega testnega okvira ne bo povezave med CLI in Pulumi motorjem.
Da se izognemo tej težavi, moramo preprosto določiti naslednje:
- Ime projekta, ki ga vsebuje okoljska spremenljivka
PULUMI_NODEJS_PROJECT(ali, v bolj splošnem primeru,PULUMI__PROJECT za druge jezike).
Ime sklada, ki je navedeno v okoljski spremenljivkiPULUMI_NODEJS_STACK(ali, v bolj splošnem primeru,PULUMI__STACK).
Vaše spremenljivke konfiguracije sklada. Te je mogoče pridobiti z okoljsko spremenljivkoPULUMI_CONFIGin njihov format predstavlja JSON mapo s pari ključ/vrednost.Program bo izdal opozorila, ki pravijo, da med izvajanjem ni dostopa do povezave s CLI/motornimi. To je pomembno, ker v resnici vaša programska oprema ne bo ničesar izvajala in lahko to postane presenečenje, če to ni tisto, kar ste želeli! Da bi Pulumi povedali, da je to tisto, kar potrebujete, lahko nastavite
PULUMI_TEST_MODEutrue.Predstavljajte si, da moramo določiti ime projekta v
my-ws, ime sklada,dev, in regijo AWSus-west-2. Ukazna vrstica za izvajanje Mocha testov bo izgledala takole:$ PULUMI_TEST_MODE=true PULUMI_NODEJS_STACK="my-ws" PULUMI_NODEJS_PROJECT="dev" PULUMI_CONFIG='{ "aws:region": "us-west-2" }' mocha tests.jsIzvedba tega, kot je bilo pričakovano, nam bo pokazala, da imamo tri neuspešne teste!
Infrastruktura #strežnik 1) mora imeti oznako imena 2) ne sme uporabljati userData (namesto tega uporabite AMI) #skupina 3) ne sme odpirati porta 22 (SSH) na internetu 0 uspehov (17ms) 3 neuspehov 1) Infrastruktura #strežnik mora imeti oznako imena: Napaka: Manjkajoča oznaka imena na strežniku urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 2) Infrastruktura #strežnik ne sme uporabljati userData (namesto tega uporabite AMI): Napaka: Nezakonita uporaba userData na strežniku urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 3) Infrastruktura #skupina ne sme odpirati porta 22 (SSH) na internetu: Napaka: Nezakonito je, da je odprt SSH port 22 na internetu (CIDR 0.0.0.0/0) na skupiniPopravimo našo programsko opremo:
"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 ponovno zaženimo teste:
Infrastruktura #server ✓ mora imeti oznako imena ✓ ne sme uporabljati userData (namesto tega uporabite AMI) #group ✓ ne sme odpirati porta 22 (SSH) za internet 3 uspešna (16ms)Vse je potekalo uspešno... Hura! ✓✓✓
To je vse za danes, o testiranju uvajanja pa se bomo pogovarjali v drugem delu prevoda 😉
Izvor: habr.com
