Тествайте инфраструктурата като код с Pulumi. Част 1

Добър ден приятели. В очакване на началото на нов поток със скорост „Практики и инструменти на DevOps“ Споделяме с вас нов превод. Отивам.

Тествайте инфраструктурата като код с Pulumi. Част 1

Използването на Pulumi и езици за програмиране с общо предназначение за инфраструктурен код (Infrastructure as Code) предоставя много предимства: наличието на умения и знания, елиминиране на шаблони в кода чрез абстракция, инструменти, познати на вашия екип, като IDE и линтери. Всички тези инструменти за софтуерно инженерство не само ни правят по-продуктивни, но и подобряват качеството на нашия код. Ето защо е съвсем естествено използването на езици за програмиране с общо предназначение ни позволява да въведем друга важна практика за разработка на софтуер - тестване.

В тази статия ще разгледаме как Pulumi ни помага да тестваме нашата инфраструктура като код.

Тествайте инфраструктурата като код с Pulumi. Част 1

Защо да тествате инфраструктура?

Преди да навлезем в подробности, струва си да си зададем въпроса: „Защо изобщо да тестваме инфраструктура?“ Има много причини за това и ето някои от тях:

  • Единично тестване на отделни функции или фрагменти от вашата програмна логика
  • Проверява желаното състояние на инфраструктурата спрямо определени ограничения.
  • Откриване на често срещани грешки, като липса на криптиране на кофа за съхранение или незащитен, отворен достъп от интернет до виртуални машини.
  • Проверка на изпълнението на инфраструктурното осигуряване.
  • Извършване на тестване по време на изпълнение на логиката на приложението, работещо във вашата „програмирана“ инфраструктура, за да се провери функционалността след предоставяне.
  • Както виждаме, има широка гама от опции за тестване на инфраструктурата. Polumi има механизми за тестване във всяка точка от този спектър. Нека да започнем и да видим как работи.

Единично тестване

Програмите Pulumi са написани на езици за програмиране с общо предназначение, като JavaScript, Python, TypeScript или Go. Следователно пълната мощност на тези езици, включително техните инструменти и библиотеки, включително тестови рамки, е достъпна за тях. Pulumi е мулти-облак, което означава, че може да се използва за тестване от всеки доставчик на облак.

(В тази статия, въпреки че сме многоезични и многооблачни, ние използваме JavaScript и Mocha и се фокусираме върху AWS. Можете да използвате Python unittest, Go test framework или всяка друга тестова рамка, която харесвате. И, разбира се, Pulumi работи чудесно с Azure, Google Cloud, Kubernetes.)

Както видяхме, има няколко причини, поради които може да искате да тествате вашия инфраструктурен код. Един от тях е конвенционалното тестване на единици. Тъй като вашият код може да има функции - например да изчислява CIDR, да изчислява динамично имена, тагове и т.н. - вероятно ще искате да ги тествате. Това е същото като писането на редовни модулни тестове за приложения на любимия ви език за програмиране.
За да станете малко по-сложни, можете да проверите как вашата програма разпределя ресурсите. За да илюстрираме, нека си представим, че трябва да създадем прост EC2 сървър и искаме да сме сигурни в следното:

  • Екземплярите имат етикет Name.
  • Екземплярите не трябва да използват вграден скрипт userData - трябва да използваме AMI (изображение).
  • Не трябва да има SSH, изложен на интернет.

Този пример се основава на моят пример aws-js-webserver:

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;

Това е основната програма Pulumi: тя просто разпределя EC2 група за сигурност и екземпляр. Все пак трябва да се отбележи, че тук нарушаваме и трите правила, посочени по-горе. Да пишем тестове!

Писане на тестове

Общата структура на нашите тестове ще изглежда като обикновените 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): Должен быть тэг Name.
        // TODO(check 2): Не должно быть inline-скрипта userData.
    });
    let group = infra.group;
    describe("#group", function() {
        // TODO(check 3): Не должно быть SSH, открытого в Интернет.
    });
});

