initial project commit

This commit is contained in:
2024-02-05 16:28:05 -05:00
commit 5861055c15
3299 changed files with 458518 additions and 0 deletions

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
output/
integration/inventory

View File

@ -0,0 +1,9 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# See template for more information:
# https://github.com/ansible/ansible/blob/devel/test/lib/ansible_test/config/config.yml
modules:
python_requires: default

View File

@ -0,0 +1,9 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
collections:
- ansible.posix
- community.crypto
- community.docker

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# No AIX LPAR available
unsupported

View File

@ -0,0 +1,81 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Scan new devices.
aix_devices:
device: all
state: present
- name: Scan new virtual devices (vio0).
aix_devices:
device: vio0
state: present
- name: Removing IP alias to en0
aix_devices:
device: en0
attributes:
delalias4: 10.0.0.100,255.255.255.0
- name: Removes ent2.
aix_devices:
device: ent2
state: absent
- name: Put device en2 in Defined
aix_devices:
device: en2
state: defined
- name: Removes ent4 (inexistent).
aix_devices:
device: ent4
state: absent
- name: Put device en4 in Defined (inexistent)
aix_devices:
device: en4
state: defined
- name: Put vscsi1 and children devices in Defined state.
aix_devices:
device: vscsi1
recursive: true
state: defined
- name: Removes vscsi1 and children devices.
aix_devices:
device: vscsi1
recursive: true
state: absent
- name: Changes en1 mtu to 9000 and disables arp.
aix_devices:
device: en1
attributes:
mtu: 900
arp: 'off'
state: present
- name: Configure IP, netmask and set en1 up.
aix_devices:
device: en1
attributes:
netaddr: 192.168.0.100
netmask: 255.255.255.0
state: up
state: present
- name: Adding IP alias to en0
aix_devices:
device: en0
attributes:
alias4: 10.0.0.100,255.255.255.0
state: present

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
unsupported

View File

@ -0,0 +1,130 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Umounting /testfs
aix_filesystem:
filesystem: /testfs
state: unmounted
- name: Removing /testfs
aix_filesystem:
filesystem: /testfs
state: absent
- name: Creating a new file system
aix_filesystem:
filesystem: /newfs
size: 1G
state: present
vg: datavg
# It requires a host (nfshost) exporting the NFS
- name: Creating NFS filesystem from nfshost (Linux NFS server)
aix_filesystem:
device: /home/ftp
nfs_server: nfshost
filesystem: /nfs/ftp
state: present
# It requires a volume group named datavg (next three actions)
- name: Creating a logical volume testlv (aix_lvol module)
aix_lvol:
vg: datavg
lv: testlv
size: 2G
state: present
- name: Create filesystem in a previously defined logical volume
aix_filesystem:
device: testlv
filesystem: /testfs
state: present
- name: Create an already existing filesystem using existing logical volume.
aix_filesystem:
vg: datavg
device: mksysblv
filesystem: /mksysb
state: present
- name: Create a filesystem in a non-existing VG
aix_filesystem:
vg: nonexistvg
filesystem: /newlv
state: present
- name: Resizing /mksysb to 1G
aix_filesystem:
filesystem: /mksysb
size: 1G
state: present
- name: Resizing /mksysb to +512M
aix_filesystem:
filesystem: /mksysb
size: +512M
state: present
- name: Resizing /mksysb to 11G
aix_filesystem:
filesystem: /mksysb
size: 11G
state: present
- name: Resizing /mksysb to 11G (already done)
aix_filesystem:
filesystem: /mksysb
size: 11G
state: present
- name: Resizing /mksysb to -2G
aix_filesystem:
filesystem: /mksysb
size: -2G
state: present
- name: Resizing /mksysb to 100G (not enough space)
aix_filesystem:
filesystem: /mksysb
size: +100G
state: present
- name: Unmount filesystem /home/ftp
aix_filesystem:
filesystem: /home/ftp
state: unmounted
- name: Remove NFS filesystem /home/ftp
aix_filesystem:
filesystem: /home/ftp
rm_mount_point: true
state: absent
- name: Mount filesystem /newfs
aix_filesystem:
filesystem: /newfs
state: mounted
- name: Remove mounted /newfs
aix_filesystem:
filesystem: /newfs
rm_mount_point: true
state: absent
- name: Umount /newfs
aix_filesystem:
filesystem: /newfs
state: unmounted
- name: Remove /newfs
aix_filesystem:
filesystem: /newfs
rm_mount_point: true
state: absent

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
disabled

View File

@ -0,0 +1,9 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
alerta_url: http://localhost:8080/
alerta_user: admin@example.com
alerta_password: password
alerta_key: demo-key

View File

@ -0,0 +1,156 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create customer (check mode)
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
check_mode: true
register: result
- name: Check result (check mode)
assert:
that:
- result is changed
- name: Create customer
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
register: result
- name: Check customer creation
assert:
that:
- result is changed
- name: Test customer creation idempotency
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
register: result
- name: Check customer creation idempotency
assert:
that:
- result is not changed
- name: Delete customer (check mode)
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
state: absent
check_mode: true
register: result
- name: Check customer deletion (check mode)
assert:
that:
- result is changed
- name: Delete customer
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
state: absent
register: result
- name: Check customer deletion
assert:
that:
- result is changed
- name: Test customer deletion idempotency
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
state: absent
register: result
- name: Check customer deletion idempotency
assert:
that:
- result is not changed
- name: Delete non-existing customer (check mode)
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_username: "{{ alerta_user }}"
api_password: "{{ alerta_password }}"
customer: customer1
match: admin@admin.admin
state: absent
check_mode: true
register: result
- name: Check non-existing customer deletion (check mode)
assert:
that:
- result is not changed
- name: Create customer with api key
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_key: "{{ alerta_key }}"
customer: customer1
match: admin@admin.admin
register: result
- name: Check customer creation with api key
assert:
that:
- result is changed
- name: Delete customer with api key
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_key: "{{ alerta_key }}"
customer: customer1
match: admin@admin.admin
state: absent
register: result
- name: Check customer deletion with api key
assert:
that:
- result is changed
- name: Use wrong api key
alerta_customer:
alerta_url: "{{ alerta_url }}"
api_key: wrong_key
customer: customer1
match: admin@admin.admin
register: result
ignore_errors: true
- name: Check customer creation with api key
assert:
that:
- result is not changed
- result is failed

View File

@ -0,0 +1,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
destructive
needs/root
skip/aix
skip/freebsd
skip/osx
skip/macos

View File

@ -0,0 +1,93 @@
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) 2017 Pierre-Louis Bonicoli <pierre-louis.bonicoli@libregerbil.fr>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: 'setup: create a dummy alternative'
block:
- import_tasks: setup.yml
##############
# Test parameters:
# link parameter present / absent ('with_link' variable)
# with / without alternatives defined in alternatives file ('with_alternatives' variable)
# auto / manual ('mode' variable)
- include_tasks: tests.yml
with_nested:
- [ true, false ] # with_link
- [ true, false ] # with_alternatives
- [ 'auto', 'manual' ] # mode
loop_control:
loop_var: test_conf
##########
# Priority
- block:
- include_tasks: remove_links.yml
- include_tasks: setup_test.yml
# at least two iterations again
- include_tasks: tests_set_priority.yml
with_sequence: start=3 end=4
vars:
with_alternatives: true
mode: auto
- block:
- include_tasks: remove_links.yml
- include_tasks: setup_test.yml
# at least two iterations again
- include_tasks: tests_set_priority.yml
with_sequence: start=3 end=4
vars:
with_alternatives: false
mode: auto
# Test that path is checked: alternatives must fail when path is nonexistent
- import_tasks: path_is_checked.yml
# Test that subcommands commands work
- import_tasks: subcommands.yml
# Test operation of the 'state' parameter
- block:
- include_tasks: remove_links.yml
- include_tasks: tests_state.yml
# Cleanup
always:
- include_tasks: remove_links.yml
- file:
path: '{{ item }}'
state: absent
with_items:
- '{{ alternatives_dir }}/dummy'
- '{{ alternatives_dir }}/dummymain'
- '{{ alternatives_dir }}/dummysubcmd'
- file:
path: '/usr/bin/dummy{{ item }}'
state: absent
with_sequence: start=1 end=4
# *Disable tests on Fedora 24*
# Shippable Fedora 24 image provides chkconfig-1.7-2.fc24.x86_64 but not the
# latest available version (chkconfig-1.8-1.fc24.x86_64). update-alternatives
# in chkconfig-1.7-2 fails when /etc/alternatives/dummy link is missing,
# error is: 'failed to read link /usr/bin/dummy: No such file or directory'.
# Moreover Fedora 24 is no longer maintained.
#
# *Disable tests on Arch Linux*
# TODO: figure out whether there is an alternatives tool for Arch Linux
#
# *Disable tests on Alpine*
# TODO: figure out whether there is an alternatives tool for Alpine
when:
- ansible_distribution != 'Fedora' or ansible_distribution_major_version|int > 24
- ansible_distribution != 'Archlinux'
- ansible_distribution != 'Alpine'

View File

@ -0,0 +1,17 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Try with nonexistent path
alternatives:
name: dummy
path: '/non/existent/path/there'
link: '/usr/bin/dummy'
ignore_errors: true
register: alternative
- name: Check previous task failed
assert:
that:
- 'alternative is failed'

View File

@ -0,0 +1,13 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: remove links
file:
path: '{{ item }}'
state: absent
with_items:
- "{{ alternatives_dir }}/dummy"
- /etc/alternatives/dummy
- /usr/bin/dummy

View File

@ -0,0 +1,19 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- include_vars: '{{ item }}'
with_first_found:
- files:
- '{{ ansible_os_family }}-{{ ansible_distribution_version }}.yml'
- '{{ ansible_os_family }}.yml'
- default.yml
paths: ../vars
- template:
src: dummy_command
dest: /usr/bin/dummy{{ item }}
owner: root
group: root
mode: '0755'
with_sequence: start=1 end=4

View File

@ -0,0 +1,16 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- template:
src: dummy_alternative
dest: '{{ alternatives_dir }}/dummy'
owner: root
group: root
mode: '0644'
when: with_alternatives or ansible_os_family != 'RedHat'
- file:
path: '{{ alternatives_dir }}/dummy'
state: absent
when: not with_alternatives and ansible_os_family == 'RedHat'

View File

