使用 Molecule 和 Podman 开发和测试 Ansible 角色 - 第 2 部分
Molecule 是一个完整的测试框架,可帮助您开发和测试 Ansible 角色,使您能够专注于角色内容,而不是专注于管理测试基础设施。在本系列的第一部分中,我们已成功安装、配置并使用 Molecule 设置了新的测试实例。
现在实例已运行,让我们开始开发新角色并应用 Molecule 以确保它根据规范运行。
此基本角色部署了一个由 Apache Web 服务器支持的 Web 应用程序。它必须支持 Red Hat Enterprise Linux (RHEL) 8 和 Ubuntu 20.04。
使用 Molecule 开发 Ansible 角色
Molecule 通过允许您使用角色内容“收敛”实例来帮助开发阶段。您可以测试每个步骤,而无需担心管理实例和测试环境。它提供快速反馈,使您能够专注于角色内容,确保其在所有平台上都能正常工作。
在本系列的第一部分中,我们初始化了一个名为“mywebapp”的新角色。如果您还没有完成此步骤,请切换到角色目录“mywebapp”并添加第一个任务,使用“package”Ansible 模块安装 Apache 包“httpd”。编辑文件“tasks/main.yaml”并包含此任务
$ vi tasks/main.yml
---
# tasks file for mywebapp
- name: Ensure httpd installed
package:
name: "httpd"
state: present
保存文件并通过运行“molecule converge”来“收敛”实例。“converge”命令将角色的当前版本应用于所有正在运行的容器实例。如果实例已在运行,Molecule “converge”不会重新启动这些实例。它尝试通过使其配置与当前正在测试的角色描述的期望状态相匹配来收敛这些实例。
$ molecule converge
... TRUNCATED OUTPUT ...
TASK [mywebapp : Ensure httpd installed] ***************************************
Saturday 27 June 2020 08:45:01 -0400 (0:00:00.060) 0:00:04.277 *********
fatal: [ubuntu]: FAILED! => {"changed": false, "msg": "No package matching 'httpd' is available"}
changed: [rhel8]
... TRUNCATED OUTPUT ...
请注意,当前版本在 RHEL8 实例上运行良好,但在 Ubuntu 实例上失败了。通过使用 Molecule,您可以快速评估所有平台中任务的结果,并验证角色是否按要求工作!但是,在此示例中,任务失败是因为 Ubuntu 没有名为“httpd”的包。对于该平台,包名为“apache2”。
因此,让我们修改角色以包含每个平台的正确包名称的变量。从 RHEL8 开始,在“vars”子目录下添加一个名为“RedHat.yaml”的文件,内容如下:
$ vi vars/RedHat.yaml
---
httpd_package: httpd
保存此文件并为 Ubuntu 添加相应的“vars/Debian.yaml”文件
$ vi vars/Debian.yaml
---
httpd_package: apache2
保存此文件并修改“tasks/main.yaml”文件以根据 Ansible 通过 系统事实变量“ansible_os_family”识别的操作系统系列包含这些变量文件。我们还必须包含一个任务来更新“Debian”系列中系统的包缓存,因为它们的包管理器会缓存结果,否则会出错。最后,我们更新安装任务以使用您在变量文件中定义的变量“httpd_package”
$ vi tasks/main.yml
- name: Include OS-specific variables.
include_vars: "{{ ansible_os_family }}.yaml"
- name: Ensure package cache up-to-date
apt:
update_cache: yes
cache_valid_time: 3600
when: ansible_os_family == "Debian"
- name: Ensure httpd installed
package:
name: "{{ httpd_package }}"
state: present
保存此文件,然后再次“收敛”实例以确保这次可以正常工作
$ molecule converge
... TRUNCATED OUTPUT ...
TASK [mywebapp : Ensure httpd installed] ***************************************
Saturday 27 June 2020 08:59:13 -0400 (0:00:07.338) 0:00:12.925 *********
ok: [rhel8]
changed: [ubuntu]
... TRUNCATED OUTPUT ...
由于包已安装在 RHEL8 实例中,因此 Ansible 返回状态“OK”并且没有进行任何更改。它这次在 Ubuntu 实例中正确安装了包。
我们已经安装了包 - 但是命名问题也存在于服务本身:它们在 RHEL 和 Ubuntu 中的命名方式不同。因此,我们将服务名称变量添加到 playbook 和变量文件中。从 RHEL8 开始
$ vi vars/RedHat.yaml
---
httpd_package: httpd
httpd_service: httpd
保存此文件,然后编辑 Ubuntu 的“vars/Debian.yaml”文件
$ vi vars/Debian.yaml
---
httpd_package: apache2
httpd_service: apache2
保存文件并在“tasks/main.yml”文件的末尾添加新任务
$ vi tasks/main.yml
- name: Ensure httpd svc started
service:
name: "{{ httpd_service }}"
state: started
enabled: yes
保存文件并再次“收敛”实例以启动 Apache httpd 服务
$ molecule converge
... TRUNCATED OUTPUT ...
TASK [mywebapp : Ensure httpd svc started] *************************************
Saturday 27 June 2020 09:34:38 -0400 (0:00:06.776) 0:00:17.233 *********
changed: [ubuntu]
changed: [rhel8]
... TRUNCATED OUTPUT ...
让我们添加一个最终任务来为 Web 应用程序创建一些内容。每个平台都需要不同组拥有的 HTML 文件。向每个变量文件添加新变量以定义组名称
$ vi vars/RedHat.yaml
---
httpd_package: httpd
httpd_service: httpd
httpd_group: apache
保存此文件,然后编辑 Ubuntu 的“vars/Debian.yaml”文件
$ vi vars/Debian.yaml
---
httpd_package: apache2
httpd_service: apache2
httpd_group: www-data
保存文件并在“tasks/main.yml”文件的末尾添加新任务
$ vi tasks/main.yml
- name: Ensure HTML Index
copy:
dest: /var/www/html/index.html
mode: 0644
owner: root
group: "{{ httpd_group }}"
content: "{{ web_content }}"
此任务允许角色用户在调用角色时使用变量“web_content”来指定内容。如果用户未指定内容,请为该变量添加默认值。
$ vi defaults/main.yml
---
# defaults file for mywebapp
web_content: There's a web server here
保存此文件,并再次“收敛”实例以添加内容。
$ molecule converge
... TRUNCATED OUTPUT ...
TASK [mywebapp : Ensure HTML Index] ********************************************
Saturday 27 June 2020 09:50:11 -0400 (0:00:03.261) 0:00:17.753 *********
changed: [rhel8]
changed: [ubuntu]
... TRUNCATED OUTPUT ...
此时,两个实例都已收敛。使用molecule login命令登录到其中一个实例并运行“curl”命令获取内容,手动验证角色是否有效。
$ molecule login -h rhel8
[root@2ce0a0ea8692 /]# curl https://127.0.0.1
There's a web server here
[root@2ce0a0ea8692 /]# exit
您使用Molecule来辅助角色开发,确保它在每个步骤中都可以在多个平台上正常工作。
接下来,让我们自动化验证过程。
使用Molecule验证角色
除了帮助您收敛实例以辅助角色开发之外,Molecule还可以通过执行验证任务来自动化测试过程。为了验证 playbook 的结果,Molecule 可以使用“testinfra”框架,也可以使用 Ansible 本身。
让我们使用 Ansible Playbook 来验证此新角色的结果。默认情况下,Molecule 提供了一个基本的验证器 playbook“molecule/default/verify.yml”作为起点。此 playbook 包含基本必需的结构,但没有执行任何有用的验证。通过使用 Ansible 的“uri”模块从运行的 Web 服务器获取内容,以及使用“assert”模块确保内容正确,更新此 playbook 以测试此角色的结果。
$ vi molecule/default/verify.yml
---
# This is an example playbook to execute Ansible tests.
- name: Verify
hosts: all
vars:
expected_content: "There's a web server here"
tasks:
- name: Get index.html
uri:
url: https://127.0.0.1
return_content: yes
register: this
failed_when: "expected_content not in this.content"
- name: Ensure content type is text/html
assert:
that:
- "'text/html' in this.content_type"
- name: Debug results
debug:
var: this.content
保存并关闭此文件。运行“molecule verify”验证结果。
$ molecule verify
... TRUNCATED OUTPUT ...
TASK [Ensure content type is text/html] ****************************************
Saturday 27 June 2020 10:03:18 -0400 (0:00:03.131) 0:00:07.255 *********
ok: [rhel8] => {
"changed": false,
"msg": "All assertions passed"
}
ok: [ubuntu] => {
"changed": false,
"msg": "All assertions passed"
}
... TRUNCATED OUTPUT ...
Verifier completed successfully.
Molecule 对所有实例运行验证器 playbook,确保结果与预期值匹配。
您可以通过编辑收敛 playbook 以更新“web_content”变量来更改测试的默认值。
$ vi molecule/default/converge.yml
---
- name: Converge
hosts: all
tasks:
- name: "Include mywebapp"
include_role:
name: "mywebapp"
vars:
web_content: "New content for testing only"
然后,在验证器 playbook 中更新“expected_content”变量。
$ vi molecule/default/verify.yml
---
# This is an example playbook to execute Ansible tests.
- name: Verify
hosts: all
vars:
expected_content: "New content for testing only"
tasks:
再次收敛实例以更新 Web 服务器内容,然后验证结果。
$ molecule converge
... TRUNCATED OUTPUT ...
TASK [mywebapp : Ensure HTML Index] ********************************************
Saturday 27 June 2020 10:09:34 -0400 (0:00:03.331) 0:00:19.607 *********
changed: [rhel8]
changed: [ubuntu]
... TRUNCATED OUTPUT ...
$ molecule verify
... TRUNCATED OUTPUT ...
TASK [Debug results] ***********************************************************
Saturday 27 June 2020 10:10:15 -0400 (0:00:00.299) 0:00:10.142 *********
ok: [rhel8] => {
"this.content": "New content for testing only"
}
ok: [ubuntu] => {
"this.content": "New content for testing only"
}
... TRUNCATED OUTPUT ...
Verifier completed successfully.
使用验证器,您可以定义一个 playbook 来执行检查并确保角色产生所需的结果。
在最后一步中,让我们将所有内容与自动化测试结合起来。
自动化完整的测试工作流
现在所有部分都已组合在一起,请使用命令“molecule test”自动化完整的测试流程工作流。
与辅助角色开发的“molecule converge”不同,“molecule test”的目标是提供一个自动化且可重复的环境,以确保角色根据其规范工作。因此,测试过程会在每次测试中销毁并重新创建实例。
默认情况下,“molecule test”按顺序执行以下步骤
- 安装所需的依赖项
- 检查项目的代码风格
- 销毁现有实例
- 运行语法检查
- 创建实例
- 准备实例(如果需要)
- 通过应用角色任务来收敛实例
- 检查角色的幂等性
- 使用定义的验证器验证结果
- 销毁实例
您可以通过在 Molecule 配置文件中添加包含所需步骤的“test_sequence”字典来更改这些步骤。有关更多信息,请查阅官方文档。
执行测试场景
$ molecule test
... TRUNCATED OUTPUT ...
--> Test matrix
└── default
├── dependency
├── lint
├── cleanup
├── destroy
├── syntax
├── create
├── prepare
├── converge
├── idempotence
├── side_effect
├── verify
├── cleanup
└── destroy
--> Scenario: 'default'
... TRUNCATED OUTPUT ...
如果测试工作流在任何时候失败,则命令将返回一个非零状态代码。您可以使用该返回代码来自动化流程或将 Molecule 集成到 CI/CD 工作流中。
结论
现在您已成功应用 Molecule 来开发和测试一个编写良好且在不同环境中可靠运行的角色,您可以将其集成到您的开发周期中,以便持续生成高质量的角色,而无需担心测试基础设施。
Molecule 在角色开发过程中提供持续反馈,从而确保您的角色在每个步骤中都按设计工作。
对于更高级的场景,Molecule 支持其他驱动程序,允许您使用不同的平台、虚拟化和云提供商来测试角色。
最后,您可以将 Molecule 与 CI/CD 工作流集成,以自动化 Ansible 角色的完整测试过程。