Menguji Infrastruktur sebagai Kode dengan Pulumi. Bagian 1

Selamat siang teman teman. Untuk mengantisipasi dimulainya aliran baru pada tingkat tersebut "Praktik dan alat DevOps" Kami berbagi dengan Anda terjemahan baru. Pergi.

Menguji Infrastruktur sebagai Kode dengan Pulumi. Bagian 1

Menggunakan Pulumi dan bahasa pemrograman tujuan umum untuk kode infrastruktur (Infrastruktur sebagai Kode) memberikan banyak keuntungan: ketersediaan keterampilan dan pengetahuan, penghapusan boilerplate dalam kode melalui abstraksi, alat yang familiar bagi tim Anda, seperti IDE dan linter. Semua alat rekayasa perangkat lunak ini tidak hanya membuat kita lebih produktif, namun juga meningkatkan kualitas kode kita. Oleh karena itu, wajar jika penggunaan bahasa pemrograman tujuan umum memungkinkan kita memperkenalkan praktik pengembangan perangkat lunak penting lainnya - pengujian.

Dalam artikel ini, kita akan melihat bagaimana Pulumi membantu kita menguji infrastruktur sebagai kode.

Menguji Infrastruktur sebagai Kode dengan Pulumi. Bagian 1

Mengapa menguji infrastruktur?

Sebelum membahas secara rinci, ada baiknya mengajukan pertanyaan: “Mengapa menguji infrastruktur?” Ada banyak alasan untuk hal ini dan berikut beberapa di antaranya:

  • Pengujian unit fungsi individual atau fragmen logika program Anda
  • Memverifikasi keadaan infrastruktur yang diinginkan terhadap batasan tertentu.
  • Deteksi kesalahan umum, seperti kurangnya enkripsi pada keranjang penyimpanan atau akses terbuka yang tidak terlindungi dari Internet ke mesin virtual.
  • Memeriksa pelaksanaan penyediaan infrastruktur.
  • Melakukan pengujian runtime logika aplikasi yang berjalan di dalam infrastruktur "terprogram" Anda untuk memeriksa fungsionalitas setelah penyediaan.
  • Seperti yang bisa kita lihat, ada beragam pilihan pengujian infrastruktur. Polumi memiliki mekanisme pengujian di setiap titik pada spektrum ini. Mari kita mulai dan lihat cara kerjanya.

Pengujian satuan

Program Pulumi ditulis dalam bahasa pemrograman tujuan umum seperti JavaScript, Python, TypeScript atau Go. Oleh karena itu, kekuatan penuh dari bahasa-bahasa ini, termasuk alat dan perpustakaannya, termasuk kerangka pengujian, tersedia untuk mereka. Pulumi bersifat multi-cloud, artinya dapat digunakan untuk pengujian dari penyedia cloud mana pun.

(Dalam artikel ini, meskipun multibahasa dan multicloud, kami menggunakan JavaScript dan Mocha dan fokus pada AWS. Anda dapat menggunakan Python unittest, Go test framework, atau framework pengujian lainnya yang Anda suka. Dan, tentu saja, Pulumi berfungsi baik dengan Azure, Google Cloud, Kubernetes.)

Seperti yang telah kita lihat, ada beberapa alasan mengapa Anda mungkin ingin menguji kode infrastruktur Anda. Salah satunya adalah pengujian unit konvensional. Karena kode Anda mungkin memiliki fungsi - misalnya, untuk menghitung CIDR, menghitung nama, tag secara dinamis, dll. - Anda mungkin ingin mengujinya. Ini sama dengan menulis pengujian unit reguler untuk aplikasi dalam bahasa pemrograman favorit Anda.
Untuk lebih rumitnya, Anda dapat memeriksa bagaimana program Anda mengalokasikan sumber daya. Sebagai ilustrasi, bayangkan kita perlu membuat server EC2 sederhana dan kita ingin memastikan hal-hal berikut:

  • Instans memiliki tag Name.
  • Instance tidak boleh menggunakan skrip inline userData - kita harus menggunakan AMI (gambar).
  • Seharusnya tidak ada SSH yang terekspos ke Internet.

Contoh ini didasarkan pada contoh saya aws-js-webserver:

indeks.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 adalah program dasar Pulumi: program ini hanya mengalokasikan grup keamanan EC2 dan sebuah instans. Namun, perlu dicatat bahwa di sini kita melanggar ketiga aturan di atas. Ayo menulis tes!

Tes menulis

Struktur umum pengujian kami akan terlihat seperti pengujian 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 kita tulis pengujian pertama kita: pastikan instance tersebut memiliki tag Name. Untuk memeriksanya kita cukup mendapatkan objek instance EC2 dan memeriksa properti yang sesuai 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();
                }
            });
        });