@ -0,0 +1,222 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Try with subcommands
alternatives:
name: dummymain
path: '/usr/bin/dummy1'
link: '/usr/bin/dummymain'
subcommands:
- name: dummysubcmd
path: '/usr/bin/dummy2'
link: '/usr/bin/dummysubcmd'
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is changed'
- name: Execute the current dummymain command
command: dummymain
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy1"
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy2"
- name: Get dummymain alternatives output
command:
cmd: '{{ alternatives_command }} --display dummymain'
register: result
- name: Print result
debug:
var: result.stdout_lines
- name: Subcommands are not removed if not specified
alternatives:
name: dummymain
path: '/usr/bin/dummy1'
link: '/usr/bin/dummymain'
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is not changed'
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy2"
- name: Subcommands are removed if set to an empty list
alternatives:
name: dummymain
path: '/usr/bin/dummy1'
link: '/usr/bin/dummymain'
subcommands: []
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is changed'
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
ignore_errors: true
- name: Ensure that the subcommand is gone
assert:
that:
- cmd.rc == 2
- '"No such file" in cmd.msg'
- name: Get dummymain alternatives output
command:
cmd: '{{ alternatives_command }} --display dummymain'
register: result
- name: Print result
debug:
var: result.stdout_lines
- name: Install other alternative with subcommands
alternatives:
name: dummymain
path: '/usr/bin/dummy3'
link: '/usr/bin/dummymain'
subcommands:
- name: dummysubcmd
path: '/usr/bin/dummy4'
link: '/usr/bin/dummysubcmd'
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is changed'
- name: Execute the current dummymain command
command: dummymain
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy3"
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy4"
- name: Get dummymain alternatives output
command:
cmd: '{{ alternatives_command }} --display dummymain'
register: result
- name: Print result
debug:
var: result.stdout_lines
- name: Switch to first alternative
alternatives:
name: dummymain
path: '/usr/bin/dummy1'
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is changed'
- name: Execute the current dummymain command
command: dummymain
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy1"
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
ignore_errors: true
- name: Ensure that the subcommand is gone
assert:
that:
- cmd.rc == 2
- '"No such file" in cmd.msg'
- name: Get dummymain alternatives output
command:
cmd: '{{ alternatives_command }} --display dummymain'
register: result
- name: Print result
debug:
var: result.stdout_lines
- name: Switch to second alternative
alternatives:
name: dummymain
path: '/usr/bin/dummy3'
register: alternative
- name: Check expected command was executed
assert:
that:
- 'alternative is changed'
- name: Execute the current dummymain command
command: dummymain
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy3"
- name: Execute the current dummysubcmd command
command: dummysubcmd
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy4"
- name: Get dummymain alternatives output
command:
cmd: '{{ alternatives_command }} --display dummymain'
register: result
- name: Print result
debug:
var: result.stdout_lines

View File

@ -0,0 +1,56 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- debug:
msg: ' with_alternatives: {{ with_alternatives }}, mode: {{ mode }}'
- block:
- name: set alternative (using link parameter)
alternatives:
name: dummy
path: '/usr/bin/dummy{{ item }}'
link: '/usr/bin/dummy'
register: alternative
- name: check expected command was executed
assert:
that:
- 'alternative is successful'
- 'alternative is changed'
when: with_link
- block:
- name: set alternative (without link parameter)
alternatives:
name: dummy
path: '/usr/bin/dummy{{ item }}'
register: alternative
- name: check expected command was executed
assert:
that:
- 'alternative is successful'
- 'alternative is changed'
when: not with_link
- name: execute dummy command
shell: dummy
register: cmd
- name: check expected command was executed
assert:
that:
- 'cmd.stdout == "dummy" ~ item'
- name: 'check mode (manual: alternatives file existed, it has been updated)'
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
when: ansible_os_family != 'RedHat' or with_alternatives or item != 1
- name: 'check mode (auto: alternatives file didn''t exist, it has been created)'
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
when: ansible_os_family == 'RedHat' and not with_alternatives and item == 1
- name: check that alternative has been updated
command: "grep -Pzq '/bin/dummy{{ item }}\\n' '{{ alternatives_dir }}/dummy'"

View File

@ -0,0 +1,20 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- block:
- include_tasks: remove_links.yml
- include_tasks: setup_test.yml
# at least two iterations:
# - first will use 'link currently absent',
# - second will receive 'link currently points to'
- include_tasks: test.yml
with_sequence: start=1 end=2
vars:
with_link: '{{ test_conf[0] }}'
with_alternatives: '{{ test_conf[1] }}'
mode: '{{ test_conf[2] }}'
# update-alternatives included in Fedora 26 (1.10) & Red Hat 7.4 (1.8) doesn't provide
# '--query' switch, 'link' is mandatory for these distributions.
when: ansible_os_family != 'RedHat' or test_conf[0]

View File

@ -0,0 +1,54 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: update dummy alternative
alternatives:
name: dummy
path: '/usr/bin/dummy{{ item }}'
link: /usr/bin/dummy
priority: '{{ 60 + item|int }}'
register: alternative
- name: execute dummy command
shell: dummy
register: cmd
- name: check if link group is in manual mode
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
- name: check expected command was executed
assert:
that:
- 'alternative is changed'
- 'cmd.stdout == "dummy" ~ item'
- name: check that alternative has been updated
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 60 + item|int }}' '{{ alternatives_dir }}/dummy'"
- name: update dummy priority
alternatives:
name: dummy
path: '/usr/bin/dummy{{ item }}'
link: /usr/bin/dummy
priority: '{{ 70 + item|int }}'
register: alternative
- name: check that alternative priority has been updated
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 70 + item|int }}' '{{ alternatives_dir }}/dummy'"
- name: no change without priority
alternatives:
name: dummy
path: '/usr/bin/dummy{{ item }}'
link: /usr/bin/dummy
register: alternative
- name: check no change was triggered without priority
assert:
that:
- 'alternative is not changed'
- name: check that alternative priority has not been changed
command: "grep -Pzq '/bin/dummy{{ item }}\\n{{ 70 + item|int }}' '{{ alternatives_dir }}/dummy'"

View File

@ -0,0 +1,120 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Add a few dummy alternatives with state = present and make sure that the
# group is in 'auto' mode and the highest priority alternative is selected.
- name: Add some dummy alternatives with state = present
alternatives:
name: dummy
path: "/usr/bin/dummy{{ item.n }}"
link: /usr/bin/dummy
priority: "{{ item.priority }}"
state: present
loop:
- { n: 1, priority: 50 }
- { n: 2, priority: 70 }
- { n: 3, priority: 25 }
- name: Ensure that the link group is in auto mode
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
# Execute current selected 'dummy' and ensure it's the alternative we expect
- name: Execute the current dummy command
shell: dummy
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy2"
# Add another alternative with state = 'selected' and make sure that
# this change results in the group being set to manual mode, and the
# new alternative being the selected one.
- name: Add another dummy alternative with state = selected
alternatives:
name: dummy
path: /usr/bin/dummy4
link: /usr/bin/dummy
priority: 10
state: selected
- name: Ensure that the link group is in manual mode
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
- name: Execute the current dummy command
shell: dummy
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy4"
# Set the currently selected alternative to state = 'present' (was previously
# selected), and ensure that this results in the group not being set to 'auto'
# mode, and the alternative is still selected.
- name: Set current selected dummy to state = present
alternatives:
name: dummy
path: /usr/bin/dummy4
link: /usr/bin/dummy
state: present
- name: Ensure that the link group is in auto mode
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^manual$"'
- name: Execute the current dummy command
shell: dummy
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy4"
# Set the currently selected alternative to state = 'auto' (was previously
# selected), and ensure that this results in the group being set to 'auto'
# mode, and the highest priority alternative is selected.
- name: Set current selected dummy to state = present
alternatives:
name: dummy
path: /usr/bin/dummy4
link: /usr/bin/dummy
state: auto
- name: Ensure that the link group is in auto mode
shell: 'head -n1 {{ alternatives_dir }}/dummy | grep "^auto$"'
- name: Execute the current dummy command
shell: dummy
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy2"
# Remove an alternative with state = 'absent' and make sure that
# this change results in the alternative being removed.
- name: Remove best dummy alternative with state = absent
alternatives:
name: dummy
path: /usr/bin/dummy2
state: absent
- name: Ensure that the link group is in auto mode
shell: 'grep "/usr/bin/dummy2" {{ alternatives_dir }}/dummy'
register: cmd
failed_when: cmd.rc == 0
- name: Execute the current dummy command
shell: dummy
register: cmd
- name: Ensure that the expected command was executed
assert:
that:
- cmd.stdout == "dummy1"

View File

