Menguji Infrastruktur sebagai Kod dengan Pulumi. Bahagian 1

Selamat petang kawan-kawan. Dalam jangkaan permulaan aliran baharu pada kadar "Amalan dan alatan DevOps" Kami berkongsi dengan anda terjemahan baharu. Pergi.

Menguji Infrastruktur sebagai Kod dengan Pulumi. Bahagian 1

Menggunakan Pulumi dan bahasa pengaturcaraan tujuan umum untuk kod infrastruktur (Infrastruktur sebagai Kod) memberikan banyak kelebihan: ketersediaan kemahiran dan pengetahuan, penghapusan boilerplate dalam kod melalui abstraksi, alat yang biasa digunakan oleh pasukan anda, seperti IDE dan linter. Semua alat kejuruteraan perisian ini bukan sahaja menjadikan kami lebih produktif, tetapi juga meningkatkan kualiti kod kami. Oleh itu, adalah wajar bahawa penggunaan bahasa pengaturcaraan tujuan umum membolehkan kami memperkenalkan satu lagi amalan pembangunan perisian penting - ujian.

Dalam artikel ini, kami akan melihat cara Pulumi membantu kami menguji infrastruktur-sebagai-kod kami.

Menguji Infrastruktur sebagai Kod dengan Pulumi. Bahagian 1

Mengapa menguji infrastruktur?

Sebelum terperinci, patut ditanya soalan: "Mengapa menguji infrastruktur sama sekali?" Terdapat banyak sebab untuk ini dan berikut adalah beberapa daripadanya:

  • Ujian unit fungsi individu atau serpihan logik program anda
  • Mengesahkan keadaan infrastruktur yang dikehendaki terhadap kekangan tertentu.
  • Pengesanan ralat biasa, seperti kekurangan penyulitan baldi storan atau tidak dilindungi, membuka akses dari Internet ke mesin maya.
  • Menyemak pelaksanaan penyediaan infrastruktur.
  • Menjalankan ujian masa jalan bagi logik aplikasi yang berjalan di dalam infrastruktur "diprogramkan" anda untuk menyemak kefungsian selepas peruntukan.
  • Seperti yang kita lihat, terdapat pelbagai pilihan ujian infrastruktur. Polumi mempunyai mekanisme untuk ujian pada setiap titik pada spektrum ini. Mari mulakan dan lihat cara ia berfungsi.

Ujian unit

Program Pulumi ditulis dalam bahasa pengaturcaraan tujuan umum seperti JavaScript, Python, TypeScript atau Go. Oleh itu, kuasa penuh bahasa ini, termasuk alatan dan perpustakaan mereka, termasuk rangka kerja ujian, tersedia untuk mereka. Pulumi adalah berbilang awan, yang bermaksud ia boleh digunakan untuk ujian daripada mana-mana pembekal awan.

(Dalam artikel ini, walaupun berbilang bahasa dan multicloud, kami menggunakan JavaScript dan Mocha dan menumpukan pada AWS. Anda boleh menggunakan Python unittest, Rangka kerja ujian Go atau mana-mana rangka kerja ujian lain yang anda suka. Dan, sudah tentu, Pulumi berfungsi hebat dengan Azure, Google Cloud, Kubernetes.)

Seperti yang telah kita lihat, terdapat beberapa sebab mengapa anda mungkin ingin menguji kod infrastruktur anda. Salah satunya adalah ujian unit konvensional. Kerana kod anda mungkin mempunyai fungsi - contohnya, untuk mengira CIDR, mengira nama, teg, dsb. - anda mungkin mahu mengujinya. Ini adalah sama seperti menulis ujian unit biasa untuk aplikasi dalam bahasa pengaturcaraan kegemaran anda.
Untuk menjadi lebih rumit, anda boleh menyemak cara program anda memperuntukkan sumber. Untuk menggambarkan, mari bayangkan bahawa kita perlu mencipta pelayan EC2 yang mudah dan kami ingin memastikan perkara berikut:

  • Kejadian mempunyai teg Name.
  • Kejadian tidak boleh menggunakan skrip sebaris userData - kita mesti menggunakan AMI (imej).
  • Seharusnya tiada SSH yang terdedah kepada Internet.

Contoh ini berdasarkan contoh saya 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;

Ini ialah program Pulumi asas: ia hanya memperuntukkan kumpulan keselamatan EC2 dan contoh. Walau bagaimanapun, perlu diingatkan bahawa di sini kita melanggar ketiga-tiga peraturan yang dinyatakan di atas. Mari tulis ujian!

Ujian menulis

Struktur umum ujian kami akan kelihatan seperti ujian Mocha biasa:

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, ΠΎΡ‚ΠΊΡ€Ρ‹Ρ‚ΠΎΠ³ΠΎ Π² Π˜Π½Ρ‚Π΅Ρ€Π½Π΅Ρ‚.
    });
});

Sekarang mari tulis ujian pertama kami: pastikan kejadian mempunyai teg Name. Untuk menyemak ini, kami hanya mendapatkan objek contoh EC2 dan semak sifat yang sepadan 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();
                }
            });
        });

