สวัสดีตอนบ่ายเพื่อน โดยคาดว่าจะเริ่มมีกระแสใหม่ในอัตราดังกล่าว
การใช้ Pulumi และภาษาการเขียนโปรแกรมทั่วไปสำหรับโค้ดโครงสร้างพื้นฐาน (โครงสร้างพื้นฐานเป็นโค้ด) ให้ข้อดีหลายประการ: ความพร้อมใช้งานของทักษะและความรู้ การขจัดส่วนสำเร็จรูปในโค้ดผ่านทางนามธรรม เครื่องมือที่ทีมของคุณคุ้นเคย เช่น IDE และ linters เครื่องมือทางวิศวกรรมซอฟต์แวร์ทั้งหมดนี้ไม่เพียงแต่ทำให้เรามีประสิทธิผลมากขึ้นเท่านั้น แต่ยังช่วยปรับปรุงคุณภาพของโค้ดของเราด้วย ดังนั้นจึงเป็นเรื่องธรรมดาที่การใช้ภาษาโปรแกรมทั่วไปช่วยให้เราสามารถแนะนำแนวทางปฏิบัติในการพัฒนาซอฟต์แวร์ที่สำคัญอีกอย่างหนึ่งได้ - การทดสอบ.
ในบทความนี้ เราจะดูว่า Pulumi ช่วยเราทดสอบโครงสร้างพื้นฐานตามโค้ดของเราได้อย่างไร
ทำไมต้องทดสอบโครงสร้างพื้นฐาน?
ก่อนที่จะลงรายละเอียด ควรถามคำถาม: “ทำไมต้องทดสอบโครงสร้างพื้นฐานเลย” มีสาเหตุหลายประการสำหรับเรื่องนี้ และนี่คือสาเหตุบางส่วน:
- การทดสอบหน่วยของแต่ละฟังก์ชันหรือส่วนของลอจิกโปรแกรมของคุณ
- ตรวจสอบสถานะโครงสร้างพื้นฐานที่ต้องการโดยเทียบกับข้อจำกัดบางประการ
- การตรวจจับข้อผิดพลาดทั่วไป เช่น การขาดการเข้ารหัสที่เก็บข้อมูลหรือการเข้าถึงแบบเปิดจากอินเทอร์เน็ตไปยังเครื่องเสมือน
- ตรวจสอบการดำเนินการจัดเตรียมโครงสร้างพื้นฐาน
- ทำการทดสอบรันไทม์ของตรรกะของแอปพลิเคชันที่ทำงานภายในโครงสร้างพื้นฐาน "ที่ตั้งโปรแกรมไว้" ของคุณเพื่อตรวจสอบฟังก์ชันการทำงานหลังจากการจัดเตรียม
- ดังที่เราเห็นแล้วว่ามีตัวเลือกการทดสอบโครงสร้างพื้นฐานที่หลากหลาย Polumi มีกลไกในการทดสอบทุกจุดบนสเปกตรัมนี้ มาเริ่มต้นและดูว่ามันทำงานอย่างไร
การทดสอบหน่วย
โปรแกรม Pulumi เขียนด้วยภาษาโปรแกรมอเนกประสงค์ เช่น JavaScript, Python, TypeScript หรือ Go ดังนั้นภาษาเหล่านี้จึงมีประสิทธิภาพเต็มที่ รวมถึงเครื่องมือและไลบรารี รวมถึงเฟรมเวิร์กการทดสอบด้วย Pulumi เป็นมัลติคลาวด์ ซึ่งหมายความว่าสามารถใช้สำหรับการทดสอบจากผู้ให้บริการคลาวด์รายใดก็ได้
(ในบทความนี้ แม้ว่าจะเป็นหลายภาษาและหลายคลาวด์ แต่เราใช้ JavaScript และ Mocha และมุ่งเน้นไปที่ AWS คุณสามารถใช้ Python unittest
, Go test framework หรือ test framework อื่นๆ ที่คุณชอบ และแน่นอนว่า Pulumi ทำงานได้ดีกับ Azure, Google Cloud, Kubernetes)
ตามที่เราได้เห็นแล้ว มีสาเหตุหลายประการที่คุณอาจต้องการทดสอบโค้ดโครงสร้างพื้นฐานของคุณ หนึ่งในนั้นคือการทดสอบหน่วยแบบธรรมดา เนื่องจากโค้ดของคุณอาจมีฟังก์ชันต่างๆ เช่น ในการคำนวณ CIDR คำนวณชื่อ แท็ก ฯลฯ แบบไดนามิก - คุณอาจต้องการทดสอบพวกเขา สิ่งนี้เหมือนกับการเขียนการทดสอบหน่วยปกติสำหรับแอปพลิเคชันในภาษาการเขียนโปรแกรมที่คุณชื่นชอบ
เพื่อให้ซับซ้อนขึ้นอีกเล็กน้อย คุณสามารถตรวจสอบวิธีที่โปรแกรมของคุณจัดสรรทรัพยากรได้ เพื่อแสดงให้เห็น สมมติว่าเราจำเป็นต้องสร้างเซิร์ฟเวอร์ EC2 แบบธรรมดา และเราต้องการความมั่นใจในสิ่งต่อไปนี้:
- อินสแตนซ์มีแท็ก
Name
. - อินสแตนซ์ไม่ควรใช้สคริปต์อินไลน์
userData
- เราต้องใช้ AMI (รูปภาพ) - ไม่ควรมีการเปิดเผย SSH บนอินเทอร์เน็ต
ตัวอย่างนี้มีพื้นฐานมาจาก
ดัชนี.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 ทั้งหมดเป็นเอาต์พุต และคุณสมบัติส่วนใหญ่ได้รับการประเมินแบบอะซิงโครนัส เราจึงจำเป็นต้องใช้วิธี Apply เพื่อเข้าถึงค่า สิ่งนี้คล้ายกันมากกับคำสัญญาและการกระทำผิด
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
และภูมิภาค AWSus-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)
ทุกอย่างเป็นไปด้วยดี... ไชโย! ✓✓✓
นั่นคือทั้งหมดสำหรับวันนี้ แต่เราจะพูดถึงการทดสอบการปรับใช้ในส่วนที่สองของการแปล 😉
ที่มา: will.com