@ -0,0 +1,17 @@
{#
Copyright (c) Ansible Project
GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
SPDX-License-Identifier: GPL-3.0-or-later
#}
{{ mode }}
/usr/bin/dummy
{% if with_alternatives %}
/usr/bin/dummy1
40
/usr/bin/dummy2
30
{% else %}
{% endif %}

View File

@ -0,0 +1,6 @@
#!/bin/sh
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
echo dummy{{ item }}

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
alternatives_dir: /var/lib/dpkg/alternatives/
alternatives_command: update-alternatives

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
alternatives_dir: /var/lib/rpm/alternatives/
alternatives_command: update-alternatives

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
alternatives_dir: /var/lib/alternatives/
alternatives_command: update-alternatives

View File

@ -0,0 +1,7 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
destructive
context/controller # While this is not really true, this module mainly is run on the controller, *and* needs access to the ansible-galaxy CLI tool

View File

@ -0,0 +1,15 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
roles:
# Install a role from Ansible Galaxy.
- name: geerlingguy.java
version: 1.9.6
collections:
# Install a collection from Ansible Galaxy.
- name: geerlingguy.php_roles
version: 0.9.3
source: https://galaxy.ansible.com

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_remote_tmp_dir

View File

@ -0,0 +1,88 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
###################################################
- name: Install collection netbox.netbox
community.general.ansible_galaxy_install:
type: collection
name: netbox.netbox
register: install_c0
- name: Assert collection netbox.netbox was installed
assert:
that:
- install_c0 is changed
- '"netbox.netbox" in install_c0.new_collections'
- name: Install collection netbox.netbox (again)
community.general.ansible_galaxy_install:
type: collection
name: netbox.netbox
register: install_c1
- name: Assert collection was not installed
assert:
that:
- install_c1 is not changed
###################################################
- name: Install role ansistrano.deploy
community.general.ansible_galaxy_install:
type: role
name: ansistrano.deploy
register: install_r0
- name: Assert collection ansistrano.deploy was installed
assert:
that:
- install_r0 is changed
- '"ansistrano.deploy" in install_r0.new_roles'
- name: Install role ansistrano.deploy (again)
community.general.ansible_galaxy_install:
type: role
name: ansistrano.deploy
register: install_r1
- name: Assert role was not installed
assert:
that:
- install_r1 is not changed
###################################################
- name: Set requirements file path
set_fact:
reqs_file: '{{ remote_tmp_dir }}/reqs.yaml'
- name: Copy requirements file
copy:
src: 'files/test.yml'
dest: '{{ reqs_file }}'
- name: Install from requirements file
community.general.ansible_galaxy_install:
type: both
requirements_file: "{{ reqs_file }}"
register: install_rq0
ignore_errors: true
- name: Assert requirements file was installed
assert:
that:
- install_rq0 is changed
- '"geerlingguy.java" in install_rq0.new_roles'
- '"geerlingguy.php_roles" in install_rq0.new_collections'
- name: Install from requirements file (again)
community.general.ansible_galaxy_install:
type: both
requirements_file: "{{ reqs_file }}"
register: install_rq1
ignore_errors: true
- name: Assert requirements file was not installed
assert:
that:
- install_rq1 is not changed

View File

@ -0,0 +1,7 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
destructive
skip/aix

View File

@ -0,0 +1,47 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# This test represent the misleading behavior of the following issue: https://github.com/ansible-collections/community.general/issues/635
- name: Disable MPM event module
apache2_module:
name: "{{ item.module}}"
state: "{{ item.state}}"
ignore_configcheck: true
register: disable_mpm_modules
with_items:
- { module: mpm_event, state: absent }
- { module: mpm_prefork, state: present }
- assert:
that:
- "'warnings' in disable_mpm_modules"
- disable_mpm_modules["warnings"] == [
"No MPM module loaded! apache2 reload AND other module actions will fail if no MPM module is loaded immediately.",
"No MPM module loaded! apache2 reload AND other module actions will fail if no MPM module is loaded immediately."
]
- name: Enable MPM event module - Revert previous change
apache2_module:
name: "{{ item.module}}"
state: "{{ item.state}}"
ignore_configcheck: true
register: disable_mpm_modules
with_items:
- { module: mpm_prefork, state: absent }
- { module: mpm_event, state: present }
- name: Disable MPM event module
apache2_module:
name: "{{ item.module}}"
state: "{{ item.state}}"
ignore_configcheck: true
warn_mpm_absent: false
register: disable_mpm_modules
with_items:
- { module: mpm_event, state: absent }
- { module: mpm_prefork, state: present }
- assert:
that:
- "'warnings' not in disable_mpm_modules"

View File

@ -0,0 +1,207 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: disable userdir module
community.general.apache2_module:
name: userdir
state: absent
register: userdir_first_disable
- name: disable userdir module, second run
community.general.apache2_module:
name: userdir
state: absent
register: disable
- name: ensure community.general.apache2_module is idempotent
assert:
that:
- disable is not changed
- name: enable userdir module
community.general.apache2_module:
name: userdir
state: present
register: enable
- name: ensure changed on successful enable
assert:
that:
- enable is changed
- name: enable userdir module, second run
community.general.apache2_module:
name: userdir
state: present
register: enabletwo
- name: ensure community.general.apache2_module is idempotent
assert:
that:
- 'not enabletwo.changed'
- name: disable userdir module, final run
community.general.apache2_module:
name: userdir
state: absent
register: disablefinal
- name: ensure changed on successful disable
assert:
that:
- 'disablefinal.changed'
- name: set userdir to original state
community.general.apache2_module:
name: userdir
state: present
when: userdir_first_disable is changed
- name: ensure autoindex enabled
community.general.apache2_module:
name: autoindex
state: present
- name: Debian/Ubuntu specific tests
when: "ansible_os_family == 'Debian'"
block:
- name: force disable of autoindex # bug #2499
community.general.apache2_module:
name: autoindex
state: absent
force: true
- name: re-enable autoindex
community.general.apache2_module:
name: autoindex
state: present
# mod_evasive is enabled by default upon the installation, so disable first and enable second, to preserve the config
- name: disable evasive module
community.general.apache2_module:
name: evasive
state: absent
- name: enable evasive module, test https://github.com/ansible/ansible/issues/22635
community.general.apache2_module:
name: evasive
state: present
- name: use identifier to enable module, fix for https://github.com/ansible/ansible/issues/33669
community.general.apache2_module:
name: dump_io
state: present
ignore_errors: true
register: enable_dumpio_wrong
- name: disable dump_io
community.general.apache2_module:
name: dump_io
identifier: dumpio_module
state: absent
- name: use identifier to enable module, fix for https://github.com/ansible/ansible/issues/33669
community.general.apache2_module:
name: dump_io
identifier: dumpio_module
state: present
register: enable_dumpio_correct_1
- name: ensure idempotency with identifier
community.general.apache2_module:
name: dump_io
identifier: dumpio_module
state: present
register: enable_dumpio_correct_2
- name: disable dump_io
community.general.apache2_module:
name: dump_io
identifier: dumpio_module
state: absent
- assert:
that:
- enable_dumpio_wrong is failed
- enable_dumpio_correct_1 is changed
- enable_dumpio_correct_2 is not changed
- name: disable mpm modules
community.general.apache2_module:
name: "{{ item }}"
state: absent
ignore_configcheck: true
with_items:
- mpm_worker
- mpm_event
- mpm_prefork
- name: enabled mpm_event
community.general.apache2_module:
name: mpm_event
state: present
ignore_configcheck: true
register: enabledmpmevent
- name: ensure changed mpm_event
assert:
that:
- 'enabledmpmevent.changed'
- name: switch between mpm_event and mpm_worker
community.general.apache2_module:
name: "{{ item.name }}"
state: "{{ item.state }}"
ignore_configcheck: true
with_items:
- name: mpm_event
state: absent
- name: mpm_worker
state: present
- name: ensure mpm_worker is already enabled
community.general.apache2_module:
name: mpm_worker
state: present
register: enabledmpmworker
- name: ensure mpm_worker unchanged
assert:
that:
- 'not enabledmpmworker.changed'
- name: try to disable all mpm modules with configcheck
community.general.apache2_module:
name: "{{item}}"
state: absent
with_items:
- mpm_worker
- mpm_event
- mpm_prefork
ignore_errors: true
register: remove_with_configcheck
- name: ensure configcheck fails task with when run without mpm modules
assert:
that:
- "{{ item.failed }}"
with_items: "{{ remove_with_configcheck.results }}"
- name: try to disable all mpm modules without configcheck
community.general.apache2_module:
name: "{{item}}"
state: absent
ignore_configcheck: true
with_items:
- mpm_worker
- mpm_event
- mpm_prefork
- name: enabled mpm_event to restore previous state
community.general.apache2_module:
name: mpm_event
state: present
ignore_configcheck: true
register: enabledmpmevent

View File

@ -0,0 +1,52 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: install apache via apt
apt:
name: "{{item}}"
state: present
when: "ansible_os_family == 'Debian'"
with_items:
- apache2
- libapache2-mod-evasive
- name: install apache via zypper
community.general.zypper:
name: apache2
state: present
when: "ansible_os_family == 'Suse'"
- name: test apache2_module
block:
- name: get list of enabled modules
shell: apache2ctl -M | sort
register: modules_before
- name: include only on supported systems
include_tasks: actualtest.yml
always:
- name: get list of enabled modules
shell: apache2ctl -M | sort
register: modules_after
- name: modules_before
debug:
var: modules_before
- name: modules_after
debug:
var: modules_after
- name: ensure that all test modules are disabled again
assert:
that: modules_before.stdout == modules_after.stdout
when: ansible_os_family in ['Debian', 'Suse']
# centos/RHEL does not have a2enmod/a2dismod
- name: include misleading warning test
include_tasks: 635-apache2-misleading-warning.yml
when: ansible_os_family in ['Debian']
# Suse has mpm_event module compiled within the base apache2

View File

@ -0,0 +1,9 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
needs/root
destructive
skip/aix
skip/osx # FIXME

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
bar.txt

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
foo.txt

View File

@ -0,0 +1,8 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_pkg_mgr
- setup_remote_tmp_dir

View File

@ -0,0 +1,145 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Test code for the archive module.
# Copyright (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Make sure we start fresh
# Test setup
- name: prep our files
copy: src={{ item }} dest={{remote_tmp_dir}}/{{ item }}
with_items:
- foo.txt
- bar.txt
- empty.txt
- sub
- sub/subfile.txt
# Run twice without lzma backport installed, to make sure it does not crash
- name: Archive - pre-test - first run
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_pretest_1.tar"
format: "tar"
register: pretest_1
- name: Archive - pre-test - second run
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_pretest_1.tar"
format: "tar"
register: pretest_2
- name: Archive - validate pre-test
assert:
that:
- pretest_1 is changed
- pretest_2 is not changed
# Install dependencies
- name: Ensure zip is present to create test archive (yum)
yum: name=zip state=latest
when: ansible_facts.pkg_mgr == 'yum'
- name: Ensure zip is present to create test archive (apt)
apt: name=zip state=latest
when: ansible_facts.pkg_mgr == 'apt'
- name: Install prerequisites for backports.lzma when using python2 (non OSX)
block:
- name: Set liblzma package name depending on the OS
set_fact:
liblzma_dev_package:
Debian: liblzma-dev
RedHat: xz-devel
Suse: xz-devel
- name: Ensure liblzma-dev is present to install backports-lzma
package: name={{ liblzma_dev_package[ansible_os_family] }} state=latest
when: ansible_os_family in liblzma_dev_package.keys()
when:
- ansible_python_version.split('.')[0] == '2'
- ansible_os_family != 'Darwin'
- name: Install prerequisites for backports.lzma when using python2 (OSX)
block:
- name: Find brew binary
command: which brew
register: brew_which
- name: Get owner of brew binary
stat: path="{{ brew_which.stdout }}"
register: brew_stat
- name: "Install package"
homebrew:
name: xz
state: present
update_homebrew: false
become: true
become_user: "{{ brew_stat.stat.pw_name }}"
# Newer versions of brew want to compile a package which takes a long time. Do not upgrade homebrew until a
# proper solution can be found
environment:
HOMEBREW_NO_AUTO_UPDATE: "True"
when:
- ansible_python_version.split('.')[0] == '2'
- ansible_os_family == 'Darwin'
- name: Ensure backports.lzma is present to create test archive (pip)
pip: name=backports.lzma state=latest
when: ansible_python_version.split('.')[0] == '2'
register: backports_lzma_pip
- name: Define formats to test
set_fact:
formats:
- tar
- zip
- gz
- bz2
- xz
# Run tests
- name: Run core tests
include_tasks:
file: ../tests/core.yml
loop: "{{ formats }}"
loop_control:
loop_var: format
- name: Run exclusions tests
include_tasks:
file: ../tests/exclusions.yml
loop: "{{ formats }}"
loop_control:
loop_var: format
- name: Run remove tests
include_tasks:
file: ../tests/remove.yml
loop: "{{ formats }}"
loop_control:
loop_var: format
- name: Run broken link tests
include_tasks:
file: ../tests/broken-link.yml
loop: "{{ formats }}"
loop_control:
loop_var: format
- name: Run Idempotency tests
include_tasks:
file: ../tests/idempotency.yml
loop: "{{ formats }}"
loop_control:
loop_var: format
# Test cleanup
- name: Remove backports.lzma if previously installed (pip)
pip: name=backports.lzma state=absent
when: backports_lzma_pip is changed

View File

@ -0,0 +1,35 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- block:
- name: Create link - broken link ({{ format }})
file:
src: /nowhere
dest: "{{ remote_tmp_dir }}/nowhere.txt"
state: link
force: true
- name: Archive - broken link ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}"
format: "{{ format }}"
- name: Verify archive exists - broken link ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}"
state: file
- name: Remove archive - broken link ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_broken_link.{{ format }}"
state: absent
- name: Remove link - broken link ({{ format }})
file:
path: "{{ remote_tmp_dir }}/nowhere.txt"
state: absent
# 'zip' does not support symlink's
when: format != 'zip'

View File

