使用 Pulumi 測試基礎設施即程式碼。 第1部分

朋友們下午好。 預計新的流量將以以下速度開始 “DevOps 實踐和工具” 我們正在與您分享一個新的翻譯。 去。

使用 Pulumi 測試基礎設施即程式碼。 第1部分

使用 Pulumi 和通用程式語言進行基礎架構程式碼(基礎架構即程式碼)提供了許多優勢:技能和知識的可用性、透過抽象消除程式碼中的樣板、團隊熟悉的工具,例如 IDE 和 linter。 所有這些軟體工程工具不僅提高了我們的工作效率,而且還提高了程式碼的品質。 因此,使用通用程式語言很自然地讓我們引入了另一個重要的軟體開發實踐—— 測試.

在本文中,我們將了解 Pulumi 如何幫助我們測試基礎設施即程式碼。

使用 Pulumi 測試基礎設施即程式碼。 第1部分

為什麼要測試基礎設施?

在詳細討論之前,有必要問一個問題:“為什麼要測試基礎設施?” 造成這種情況的原因有很多,以下是其中的一些原因:

  • 對程式邏輯的各個函數或片段進行單元測試
  • 根據某些限制驗證基礎設施的所需狀態。
  • 偵測常見錯誤,例如儲存桶缺乏加密或未受保護的、從網路到虛擬機器的開放存取。
  • 檢查基礎設施配置落實。
  • 對「編程」基礎架構內運行的應用程式邏輯執行運行時測試,以在配置後檢查功能。
  • 正如我們所看到的,基礎設施測試有多種選擇。 Polumi 擁有針對該範圍內每個點進行測試的機制。 讓我們開始看看它是如何工作的。

單元測試

Pulumi 程式是用 JavaScript、Python、TypeScript 或 Go 等通用程式語言編寫的。 因此,他們可以使用這些語言的全部功能,包括它們的工具和函式庫,包括測試框架。 Pulumi 是多雲的,這意味著它可以用於任何雲端提供者的測試。

(在本文中,儘管是多語言和多雲,但我們使用 JavaScript 和 Mocha 並專注於 AWS。您可以使用 Python unittest、Go 測試框架或您喜歡的任何其他測試框架。 當然,Pulumi 可以很好地與 Azure、Google Cloud、Kubernetes 搭配使用。)

正如我們所看到的,您可能想要測試基礎設施程式碼的原因有很多。 其中之一是傳統的單元測試。 因為你的程式碼可能有函數──例如,計算CIDR、動態計算名稱、標籤等。 - 你可能想測試它們。 這與用您最喜歡的程式語言為應用程式編寫常規單元測試相同。
更複雜一點的是,您可以檢查程式如何分配資源。 為了說明這一點,我們假設我們需要建立一個簡單的 EC2 伺服器,並且我們希望確定以下幾點:

  • 實例有一個標籤 Name.
  • 實例不應使用內聯腳本 userData - 我們必須使用 AMI(圖像)。
  • 不應將 SSH 暴露於 Internet。

這個例子是基於 我的範例 aws-js-webserver:

索引.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 安全群組和一個執行個體。 然而,應該指出的是,我們在這裡違反了上述所有三個規則。 讓我們來寫測試吧!

編寫測試

我們測試的整體結構將類似於常規的 Mocha 測試:

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 資源屬性都是輸出,並且其中許多屬性是非同步計算的,因此我們需要使用 apply 方法來存取這些值。 這與 Promise 和 afunction 非常相似 then .
  • 由於我們使用多個屬性來顯示錯誤訊息中的資源 URN,因此我們需要使用該函數 pulumi.all將它們結合起來。
  • 最後,由於這些值是非同步計算的,所以我們需要使用Mocha內建的非同步回呼功能 done 或返回一個承諾。

一旦我們完成所有設置,我們就可以以簡單的 JavaScript 值的形式存取輸入。 財產 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有一個特點值得關注。
通常,要執行 Pulumi 程序,需要使用 pulimi CLI(命令列介面),它可以配置語言運行時、控制 Pulumi 引擎的啟動,以便記錄對資源的操作並將其包含在計劃中等。 然而,有一個問題。 當在測試框架的控制下運作時,CLI 和 Pulumi 引擎之間不會進行通訊。

為了解決這個問題,我們只需要指定以下內容:

  • 項目名稱,包含在環境變數中 PULUMI_NODEJS_PROJECT (或者,更一般地說, PULUMI__PROJECT для других языков).
    環境變數中指定的堆疊名稱 PULUMI_NODEJS_STACK (或者,更一般地說, PULUMI__ STACK).
    您的堆疊配置變數。 可以使用環境變數來取得它們 PULUMI_CONFIG 它們的格式是帶有鍵/值對的 JSON 映射。

    程式將發出警告,指示在執行期間與 CLI/引擎的連線不可用。 這很重要,因為您的程式實際上不會部署任何東西,如果這不是您想要做的,您可能會感到驚訝! 要告訴 Pulumi 這正是您所需要的,您可以安裝 PULUMI_TEST_MODE в true.

    想像一下我們需要在中指定項目名稱 my-ws, 堆疊名稱 dev以及 AWS 區域 us-west-2。 執行 Mocha 測試的命令列如下所示:

    $ 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

添加評論