如何在Ubuntu 18.04上使用分子和Travis CI实现Ansible角色的连续测试

Molecule是一种用于执行Ansible角色自动测试的工具。 Travis CI是一个持续集成工具,允许测试持续运行,以确保对代码的贡献不会引入重大变化。在本教程中,您将配置预先制定的基本角色,初始化Molecule场景以测试您的角色,并设置Travis CI以针对您的角色更改持续运行测试。

作者选择了Mozilla基金会作为Write for DOnations计划的一部分进行捐赠。

介绍

Ansible是一种无代理配置管理工具,它使用YAML模板定义要在主机上执行的任务列表。 在Ansible中, 角色是变量,任务,文件,模板和模块的集合,它们一起用于执行单一的复杂功能。

Molecule是一种用于执行Ansible角色自动化测试的工具,专门用于支持开发始终如一的良好编写和维护的角色。 Molecule的单元测试允许开发人员在多个环境和不同参数下同时测试角色。 开发人员必须不断地针对经常变化的代码运行测试; 此工作流程可确保角色在更新代码库时继续有效。 使用Travis CI等持续集成工具运行Molecule可以让测试持续运行,确保对代码的贡献不会引入重大变化。

在本教程中,您将使用预先制作的基本角色,在Ubuntu和CentOS服务器上安装和配置Apache Web服务器和防火墙。 然后,您将初始化该角色中的Molecule场景,以创建测试并确保角色在目标环境中按预期执行。 配置Molecule后,您将使用Travis CI连续测试新创建的角色。 每次对代码进行更改时,Travis CI都会运行molecule test以确保角色仍能正确执行。

先决条件

在开始本教程之前,您需要:

第1步 - 分叉基本角色存储库

您将使用一个名为ansible-apache的预制角色来安装Apache并在基于Debian和Red Hat的发行版上配置防火墙。 您将分叉并使用此角色作为基础,然后在其上构建分子测试。 Forking允许您创建存储库的副本,以便您可以对其进行更改而不会篡改原始项目。

首先创建一个ansible-apache角色的分支。 转到ansible-apache存储库,然后单击Fork按钮。

一旦你分叉了存储库,GitHub将引导你进入你的fork页面。 这将是基本存储库的副本,但是您自己的帐户。

单击绿色克隆或下载按钮,您将看到一个包含使用HTTPS克隆的框。

复制为存储库显示的URL。 您将在下一步中使用它。 URL将类似于:

https://github.com/username/ansible-apache.git

您将使用您的GitHub用户名替换username。

设置fork后,您将在服务器上克隆它并在下一部分开始准备您的角色。

第2步 - 准备你的角色

按照Ubuntu 18.04上的如何使用Molecule测试Ansible Roles的先决条件的第1步,您将在虚拟环境中安装Molecule和Ansible。 您将使用此虚拟环境来开发新角色。

首先,通过运行以下命令来激活您在创建先前条件时创建的虚拟环境:

source my_env/bin/activate

运行以下命令以使用您在第1步中复制的URL克隆存储库:

git clone https://github.com/username/ansible-apache.git

您的输出将类似于以下内容:

Cloning into 'ansible-apache'...
remote: Enumerating objects: 16, done.
remote: Total 16 (delta 0), reused 0 (delta 0), pack-reused 16
Unpacking objects: 100% (16/16), done.

进入新创建的目录:

cd ansible-apache

您下载的基本角色执行以下任务:

  • 包含变量 :角色首先根据主机的分布包含所有必需的变量 Ansible使用变量来处理不同系统之间的差异。 由于您使用Ubuntu 18.04和CentOS 7作为主机,该角色将分别识别OS系列是Debian和Red Hat,并包含来自vars/Debian.ymlvars/RedHat.yml

  • 包括与分发相关的任务 :这些任务包括tasks/install-Debian.ymltasks/install-RedHat.yml 根据指定的分发,它会安装相关的包。 对于Ubuntu,这些包是apache2ufw 对于CentOS,这些软件包是httpdfirewalld

  • 确保存在最新的index.html :此任务复制Apache将用作Web服务器主页的模板templates/index.html.j2

  • 启动相关服务并在启动时启用它们 :启动并启用作为第一个任务的一部分安装的所需服务。 对于CentOS,这些服务是httpdfirewalld ,对于Ubuntu,它们是apache2ufw

  • 配置防火墙以允许流量 :这包括tasks/configure-Debian-firewall.ymltasks/configure-RedHat-firewall.yml Ansible将Firewalld或UFW配置为防火墙并将http服务列入白名单。