@ -0,0 +1,177 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Test code for the archive module.
# Copyright (c) 2017, Abhijeet Kasurde <akasurde@redhat.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
# Make sure we start fresh
# Core functionality tests
- name: Archive - no options ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_no_opts.{{ format }}"
format: "{{ format }}"
register: archive_no_options
- name: Verify that archive exists - no options ({{ format }})
file:
path: "{{remote_tmp_dir}}/archive_no_opts.{{ format }}"
state: file
- name: Verify that archive result is changed and includes all files - no options ({{ format }})
assert:
that:
- archive_no_options is changed
- "archive_no_options.dest_state == 'archive'"
- "archive_no_options.archived | length == 3"
- name: Remove the archive - no options ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_no_options.{{ format }}"
state: absent
- name: Archive - file options ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}"
format: "{{ format }}"
mode: "u+rwX,g-rwx,o-rwx"
register: archive_file_options
- name: Retrieve archive file information - file options ({{ format }})
stat:
path: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}"
register: archive_file_options_stat
- name: Test that the file modes were changed
assert:
that:
- archive_file_options_stat is not changed
- "archive_file_options.mode == '0600'"
- "archive_file_options.archived | length == 3"
- name: Remove the archive - file options ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_file_options.{{ format }}"
state: absent
- name: Archive - non-ascii ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}"
format: "{{ format }}"
register: archive_nonascii
- name: Retrieve archive file information - non-ascii ({{ format }})
stat:
path: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}"
register: archive_nonascii_stat
- name: Test that archive exists - non-ascii ({{ format }})
assert:
that:
- archive_nonascii is changed
- archive_nonascii_stat.stat.exists == true
- name: Remove the archive - non-ascii ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_nonascii_くらとみ.{{ format }}"
state: absent
- name: Archive - single target ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/foo.txt"
dest: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}"
format: "{{ format }}"
register: archive_single_target
- name: Assert archive has correct state - single target ({{ format }})
assert:
that:
- archive_single_target.dest_state == state_map[format]
vars:
state_map:
tar: archive
zip: archive
gz: compress
bz2: compress
xz: compress
- block:
- name: Retrieve contents of archive - single target ({{ format }})
ansible.builtin.unarchive:
src: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}"
dest: .
list_files: true
check_mode: true
ignore_errors: true
register: archive_single_target_contents
- name: Assert that file names are preserved - single target ({{ format }})
assert:
that:
- "'oo.txt' not in archive_single_target_contents.files"
- "'foo.txt' in archive_single_target_contents.files"
# ``unarchive`` fails for RHEL and FreeBSD on ansible 2.x
when: archive_single_target_contents is success and archive_single_target_contents is not skipped
when: "format == 'zip'"
- name: Remove archive - single target ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_single_target.{{ format }}"
state: absent
- name: Archive - path list ({{ format }})
archive:
path:
- "{{ remote_tmp_dir }}/empty.txt"
- "{{ remote_tmp_dir }}/foo.txt"
- "{{ remote_tmp_dir }}/bar.txt"
dest: "{{ remote_tmp_dir }}/archive_path_list.{{ format }}"
format: "{{ format }}"
register: archive_path_list
- name: Verify that archive exists - path list ({{ format }})
file:
path: "{{remote_tmp_dir}}/archive_path_list.{{ format }}"
state: file
- name: Assert that archive contains all files - path list ({{ format }})
assert:
that:
- archive_path_list is changed
- "archive_path_list.archived | length == 3"
- name: Remove archive - path list ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_path_list.{{ format }}"
state: absent
- name: Archive - missing paths ({{ format }})
archive:
path:
- "{{ remote_tmp_dir }}/*.txt"
- "{{ remote_tmp_dir }}/dne.txt"
exclude_path: "{{ remote_tmp_dir }}/foo.txt"
dest: "{{ remote_tmp_dir }}/archive_missing_paths.{{ format }}"
format: "{{ format }}"
register: archive_missing_paths
- name: Assert that incomplete archive has incomplete state - missing paths ({{ format }})
assert:
that:
- archive_missing_paths is changed
- "archive_missing_paths.dest_state == 'incomplete'"
- "(remote_tmp_dir ~ '/dne.txt') in archive_missing_paths.missing"
- "(remote_tmp_dir ~ '/foo.txt') not in archive_missing_paths.missing"
- name: Remove archive - missing paths ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_missing_paths.{{ format }}"
state: absent

View File

@ -0,0 +1,44 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Archive - exclusion patterns ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_exclusion_patterns.{{ format }}"
format: "{{ format }}"
exclusion_patterns: b?r.*
register: archive_exclusion_patterns
- name: Assert that only included files are archived - exclusion patterns ({{ format }})
assert:
that:
- archive_exclusion_patterns is changed
- "'bar.txt' not in archive_exclusion_patterns.archived"
- name: Remove archive - exclusion patterns ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_exclusion_patterns.{{ format }}"
state: absent
- name: Archive - exclude path ({{ format }})
archive:
path:
- "{{ remote_tmp_dir }}/sub/subfile.txt"
- "{{ remote_tmp_dir }}"
exclude_path:
- "{{ remote_tmp_dir }}"
dest: "{{ remote_tmp_dir }}/archive_exclude_paths.{{ format }}"
format: "{{ format }}"
register: archive_excluded_paths
- name: Assert that excluded paths do not influence archive root - exclude path ({{ format }})
assert:
that:
- archive_excluded_paths.arcroot != remote_tmp_dir
- name: Remove archive - exclude path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_exclude_paths.{{ format }}"
state: absent

View File

@ -0,0 +1,144 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Archive - file content idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}"
format: "{{ format }}"
register: file_content_idempotency_before
- name: Modify file - file content idempotency ({{ format }})
lineinfile:
line: bar.txt
regexp: "^foo.txt$"
path: "{{ remote_tmp_dir }}/foo.txt"
- name: Archive second time - file content idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}"
format: "{{ format }}"
register: file_content_idempotency_after
- name: Assert task status is changed - file content idempotency ({{ format }})
assert:
that:
- file_content_idempotency_after is changed
# Only ``zip`` archives are guaranteed to compare file content checksums rather than header checksums
when: "format == 'zip'"
- name: Remove archive - file content idempotency ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_file_content_idempotency.{{ format }}"
state: absent
- name: Modify file back - file content idempotency ({{ format }})
lineinfile:
line: foo.txt
regexp: "^bar.txt$"
path: "{{ remote_tmp_dir }}/foo.txt"
- name: Archive - file name idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}"
format: "{{ format }}"
register: file_name_idempotency_before
- name: Rename file - file name idempotency ({{ format }})
command: "mv {{ remote_tmp_dir }}/foo.txt {{ remote_tmp_dir }}/fii.txt"
- name: Archive again - file name idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}"
format: "{{ format }}"
register: file_name_idempotency_after
- name: Check task status - file name idempotency ({{ format }})
assert:
that:
- file_name_idempotency_after is changed
- name: Remove archive - file name idempotency ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_file_name_idempotency.{{ format }}"
state: absent
- name: Rename file back - file name idempotency ({{ format }})
command: "mv {{ remote_tmp_dir }}/fii.txt {{ remote_tmp_dir }}/foo.txt"
- name: Archive - single file content idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/foo.txt"
dest: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}"
format: "{{ format }}"
register: single_file_content_idempotency_before
- name: Modify file - single file content idempotency ({{ format }})
lineinfile:
line: bar.txt
regexp: "^foo.txt$"
path: "{{ remote_tmp_dir }}/foo.txt"
- name: Archive second time - single file content idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/foo.txt"
dest: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}"
format: "{{ format }}"
register: single_file_content_idempotency_after
- name: Assert task status is changed - single file content idempotency ({{ format }})
assert:
that:
- single_file_content_idempotency_after is changed
# ``tar`` archives are not guaranteed to identify changes to file content if the file meta properties are unchanged.
when: "format != 'tar'"
- name: Remove archive - single file content idempotency ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_single_file_content_idempotency.{{ format }}"
state: absent
- name: Modify file back - single file content idempotency ({{ format }})
lineinfile:
line: foo.txt
regexp: "^bar.txt$"
path: "{{ remote_tmp_dir }}/foo.txt"
- name: Archive - single file name idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/foo.txt"
dest: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}"
format: "{{ format }}"
register: single_file_name_idempotency_before
- name: Rename file - single file name idempotency ({{ format }})
command: "mv {{ remote_tmp_dir }}/foo.txt {{ remote_tmp_dir }}/fii.txt"
- name: Archive again - single file name idempotency ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/fii.txt"
dest: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}"
format: "{{ format }}"
register: single_file_name_idempotency_after
# The gz, bz2, and xz formats do not store the original file name
# so it is not possible to identify a change in this scenario.
- name: Check task status - single file name idempotency ({{ format }})
assert:
that:
- single_file_name_idempotency_after is changed
when: "format in ('tar', 'zip')"
- name: Remove archive - single file name idempotency ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_single_file_name_idempotency.{{ format }}"
state: absent
- name: Rename file back - single file name idempotency ({{ format }})
command: "mv {{ remote_tmp_dir }}/fii.txt {{ remote_tmp_dir }}/foo.txt"

View File

