Günortanız xeyir dostlar. Məzənnədə yeni axının başlaması ərəfəsində
İnfrastruktur kodu üçün Pulumi və ümumi təyinatlı proqramlaşdırma dillərindən istifadə (Infrastructure as Code) bir çox üstünlüklər təmin edir: bacarıq və biliklərin mövcudluğu, abstraksiya vasitəsilə koddakı boşluqların aradan qaldırılması, komandanıza tanış olan alətlər, məsələn, IDE və linters. Bütün bu proqram mühəndisliyi vasitələri bizi nəinki daha məhsuldar edir, həm də kodun keyfiyyətini artırır. Buna görə də, ümumi təyinatlı proqramlaşdırma dillərinin istifadəsi bizə daha bir vacib proqram təminatının inkişaf etdirilməsi təcrübəsini tətbiq etməyə imkan verməsi təbiidir - test.
Bu yazıda Pulumi-nin kod olaraq infrastrukturumuzu sınamağımıza necə kömək edəcəyinə baxacağıq.
İnfrastruktur niyə sınaqdan keçirilməlidir?
Təfərrüata varmazdan əvvəl sualı verməyə dəyər: "İnfrastruktur ümumiyyətlə sınaqdan keçirilməlidir?" Bunun bir çox səbəbi var və onlardan bəziləri bunlardır:
- Fərdi funksiyaların və ya proqram məntiqinizin fraqmentlərinin vahid testi
- Müəyyən məhdudiyyətlərə qarşı infrastrukturun istənilən vəziyyətini yoxlayır.
- Yaddaş qutusunun şifrələməsinin olmaması və ya İnternetdən virtual maşınlara qorunmayan, açıq giriş kimi ümumi səhvlərin aşkarlanması.
- İnfrastruktur təminatının həyata keçirilməsinin yoxlanılması.
- Təminatdan sonra funksionallığı yoxlamaq üçün “proqramlaşdırılmış” infrastrukturunuzda işləyən tətbiq məntiqinin iş vaxtı testinin həyata keçirilməsi.
- Gördüyümüz kimi, infrastruktur testlərinin geniş çeşidi var. Polumi bu spektrin hər nöqtəsində sınaq üçün mexanizmlərə malikdir. Başlayaq və bunun necə işlədiyini görək.
Vahid sınağı
Pulumi proqramları JavaScript, Python, TypeScript və ya Go kimi ümumi təyinatlı proqramlaşdırma dillərində yazılmışdır. Buna görə də, bu dillərin tam gücü, o cümlədən onların alətləri və kitabxanaları, o cümlədən test çərçivələri onlar üçün əlçatandır. Pulumi çoxlu buluddur, yəni istənilən bulud provayderindən sınaq üçün istifadə edilə bilər.
(Bu məqalədə çoxdilli və çoxbuludlu olmasına baxmayaraq, biz JavaScript və Mocha istifadə edirik və AWS-ə diqqət yetiririk. Siz Python-dan istifadə edə bilərsiniz. unittest
, Test çərçivəsinə və ya bəyəndiyiniz hər hansı digər test çərçivəsinə keçin. Və əlbəttə ki, Pulumi Azure, Google Cloud, Kubernetes ilə əla işləyir.)
Gördüyümüz kimi, infrastruktur kodunuzu yoxlamaq istəməyinizin bir neçə səbəbi var. Onlardan biri şərti vahid testidir. Çünki kodunuz funksiyalara malik ola bilər - məsələn, CIDR-i hesablamaq, adları, teqləri dinamik hesablamaq və s. - Yəqin ki, onları sınamaq istəyəcəksiniz. Bu, sevimli proqramlaşdırma dilində tətbiqlər üçün müntəzəm vahid testləri yazmaqla eynidir.
Bir az daha mürəkkəb olmaq üçün proqramınızın resursları necə ayırdığını yoxlaya bilərsiniz. Nümunə etmək üçün təsəvvür edək ki, sadə EC2 serveri yaratmalıyıq və biz aşağıdakılardan əmin olmaq istəyirik:
- Nümunələrin etiketi var
Name
. - Nümunələr daxili skriptdən istifadə etməməlidir
userData
- biz AMI (şəkil) istifadə etməliyik. - İnternetə məruz qalan SSH olmamalıdır.
Bu nümunə əsaslanır
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;
Bu, əsas Pulumi proqramıdır: o, sadəcə bir EC2 təhlükəsizlik qrupu və bir nümunə ayırır. Ancaq qeyd etmək lazımdır ki, burada yuxarıda qeyd olunan hər üç qaydanı pozuruq. Testlər yazaq!
Yazı testləri
Testlərimizin ümumi quruluşu adi Mocha testlərinə bənzəyəcək:
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, открытого в Интернет.
});
});
İndi ilk testimizi yazaq: nümunələrin etiketinə sahib olduğundan əmin olun Name
. Bunu yoxlamaq üçün biz sadəcə EC2 instansiya obyektini alırıq və müvafiq xassəni yoxlayırıq 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();
}
});
});
Bu, adi bir test kimi görünür, lakin qeyd etməyə dəyər bir neçə xüsusiyyətlə:
- Yerləşdirmədən əvvəl mənbənin vəziyyətini sorğuladığımız üçün testlərimiz həmişə “plan” (və ya “öncədən baxış”) rejimində aparılır. Beləliklə, dəyərləri sadəcə olaraq alınmayacaq və ya müəyyən edilməyəcək bir çox xüsusiyyət var. Buraya bulud provayderiniz tərəfindən hesablanmış bütün çıxış xüsusiyyətləri daxildir. Testlərimiz üçün bu normaldır - biz yalnız daxil olan məlumatları yoxlayırıq. İnteqrasiya testlərinə gəldikdə, bu məsələyə daha sonra qayıdacağıq.
- Bütün Pulumi resurs xassələri çıxış olduğundan və onların bir çoxu asinxron olaraq qiymətləndirilir, biz dəyərlərə daxil olmaq üçün tətbiq metodundan istifadə etməliyik. Bu, vədlərə və funksiyaya çox bənzəyir
then
. - Səhv mesajında resurs URN-ni göstərmək üçün bir neçə xüsusiyyətdən istifadə etdiyimiz üçün funksiyadan istifadə etməliyik
pulumi.all
onları birləşdirmək. - Nəhayət, bu dəyərlər asinxron hesablandığı üçün Mocha-nın daxili asinx geri çağırış xüsusiyyətindən istifadə etməliyik.
done
və ya vədi geri qaytarmaq.
Hər şeyi qurduqdan sonra sadə JavaScript dəyərləri kimi daxiletmələrə giriş əldə edəcəyik. Əmlak tags
xəritədir (assosiativ massiv), ona görə də onun (1) yalan olmadığına və (2) üçün açar olduğuna əmin olacağıq. Name
. Bu, çox sadədir və indi biz hər şeyi sınaya bilərik!
İndi ikinci çekimizi yazaq. Daha sadədir:
// 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();
}
});
});
Və nəhayət, üçüncü testi yazaq. Bu bir az daha mürəkkəb olacaq, çünki biz təhlükəsizlik qrupu ilə əlaqəli giriş qaydalarını axtarırıq, onlardan çox ola bilər və bu qaydalarda CIDR diapazonları da çox ola bilər. Ancaq bacardıq:
// 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();
}
});
});
Hamısı budur. İndi testləri keçirək!
Çalışan testlər
Əksər hallarda, siz seçdiyiniz test çərçivəsindən istifadə edərək, adi şəkildə testlər keçirə bilərsiniz. Ancaq Puluminin diqqət yetirməyə dəyər bir xüsusiyyəti var.
Tipik olaraq, Pulumi proqramlarını işə salmaq üçün pulimi CLI (Command Line interfeysi) istifadə olunur ki, bu da dilin işləmə vaxtını konfiqurasiya edir, Pulumi mühərrikinin işə salınmasına nəzarət edir ki, resurslarla əməliyyatlar qeydə alınsın və plana daxil edilsin və s. Bununla belə, bir problem var. Test çərçivənizin nəzarəti altında işləyərkən CLI və Pulumi mühərriki arasında əlaqə olmayacaq.
Bu problemi həll etmək üçün sadəcə olaraq aşağıdakıları qeyd etməliyik:
- Ətraf dəyişəninin tərkibində olan layihə adı
PULUMI_NODEJS_PROJECT
(və ya daha çox,PULUMI__PROJECT для других языков).
Mühit dəyişənində göstərilən yığının adıPULUMI_NODEJS_STACK
(və ya daha çox,PULUMI__ STACK).
Yığın konfiqurasiya dəyişənləriniz. Onlar mühit dəyişənindən istifadə etməklə əldə edilə bilərPULUMI_CONFIG
və onların formatı açar/dəyər cütləri ilə JSON xəritəsidir.Proqram icra zamanı CLI/mühərriklə əlaqənin mövcud olmadığını göstərən xəbərdarlıqlar verəcək. Bu vacibdir, çünki proqramınız əslində heç bir şey tətbiq etməyəcək və bunu etmək niyyətində deyilsinizsə, bu sürpriz ola bilər! Pulumi-yə bunun sizə lazım olduğunu söyləmək üçün quraşdıra bilərsiniz
PULUMI_TEST_MODE
вtrue
.Təsəvvür edin ki, layihənin adını qeyd etməliyik
my-ws
, yığın adıdev
, və AWS Regionus-west-2
. Mocha testlərini yerinə yetirmək üçün əmr xətti belə görünəcək:$ PULUMI_TEST_MODE=true PULUMI_NODEJS_STACK="my-ws" PULUMI_NODEJS_PROJECT="dev" PULUMI_CONFIG='{ "aws:region": "us-west-2" }' mocha tests.js
Bunu etmək, gözlənildiyi kimi, üç uğursuz testimiz olduğunu göstərəcək!
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
Proqramımızı düzəldək:
"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;
Və sonra testləri yenidən həyata keçirin:
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)
Hər şey yaxşı keçdi... Ura! ✓✓✓
Hamısı bu gündür, lakin tərcümənin ikinci hissəsində yerləşdirmə testi haqqında danışacağıq 😉
Mənbə: www.habr.com