现在您已了解此角色的工作原理,您将配置Molecule进行测试。 您将为这些任务编写测试用例,以涵盖他们所做的更改。

第3步 - 编写测试

要检查基本角色是否按预期执行任务,您将启动Molecule方案,指定目标环境并创建三个自定义测试文件。

首先使用以下命令初始化此角色的Molecule场景:

molecule init scenario -r ansible-apache

您将看到以下输出:

--> Initializing new scenario default...
Initialized scenario in /home/sammy/ansible-apache/molecule/default successfully.

您将CentOS和Ubuntu作为目标环境添加为Molecule配置文件中的平台。 为此,请使用文本编辑器编辑molecule.yml文件:

nano molecule/default/molecule.yml

将以下突出显示的内容添加到Molecule配置中:

〜/ ansible-Apache/分子/默认/ molecule.yml
---
dependency:
  name: galaxy
driver:
  name: docker
lint:
  name: yamllint
platforms:
  - name: centos7
    image: milcom/centos7-systemd
    privileged: true
  - name: ubuntu18
    image: solita/ubuntu-systemd
    command: /sbin/init
    privileged: true
    volumes:
      - /lib/modules:/lib/modules:ro
provisioner:
  name: ansible
  lint:
    name: ansible-lint
scenario:
  name: default
verifier:
  name: testinfra
  lint:
    name: flake8

在这里,您将指定在使用systemd服务时以特权模式启动的两个目标平台:

  • centos7是第一个使用milcom/centos7-systemd映像的平台。
  • ubuntu18是第二个平台,使用solita/ubuntu-systemd映像。 除了使用特权模式并安装所需的内核模块之外,您还在启动时运行/sbin/init以确保iptables启动并运行。

保存并退出该文件。

有关运行特权容器的更多信息,请访问官方Molecule文档

您将创建三个自定义测试文件,一个用于每个目标平台,一个文件用于编写所有平台之间通用的测试,而不是使用默认的Molecule测试文件。 首先使用以下命令删除方案的默认测试文件test_default.py

rm molecule/default/tests/test_default.py

您现在可以继续为每个目标平台创建三个自定义测试文件test_common.pytest_Debian.pytest_RedHat.py

第一个测试文件test_common.py将包含每个主机将执行的常见测试。 创建和编辑公共测试文件test_common.py

nano molecule/default/tests/test_common.py

将以下代码添加到文件中:

〜/ ansible的Apache /分子/默认/测试/ test_common.py
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('file, content', [
  ("/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)

test_common.py文件中,您已导入所需的库。 您还编写了一个名为test_files()的测试,该测试保存您的角色执行的发行版之间唯一的常见任务:将模板复制为Web服务器主页。

下一个测试文件test_Debian.py包含特定于Debian发行版的测试。 此测试文件将专门针对您的Ubuntu平台。

通过运行以下命令来创建和编辑Ubuntu测试文件:

nano molecule/default/tests/test_Debian.py

您现在可以导入所需的库并将ubuntu18平台定义为目标主机。 将以下代码添加到此文件的开头:

〜/ ansible的Apache /分子/默认/测试/ test_Debian.py
import os
import pytest

import testinfra.utils.ansible_runner

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

然后,在同一个文件中,您将添加test_pkg()测试。

将以下代码添加到文件中,该文件定义了test_pkg()测试:

〜/ ansible的Apache /分子/默认/测试/ test_Debian.py
...
@pytest.mark.parametrize('pkg', [
    'apache2',
    'ufw'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

    assert package.is_installed

此测试将检查主机上是否安装了apache2ufw软件包。

注意:将多个测试添加到Molecule测试文件时,请确保每个测试之间有两个空行,否则您将从Molecule获得语法错误。

要定义下一个测试test_svc() ,请在文件中的test_pkg()测试下添加以下代码:

〜/ ansible的Apache /分子/默认/测试/ test_Debian.py
...
@pytest.mark.parametrize('svc', [
    'apache2',
    'ufw'
])
def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled

test_svc()将检查apache2ufw服务是否正在运行并启用。

最后,您将最后一个测试test_ufw_rules()添加到test_Debian.py文件中。

在您的文件中的test_svc()测试下添加此代码以定义test_ufw_rules()

〜/ ansible的Apache /分子/默认/测试/ test_Debian.py
...
@pytest.mark.parametrize('rule', [
    '-A ufw-user-input -p tcp -m tcp --dport 80 -j ACCEPT'
])
def test_ufw_rules(host, rule):
    cmd = host.run('iptables -t filter -S')

    assert rule in cmd.stdout

test_ufw_rules()将检查您的防火墙配置是否允许Apache服务使用的端口上的流量。

添加了每个测试后, test_Debian.py文件将如下所示:

〜/ ansible的Apache /分子/默认/测试/ test_Debian.py
import os
import pytest

import testinfra.utils.ansible_runner

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


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

    assert package.is_installed


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

    assert service.is_running
    assert service.is_enabled


@pytest.mark.parametrize('rule', [
    '-A ufw-user-input -p tcp -m tcp --dport 80 -j ACCEPT'
])
def test_ufw_rules(host, rule):
    cmd = host.run('iptables -t filter -S')

    assert rule in cmd.stdout

test_Debian.py文件现在包含三个测试: test_pkg()test_svc()test_ufw_rules()

保存并退出test_Debian.py

接下来,您将创建test_RedHat.py测试文件,该文件将包含针对您的CentOS平台的特定于Red Hat发行版的测试。

通过运行以下命令创建和编辑CentOS测试文件test_RedHat.py

nano molecule/default/tests/test_RedHat.py

与Ubuntu测试文件类似,您现在将编写三个测试以包含在test_RedHat.py文件中。 在添加测试代码之前,您可以导入所需的库并将centos7平台定义为目标主机,方法是将以下代码添加到文件的开头:

〜/ ansible的Apache /分子/默认/测试/ test_RedHat.py
import os
import pytest

import testinfra.utils.ansible_runner

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

然后,添加test_pkg()测试,该测试将检查主机上是否安装了httpdfirewalld软件包。

按照库导入的代码,将test_pkg()测试添加到您的文件中。 (再次,记住在每次新测试之前包括两个空行。)

〜/ ansible的Apache /分子/默认/测试/ test_RedHat.py
...
@pytest.mark.parametrize('pkg', [
    'httpd',
    'firewalld'
])
def test_pkg(host, pkg):
    package = host.package(pkg)

      assert package.is_installed

现在,您可以添加test_svc()测试以确保httpdfirewalld服务正在运行和启用。

test_pkg()测试之后将test_pkg()代码添加到您的文件中:

〜/ ansible的Apache /分子/默认/测试/ test_RedHat.py
...
@pytest.mark.parametrize('svc', [
    'httpd',
    'firewalld'
])
  def test_svc(host, svc):
    service = host.service(svc)

    assert service.is_running
    assert service.is_enabled

test_RedHat.py文件中的最终测试将是test_firewalld() ,它将检查Firewalld是否将http服务列入白名单。

test_svc()代码之后将test_svc()测试添加到您的文件中:

〜/ ansible的Apache /分子/默认/测试/ test_RedHat.py
...
@pytest.mark.parametrize('file, content', [
    ("/etc/firewalld/zones/public.xml", "<service name=\"http\"/>")
])
def test_firewalld(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

导入库并添加三个测试后, test_RedHat.py文件将如下所示:

〜/ ansible的Apache /分子/默认/测试/ test_RedHat.py
import os
import pytest

import testinfra.utils.ansible_runner

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


@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\"/>")
])
def test_firewalld(host, file, content):
    file = host.file(file)

    assert file.exists
    assert file.contains(content)

现在您已经完成了在所有三个文件test_common.pytest_Debian.pytest_RedHat.py编写测试,您的角色已准备好进行测试。 在下一步中,您将使用Molecule针对新配置的角色运行这些测试。

第4步 - 针对您的角色进行测试

现在,您将使用Molecule对基础角色ansible-apache执行新创建的测试。 要运行测试,请使用以下命令:

molecule test

一旦Molecule完成所有测试,您将看到以下输出:

...
--> Scenario: 'default'
--> Action: 'verify'
--> Executing Testinfra tests found in /home/sammy/ansible-apache/molecule/default/tests/...
    ============================= test session starts ==============================
    platform linux -- Python 3.6.7, pytest-4.1.1, py-1.7.0, pluggy-0.8.1
    rootdir: /home/sammy/ansible-apache/molecule/default, inifile:
    plugins: testinfra-1.16.0
collected 12 items

    tests/test_common.py ..                                                  [ 16%]
    tests/test_RedHat.py .....                                               [ 58%]
    tests/test_Debian.py .....                                               [100%]

    ========================== 12 passed in 80.70 seconds ==========================
Verifier completed successfully.

您将看到Verifier completed successfully在您的输出中Verifier completed successfully ; 这意味着验证程序执行了所有测试并成功返回。

既然您已成功完成角色的开发,您可以将更改提交给Git并设置Travis CI以进行持续测试。

第5步 - 使用Git共享更新的角色

在本教程中,到目前为止,您已经克隆了一个名为ansible-apache的角色并为其添加了测试,以确保它可以对抗Ubuntu和CentOS主机。 要与公众共享更新的角色,您必须提交这些更改并将其推送到您的分支。

运行以下命令以添加文件并提交您所做的更改:

git add .

此命令将您在当前目录中修改的所有文件添加到暂存区域。

您还需要在git config中设置您的姓名和电子邮件地址才能成功提交。 您可以使用以下命令执行此操作:

git config user.email "sammy@digitalocean.com"
git config user.name "John Doe"

将更改的文件提交到存储库:

git commit -m "Configured Molecule"

您将看到以下输出:

[master b2d5a5c] Configured Molecule
 8 files changed, 155 insertions(+), 1 deletion(-)
 create mode 100644 molecule/default/Dockerfile.j2
 create mode 100644 molecule/default/INSTALL.rst
 create mode 100644 molecule/default/molecule.yml
 create mode 100644 molecule/default/playbook.yml
 create mode 100644 molecule/default/tests/test_Debian.py
 create mode 100644 molecule/default/tests/test_RedHat.py
 create mode 100644 molecule/default/tests/test_common.py

这表示您已成功提交更改。 现在,使用以下命令将这些更改推送到fork:

git push -u origin master

您将看到GitHub凭据的提示。 输入这些凭据后,您的代码将被推送到您的存储库,您将看到此输出:

Counting objects: 13, done.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (13/13), 2.32 KiB | 2.32 MiB/s, done.
Total 13 (delta 3), reused 0 (delta 0)
remote: Resolving deltas: 100% (3/3), completed with 2 local objects.
To https://github.com/username/ansible-apache.git
   009d5d6..e4e6959  master -> master
Branch 'master' set up to track remote branch 'master' from 'origin'.

如果您通过github.com/ username /ansible-apache转到fork的存储库,您将看到一个名为Configured Molecule的新提交,它反映了您在文件中所做的更改。

现在,您可以将Travis CI与新存储库集成,以便对您的角色所做的任何更改都将自动触发Molecule测试。 这将确保您的角色始终与Ubuntu和CentOS主机一起使用。

第6步 - 整合Travis CI

在此步骤中,您将把Travis CI集成到您的工作流程中。 启用后,您推送到fork的任何更改都将触发Travis CI构建。 这样做的目的是确保Travis CI在贡献者进行更改时始终进行molecule test 如果发生任何重大更改,Travis将声明构建状态。

继续Travis CI以启用您的存储库。 导航到您的个人资料页面,您可以在其中单击GitHub的“ 激活”按钮。

您可以在此处找到有关在Travis CI中激活存储库的更多指导。

要使Travis CI正常工作,您必须创建包含相关说明的配置文件。 要创建Travis配置文件,请返回到您的服务器并运行以下命令:

nano .travis.yml

要复制您在本教程中创建的环境,您将在Travis配置文件中指定参数。 将以下内容添加到您的文件中:

〜/ ansible-Apache/ .travis.yml
---
language: python
python:
  - "2.7"
  - "3.6"
services:
  - docker
install:
  - pip install molecule docker
script:
  - molecule --version
  - ansible --version
  - molecule test

您在此文件中指定的参数是:

  • language :当您指定Python作为语言时,CI环境对您在python密钥下指定的每个Python版本使用单独的virtualenv实例。
  • python :在这里,您指定Travis将同时使用Python 2.7和Python 3.6来运行测试。
  • services :您需要Docker在Molecule中运行测试。 您指定Travis应确保您的CI环境中存在Docker。
  • install :在这里,您将指定Travis CI将在您的virtualenv执行的初步安装步骤。
    • pip install molecule docker docker检查Ansible和Molecule是否与Docker远程API的Python库一起出现。
  • script :这是指定Travis CI需要执行的步骤。 在您的文件中,您指定了三个步骤:
    • 如果已成功安装Molecule,则molecule --version打印Molecule版本。
    • 如果已成功安装Ansible,则ansible ansible --version打印Ansible版本。
    • molecule test终于进行了你的分子测试。

你指定molecule --versionansible --version的原因是为了在构建因版本控制导致的安全性或molecule错误配置失败的情况下捕获错误。

将内容添加到Travis CI配置文件后,保存并退出.travis.yml

现在,每次将任何更改推送到存储库时,Travis CI将根据上述配置文件自动运行构建。 如果script块中的任何命令失败,Travis CI将报告构建状态。

为了更容易查看构建状态,您可以将指示构建状态的徽章添加到角色的README中。 使用文本编辑器打开README.md文件:

nano README.md

README.md添加到README.md以显示构建状态:

〜/ ansible-Apache/ README.md
[![Build Status](https://travis-ci.org/username/ansible-apache.svg?branch=master)](https://travis-ci.org/username/ansible-apache)

用您的GitHub用户名替换username名。 像之前一样提交并将更改推送到存储库。

首先,运行以下命令将.travis.ymlREADME.md添加到暂存区域:

git add .travis.yml README.md

现在通过执行以下命令将更改提交到存储库:

git commit -m "Configured Travis"

最后,使用以下命令将这些更改推送到fork:

git push -u origin master

如果您导航到GitHub存储库,您将看到它最初报告构建:未知

建状态未知

在几分钟内,Travis将启动您可以在Travis CI网站上监控的构建。 一旦构建成功,GitHub也会在您的存储库中报告状态 - 使用您在README文件中放置的徽章:

建状态扯皮

您可以访问Travis CI网站访问构建的完整详细信息:

特拉维斯 - 建造 - 状态

现在您已成功为新角色设置了Travis CI,您可以不断测试和集成对Ansible角色的更改。

结论

在本教程中,您分叉了一个角色,该角色通过GitHub安装和配置Apache Web服务器,并通过编写测试和配置这些测试来处理运行Ubuntu和CentOS的Docker容器,从而为Molecule添加了集成。 通过将新创建的角色推送到GitHub,您已允许其他用户访问您的角色。 当贡献者对您的角色进行更改时,Travis CI将自动运行Molecule来测试您的角色。

一旦您对创建角色并使用Molecule进行测试感到满意,就可以将其与Ansible Galaxy集成,以便在构建成功后自动推送角色。


分享按钮