Тестирање инфраструктуре као кода са Пулумијем. Део 1

Добар дан пријатељи. У очекивању почетка новог тока по стопи „ДевОпс праксе и алати“ Делимо са вама нови превод. Иди.

Тестирање инфраструктуре као кода са Пулумијем. Део 1

Коришћење Пулуми и програмских језика опште намене за инфраструктурни код (Инфраструцтуре ас Цоде) пружа многе предности: доступност вештина и знања, елиминисање шаблона у коду кроз апстракцију, алате који су познати вашем тиму, као што су ИДЕ и линтери. Сви ови алати за софтверско инжењерство не само да нас чине продуктивнијима, већ и побољшавају квалитет нашег кода. Стога је сасвим природно да нам употреба програмских језика опште намене омогућава да уведемо још једну важну праксу развоја софтвера - тестирање.

У овом чланку ћемо погледати како нам Пулуми помаже да тестирамо нашу инфраструктуру као код.

Тестирање инфраструктуре као кода са Пулумијем. Део 1

Зашто тестирати инфраструктуру?

Пре него што уђемо у детаље, вреди поставити питање: „Зашто уопште тестирати инфраструктуру?“ Постоји много разлога за то и ево неких од њих:

  • Јединично тестирање појединачних функција или фрагмената логике вашег програма
  • Верификује жељено стање инфраструктуре у односу на одређена ограничења.
  • Откривање уобичајених грешака, као што је недостатак енкрипције канте за складиштење или незаштићени, отворени приступ са Интернета виртуелним машинама.
  • Провера имплементације инфраструктурног обезбеђења.
  • Извођење тестирања логике апликације која се покреће унутар ваше „програмиране“ инфраструктуре да би се проверила функционалност након обезбеђивања.
  • Као што видимо, постоји широк спектар опција за тестирање инфраструктуре. Полуми има механизме за тестирање у свакој тачки овог спектра. Хајде да почнемо и видимо како то функционише.

Јединично тестирање

Пулуми програми су написани у програмским језицима опште намене као што су ЈаваСцрипт, Питхон, ТипеСцрипт или Го. Стога им је доступна пуна моћ ових језика, укључујући њихове алате и библиотеке, укључујући оквире за тестирање. Пулуми је мулти-цлоуд, што значи да се може користити за тестирање од било ког добављача облака.

(У овом чланку, упркос томе што смо вишејезични и вишеоблачни, користимо ЈаваСцрипт и Моцха и фокусирамо се на АВС. Можете да користите Питхон unittest, Го тест оквир или било који други оквир за тестирање који волите. И, наравно, Пулуми одлично ради са Азуре, Гоогле Цлоуд, Кубернетес.)

Као што смо видели, постоји неколико разлога зашто бисте можда желели да тестирате свој инфраструктурни код. Један од њих је конвенционално тестирање јединица. Зато што ваш код може имати функције - на пример, за израчунавање ЦИДР-а, динамички израчунавање имена, ознака итд. - вероватно ћете желети да их тестирате. Ово је исто као писање редовних јединичних тестова за апликације на вашем омиљеном програмском језику.
Да будете мало компликованији, можете да проверите како ваш програм додељује ресурсе. За илустрацију, замислимо да треба да креирамо једноставан ЕЦ2 сервер и желимо да будемо сигурни у следеће:

  • Инстанце имају ознаку Name.
  • Инстанце не би требало да користе инлине скрипту userData - морамо користити АМИ (слика).
  • Не би требало да буде ССХ изложен Интернету.

Овај пример се заснива на мој пример авс-јс-вебсервер:

индек.јс:

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

Ово је основни Пулуми програм: он једноставно додељује ЕЦ2 безбедносну групу и инстанцу. Међутим, треба напоменути да овде кршимо сва три горе наведена правила. Хајде да пишемо тестове!

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

Општа структура наших тестова ће изгледати као редовни Моцха тестови:

ец2тестс.јс

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. Да бисмо ово проверили, једноставно добијамо објекат инстанце ЕЦ2 и проверавамо одговарајуће својство 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();
                }
            });
        });

Изгледа као обичан тест, али са неколико карактеристика које вреди напоменути:

  • Пошто испитујемо стање ресурса пре примене, наши тестови се увек изводе у режиму „планирања“ (или „прегледа“). Дакле, постоји много својстава чије вредности једноставно неће бити преузете или неће бити дефинисане. Ово укључује све излазне особине које је израчунао ваш добављач у облаку. Ово је нормално за наше тестове - проверавамо само улазне податке. Вратићемо се на ово питање касније, када су у питању интеграциони тестови.
  • Пошто су сва својства Пулуми ресурса излази, а многа од њих се процењују асинхроно, морамо да користимо метод примене да бисмо приступили вредностима. Ово је веома слично обећањима и функцији then .
  • Пошто користимо неколико својстава да прикажемо УРН ресурса у поруци о грешци, морамо да користимо функцију pulumi.allда их комбинују.
  • Коначно, пошто се ове вредности израчунавају асинхроно, морамо да користимо Моцха-ину уграђену функцију асинхроног повратног позива done или враћање обећања.

Када све подесимо, имаћемо приступ улазима као једноставним ЈаваСцрипт вредностима. Имовина 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();
                }
            });
        });

И на крају, хајде да напишемо трећи тест. Ово ће бити мало компликованије јер тражимо правила за пријаву повезана са безбедносном групом, којих може бити много, а ЦИДР распони у тим правилима, којих такође може бити много. Али успели смо:

    // 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_NODEJS_PROJECT (или, уопштеније, PULUMI__PROJECT для других языков).
    Име стека које је наведено у променљивој окружења PULUMI_NODEJS_STACK (или, уопштеније, PULUMI__ STACK).
    Променљиве ваше конфигурације стека. Могу се добити помоћу променљиве окружења PULUMI_CONFIG а њихов формат је ЈСОН мапа са паровима кључ/вредност.

    Програм ће издати упозорења која указују да веза са ЦЛИ/мотором није доступна током извршавања. Ово је важно јер ваш програм заправо неће ништа да примењује и може бити изненађење ако то није оно што сте намеравали да урадите! Да бисте рекли Пулумију да је то управо оно што вам треба, можете инсталирати PULUMI_TEST_MODE в true.

    Замислите да треба да наведемо име пројекта my-ws, име стека devи АВС регион us-west-2. Командна линија за покретање Моцха тестова ће изгледати овако:

    $ 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)

    Све је прошло добро... Ура! ✓✓✓

    То је све за данас, али о тестирању примене ћемо говорити у другом делу превода 😉

Извор: ввв.хабр.цом

Додај коментар