大家好!
我在酒店预订服务Ostrovok.ru中担任DevOps工程师。 在本文中,我想谈谈我们在测试角色扮演方面的经验。
在Ostrovok.ru,我们使用ansible作为配置管理器。 最近,我们提出了测试角色的需求,但事实证明,没有足够的工具可用于此目的-最受欢迎的也许是Molecule框架,因此我们决定使用它。 但是事实证明,他的文档对许多陷阱保持沉默。 我们找不到俄文的足够详细的指南,因此我们决定写这篇文章。

分子
分子 -帮助测试难胜任角色的框架。
简化描述:分子在您指定的平台(云,虚拟机,容器;有关更多详细信息,请参见“ 驱动程序”部分)上创建实例,在其上运行您的角色,然后运行测试并删除该实例。 如果其中一个步骤失败,分子将通知您。
现在更详细。
一点理论
考虑分子的两个关键实体:场景和驱动程序。
情境
该脚本包含将执行的内容,位置,方式和顺序的描述。 一个角色可以具有多个脚本,每个脚本都位于路径<role>/molecule/<scenario>
,其中包含测试所需动作的描述。 必须存在default
脚本,如果您使用Molecule初始化角色,则会自动创建该脚本。 您可以自行选择以下方案的名称。
脚本中的测试序列称为matrix ,默认情况下如下:
(如果用户未描述,则默认情况下会跳过标有?
步骤)
lint
-运行短绒lint
。 默认情况下,使用yamllint
和flake8
,destroy
-从上一次分子发射中删除实例(如果有的话),dependency
? -安装受测角色的ansible依赖性,syntax
-使用ansible-playbook --syntax-check
角色的语法,create
-创建一个实例,prepare
? -实例的准备; 例如检查/安装python2converge
-推出经过测试的剧本,idempotence
-重新启动剧本进行幂等性测试,side_effect
吗? -与角色没有直接关系,但对于测试是必需的动作,verify
-使用testinfra
(默认)/ inspec
/ inspec
对结果配置进行测试,cleanup
? -(在新版本中)-粗略地说,“清洗”受分子影响的外部基础设施,destroy
-删除实例。
此顺序适用于大多数情况,但是您可以根据需要进行更改。
以上每个步骤都可以使用molecule <command>
单独运行。 但是值得了解的是,对于每个这样的cli命令,通过执行molecule matrix <command>
,您可能都可以识别出一系列动作。 例如,当您运行converge
命令(运行经过测试的剧本)时,将执行以下操作:
$ molecule matrix converge ... └── default
这些动作的顺序可以编辑。 如果列表中的内容已经完成,则将被跳过。 当前状态以及实例配置存储在$TMPDIR/molecule/<role>/<scenario>
目录中。
用?
添加步骤 您可以用ansible-playbook格式描述所需的操作,然后按照以下步骤创建文件名: side_effect.yml
/ side_effect.yml
。 分子将等待脚本文件夹中的这些文件。
司机
驱动程序是创建测试实例的实体。
分子已准备好模板的标准驱动程序如下:Azure,Docker,EC2,GCE,LXC,LXD,OpenStack,Vagrant,Delegated。
在大多数情况下,模板是脚本文件夹中的create.yml
和destroy.yml
,分别描述了实例的创建和删除。
Docker和Vagrant例外,因为在没有上述文件的情况下可以与其模块进行交互。
值得强调一下Delegated驱动程序,因为如果在文件中使用它来创建和删除实例,则仅描述了实例的配置,其余的应由工程师描述。
默认驱动程序是Docker。
现在我们转向实践并考虑那里的其他功能。
开始使用
作为“ hello world”,我们测试了安装nginx的简单作用。 我们将选择docker作为驱动程序-我认为它已安装在大多数人中(请记住docker是默认驱动程序)。
准备virtualenv
并在其中安装molecule
:
> pip install virtualenv > virtualenv -p `which python2` venv > source venv/bin/activate > pip install molecule docker
下一步是初始化一个新角色。
新的角色以及新场景的molecule init <params>
是使用molecule init <params>
命令执行的:
> molecule init role -r nginx --> Initializing new role nginx... Initialized role in <path>/nginx successfully. > cd nginx > tree -L 1 . ├── README.md ├── defaults ├── handlers ├── meta ├── molecule ├── tasks └── vars 6 directories, 1 file
结果是一个典型的角色。 此外,与CLI分子的所有交互都是从角色的根源开始的。
让我们看看角色目录中的内容:
> tree molecule/default/ molecule/default/ ├── Dockerfile.j2
让我们分析一下molecule/default/molecule.yml
配置(我们将仅替换docker映像):
--- dependency: name: galaxy driver: name: docker lint: name: yamllint platforms: - name: instance image: centos:7 provisioner: name: ansible lint: name: ansible-lint scenario: name: default verifier: name: testinfra lint: name: flake8
依赖
本节描述了依赖项的来源。
可能的选项: 银河系 , 镀金面 ,外壳。
外壳程序只是命令外壳程序,如果银河和镀金不满足您的需求,则可以使用该外壳程序。
我不会在这里停留很长时间,在文档中对此进行了足够的描述 。
司机
驱动程序的名称。 我们有这个码头工人。
皮棉
作为短绒,使用yamllint。
在配置的这一部分中,有用的选项是为yamllint指定配置文件,转发环境变量或禁用linter的功能:
lint: name: yamllint options: config-file: foo/bar env: FOO: bar enabled: False
描述实例的配置。
如果将docker作为驱动程序,则在此部分上迭代Molecule,并且Dockerfile.j2
中的每个列表项Dockerfile.j2
作为item
变量使用。
对于destroy.yml
create.yml
和destroy.yml
的驱动程序,该部分在它们中的名称为destroy.yml
,这些文件中已经描述了该版本。
由于Molecule为ansible模块提供了实例控制,因此必须在此处查找可能的设置列表。 例如,对于docker ,使用docker_container_module模块。 可以在文档中找到其他驱动程序中使用了哪些模块。
在分子本身的测试中也可以找到使用各种驱动程序的示例。
替换centos:在此处ubuntu上的 7 。
供应者
“提供者”是控制实例的实体。 在分子的情况下,这是ansible的,没有计划为他人提供支持,因此可以保留保留地将其扩展配置为ansible。
在这里,您可以指定很多东西,我认为主要是重要的时刻:
provisioner: name: ansible playbooks: create: create.yml destroy: ../default/destroy.yml converge: playbook.yml side_effect: side_effect.yml cleanup: cleanup.yml
provisioner: name: ansible config_options: defaults: fact_caching: jsonfile ssh_connection: scp_if_ssh: True
- connection_options : 连接参数
provisioner: name: ansible connection_options: ansible_ssh_common_args: "-o 'UserKnownHostsFile=/dev/null' -o 'ForwardAgent=yes'"
provisioner: name: ansible options: vvv: true diff: true env: FOO: BAR
情景
脚本序列的名称和描述。
您可以<command>_sequence
添加<command>_sequence
并确定我们需要作为其值的步骤列表来更改<command>_sequence
的默认操作矩阵。
假设我们要在运行playbook run命令时更改操作顺序: molecule converge
# : # - dependency # - create # - prepare # - converge scenario: name: default converge_sequence: - create - converge
验证者
设置测试框架和测试框架。 默认情况下, testinfra
和flake8
用作flake8
。 可能的选项与上述类似:
verifier: name: testinfra additional_files_or_dirs: - ../path/to/test_1.py - ../path/to/test_2.py - ../path/to/directory/* options: n: 1 enabled: False env: FOO: bar lint: name: flake8 options: benchmark: True enabled: False env: FOO: bar
让我们回到我们的角色。 编辑tasks/main.yml
,如下所示:
--- - name: Install nginx apt: name: nginx state: present - name: Start nginx service: name: nginx state: started
并将测试添加到molecule/default/tests/test_default.py
def test_nginx_is_installed(host): nginx = host.package("nginx") assert nginx.is_installed def test_nginx_running_and_enabled(host): nginx = host.service("nginx") assert nginx.is_running assert nginx.is_enabled def test_nginx_config(host): host.run("nginx -t")
完成后,它仅能运行(我提醒您从角色的根源开始):
> molecule test
扰流板下的长排气口: --> Validating schema <path>/nginx/molecule/default/molecule.yml. Validation completed successfully. --> Test matrix └── default ├── lint ├── destroy ├── dependency ├── syntax ├── create ├── prepare ├── converge ├── idempotence ├── side_effect ├── verify └── destroy --> Scenario: 'default' --> Action: 'lint' --> Executing Yamllint on files found in <path>/nginx/... Lint completed successfully. --> Executing Flake8 on files found in <path>/nginx/molecule/default/tests/... Lint completed successfully. --> Executing Ansible Lint on <path>/nginx/molecule/default/playbook.yml... Lint completed successfully. --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* ok: [localhost] => (item=None) ok: [localhost] TASK [Delete docker network(s)] ************************************************ PLAY RECAP ********************************************************************* localhost : ok=2 changed=1 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'dependency' Skipping, missing the requirements file. --> Scenario: 'default' --> Action: 'syntax' playbook: <path>/nginx/molecule/default/playbook.yml --> 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) changed: [localhost] TASK [Discover local Docker images] ******************************************** ok: [localhost] => (item=None) ok: [localhost] TASK [Build an Ansible compatible image] *************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Create docker network(s)] ************************************************ TASK [Create molecule instance(s)] ********************************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) creation to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] PLAY RECAP ********************************************************************* localhost : ok=5 changed=4 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'prepare' Skipping, prepare playbook not configured. --> Scenario: 'default' --> Action: 'converge' PLAY [Converge] **************************************************************** TASK [Gathering Facts] ********************************************************* ok: [instance] TASK [nginx : Install nginx] *************************************************** changed: [instance] TASK [nginx : Start nginx] ***************************************************** changed: [instance] PLAY RECAP ********************************************************************* instance : ok=3 changed=2 unreachable=0 failed=0 --> Scenario: 'default' --> Action: 'idempotence' Idempotence completed successfully. --> Scenario: 'default' --> Action: 'side_effect' Skipping, side effect playbook not configured. --> Scenario: 'default' --> Action: 'verify' --> Executing Testinfra tests found in <path>/nginx/molecule/default/tests/... ============================= test session starts ============================== platform darwin -- Python 2.7.15, pytest-4.3.0, py-1.8.0, pluggy-0.9.0 rootdir: <path>/nginx/molecule/default, inifile: plugins: testinfra-1.16.0 collected 4 items tests/test_default.py .... [100%] ========================== 4 passed in 27.23 seconds =========================== Verifier completed successfully. --> Scenario: 'default' --> Action: 'destroy' PLAY [Destroy] ***************************************************************** TASK [Destroy molecule instance(s)] ******************************************** changed: [localhost] => (item=None) changed: [localhost] TASK [Wait for instance(s) deletion to complete] ******************************* changed: [localhost] => (item=None) changed: [localhost] TASK [Delete docker network(s)] ************************************************ PLAY RECAP ********************************************************************* localhost : ok=2 changed=2 unreachable=0 failed=0
我们的简单角色经过了测试,没有任何问题。
值得记住的是,如果在molecule test
的操作过程中出现问题,那么如果您没有更改标准序列,则分子将删除该实例。
以下命令对于调试很有用:
> molecule --debug <command>
现有角色
使用以下命令从角色目录向现有角色添加新脚本:
如果这是该角色中的第一个脚本,则-s
可以省略,因为将创建default
脚本。
结论
如您所见,Molecule并不是很复杂,并且在使用自己的模板时,可以减少新脚本的部署,以编辑剧本中的变量来创建和删除实例。 该分子与CI系统无缝集成,可通过减少手动测试剧本所需的时间来提高开发速度。
谢谢您的关注。 如果您有测试角色的经验,并且与分子无关,请在评论中告诉我们!