前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何用Molecule测试Ansible角色

如何用Molecule测试Ansible角色

原创
作者头像
丰一川
修改2018-08-13 10:32:39
5.2K0
修改2018-08-13 10:32:39
举报

介绍

Ansible中的单元测试是确保角色按预期运行的关键。通过允许您指定针对不同环境测试角色的方案,Molecule使此过程更容易。使用Ansible,Molecule将角色卸载到配置器,该配置器在配置的环境中部署角色并调用验证器(例如Testinfra)来检查配置偏差。这可确保您的角色在该特定方案中对环境进行了所有预期的更改。

在本指南中,您将构建一个Ansible角色,将Apache部署到主机并配置Firewalld。要测试此角色是否按预期工作,您将使用Docker作为驱动程序在Molecule中创建测试,并使用Testinfra(用于测试服务器状态的Python库)创建测试。Molecule将提供Docker容器来测试角色,Testinfra将验证服务器是否已按预期配置。完成后,您将能够跨环境创建多个测试用例,并使用Molecule运行这些测试。

准备

在开始本指南之前,您需要以下内容:

  • 一个Ubuntu 16.04服务器,具有sudo权限的非root用户,并确保无需密码即可连接到服务器。

注意:Molecule可以使用Python 2.7或Python 3.6。由于Ubuntu 16.04默认包含Python 3.5和2.7,因此我们将在本教程中安装和使用Python 2.7以使用内置存储库。

  • Docker安装在您的服务器上。按照腾讯云+社区的如何在Ubuntu上安装使用Docker中的步骤进行操作,并确保将非root用户添加到该docker组。
  • 熟悉Ansible Playbook。

第一步 - 准备环境

让我们首先在我们的主机上创建一个虚拟环境,然后在该环境中安装我们的测试所需的软件包。

首先以非root用户身份登录并确保您的存储库是最新的:

代码语言:txt
复制
$ sudo apt-get update -y

这将确保您的软件包存储库包含python-pip将安装的软件包的最新版本pip和Python 2.7。我们将用于pip创建虚拟环境并安装其他软件包。要安装pip,请运行:

代码语言:txt
复制
$ sudo apt-get install -y python-pip

使用pip安装virtualenvPython模块和任何更新:

代码语言:txt
复制
$ pip install pip virtualenv -U

-U标志告诉pip更新任何以前安装的包。

接下来,让我们创建并激活虚拟环境:

代码语言:txt
复制
$ virtualenv my_env

激活它以确保您的操作仅限于该环境:

代码语言:txt
复制
$ . my_env/bin/activate

使用pip 安装moleculeansibledocker-py

代码语言:txt
复制
(my_env) sammy@ubuntu:$ pip install molecule ansible docker-py

以下是每个包的功能:

  • molecule:这是主要的Molecule包,用于测试角色。
  • ansible:此软件包允许使用Ansible playbooks,它执行角色及其相关测试。
  • docker-py:Molecule使用此Python库与Docker进行交互。因为我们使用Docker作为驱动程序。

接下来,让我们在Molecule中创建一个角色。

第二步 - 在Molecule中创建角色

在我们的环境设置之后,让我们使用Molecule来创建一个基本角色,我们将用它来测试Apache的安装。此过程将创建目录结构和一些初始测试,并将Docker指定为驱动程序,以便Molecule使用Docker运行其测试。

创建一个名为httpd的新角色:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ molecule init role -r httpd -d docker

-r标志指定角色的名称,同时-d指定驱动程序,该驱动程序为Molecule提供主机以供测试。切换到新创建的角色的目录:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ cd httpd

测试默认角色以检查Molecule是否已正确设置:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ molecule test

您将看到将每一个列出默认测试操作的输出:

代码语言:txt
复制
--> Validating schema /home/sammy/httpd/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy
...

在开始测试之前,Molecule验证配置文件molecule.yml以确保一切正常。它还会打印此测试矩阵,该矩阵指定测试操作的顺序。

一旦我们创建了角色并定制了测试,我们将详细讨论每个测试操作。现在,请注意PLAY_RECAP每个测试,并确保没有任何默认操作返回failed状态。例如,PLAY_RECAP默认'create'操作应如下所示:

代码语言:txt
复制
...
PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

让我们继续修改我们的角色来配置Apache和Firewalld。

第三步 - 配置Apache

要配置Apache,我们将为角色创建任务文件,指定要安装的软件包和要启用的服务。这些详细信息将从我们用于替换默认Apache索引页的变量文件和模板中提取。

使用nano或您喜欢的文本编辑器为角色创建任务文件:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ nano tasks/main.yml

您将看到该文件已存在。删除那里的内容并粘贴以下代码去安装所需的软件包并启用正确的服务,HTML默认值和防火墙设置:

~/httpd/tasks/main.yml

