我们希望听到您的声音!帮助我们洞悉 Ansible 生态系统的现状。
参加 Ansible 项目调查 2024

使用 Molecule 和 Podman 开发和测试 Ansible 角色 - 第 2 部分

使用 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 中命名不同。因此,我们在剧本和变量文件中添加了服务名称变量。从 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 http://localhost
There's a web server here
[root@2ce0a0ea8692 /]# exit

您使用 Molecule 帮助进行角色开发,确保它在每个步骤中都跨多个平台正常工作。

接下来,让我们自动化验证过程。

使用 Molecule 验证角色

除了帮助您收敛实例以帮助进行角色开发之外,Molecule 还可以通过执行验证任务来自动化测试过程。为了验证剧本的结果,Molecule 可以使用“testinfra”框架,也可以使用 Ansible 本身。

让我们使用 Ansible 剧本来验证这个新角色的结果。默认情况下,Molecule 提供一个基本的验证器剧本“molecule/default/verify.yml”作为起点。这个剧本包含基本所需的结构,但没有进行任何有用的验证。更新此剧本以使用 Ansible 的“uri”模块从运行的 Web 服务器获取内容,并使用“assert”模块确保它是正确的内容

$ 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: http://localhost
      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 对所有实例运行验证器剧本,确保结果与预期值相匹配。

您可以通过编辑收敛剧本来更新“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"

然后,更新验证器剧本中的“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.

使用验证器,您可以定义一个剧本来执行检查,并确保角色产生所需的结果。

在最后一步中,让我们将所有内容整合到自动测试中。

自动化完整的测试工作流

现在所有部分都已整合在一起,请使用命令“molecule test”自动执行完整的测试过程工作流。

与帮助进行角色开发的“molecule converge”不同,“molecule test”的目标是提供一个自动化的可重复的环境,以确保角色按照其规范运行。因此,测试过程会为每次测试销毁并重新创建实例。

默认情况下,“molecule test”按顺序执行以下步骤

  1. 安装所需的依赖项
  2. 检查项目的代码风格
  3. 销毁现有实例
  4. 运行语法检查
  5. 创建实例
  6. 准备实例(如果需要)
  7. 通过应用角色任务来收敛实例
  8. 检查角色的幂等性
  9. 使用已定义的验证器验证结果
  10. 销毁实例

您可以通过在 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 角色的完整测试过程。