@ -0,0 +1,225 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Archive - remove source files ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/*.txt"
dest: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}"
format: "{{ format }}"
remove: true
register: archive_remove_source_files
- name: Verify archive exists - remove source files ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}"
state: file
- name: Verify all files were archived - remove source files ({{ format }})
assert:
that:
- archive_remove_source_files is changed
- "archive_remove_source_files.archived | length == 3"
- name: Remove Archive - remove source files ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_files.{{ format }}"
state: absent
- name: Remove source files in check mode ({{ format }})
file:
path: "{{ remote_tmp_dir }}/{{ item }}"
state: absent
check_mode: true
with_items:
- foo.txt
- bar.txt
- empty.txt
register: remove_files
- name: Assert that source files were removed - remove source files ({{ format }})
assert:
that:
- remove_files is not changed
- name: Copy source files - remove source directory ({{ format }})
copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/{{ item }}"
with_items:
- foo.txt
- bar.txt
- empty.txt
- name: Create temporary directory - remove source directory ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir"
state: directory
- name: Copy source files to temporary directory - remove source directory ({{ format }})
copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}"
with_items:
- foo.txt
- bar.txt
- empty.txt
- name: Archive - remove source directory ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/tmpdir"
dest: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}"
format: "{{ format }}"
remove: true
register: archive_remove_source_directory
- name: Verify archive exists - remove source directory ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}"
state: file
- name: Verify archive contains all files - remove source directory ({{ format }})
assert:
that:
- archive_remove_source_directory is changed
- "archive_remove_source_directory.archived | length == 3"
- name: Remove archive - remove source directory ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_directory.{{ format }}"
state: absent
- name: Remove source source directory in check mode ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir"
state: absent
check_mode: true
register: remove_dir
- name: Verify source directory was removed - remove source directory ({{ format }})
assert:
that:
- remove_dir is not changed
- name: Create temporary directory - remove source excluding path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir"
state: directory
- name: Copy source files to temporary directory - remove source excluding path ({{ format }})
copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}"
with_items:
- foo.txt
- bar.txt
- empty.txt
- name: Archive - remove source excluding path ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/tmpdir/*"
dest: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}"
format: "{{ format }}"
remove: true
exclude_path: "{{ remote_tmp_dir }}/tmpdir/empty.txt"
register: archive_remove_source_excluding_path
- name: Verify archive exists - remove source excluding path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}"
state: file
- name: Verify all files except excluded are archived - remove source excluding path ({{ format }})
assert:
that:
- archive_remove_source_excluding_path is changed
- "archive_remove_source_excluding_path.archived | length == 2"
- name: Remove archive - remove source excluding path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_path.{{ format }}"
state: absent
- name: Verify that excluded file still exists - remove source excluding path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir/empty.txt"
state: file
- name: Copy source files to temporary directory - remove source excluding sub path ({{ format }})
copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}"
with_items:
- foo.txt
- bar.txt
- empty.txt
- sub
- sub/subfile.txt
- name: Archive - remove source excluding sub path ({{ format }})
archive:
path:
- "{{ remote_tmp_dir }}/tmpdir/*.txt"
- "{{ remote_tmp_dir }}/tmpdir/sub/*"
dest: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}"
format: "{{ format }}"
remove: true
exclude_path: "{{ remote_tmp_dir }}/tmpdir/sub/subfile.txt"
register: archive_remove_source_excluding_sub_path
- name: Verify archive exists - remove source excluding sub path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}"
state: file
- name: Remove archive - remove source excluding sub path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_excluding_sub_path.{{ format }}"
state: absent
- name: Verify that sub path still exists - remove source excluding sub path ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir/sub/subfile.txt"
state: file
- name: Copy source files to temporary directory - remove source with nested paths ({{ format }})
copy:
src: "{{ item }}"
dest: "{{ remote_tmp_dir }}/tmpdir/{{ item }}"
with_items:
- foo.txt
- bar.txt
- empty.txt
- sub
- sub/subfile.txt
- name: Archive - remove source with nested paths ({{ format }})
archive:
path: "{{ remote_tmp_dir }}/tmpdir/"
dest: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}"
format: "{{ format }}"
remove: true
register: archive_remove_nested_paths
- name: Verify archive exists - remove source with nested paths ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}"
state: file
- name: Verify source files were removed - remove source with nested paths ({{ format }})
file:
path: "{{ remote_tmp_dir }}/tmpdir"
state: absent
register: archive_remove_nested_paths_status
- name: Assert tasks status - remove source with nested paths ({{ format }})
assert:
that:
- archive_remove_nested_paths is success
- archive_remove_nested_paths_status is not changed
- name: Remove archive - remove source with nested paths ({{ format }})
file:
path: "{{ remote_tmp_dir }}/archive_remove_source_nested_paths.{{ format }}"
state: absent

View File

@ -0,0 +1,12 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
azp/posix/vm
destructive
needs/privileged
skip/aix
skip/freebsd
skip/osx
skip/macos

View File

@ -0,0 +1,20 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
btrfs_subvolume_single_configs:
- file: "/tmp/disks0.img"
loop: "/dev/loop95"
btrfs_subvolume_multiple_configs:
- file: "/tmp/diskm0.img"
loop: "/dev/loop97"
- file: "/tmp/diskm1.img"
loop: "/dev/loop98"
- file: "/tmp/diskm2.img"
loop: "/dev/loop99"
btrfs_subvolume_configs: "{{ btrfs_subvolume_single_configs + btrfs_subvolume_multiple_configs }}"
btrfs_subvolume_single_devices: "{{ btrfs_subvolume_single_configs | map(attribute='loop') }}"
btrfs_subvolume_single_label: "single"
btrfs_subvolume_multiple_devices: "{{ btrfs_subvolume_multiple_configs | map(attribute='loop') }}"
btrfs_subvolume_multiple_label: "multiple"

View File

@ -0,0 +1,29 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Install required packages
ansible.builtin.package:
name:
- btrfs-progs # btrfs userspace
- util-linux # losetup
ignore_errors: True
register: btrfs_installed
- name: Execute integration tests tests
block:
- ansible.builtin.include_tasks: 'setup.yml'
- name: "Execute test scenario for single device filesystem"
ansible.builtin.include_tasks: 'run_filesystem_tests.yml'
vars:
btrfs_subvolume_target_device: "{{ btrfs_subvolume_single_devices | first }}"
btrfs_subvolume_target_label: "{{ btrfs_subvolume_single_label }}"
- name: "Execute test scenario for multiple device configuration"
ansible.builtin.include_tasks: 'run_filesystem_tests.yml'
vars:
btrfs_subvolume_target_device: "{{ btrfs_subvolume_multiple_devices | first }}"
btrfs_subvolume_target_label: "{{ btrfs_subvolume_multiple_label }}"
when: btrfs_installed is success

View File

@ -0,0 +1,15 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- ansible.builtin.include_tasks: 'test_subvolume_simple.yml'
- ansible.builtin.include_tasks: 'test_subvolume_nested.yml'
- ansible.builtin.include_tasks: 'test_subvolume_recursive.yml'
- ansible.builtin.include_tasks: 'test_subvolume_default.yml'
- ansible.builtin.include_tasks: 'test_snapshot_skip.yml'
- ansible.builtin.include_tasks: 'test_snapshot_clobber.yml'
- ansible.builtin.include_tasks: 'test_snapshot_error.yml'
- ansible.builtin.include_tasks: 'test_subvolume_whitespace.yml'

View File

@ -0,0 +1,32 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- ansible.builtin.include_tasks: 'test_filesystem_matching.yml'
- name: "Execute all test scenario for unmounted filesystem"
ansible.builtin.include_tasks: 'run_common_tests.yml'
- name: "Execute test scenarios where non-root subvolume is mounted"
block:
- name: Create subvolume '/nonroot'
community.general.btrfs_subvolume:
automount: Yes
name: "/nonroot"
filesystem_label: "{{ btrfs_subvolume_target_label }}"
state: "present"
register: nonroot
- name: "Mount subvolume '/nonroot'"
ansible.posix.mount:
src: "{{ nonroot.filesystem.devices | first }}"
path: /mnt
opts: "subvolid={{ nonroot.target_subvolume_id }}"
fstype: btrfs
state: mounted
- name: "Run tests for explicit, mounted single device configuration"
ansible.builtin.include_tasks: 'run_common_tests.yml'
- name: "Unmount subvolume /nonroot"
ansible.posix.mount:
path: /mnt
state: absent

View File

@ -0,0 +1,37 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: "Create file {{ item.file }} to back loop device {{ item.loop }}"
ansible.builtin.command:
cmd: "dd if=/dev/zero of={{ item.file }} bs=1M count=200" ## minimum count 109
creates: "{{ item.file }}"
with_items: "{{ btrfs_subvolume_configs }}"
- name: "Setup loop device {{ item.loop }}"
ansible.builtin.command:
cmd: "losetup {{ item.loop }} {{ item.file }}"
creates: "{{ item.loop }}"
with_items: "{{ btrfs_subvolume_configs }}"
- name: Create single device btrfs filesystem
ansible.builtin.command:
cmd: "mkfs.btrfs --label {{ btrfs_subvolume_single_label }} -f {{ btrfs_subvolume_single_devices | first }}"
changed_when: True
- name: Create multiple device btrfs filesystem
ansible.builtin.command:
cmd: "mkfs.btrfs --label {{ btrfs_subvolume_multiple_label }} -f -d raid0 {{ btrfs_subvolume_multiple_devices | join(' ') }}"
changed_when: True
# Typically created by udev, but apparently missing on Alpine
- name: Create btrfs control device node
ansible.builtin.command:
cmd: "mknod /dev/btrfs-control c 10 234"
creates: "/dev/btrfs-control"
- name: Force rescan to ensure all device are detected
ansible.builtin.command:
cmd: "btrfs device scan"
changed_when: True

View File

@ -0,0 +1,80 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: "Match targeted filesystem by label"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by label
community.general.btrfs_subvolume:
automount: Yes
name: "/match_label"
filesystem_label: "{{ btrfs_subvolume_target_label }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match targeted filesystem by uuid"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by uuid
community.general.btrfs_subvolume:
automount: Yes
name: "/match_uuid"
filesystem_uuid: "{{ result.filesystem.uuid }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match targeted filesystem by devices"
block:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem by device
community.general.btrfs_subvolume:
automount: Yes
name: "/match_device"
filesystem_device: "{{ result.filesystem.devices | first }}"
state: "present"
register: result
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
- name: "Match only mounted filesystem"
block:
- name: "Mount filesystem '{{ btrfs_subvolume_target_label }}'"
ansible.posix.mount:
src: "{{ result.filesystem.devices | first }}"
path: /mnt
opts: "subvolid={{ 5 }}"
fstype: btrfs
state: mounted
- name: Print current status
community.general.btrfs_info:
- name: Match '{{ btrfs_subvolume_target_label }}' filesystem when only mount
community.general.btrfs_subvolume:
automount: Yes
name: "/match_only_mounted"
state: "present"
register: result
- name: "Unmount filesystem '{{ btrfs_subvolume_target_label }}'"
ansible.posix.mount:
path: /mnt
state: absent
- name: Validate the '{{ btrfs_subvolume_target_label }}' filesystem was chosen
ansible.builtin.assert:
that:
- result.filesystem.label == btrfs_subvolume_target_label
when: False # TODO don't attempt this if the host already has a pre-existing btrfs filesystem

View File

@ -0,0 +1,41 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot, overwriting if one already exists at path
block:
- name: Create a snapshot named 'snapshot_clobber'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
snapshot_source: "/"
snapshot_conflict: "clobber"
state: "present"
register: result
- name: Snapshot 'snapshot_clobber' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_clobber' (no idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
snapshot_source: "/"
snapshot_conflict: "clobber"
state: "present"
register: result
- name: Snapshot 'snapshot_clobber' created (no idempotency)
ansible.builtin.assert:
that:
- result is changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_clobber"
state: "absent"

View File

@ -0,0 +1,42 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot, erroring if one already exists at path
block:
- name: Create a snapshot named 'snapshot_error'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
snapshot_source: "/"
snapshot_conflict: "error"
state: "present"
register: result
- name: Snapshot 'snapshot_error' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_error' (no idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
snapshot_source: "/"
snapshot_conflict: "error"
state: "present"
register: result
ignore_errors: true
- name: Snapshot 'snapshot_error' created (no idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_error"
state: "absent"

View File

@ -0,0 +1,41 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a snapshot if one does not already exist at path
block:
- name: Create a snapshot named 'snapshot_skip'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
snapshot_source: "/"
snapshot_conflict: "skip"
state: "present"
register: result
- name: Snapshot 'snapshot_skip' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a snapshot named 'snapshot_skip' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
snapshot_source: "/"
snapshot_conflict: "skip"
state: "present"
register: result
- name: Snapshot 'snapshot_skip' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Cleanup created snapshot
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/snapshot_skip"
state: "absent"

View File

@ -0,0 +1,99 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Change the default subvolume
block:
- name: Update filesystem default subvolume to '@'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Update filesystem default subvolume to '@' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Revert the default subvolume
block:
- name: Revert filesystem default subvolume to '/'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/"
state: "present"
register: result
- name: Subvolume '/' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Revert filesystem default subvolume to '/' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/"
state: "present"
register: result
- name: Subvolume '/' set to default (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Change the default subvolume again
block:
- name: Update filesystem default subvolume to '@'
community.general.btrfs_subvolume:
automount: Yes
default: True
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "present"
register: result
- name: Subvolume '@' set to default
ansible.builtin.assert:
that:
- result is changed
- name: Revert custom default subvolume to fs_tree root when deleted
block:
- name: Delete custom default subvolume '@'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "absent"
register: result
- name: Subvolume '@' deleted
ansible.builtin.assert:
that:
- result is changed
- name: Delete custom default subvolume '@' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/@"
state: "absent"
register: result
- name: Subvolume '@' deleted (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@ -0,0 +1,61 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create parent subvolume 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container"
state: "present"
- name: Create a nested subvolume
block:
- name: Create a subvolume named 'nested' inside 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "present"
register: result
- name: Subvolume 'container/nested' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'nested' inside 'container' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "present"
register: result
- name: Subvolume 'container/nested' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a nested subvolume
block:
- name: Remove a subvolume named 'nested' inside 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "absent"
register: result
- name: Subvolume 'container/nested' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'nested' inside 'container' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/nested"
state: "absent"
register: result
- name: Subvolume 'container/nested' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@ -0,0 +1,86 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Recursively create subvolumes
block:
- name: Create a subvolume named '/recursive/son/grandson'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/son/grandson"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named '/recursive/son/grandson' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/son/grandson"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Create a subvolume named '/recursive/daughter/granddaughter'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/daughter/granddaughter"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named '/recursive/daughter/granddaughter' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive/daughter/granddaughter"
recursive: Yes
state: "present"
register: result
- name: Subvolume named '/recursive/son/grandson' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Recursively remove subvolumes
block:
- name: Remove subvolume '/recursive' and all descendents
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive"
recursive: Yes
state: "absent"
register: result
- name: Subvolume '/recursive' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove subvolume '/recursive' and all descendents (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/recursive"
recursive: Yes
state: "absent"
register: result
- name: Subvolume '/recursive' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@ -0,0 +1,54 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a simple subvolume
block:
- name: Create a subvolume named 'simple'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "present"
register: result
- name: Subvolume named 'simple' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'simple' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "present"
register: result
- name: Subvolume named 'simple' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a simple subvolume
block:
- name: Remove a subvolume named 'simple'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "absent"
register: result
- name: Subvolume named 'simple' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'simple' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/simple"
state: "absent"
register: result
- name: Subvolume named 'simple' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@ -0,0 +1,62 @@
---
# Copyright (c) 2022, Gregory Furlong <gnfzdz@fzdz.io>
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Create a subvolume named 'container'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container"
state: "present"
- name: Create a subvolume with whitespace in the name
block:
- name: Create a subvolume named 'container/my data'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "present"
register: result
- name: Subvolume named 'container/my data' created
ansible.builtin.assert:
that:
- result is changed
- name: Create a subvolume named 'container/my data' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "present"
register: result
- name: Subvolume named 'container/my data' created (idempotency)
ansible.builtin.assert:
that:
- result is not changed
- name: Remove a subvolume with whitespace in the name
block:
- name: Remove a subvolume named 'container/my data'
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "absent"
register: result
- name: Subvolume named 'container/my data' removed
ansible.builtin.assert:
that:
- result is changed
- name: Remove a subvolume named 'container/my data' (idempotency)
community.general.btrfs_subvolume:
automount: Yes
filesystem_label: "{{ btrfs_subvolume_target_label }}"
name: "/container/my data"
state: "absent"
register: result
- name: Subvolume named 'container/my data' removed (idempotency)
ansible.builtin.assert:
that:
- result is not changed

View File

@ -0,0 +1,9 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
all:
hosts:
testhost:
ansible_connection: local

View File

@ -0,0 +1,100 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- block:
- name: Create temporary playbook files
tempfile:
state: file
suffix: temp
loop: "{{ tests }}"
loop_control:
loop_var: test
label: "{{ test.name }}"
register: temporary_playbook_files
- name: Set temporary playbook file content
copy:
content: "{{ test.playbook }}"
dest: "{{ temporary_playbook_files.results[test_idx].path }}"
loop: "{{ tests }}"
loop_control:
loop_var: test
index_var: test_idx
label: "{{ test.name }}"
- name: Collect outputs
command: "ansible-playbook -i {{ inventory }} {{ playbook }}"
environment: "{{ test.environment }}"
loop: "{{ tests }}"
loop_control:
loop_var: test
label: "{{ test.name }}"
register: outputs
changed_when: false
vars:
inventory: "{{ role_path }}/inventory.yml"
playbook: "
{%- for result in temporary_playbook_files.results -%}
{%- if result.test.name == test.name -%}
{{- result.path -}}
{%- endif -%}
{%- endfor -%}"
- name: Assert test output equals expected output
assert:
that: result.output.differences | length == 0
loop: "{{ results }}"
loop_control:
loop_var: result
label: "{{ result.name }}"
register: assertions
vars:
results: >-
{%- set results = [] -%}
{%- for result in outputs.results -%}
{%- set differences = [] -%}
{%- for i in range([result.test.expected_output | count, result.stdout_lines | count] | max) -%}
{%- set line = "line_%s" | format(i+1) -%}
{%- set test_line = result.stdout_lines[i] | default(none) -%}
{%- set expected_lines = result.test.expected_output[i] | default(none) -%}
{%- if expected_lines is not string and expected_lines is not none -%}
{%- if test_line not in expected_lines -%}
{{- differences.append({
line: {
'expected_one_of': expected_lines,
'got': test_line }}) -}}
{%- endif -%}
{%- else -%}
{%- if expected_lines != test_line -%}
{{- differences.append({
line: {
'expected': expected_lines,
'got': test_line }}) -}}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{{- results.append({
'name': result.test.name,
'output': {
'differences': differences,
'expected': result.test.expected_output,
'got': result.stdout_lines }}) -}}
{%- endfor -%}
{{- results -}}
always:
- name: Remove temporary playbooks
file:
path: "{{ temporary_file.path }}"
state: absent
loop: "{{ temporary_playbook_files.results }}"
loop_control:
loop_var: temporary_file
label: "{{ temporary_file.test.name }}: {{ temporary_file.path }}"

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
needs/target/callback

View File

@ -0,0 +1,462 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Run tests
include_role:
name: callback
vars:
tests:
- name: Not using diy callback options
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set playbook_on_start_msg callback using environment variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
ANSIBLE_CALLBACK_DIY_PLAYBOOK_ON_START_MSG: "Sample output Sample playbook message"
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"Sample output Sample playbook message",
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set playbook_on_play_start_msg callback using play variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- name: Sample play name
hosts: testhost
gather_facts: false
vars:
ansible_callback_diy_playbook_on_play_start_msg: Sample output {{ ansible_callback_diy.play.name }}
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"Sample output Sample play name",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set playbook_on_task_start_msg callback using play variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
vars:
ansible_callback_diy_playbook_on_task_start_msg: Sample output {{ ansible_callback_diy.task.name }}
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"Sample output Sample task name",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set playbook_on_task_start_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
vars:
ansible_callback_diy_playbook_on_task_start_msg: Sample output {{ ansible_callback_diy.task.name }}
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"Sample output Sample task name",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set runner_on_ok_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
vars:
ansible_callback_diy_runner_on_ok_msg: Sample output {{ ansible_callback_diy.result.output.msg }}
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"Sample output sample debug msg",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set runner_on_failed_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
failed_when: true
ignore_errors: true
vars:
ansible_callback_diy_runner_on_failed_msg: Sample output Sample failure message
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"Sample output Sample failure message",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 "
]
- name: Set runner_on_skipped_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
when: false
vars:
ansible_callback_diy_runner_on_skipped_msg: Sample output Skipped {{ ansible_callback_diy.task.name }}
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"Sample output Skipped Sample task name",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=0 changed=0 unreachable=0 failed=0 skipped=1 rescued=0 ignored=0 "
]
- name: Set runner_item_on_ok_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg {{ item }}
loop:
- sample item 1
- sample item 2
- sample item 3
vars:
ansible_callback_diy_runner_item_on_ok_msg: Sample output Looping {{ ansible_callback_diy.result.output.msg }}
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"Sample output Looping sample debug msg sample item 1",
"Sample output Looping sample debug msg sample item 2",
"Sample output Looping sample debug msg sample item 3",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set runner_item_on_failed_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg {{ item }}
loop:
- sample item 1
- sample item 2
- sample item 3
failed_when: item == 'sample item 2'
ignore_errors: true
vars:
ansible_callback_diy_runner_item_on_failed_msg: Sample output Looping sample failure message
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => (item=sample item 1) => {",
" \"msg\": \"sample debug msg sample item 1\"",
"}",
"Sample output Looping sample failure message",
"ok: [testhost] => (item=sample item 3) => {",
" \"msg\": \"sample debug msg sample item 3\"",
"}",
[
# Apparently a bug was fixed in Ansible, as before it ran through with "All items completed"
"fatal: [testhost]: FAILED! => {\"msg\": \"All items completed\"}",
"fatal: [testhost]: FAILED! => {\"msg\": \"One or more items failed\"}",
],
"...ignoring",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=1 "
]
- name: Set runner_item_on_skipped_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg {{ item }}
loop:
- sample item 1
- sample item 2
- sample item 3
when: item != 'sample item 2'
vars:
ansible_callback_diy_runner_item_on_skipped_msg: Sample output Looping Skipped {{ ansible_callback_diy.result.output.item }}
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => (item=sample item 1) => {",
" \"msg\": \"sample debug msg sample item 1\"",
"}",
"Sample output Looping Skipped sample item 2",
"ok: [testhost] => (item=sample item 3) => {",
" \"msg\": \"sample debug msg sample item 3\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set playbook_on_stats_msg callback using play variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
vars:
ansible_callback_diy_playbook_on_stats_msg: |+2
Sample output stats
===============================
{% for key in ansible_callback_diy.stats | sort %}
{% set color_one = "" %}
{% set color_two = "" %}
{% if ansible_callback_diy.stats[key] %}
{% if key == 'ok' %}
{% set prefix = ' ' %}
{% set suffix = ' ' %}
{% elif key == 'changed' %}
{% set prefix = ' ' %}
{% set suffix = ' ' %}
{% elif key == 'processed' %}
{% set prefix = ' ' %}
{% set suffix = ' ' %}
{% elif key == 'skipped' %}
{% set prefix = ' ' %}
{% set suffix = ' ' %}
{% else %}
{% set prefix = "" %}
{% set suffix = "" %}
{% endif %}
{{ color_one }}{{ "%s%s%s" | format(prefix,key,suffix) }}{{ color_two }}: {{ ansible_callback_diy.stats[key] | to_nice_yaml }}
{% endif %}
{% endfor %}
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
" Sample output stats",
"===============================",
" ok : testhost: 1",
"",
" processed : testhost: 1"
]
- name: Suppress output on playbook_on_task_start_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
vars:
ansible_callback_diy_playbook_on_task_start_msg: ''
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"ok: [testhost] => {",
" \"msg\": \"sample debug msg\"",
"}",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Suppress output on runner_on_ok_msg callback using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
vars:
ansible_callback_diy_runner_on_ok_msg: ''
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Set runner_on_ok_msg_color using task variable
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.diy
playbook: !unsafe |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
vars:
ansible_callback_diy_runner_on_ok_msg: Sample output {{ ansible_callback_diy.result.output.msg }}
ansible_callback_diy_runner_on_ok_msg_color: blue
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"Sample output sample debug msg",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3

View File

@ -0,0 +1,9 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- hosts: localhost
gather_facts: false
tasks:
- ping:

View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -eux
# ANSIBLE_CALLBACK_WHITELIST has been deprecated in ansible-base 2.11, ANSIBLE_CALLBACKS_ENABLED should be used
export ANSIBLE_CALLBACK_WHITELIST="community.general.log_plays,${ANSIBLE_CALLBACK_WHITELIST:-}"
export ANSIBLE_CALLBACKS_ENABLED="community.general.log_plays,${ANSIBLE_CALLBACKS_ENABLED:-}"
# run play, should create log and dir if needed
export ANSIBLE_LOG_FOLDER="logit"
ansible-playbook ping_log.yml -v "$@"
[[ -f "${ANSIBLE_LOG_FOLDER}/localhost" ]]
# now force it to fail
export ANSIBLE_LOG_FOLDER="logit.file"
touch "${ANSIBLE_LOG_FOLDER}"
ansible-playbook ping_log.yml -v "$@" 2>&1| grep 'Failure using method (v2_runner_on_ok) in callback plugin'
[[ ! -f "${ANSIBLE_LOG_FOLDER}/localhost" ]]

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
needs/target/callback

View File

@ -0,0 +1,101 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Run tests
include_role:
name: callback
vars:
tests:
- name: Basic run
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Sample task name
debug:
msg: sample debug msg
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Sample task name] ********************************************************",
"ok: [testhost] => ",
" msg: sample debug msg",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Test umlauts in multiline
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: |
- hosts: testhost
gather_facts: false
tasks:
- name: Umlaut output
debug:
msg: "äöü\néêè\nßï☺"
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Umlaut output] ***********************************************************",
"ok: [testhost] => ",
" msg: |-",
" äöü",
" éêè",
" ßï☺",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]
- name: Test to_yaml
environment:
ANSIBLE_NOCOLOR: 'true'
ANSIBLE_FORCE_COLOR: 'false'
ANSIBLE_STDOUT_CALLBACK: community.general.yaml
playbook: |
- hosts: testhost
gather_facts: false
vars:
data: |
line 1
line 2
line 3
tasks:
- name: Test to_yaml
debug:
msg: "{{ '{{' }}'{{ '{{' }}'{{ '}}' }} data | to_yaml {{ '{{' }}'{{ '}}' }}'{{ '}}' }}"
# The above should be: msg: "{{ data | to_yaml }}"
# Unfortunately, the way Ansible handles templating, we need to do some funny 'escaping' tricks...
expected_output: [
"",
"PLAY [testhost] ****************************************************************",
"",
"TASK [Test to_yaml] ************************************************************",
"ok: [testhost] => ",
" msg: |-",
" 'line 1",
" ",
" line 2",
" ",
" line 3",
" ",
" '",
"",
"PLAY RECAP *********************************************************************",
"testhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 "
]

View File

@ -0,0 +1,7 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2
destructive
skip/aix

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_pkg_mgr

View File

@ -0,0 +1,22 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- import_tasks: setup.yml
- name: Set default environment
set_fact:
cargo_environment: {}
- name: Set special environment to work around cargo bugs
set_fact:
cargo_environment:
# See https://github.com/rust-lang/cargo/issues/10230#issuecomment-1201662729:
CARGO_NET_GIT_FETCH_WITH_CLI: "true"
when: has_cargo | default(false) and ansible_distribution == 'Alpine'
- block:
- import_tasks: test_general.yml
- import_tasks: test_version.yml
environment: "{{ cargo_environment }}"
when: has_cargo | default(false)
- import_tasks: test_rustup_cargo.yml
when: rustup_cargo_bin | default(false)

View File

@ -0,0 +1,42 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- block:
- name: Install cargo
package:
name: cargo
state: present
- set_fact:
has_cargo: true
when:
- ansible_system != 'FreeBSD'
- ansible_distribution != 'MacOSX'
- ansible_distribution != 'RedHat' or ansible_distribution_version is version('8.0', '>=')
- ansible_distribution != 'CentOS' or ansible_distribution_version is version('7.0', '>=')
- ansible_distribution != 'Ubuntu' or ansible_distribution_version is version('18', '>=')
- block:
- name: Install rust (containing cargo)
package:
name: rust
state: present
- set_fact:
has_cargo: true
when:
- ansible_system == 'FreeBSD' and ansible_distribution_version is version('13.0', '>')
- block:
- name: Download rustup
get_url:
url: https://sh.rustup.rs
dest: /tmp/sh.rustup.rs
mode: "0750"
force: true
- name: Install rustup cargo
command: /tmp/sh.rustup.rs -y
- set_fact:
rustup_cargo_bin: "{{ lookup('env', 'HOME') }}/.cargo/bin/cargo"
when:
- ansible_distribution != 'CentOS' or ansible_distribution_version is version('7.0', '>=')

View File

@ -0,0 +1,35 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Ensure application helloworld is uninstalled
community.general.cargo:
state: absent
name: helloworld
register: uninstall_absent_helloworld
- name: Install application helloworld
community.general.cargo:
name: helloworld
register: install_absent_helloworld
- name: Install application helloworld again
community.general.cargo:
name: helloworld
register: install_present_helloworld
ignore_errors: true
- name: Uninstall application helloworld
community.general.cargo:
state: absent
name: helloworld
register: uninstall_present_helloworld
- name: Check assertions helloworld
assert:
that:
- uninstall_absent_helloworld is not changed
- install_absent_helloworld is changed
- install_present_helloworld is not changed
- uninstall_present_helloworld is changed

View File

@ -0,0 +1,23 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
#
- name: Install application helloworld
community.general.cargo:
executable: "{{ rustup_cargo_bin }}"
name: helloworld
register: rustup_install_absent_helloworld
- name: Uninstall application helloworld
community.general.cargo:
executable: "{{ rustup_cargo_bin }}"
state: absent
name: helloworld
register: rustup_uninstall_present_helloworld
- name: Check assertions helloworld
assert:
that:
- rustup_install_absent_helloworld is changed
- rustup_uninstall_present_helloworld is changed

View File

@ -0,0 +1,50 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: Install application helloworld-yliu 0.1.0
community.general.cargo:
name: helloworld-yliu
version: 0.1.0
register: install_helloworld_010
- name: Install application helloworld-yliu 0.1.0 (idempotent)
community.general.cargo:
name: helloworld-yliu
version: 0.1.0
register: install_helloworld_010_idem
- name: Upgrade helloworld-yliu 0.1.0
community.general.cargo:
name: helloworld-yliu
state: latest
register: upgrade_helloworld_010
- name: Upgrade helloworld-yliu 0.1.0 (idempotent)
community.general.cargo:
name: helloworld-yliu
state: latest
register: upgrade_helloworld_010_idem
- name: Downgrade helloworld-yliu 0.1.0
community.general.cargo:
name: helloworld-yliu
version: 0.1.0
register: downgrade_helloworld_010
- name: Downgrade helloworld-yliu 0.1.0 (idempotent)
community.general.cargo:
name: helloworld-yliu
version: 0.1.0
register: downgrade_helloworld_010_idem
- name: Check assertions helloworld-yliu
assert:
that:
- install_helloworld_010 is changed
- install_helloworld_010_idem is not changed
- upgrade_helloworld_010 is changed
- upgrade_helloworld_010_idem is not changed
- downgrade_helloworld_010 is changed
- downgrade_helloworld_010_idem is not changed

View File

@ -0,0 +1,10 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/1
destructive
skip/aix
skip/osx
skip/macos
skip/freebsd

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_pkg_mgr

View File

@ -0,0 +1,68 @@
---
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: test cloud-init
# TODO: check for a workaround
# install 'cloud-init'' failed: dpkg-divert: error: `diversion of /etc/init/ureadahead.conf
# to /etc/init/ureadahead.conf.disabled by cloud-init' clashes with `local diversion of
# /etc/init/ureadahead.conf to /etc/init/ureadahead.conf.distrib
# https://bugs.launchpad.net/ubuntu/+source/ureadahead/+bug/997838
# Will also have to skip on OpenSUSE when running on Python 2 on newer Leap versions
# (!= 42 and >= 15) ascloud-init will install the Python 3 package, breaking our build on py2.
when:
- not (ansible_distribution == "Ubuntu" and ansible_distribution_major_version|int == 14)
- not (ansible_os_family == "Suse" and ansible_distribution_major_version|int != 42 and ansible_python.version.major != 3)
- not (ansible_distribution == "CentOS" and ansible_distribution_major_version|int == 8) # TODO: cannot start service
- not (ansible_distribution == 'Archlinux') # TODO: package seems to be broken, cannot be downloaded from mirrors?
- not (ansible_distribution == 'Alpine') # TODO: not sure what's wrong here, the module doesn't return what the tests expect
block:
- name: setup install cloud-init
package:
name:
- cloud-init
- udev
- name: Ensure systemd-network user exists
user:
name: systemd-network
state: present
when: ansible_distribution == 'Fedora' and ansible_distribution_major_version|int >= 37
- name: setup run cloud-init
service:
name: cloud-init-local
state: restarted
- name: test gather cloud-init facts in check mode
cloud_init_data_facts:
check_mode: true
register: result
- name: verify test gather cloud-init facts in check mode
assert:
that:
- result.cloud_init_data_facts.status.v1 is defined
- result.cloud_init_data_facts.status.v1.stage is defined
- not result.cloud_init_data_facts.status.v1.stage
- cloud_init_data_facts.status.v1 is defined
- cloud_init_data_facts.status.v1.stage is defined
- not cloud_init_data_facts.status.v1.stage
- name: test gather cloud-init facts
cloud_init_data_facts:
register: result
- name: verify test gather cloud-init facts
assert:
that:
- result.cloud_init_data_facts.status.v1 is defined
- result.cloud_init_data_facts.status.v1.stage is defined
- not result.cloud_init_data_facts.status.v1.stage
- cloud_init_data_facts.status.v1 is defined
- cloud_init_data_facts.status.v1.stage is defined
- not cloud_init_data_facts.status.v1.stage

View File

@ -0,0 +1,56 @@
# Copyright 2012, Dag Wieers <dag@wieers.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import (absolute_import, division, print_function)
__metaclass__ = type
from ansible.errors import AnsibleError
from ansible.playbook.conditional import Conditional
from ansible.plugins.action import ActionBase
class ActionModule(ActionBase):
''' Fail with custom message '''
_requires_connection = False
_VALID_ARGS = frozenset(('msg', 'that'))
def _make_safe(self, text):
# A simple str(text) won't do it since AnsibleUnsafeText is clever :-)
return ''.join(chr(ord(x)) for x in text)
def run(self, tmp=None, task_vars=None):
if task_vars is None:
task_vars = dict()
result = super(ActionModule, self).run(tmp, task_vars)
del tmp # tmp no longer has any effect
if 'that' not in self._task.args:
raise AnsibleError('conditional required in "that" string')
fail_msg = 'Assertion failed'
success_msg = 'All assertions passed'
thats = self._task.args['that']
cond = Conditional(loader=self._loader)
result['_ansible_verbose_always'] = True
for that in thats:
cond.when = [str(self._make_safe(that))]
test_result = cond.evaluate_conditional(templar=self._templar, all_vars=task_vars)
if not test_result:
result['failed'] = True
result['evaluated_to'] = test_result
result['assertion'] = that
result['msg'] = fail_msg
return result
result['changed'] = False
result['msg'] = success_msg
return result

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/2

View File

@ -0,0 +1,58 @@
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2022, Alexei Znamensky <russoz@gmail.com>
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = ""
EXAMPLES = ""
RETURN = ""
from ansible.module_utils.basic import AnsibleModule
from ansible_collections.community.general.plugins.module_utils.cmd_runner import CmdRunner, cmd_runner_fmt as fmt
def main():
module = AnsibleModule(
argument_spec=dict(
cmd=dict(type="str", default="echo"),
path_prefix=dict(type="str"),
arg_formats=dict(type="dict", default={}),
arg_order=dict(type="raw", required=True),
arg_values=dict(type="dict", default={}),
check_mode_skip=dict(type="bool", default=False),
aa=dict(type="raw"),
tt=dict(),
),
supports_check_mode=True,
)
p = module.params
info = None
arg_formats = {}
for arg, fmt_spec in p['arg_formats'].items():
func = getattr(fmt, fmt_spec['func'])
args = fmt_spec.get("args", [])
arg_formats[arg] = func(*args)
runner = CmdRunner(module, [module.params["cmd"], '--'], arg_formats=arg_formats, path_prefix=module.params["path_prefix"])
with runner.context(p['arg_order'], check_mode_skip=p['check_mode_skip']) as ctx:
result = ctx.run(**p['arg_values'])
info = ctx.run_info
check = "check"
rc, out, err = result if result is not None else (None, None, None)
module.exit_json(rc=rc, out=out, err=err, info=info)
if __name__ == '__main__':
main()

View File

@ -0,0 +1,7 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
dependencies:
- setup_remote_tmp_dir

View File

@ -0,0 +1,9 @@
# Copyright (c) 2022, Alexei Znamensky
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: parameterized test cmd_echo
ansible.builtin.include_tasks:
file: test_cmd_echo.yml
loop: "{{ cmd_echo_tests }}"
when: item.condition | default(true) | bool

View File

@ -0,0 +1,28 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- name: create copy of /bin/echo ({{ item.name }})
ansible.builtin.copy:
src: /bin/echo
dest: "{{ item.copy_to }}/echo"
mode: "755"
when: item.copy_to is defined
- name: test cmd_echo module ({{ item.name }})
cmd_echo:
cmd: "{{ item.cmd | default(omit) }}"
path_prefix: "{{ item.path_prefix | default(omit) }}"
arg_formats: "{{ item.arg_formats | default(omit) }}"
arg_order: "{{ item.arg_order }}"
arg_values: "{{ item.arg_values | default(omit) }}"
check_mode_skip: "{{ item.check_mode_skip | default(omit) }}"
aa: "{{ item.aa | default(omit) }}"
register: test_result
check_mode: "{{ item.check_mode | default(omit) }}"
ignore_errors: "{{ item.expect_error | default(omit) }}"
- name: check results ({{ item.name }})
_unsafe_assert:
that: "{{ item.assertions }}"

View File

@ -0,0 +1,262 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2022, Alexei Znamensky
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
cmd_echo_tests:
- name: set aa and bb value
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
bb:
func: as_bool
args: [--bb-here]
arg_order: 'aa bb'
arg_values:
bb: true
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --bb-here\n"
- test_result.err == ""
- name: default aa value
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
bb:
func: as_bool
args: [--bb-here]
arg_order: ['aa', 'bb']
arg_values:
aa: 43
bb: true
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=43 --bb-here\n"
- test_result.err == ""
- name: implicit aa format
arg_formats:
bb:
func: as_bool
args: [--bb-here]
arg_order: ['aa', 'bb']
arg_values:
bb: true
aa: 1984
assertions:
- test_result.rc == 0
- test_result.out == "-- --aa 1984 --bb-here\n"
- test_result.err == ""
- name: missing bb format
arg_order: ['aa', 'bb']
arg_values:
bb: true
aa: 1984
expect_error: true
assertions:
- test_result is failed
- test_result.rc == 1
- '"out" not in test_result'
- '"err" not in test_result'
- >-
"MissingArgumentFormat: Cannot find format for parameter bb"
in test_result.module_stderr
- name: missing bb value
arg_formats:
bb:
func: as_bool
args: [--bb-here]
arg_order: 'aa bb'
aa: 1984
expect_error: true
assertions:
- test_result is failed
- test_result.rc == 1
- '"out" not in test_result'
- '"err" not in test_result'
- >-
"MissingArgumentValue: Cannot find value for parameter bb"
in test_result.module_stderr
- name: set aa and bb value with check_mode on
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
bb:
func: as_bool
args: [--bb-here]
arg_order: 'aa bb'
arg_values:
bb: true
aa: 11
check_mode: true
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --bb-here\n"
- test_result.err == ""
- name: set aa and bb value with check_mode and check_mode_skip on
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
bb:
func: as_bool
args: [--bb-here]
arg_order: 'aa bb'
arg_values:
bb: true
check_mode_skip: true
aa: 11
check_mode: true
expect_error: true # because if result contains rc != 0, ansible assumes error
assertions:
- test_result.rc == None
- test_result.out == None
- test_result.err == None
- name: set aa and tt value
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
- test_result.err == ""
- name: use cmd echo
cmd: echo
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
- test_result.err == ""
- name: use cmd /bin/echo
cmd: /bin/echo
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
- test_result.err == ""
# this will not be in the regular set of paths get_bin_path() searches
- name: use cmd {{ remote_tmp_dir }}/echo
condition: >
{{
ansible_distribution != "MacOSX" and
not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
}}
copy_to: "{{ remote_tmp_dir }}"
cmd: "{{ remote_tmp_dir }}/echo"
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
- test_result.err == ""
- name: use cmd echo with path_prefix {{ remote_tmp_dir }}
cmd: echo
condition: >
{{
ansible_distribution != "MacOSX" and
not (ansible_distribution == "CentOS" and ansible_distribution_major_version is version('7.0', '<'))
}}
copy_to: "{{ remote_tmp_dir }}"
path_prefix: "{{ remote_tmp_dir }}"
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
assertions:
- test_result.rc == 0
- test_result.out == "-- --answer=11 --tt-arg potatoes\n"
- test_result.err == ""
- name: use cmd never-existed
cmd: never-existed
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
expect_error: true
assertions:
- >
"Failed to find required executable" in test_result.msg
- name: use cmd /usr/bin/never-existed
cmd: /usr/bin/never-existed
arg_formats:
aa:
func: as_opt_eq_val
args: [--answer]
tt:
func: as_opt_val
args: [--tt-arg]
arg_order: 'aa tt'
arg_values:
tt: potatoes
aa: 11
expect_error: true
assertions:
- >
"No such file or directory" in test_result.msg

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
hidden

View File

@ -0,0 +1,16 @@
#!/usr/bin/env bash
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
set -eux
[ -f "${INVENTORY}" ]
# Run connection tests with both the default and C locale.
ansible-playbook test_connection.yml -i "${INVENTORY}" "$@"
if ansible --version | grep ansible | grep -E ' 2\.(9|10|11|12|13)\.'; then
LC_ALL=C LANG=C ansible-playbook test_connection.yml -i "${INVENTORY}" "$@"
fi

View File

@ -0,0 +1,48 @@
---
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
- hosts: "{{ target_hosts }}"
gather_facts: false
serial: 1
tasks:
### raw with unicode arg and output
- name: raw with unicode arg and output
raw: echo 汉语
register: command
- name: check output of raw with unicode arg and output
assert:
that:
- "'汉语' in command.stdout"
- command is changed # as of 2.2, raw should default to changed: true for consistency w/ shell/command/script modules
### copy local file with unicode filename and content
- name: create local file with unicode filename and content
local_action: lineinfile dest={{ local_tmp }}-汉语/汉语.txt create=true line=汉语
- name: remove remote file with unicode filename and content
action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语/汉语.txt state=absent"
- name: create remote directory with unicode name
action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=directory"
- name: copy local file with unicode filename and content
action: "{{ action_prefix }}copy src={{ local_tmp }}-汉语/汉语.txt dest={{ remote_tmp }}-汉语/汉语.txt"
### fetch remote file with unicode filename and content
- name: remove local file with unicode filename and content
local_action: file path={{ local_tmp }}-汉语/汉语.txt state=absent
- name: fetch remote file with unicode filename and content
fetch: src={{ remote_tmp }}-汉语/汉语.txt dest={{ local_tmp }}-汉语/汉语.txt fail_on_missing=true validate_checksum=true flat=true
### remove local and remote temp files
- name: remove local temp file
local_action: file path={{ local_tmp }}-汉语 state=absent
- name: remove remote temp file
action: "{{ action_prefix }}file path={{ remote_tmp }}-汉语 state=absent"
### test wait_for_connection plugin
- ansible.builtin.wait_for_connection:

View File

@ -0,0 +1,7 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
azp/posix/3
needs/root
skip/macos # Skipped due to limitation of macOS 10.15 SIP, please read https://github.com/ansible-collections/community.general/issues/1017#issuecomment-755088895

View File

@ -0,0 +1 @@
../connection_posix/test.sh

View File

@ -0,0 +1,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
[chroot]
chroot-pipelining ansible_ssh_pipelining=true
chroot-no-pipelining ansible_ssh_pipelining=false
[chroot:vars]
ansible_host=/
ansible_connection=community.general.chroot
ansible_python_interpreter="{{ ansible_playbook_python }}"

View File

@ -0,0 +1,6 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
non_local
unsupported

View File

@ -0,0 +1 @@
../connection_posix/test.sh

View File

@ -0,0 +1,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
[incus]
incus-pipelining ansible_ssh_pipelining=true
incus-no-pipelining ansible_ssh_pipelining=false
[incus:vars]
ansible_host=ubuntu-2204
ansible_connection=community.general.incus
ansible_python_interpreter=python3

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
unsupported

View File

@ -0,0 +1 @@
../connection_posix/test.sh

View File

@ -0,0 +1,11 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
[jail]
jail-pipelining ansible_ssh_pipelining=true
jail-no-pipelining ansible_ssh_pipelining=false
[jail:vars]
ansible_host=freebsd_10_2
ansible_connection=community.general.jail
ansible_python_interpreter=/usr/local/bin/python

View File

@ -0,0 +1,5 @@
# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
unsupported

Some files were not shown because too many files have changed in this diff Show More