Ia kelihatan seperti ujian biasa, tetapi dengan beberapa ciri yang perlu diberi perhatian:

  • Kerana kami menanyakan keadaan sumber sebelum penggunaan, ujian kami sentiasa dijalankan dalam mod "pelan" (atau "pratonton"). Oleh itu, terdapat banyak sifat yang nilainya tidak akan diambil atau tidak akan ditakrifkan. Ini termasuk semua sifat output yang dikira oleh pembekal awan anda. Ini adalah perkara biasa untuk ujian kami - kami hanya menyemak data input. Kami akan kembali kepada isu ini kemudian, apabila ia datang kepada ujian penyepaduan.
  • Memandangkan semua sifat sumber Pulumi adalah output, dan kebanyakannya dinilai secara tak segerak, kita perlu menggunakan kaedah guna untuk mengakses nilai. Ini sangat serupa dengan janji dan fungsi then .
  • Memandangkan kami menggunakan beberapa sifat untuk menunjukkan URN sumber dalam mesej ralat, kami perlu menggunakan fungsi tersebut pulumi.alluntuk menggabungkan mereka.
  • Akhir sekali, memandangkan nilai ini dikira secara tak segerak, kita perlu menggunakan ciri panggil balik async terbina dalam Mocha done atau membalas janji.

Setelah kami menyediakan segala-galanya, kami akan mempunyai akses kepada input sebagai nilai JavaScript yang mudah. Harta benda tags ialah peta (tatasusunan bersekutu), jadi kami hanya akan memastikan ia (1) tidak palsu, dan (2) terdapat kunci untuk Name. Ia sangat mudah dan kini kami boleh menguji apa sahaja!

Sekarang mari kita tulis cek kedua kita. Ia lebih mudah:

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

Dan akhirnya, mari kita tulis ujian ketiga. Ini akan menjadi lebih rumit sedikit kerana kami sedang mencari peraturan log masuk yang dikaitkan dengan kumpulan keselamatan, yang mungkin terdapat banyak, dan julat CIDR dalam peraturan tersebut, yang boleh juga banyak. Tetapi kami berjaya:

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

Itu sahaja. Sekarang mari jalankan ujian!

Menjalankan ujian

Dalam kebanyakan kes, anda boleh menjalankan ujian dengan cara biasa, menggunakan rangka kerja ujian pilihan anda. Tetapi ada satu ciri Pulumi yang patut diberi perhatian.
Biasanya, untuk menjalankan program Pulumi, CLI (antara muka Talian Perintah) cili digunakan, yang mengkonfigurasi masa jalan bahasa, mengawal pelancaran enjin Pulumi supaya operasi dengan sumber boleh direkodkan dan dimasukkan ke dalam pelan, dsb. Walau bagaimanapun, terdapat satu masalah. Apabila berjalan di bawah kawalan rangka kerja ujian anda, tidak akan ada komunikasi antara CLI dan enjin Pulumi.

Untuk mengatasi masalah ini, kami hanya perlu menentukan perkara berikut:

  • Nama projek, yang terkandung dalam pembolehubah persekitaran PULUMI_NODEJS_PROJECT (atau, lebih umum, PULUMI__PROJECT для Π΄Ρ€ΡƒΠ³ΠΈΡ… языков).
    Nama timbunan yang dinyatakan dalam pembolehubah persekitaran PULUMI_NODEJS_STACK (atau, lebih umum, PULUMI__ STACK).
    Pembolehubah konfigurasi tindanan anda. Mereka boleh didapati menggunakan pembolehubah persekitaran PULUMI_CONFIG dan formatnya ialah peta JSON dengan pasangan kunci/nilai.

    Program ini akan mengeluarkan amaran yang menunjukkan bahawa sambungan kepada CLI/enjin tidak tersedia semasa pelaksanaan. Ini penting kerana program anda sebenarnya tidak akan menggunakan apa-apa dan ia mungkin mengejutkan jika bukan itu yang anda ingin lakukan! Untuk memberitahu Pulumi bahawa ini adalah apa yang anda perlukan, anda boleh memasang PULUMI_TEST_MODE Π² true.

    Bayangkan kita perlu menentukan nama projek dalam my-ws, nama timbunan dev, dan Wilayah AWS us-west-2. Baris arahan untuk menjalankan ujian Mocha akan kelihatan seperti ini:

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

    Melakukan ini, seperti yang dijangka, akan menunjukkan kepada kita bahawa kita mempunyai tiga ujian yang gagal!

    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

    Mari kita betulkan program kami:

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

    Dan kemudian jalankan ujian sekali lagi:

    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)

    Semuanya berjalan lancar... Hore! βœ“βœ“βœ“

    Itu sahaja untuk hari ini, tetapi kita akan bercakap tentang ujian penggunaan dalam bahagian kedua terjemahan πŸ˜‰

Sumber: www.habr.com

Tambah komen