代码语言:txt
复制
---
- name: "Ensure required packages are present"
  yum:
    name: "{{ pkg_list }}"
    state: present

- name: "Ensure latest index.html is present"
  template:
    src: index.html.j2
    dest: /var/www/html/index.html

- name: "Ensure httpd service is started and enabled"
  service:
    name: "{{ item }}"
    state: started
    enabled: True
  with_items: "{{ svc_list }}"

- name: "Whitelist http in firewalld"
  firewalld:
    service: http
    state: enabled
    permanent: True
    immediate: True

这剧本包括4个任务:

  • Ensure required packages are present:此任务将安装在pkg_list变量文件中列出的包。变量文件将位于~/httpd/vars/main.yml您将在本节末尾创建它。
  • Ensure latest index.html is present(确保最新的index.html存在):此任务将复制模板页index.html.j2并将其粘贴到Apache生成的默认/var/www/html/index.html文件上。
  • Ensure httpd service is started and enabled(确保启动并启用httpd服务):此任务将启动并启用svc_list变量文件中列出的服务。
  • Whitelist http in firewalld(firewalld中的白名单http):此任务将使http服务列入白名单firewalld。Firewalld是一个完整的防火墙解决方案,默认存在于CentOS服务器上。要使http服务正常工作,我们需要公开所需的端口。指示firewalld将服务列入白名单可确保将服务所需的所有端口列入白名单。

完成后保存并关闭文件。

接下来,让我们为index.html.j2模板页面创建一个templates目录:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ mkdir templates

创建页面本身:

代码语言:txt
复制
(my_env) sammy@ubuntu:$  nano templates/index.html.j2

粘贴以下样板代码:

~/httpd/templates/index.html.j2

代码语言:txt
复制
<div style="text-align: center">
    <h2>Managed by Ansible</h2>
</div>

保存并关闭文件。

完成角色的最后一步是编写变量文件,该文件为我们的主角色playbook提供包和服务的名称:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ nano vars/main.yml

使用以下代码粘贴默认内容,该代码指定pkg_listsvc_list

vars/main.yml

代码语言:txt
复制
---
pkg_list:
  - httpd
  - firewalld
svc_list:
  - httpd
  - firewalld

这些列表包含以下信息:

  • pkg_list:这包含角色将安装的软件包的名称:httpd和firewalld。
  • svc_list:这包含角色将启动和启用的服务的名称:httpd和firewalld。

注意:确保您的变量文件没有任何空行,否则您的测试将在linting期间失败。

现在我们已经完成了我们的角色创建,让我们配置Molecule以测试它是否按预期工作。

第四步 - 修改运行测试的角色

配置Molecule涉及两个步骤:修改Molecule配置文件本身,以及创建自定义yamllint文件。Yamllint是一个YAML代码linter,用于检查语法有效性,密钥重复以及行长度,尾随空格和缩进等外观问题。

我们的修改包括:

  • ~/httpd/molecule/default/molecule.yml添加选项使用自定义yamllint配置文件并创建文件本身。此文件将启用两个例外:大于80个字符的行和truthy值。因为Ansible和Yamllint使用冲突的语法来表达truthy值,这将防止不必要的语法错误。
  • 添加平台规范。因为我们正在测试配置和启动httpd systemd服务的角色,所以我们需要使用启用了systemd配置和特权模式的映像。在本教程中,我们将使用Docker Hub上提供milcom/centos7-systemd映像。特权模式几乎允许容器运行其主机的所有功能。

让我们编辑molecule.yml以反映这些变化:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ nano molecule/default/molecule.yml

添加yamllint选项和平台信息:

~/httpd/molecule/default/molecule.yml

代码语言:txt
复制
---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
  options: 
    config-file: molecule/default/yamllint.yml
platforms:
  - name: centos7
    image: milcom/centos7-systemd
    privileged: True
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

完成后保存并关闭文件。

请注意在molecule.yml中的参考文献molecule/default/yamllint.ymlyamllint文件。我们来创建这个文件:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ nano molecule/default/yamllint.yml

通过粘贴以下规则输入测试环境的自定义配置,这些规则定义了行的长度规范和真值设置:

~/httpd/molecule/default/yamllint.yml

代码语言:txt
复制
---
extends: default
rules:
  line-length:
    max: 120
    level: warning
  truthy: disable

我们添加了两条规则:

  • line_length:此规则指定允许的最大行长度为120个字符(最多80个字符),并且如果违反规则,则linter应发出警告。
  • truthy:此规则禁用truthy值,因为Ansible和Yamllint使用冲突的语法来表达它们。这将防止不必要的语法错误。

既然我们已经成功配置了测试环境,那么让我们继续编写Molecule在执行角色后对我们的容器运行的测试用例。

第五步 - 编写测试用例

