Pulumi bilan kod sifatida infratuzilmani sinovdan o'tkazish. 1-qism

Xayrli kun do'stlar. Kurs bo'yicha yangi oqim boshlanishini kutish bilan "DevOps amaliyotlari va vositalari" Siz bilan yangi tarjimani baham ko'ramiz. Bor.

Pulumi bilan kod sifatida infratuzilmani sinovdan o'tkazish. 1-qism

Infratuzilma kodi uchun Pulumi va umumiy maqsadli dasturlash tillaridan foydalanish (Infrastructure as Code) ko'plab afzalliklarni beradi: ko'nikma va bilimlarning mavjudligi, mavhumlashtirish orqali koddagi noaniqlikni yo'q qilish, IDE va ​​linterlar kabi jamoangizga tanish vositalar. Ushbu dasturiy ta'minot muhandisligi vositalari nafaqat bizni samaraliroq qiladi, balki kodimiz sifatini ham oshiradi. Shu sababli, umumiy maqsadli dasturlash tillaridan foydalanish bizga dasturiy ta'minotni ishlab chiqishning yana bir muhim amaliyotini joriy qilish imkonini berishi tabiiydir - sinov.

Ushbu maqolada biz Pulumi bizning infratuzilmamizni kod sifatida sinab ko'rishimizga qanday yordam berishini ko'rib chiqamiz.

Pulumi bilan kod sifatida infratuzilmani sinovdan o'tkazish. 1-qism

Nega infratuzilmani sinab ko'rish kerak?

Tafsilotlarga kirishdan oldin, "Nima uchun infratuzilmani umuman sinab ko'rish kerak?" Degan savolni berishga arziydi. Buning bir qancha sabablari bor va ulardan ba'zilari:

  • Dastur mantig'ining individual funktsiyalari yoki qismlarini birlik sinovi
  • Muayyan cheklovlarga qarshi infratuzilmaning istalgan holatini tekshiradi.
  • Saqlash paqirining shifrlanmaganligi yoki Internetdan virtual mashinalarga himoyalanmagan, ochiq kirish kabi keng tarqalgan xatolarni aniqlash.
  • Infratuzilmani ta'minlashning amalga oshirilishini tekshirish.
  • Ta'minlashdan so'ng funksionallikni tekshirish uchun "dasturlashtirilgan" infratuzilmangiz ichida ishlaydigan dastur mantig'ining ish vaqti sinovini o'tkazish.
  • Ko'rib turganimizdek, infratuzilmani sinovdan o'tkazish imkoniyatlari keng. Polumi ushbu spektrning har bir nuqtasida sinov uchun mexanizmlarga ega. Keling, boshlaymiz va u qanday ishlashini ko'rib chiqamiz.

Birlik sinovi

Pulumi dasturlari JavaScript, Python, TypeScript yoki Go kabi umumiy maqsadli dasturlash tillarida yozilgan. Shu sababli, ushbu tillarning to'liq quvvati, jumladan, ularning vositalari va kutubxonalari, shu jumladan test ramkalari ular uchun mavjud. Pulumi ko'p bulutli, ya'ni u har qanday bulutli provayderdan sinov uchun ishlatilishi mumkin.

(Ushbu maqolada ko'p tilli va ko'p bulutli bo'lishiga qaramay, biz JavaScript va Mocha-dan foydalanamiz va AWS-ga e'tibor qaratamiz. Python-dan foydalanishingiz mumkin. unittest, Test tizimiga yoki oʻzingiz yoqtirgan boshqa test tizimiga oʻting. Va, albatta, Pulumi Azure, Google Cloud, Kubernetes bilan ajoyib ishlaydi.)

Ko'rib turganimizdek, infratuzilma kodingizni sinab ko'rishning bir qancha sabablari bor. Ulardan biri an'anaviy birlik sinovidir. Chunki sizning kodingiz funktsiyalarga ega bo'lishi mumkin - masalan, CIDRni hisoblash, nomlarni, teglarni va hokazolarni dinamik ravishda hisoblash. - ehtimol siz ularni sinab ko'rmoqchisiz. Bu sevimli dasturlash tilingizdagi ilovalar uchun muntazam birlik testlarini yozish bilan bir xil.
Biroz murakkabroq bo'lish uchun dasturingiz resurslarni qanday taqsimlashini tekshirishingiz mumkin. Tasavvur qilish uchun, biz oddiy EC2 serverini yaratishimiz kerakligini tasavvur qilaylik va biz quyidagilarga ishonch hosil qilishni xohlaymiz:

  • Misollar tegga ega Name.
  • Instances inline skriptdan foydalanmasligi kerak userData - biz AMI (tasvir) dan foydalanishimiz kerak.
  • Internetga ta'sir qiladigan SSH bo'lmasligi kerak.

Ushbu misolga asoslanadi mening misolim 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;

Bu asosiy Pulumi dasturi: u oddiygina EC2 xavfsizlik guruhi va nusxasini ajratadi. Ammo shuni ta'kidlash kerakki, bu erda biz yuqorida aytib o'tilgan uchta qoidani buzamiz. Keling, testlarni yozamiz!

Yozish testlari

Sinovlarimizning umumiy tuzilishi odatiy Mocha testlariga o'xshaydi:

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, открытого в Интернет.
    });
});

Endi birinchi testimizni yozamiz: misollarda teg borligiga ishonch hosil qiling Name. Buni tekshirish uchun biz EC2 misol obyektini olamiz va tegishli xususiyatni tekshiramiz 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();
                }
            });
        });

