如同程式需要測試,Ansible Playbook 或 Roles 其實也是某種 Code,因此最好也能為其撰寫適當的測試。本文就讓我們向 Ansible 圈內的大神 geerlingguy 求教,學習 geerlingguy 大神測試 Roles 的方式。


本文的內容也正是我在 DevOps Taiwan Meetup #13 — Ansible User 小聚所分享的閃電秀,當時的簡報在此:


建立你的 Roles 並透過 Travis CI 測試

要學習 geerlingguy 大神的測試方式之前,當然請先準備好你的 Roles,將其推上 GitHub。關於 Roles、Ansible Galaxy 及 GitHub 三者的關係,這邊就不再重複說明了,請直接參閱凍仁翔的文章「怎麼在 Ansible Galaxy 分享 Roles?」()。

推上 GitHub 之後,接著要將 Repository 與 Travis CI 建立串連,這部分也同樣可以參閱凍仁翔的文章「怎麼用 Travis CI 測試 Roles?」。

接下來就讓我們一窺 geerlingguy 大神是如何測試的。


geerlingguy 大神的測試妙招

Ansible 的使用者們應該或多或少都曾使用或參考過 geerlingguy 大神所撰寫的 Roles,如此大量高品質的 Roles,全部都有通過 Travis CI 測試,到底是如何做到的?關鍵在於 geerlingguy 充分利用了 Travis CI 的特性,並且撰寫一支專門協助測試的 shell script。

各位可以隨意瀏覽 geerlingguy 放於 GitHub 的各個 Roles 原始檔,若去觀察其中.travis.yml 的內容,必定會發現有著固定的模式。

以下就用 geerlingguy/ansible-role-docker 的 .travis.yml 為例:

---
services: docker
env:
- distro: centos7
- distro: ubuntu1604
- distro: ubuntu1404
- distro: debian8
script:
# Configure test script so we can run extra tests after playbook is run.
- export container_id=$(date +%s)
- export cleanup=false
# Download test shim.
- wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/raw/
- chmod +x ${PWD}/tests/test.sh
# Run tests.
- ${PWD}/tests/test.sh
# Test whether Docker is running correctly (Dockerception!).
- docker exec --tty ${container_id} docker run hello-world

geerlingguy 的測試妙招簡單來說即是善用 docker 建立不同的測試環境,藉此測試 Roles 在不同的 os distribution 是否皆能順利執行,同時可以針對不同的 os distribution 設置不同的參數,藉此執行不同的測試步驟。

因此我們可以看見在 .travis.yml 當中,首先是指定 Travis 使用 Docker 來建立測試環境。(有興趣了解 .travis.yml 詳細使用方式者請自行參閱 Travis CI 官方文件。)

services: docker

配合欲測試的 os distribution,在 env 中設置多個項目。

env:
- distro: centos7
- distro: ubuntu1604
- distro: ubuntu1404
- distro: debian8

對應至 Travis CI,屆時就會產生多個測試環境,例如下圖就是一個範例。

如果想要測試更多不同的環境,只要增加更多的項目即可。

env:
- distro: centos7
- distro: centos6
- distro: ubuntu1604
- distro: ubuntu1404
- distro: debian9
- distro: debian8

不過 env 的用途不只如此,還能用來設置不同的 OPTIONS,例如下面的範例,可以針對 centos7,指定要分別執行不同的測試用 playbook。

env:
- distro: centos7

- distro: centos7
playbook: test-latest.yml

針對 env 首先要注意的是,能設置的 os distribution 僅限 geerlingguy 有製作對應之 docker images 的項目,基本上常用的 os distribution 都能設置,詳細清單就請自行參閱 geerlingguy 的 Docker Hub,你會發現他有建立很多命名規則為 geerlingguy/docker-$distro-ansible 的 docker image。或者也可以從後面會介紹的 shell script 內容中得知有哪些 docker image 可以選擇。

再來是重頭戲 script,可以分成三個區塊解釋,分別是「設置 OPTIONS」、「取得測試用 shell script 並執行測試」、「驗證 Roles 執行成果」。

第一段是「設置 OPTIONS」,與 env 的地方類似,這些「OPTIONS」將會影響 shell script 中的動作,所有的 OPTIONS 都有預設值,因此沒有特別設置也沒關係。

# Configure test script so we can run extra tests after playbook is run.
- export container_id=$(date +%s)
- export cleanup=false

接著是最重要的「取得測試用 shell script 並執行測試」。

# Download test shim.
- wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/raw/
- chmod +x ${PWD}/tests/test.sh

# Run tests.
- ${PWD}/tests/test.sh

geerlingguy 大神使用的 shell script 存放在他的 gist ,而這段 code 即是透過 wget 下載該 shell script 至指定路徑,再給予可執行的權限,最後運行 script。

而一切的精華就在於這個 shell script 之中,為了避免佔據版面就不轉貼,有興趣者可以前往 gist 查看。

