God dag, vänner. I förberedelse inför starten av den nya omgången av kursen delar vi med er en ny översättning. Låt oss börja.

Att använda Pulumi och allmängiltiga programmeringsspråk för infrastrukturkod (Infrastructure as Code) ger många fördelar: tillgång till färdigheter och kunskaper, eliminering av boilerplate-kod genom abstraktion, verktyg som din team är bekanta med, såsom IDE:er och linters. Alla dessa programvaruingenjörsverktyg gör oss inte bara mer produktiva, utan förbättrar även kodens kvalitet. Därför är det helt naturligt att använda allmängiltiga programmeringsspråk för att införa ytterligare en viktig programvaruutvecklingspraxis — testning.
I denna artikel kommer vi att se hur Pulumi hjälper oss att testa vår "infrastruktur som kod".

Varför testa infrastruktur?
Innan vi går in på detaljerna är det värt att ställa frågan: "Varför ska vi ens testa infrastruktur?" Det finns många anledningar till detta, och här är några av dem:
- Enhetstestning av enskilda funktioner eller fragment av logiken i ditt program
- Kontroll av det önskade tillståndet för infrastrukturen i förhållande till vissa begränsningar.
- Upptäckte vanliga fel, som brist på kryptering av lagringsbucket eller oskyddad, öppen åtkomst från Internet till virtuella maskiner.
- Verifiera att provisioneringen av infrastrukturen genomförs.
- Genomföra runtime-testning av applikationslogik som körs inom din "programmerade" infrastruktur för att säkerställa funktionalitet efter provisionering.
- Som vi ser finns det ett brett spektrum av metoder för att testa infrastruktur. Pulumi har mekanismer för att testa vid varje punkt i detta spektrum. Låt oss börja och se hur det fungerar.
Enhetstestning
Pulumi-program skapas med allmängiltiga programmeringsspråk som JavaScript, Python, TypeScript eller Go. Därför har de tillgång till hela kraften i dessa språk, inklusive deras verktyg och bibliotek, inklusive test-ramverk. Pulumi är multisky, vilket innebär att det kan använda alla molnleverantörer för tester.
(I denna artikel, trots flerspråkighet och multi-moln, använder vi JavaScript och Mocha och fokuserar på AWS. Det går att använda Python unittest, test-ramverket Go eller vilket annat test-ramverk du föredrar. Och givetvis fungerar Pulumi utmärkt med Azure, Google Cloud, Kubernetes.)
Som vi har sett finns det flera anledningar till att testa din infrastrukturkod. En av dem är vanlig modulär testning. Eftersom din kod kan ha funktioner — till exempel för att beräkna CIDR, dynamisk beräkning av namn, etiketter, etc. — vill du sannolikt testa dem. Det är densamma som att skriva vanliga modulära tester för program i ditt favoritprogrammeringsspråk.
Om vi gör det lite mer komplext kan vi kontrollera hur ditt program fördelar resurser. För att illustrera, låt oss tänka oss att vi behöver skapa en enkel EC2-server och vi vill säkerställa följande:
- Instanserna har en etikett
Namn. - Instanser får inte använda inline-skript
userData— vi måste använda AMI (avbild). - Det får inte finnas SSH öppet mot Internet.
Detta exempel är inspirerat av :
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 ], // referera till gruppobjektet ovan
ami: "ami-c55673a0" // AMI för us-east-2 (Ohio),
userData: userData // starta en enkel webbserver
});
exports.group = group;
exports.server = server;
exports.publicIp = server.publicIp;
exports.publicHostName = server.publicDns;
Detta är det grundläggande Pulumi-programmet: det allokerar helt enkelt en säkerhetsgrupp för EC2 och en instans. Men det är värt att notera att vi här bryter mot alla tre reglerna som anges ovan. Låt oss skriva tester!
Skriv tester
Den allmänna strukturen för våra tester kommer att se ut som vanliga Mocha-tester:
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): Det ska finnas en Name-etikett.
// TODO(check 2): Det ska inte finnas något inline-skript userData.
});
let group = infra.group;
describe("#group", function() {
// TODO(check 3): Det ska inte finnas SSH öppet mot Internet.
});
}); Låt oss nu skriva vårt första test: vi ska säkerställa att instanserna har en tagg Namn. För att kontrollera detta hämtar vi helt enkelt ett EC2-instanser och kontrollerar den relevanta egenskapen 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();
}
});
});Det ser ut som ett vanligt test, men med några aspekter som är värda att notera:
- Eftersom vi begär resursens tillstånd innan utrullning, körs våra tester alltid i "plan" (eller "preview"). Därför finns det många egenskaper vars värden helt enkelt inte kommer att hämtas eller kommer att vara odefinierade. Detta inkluderas i alla utdataegenskaper som beräknas av din molnleverantör. För våra tester är detta okej – vi kontrollerar bara indata. Vi kommer att återkomma till detta senare när det gäller integrationstester.
- Eftersom alla Pulumi-resursegenskaper är "utgångar", och många av dem beräknas asynkront, måste vi använda metoden apply för att komma åt värdena. Det liknar mycket löften och en funktionsmetod
then. - Eftersom vi använder flera egenskaper för att visa resursens URN i felmeddelandet, behöver vi använda funktionen
pulumi.all, för att kombinera dem. - Slutligen, eftersom dessa värden beräknas asynkront, måste vi använda den inbyggda asynkrona funktionen från Mocha med ett callback
doneeller genom att returnera ett löfte.
När vi har ställt in allt, kommer vi att ha tillgång till indata som enkla JavaScript-värden. Egenskapen tags är en karta (associativ array), så vi ser helt enkelt till att det (1) inte är false, och (2) finns en nyckel för Namn. Detta är mycket enkelt och nu kan vi kontrollera vad som helst!
Nu låt oss skriva vår andra kontroll. Detta är ännu enklare:
// 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();
}
});
});Och slutligen, låt oss skriva det tredje testet. Detta kommer att vara lite svårare, eftersom vi letar efter in- och utregler kopplade till säkerhetsgruppen, som kan vara många, och CIDR-intervall i dessa regler, som också kan vara många. Men vi klarade det:
// 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();
}
});
});Det är allt. Låt oss nu köra testerna!
Kör tester
Att köra tester går i de flesta fall att göra på vanligt sätt, med hjälp av den testram som du valt. Men det finns en egenskap hos Pulumi som är värt att notera.
Vanligtvis används pulimi CLI (Command Line Interface, kommandoradsgränssnitt) för att köra Pulumi-program, som konfigurerar språkets runtime, kontrollerar körningarna av Pulumi-motorn för att registrera åtgärder med resurser och inkludera dem i planen osv. Men det finns ett problem. När du kör under övervakning av ditt testframework kommer det inte att finnas någon koppling mellan CLI och Pulumi-motorn.
För att kringgå detta problem behöver vi helt enkelt ange följande:
- Projektets namn, vilket finns i miljövariabeln
PULUMI_NODEJS_PROJECT(eller mer allmänt,PULUMI__PROJECT för andra språk).
Namn på stacken, vilket anges i miljövariabelnPULUMI_NODEJS_STACK(eller mer allmänt,PULUMI__STACK).
Dina konfigurationsvariabler för stacken. Dessa kan erhållas med hjälp av miljövariabelnPULUMI_CONFIGoch deras format är en JSON-karta med nyckel/värde-par.Programmet kommer att ge varningar som säger att det under körning inte finns någon anslutning till CLI/motorn. Detta är viktigt, eftersom din program i själva verket inte kommer att distribuera något, och det kan bli en överraskning om det inte var vad du ville göra! För att säga till Pulumi att detta är exakt vad du behöver kan du ställa in
PULUMI_TEST_MODEpåtrue.Tänk dig att vi behöver ange namnet på projektet som
my-ws, namnet på stackendev, och AWS-regionenus-west-2. Kommandoraden för att köra Mocha-testerna ser ut så här:$ PULUMI_TEST_MODE=true PULUMI_NODEJS_STACK="my-ws" PULUMI_NODEJS_PROJECT="dev" PULUMI_CONFIG='{ "aws:region": "us-west-2" }' mocha tests.jsAtt köra detta, som förväntat, visar oss att vi har tre misslyckade tester!
Infrastruktur #server 1) måste ha en namnetikett 2) får inte använda userData (använd en AMI istället) #grupp 3) får inte öppna port 22 (SSH) för Internet 0 passerar (17ms) 3 misslyckas 1) Infrastruktur #server måste ha en namnetikett: Fel: Saknar en namnetikett för server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 2) Infrastruktur #server får inte använda userData (använd en AMI istället): Fel: Otillåten användning av userData på server urn:pulumi:my-ws::my-dev::aws:ec2/instance:Instance::web-server-www 3) Infrastruktur #grupp får inte öppna port 22 (SSH) för Internet: Fel: Otillåten SSH-port 22 öppen för Internet (CIDR 0.0.0.0/0) på gruppenLåt oss fixa vårt program:
"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 ], // referera till gruppobjektet ovan ami: "ami-c55673a0" // AMI för us-east-2 (Ohio), }); exports.group = group; exports.server = server; exports.publicIp = server.publicIp; exports.publicHostName = server.publicDns;Och sedan kör vi testerna igen:
Infrastruktur #server ✓ måste ha en namnetikett ✓ får inte använda userData (använd en AMI istället) #grupp ✓ får inte öppna port 22 (SSH) för Internet 3 godkända (16ms)Allt gick bra… Hurrra! ✓✓✓
Det var allt för idag, vi pratar om testning av distributionen i den andra delen av översättningen 😉
Källa: habr.com