在此角色的测试中,我们将检查以下条件:

  • httpdfirewalld正在安装的软件包。
  • httpdfirewalld服务正在运行并启用。
  • 我们的防火墙设置中启用了http服务。
  • index.html包含我们的模板文件中指定的相同数据。

如果所有这些测试都通过,则角色按预期工作。

要编写这些条件的测试用例,让我们编辑默认测试~/httpd/molecule/default/tests/test_default.py。使用Testinfra,我们将测试用例编写为使用Molecule类的Python函数。

打开test_default.py

代码语言:txt
复制
(my_env) sammy@ubuntu:$ nano molecule/default/tests/test_default.py

删除文件的内容,以便您可以从头开始编写测试。

注意:在编写测试时,请确保它们由两个新行分隔,否则它们将失败。

首先导入所需的Python模块:

~/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
import os
import pytest

import testinfra.utils.ansible_runner

这些模块包括:

  • os:这个内置的Python模块支持与操作系统相关的功能,使Python可以与底层操作系统进行交互。
  • pytest:该pytest模块可以进行测试编写。
  • testinfra.utils.ansible_runner:此Testinfra模块使用Ansible作为命令执行的后端。

在模块导入下,粘贴以下代码,该代码使用Ansible后端返回当前主机实例:

~/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
...
testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')

将我们的测试文件配置为使用Ansible后端,让我们编写单元测试来测试主机的状态。

第一个测试将确保httpdfirewalld安装:

~/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
... 

@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed

测试使用pytest.mark.parametrize,允许我们参数化测试的参数。第一个测试将test_pkg作为参数来测试httpdfirewalld包的存在。

接下来的测试,检查是否httpdfirewalld正在运行并启用。它需要test_svc作为参数:

~/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
... 

@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled

最后一个测试检查传递给parametrize()的文件和内容是否存在。如果文件不是由我们的角色创建的且内容设置不正确,assert则会返回False

~/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
... 

@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

在每次测试中,assert将根据测试结果来返回True或False。

最终的文件如下所示:

/httpd/molecule/default/tests/test_default.py

代码语言:txt
复制
import os
import pytest

import testinfra.utils.ansible_runner

testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner(
    os.environ['MOLECULE_INVENTORY_FILE']).get_hosts('all')