該 shell script 會幫助你完成以下幾個動作:

  • 根據 env 設置的 os distribution,透過 docker pull 並 docker run 產生對應的測試環境。
  • 根據 test/requirements.yml 下載相依之其他 Roles。
  • 執行 --syntax-check 進行語法檢查。
  • 執行 test/test.yml,或執行 env 中指定其他的 playbook。
  • 再次執行 test/test.yml 檢查 Role 是否具備 idempotence 特性。
  • 根據 OPTION 決定是否要刪除 docker container。

如上述可知,一切的關鍵都在此 shell script 中,透過 OPTIONS 與 script 的搭配,即可方便地測試不同的 os distribution,並且根據 OPTIONS 的不同而控制測試內容的不同。

geerlingguy 的 shell script 提供下述的 OPTIONS,已經解說得很清楚,就不再重複說明,裡面比較重要的是 distroplaybookcleanuptest_idempotence

# Usage: [OPTIONS] ./tests/test.sh
# - distro: a supported Docker distro version (default = "centos7")
# - playbook: a playbook in the tests directory (default = "test.yml")
# - role_dir: the directory where the role exists (default = $PWD)
# - cleanup: whether to remove the Docker container (default = true)
# - container_id: the --name to set for the container (default = timestamp)
# - test_idempotence: whether to test playbook's idempotence (default = true)
  • distro 主要會設置於 env,用來控制要在哪個 os distribution 上執行 Roles。
  • playbook 主要會設置於 env,用來控制要執行哪一個測試用 playbook。例如有些 os distribution 可能在執行 Roles 之前需要特別的前置作業,就可以設置此 OPTION,讓它運行不同的 playbook 進行測試。
  • cleanup 如果在 test.yml 執行完畢之後,你還想要補上一些額外的驗證動作,cleanup 則需要特別設置為 false。(因為預設值是 true
  • test_idempotence 用來控制要不要執行 playbook 兩次,藉此驗證 Roles 是否具備 idempotence 特性。

在 .travis.ymlscript 的最後一段「驗證 Roles 執行成果」,你可以另外撰寫一些方法來再次驗證 Roles 是否有正確執行。

# Test whether Docker is running correctly (Dockerception!).
- docker exec --tty ${container_id} docker run hello-world

上述的範例為例,這是來自於 install docker 的 Role,所以它就是採用是否能順利執行 docker run 指令來當作驗證。同理,如果是 install nginx 或其他軟體,就可以修改為透過執行其他的指令作為驗證。


套用 geerlingguy 大神的測試方法

最後說明怎麼套用 geerlingguy 的測試方法,如以下步驟:

  • 在你的 Roles 內建立 tests 資料夾。
  • tests 資料夾內,建立 test.yml 或其他測試用 playbook。

test.yml 只需要以下的內容即可。

- hosts: all
roles:
- role_under_test

如果在執行 roles 之前有其他需要完成的前置動作,可以透過 pre_tasks,下面即是其中一個範例。

---
- hosts: all
pre_tasks:
- name: Update apt cache.
apt: update_cache=yes
when: ansible_os_family == 'Debian'
- name: Install test dependencies.
package: name=curl state=present
roles:
- role_under_test
  • 如果有相依於其他的 Roles,請建立 tests/requirements.ymlrequirements.yml 的格式就請參閱 Ansible 官方文件
  • 最後建立 .travis.yml

就如前面的說明,請包含以下的內容,然後根據你的狀況微調。

services: docker

env:
- distro: centos7

script:
# Configure test script so we can run extra tests after playbook is run.
- export container_id=$(date +%s)
- export cleanup=false

# Download test shim.
- wget -O ${PWD}/tests/test.sh https://gist.githubusercontent.com/geerlingguy/73ef1e5ee45d8694570f334be385e181/raw/
- chmod +x ${PWD}/tests/test.sh

# Run tests.
- ${PWD}/tests/test.sh

# Test whether Docker is running correctly (Dockerception!).
# 根據你是否要另外驗證 Roles 成果來設置,例如下兩行的範例
# - docker exec --tty ${container_id} docker run hello-world
# - 'docker exec ${container_id} ls /usr/bin/docker | grep "/usr/bin/docker"'

notifications:
webhooks: https://galaxy.ansible.com/api/v1/notifications/

最後,一個小提醒,geerlingguy 的 shell script 是採用 MIT 授權,因此大家其實可以複製一份改存在自己的 gist,並且將其調整得更加吻合你自己的使用情境。

小結

geerlingguy 大神不愧是 Ansible 圈內的先驅者,跟著大神的腳步走一遭,可以讓人學習到不少 Ansible 的相關技巧。

在 geerlingguy 的測試方式中,可以看見他是如何善用 Docker 協助測試、利用 Docker 建立符合自己測試需求的測試環境、重視 Roles 是否具備 idempotence 特性,以及最終再一次透過非 Ansible 的方式進行驗證,確保 Roles 有確實執行成功。

如果你不清楚該如何開始起步測試 Ansible Roles,相信跟著 geerlingguy 大神的腳步將會是一條不錯的捷徑,希望大家都能寫出跟大神一樣高品質的 Roles。

如果您也有看見任何優質的 Ansible 相關技巧,不論是使用上的小撇步、厲害的 Roles 或其他優質的 Roles 測試方法,也歡迎與我分享!


Like what you read? Give Chengwei Chen a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.