Bu oddiy sinovga o'xshaydi, lekin e'tiborga olish kerak bo'lgan bir nechta xususiyatlarga ega:

  • Joylashtirishdan oldin biz resurs holatini so'raganimiz sababli, testlarimiz har doim "reja" (yoki "oldindan ko'rish") rejimida amalga oshiriladi. Shunday qilib, qiymatlari oddiygina olinmaydigan yoki aniqlanmaydigan ko'plab xususiyatlar mavjud. Bunga bulut provayderingiz tomonidan hisoblangan barcha chiqish xususiyatlari kiradi. Bu bizning testlarimiz uchun odatiy hol - biz faqat kiritilgan ma'lumotlarni tekshiramiz. Keyinchalik, integratsiya testlari haqida gap ketganda, biz bu masalaga qaytamiz.
  • Barcha Pulumi resurs xususiyatlari chiqishlar bo'lgani uchun va ularning ko'pchiligi asinxron tarzda baholanadi, biz qiymatlarga kirish uchun qo'llash usulidan foydalanishimiz kerak. Bu va'dalar va funktsiyaga juda o'xshaydi then .
  • Xato xabarida URN resursini ko'rsatish uchun bir nechta xususiyatlardan foydalanayotganimiz sababli, biz funktsiyadan foydalanishimiz kerak pulumi.allularni birlashtirish uchun.
  • Va nihoyat, bu qiymatlar asinxron hisoblanganligi sababli, biz Mocha-ning o'rnatilgan asinxron qayta qo'ng'iroq qilish xususiyatidan foydalanishimiz kerak. done yoki va'dani qaytarish.

Biz hamma narsani o'rnatganimizdan so'ng, biz oddiy JavaScript qiymatlari sifatida kirishlarga kirish imkoniyatiga ega bo'lamiz. Mulk tags bu xarita (assotsiativ massiv), shuning uchun biz uning (1) noto'g'ri emasligiga va (2) kaliti borligiga ishonch hosil qilamiz. Name. Bu juda oddiy va endi biz hamma narsani sinab ko'rishimiz mumkin!

Endi ikkinchi chekimizni yozamiz. Bu yanada oddiyroq:

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

Va nihoyat, uchinchi testni yozamiz. Bu biroz murakkabroq bo'ladi, chunki biz xavfsizlik guruhi bilan bog'liq kirish qoidalarini qidirmoqdamiz, ular ko'p bo'lishi mumkin va bu qoidalarda CIDR diapazonlari ham ko'p bo'lishi mumkin. Ammo biz muvaffaq bo'ldik:

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

Ana xolos. Endi testlarni o'tkazamiz!

Ishlayotgan testlar

Ko'pgina hollarda siz o'zingiz tanlagan test tizimidan foydalanib, odatdagi usulda testlarni o'tkazishingiz mumkin. Ammo Pulumining bir xususiyati bor, unga e'tibor berishga arziydi.
Odatda, Pulumi dasturlarini ishga tushirish uchun pulimi CLI (Buyruqlar qatori interfeysi) ishlatiladi, u tilning ishlash vaqtini sozlaydi, Pulumi dvigatelining ishga tushirilishini boshqaradi, shunda resurslar bilan operatsiyalar yozib olinadi va rejaga kiritiladi va hokazo. Biroq, bitta muammo bor. Sinov tizimi nazorati ostida ishlayotganingizda, CLI va Pulumi dvigateli o'rtasida aloqa bo'lmaydi.

Ushbu muammoni hal qilish uchun biz quyidagilarni aniqlashimiz kerak:

  • Atrof-muhit o'zgaruvchisida joylashgan loyiha nomi PULUMI_NODEJS_PROJECT (yoki umuman olganda, PULUMI__PROJECT для других языков).
    Atrof-muhit o'zgaruvchisida ko'rsatilgan stek nomi PULUMI_NODEJS_STACK (yoki umuman olganda, PULUMI__ STACK).
    Sizning stek konfiguratsiya o'zgaruvchilaringiz. Ularni muhit o'zgaruvchisi yordamida olish mumkin PULUMI_CONFIG va ularning formati kalit/qiymat juftliklari bilan JSON xaritasi.

    Dastur ijro paytida CLI/dvigatelga ulanish mavjud emasligini ko'rsatuvchi ogohlantirishlar chiqaradi. Bu juda muhim, chunki sizning dasturingiz aslida hech narsa o'rnatmaydi va agar siz buni rejalashtirmagan bo'lsangiz, bu ajablanib bo'lishi mumkin! Pulumiga bu sizga kerak bo'lgan narsa ekanligini aytish uchun siz o'rnatishingiz mumkin PULUMI_TEST_MODE в true.

    Tasavvur qiling, biz loyiha nomini ko'rsatishimiz kerak my-ws, stek nomi dev, va AWS mintaqasi us-west-2. Mocha testlarini ishga tushirish uchun buyruq qatori quyidagicha ko'rinadi:

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

    Buni qilish, kutilganidek, bizda uchta muvaffaqiyatsiz sinov borligini ko'rsatadi!

    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

    Keling, dasturimizni tuzatamiz:

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

    Va keyin yana testlarni bajaring:

    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)

    Hammasi yaxshi o'tdi... Huray! ✓✓✓

    Hammasi bugun uchun, lekin biz tarjimaning ikkinchi qismida joylashtirish testi haqida gaplashamiz 😉

Manba: www.habr.com

a Izoh qo'shish