Infrastruktúra mint kód tesztelése Pulumi segítségével. 1. rész

Üdvözlet, barátok. A „DevOps gyakorlatok és eszközök” kurzus új előadásának kezdete előtt az új fordítást osztjuk meg veletek. Induljunk. A Pulumi és általános célú programozási nyelvek használata az infrastruktúrakódhoz (Infrastructure as Code) számos előnnyel jár: a készségek és tudás rendelkezésre állása, a kód boilerplate eltüntetése absztrakcióval, és a csapatotok által ismert eszközök, mint az IDE-k és linterek. Ezek a szoftvermérnöki eszközök nemcsak hogy termelékenyebbé tesznek minket, hanem javítják a kód minőségét is. Ezért teljesen természetes, hogy az általános célú programozási nyelvek használata lehetővé teszi egy másik fontos szoftverfejlesztési gyakorlat bevezetését -

Infrastruktúra mint kód tesztelése Pulumi segítségével. 1. rész

Ebben a cikkben megvizsgáljuk, hogyan segít a Pulumi tesztelni az „infrastruktúránkat mint kódot”. tesztelés.

Miért teszteljük az infrastruktúrát?

Infrastruktúra mint kód tesztelése Pulumi segítségével. 1. rész

Mielőtt a részletekbe mennénk, érdemes feltenni a kérdést: „Miért is teszteljük az infrastruktúrát?” Sok oka van, és az alábbiak néhány közülük:

Az egyes funkciók vagy a programod logikai töredékeinek moduláris tesztelése.

  • Az infrastruktúra kívánt állapotának ellenőrzése bizonyos korlátozásoknak való megfelelés szempontjából.
  • Gyakori hibák észlelése, például a storage bucket titkosításának hiánya vagy védtelen, nyilvános internetes hozzáférés virtuális gépekhez.
  • Az infrastruktúra provisioningjának ellenőrzése.
  • Runtime tesztelés végrehajtása a „programozott” infrastruktúrádba telepített alkalmazás logikáján a provisioning utáni működés ellenőrzésére.
  • Ahogy látjuk, széles spektrum áll rendelkezésre az infrastruktúra tesztelésére. A Pulumi rendelkezik mechanizmusokkal a spektrum minden pontjában történő teszteléshez. Kezdjük el, és nézzük meg, hogyan működik ez.
  • Moduláris tesztelés

A Pulumi programokat általános célú programozási nyelveken, például JavaScript, Python, TypeScript vagy Go nyelveken hozzák létre. Ezért ezen nyelvek teljes ereje, beleértve az eszközeiket és könyvtáraikat, köztük a tesztkeretrendszereket is, rendelkezésre áll számukra. A Pulumi multicloud, ami lehetővé teszi bármilyen felhőszolgáltató használatát a tesztekhez.

A Pulumi programok általános célú programozási nyelveken készülnek, mint például JavaScript, Python, TypeScript vagy Go. Ezért ezek a nyelvek teljes ereje, beleértve az eszközkészleteket és könyvtárakat, például a tesztkeretrendszereket is, elérhető számukra. A Pulumi többfelhős, ami azt jelenti, hogy bármely felhőszolgáltató használható a tesztelés során.

(A cikkben, a többnyelvűség és a többfelhős környezet ellenére, JavaScriptet és Mocha-t használunk, és az AWS-re összpontosítunk. Python is használható unittest, a Go tesztkeretrendszer vagy bármely más kedvenc tesztkeretrendszere. És természetesen a Pulumi remekül működik az Azure-val, a Google Cloud-dal és a Kubernetes-szel.)

Ahogy láttuk, számos oka lehet annak, hogy tesztelni kell a infrastrukturális kódot. Az egyik ok a szokásos egységtesztelés. Mivel kódodnak lehetnek funkciói — például a CIDR kiszámítása, dinamikus nevek vagy címkék generálása stb. — valószínűleg tesztelni szeretnéd őket. Ez ugyanolyan, mint a szokásos egységtesztek írása a kedvenc programozási nyelveden.
Ha egy kicsit bonyolultabbá tesszük, ellenőrizhetjük, hogyan osztja el a programod az erőforrásokat. Tegyük fel, hogy létre kell hoznunk egy egyszerű EC2-szervert, és biztosnak kell lennünk abban, hogy a következők teljesülnek:

  • Az instance-nek van egy címke Név.
  • Az instance-nek nem szabad inline szkriptet használnia userData — használnunk kell AMI-t (képet).
  • Nem szabad, hogy SSH nyitva legyen az Interneten.

Ez a példa a aws-js-webserver példámon alapul.:

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 ], // hivatkozás a fenti csoportobjektumra
    ami: "ami-c55673a0"            // AMI az us-east-2 (Ohio) számára,
    userData: userData              // egy egyszerű webkiszolgáló indítása
});

exports.group = group;
exports.server = server;
exports.publicIp = server.publicIp;
exports.publicHostName = server.publicDns;

Ez egy alap Pulumi program: egyszerűen létrehoz egy EC2 biztonsági csoportot és egy instance-t. Fontos megjegyezni, hogy itt mindhárom fent említett szabályt megszegjük. Jöjjön a tesztelés!

Teszteket írunk

A tesztjeink általános felépítése olyan lesz, mint a szokásos Mocha teszteké:

ec2tests.js

test.js:
let assert = require("assert");
let mocha = require("mocha");
let pulumi = require("@pulumi/pulumi");
let infra = require("./index");

describe("Infrastruktúra", function() {
    let server = infra.server;
    describe("#server", function() {
        // TODO(check 1): Kell, hogy legyen egy Name címke.
        // TODO(check 2): Nem szabad inline szkript a userData.
    });
    let group = infra.group;
    describe("#group", function() {
        // TODO(check 3): Nem szabad, hogy SSH nyitva legyen az Interneten.
    });
});

