تست زیرساخت به عنوان کد با Pulumi. قسمت 1

بعد از ظهر خوبی داشته باشید دوستان. در انتظار شروع یک جریان جدید در نرخ "روش ها و ابزارهای DevOps" ترجمه جدیدی را با شما به اشتراک می گذاریم. برو

تست زیرساخت به عنوان کد با Pulumi. قسمت 1

استفاده از Pulumi و زبان های برنامه نویسی همه منظوره برای کد زیرساخت (Infrastructure as Code) مزایای زیادی را به همراه دارد: در دسترس بودن مهارت ها و دانش، حذف boilerplate در کد از طریق انتزاع، ابزارهای آشنا برای تیم شما، مانند IDE ها و linters. همه این ابزارهای مهندسی نرم افزار نه تنها ما را بهره وری می کنند، بلکه کیفیت کد ما را نیز بهبود می بخشند. بنابراین، طبیعی است که استفاده از زبان های برنامه نویسی همه منظوره به ما اجازه می دهد تا یک روش مهم توسعه نرم افزار را معرفی کنیم - تست.

در این مقاله، ما به این خواهیم پرداخت که چگونه Pulumi به ما کمک می کند زیرساخت به عنوان کد خود را آزمایش کنیم.

تست زیرساخت به عنوان کد با Pulumi. قسمت 1

چرا زیرساخت را آزمایش کنیم؟

قبل از پرداختن به جزئیات، ارزش این را دارد که این سؤال را بپرسیم: "اصلاً چرا زیرساخت را آزمایش کنیم؟" دلایل زیادی برای این امر وجود دارد که در اینجا به برخی از آنها اشاره می کنیم:

  • تست واحد توابع یا قطعاتی از منطق برنامه شما
  • وضعیت مطلوب زیرساخت را در برابر محدودیت های خاص تأیید می کند.
  • تشخیص خطاهای رایج، مانند عدم رمزگذاری سطل ذخیره سازی یا دسترسی باز و محافظت نشده از اینترنت به ماشین های مجازی.
  • بررسی اجرای تامین زیرساخت.
  • انجام تست زمان اجرا منطق برنامه در حال اجرا در زیرساخت "برنامه ریزی شده" شما برای بررسی عملکرد پس از تهیه.
  • همانطور که می بینیم، طیف گسترده ای از گزینه های تست زیرساخت وجود دارد. Polumi مکانیسم هایی برای آزمایش در هر نقطه از این طیف دارد. بیایید شروع کنیم و ببینیم چگونه کار می کند.

تست واحد

برنامه های Pulumi در زبان های برنامه نویسی همه منظوره مانند جاوا اسکریپت، پایتون، تایپ اسکریپت یا Go نوشته می شوند. بنابراین قدرت کامل این زبان ها از جمله ابزارها و کتابخانه های آنها از جمله چارچوب های آزمایشی در دسترس آنهاست. Pulumi چند ابری است، به این معنی که می توان از آن برای آزمایش از هر ارائه دهنده ابری استفاده کرد.

(در این مقاله با وجود چند زبانه بودن و چند ابری بودن، از جاوا اسکریپت و موکا استفاده می کنیم و روی AWS تمرکز می کنیم. می توانید از پایتون استفاده کنید. unittest، چارچوب تست برو یا هر چارچوب تست دیگری که دوست دارید. و البته، 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 و یک نمونه را اختصاص می دهد. با این حال، باید توجه داشت که در اینجا ما هر سه قانون ذکر شده در بالا را زیر پا می گذاریم. بیایید تست بنویسیم!

تست های نوشتاری

ساختار کلی تست‌های ما شبیه تست‌های معمولی موکا خواهد بود:

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 خروجی هستند و بسیاری از آنها به صورت ناهمزمان ارزیابی می شوند، برای دسترسی به مقادیر باید از روش application استفاده کنیم. این بسیار شبیه به وعده و عمل است then .
  • از آنجایی که ما از چندین ویژگی برای نشان دادن URN منبع در پیام خطا استفاده می کنیم، باید از تابع استفاده کنیم pulumi.allبرای ترکیب آنها.
  • در نهایت، از آنجایی که این مقادیر به‌صورت ناهمزمان محاسبه می‌شوند، باید از ویژگی پاسخگویی غیرهمگام داخلی Mocha استفاده کنیم. 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();
                }
            });
        });

و در آخر بیایید تست سوم را بنویسیم. این کمی پیچیده‌تر خواهد بود زیرا ما به دنبال قوانین ورود به سیستم مرتبط با گروه امنیتی هستیم که می‌تواند تعداد زیادی از آنها وجود داشته باشد، و محدوده 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 از pulimi CLI (واسط خط فرمان) استفاده می‌شود که زمان اجرای زبان را پیکربندی می‌کند، راه‌اندازی موتور Pulumi را کنترل می‌کند تا عملیات با منابع را بتوان ثبت کرد و در برنامه گنجاند و غیره. با این حال، یک مشکل وجود دارد. هنگامی که تحت کنترل چارچوب تست شما اجرا می شود، هیچ ارتباطی بین CLI و موتور Pulumi وجود نخواهد داشت.

برای حل این مشکل فقط باید موارد زیر را مشخص کنیم:

  • نام پروژه که در متغیر محیطی موجود است PULUMI_NODEJS_PROJECT (یا به طور کلی تر، PULUMI__PROJECT для других языков).
    نام پشته ای که در متغیر محیطی مشخص شده است PULUMI_NODEJS_STACK (یا به طور کلی تر، PULUMI__ STACK).
    متغیرهای پیکربندی پشته شما. آنها را می توان با استفاده از یک متغیر محیطی به دست آورد PULUMI_CONFIG و فرمت آنها نقشه JSON با جفت کلید/مقدار است.

    برنامه هشدارهایی صادر می کند که نشان می دهد اتصال به CLI/موتور در حین اجرا در دسترس نیست. این مهم است زیرا برنامه شما در واقع هیچ چیزی را اجرا نمی کند و ممکن است تعجب آور باشد اگر این چیزی نباشد که شما قصد انجام آن را داشتید! برای اینکه به پولومی بگویید این دقیقا همان چیزی است که نیاز دارید، می توانید نصب کنید PULUMI_TEST_MODE в true.

    تصور کنید باید نام پروژه را در آن مشخص کنیم my-ws، نام پشته dev، و منطقه AWS 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)

    همه چیز خوب پیش رفت... هورای! ✓✓✓

    این همه برای امروز است، اما در قسمت دوم ترجمه در مورد آزمایش استقرار صحبت خواهیم کرد.

منبع: www.habr.com

اضافه کردن نظر