@pytest.mark.parametrize('pkg', [
  'httpd',
  'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed


@pytest.mark.parametrize('svc', [
  'httpd',
  'firewalld'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled


@pytest.mark.parametrize('file, content', [
  ("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>"),
  ("/var/www/html/index.html", "Managed by Ansible")
])
def test_files(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

现在我们已经指定了测试用例,让我们测试一下这个角色。

第六步 - 使用Molecule测试角色

一旦我们启动测试,Molecule将执行我们在场景中定义的操作。我们将再次运行默认molecule场景,在默认测试序列中执行操作,同时更仔细地查看每个场景。

再次运行默认方案的测试:

代码语言:txt
复制
(my_env) sammy@ubuntu:$ molecule test

这将启动测试运行。初始输出打印默认测试矩阵:

代码语言:txt
复制
--> Validating schema /home/sammy/httpd/molecule/default/molecule.yml.
Validation completed successfully.
--> Test matrix

└── default
    ├── lint
    ├── destroy
    ├── dependency
    ├── syntax
    ├── create
    ├── prepare
    ├── converge
    ├── idempotence
    ├── side_effect
    ├── verify
    └── destroy

让我们来看看每个测试动作和预期的输出,从linting开始。

linting操作执行yamllintflake8以及ansible-lint

  • yamllint:此linter被执行在角色目录中的所有YAML文件上。
  • flake8:这个Python代码linter检查为Testinfra创建的测试。
  • ansible-lint:Ansible playbooks的这个linter在所有场景中都会执行。
代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'lint'
--> Executing Yamllint on files found in /home/sammy/httpd/...
Lint completed successfully.
--> Executing Flake8 on files found in /home/sammy/httpd/molecule/default/tests/...
Lint completed successfully.
--> Executing Ansible Lint on /home/sammy/httpd/molecule/default/playbook.yml...
Lint completed successfully.

使用destroy.yml文件执行下一个操作destroy。这样做是为了测试我们在新创建的容器上的角色。

默认情况下,destroy被调用两次:在测试运行开始时,删除任何预先存在的容器,最后删除新创建的容器:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)

    TASK [Wait for instance(s) deletion to complete] *******************************
    ok: [localhost] => (item=None)

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=1    unreachable=0    failed=0

在销毁操作完成后,测试将继续进行依赖。 如果您的角色需要,此操作允许您从ansible-galaxy中提取依赖项。

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'dependency'
Skipping, missing the requirements file.

下一个测试操作是语法检查,它在默认的playbook.yml playbook上执行。它的工作方式--syntax-check与命令中的标志`ansible-playbook --syntax-check playbook.yml类似:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'syntax'

    playbook: /home/sammy/httpd/molecule/default/playbook.yml

接下来,测试继续进行创建操作。使用我们角色的Molecule目录中的create.yml文件来创建具有我们规范的Docker容器:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'create'

    PLAY [Create] ******************************************************************

    TASK [Log into a Docker registry] **********************************************
    skipping: [localhost] => (item=None)

    TASK [Create Dockerfiles from image names] *************************************
    changed: [localhost] => (item=None)

    TASK [Discover local Docker images] ********************************************
    ok: [localhost] => (item=None)

    TASK [Build an Ansible compatible image] ***************************************
    changed: [localhost] => (item=None)

    TASK [Create docker network(s)] ************************************************

    TASK [Create molecule instance(s)] *********************************************
    changed: [localhost] => (item=None)

    TASK [Wait for instance(s) creation to complete] *******************************
    changed: [localhost] => (item=None)

    PLAY RECAP *********************************************************************
    localhost                  : ok=5    changed=4    unreachable=0    failed=0

创建后,测试继续进行准备操作。此操作执行prepare playbook,它在运行掩盖之前将主机置于特定状态。如果您的角色需要在执行角色之前预先配置系统,这将非常有用。同样,这不适用于我们的角色:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'prepare'
Skipping, prepare playbook not configured

准备好后,converge 操作通过运行playbook.yml playbook 在容器上执行您的角色。如果molecule.yml文件中配置了多个平台,Molecule将执行完如下:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'converge'

    PLAY [Converge] ****************************************************************

    TASK [Gathering Facts] *********************************************************
    ok: [centos7]

    TASK [httpd : Ensure required packages are present] ****************************
    changed: [centos7] => (item=[u'httpd', u'firewalld'])

    TASK [httpd : Ensure latest index.html is present] *****************************
    changed: [centos7]

    TASK [httpd : Ensure httpd service is started and enabled] *********************
    changed: [centos7] => (item=httpd)
    changed: [centos7] => (item=firewalld)

    TASK [httpd : Whitelist http in firewalld] *************************************
    changed: [centos7]

    PLAY RECAP *********************************************************************
    centos7                   : ok=5    changed=4    unreachable=0    failed=0

在converge操作之后,测试继续进行idempotence操作。此操作测试剧本的idempotence确保多次运行中没有发生意外更改:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'idempotence'
Idempotence completed successfully.

下一个测试动作是side-effect动作。这使您生成可以测试更多内容的情况,例如HA故障转移。默认情况下,Molecule不配置side-effect playbook并跳过任务:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'side_effect'
Skipping, side effect playbook not configured.

然后,Molecule将使用默认验证程序Testinfra运行验证程序操作。此操作执行您之前编写的测试test_default.py。如果所有测试成功通过,您将看到成功消息,Molecule将继续执行下一步:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/sammy/httpd/molecule/default/tests/...
    ============================= test session starts ==============================
    platform linux2 -- Python 2.7.12, pytest-3.5.1, py-1.5.3, pluggy-0.6.0
    rootdir: /home/sammy/httpd/molecule/default, inifile:
    plugins: testinfra-1.12.0
collected 6 items

    tests/test_default.py ......                                             [100%]

    ========================== 6 passed in 37.88 seconds ===========================
Verifier completed successfully.

最后,Molecule 会破坏测试期间完成的实例,并删除分配给这些实例的网络:

代码语言:txt
复制
...
--> Scenario: 'default'
--> Action: 'destroy'

    PLAY [Destroy] *****************************************************************

    TASK [Destroy molecule instance(s)] ********************************************
    changed: [localhost] => (item=None)

    TASK [Wait for instance(s) deletion to complete] *******************************
    changed: [localhost] => (item=None)

    TASK [Delete docker network(s)] ************************************************

    PLAY RECAP *********************************************************************
    localhost                  : ok=2    changed=2    unreachable=0    failed=0

测试操作现已完成,验证我们的角色是否按预期工作。

结论

在本文中,您创建了一个Ansible角色来安装和配置Apache和Firewalld。然后,您使用Testinfra编写了单元测试,Molecule用它来评估角色是否成功运行。

您可以使用相同的方法处理非常复杂的角色,并使用CI管道自动化测试。Molecule是一个高度可配置的工具,可用于测试Ansible支持的任何提供者的角色,而不仅仅是Docker。它还可以针对您自己的基础架构进行自动化测试,确保您的角色始终保持最新且功能正常。可以使用官方Molecule文档是学习如何使用Molecule的最佳资源。


参考文献:《How To Test Ansible Roles with Molecule》

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 第一步 - 准备环境
  • 第二步 - 在Molecule中创建角色
  • 第三步 - 配置Apache
  • 第四步 - 修改运行测试的角色
  • 第五步 - 编写测试用例
  • 第六步 - 使用Molecule测试角色
  • 结论
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档