Most tegyük meg az első tesztünket: ellenőrizzük, hogy az instanciáknak van címkéje. NévEhhez egyszerűen lekérjük az EC2-instance objektumot, és ellenőrizzük a megfelelő tulajdonságot. címkék:

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

Úgy néz ki, mint egy szokásos teszt, de van néhány figyelemre méltó sajátossága:

  • Mivel az erőforrás állapotát kérjük le a telepítés előtt, a tesztjeink mindig „terv” (vagy „előnézet”) módban futnak. Így sok tulajdonság értéke egyszerűen nem lesz elérhető vagy meghatározatlan. Ide tartoznak azok a kimeneti tulajdonságok is, amelyeket a felhőszolgáltatód számít ki. A tesztjeink szempontjából ez rendben van — csak a bemeneteket ellenőrizzük. Ezzel a kérdéssel később foglalkozunk, amikor az integrációs tesztekről van szó.
  • Mivel a Pulumi-erőforrások minden tulajdonsága „kimenet”, és sok közülük aszinkron módon számítódik ki, ezért használnunk kell az apply metódust az értékek eléréséhez. Ez nagyon hasonló a promiszokhoz és az afunkcióhoz. then .
  • Mivel több tulajdonságot használnánk, hogy megmutassuk az erőforrás URN-jét a hibaüzenetben, szükségünk van a pulumi.all, hogy ezeket össze tudjuk kapcsolni.
  • Végül, mivel ezek az értékek aszinkron módon számítódnak ki, használnunk kell a Mocha beépített aszinkron képességét visszahívással. kész vagy ígétek visszatérésével.

Miután mindent beállítottunk, a bemenetekhez egyszerű JavaScript értékekként férünk hozzá. A tulajdonság címkék térkép (asszociatív tömb), ezért egyszerűen csak ellenőrizzük, hogy (1) nem false, és (2) van egy kulcs a Név. Ez nagyon egyszerű, most már bármit ellenőrizhetünk!

Most írjuk meg a második ellenőrzésünket. Ez még egyszerűbb:

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

És végül írjuk meg a harmadik tesztet. Ez kicsit bonyolultabb lesz, mivel a biztonsági csoporthoz kapcsolódó bejövő szabályokat keresünk, amelyekből sok lehet, és a CIDR tartományokat ezekben a szabályokban, amelyekből szintén sok lehet. De megoldottuk:

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

Ennyi. Most futtassuk a teszteket!

Tesztelés indítása

A teszteket a legtöbb esetben a választott tesztkeretrendszer használatával, a szokásos módon lehet futtatni. De van egy jellemző Pulumi, amire érdemes figyelni.
Általában a Pulumi programok elindításához a pulimi CLI (parancssori felület) használatos, amely beállítja a nyelv runtime-ját, ellenőrzi a Pulumi motor futásait, lehetővé téve a forrásokkal kapcsolatos műveletek rögzítését és azok beépítését a tervbe stb. Azonban van egy probléma. Ha a tesztkeretrendszered alatt fut a program, nem lesz kapcsolat a CLI és a Pulumi motor között.

A probléma megkerüléséhez csak a következőket kell megadnunk:

  • A projekt neve, amely a környezeti változóban található PULUMI_NODEJS_PROJECT (vagy általánosabb esetben, PULUMI__PROJECT más nyelvekhez).
    A verem neve, amely a környezeti változóban van PULUMI_NODEJS_STACK (vagy általánosabb esetben, PULUMI__STACK).
    A veremhez tartozó konfigurációs változóid. Ezeket a környezeti változó segítségével nyerheted ki PULUMI_CONFIG és a formátumuk egy JSON térkép, amely kulcs/érték párokat tartalmaz.

    A program figyelmeztetéseket fog kiadni, amelyek azt jelzik, hogy a futás során nincs kapcsolat a CLI/központi egységgel. Ez fontos, mert valójában a programod semmit sem fog telepíteni, és meglepetést okozhat, ha nem ez volt a szándékod! Annak érdekében, hogy a Pulumi tudja, hogy ez pontosan az, amire szükséged van, beállíthatod a PULUMI_TEST_MODE be. igaz.

    Képzeld el, hogy meg kell adnod a projekt nevét a my-ws, a verem nevét dev, és az AWS régiót us-west-2. A Mocha tesztek végrehajtásához használandó parancssor a következőképpen fog kinézni:

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

    Ezt végrehajtva, ahogy vártuk, látni fogjuk, hogy három hibás tesztünk van!

    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

    Javítsuk ki a programunkat:

    "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 ], // referáljuk a fenti csoport objektumot
        ami: "ami-c55673a0"             // AMI az us-east-2 (Ohio) számára,
    });
     
    exports.group = group;
    exports.server = server;
    exports.publicIp = server.publicIp;
    exports.publicHostName = server.publicDns;
    

    És ezután futtassuk újra a teszteket:

    Infrastruktúra
        #szerver
          ✓ név címkével kell rendelkeznie
          ✓ nem szabad userData-t használni (helyette használjon AMI-t)
        #csoport
          ✓ nem szabad 22-es portot (SSH) megnyitni az Internet felé
     
     
     3 sikeres (16ms)

    Minden sikerült… Hurrá! ✓✓✓

    Ma ennyi, a telepítés teszteléséről a fordítás második részében beszélünk 😉

Forrás: habr.com

Megbízható hosting vásárlása weboldalak számára, DDoS védelemmel, VPS VDS szerverek. 🔥 Megbízható webtárhely vásárlása DDoS védelemmel, VPS VDS szerverek | ProHoster