Skip to content

Commit 99390fd

Browse files
authored
Test case verification using testinfra (#17)
1 parent 421a186 commit 99390fd

10 files changed

+209
-175
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.pytest_cache/
2+
*.pyc

.travis.yml

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ language: python
77
python: 3.6
88

99
env:
10-
- ANSIBLE_VER=2.5.2
11-
- ANSIBLE_VER=2.6.1
10+
- ANSIBLE_VER=2.5.2 TESTINFRA_VER=1.14.0
11+
- ANSIBLE_VER=2.6.1 TESTINFRA_VER=1.14.0
1212

1313
sudo: true
1414

@@ -30,18 +30,20 @@ before_install:
3030

3131
install:
3232
- sudo apt-get install sshpass
33+
- pip install pytest
3334
- pip install ansible==$ANSIBLE_VER
34-
- pip install ansible-lint
35+
- pip install testinfra==$TESTINFRA_VER
3536

3637
script:
3738
- |
3839
for testcase in $(ls tests/cases/*.{yml,yaml}); do
39-
echo -e "\033[35m ===================================================== \033[0m"
40-
echo -e "\033[35m === $testcase \033[0m"
41-
echo -e "\033[35m ===================================================== \033[0m"
42-
docker-compose -f tests/docker-compose.yml up -d
43-
ansible-playbook tests/test-role.yml --extra-vars="test_case=$(basename ${testcase})" -v || exit 1
44-
docker-compose -f tests/docker-compose.yml down
40+
echo -e "\033[35m ==================================================================== \033[0m"
41+
echo -e "\033[35m ===== $(basename ${testcase%.*}) \033[0m"
42+
echo -e "\033[35m ==================================================================== \033[0m"
43+
docker-compose -f tests/docker-compose.yml up -d &> /dev/null
44+
ansible-playbook tests/cases/$(basename $testcase) -v || true
45+
py.test --hosts='ansible://containers' --ansible-inventory='/etc/ansible/hosts' tests/cases/$(basename ${testcase%.*})_spec.py || exit 1
46+
docker-compose -f tests/docker-compose.yml down &> /dev/null
4547
done
4648
4749
jobs:
@@ -52,6 +54,8 @@ jobs:
5254
include:
5355
- stage: lint
5456
before_install: []
57+
install:
58+
- pip install ansible-lint
5559
script:
5660
- ansible-lint -p .
5761

tests/README.md

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,22 @@ Then in `docker-compose.yml`, add this environment as a new container using a ne
1313

1414
## New Test Case
1515

16-
Create a new test case file in `cases/<test-name>.yml`. Test cases should follow the following principles:
16+
Test cases consists of 2 components.
1717

18-
* Filename should be meaningful, and use dashes (-) to delimit words.
19-
* First line of the test case file should contain a description of the test (as comments).
18+
1. Ansible playbook to prepare and execute the role.
19+
2. Pytest file to verify the state of the container after running the role.
20+
21+
### Ansible playbook test
22+
23+
Create a new test case file in `cases/<test-name>.yml`. The test case should follow the following principles:
24+
25+
* Filename should be meaningful to the test, using dashes (-) to delimit words.
26+
* The playbook should have some comments at the top to describe the test.
27+
* Use `pre_tasks` to do any setup for the test, and use `tasks` to actually execute the role.
2028
* The test should not rely on variables defined in the role. Instead, the test should override the variables.
21-
* The test case should be split into 3 parts (with comments in the file indicating each part)
22-
* **Setup** stage which prepares your variables, and/or environment
23-
* **Run** stage which imports and runs the role
24-
* **Verification** stage which checks the changes were made correctly
2529

26-
After creating the test case file under `cases/`, the travis ci should automatically pick up the test case and run it against all environments.
30+
### Verification using Pytest
31+
32+
Create a new test case spec file in `cases/<test-name>_spec.py`. The `<test-name>` should match the one for the corresponding ansible playbook file so that the CI can associate the two. Make use of the `pytest` testing framework with the `testinfra` plugin to write your verification.
33+
34+
After creating the test case files under `tests/cases/`, the travis ci should automatically pick up the test case and run it against all environments.

tests/cases/add-user-group.yml

Lines changed: 34 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3,59 +3,38 @@
33
### Description
44
# Test a generic adding of both user and group.
55

6-
### Setup
6+
- hosts: containers
7+
pre_tasks:
8+
- name: Set variables
9+
set_fact:
10+
um_uid_base: 10000
11+
um_gid_base: 10000
12+
um_group_inventory:
13+
- name: customgroup
14+
rel_gid: 1
15+
present:
16+
- all
17+
absent: []
18+
sudoer: |
19+
%customgroup ALL=(ALL) ALL
20+
um_user_inventory:
21+
- name: foo
22+
rel_uid: 1
23+
present:
24+
- all
25+
absent: []
26+
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
27+
groups:
28+
- customgroup
29+
- name: bar
30+
rel_uid: 2
31+
present:
32+
- all
33+
absent: []
34+
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
35+
36+
tasks:
37+
- name: Run role
38+
include_role:
39+
name: "user_management"
740

8-
- name: Set variables
9-
set_fact:
10-
um_uid_base: 10000
11-
um_gid_base: 10000
12-
um_group_inventory:
13-
- name: customgroup
14-
rel_gid: 1
15-
present:
16-
- all
17-
absent: []
18-
sudoer: |
19-
%customgroup ALL=(ALL) ALL
20-
um_user_inventory:
21-
- name: foo
22-
rel_uid: 1
23-
present:
24-
- all
25-
absent: []
26-
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
27-
groups:
28-
- customgroup
29-
- name: bar
30-
rel_uid: 2
31-
present:
32-
- all
33-
absent: []
34-
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
35-
36-
37-
### Run
38-
39-
- name: Run role
40-
include_role:
41-
name: "user_management"
42-
43-
44-
### Verification
45-
46-
- name: Verify user exists and has correct UID
47-
shell: "id -u {{ item.name }} | grep '^{{ item.rel_uid + um_uid_base }}$'"
48-
loop: "{{ um_user_inventory }}"
49-
50-
- name: Verify group exists and has correct GID
51-
shell: "getent group {{ item.name }} | cut -d: -f3 | grep '^{{ item.rel_gid + um_gid_base }}$'"
52-
loop: "{{ um_group_inventory }}"
53-
54-
- name: Verify user 'foo' is in group
55-
shell: "id -Gn foo | grep -w customgroup"
56-
57-
- name: Verify user 'bar' is not in group
58-
shell: "! id -Gn bar | grep -w customgroup"
59-
60-
- name: Verify sudoer file exists
61-
shell: "ls -1 /etc/sudoers.d/ | grep '^rel_gid_1$'"

tests/cases/add-user-group_spec.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
3+
4+
@pytest.mark.parametrize("user,uid", [
5+
("foo", 10001),
6+
("bar", 10002),
7+
])
8+
def test_user_exists_and_uid(host, user, uid):
9+
user = host.user(user)
10+
assert user.exists
11+
assert user.uid == uid
12+
13+
14+
def test_group_exists_and_gid(host):
15+
group = host.group("customgroup")
16+
assert group.exists
17+
assert group.gid == 10001
18+
19+
20+
def test_user1_in_group(host):
21+
user = host.user("foo")
22+
assert "customgroup" in user.groups
23+
24+
25+
def test_user2_not_in_group(host):
26+
user = host.user("bar")
27+
assert "customgroup" not in user.groups
28+
29+
30+
def test_group_sudoers_file(host):
31+
file = host.file("/etc/sudoers.d/rel_gid_1")
32+
assert file.exists and file.is_file
33+
assert file.mode == 0o440

tests/cases/delete-user-group.yml

Lines changed: 44 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -3,60 +3,47 @@
33
### Description
44
# Test setting absent to 'all' for both user and groups.
55

6-
### Setup
7-
8-
- name: Set variables
9-
set_fact:
10-
um_uid_base: 10000
11-
um_gid_base: 10000
12-
um_group_inventory:
13-
- name: customgroup
14-
rel_gid: 1
15-
present:
16-
- all
17-
absent:
18-
- all
19-
um_user_inventory:
20-
- name: foo
21-
rel_uid: 1
22-
present:
23-
- all
24-
absent:
25-
- all
26-
- name: bar
27-
rel_uid: 2
28-
present:
29-
- all
30-
absent:
31-
- all
32-
33-
- name: Set the group to be removed
34-
loop: "{{ um_group_inventory }}"
35-
group:
36-
name: "{{ item.name }}"
37-
gid: "{{ um_gid_base + item.rel_gid }}"
38-
39-
- name: Set the users to be removed
40-
loop: "{{ um_user_inventory }}"
41-
user:
42-
name: "{{ item.name }}"
43-
uid: "{{ um_uid_base + item.rel_uid }}"
44-
group: customgroup
45-
46-
47-
### Run
48-
49-
- name: Run role
50-
include_role:
51-
name: "user_management"
52-
53-
54-
### Verification
55-
56-
- name: Verify users don't exist
57-
shell: "! id {{ item.name }}"
58-
loop: "{{ um_user_inventory }}"
59-
60-
- name: Verify groups don't exist
61-
shell: "! getent group {{ item.name }}"
62-
loop: "{{ um_group_inventory }}"
6+
- hosts: containers
7+
pre_tasks:
8+
- name: Set variables
9+
set_fact:
10+
um_uid_base: 10000
11+
um_gid_base: 10000
12+
um_group_inventory:
13+
- name: customgroup
14+
rel_gid: 1
15+
present:
16+
- all
17+
absent:
18+
- all
19+
um_user_inventory:
20+
- name: foo
21+
rel_uid: 1
22+
present:
23+
- all
24+
absent:
25+
- all
26+
- name: bar
27+
rel_uid: 2
28+
present:
29+
- all
30+
absent:
31+
- all
32+
33+
- name: Set the group to be removed
34+
loop: "{{ um_group_inventory }}"
35+
group:
36+
name: "{{ item.name }}"
37+
gid: "{{ um_gid_base + item.rel_gid }}"
38+
39+
- name: Set the users to be removed
40+
loop: "{{ um_user_inventory }}"
41+
user:
42+
name: "{{ item.name }}"
43+
uid: "{{ um_uid_base + item.rel_uid }}"
44+
group: customgroup
45+
46+
tasks:
47+
- name: Run role
48+
include_role:
49+
name: "user_management"

tests/cases/delete-user-group_spec.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import pytest
2+
3+
4+
@pytest.mark.parametrize("user", [
5+
("foo"),
6+
("bar"),
7+
])
8+
def test_user_not_exist(host, user):
9+
user = host.user(user)
10+
assert not user.exists
11+
12+
13+
def test_group_not_exist(host):
14+
group = host.group("customgroup")
15+
assert not group.exists

tests/cases/minimal-fields.yml

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,24 @@
33
### Description
44
# Test adding user and group using the minimal set of fields to pass into user/group inventory.
55

6-
### Setup
7-
8-
- name: Set variables
9-
set_fact:
10-
um_uid_base: 10000
11-
um_gid_base: 10000
12-
um_group_inventory:
13-
- name: customgroup
14-
rel_gid: 1
15-
um_user_inventory:
16-
- name: foo
17-
rel_uid: 1
18-
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
19-
- name: bar
20-
rel_uid: 2
21-
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
22-
23-
24-
### Run
25-
26-
- name: Run role
27-
include_role:
28-
name: "user_management"
29-
30-
31-
### Verification
32-
33-
- name: Verify user exists and has correct UID
34-
shell: "id -u {{ item.name }} | grep '^{{ item.rel_uid + um_uid_base }}$'"
35-
loop: "{{ um_user_inventory }}"
36-
37-
- name: Verify group exists and has correct GID
38-
shell: "getent group {{ item.name }} | cut -d: -f3 | grep '^{{ item.rel_gid + um_gid_base }}$'"
39-
loop: "{{ um_group_inventory }}"
40-
41-
- name: Verify user 'foo' is not in group
42-
shell: "! id -Gn foo | grep -w customgroup"
43-
44-
- name: Verify sudoer file does not exist
45-
shell: "! ls -1 /etc/sudoers.d/ | grep '^rel_gid_1$'"
6+
- hosts: containers
7+
pre_tasks:
8+
- name: Set variables
9+
set_fact:
10+
um_uid_base: 10000
11+
um_gid_base: 10000
12+
um_group_inventory:
13+
- name: customgroup
14+
rel_gid: 1
15+
um_user_inventory:
16+
- name: foo
17+
rel_uid: 1
18+
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
19+
- name: bar
20+
rel_uid: 2
21+
passwd: $6$CH4pzUgP$PJHNOCKVcWA5ai0kU5qwTkCeK08sx86T6o9exHz7Ki7zN/ip6pPBecr6RYqCaBNDSVobuzwwREHu8KbgNncxf1
22+
23+
tasks:
24+
- name: Run role
25+
include_role:
26+
name: "user_management"

0 commit comments

Comments
 (0)