Сега нека напишем нашия първи тест: уверете се, че екземплярите имат етикета Name. За да проверим това, ние просто получаваме обекта на екземпляра на EC2 и проверяваме съответното свойство 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();
                }
            });
        });

Изглежда като обикновен тест, но с няколко функции, които си струва да се отбележат:

  • Тъй като отправяме запитвания за състоянието на даден ресурс преди внедряване, нашите тестове винаги се изпълняват в режим „планиране“ (или „предварителен преглед“). По този начин има много свойства, чиито стойности просто няма да бъдат извлечени или няма да бъдат дефинирани. Това включва всички изходни свойства, изчислени от вашия доставчик на облак. Това е нормално за нашите тестове - проверяваме само входните данни. Ще се върнем към този въпрос по-късно, когато стане въпрос за интеграционни тестове.
  • Тъй като всички свойства на ресурсите на Pulumi са изходи и много от тях се оценяват асинхронно, трябва да използваме метода apply за достъп до стойностите. Това е много подобно на обещанията и функцията then .
  • Тъй като използваме няколко свойства, за да покажем URN на ресурса в съобщението за грешка, трябва да използваме функцията pulumi.allда ги комбинирате.
  • И накрая, тъй като тези стойности се изчисляват асинхронно, трябва да използваме вградената функция за асинхронно обратно извикване на Mocha done или връщане на обещание.

След като настроим всичко, ще имаме достъп до входовете като прости стойности на JavaScript. Имот tags е карта (асоциативен масив), така че просто ще се уверим, че е (1) не е невярно и (2) има ключ за Name. Много е просто и сега можем да тестваме всичко!

Сега нека напишем нашия втори чек. Още по-просто е:

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

И накрая, нека напишем третия тест. Това ще бъде малко по-сложно, защото търсим правилата за влизане, свързани с групата за сигурност, от които може да има много, и диапазоните на CIDR в тези правила, от които също може да има много. Но успяхме:

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

Това е всичко. Сега нека проведем тестовете!

Течащи тестове

В повечето случаи можете да изпълнявате тестове по обичайния начин, като използвате тестовата рамка по ваш избор. Но има една особеност на Pulumi, на която си струва да се обърне внимание.
Обикновено за стартиране на програми Pulumi се използва pulimi CLI (интерфейс на командния ред), който конфигурира времето за изпълнение на езика, контролира стартирането на двигателя Pulumi, така че операциите с ресурси да могат да бъдат записани и включени в плана и т.н. Има обаче един проблем. Когато работите под контрола на вашата тестова рамка, няма да има комуникация между CLI и двигателя Pulumi.

За да заобиколим този проблем, просто трябва да посочим следното:

  • Име на проекта, което се съдържа в променливата на средата PULUMI_NODEJS_PROJECT (или по-общо, PULUMI__PROJECT для других языков).
    Името на стека, който е посочен в променливата на средата PULUMI_NODEJS_STACK (или по-общо, PULUMI__ STACK).
    Вашите променливи за конфигурация на стека. Те могат да бъдат получени с помощта на променлива на средата PULUMI_CONFIG и техният формат е JSON карта с двойки ключ/стойност.

    Програмата ще издаде предупреждения, показващи, че връзката с CLI/двигателя не е налична по време на изпълнение. Това е важно, защото вашата програма всъщност няма да внедрява нищо и може да бъде изненада, ако това не е това, което възнамерявате да направите! За да кажете на Pulumi, че това е точно това, от което се нуждаете, можете да инсталирате PULUMI_TEST_MODE в true.

    Представете си, че трябва да посочим името на проекта в my-ws, име на стека devи AWS регион us-west-2. Командният ред за изпълнение на Mocha тестове ще изглежда така:

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

    Правейки това, както се очаква, ще ни покаже, че имаме три неуспешни теста!

    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

    Нека поправим нашата програма:

    "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;
    

    И след това изпълнете тестовете отново:

    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)

    Всичко мина добре... Ура! ✓✓✓

    Това е всичко за днес, но ще говорим за тестване на внедряване във втората част на превода 😉

Източник: www.habr.com

Добавяне на нов коментар