Ini terlihat seperti tes biasa, tetapi dengan beberapa fitur yang perlu diperhatikan:

  • Karena kami menanyakan status sumber daya sebelum penerapan, pengujian kami selalu dijalankan dalam mode “rencana” (atau “pratinjau”). Jadi, ada banyak properti yang nilainya tidak dapat diambil atau ditentukan. Ini mencakup semua properti keluaran yang dihitung oleh penyedia cloud Anda. Ini normal untuk pengujian kami - kami hanya memeriksa data masukan. Kami akan kembali ke masalah ini nanti, terkait pengujian integrasi.
  • Karena semua properti sumber daya Pulumi adalah keluaran, dan banyak di antaranya dievaluasi secara asinkron, kita perlu menggunakan metode apply untuk mengakses nilainya. Ini sangat mirip dengan janji dan fungsi then .
  • Karena kita menggunakan beberapa properti untuk menampilkan URN sumber daya dalam pesan kesalahan, kita perlu menggunakan fungsi tersebut pulumi.alluntuk menggabungkannya.
  • Terakhir, karena nilai-nilai ini dihitung secara asinkron, kita perlu menggunakan fitur panggilan balik asinkron bawaan Mocha done atau mengembalikan janji.

Setelah kami menyiapkan semuanya, kami akan memiliki akses ke input sebagai nilai JavaScript sederhana. Properti tags adalah peta (array asosiatif), jadi kami pastikan saja (1) tidak salah, dan (2) ada kunci untuk Name. Ini sangat sederhana dan sekarang kita bisa menguji apa saja!

Sekarang mari kita tulis cek kedua kita. Ini bahkan lebih sederhana:

 // 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 terakhir, mari kita menulis tes ketiga. Ini akan menjadi sedikit lebih rumit karena kita mencari aturan login yang terkait dengan grup keamanan, yang jumlahnya bisa banyak, dan rentang CIDR dalam aturan tersebut, yang juga bisa banyak. Tapi kami berhasil:

    // 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 saja. Sekarang mari kita jalankan tesnya!

Menjalankan tes

Dalam kebanyakan kasus, Anda dapat menjalankan pengujian dengan cara biasa, menggunakan kerangka pengujian pilihan Anda. Namun ada satu fitur Pulumi yang patut diperhatikan.
Biasanya, untuk menjalankan program Pulumi, CLI (antarmuka Baris Perintah) pulimi digunakan, yang mengonfigurasi runtime bahasa, mengontrol peluncuran mesin Pulumi sehingga operasi dengan sumber daya dapat direkam dan disertakan dalam rencana, dll. Namun, ada satu masalah. Saat berjalan di bawah kendali kerangka pengujian Anda, tidak akan ada komunikasi antara CLI dan mesin Pulumi.

Untuk mengatasi masalah ini, kita hanya perlu menentukan hal berikut:

  • Nama proyek, yang terdapat dalam variabel lingkungan PULUMI_NODEJS_PROJECT (atau, lebih umum lagi, PULUMI__PROJECT для других языков).
    Nama tumpukan yang ditentukan dalam variabel lingkungan PULUMI_NODEJS_STACK (atau, lebih umum lagi, PULUMI__ STACK).
    Variabel konfigurasi tumpukan Anda. Mereka dapat diperoleh dengan menggunakan variabel lingkungan PULUMI_CONFIG dan formatnya adalah peta JSON dengan pasangan kunci/nilai.

    Program akan mengeluarkan peringatan yang menunjukkan bahwa koneksi ke CLI/mesin tidak tersedia selama eksekusi. Hal ini penting karena program Anda tidak akan benar-benar menerapkan apa pun dan mungkin akan mengejutkan jika Anda tidak ingin melakukan hal tersebut! Untuk memberi tahu Pulumi bahwa inilah yang Anda perlukan, Anda dapat menginstal PULUMI_TEST_MODE в true.

    Bayangkan kita perlu menentukan nama proyek di my-ws, nama tumpukan dev, dan Wilayah AWS us-west-2. Baris perintah untuk menjalankan tes Mocha akan terlihat 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 hal ini, seperti yang diharapkan, akan menunjukkan kepada kita bahwa kita memiliki tiga pengujian 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 perbaiki program kita:

    "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 tes 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 baik... Hore! ✓✓✓

    Itu saja untuk hari ini, tapi kita akan membahas tentang pengujian penerapan di bagian kedua terjemahan 😉

Sumber: www.habr.com

Tambah komentar