朋友们,下午好。 期待
“ DevOps实践和工具”课程上有新的开端,
我们正在与您分享新的翻译。 走吧

将Pulumi和通用编程语言用于基础结构作为代码具有许多优点:拥有技能和知识,通过抽象消除代码中的样板,团队熟悉的工具,例如IDE和linters。 所有这些软件工程工具不仅使我们工作效率更高,而且还提高了代码质量。 因此,使用通用编程语言自然可以使您在软件开发-
测试中引入另一种重要实践。
在本文中,我们将研究Pulumi如何帮助测试“基础结构即代码”。

为什么要测试基础架构?
在详细介绍之前,值得提出一个问题:“为什么我们需要对基础设施进行全面测试?” 造成这种情况的原因有很多,其中有一些原因:
- 对程序中的各个功能或逻辑部分进行单元测试
- 检查所需的基础结构状态,以确保符合某些限制。
- 检测常见错误,例如缺少存储桶加密或不安全,可以从Internet开放访问虚拟机。
- 验证基础架构配置。
- 对在“编程”基础架构中运行的应用程序的逻辑执行运行时测试,以在置备后检查运行状况。
- 我们可以看到,有各种各样的基础架构测试选项。 Polumi具有在此频谱中每个点进行测试的机制。 让我们开始,看看它是如何工作的。
单元测试
Pulumi程序是使用通用编程语言(例如JavaScript,Python,TypeScript或Go)创建的。 因此,它们可以使用这些语言的全部功能,包括其工具和库,包括测试框架。 Pulumi是多云的,这意味着可以使用任何云提供商进行测试。
(在本文中,尽管是多语言和多云的,但我们还是使用JavaScript和Mocha并专注于AWS。您可以使用Python
unittest
,Go测试框架或您喜欢的任何其他测试框架。当然,Pulumi与Azure,Google Cloud, Kubernetes。)
正如我们已经看到的,有几个原因可能导致您需要测试基础结构代码。 其中之一是通常的单元测试。 由于您的代码可能具有某些功能-例如,计算CIDR,动态计算名称,标签等。 -您可能要测试它们。 这与使用您喜欢的编程语言为应用程序编写常规单元测试相同。
如果您使事情复杂化,可以检查程序如何分配资源。 为了说明,假设我们需要创建一个简单的EC2服务器,并且需要确保以下几点:
- 实例具有
Name
标签。 - 实例不应使用userData内联脚本-我们必须使用AMI(图像)。
- Internet上不应打开SSH。
本示例是基于
我的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 ],
这是基本的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() {
现在让我们编写第一个测试:确保实例具有
Name
标记。 为了验证这一点,我们只需获取EC2实例对象并检查相应的
tags
属性:
它看起来像是常规测试,但有一些功能值得关注:
- 由于我们在部署之前要求资源的状态,因此我们的测试始终以“计划”(或“预览”)模式执行,因此,许多属性的值将根本不会被接收或无法确定,其中包括计算出的所有输出属性您的云提供商。对于我们的测试,这是正常的-我们仅检查输入数据。稍后,在进行集成测试时,我们将再次讨论此问题。
- 由于Pulumi资源的所有属性都是“输出”,并且其中许多都是异步计算的,因此我们需要使用apply方法访问值。 这和诺言非常相似。
- 由于我们使用几个属性来在错误消息中显示资源URN,因此我们需要使用
pulumi.all
函数将它们组合在一起。 - 最后,由于这些值是异步计算的,因此我们需要将Mocha的内置异步功能与
done
回调或promise返回一起使用。
配置完所有内容后,我们将可以使用简单的JavaScript值访问输入数据。
tags
属性是一个映射(关联数组),因此我们只需确保它是(1)不为false,并且(2)存在
Name
的键。 这很简单,现在我们可以检查任何东西!
现在让我们写第二张支票。 这甚至更简单:
最后,我们将编写第三个测试。 这将稍微复杂一点,因为我们正在寻找与安全组关联的登录规则,该规则可能很多,而这些规则中的CIDR范围也可能很多。 但是我们设法:
仅此而已。 现在开始运行测试!运行测试
在大多数情况下,您可以使用所选的测试框架以通常的方式运行测试。 但是,您应该注意Pulumi的一项功能。
通常,Pulimi CLI(命令行界面,命令行界面)用于启动Pulumi程序,该程序设置语言的运行时,控制Pulumi引擎的启动,以便可以记录资源操作并将其包含在计划中,等等。 但是,有一个问题。 在测试框架的控制下启动时,CLI和Pulumi引擎之间将没有连接。
为了解决这个问题,我们只需要指定以下内容:
- 项目的名称,包含在环境变量
PULUMI_NODEJS_PROJECT
(或更PULUMI__PROJECT ).
, PULUMI__PROJECT ).
, PULUMI__PROJECT ).
在环境变量PULUMI_NODEJS_STACK
(或更PULUMI__ STACK).
指定的堆栈名称PULUMI__ STACK).
您的堆栈配置变量。 可以使用PULUMI_CONFIG
环境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 ],
然后重新运行测试:
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)
一切进展顺利...万岁! ✓✓✓
今天就这些了,但是我们将在翻译的第二部分中讨论测试部署;-)