add docker examples

This commit is contained in:
Dany LE
2025-04-11 10:11:27 +00:00
parent 453233a25b
commit f135dcc118
704 changed files with 101445 additions and 21 deletions

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: '>= 2.7'

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
- hosts: localhost
vars:
docker_test_image_alpine: quay.io/ansible/docker-test-containers:alpine3.8
tasks:
- name: Find all roles
find:
paths:
- "{{ (playbook_dir | default('.')) ~ '/roles' }}"
file_type: directory
depth: 1
register: result
- name: Include all roles
include_role:
name: "{{ item }}"
loop: "{{ result.files | map(attribute='path') | map('regex_replace', '.*/', '') | sort }}"

View File

@ -0,0 +1,32 @@
---
# 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: Retrieve information on current container
community.docker.current_container_facts:
register: result
# The following two tasks are useful if we ever have to debug why this fails.
- name: Print all Ansible facts
debug:
var: ansible_facts
- name: Read some files
slurp:
src: "{{ item }}"
loop:
- /proc/self/cpuset
- /proc/1/cgroup
- /proc/1/environ
- name: Print facts returned by module
debug:
var: result.ansible_facts
- name: Validate results
assert:
that:
- ansible_module_running_in_container
- ansible_module_container_type != ''

View File

@ -0,0 +1,32 @@
---
# 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
# Create random name prefix (for containers, networks, ...)
- name: Create random container name prefix
set_fact:
cname_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
- name: Make sure image is absent
community.docker.docker_image:
name: "{{ docker_test_image_alpine }}"
state: absent
- name: Make sure image is pulled
community.docker.docker_image:
name: "{{ docker_test_image_alpine }}"
source: pull
- name: Start container
community.docker.docker_container:
name: "{{ cname_prefix }}-1"
image: "{{ docker_test_image_alpine }}"
state: started
- name: Remove container
community.docker.docker_container:
name: "{{ cname_prefix }}-1"
state: absent
stop_timeout: 1
force_kill: true

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
# Currently the docker_stack* modules are not supported in the EE since we'd need to install the Docker CLI client

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
[galaxy-importer]
# This is only needed to make Zuul's third-party-check happy.
# It is not needed by anything else.
run_ansible_doc=false

View File

@ -0,0 +1,26 @@
#!/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
if [ ! -e main.go ]; then
echo "Must be run in a directory that contains main.go."
exit 1
fi
set -eux
IMAGE_NAME="${1:-localhost/$(basename "$(pwd)"):latest}"
podman manifest rm "${IMAGE_NAME}" || true
podman image rm "${IMAGE_NAME}" || true
buildah manifest create "${IMAGE_NAME}"
for ARCH in amd64 arm64 386; do
rm -f "main-${ARCH}"
GOARCH="${ARCH}" go build -ldflags "-s -w" -o "main-${ARCH}" main.go
WORKING_CONTAINER="$(buildah from --arch "${ARCH}" scratch)"
buildah copy "${WORKING_CONTAINER}" "main-${ARCH}" "/runme"
buildah config --entrypoint '["/runme"]' "${WORKING_CONTAINER}"
buildah commit --manifest "${IMAGE_NAME}" "${WORKING_CONTAINER}"
rm -f "main-${ARCH}"
done

View File

@ -0,0 +1,24 @@
#!/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 -e
# Uncomment the container image to copy, and run this script to copy it.
DESTINATION_REPO=ansible-collections/community.docker
function convert_image {
echo "========================================================================================================="
local IMAGE_NAME="$1"
local DEST_IMAGE="$2"
echo "FROM ${IMAGE_NAME}" | podman build --annotation "org.opencontainers.image.source=https://github.com/${DESTINATION_REPO}" -t "ghcr.io/${DESTINATION_REPO}/${DEST_IMAGE}" -
podman push "ghcr.io/${DESTINATION_REPO}/${DEST_IMAGE}"
podman rmi "${IMAGE_NAME}"
podman rmi "ghcr.io/${DESTINATION_REPO}/${DEST_IMAGE}"
}
# convert_image docker.io/library/registry:2.6.1 docker-distribution:2.6.1
# convert_image docker.io/library/registry:2.8.3 docker-distribution:2.8.3
convert_image docker.io/library/python:3-alpine docker-python:3-alpine

View File

@ -0,0 +1,39 @@
#!/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
if [ ! -e main.go ]; then
echo "Must be run in a directory that contains main.go."
exit 1
fi
PROGRAMS="main is-healthy make-healthy"
set -eux
IMAGE_NAME="${1:-localhost/$(basename "$(pwd)"):latest}"
podman manifest rm "${IMAGE_NAME}" || true
podman image rm "${IMAGE_NAME}" || true
buildah manifest create "${IMAGE_NAME}"
for ARCH in amd64 arm64 386; do
for PROGRAM in ${PROGRAMS}; do
rm -f "${PROGRAM}-${ARCH}"
GOARCH="${ARCH}" go build -ldflags "-s -w" -o "${PROGRAM}-${ARCH}" "${PROGRAM}.go"
done
# Need format=docker for health checks to work
WORKING_CONTAINER="$(buildah from --arch "${ARCH}" --format docker scratch)"
for PROGRAM in ${PROGRAMS}; do
buildah copy "${WORKING_CONTAINER}" "${PROGRAM}-${ARCH}" "/${PROGRAM}"
done
buildah config --entrypoint '["/main"]' "${WORKING_CONTAINER}"
buildah config --healthcheck 'CMD /is-healthy' "${WORKING_CONTAINER}"
buildah config --healthcheck-interval 1s "${WORKING_CONTAINER}"
buildah config --healthcheck-retries 1 "${WORKING_CONTAINER}"
buildah config --healthcheck-start-period 10s "${WORKING_CONTAINER}"
buildah commit --format docker --manifest "${IMAGE_NAME}" "${WORKING_CONTAINER}"
for PROGRAM in ${PROGRAMS}; do
rm -f "${PROGRAM}-${ARCH}"
done
done

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
*/
package main
import (
"bytes"
"fmt"
"os"
)
func main() {
data, err := os.ReadFile("/health.txt")
if err != nil {
fmt.Fprintf(os.Stderr, "Error while reading health status: %s\n", err)
os.Exit(1)
}
if bytes.Equal(data, []byte("healthy")) {
fmt.Fprintf(os.Stdout, "Healthy.\n")
os.Exit(0)
}
if bytes.Equal(data, []byte("unhealthy")) {
fmt.Fprintf(os.Stdout, "Unhealthy!\n")
os.Exit(1)
}
if bytes.Equal(data, []byte("starting")) {
fmt.Fprintf(os.Stdout, "Starting...\n")
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Unknown health status: %s\n", data)
os.Exit(1)
}

View File

@ -0,0 +1,51 @@
/*
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
*/
package main
import (
"fmt"
"os"
"time"
)
func main() {
os.WriteFile("health.txt", []byte("starting"), 0644)
if len(os.Args) > 3 || len(os.Args) < 2 {
fmt.Fprintf(os.Stderr, "%s must have 1 or 2 arguments, not %d arguments\n", os.Args[0], len(os.Args))
os.Exit(1)
}
runtimeDelay, err := time.ParseDuration(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot parse runtime duration: %s\n", err)
os.Exit(1)
}
if runtimeDelay.Microseconds() <= 0 {
fmt.Fprintf(os.Stderr, "Delay must be positive!\n")
os.Exit(1)
}
var healthyDelay time.Duration
if len(os.Args) == 3 {
healthyDelay, err = time.ParseDuration(os.Args[2])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot parse healthy delay: %s\n", err)
os.Exit(1)
}
if healthyDelay.Microseconds() <= 0 {
fmt.Fprintf(os.Stderr, "Healthy delay must not be negative!\n")
os.Exit(1)
}
}
if healthyDelay.Microseconds() > 0 {
fmt.Fprintf(os.Stderr, "Waiting %s until setting to healthy...\n", healthyDelay)
time.Sleep(healthyDelay)
os.WriteFile("/health.txt", []byte("healthy"), 0644)
fmt.Fprintf(os.Stderr, "Set state to healthy.\n")
}
fmt.Fprintf(os.Stderr, "Waiting %s until quitting...\n", runtimeDelay)
time.Sleep(runtimeDelay)
fmt.Fprintf(os.Stderr, "Goodbye.\n")
}

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
*/
package main
import (
"fmt"
"os"
"strconv"
)
func main() {
healthy := true
if len(os.Args) > 2 {
fmt.Fprintf(os.Stderr, "%s must have 0 or 1 argument, not %d arguments\n", os.Args[0], len(os.Args))
os.Exit(1)
} else if len(os.Args) == 2 {
var err error
healthy, err = strconv.ParseBool(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot parse boolean: %s\n", err)
os.Exit(1)
}
}
var state []byte
if healthy {
state = []byte("healthy")
} else {
state = []byte("unhealthy")
}
os.WriteFile("/health.txt", state, 0644)
}

View File

@ -0,0 +1,34 @@
/*
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
*/
package main
import (
"fmt"
"os"
"time"
)
func main() {
var delay time.Duration
if len(os.Args) > 2 {
fmt.Fprintf(os.Stderr, "%s must have 0 or 1 argument, not %d arguments\n", os.Args[0], len(os.Args))
os.Exit(1)
} else if len(os.Args) == 2 {
var err error
delay, err = time.ParseDuration(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot parse delay duration: %s\n", err)
os.Exit(1)
}
if delay.Microseconds() < 0 {
fmt.Fprintf(os.Stderr, "Delay must not be negative!\n")
os.Exit(1)
}
}
fmt.Println("Hello!")
time.Sleep(delay)
}

View File

@ -0,0 +1,34 @@
/*
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
*/
package main
import (
"fmt"
"os"
"time"
)
func main() {
var delay time.Duration
if len(os.Args) > 2 {
fmt.Fprintf(os.Stderr, "%s must have 0 or 1 argument, not %d arguments\n", os.Args[0], len(os.Args))
os.Exit(1)
} else if len(os.Args) == 2 {
var err error
delay, err = time.ParseDuration(os.Args[1])
if err != nil {
fmt.Fprintf(os.Stderr, "Cannot parse delay duration: %s\n", err)
os.Exit(1)
}
if delay.Microseconds() < 0 {
fmt.Fprintf(os.Stderr, "Delay must not be negative!\n")
os.Exit(1)
}
}
fmt.Println("World!")
time.Sleep(delay)
}

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
collections:
- ansible.posix
- community.crypto
- community.general
- community.internal_test_tools
- community.library_inventory_filtering_v1

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/4
skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container; also privileged doesn't work
destructive

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_docker

View File

@ -0,0 +1,67 @@
#!/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
# If you use another image, you possibly also need to adjust
# ansible_python_interpreter in test_connection.inventory.
source ../setup_docker/vars/main.env
IMAGE="${DOCKER_TEST_IMAGE_PYTHON3}"
# Setup phase
echo "Setup"
ANSIBLE_ROLES_PATH=.. ansible-playbook setup.yml
# If docker wasn't installed, don't run the tests
if [ "$(command -v docker)" == "" ]; then
exit
fi
# Test phase
CONTAINER_SUFFIX=-${RANDOM}
DOCKER_CONTAINERS="docker-connection-test-container${CONTAINER_SUFFIX}"
[[ -n "$DEBUG" || -n "$ANSIBLE_DEBUG" ]] && set -x
set -euo pipefail
cleanup() {
echo "Cleanup"
docker rm -f ${DOCKER_CONTAINERS}
echo "Shutdown"
ANSIBLE_ROLES_PATH=.. ansible-playbook shutdown.yml
echo "Done"
}
trap cleanup INT TERM EXIT
echo "Start containers"
for CONTAINER in ${DOCKER_CONTAINERS}; do
if [ "${ANSIBLE_TEST_COVERAGE:-}" == "" ]; then
docker run --rm --name "${CONTAINER}" --detach "${IMAGE}" /bin/sh -c 'sleep 10m'
else
docker run --rm --name "${CONTAINER}" --detach -v /tmp:/tmp "${IMAGE}" /bin/sh -c 'sleep 10m'
docker exec "${CONTAINER}" pip3 install coverage
fi
echo "${CONTAINER}"
done
cat > test_connection.inventory << EOF
[docker]
docker-no-pipelining ansible_pipelining=false
docker-pipelining ansible_pipelining=true
docker-working-dir ansible_docker_working_dir=/home
docker-privileged ansible_docker_privileged=true
[docker:vars]
ansible_host=docker-connection-test-container${CONTAINER_SUFFIX}
ansible_connection=community.docker.docker
ansible_python_interpreter=/usr/local/bin/python3
EOF
echo "Run tests"
./runme-connection.sh "$@"

View File

@ -0,0 +1,14 @@
---
# 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
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Setup docker
import_role:
name: setup_docker

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
- hosts: localhost
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Remove docker packages
action: "{{ ansible_facts.pkg_mgr }}"
args:
name:
- docker
- docker-ce
- docker-ce-cli
state: absent
when: not docker_skip_cleanup

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/4
skip/docker # coverage does not work if we're inside a docker container, since we cannot access this container's /tmp dir from the new container; also privileged doesn't work
destructive

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_docker
- setup_docker_python_deps

View File

@ -0,0 +1,67 @@
#!/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
# If you use another image, you possibly also need to adjust
# ansible_python_interpreter in test_connection.inventory.
source ../setup_docker/vars/main.env
IMAGE="${DOCKER_TEST_IMAGE_PYTHON3}"
# Setup phase
echo "Setup"
ANSIBLE_ROLES_PATH=.. ansible-playbook setup.yml
# If docker wasn't installed, don't run the tests
if [ "$(command -v docker)" == "" ]; then
exit
fi
# Test phase
CONTAINER_SUFFIX=-${RANDOM}
DOCKER_CONTAINERS="docker-connection-test-container${CONTAINER_SUFFIX}"
[[ -n "$DEBUG" || -n "$ANSIBLE_DEBUG" ]] && set -x
set -euo pipefail
cleanup() {
echo "Cleanup"
docker rm -f ${DOCKER_CONTAINERS}
echo "Shutdown"
ANSIBLE_ROLES_PATH=.. ansible-playbook shutdown.yml
echo "Done"
}
trap cleanup INT TERM EXIT
echo "Start containers"
for CONTAINER in ${DOCKER_CONTAINERS}; do
if [ "${ANSIBLE_TEST_COVERAGE:-}" == "" ]; then
docker run --rm --name "${CONTAINER}" --detach "${IMAGE}" /bin/sh -c 'sleep 10m'
else
docker run --rm --name "${CONTAINER}" --detach -v /tmp:/tmp "${IMAGE}" /bin/sh -c 'sleep 10m'
docker exec "${CONTAINER}" pip3 install coverage
fi
echo "${CONTAINER}"
done
cat > test_connection.inventory << EOF
[docker_api]
docker_api-no-pipelining ansible_pipelining=false
docker_api-pipelining ansible_pipelining=true
docker_api-working-dir ansible_docker_working_dir=/home
docker_api-privileged ansible_docker_privileged=true
[docker_api:vars]
ansible_host=docker-connection-test-container${CONTAINER_SUFFIX}
ansible_connection=community.docker.docker_api
ansible_python_interpreter=/usr/local/bin/python3
EOF
echo "Run tests"
./runme-connection.sh "$@"

View File

@ -0,0 +1,18 @@
---
# 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
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Setup docker
import_role:
name: setup_docker
- name: Setup docker Python deps
import_role:
name: setup_docker_python_deps

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
- hosts: localhost
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Remove docker packages
action: "{{ ansible_facts.pkg_mgr }}"
args:
name:
- docker
- docker-ce
- docker-ce-cli
state: absent
when: not docker_skip_cleanup

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
azp/5
skip/docker # this requires unfettered access to the container host
skip/rhel7.9 # nsenter does not work out of the box
destructive

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_docker

View File

@ -0,0 +1,75 @@
#!/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 -euo pipefail
[[ -n "${DEBUG:-}" || -n "${ANSIBLE_DEBUG:-}" ]] && set -x
readonly IMAGE="quay.io/ansible/ansible-runner:devel"
# shellcheck disable=SC2155
readonly PYTHON="$(command -v python3 python | head -n1)"
# Determine collection root
COLLECTION_ROOT=./
while true; do
if [ -e ${COLLECTION_ROOT}galaxy.yml ] || [ -e ${COLLECTION_ROOT}MANIFEST.json ]; then
break
fi
COLLECTION_ROOT="${COLLECTION_ROOT}../"
done
# shellcheck disable=SC2155
readonly COLLECTION_ROOT="$(cd ${COLLECTION_ROOT} ; pwd)"
# Setup phase
echo "Setup"
ANSIBLE_ROLES_PATH=.. ansible-playbook setup.yml
# If docker wasn't installed, don't run the tests
if [ "$(command -v docker)" == "" ]; then
exit
fi
cleanup() {
echo "Cleanup"
echo "Shutdown"
ANSIBLE_ROLES_PATH=.. ansible-playbook shutdown.yml
echo "Done"
}
envs=(--env "HOME=${HOME:-}")
while IFS=$'\0' read -d '' -r line; do
key="$(echo "$line" | cut -d= -f1)"
value="$(echo "$line" | cut -d= -f2-)"
if [[ "${key}" =~ ^(ANSIBLE_|JUNIT_OUTPUT_DIR$|OUTPUT_DIR$|PYTHONPATH$) ]]; then
envs+=(--env "${key}=${value}")
fi
done < <(printenv -0)
# Test phase
cat > test_connection.inventory << EOF
[nsenter]
nsenter-no-pipelining ansible_pipelining=false
nsenter-pipelining ansible_pipelining=true
[nsenter:vars]
ansible_host=localhost
ansible_connection=community.docker.nsenter
ansible_host_volume_mount=/host
ansible_nsenter_pid=1
ansible_python_interpreter=${PYTHON}
EOF
echo "Run tests"
set -x
docker run \
-i \
--rm \
--privileged \
--pid host \
"${envs[@]}" \
--volume "${COLLECTION_ROOT}:${COLLECTION_ROOT}" \
--workdir "$(pwd)" \
"${IMAGE}" \
./runme-connection.sh "$@"

View File

@ -0,0 +1,14 @@
---
# 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
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Setup docker
import_role:
name: setup_docker

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
- hosts: localhost
connection: local
vars:
docker_skip_cleanup: true
tasks:
- name: Remove docker packages
action: "{{ ansible_facts.pkg_mgr }}"
args:
name:
- docker
- docker-ce
- docker-ce-cli
state: absent
when: not docker_skip_cleanup

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
needs/target/connection
hidden

View File

@ -0,0 +1,23 @@
#!/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
# Connection tests for POSIX platforms use this script by linking to it from the appropriate 'connection_' target dir.
# The name of the inventory group to test is extracted from the directory name following the 'connection_' prefix.
PYTHON="$(command -v python3 python | head -n1)"
group=$(${PYTHON} -c \
"from os import path; print(path.basename(path.abspath(path.dirname('$0'))).replace('connection_', ''))")
cd ../connection
INVENTORY="../connection_${group}/test_connection.inventory" ./test.sh \
-e target_hosts="${group}" \
-e action_prefix= \
-e local_tmp=/tmp/ansible-local \
-e remote_tmp=/tmp/ansible-remote \
"$@"

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/4
skip/rhel

View File

@ -0,0 +1,41 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- name: Get facts
current_container_facts:
register: result
# WARNING: This is not a proper test as it won't fail when the module does not work!
# To make this a proper test, we need to know the environment in which this
# test runs, which we do not know in general...
- name: Print facts
ansible.builtin.debug:
var: result.ansible_facts
- name: Read files
ansible.builtin.slurp:
src: '{{ item }}'
loop:
- /proc/self/cgroup
- /proc/self/cpuset
- /proc/self/mountinfo
register: slurp
ignore_errors: true
- name: Print files
ansible.builtin.debug:
msg: |-
{{ item.content | ansible.builtin.b64decode | split('
') }}
loop: '{{ slurp.results }}'
loop_control:
label: '{{ item.source | default(item.item) }}'
when: item is not failed

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/4
destructive

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
dependencies:
- setup_docker_cli_compose
# The Python dependencies are needed for the other modules
- setup_docker_python_deps
- setup_remote_tmp_dir

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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Create random name prefix (for services, ...)
- name: Create random container name prefix
set_fact:
name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
dnetworks: []
images: []
- debug:
msg: "Using name prefix {{ name_prefix }}"
# Run the tests
- block:
- name: Show docker compose --help output
command: docker compose --help
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
loop: "{{ cnames }}"
diff: false
- name: "Make sure all networks are removed"
docker_network:
name: "{{ item }}"
state: absent
force: true
loop: "{{ dnetworks }}"
diff: false
- name: "Make sure all images are removed"
docker_image_remove:
name: "{{ item }}"
loop: "{{ images }}"
diff: false
when: docker_has_compose and docker_compose_version is version('2.18.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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

View File

@ -0,0 +1,135 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-build"
cname: "{{ name_prefix }}-container"
iname: "{{ name_prefix }}-image"
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service: |
services:
{{ cname }}:
build: ./build
image: "{{ iname }}"
pull_policy: never
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
images: "{{ images + [iname] }}"
- name: Create project directory
file:
path: '{{ item }}'
state: directory
loop:
- '{{ project_src }}'
- '{{ project_src }}/build'
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- name: Template Dockerfile
copy:
dest: '{{ project_src }}/build/Dockerfile'
content: |
FROM {{ docker_test_image_alpine }}
ENTRYPOINT ["/bin/sh", "-c", "sleep 10m"]
- name: Present (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_1_check
- name: Present
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_1
- name: Present (idempotent check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_2_check
- name: Present (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_2
- name: Present (idempotent check, build=always, ignore_build_events=false)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
build: always
ignore_build_events: false
check_mode: true
register: present_3_check
- name: Present (idempotent, build=always, ignore_build_events=false)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
build: always
ignore_build_events: false
register: present_3
- name: Present (idempotent check, build=always, ignore_build_events=true)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
build: always
ignore_build_events: true
check_mode: true
register: present_4_check
- name: Present (idempotent, build=always, ignore_build_events=true)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
build: always
ignore_build_events: true
register: present_4
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- present_1.containers | length == 1
- present_1.containers[0].Name == (pname ~ '-' ~ cname ~ '-1')
- present_1.images | length == 1
- present_1.images[0].ContainerName == (pname ~ '-' ~ cname ~ '-1')
- present_1.images[0].Repository == iname
- present_1.images[0].Tag == "latest"
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- ((present_3 is changed) if docker_compose_version is version('2.31.0', '>=') and docker_compose_version is version('2.32.2', '<') else (present_3 is not changed))
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4_check is changed
- present_4_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4 is not changed
- present_4.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
always:
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent

View File

@ -0,0 +1,70 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-exit"
cname: "{{ name_prefix }}-container"
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "exit 0"'
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- name: Present (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
wait: true
check_mode: true
register: present_1_check
- name: Present
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
wait: true
register: present_1
ignore_errors: true
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is failed
- present_1.containers | length == 1
- present_1.containers[0].Name == (pname ~ '-' ~ cname ~ '-1')
- present_1.containers[0].Image == docker_test_image_alpine
- present_1.containers[0].State == 'exited'
- present_1.containers[0].ExitCode == 0
- present_1.images | length == 1
- present_1.images[0].ContainerName == (pname ~ '-' ~ cname ~ '-1')
- present_1.images[0].Repository == (docker_test_image_alpine | split(':') | first)
- present_1.images[0].Tag == (docker_test_image_alpine | split(':') | last)
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- >-
("container " ~ pname ~ '-' ~ cname ~ "-1 exited (0)") in present_1.msg

View File

@ -0,0 +1,288 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-definition"
cname: "{{ name_prefix }}-container"
test_service: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_grace_period: 1s
test_service_mod: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 15m"'
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
####################################################################
## Present #########################################################
####################################################################
- name: Present (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
check_mode: true
register: present_1_check
- name: Present
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
register: present_1
- name: Present (idempotent check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
check_mode: true
register: present_2_check
- name: Present (idempotent)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
register: present_2
- name: Present (changed check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: present
check_mode: true
register: present_3_check
- name: Present (changed)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: present
register: present_3
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- present_1.containers | length == 1
- present_1.containers[0].Name == (pname ~ '-' ~ cname ~ '-1')
- present_1.containers[0].Image == docker_test_image_alpine
- present_1.images | length == 1
- present_1.images[0].ContainerName == (pname ~ '-' ~ cname ~ '-1')
- present_1.images[0].Repository == (docker_test_image_alpine | split(':') | first)
- present_1.images[0].Tag == (docker_test_image_alpine | split(':') | last)
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3 is changed
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
####################################################################
## Absent ##########################################################
####################################################################
- name: Absent (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: absent
check_mode: true
register: absent_1_check
- name: Absent
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: absent
register: absent_1
- name: Absent (idempotent check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: absent
check_mode: true
register: absent_2_check
- name: Absent (idempotent)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service_mod | from_yaml }}'
state: absent
register: absent_2
- assert:
that:
- absent_1_check is changed
- absent_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_1 is changed
- absent_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_2_check is not changed
- absent_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_2 is not changed
- absent_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
####################################################################
## Stopping and starting ###########################################
####################################################################
- name: Present stopped (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
check_mode: true
register: present_1_check
- name: Present stopped
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
register: present_1
- name: Present stopped (idempotent check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
check_mode: true
register: present_2_check
- name: Present stopped (idempotent)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
register: present_2
- name: Started (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
check_mode: true
register: present_3_check
- name: Started
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
register: present_3
- name: Started (idempotent check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
check_mode: true
register: present_4_check
- name: Started (idempotent)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: present
register: present_4
- name: Restarted (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: restarted
check_mode: true
register: present_5_check
- name: Restarted
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: restarted
register: present_5
- name: Stopped (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
check_mode: true
register: present_6_check
- name: Stopped
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: stopped
register: present_6
- name: Restarted (check)
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: restarted
check_mode: true
register: present_7_check
- name: Restarted
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: restarted
register: present_7
- name: Cleanup
docker_compose_v2:
project_name: '{{ pname }}'
definition: '{{ test_service | from_yaml }}'
state: absent
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3 is changed
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4_check is not changed
- present_4_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4 is not changed
- present_4.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5_check is changed
- present_5_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5 is changed
- present_5.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_6_check is changed
- present_6_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_6 is changed
- present_6.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_7_check is changed
- present_7_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_7 is changed
- present_7.warnings | default([]) | select('regex', ' Please report this at ') | length == 0

View File

@ -0,0 +1,223 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-pull"
cname: "{{ name_prefix }}-cont"
non_existing_image: does-not-exist:latest
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service_non_existing: |
services:
{{ cname }}:
image: {{ non_existing_image }}
test_service_simple: |
services:
{{ cname }}:
image: {{ docker_test_image_simple_1 }}
command: 10m
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
- name: Make sure images are not around
docker_image_remove:
name: '{{ item }}'
loop:
- '{{ non_existing_image }}'
- '{{ docker_test_image_simple_1 }}'
####################################################################
## Missing image ###################################################
####################################################################
- name: Template project file with non-existing image
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service_non_existing }}'
- name: Present with pull=never (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: never
check_mode: true
register: present_1_check
ignore_errors: true
- name: Present with pull=never
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: never
register: present_1
ignore_errors: true
- name: Present without explicit pull (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_2_check
ignore_errors: true
- name: Present without explicit pull
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_2
ignore_errors: true
- assert:
that:
- present_1_check is failed or present_1_check is changed
- present_1_check is changed or present_1_check.msg.startswith('General error:')
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is failed
- present_1.msg.startswith('General error:')
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is failed
- present_2_check.msg.startswith('Error when processing ' ~ cname ~ ':')
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is failed
- present_2.msg.startswith('Error when processing ' ~ cname ~ ':')
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
####################################################################
## Regular image ###################################################
####################################################################
- name: Template project file with simple image
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service_simple }}'
- name: Present with pull=missing (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
check_mode: true
register: present_1_check
- name: Present with pull=missing
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
register: present_1
- name: Present with pull=missing (idempotent, check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
check_mode: true
register: present_2_check
- name: Present with pull=missing (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
register: present_2
- name: Present with pull=always (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: always
check_mode: true
register: present_3_check
- name: Present with pull=always
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: always
register: present_3
- name: Stopping service
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
- name: Present with pull=never (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
check_mode: true
register: present_4_check
- name: Present with pull=never
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
register: present_4
- name: Present with pull=never (idempotent, check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
check_mode: true
register: present_5_check
- name: Present with pull=never (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
pull: missing
register: present_5
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
- assert:
that:
- present_1_check is changed
- (present_1_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- (present_1_check.actions | selectattr('status', 'eq', 'Creating') | first) is truthy
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- (present_1.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- (present_1.actions | selectattr('status', 'eq', 'Creating') | first) is truthy
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- (present_3_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- present_3_check.actions | selectattr('status', 'eq', 'Creating') | length == 0
- present_3_check.actions | selectattr('status', 'eq', 'Recreating') | length == 0
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3 is not changed
- (present_3.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- present_3.actions | selectattr('status', 'eq', 'Creating') | length == 0
- present_3.actions | selectattr('status', 'eq', 'Recreating') | length == 0
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4_check is changed
- present_4_check.actions | selectattr('status', 'eq', 'Pulling') | length == 0
- present_4_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4 is changed
- present_4.actions | selectattr('status', 'eq', 'Pulling') | length == 0
- present_4.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5_check is not changed
- present_5_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5 is not changed
- present_5.warnings | default([]) | select('regex', ' Please report this at ') | length == 0

View File

@ -0,0 +1,291 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-start-stop"
cname: "{{ name_prefix }}-container"
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_grace_period: 1s
test_service_mod: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 15m"'
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
####################################################################
## Present #########################################################
####################################################################
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- name: Present (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_1_check
- name: Present
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_1
- name: Present (idempotent check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_2_check
- name: Present (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_2
- name: Template modified project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service_mod }}'
- name: Present (changed check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_3_check
- name: Present (changed)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_3
- name: Present with --yes
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
assume_yes: true
when: docker_compose_version is version('2.32.0', '>=')
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- present_1.containers | length == 1
- present_1.containers[0].Name == (pname ~ '-' ~ cname ~ '-1')
- present_1.containers[0].Image == docker_test_image_alpine
- present_1.images | length == 1
- present_1.images[0].ContainerName == (pname ~ '-' ~ cname ~ '-1')
- present_1.images[0].Repository == (docker_test_image_alpine | split(':') | first)
- present_1.images[0].Tag == (docker_test_image_alpine | split(':') | last)
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3 is changed
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
####################################################################
## Absent ##########################################################
####################################################################
- name: Absent (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
check_mode: true
register: absent_1_check
- name: Absent
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
register: absent_1
- name: Absent (idempotent check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
check_mode: true
register: absent_2_check
- name: Absent (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
register: absent_2
- assert:
that:
- absent_1_check is changed
- absent_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_1 is changed
- absent_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_2_check is not changed
- absent_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- absent_2 is not changed
- absent_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
####################################################################
## Stopping and starting ###########################################
####################################################################
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- name: Present stopped (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
check_mode: true
register: present_1_check
- name: Present stopped
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
register: present_1
- name: Present stopped (idempotent check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
check_mode: true
register: present_2_check
- name: Present stopped (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
register: present_2
- name: Started (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_3_check
- name: Started
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_3
- name: Started (idempotent check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
check_mode: true
register: present_4_check
- name: Started (idempotent)
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
register: present_4
- name: Restarted (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: restarted
check_mode: true
register: present_5_check
- name: Restarted
docker_compose_v2:
project_src: '{{ project_src }}'
state: restarted
register: present_5
- name: Stopped (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
check_mode: true
register: present_6_check
- name: Stopped
docker_compose_v2:
project_src: '{{ project_src }}'
state: stopped
register: present_6
- name: Restarted (check)
docker_compose_v2:
project_src: '{{ project_src }}'
state: restarted
check_mode: true
register: present_7_check
- name: Restarted
docker_compose_v2:
project_src: '{{ project_src }}'
state: restarted
register: present_7
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
state: absent
- assert:
that:
- present_1_check is changed
- present_1_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_1 is changed
- present_1.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2_check is not changed
- present_2_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_2 is not changed
- present_2.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3_check is changed
- present_3_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_3 is changed
- present_3.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4_check is not changed
- present_4_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_4 is not changed
- present_4.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5_check is changed
- present_5_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_5 is changed
- present_5.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_6_check is changed
- present_6_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_6 is changed
- present_6.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_7_check is changed
- present_7_check.warnings | default([]) | select('regex', ' Please report this at ') | length == 0
- present_7 is changed
- present_7.warnings | default([]) | select('regex', ' Please report this at ') | length == 0

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/4
destructive

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
dependencies:
- setup_docker_cli_compose
# The Python dependencies are needed for the other modules
- setup_docker_python_deps
- setup_remote_tmp_dir

View File

@ -0,0 +1,49 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Create random name prefix (for services, ...)
- name: Create random container name prefix
set_fact:
name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
dnetworks: []
- debug:
msg: "Using name prefix {{ name_prefix }}"
# Run the tests
- block:
- name: Show docker compose --help output
command: docker compose --help
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
with_items: "{{ cnames }}"
diff: false
- name: "Make sure all networks are removed"
docker_network:
name: "{{ item }}"
state: absent
force: true
with_items: "{{ dnetworks }}"
diff: false
when: docker_has_compose and docker_compose_version is version('2.18.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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

View File

@ -0,0 +1,97 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-start-stop"
cname: "{{ name_prefix }}-container"
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- block:
- name: Start services
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
- name: Run command with command
docker_compose_v2_exec:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "ls /"
register: result_1
- name: Run command with argv
docker_compose_v2_exec:
project_src: '{{ project_src }}'
service: '{{ cname }}'
argv:
- /bin/sh
- "-c"
- whoami
user: "1234"
register: result_2
failed_when: result_2.rc != 1
- name: Run detached command
docker_compose_v2_exec:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "sleep 1"
detach: true
register: result_3
- name: Run command with input
docker_compose_v2_exec:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "cat"
stdin: This is a test
register: result_4
- assert:
that:
- result_1.rc == 0
- result_1.stderr == ""
- >-
"usr" in result_1.stdout_lines
and
"etc" in result_1.stdout_lines
- result_2.rc == 1
- >-
"whoami: unknown uid 1234" in result_2.stderr
- result_2.stdout == ""
- result_3.rc is not defined
- result_3.stdout is not defined
- result_3.stderr is not defined
- result_4.rc == 0
- result_4.stdout == "This is a test"
- result_4.stderr == ""
always:
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
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/4
destructive

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
dependencies:
- setup_docker_cli_compose
# The Python dependencies are needed for the other modules
- setup_docker_python_deps
- setup_remote_tmp_dir

View File

@ -0,0 +1,52 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Create random name prefix (for services, ...)
- name: Create random container name prefix
set_fact:
name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
dnetworks: []
- debug:
msg: "Using name prefix {{ name_prefix }}"
- name: Show images
command: docker images --all --digests
# Run the tests
- block:
- name: Show docker compose --help output
command: docker compose --help
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
with_items: "{{ cnames }}"
diff: false
- name: "Make sure all networks are removed"
docker_network:
name: "{{ item }}"
state: absent
force: true
with_items: "{{ dnetworks }}"
diff: false
when: docker_has_compose and docker_compose_version is version('2.18.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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

View File

@ -0,0 +1,206 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-pull"
cname: "{{ name_prefix }}-cont"
non_existing_image: does-not-exist:latest
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service_non_existing: |
version: '3'
services:
{{ cname }}:
image: {{ non_existing_image }}
test_service_simple: |
version: '3'
services:
{{ cname }}:
image: {{ docker_test_image_simple_1 }}
command: 10m
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
- name: Make sure images are not around
docker_image_remove:
name: '{{ item }}'
loop:
- '{{ non_existing_image }}'
- '{{ docker_test_image_simple_1 }}'
####################################################################
## Missing image ###################################################
####################################################################
- name: Template project file with non-existing image
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service_non_existing }}'
- name: Pull (check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
check_mode: true
register: pull_1_check
ignore_errors: true
- name: Pull
docker_compose_v2_pull:
project_src: '{{ project_src }}'
register: pull_1
ignore_errors: true
- assert:
that:
- pull_1_check is failed or pull_1_check is changed
- pull_1_check is changed or pull_1_check.msg.startswith('Error when processing ')
- pull_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_1 is failed
- pull_1.msg.startswith('Error when processing ')
- pull_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
####################################################################
## Regular image ###################################################
####################################################################
- name: Template project file with simple image
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service_simple }}'
- when: docker_compose_version is version('2.22.0', '>=')
block:
- name: Pull with policy=missing (check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: missing
check_mode: true
register: pull_1_check
- name: Pull with policy=missing
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: missing
register: pull_1
- name: Pull with policy=missing (idempotent, check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: missing
check_mode: true
register: pull_2_check
- name: Pull with policy=missing (idempotent)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: missing
register: pull_2
- name: Make sure image is not around
docker_image_remove:
name: '{{ docker_test_image_simple_1 }}'
- name: Pull with policy=always (check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
check_mode: true
register: pull_3_check
- name: Pull with policy=always
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
register: pull_3
- name: Pull with policy=always (check, idempotent)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
check_mode: true
register: pull_4_check
- name: Pull with policy=always (idempotent)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
register: pull_4
- assert:
that:
- pull_1_check is changed
- (pull_1_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_1 is changed
- (pull_1.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_2_check is not changed
- pull_2_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_2 is not changed
- pull_2.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_3_check is changed
- (pull_3_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_3_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_3 is changed
- (pull_3.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_3.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_4_check is changed
- (pull_4_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_4_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_4 is not changed
- (pull_4.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_4.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- when: docker_compose_version is version('2.22.0', '<')
block:
- name: Pull with policy=always (check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
check_mode: true
register: pull_1_check
- name: Pull with policy=always
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
register: pull_1
- name: Pull with policy=always (again, check)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
check_mode: true
register: pull_2_check
- name: Pull with policy=always (again)
docker_compose_v2_pull:
project_src: '{{ project_src }}'
policy: always
register: pull_2
- assert:
that:
- pull_1_check is changed
- (pull_1_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_1_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_1 is changed
- (pull_1.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_1.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_2_check is changed
- (pull_2_check.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_2_check.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0
- pull_2 is not changed
- (pull_2.actions | selectattr('status', 'eq', 'Pulling') | first) is truthy
- pull_2.warnings | default([]) | select('regex', 'Cannot parse event from ') | length == 0

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/4
destructive

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
dependencies:
- setup_docker_cli_compose
# The Python dependencies are needed for the other modules
- setup_docker_python_deps
- setup_remote_tmp_dir

View File

@ -0,0 +1,49 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
# Create random name prefix (for services, ...)
- name: Create random container name prefix
set_fact:
name_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
dnetworks: []
- debug:
msg: "Using name prefix {{ name_prefix }}"
# Run the tests
- block:
- name: Show docker compose --help output
command: docker compose --help
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
with_items: "{{ cnames }}"
diff: false
- name: "Make sure all networks are removed"
docker_network:
name: "{{ item }}"
state: absent
force: true
with_items: "{{ dnetworks }}"
diff: false
when: docker_has_compose and docker_compose_version is version('2.18.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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

View File

@ -0,0 +1,104 @@
---
# 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
- vars:
pname: "{{ name_prefix }}-start-stop"
cname: "{{ name_prefix }}-container"
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
test_service: |
services:
{{ cname }}:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_grace_period: 1s
block:
- name: Registering container name
set_fact:
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
- name: Create project directory
file:
path: '{{ project_src }}'
state: directory
- name: Template default project file
copy:
dest: '{{ project_src }}/docker-compose.yml'
content: '{{ test_service }}'
- block:
- name: Start services
docker_compose_v2:
project_src: '{{ project_src }}'
state: present
- name: Run command with command
docker_compose_v2_run:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "ls /"
cleanup: true
register: result_1
- name: Run command with argv
docker_compose_v2_run:
project_src: '{{ project_src }}'
service: '{{ cname }}'
argv:
- /bin/sh
- "-c"
- whoami
user: "1234"
cleanup: true
register: result_2
failed_when: result_2.rc != 1
- name: Run detached command
docker_compose_v2_run:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "sleep 1"
detach: true
cleanup: true
register: result_3
- name: Run command with input
docker_compose_v2_run:
project_src: '{{ project_src }}'
service: '{{ cname }}'
command: /bin/sh -c "cat"
stdin: This is a test
register: result_4
- assert:
that:
- result_1.rc == 0
- result_1.stderr == ""
- >-
"usr" in result_1.stdout_lines
and
"etc" in result_1.stdout_lines
- result_1.container_id is not defined
- result_2.rc == 1
- >-
"whoami: unknown uid 1234" in result_2.stderr
- result_2.stdout == ""
- result_2.container_id is not defined
- result_3.rc is not defined
- result_3.stdout is not defined
- result_3.stderr is not defined
- result_3.container_id is string
- result_4.rc == 0
- result_4.stdout == "This is a test"
- result_4.stderr is string
- result_4.container_id is not defined
always:
- name: Cleanup
docker_compose_v2:
project_src: '{{ project_src }}'
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/3
destructive

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
dependencies:
- setup_docker
- setup_docker_sdk_for_python
- setup_remote_tmp_dir

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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- include_tasks: test_docker_config.yml
when: docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.30', '>=')
- fail: msg="Too old docker / docker-py version to run docker_config tests!"
when: not(docker_py_version is version('2.6.0', '>=') and docker_api_version is version('1.30', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

View File

@ -0,0 +1,334 @@
---
# 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:
- shell: "docker info --format '{% raw %}{{json .}}{% endraw %}' | python -m json.tool"
- name: Make sure we're not already using Docker swarm
docker_swarm:
state: absent
force: true
- shell: "docker info --format '{% raw %}{{json .}}{% endraw %}' | python -m json.tool"
- name: Create a Swarm cluster
docker_swarm:
name: default
state: present
advertise_addr: "{{ ansible_default_ipv4.address | default('127.0.0.1') }}"
- name: Parameter name should be required
docker_config:
state: present
ignore_errors: true
register: output
- name: Assert failure when called with no name
assert:
that:
- 'output is failed'
- 'output.msg == "missing required arguments: name"'
- name: Test parameters
docker_config:
name: foo
state: present
ignore_errors: true
register: output
- name: Assert failure when called with no data
assert:
that:
- 'output is failed'
- 'output.msg == "state is present but any of the following are missing: data, data_src"'
- name: Create config
docker_config:
name: db_password
data: opensesame!
state: present
register: output
- name: Create variable config_id
set_fact:
config_id: "{{ output.config_id }}"
- name: Inspect config
command: "docker config inspect {{ config_id }}"
register: inspect
ignore_errors: true
- debug:
var: inspect
- name: Assert config creation succeeded
assert:
that:
- "'db_password' in inspect.stdout"
- "'ansible_key' in inspect.stdout"
when: inspect is not failed
- assert:
that:
- "'is too new. Maximum supported API version is' in inspect.stderr"
when: inspect is failed
- name: Create config again
docker_config:
name: db_password
data: opensesame!
state: present
register: output
- name: Assert create config is idempotent
assert:
that:
- output is not changed
- name: Write config into file
copy:
dest: "{{ remote_tmp_dir }}/data"
content: |-
opensesame!
- name: Create config again (from file)
docker_config:
name: db_password
data_src: "{{ remote_tmp_dir }}/data"
state: present
register: output
- name: Assert create config is idempotent
assert:
that:
- output is not changed
- name: Create config again (base64)
docker_config:
name: db_password
data: b3BlbnNlc2FtZSE=
data_is_b64: true
state: present
register: output
- name: Assert create config (base64) is idempotent
assert:
that:
- output is not changed
- name: Update config
docker_config:
name: db_password
data: newpassword!
state: present
register: output
- name: Assert config was updated
assert:
that:
- output is changed
- output.config_id != config_id
- name: Remove config
docker_config:
name: db_password
state: absent
- name: Check that config is removed
command: "docker config inspect {{ config_id }}"
register: output
ignore_errors: true
- name: Assert config was removed
assert:
that:
- output is failed
- name: Remove config
docker_config:
name: db_password
state: absent
register: output
- name: Assert remove config is idempotent
assert:
that:
- output is not changed
# Rolling update
- name: Create rolling config
docker_config:
name: rolling_password
data: opensesame!
rolling_versions: true
state: present
register: original_output
- name: Create variable config_id
set_fact:
config_id: "{{ original_output.config_id }}"
- name: Inspect config
command: "docker config inspect {{ config_id }}"
register: inspect
ignore_errors: true
- debug:
var: inspect
- name: Assert config creation succeeded
assert:
that:
- "'rolling_password' in inspect.stdout"
- "'ansible_key' in inspect.stdout"
- "'ansible_version' in inspect.stdout"
- original_output.config_name == 'rolling_password_v1'
when: inspect is not failed
- assert:
that:
- "'is too new. Maximum supported API version is' in inspect.stderr"
when: inspect is failed
- name: Create config again
docker_config:
name: rolling_password
data: newpassword!
rolling_versions: true
state: present
register: new_output
- name: Assert that new version is created
assert:
that:
- new_output is changed
- new_output.config_id != original_output.config_id
- new_output.config_name != original_output.config_name
- new_output.config_name == 'rolling_password_v2'
- name: Remove rolling configs
docker_config:
name: rolling_password
rolling_versions: true
state: absent
- name: Check that config is removed
command: "docker config inspect {{ original_output.config_id }}"
register: output
ignore_errors: true
- name: Assert config was removed
assert:
that:
- output is failed
- name: Check that config is removed
command: "docker config inspect {{ new_output.config_id }}"
register: output
ignore_errors: true
- name: Assert config was removed
assert:
that:
- output is failed
# template_driver tests
- when: docker_py_version is version('5.0.3', '>=') and docker_api_version is version('1.37', '>=')
block:
- name: Create regular config
docker_config:
name: db_password
data: opensesame!
state: present
- name: Update config with template_driver
docker_config:
name: db_password
data: opensesame!
template_driver: golang
state: present
register: output
- name: Assert config was updated
assert:
that:
- output is changed
- name: Invalid template_driver
docker_config:
name: db_password
data: opensesame!
template_driver: "not a template driver"
state: present
ignore_errors: true
register: output
- name: Assert failure when called with invalid template_driver
assert:
that:
- 'output is failed'
- 'output.msg == "value of template_driver must be one of: golang, got: not a template driver"'
- name: Create config again
docker_config:
name: db_password
data: opensesame!
template_driver: golang
state: present
register: output
- name: Assert create config is idempotent
assert:
that:
- output is not changed
# data is the docker swarm's name
- name: Update config with template data
docker_config:
name: db_password
data: "{{ '{{' }} .Service.Name {{ '}}' }}"
template_driver: golang
state: present
register: output
- name: Inspect config
command: "docker config inspect {{ output.config_id }}"
register: inspect
- name: Show inspection result
debug:
var: inspect
- name: Assert config creation succeeded
assert:
that:
- "'db_password' in inspect.stdout"
- "'ansible_key' in inspect.stdout"
# According to the API docs, 'Data' is "Base64-url-safe-encoded (RFC 4648) config data."
- "'\"Data\": \"e3sgLlNlcnZpY2UuTmFtZSB9fQ==\"' in inspect.stdout"
- "'Templating' in inspect.stdout"
- "'\"Name\": \"golang\"' in inspect.stdout"
- name: Remove config
docker_config:
name: db_password
state: absent
- name: Check that config is removed
command: "docker config inspect {{ output.config_id }}"
register: output
ignore_errors: true
- name: Assert config was removed
assert:
that:
- output is failed
always:
- name: Remove a Swarm cluster
docker_swarm:
state: absent
force: true

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/5
destructive

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
TEST3=val3
TEST4=val4

View File

@ -0,0 +1,21 @@
# Copyright (c) 2020, Felix Fontein <felix@fontein.de>
# 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
def _normalize_ipaddr(ipaddr):
# Import when needed, to allow installation of that module in the test setup
import ipaddress
return ipaddress.ip_address(ipaddr).compressed
class FilterModule(object):
""" IP address and network manipulation filters """
def filters(self):
return {
'normalize_ipaddr': _normalize_ipaddr,
}

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_docker
- setup_docker_python_deps

View File

@ -0,0 +1,70 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- name: Gather facts on controller
setup:
gather_subset: '!all'
delegate_to: localhost
delegate_facts: true
run_once: true
- name: Make sure ipaddress is available on controller
pip:
name: ipaddress
delegate_to: localhost
when: hostvars['localhost'].ansible_facts.python.version.major < 3
# Create random name prefix (for containers, networks, ...)
- name: Create random container name prefix
set_fact:
cname_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
inames: []
dnetworks: []
- debug:
msg: "Using container name prefix {{ cname_prefix }}"
- name: Retrieve docker host info
docker_host_info:
register: docker_host_info
# Run the tests
- block:
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
with_items: "{{ cnames }}"
diff: false
- name: "Make sure all images are removed"
docker_image_remove:
name: "{{ item }}"
with_items: "{{ inames }}"
- name: "Make sure all networks are removed"
docker_network:
name: "{{ item }}"
state: absent
force: true
with_items: "{{ dnetworks }}"
diff: false
when: docker_api_version is version('1.25', '>=')
- fail: msg="Too old docker / docker-py version to run all docker_container tests!"
when: not(docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

View File

@ -0,0 +1,466 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-comparisons' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
####################################################################
## value ###########################################################
####################################################################
- name: value
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.com
register: value_1
- name: value (change, ignore)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.org
force_kill: true
comparisons:
hostname: ignore
register: value_2
- name: value (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.org
force_kill: true
comparisons:
hostname: strict
register: value_3
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- value_1 is changed
- value_2 is not changed
- value_3 is changed
####################################################################
## list ############################################################
####################################################################
- name: list
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
dns_servers:
- 1.1.1.1
- 8.8.8.8
register: list_1
- name: list (change, ignore)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
dns_servers:
- 9.9.9.9
force_kill: true
comparisons:
dns_servers: ignore
register: list_2
- name: list (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
dns_servers:
- 9.9.9.9
force_kill: true
comparisons:
dns_servers: strict
register: list_3
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- list_1 is changed
- list_2 is not changed
- list_3 is changed
####################################################################
## set #############################################################
####################################################################
- name: set
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
groups:
- "1010"
- "1011"
register: set_1
- name: set (change, ignore)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
groups:
- "1010"
- "1011"
- "1012"
force_kill: true
comparisons:
groups: ignore
register: set_2
- name: set (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
groups:
- "1010"
- "1011"
- "1012"
force_kill: true
comparisons:
groups: allow_more_present
register: set_3
- name: set (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
groups:
- "1010"
- "1012"
force_kill: true
comparisons:
groups: allow_more_present
register: set_4
- name: set (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
groups:
- "1010"
- "1012"
force_kill: true
comparisons:
groups: strict
register: set_5
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- set_1 is changed
- set_2 is not changed
- set_3 is changed
- set_4 is not changed
- set_5 is changed
####################################################################
## set(dict) #######################################################
####################################################################
- name: set(dict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
devices:
- "/dev/random:/dev/virt-random:rwm"
- "/dev/urandom:/dev/virt-urandom:rwm"
register: set_dict_1
- name: set(dict) (change, ignore)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
devices:
- "/dev/random:/dev/virt-random:rwm"
- "/dev/urandom:/dev/virt-urandom:rwm"
- "/dev/null:/dev/virt-null:rwm"
force_kill: true
comparisons:
devices: ignore
register: set_dict_2
- name: set(dict) (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
devices:
- "/dev/random:/dev/virt-random:rwm"
- "/dev/urandom:/dev/virt-urandom:rwm"
- "/dev/null:/dev/virt-null:rwm"
force_kill: true
comparisons:
devices: allow_more_present
register: set_dict_3
- name: set(dict) (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
devices:
- "/dev/random:/dev/virt-random:rwm"
- "/dev/null:/dev/virt-null:rwm"
force_kill: true
comparisons:
devices: allow_more_present
register: set_dict_4
- name: set(dict) (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
devices:
- "/dev/random:/dev/virt-random:rwm"
- "/dev/null:/dev/virt-null:rwm"
force_kill: true
comparisons:
devices: strict
register: set_dict_5
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- set_dict_1 is changed
- set_dict_2 is not changed
- set_dict_3 is changed
- set_dict_4 is not changed
- set_dict_5 is changed
####################################################################
## dict ############################################################
####################################################################
- name: dict
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
labels:
ansible.test.1: hello
ansible.test.2: world
register: dict_1
- name: dict (change, ignore)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
labels:
ansible.test.1: hello
ansible.test.2: world
ansible.test.3: ansible
force_kill: true
comparisons:
labels: ignore
register: dict_2
- name: dict (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
labels:
ansible.test.1: hello
ansible.test.2: world
ansible.test.3: ansible
force_kill: true
comparisons:
labels: allow_more_present
register: dict_3
- name: dict (change, allow_more_present)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
labels:
ansible.test.1: hello
ansible.test.3: ansible
force_kill: true
comparisons:
labels: allow_more_present
register: dict_4
- name: dict (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
labels:
ansible.test.1: hello
ansible.test.3: ansible
force_kill: true
comparisons:
labels: strict
register: dict_5
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- dict_1 is changed
- dict_2 is not changed
- dict_3 is changed
- dict_4 is not changed
- dict_5 is changed
####################################################################
## wildcard ########################################################
####################################################################
- name: Pull {{ docker_test_image_hello_world }} image to make sure wildcard_2 test succeeds
# If the image isn't there, it will pull it and return 'changed'.
docker_image_pull:
name: "{{ docker_test_image_hello_world }}"
- name: wildcard
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.com
stop_timeout: 1
labels:
ansible.test.1: hello
ansible.test.2: world
ansible.test.3: ansible
register: wildcard_1
- name: wildcard (change, ignore)
docker_container:
image: "{{ docker_test_image_hello_world }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.org
stop_timeout: 2
labels:
ansible.test.1: hello
ansible.test.4: ignore
force_kill: true
comparisons:
'*': ignore
register: wildcard_2
- name: wildcard (change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.org
stop_timeout: 1
labels:
ansible.test.1: hello
ansible.test.2: world
ansible.test.3: ansible
force_kill: true
comparisons:
'*': strict
register: wildcard_3
- name: wildcard (no change, strict)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
hostname: example.org
stop_timeout: 1
labels:
ansible.test.1: hello
ansible.test.2: world
ansible.test.3: ansible
force_kill: true
comparisons:
'*': strict
register: wildcard_4
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- wildcard_1 is changed
- wildcard_2 is not changed
- wildcard_3 is changed
- wildcard_4 is not changed

View File

@ -0,0 +1,122 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-hi' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
####################################################################
## container_default_behavior: compatibility #######################
####################################################################
- name: Start container (check)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: compatibility
check_mode: true
register: start_1
- name: Start container
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: compatibility
register: start_2
- name: Start container (idempotent)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: compatibility
register: start_3
- name: Start container (idempotent check)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
state: started
container_default_behavior: compatibility
check_mode: true
register: start_4
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- start_1 is changed
- start_2 is changed
- start_3 is not changed
- start_4 is not changed
####################################################################
## container_default_behavior: no_defaults #########################
####################################################################
- name: Start container (check)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: no_defaults
check_mode: true
register: start_1
- name: Start container
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: no_defaults
register: start_2
- name: Start container (idempotent)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: no_defaults
register: start_3
- name: Start container (idempotent check)
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
container_default_behavior: no_defaults
check_mode: true
register: start_4
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- start_1 is changed
- start_2 is changed
- start_3 is not changed
- start_4 is not changed

View File

@ -0,0 +1,64 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-hi' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
- name: Prepare container
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_healthcheck }}"
command: '10m'
state: stopped
register: healthy_1
- debug: var=healthy_1.container.State
- name: Start container (not healthy in time)
docker_container:
name: "{{ cname }}"
state: healthy
healthy_wait_timeout: 1
register: healthy_2
ignore_errors: true
- debug: var=healthy_2.container.State
- name: Prepare container
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_healthcheck }}"
command: '10m 5s'
state: stopped
force_kill: true
register: healthy_3
- debug: var=healthy_3.container.State
- name: Start container (healthy in time)
docker_container:
name: "{{ cname }}"
state: healthy
healthy_wait_timeout: 10
register: healthy_4
- debug: var=healthy_4.container.State
- name: Cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
- assert:
that:
- healthy_2 is failed
- healthy_2.container.State.Health.Status == "starting"
- healthy_2.msg.startswith("Timeout of 1.0 seconds exceeded while waiting for container ")
- healthy_4 is changed
- healthy_4.container.State.Health.Status == "healthy"

View File

@ -0,0 +1,155 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-iid' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
- name: Pull images
docker_image_pull:
name: "{{ image }}"
loop:
- "{{ docker_test_image_hello_world }}"
- "{{ docker_test_image_alpine }}"
loop_control:
loop_var: image
- name: Get image ID of {{ docker_test_image_hello_world }} and {{ docker_test_image_alpine }} images
docker_image_info:
name:
- "{{ docker_test_image_hello_world }}"
- "{{ docker_test_image_alpine }}"
register: image_info
- assert:
that:
- image_info.images | length == 2
- name: Print image IDs
debug:
msg: "{{ docker_test_image_hello_world }}: {{ image_info.images[0].Id }}; {{ docker_test_image_alpine }}: {{ image_info.images[1].Id }}"
- name: Create container with {{ docker_test_image_hello_world }} image via ID
docker_container:
image: "{{ image_info.images[0].Id }}"
name: "{{ cname }}"
state: present
force_kill: true
register: create_1
- name: Create container with {{ docker_test_image_hello_world }} image via ID (idempotent)
docker_container:
image: "{{ image_info.images[0].Id }}"
name: "{{ cname }}"
state: present
force_kill: true
register: create_2
- name: Create container with {{ docker_test_image_alpine }} image via ID
docker_container:
image: "{{ image_info.images[1].Id }}"
name: "{{ cname }}"
state: present
force_kill: true
register: create_3
- name: Create container with {{ docker_test_image_alpine }} image via ID (idempotent)
docker_container:
image: "{{ image_info.images[1].Id }}"
name: "{{ cname }}"
state: present
force_kill: true
register: create_4
- name: Untag image
# Image will not be deleted since the container still uses it
docker_image_remove:
name: "{{ docker_test_image_alpine }}"
force: true
- name: Create container with {{ docker_test_image_alpine }} image via name (check mode, will pull, same image)
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: present
image_name_mismatch: ignore
register: create_5
check_mode: true
- name: Create container with {{ docker_test_image_alpine }} image via name (will pull, same image)
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: present
image_name_mismatch: ignore
register: create_6
- name: Cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- create_1 is changed
- create_2 is not changed
- create_3 is changed
- create_4 is not changed
- create_5 is changed
- create_6 is changed
- create_6.container.Image == image_info.images[1].Id
- create_6.container.Id == create_4.container.Id # make sure container wasn't recreated
- name: Create container with {{ docker_test_image_digest_base }} image via old digest
docker_container:
image: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}"
name: "{{ cname }}"
state: present
force_kill: true
register: digest_1
- name: Create container with {{ docker_test_image_digest_base }} image via old digest (idempotent)
docker_container:
image: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}"
name: "{{ cname }}"
state: present
force_kill: true
register: digest_2
- name: Create container with {{ docker_test_image_digest_base }} image via old digest (idempotent, pull)
docker_container:
image: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v1 }}"
name: "{{ cname }}"
pull: true
state: present
force_kill: true
register: digest_3
- name: Update container with {{ docker_test_image_digest_base }} image via new digest
docker_container:
image: "{{ docker_test_image_digest_base }}@sha256:{{ docker_test_image_digest_v2 }}"
name: "{{ cname }}"
state: present
force_kill: true
register: digest_4
- name: Cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- digest_1 is changed
- digest_2 is not changed
- digest_3 is not changed
- digest_4 is changed

View File

@ -0,0 +1,558 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-mounts' }}"
cname_h1: "{{ cname_prefix ~ '-mounts-h1' }}"
cname_h2: "{{ cname_prefix ~ '-mounts-h2' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname, cname_h1, cname_h2] }}"
####################################################################
## keep_volumes ####################################################
####################################################################
# TODO: - keep_volumes
####################################################################
## mounts ##########################################################
####################################################################
- name: mounts
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
- source: /
target: /whatever
type: bind
read_only: false
register: mounts_1
- name: mounts (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /
target: /whatever
type: bind
read_only: false
- source: /tmp
target: /tmp
type: bind
register: mounts_2
- name: mounts (less mounts)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
register: mounts_3
- name: mounts (more mounts)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
- source: /tmp
target: /somewhereelse
type: bind
read_only: true
force_kill: true
register: mounts_4
- name: mounts (different modes)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
- source: /tmp
target: /somewhereelse
type: bind
read_only: false
force_kill: true
register: mounts_5
- name: mounts (endpoint collision)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /home
target: /x
type: bind
- source: /etc
target: /x
type: bind
read_only: false
force_kill: true
register: mounts_6
ignore_errors: true
- name: mounts (anonymous volume)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /tmp
type: volume
force_kill: true
register: mounts_7
- name: mounts (anonymous volume idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /tmp
type: volume
force_kill: true
register: mounts_8
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- mounts_1 is changed
- mounts_2 is not changed
- mounts_3 is not changed
- mounts_4 is changed
- mounts_5 is changed
- mounts_6 is failed
- "'The mount point \"/x\" appears twice in the mounts option' == mounts_6.msg"
- mounts_7 is changed
- mounts_8 is not changed
####################################################################
## tmpfs ###########################################################
####################################################################
- name: tmpfs
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /cache1
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
- target: /cache2
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
force_kill: true
register: tmpfs_1
- name: tmpfs (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /cache2
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
- target: /cache1
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
force_kill: true
register: tmpfs_2
- name: tmpfs (more mounts)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /cache1
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
- target: /cache2
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
- target: /cache3
type: tmpfs
tmpfs_mode: "1777"
tmpfs_size: "1GB"
force_kill: true
register: tmpfs_3
- name: tmpfs (change mode)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /cache1
type: tmpfs
tmpfs_mode: "1700"
tmpfs_size: "1GB"
force_kill: true
register: tmpfs_4
- name: tmpfs (change size)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- target: /cache1
type: tmpfs
tmpfs_mode: "1700"
tmpfs_size: "2GB"
force_kill: true
register: tmpfs_5
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- tmpfs_1 is changed
- tmpfs_2 is not changed
- tmpfs_3 is changed
- tmpfs_4 is changed
- tmpfs_5 is changed
####################################################################
## mounts + volumes ################################################
####################################################################
- name: mounts + volumes
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /
target: /whatever
type: bind
read_only: true
volumes:
- /tmp:/tmp
register: mounts_volumes_1
- name: mounts + volumes (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /
target: /whatever
type: bind
read_only: true
volumes:
- /tmp:/tmp
register: mounts_volumes_2
- name: mounts + volumes (switching)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
read_only: false
volumes:
- /:/whatever:ro
force_kill: true
register: mounts_volumes_3
- name: mounts + volumes (collision, should fail)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
mounts:
- source: /tmp
target: /tmp
type: bind
read_only: false
volumes:
- /tmp:/tmp
force_kill: true
register: mounts_volumes_4
ignore_errors: true
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- mounts_volumes_1 is changed
- mounts_volumes_2 is not changed
- mounts_volumes_3 is changed
- mounts_volumes_4 is failed
- "'The mount point \"/tmp\" appears both in the volumes and mounts option' in mounts_volumes_4.msg"
####################################################################
## volume_driver ###################################################
####################################################################
- name: volume_driver
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
volume_driver: local
state: started
register: volume_driver_1
- name: volume_driver (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
volume_driver: local
state: started
register: volume_driver_2
- name: volume_driver (change)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
volume_driver: /
state: started
force_kill: true
register: volume_driver_3
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- volume_driver_1 is changed
- volume_driver_2 is not changed
- volume_driver_3 is changed
####################################################################
## volumes #########################################################
####################################################################
- name: volumes
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/tmp:/tmp"
- "/:/whatever:rw,z"
- "/anon:rw"
register: volumes_1
- name: volumes (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/:/whatever:rw,z"
- "/tmp:/tmp"
- "/anon:rw"
register: volumes_2
- name: volumes (less volumes)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/tmp:/tmp"
register: volumes_3
- name: volumes (more volumes)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/tmp:/tmp"
- "/tmp:/somewhereelse:ro,Z"
force_kill: true
register: volumes_4
- name: volumes (different modes)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/tmp:/tmp"
- "/tmp:/somewhereelse:ro"
force_kill: true
register: volumes_5
- name: volumes (collision)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes:
- "/etc:/tmp"
- "/home:/tmp:ro"
force_kill: true
register: volumes_6
ignore_errors: true
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- volumes_1 is changed
- volumes_1.container.Config.Volumes | length == 1
- volumes_1.container.Config.Volumes['/anon:rw'] | length == 0
- volumes_2 is not changed
- volumes_3 is not changed
- volumes_4 is changed
- not volumes_4.container.Config.Volumes
- volumes_5 is changed
- volumes_6 is failed
- "'The mount point \"/tmp\" appears twice in the volumes option' in volumes_6.msg"
####################################################################
## volumes_from ####################################################
####################################################################
- name: start helpers
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ container_name }}"
state: started
volumes:
- "{{ '/tmp:/tmp' if container_name == cname_h1 else '/:/whatever:ro' }}"
loop:
- "{{ cname_h1 }}"
- "{{ cname_h2 }}"
loop_control:
loop_var: container_name
- name: volumes_from
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes_from: "{{ cname_h1 }}"
register: volumes_from_1
- name: volumes_from (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes_from: "{{ cname_h1 }}"
register: volumes_from_2
- name: volumes_from (change)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
volumes_from: "{{ cname_h2 }}"
force_kill: true
register: volumes_from_3
- name: cleanup
docker_container:
name: "{{ container_name }}"
state: absent
force_kill: true
loop:
- "{{ cname }}"
- "{{ cname_h1 }}"
- "{{ cname_h2 }}"
loop_control:
loop_var: container_name
diff: false
- assert:
that:
- volumes_from_1 is changed
- volumes_from_2 is not changed
- volumes_from_3 is changed
####################################################################
####################################################################
####################################################################

View File

@ -0,0 +1,745 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-network' }}"
cname_h1: "{{ cname_prefix ~ '-network-h1' }}"
nname_1: "{{ cname_prefix ~ '-network-1' }}"
nname_2: "{{ cname_prefix ~ '-network-2' }}"
nname_3: "{{ cname_prefix ~ '-network-3' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname, cname_h1] }}"
dnetworks: "{{ dnetworks + [nname_1, nname_2, nname_3] }}"
- name: Create networks
docker_network:
name: "{{ network_name }}"
state: present
loop:
- "{{ nname_1 }}"
- "{{ nname_2 }}"
loop_control:
loop_var: network_name
- set_fact:
subnet_ipv4_base: 10.{{ 16 + (240 | random) }}.{{ 16 + (240 | random) }}
subnet_ipv6_base: fdb6:feea:{{ '%0.4x:%0.4x' | format(65536 | random, 65536 | random) }}
# If netaddr would be installed on the controller, one could do:
# subnet_ipv4: "10.{{ 16 + (240 | random) }}.{{ 16 + (240 | random) }}.0/24"
# subnet_ipv6: "fdb6:feea:{{ '%0.4x:%0.4x' | format(65536 | random, 65536 | random) }}::/64"
- set_fact:
subnet_ipv4: "{{ subnet_ipv4_base }}.0/24"
subnet_ipv6: "{{ subnet_ipv6_base }}::/64"
nname_3_ipv4_2: "{{ subnet_ipv4_base }}.2"
nname_3_ipv4_3: "{{ subnet_ipv4_base }}.3"
nname_3_ipv4_4: "{{ subnet_ipv4_base }}.4"
nname_3_ipv6_2: "{{ subnet_ipv6_base }}::2"
nname_3_ipv6_3: "{{ subnet_ipv6_base }}::3"
nname_3_ipv6_4: "{{ subnet_ipv6_base }}::4"
# If netaddr would be installed on the controller, one could do:
# nname_3_ipv4_2: "{{ subnet_ipv4 | ansible.netcommon.next_nth_usable(2) }}"
# nname_3_ipv4_3: "{{ subnet_ipv4 | ansible.netcommon.next_nth_usable(3) }}"
# nname_3_ipv4_4: "{{ subnet_ipv4 | ansible.netcommon.next_nth_usable(4) }}"
# nname_3_ipv6_2: "{{ subnet_ipv6 | ansible.netcommon.next_nth_usable(2) }}"
# nname_3_ipv6_3: "{{ subnet_ipv6 | ansible.netcommon.next_nth_usable(3) }}"
# nname_3_ipv6_4: "{{ subnet_ipv6 | ansible.netcommon.next_nth_usable(4) }}"
- debug:
msg: "Chose random IPv4 subnet {{ subnet_ipv4 }} and random IPv6 subnet {{ subnet_ipv6 }}"
- name: Create network with fixed IPv4 and IPv6 subnets
docker_network:
name: "{{ nname_3 }}"
enable_ipv6: true
ipam_config:
- subnet: "{{ subnet_ipv4 }}"
- subnet: "{{ subnet_ipv6 }}"
state: present
####################################################################
## network_mode ####################################################
####################################################################
- name: network_mode
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
network_mode: host
register: network_mode_1
- name: network_mode (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
network_mode: host
register: network_mode_2
- name: network_mode (change)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
network_mode: none
force_kill: true
register: network_mode_3
- name: network_mode (container mode setup)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname_h1 }}"
state: started
register: cname_h1_id
- name: network_mode (container mode)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
network_mode: "container:{{ cname_h1_id.container.Id }}"
force_kill: true
register: network_mode_4
- name: network_mode (container mode idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
network_mode: "container:{{ cname_h1 }}"
register: network_mode_5
- name: cleanup
docker_container:
name: "{{ container_name }}"
state: absent
force_kill: true
loop:
- "{{ cname }}"
- "{{ cname_h1 }}"
loop_control:
loop_var: container_name
diff: false
- assert:
that:
- network_mode_1 is changed
- network_mode_1.container.HostConfig.NetworkMode == 'host'
- network_mode_2 is not changed
- network_mode_2.container.HostConfig.NetworkMode == 'host'
- network_mode_3 is changed
- network_mode_3.container.HostConfig.NetworkMode == 'none'
- network_mode_4 is changed
- network_mode_4.container.HostConfig.NetworkMode == ('container:' ~ cname_h1_id.container.Id)
- network_mode_5 is not changed
- network_mode_5.container.HostConfig.NetworkMode == ('container:' ~ cname_h1_id.container.Id)
####################################################################
## networks, purge_networks for networks_cli_compatible=no #########
####################################################################
- name: networks_cli_compatible=no, networks w/o purge_networks
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_1 }}"
- name: "{{ nname_2 }}"
networks_cli_compatible: false
register: networks_1
- name: networks_cli_compatible=no, networks w/o purge_networks
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_1 }}"
- name: "{{ nname_2 }}"
networks_cli_compatible: false
register: networks_2
- name: networks_cli_compatible=no, networks, purge_networks
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
comparisons:
networks: strict
networks:
- name: bridge
- name: "{{ nname_1 }}"
networks_cli_compatible: false
force_kill: true
register: networks_3
- name: networks_cli_compatible=no, networks, purge_networks (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
comparisons:
networks: strict
networks:
- name: "{{ nname_1 }}"
- name: bridge
networks_cli_compatible: false
register: networks_4
- name: networks_cli_compatible=no, networks (less networks)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: bridge
networks_cli_compatible: false
register: networks_5
- name: networks_cli_compatible=no, networks, purge_networks (less networks)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
comparisons:
networks: strict
networks:
- name: bridge
networks_cli_compatible: false
force_kill: true
register: networks_6
- name: networks_cli_compatible=no, networks, purge_networks (more networks)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
comparisons:
networks: strict
networks:
- name: bridge
- name: "{{ nname_2 }}"
networks_cli_compatible: false
force_kill: true
register: networks_7
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
# networks_1 has networks default, 'bridge', nname_1
- networks_1 is changed
- networks_1.container.NetworkSettings.Networks | length == 3
- nname_1 in networks_1.container.NetworkSettings.Networks
- nname_2 in networks_1.container.NetworkSettings.Networks
- "'default' in networks_1.container.NetworkSettings.Networks or 'bridge' in networks_1.container.NetworkSettings.Networks"
# networks_2 has networks default, 'bridge', nname_1
- networks_2 is not changed
- networks_2.container.NetworkSettings.Networks | length == 3
- nname_1 in networks_2.container.NetworkSettings.Networks
- nname_2 in networks_1.container.NetworkSettings.Networks
- "'default' in networks_1.container.NetworkSettings.Networks or 'bridge' in networks_1.container.NetworkSettings.Networks"
# networks_3 has networks 'bridge', nname_1
- networks_3 is changed
- networks_3.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_3.container.NetworkSettings.Networks
- "'default' in networks_3.container.NetworkSettings.Networks or 'bridge' in networks_3.container.NetworkSettings.Networks"
# networks_4 has networks 'bridge', nname_1
- networks_4 is not changed
- networks_4.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_4.container.NetworkSettings.Networks
- "'default' in networks_4.container.NetworkSettings.Networks or 'bridge' in networks_4.container.NetworkSettings.Networks"
# networks_5 has networks 'bridge', nname_1
- networks_5 is not changed
- networks_5.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_5.container.NetworkSettings.Networks
- "'default' in networks_5.container.NetworkSettings.Networks or 'bridge' in networks_5.container.NetworkSettings.Networks"
# networks_6 has networks 'bridge'
- networks_6 is changed
- networks_6.container.NetworkSettings.Networks | length == 1
- "'default' in networks_6.container.NetworkSettings.Networks or 'bridge' in networks_6.container.NetworkSettings.Networks"
# networks_7 has networks 'bridge', nname_2
- networks_7 is changed
- networks_7.container.NetworkSettings.Networks | length == 2
- nname_2 in networks_7.container.NetworkSettings.Networks
- "'default' in networks_7.container.NetworkSettings.Networks or 'bridge' in networks_7.container.NetworkSettings.Networks"
####################################################################
## networks for networks_cli_compatible=yes ########################
####################################################################
- name: networks_cli_compatible=yes, networks specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_1 }}"
aliases:
- alias1
- alias2
- name: "{{ nname_2 }}"
networks_cli_compatible: true
register: networks_1
- name: networks_cli_compatible=yes, networks specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_1 }}"
- name: "{{ nname_2 }}"
networks_cli_compatible: true
register: networks_2
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- name: networks_cli_compatible=yes, empty networks list specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
register: networks_3
- name: networks_cli_compatible=yes, empty networks list specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
register: networks_4
- name: networks_cli_compatible=yes, empty networks list specified, purge_networks
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
comparisons:
networks: strict
force_kill: true
register: networks_5
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- name: networks_cli_compatible=yes, networks not specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks_cli_compatible: true
force_kill: true
register: networks_6
- name: networks_cli_compatible=yes, networks not specified
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks_cli_compatible: true
register: networks_7
- name: networks_cli_compatible=yes, networks empty, purge_networks
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks_cli_compatible: true
comparisons:
networks: strict
networks: []
force_kill: true
register: networks_8
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- debug: var=networks_3
- assert:
that:
# networks_1 has networks nname_1, nname_2
- networks_1 is changed
- networks_1.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_1.container.NetworkSettings.Networks
- nname_2 in networks_1.container.NetworkSettings.Networks
# networks_2 has networks nname_1, nname_2
- networks_2 is not changed
- networks_2.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_2.container.NetworkSettings.Networks
- nname_2 in networks_1.container.NetworkSettings.Networks
# networks_3 has networks 'bridge'
- networks_3 is changed
- networks_3.container.NetworkSettings.Networks | length == 1
- "'default' in networks_3.container.NetworkSettings.Networks or 'bridge' in networks_3.container.NetworkSettings.Networks"
# networks_4 has networks 'bridge'
- networks_4 is not changed
- networks_4.container.NetworkSettings.Networks | length == 1
- "'default' in networks_4.container.NetworkSettings.Networks or 'bridge' in networks_4.container.NetworkSettings.Networks"
# networks_5 has no networks
- networks_5 is changed
- networks_5.container.NetworkSettings.Networks | length == 0
# networks_6 has networks 'bridge'
- networks_6 is changed
- networks_6.container.NetworkSettings.Networks | length == 1
- "'default' in networks_6.container.NetworkSettings.Networks or 'bridge' in networks_6.container.NetworkSettings.Networks"
# networks_7 has networks 'bridge'
- networks_7 is not changed
- networks_7.container.NetworkSettings.Networks | length == 1
- "'default' in networks_7.container.NetworkSettings.Networks or 'bridge' in networks_7.container.NetworkSettings.Networks"
# networks_8 has no networks
- networks_8 is changed
- networks_8.container.NetworkSettings.Networks | length == 0
####################################################################
## networks with comparisons #######################################
####################################################################
- name: create container with one network
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_1 }}"
networks_cli_compatible: true
register: networks_1
- name: different networks, comparisons=ignore
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_2 }}"
networks_cli_compatible: true
comparisons:
network_mode: ignore # otherwise we'd have to set network_mode to nname_1
networks: ignore
register: networks_2
- name: less networks, comparisons=ignore
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
comparisons:
networks: ignore
register: networks_3
- name: less networks, comparisons=allow_more_present
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
comparisons:
networks: allow_more_present
register: networks_4
- name: different networks, comparisons=allow_more_present
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_2 }}"
networks_cli_compatible: true
comparisons:
network_mode: ignore # otherwise we'd have to set network_mode to nname_1
networks: allow_more_present
force_kill: true
register: networks_5
- name: different networks, comparisons=strict
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_2 }}"
networks_cli_compatible: true
comparisons:
networks: strict
force_kill: true
register: networks_6
- name: less networks, comparisons=strict
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks: []
networks_cli_compatible: true
comparisons:
networks: strict
force_kill: true
register: networks_7
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
# networks_1 has networks nname_1
- networks_1 is changed
- networks_1.container.NetworkSettings.Networks | length == 1
- nname_1 in networks_1.container.NetworkSettings.Networks
# networks_2 has networks nname_1
- networks_2 is not changed
- networks_2.container.NetworkSettings.Networks | length == 1
- nname_1 in networks_2.container.NetworkSettings.Networks
# networks_3 has networks nname_1
- networks_3 is not changed
- networks_3.container.NetworkSettings.Networks | length == 1
- nname_1 in networks_3.container.NetworkSettings.Networks
# networks_4 has networks nname_1
- networks_4 is not changed
- networks_4.container.NetworkSettings.Networks | length == 1
- nname_1 in networks_4.container.NetworkSettings.Networks
# networks_5 has networks nname_1, nname_2
- networks_5 is changed
- networks_5.container.NetworkSettings.Networks | length == 2
- nname_1 in networks_5.container.NetworkSettings.Networks
- nname_2 in networks_5.container.NetworkSettings.Networks
# networks_6 has networks nname_2
- networks_6 is changed
- networks_6.container.NetworkSettings.Networks | length == 1
- nname_2 in networks_6.container.NetworkSettings.Networks
# networks_7 has no networks
- networks_7 is changed
- networks_7.container.NetworkSettings.Networks | length == 0
####################################################################
## networks with IP address ########################################
####################################################################
- name: create container (stopped) with one network and fixed IP
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_2 }}"
ipv6_address: "{{ nname_3_ipv6_2 }}"
networks_cli_compatible: true
register: networks_1
- name: create container (stopped) with one network and fixed IP (idempotent)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_2 }}"
ipv6_address: "{{ nname_3_ipv6_2 }}"
networks_cli_compatible: true
register: networks_2
- name: create container (stopped) with one network and fixed IP (different IPv4)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_3 }}"
ipv6_address: "{{ nname_3_ipv6_2 }}"
networks_cli_compatible: true
register: networks_3
- name: create container (stopped) with one network and fixed IP (different IPv6)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: stopped
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_3 }}"
ipv6_address: "{{ nname_3_ipv6_3 }}"
networks_cli_compatible: true
register: networks_4
- name: create container (started) with one network and fixed IP
docker_container:
name: "{{ cname }}"
state: started
register: networks_5
- name: create container (started) with one network and fixed IP (different IPv4)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_4 }}"
ipv6_address: "{{ nname_3_ipv6_3 }}"
networks_cli_compatible: true
force_kill: true
register: networks_6
- name: create container (started) with one network and fixed IP (different IPv6)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_4 }}"
ipv6_address: "{{ nname_3_ipv6_4 }}"
networks_cli_compatible: true
force_kill: true
register: networks_7
- name: create container (started) with one network and fixed IP (idempotent)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
networks:
- name: "{{ nname_3 }}"
ipv4_address: "{{ nname_3_ipv4_4 }}"
ipv6_address: "{{ nname_3_ipv6_4 }}"
networks_cli_compatible: true
register: networks_8
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- networks_1 is changed
- networks_1.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_2
- networks_1.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_2 | normalize_ipaddr
- networks_1.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_1.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address == ""
- networks_2 is not changed
- networks_2.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_2
- networks_2.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_2 | normalize_ipaddr
- networks_2.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_2.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address == ""
- networks_3 is changed
- networks_3.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_3
- networks_3.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_2 | normalize_ipaddr
- networks_3.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_3.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address == ""
- networks_4 is changed
- networks_4.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_3
- networks_4.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_3 | normalize_ipaddr
- networks_4.container.NetworkSettings.Networks[nname_3].IPAddress == ""
- networks_4.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address == ""
- networks_5 is changed
- networks_5.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_3
- networks_5.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_3 | normalize_ipaddr
- networks_5.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_3
- networks_5.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address | normalize_ipaddr == nname_3_ipv6_3 | normalize_ipaddr
- networks_6 is changed
- networks_6.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_4
- networks_6.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_3 | normalize_ipaddr
- networks_6.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_4
- networks_6.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address | normalize_ipaddr == nname_3_ipv6_3 | normalize_ipaddr
- networks_7 is changed
- networks_7.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_4
- networks_7.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_4 | normalize_ipaddr
- networks_7.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_4
- networks_7.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address | normalize_ipaddr == nname_3_ipv6_4 | normalize_ipaddr
- networks_8 is not changed
- networks_8.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv4Address == nname_3_ipv4_4
- networks_8.container.NetworkSettings.Networks[nname_3].IPAMConfig.IPv6Address | normalize_ipaddr == nname_3_ipv6_4 | normalize_ipaddr
- networks_8.container.NetworkSettings.Networks[nname_3].IPAddress == nname_3_ipv4_4
- networks_8.container.NetworkSettings.Networks[nname_3].GlobalIPv6Address | normalize_ipaddr == nname_3_ipv6_4 | normalize_ipaddr
####################################################################
####################################################################
####################################################################
- name: Delete networks
docker_network:
name: "{{ network_name }}"
state: absent
force: true
loop:
- "{{ nname_1 }}"
- "{{ nname_2 }}"
- "{{ nname_3 }}"
loop_control:
loop_var: network_name

View File

@ -0,0 +1,328 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-options' }}"
cname2: "{{ cname_prefix ~ '-options-h1' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname, cname2] }}"
####################################################################
## published_ports: error handling #################################
####################################################################
- name: published_ports -- non-closing square bracket
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "[::1:2000:3000"
register: published_ports_1
ignore_errors: true
- name: published_ports -- forgot square brackets for IPv6
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "::1:2000:3000"
register: published_ports_2
ignore_errors: true
- name: published_ports -- disallow hostnames
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "foo:2000:3000"
register: published_ports_3
ignore_errors: true
- assert:
that:
- published_ports_1 is failed
- published_ports_1.msg == 'Cannot find closing "]" in input "[::1:2000:3000" for opening "[" at index 1!'
- published_ports_2 is failed
- published_ports_2.msg == 'Invalid port description "::1:2000:3000" - expected 1 to 3 colon-separated parts, but got 5. Maybe you forgot to use square brackets ([...]) around an IPv6 address?'
- published_ports_3 is failed
- "published_ports_3.msg == 'Bind addresses for published ports must be IPv4 or IPv6 addresses, not hostnames. Use the dig lookup to resolve hostnames. (Found hostname: foo)'"
####################################################################
## published_ports: port range #####################################
####################################################################
- name: published_ports -- port range
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
exposed_ports:
- "9001"
- "9010-9050"
published_ports:
- "9001:9001"
- "9010-9050:9010-9050"
force_kill: true
register: published_ports_1
- name: published_ports -- port range (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
exposed_ports:
- "9001"
- "9010-9050"
published_ports:
- "9001:9001"
- "9010-9050:9010-9050"
force_kill: true
register: published_ports_2
- name: published_ports -- port range (different range)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
exposed_ports:
- "9001"
- "9010-9050"
published_ports:
- "9001:9001"
- "9020-9060:9020-9060"
force_kill: true
register: published_ports_3
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- published_ports_1 is changed
- published_ports_2 is not changed
- published_ports_3 is changed
####################################################################
## published_ports: one-element container port range ###############
####################################################################
- name: published_ports -- one-element container port range
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ item }}"
state: started
published_ports:
- "9010-9050:9010"
force_kill: true
loop:
- '{{ cname }}'
- '{{ cname2 }}'
register: published_ports_1
- name: published_ports -- one-element container port range (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ item }}"
state: started
published_ports:
- "9010-9050:9010"
force_kill: true
loop:
- '{{ cname }}'
- '{{ cname2 }}'
register: published_ports_2
- name: published_ports -- one-element container port range (different range)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ item }}"
state: started
published_ports:
- "9010-9051:9010"
force_kill: true
loop:
- '{{ cname }}'
- '{{ cname2 }}'
register: published_ports_3
- name: cleanup
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
loop:
- '{{ cname }}'
- '{{ cname2 }}'
diff: false
- assert:
that:
- published_ports_1 is changed
- published_ports_2 is not changed
- published_ports_3 is changed
####################################################################
## published_ports: IPv6 addresses #################################
####################################################################
- when: docker_host_info.host_info.ServerVersion is version('27.0.0', '<')
block:
- name: published_ports -- IPv6
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "[::1]:9001:9001"
force_kill: true
register: published_ports_1
- name: published_ports -- IPv6 (idempotency)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "[::1]:9001:9001"
force_kill: true
register: published_ports_2
- name: published_ports -- IPv6 (different IP)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "127.0.0.1:9001:9001"
force_kill: true
register: published_ports_3
- name: published_ports -- IPv6 (hostname)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
published_ports:
- "localhost:9001:9001"
force_kill: true
register: published_ports_4
ignore_errors: true
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- published_ports_1 is changed
- published_ports_2 is not changed
- published_ports_3 is changed
- published_ports_4 is failed
####################################################################
## publish_all_ports ###############################################
####################################################################
- set_fact:
publish_all_ports_test_cases:
- test_name: no_options
changed: true
- test_name: null_to_true
publish_all_ports_value: true
changed: true
- test_name: true_idempotency
publish_all_ports_value: true
changed: false
- test_name: true_to_null
changed: false
- test_name: null_to_true_2
publish_all_ports_value: true
changed: false
- test_name: true_to_false
publish_all_ports_value: false
changed: true
- test_name: false_idempotency
publish_all_ports_value: false
changed: false
- test_name: false_to_null
changed: false
- test_name: null_with_published_ports
published_ports_value: &ports
- "9001:9001"
- "9010-9050:9010-9050"
changed: true
- test_name: null_to_true_with_published_ports
publish_all_ports_value: true
published_ports_value: *ports
changed: true
- test_name: true_idempotency_with_published_ports
publish_all_ports_value: true
published_ports_value: *ports
changed: false
- test_name: true_to_null_with_published_ports
published_ports_value: *ports
changed: false
- test_name: null_to_true_2_with_published_ports
publish_all_ports_value: true
published_ports_value: *ports
changed: false
- test_name: true_to_false_with_published_ports
publish_all_ports_value: false
published_ports_value: *ports
changed: true
- test_name: false_idempotency_with_published_ports
publish_all_ports_value: false
published_ports_value: *ports
changed: false
- test_name: false_to_null_with_published_ports
published_ports_value: *ports
changed: false
- name: publish_all_ports ({{ test_case.test_name }})
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
publish_all_ports: "{{ test_case.publish_all_ports_value | default(omit) }}"
published_ports: "{{ test_case.published_ports_value | default(omit) }}"
force_kill: true
register: publish_all_ports
loop_control:
loop_var: test_case
loop: "{{ publish_all_ports_test_cases }}"
- assert:
that:
- publish_all_ports.results[index].changed == test_case.changed
loop: "{{ publish_all_ports_test_cases }}"
loop_control:
index_var: index
loop_var: test_case

View File

@ -0,0 +1,38 @@
---
# 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
# Regression test for https://github.com/ansible/ansible/pull/45700
- name: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-45700' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
- name: Start container
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
- name: Stop container with a lot of invalid options
docker_container:
name: "{{ cname }}"
force_kill: true
# Some options with "invalid" values, which would
# have to be parsed. The values are "invalid" because
# the containers and networks listed here do not exist.
# This can happen because the networks are removed
# before the container is stopped (see
# https://github.com/ansible/ansible/issues/45486).
networks:
- name: "nonexistant-network-{{ (2**32) | random }}"
published_ports:
- '1:2'
- '3'
links:
- "nonexistant-container-{{ (2**32) | random }}:test"
state: absent

View File

@ -0,0 +1,459 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-hi' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
####################################################################
## Creation ########################################################
####################################################################
- name: Create container (check)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
check_mode: true
register: create_1
- name: Create container
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
register: create_2
- name: Create container (idempotent)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
register: create_3
- name: Create container (idempotent check)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
check_mode: true
register: create_4
- assert:
that:
- create_1 is changed
- create_2 is changed
- create_3 is not changed
- create_4 is not changed
####################################################################
## Starting (after creation) #######################################
####################################################################
- name: Start container (check)
docker_container:
name: "{{ cname }}"
state: started
check_mode: true
register: start_1
- name: Start container
docker_container:
name: "{{ cname }}"
state: started
register: start_2
- name: Start container (idempotent)
docker_container:
name: "{{ cname }}"
state: started
register: start_3
- name: Start container (idempotent check)
docker_container:
name: "{{ cname }}"
state: started
check_mode: true
register: start_4
- assert:
that:
- start_1 is changed
- start_2 is changed
- start_3 is not changed
- start_4 is not changed
####################################################################
## Present check for running container #############################
####################################################################
- name: Present check for running container (check)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
check_mode: true
register: present_check_1
- name: Present check for running container
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
register: present_check_2
- assert:
that:
- present_check_1 is not changed
- present_check_2 is not changed
####################################################################
## Starting (from scratch) #########################################
####################################################################
- name: Remove container (setup for starting from scratch)
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
- name: Start container from scratch (check)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_timeout: 1
name: "{{ cname }}"
state: started
check_mode: true
register: start_scratch_1
- name: Start container from scratch
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_timeout: 1
name: "{{ cname }}"
state: started
register: start_scratch_2
- name: Start container from scratch (idempotent)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_timeout: 1
name: "{{ cname }}"
state: started
register: start_scratch_3
- name: Start container from scratch (idempotent check)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
stop_timeout: 1
name: "{{ cname }}"
state: started
check_mode: true
register: start_scratch_4
- assert:
that:
- start_scratch_1 is changed
- start_scratch_2 is changed
- start_scratch_3 is not changed
- start_scratch_4 is not changed
####################################################################
## Recreating ######################################################
####################################################################
- name: Recreating container (created)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: present
force_kill: true
register: recreate_1
- name: Recreating container (created, recreate, check mode)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
recreate: true
state: present
force_kill: true
register: recreate_2
check_mode: true
- name: Recreating container (created, recreate)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
recreate: true
state: present
force_kill: true
register: recreate_3
- name: Recreating container (started)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
force_kill: true
register: recreate_4
- name: Recreating container (started, recreate, check mode)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
recreate: true
removal_wait_timeout: 10
state: started
force_kill: true
register: recreate_5
check_mode: true
- name: Recreating container (started, recreate)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
recreate: true
removal_wait_timeout: 10
state: started
force_kill: true
register: recreate_6
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- debug: var=recreate_1
- debug: var=recreate_3
- debug: var=recreate_4
- debug: var=recreate_6
- assert:
that:
- recreate_2 is changed
- recreate_3 is changed
- recreate_4 is changed
- recreate_5 is changed
- recreate_6 is changed
- recreate_1.container.Id == recreate_2.container.Id
- recreate_1.container.Id != recreate_3.container.Id
- recreate_3.container.Id == recreate_4.container.Id
- recreate_4.container.Id == recreate_5.container.Id
- recreate_4.container.Id != recreate_6.container.Id
####################################################################
## Restarting ######################################################
####################################################################
- name: Restarting
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
stop_timeout: 1
volumes:
- /tmp/tmp
register: restart_1
- name: Restarting (restart, check mode)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
restart: true
state: started
stop_timeout: 1
force_kill: true
register: restart_2
check_mode: true
- name: Restarting (restart)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
restart: true
state: started
stop_timeout: 1
force_kill: true
register: restart_3
- name: Restarting (verify volumes)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
stop_timeout: 1
volumes:
- /tmp/tmp
register: restart_4
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- assert:
that:
- restart_1 is changed
- restart_2 is changed
- restart_3 is changed
- restart_1.container.Id == restart_3.container.Id
- restart_4 is not changed
####################################################################
## Stopping ########################################################
####################################################################
- name: Stop container (check)
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: stopped
stop_timeout: 1
check_mode: true
register: stop_1
- name: Stop container
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: stopped
stop_timeout: 1
register: stop_2
- name: Stop container (idempotent)
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: stopped
stop_timeout: 1
register: stop_3
- name: Stop container (idempotent check)
docker_container:
image: "{{ docker_test_image_alpine }}"
name: "{{ cname }}"
state: stopped
stop_timeout: 1
check_mode: true
register: stop_4
- assert:
that:
- stop_1 is changed
- stop_2 is changed
- stop_3 is not changed
- stop_4 is not changed
####################################################################
## Removing ########################################################
####################################################################
- name: Remove container (check)
docker_container:
name: "{{ cname }}"
state: absent
check_mode: true
register: remove_1
- name: Remove container
docker_container:
name: "{{ cname }}"
state: absent
register: remove_2
- name: Remove container (idempotent)
docker_container:
name: "{{ cname }}"
state: absent
register: remove_3
- name: Remove container (idempotent check)
docker_container:
name: "{{ cname }}"
state: absent
check_mode: true
register: remove_4
- assert:
that:
- remove_1 is changed
- remove_2 is changed
- remove_3 is not changed
- remove_4 is not changed
####################################################################
## Removing (from running) #########################################
####################################################################
- name: Start container (setup for removing from running)
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
- name: Remove container from running (check)
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
check_mode: true
register: remove_from_running_1
- name: Remove container from running
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
register: remove_from_running_2
- name: Remove container from running (idempotent)
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
register: remove_from_running_3
- name: Remove container from running (idempotent check)
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
check_mode: true
register: remove_from_running_4
- assert:
that:
- remove_from_running_1 is changed
- remove_from_running_2 is changed
- remove_from_running_3 is not changed
- remove_from_running_4 is not changed

View File

@ -0,0 +1,221 @@
---
# 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: Registering container name
set_fact:
cname: "{{ cname_prefix ~ '-update' }}"
- name: Registering container name
set_fact:
cnames: "{{ cnames + [cname] }}"
# We do not test cpuset_cpus and cpuset_mems since changing it fails if the system does
# not have 'enough' CPUs. We do not test kernel_memory since it is deprecated and fails.
- set_fact:
has_blkio_weight: true
- name: Create container
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
blkio_weight: "{{ 123 if has_blkio_weight else omit }}"
cpu_period: 90000
cpu_quota: 150000
cpu_shares: 900
memory: 64M
memory_reservation: 64M
memory_swap: 64M
restart_policy: on-failure
restart_retries: 5
register: create
ignore_errors: true
- when: create is failed
block:
- name: Make sure container is not there
docker_container:
name: "{{ cname }}"
state: absent
- when: "'setting cgroup config for procHooks process caused: failed to write' in create.msg and 'io.bfq.weight' in create.msg"
set_fact:
has_blkio_weight: false
- name: Create container again
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
blkio_weight: "{{ 123 if has_blkio_weight else omit }}"
cpu_period: 90000
cpu_quota: 150000
cpu_shares: 900
memory: 64M
memory_reservation: 64M
memory_swap: 64M
restart_policy: on-failure
restart_retries: 5
register: create_2
- when: "'setting cgroup config for procHooks process caused: failed to write' in create.msg and 'io.bfq.weight' in create.msg"
set_fact:
create: "{{ create_2 }}"
- name: Update values
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
blkio_weight: "{{ 234 if has_blkio_weight else omit }}"
cpu_period: 50000
cpu_quota: 50000
cpu_shares: 1100
memory: 48M
memory_reservation: 48M
memory_swap: unlimited
restart_policy: on-failure # only on-failure can have restart_retries, so don't change it here
restart_retries: 2
register: update
diff: true
- name: Update values again
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
name: "{{ cname }}"
state: started
blkio_weight: "{{ 135 if has_blkio_weight else omit }}"
cpu_period: 30000
cpu_quota: 40000
cpu_shares: 1000
memory: 32M
memory_reservation: 30M
memory_swap: 128M
restart_policy: always
restart_retries: 0
register: update2
diff: true
- name: Recreate container
docker_container:
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 20m"' # this will force re-creation
name: "{{ cname }}"
state: started
blkio_weight: "{{ 234 if has_blkio_weight else omit }}"
cpu_period: 50000
cpu_quota: 50000
cpu_shares: 1100
memory: 48M
memory_reservation: 48M
memory_swap: unlimited
restart_policy: on-failure
restart_retries: 2
force_kill: true
register: recreate
diff: true
- name: cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
diff: false
- name: Check general things
assert:
that:
- create is changed
- update is changed
- update2 is changed
- recreate is changed
# Make sure the container was *not* recreated when it should not be
- create.container.Id == update.container.Id
- create.container.Id == update2.container.Id
# Make sure that the container was recreated when it should be
- create.container.Id != recreate.container.Id
- name: Check diff for first update
assert:
that:
# blkio_weight sometimes cannot be set, then we end up with 0 instead of the value we had
- >-
not has_blkio_weight or update.diff.before.blkio_weight == 123 or ('Docker warning: Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.' in (create.warnings | default([])))
- not has_blkio_weight or update.diff.after.blkio_weight == 234
- update.diff.before.cpu_period == 90000
- update.diff.after.cpu_period == 50000
- update.diff.before.cpu_quota == 150000
- update.diff.after.cpu_quota == 50000
- update.diff.before.cpu_shares == 900
- update.diff.after.cpu_shares == 1100
- update.diff.before.memory == 67108864
- update.diff.after.memory == 50331648
- update.diff.before.memory_reservation == 67108864
- update.diff.after.memory_reservation == 50331648
- >-
(update.diff.before.memory_swap | default(0)) == 67108864 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- >-
(update.diff.after.memory_swap | default(0)) == -1 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- "'restart_policy' not in update.diff.before"
- update.diff.before.restart_retries == 5
- update.diff.after.restart_retries == 2
- name: Check diff for second update
assert:
that:
- >-
not has_blkio_weight or update2.diff.before.blkio_weight == 234 or ('Docker warning: Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.' in (create.warnings | default([])))
- not has_blkio_weight or update2.diff.after.blkio_weight == 135
- update2.diff.before.cpu_period == 50000
- update2.diff.after.cpu_period == 30000
- update2.diff.before.cpu_quota == 50000
- update2.diff.after.cpu_quota == 40000
- update2.diff.before.cpu_shares == 1100
- update2.diff.after.cpu_shares == 1000
- update2.diff.before.memory == 50331648
- update2.diff.after.memory == 33554432
- update2.diff.before.memory_reservation == 50331648
- update2.diff.after.memory_reservation == 31457280
- >-
(update2.diff.before.memory_swap | default(0)) == -1 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- >-
(update2.diff.after.memory_swap | default(0)) == 134217728 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- update2.diff.before.restart_policy == 'on-failure'
- update2.diff.after.restart_policy == 'always'
- update2.diff.before.restart_retries == 2
- update2.diff.after.restart_retries == 0
- name: Check diff for recreation
assert:
that:
- >-
not has_blkio_weight or recreate.diff.before.blkio_weight == 135 or ('Docker warning: Your kernel does not support Block I/O weight or the cgroup is not mounted. Weight discarded.' in (create.warnings | default([])))
- not has_blkio_weight or recreate.diff.after.blkio_weight == 234
- recreate.diff.before.cpu_period == 30000
- recreate.diff.after.cpu_period == 50000
- recreate.diff.before.cpu_quota == 40000
- recreate.diff.after.cpu_quota == 50000
- recreate.diff.before.cpu_shares == 1000
- recreate.diff.after.cpu_shares == 1100
- recreate.diff.before.memory == 33554432
- recreate.diff.after.memory == 50331648
- recreate.diff.before.memory_reservation == 31457280
- recreate.diff.after.memory_reservation == 50331648
- >-
(recreate.diff.before.memory_swap | default(0)) == 134217728 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- >-
(recreate.diff.after.memory_swap | default(0)) == -1 or ('Docker warning: Your kernel does not support swap limit capabilities or the cgroup is not mounted. Memory limited without swap.' in (create.warnings | default([])))
- recreate.diff.before.restart_policy == 'always'
- recreate.diff.after.restart_policy == 'on-failure'
- recreate.diff.before.restart_retries == 0
- recreate.diff.after.restart_retries == 2
- recreate.diff.before.command == ['/bin/sh', '-c', 'sleep 10m']
- recreate.diff.after.command == ['/bin/sh', '-c', 'sleep 20m']

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/4
destructive

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
dependencies:
- setup_docker
- setup_docker_python_deps
- setup_remote_tmp_dir

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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- name: Gather facts on controller
setup:
gather_subset: '!all'
delegate_to: localhost
delegate_facts: true
run_once: true
# Create random name prefix (for containers)
- name: Create random container name prefix
set_fact:
cname_prefix: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
cnames: []
- debug:
msg: "Using container name prefix {{ cname_prefix }}"
# Run the tests
- block:
- include_tasks: run-test.yml
with_fileglob:
- "tests/*.yml"
loop_control:
loop_var: test_name
always:
- name: "Make sure all containers are removed"
docker_container:
name: "{{ item }}"
state: absent
force_kill: true
with_items: "{{ cnames }}"
diff: false
when: docker_api_version is version('1.25', '>=')
- fail: msg="Too old Docker API version to run all docker_container_copy_into tests!"
when: not(docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

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
- name: "Loading tasks from {{ test_name }}"
include_tasks: "{{ test_name }}"

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/4
destructive

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_docker
- setup_docker_python_deps

View File

@ -0,0 +1,228 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- block:
- name: Create random container name
set_fact:
cname: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
- name: Make sure container is not there
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
- name: Execute in a non-present container
docker_container_exec:
container: "{{ cname }}"
command: "/bin/bash -c 'ls -a'"
register: result
ignore_errors: true
- assert:
that:
- result is failed
- "'Could not find container' in result.msg"
- name: Make sure container exists
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
force_kill: true
- name: Execute in a present container (command)
docker_container_exec:
container: "{{ cname }}"
command: "/bin/sh -c 'ls -a'"
register: result_cmd
- assert:
that:
- result_cmd.rc == 0
- "'stdout' in result_cmd"
- "'stdout_lines' in result_cmd"
- "'stderr' in result_cmd"
- "'stderr_lines' in result_cmd"
- name: Execute in a present container (argv)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- ls -a
register: result_argv
- assert:
that:
- result_argv.rc == 0
- "'stdout' in result_argv"
- "'stdout_lines' in result_argv"
- "'stderr' in result_argv"
- "'stderr_lines' in result_argv"
- result_cmd.stdout == result_argv.stdout
- name: Execute in a present container (cat without stdin)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- cat
register: result
- assert:
that:
- result.rc == 0
- result.stdout == ''
- result.stdout_lines == []
- result.stderr == ''
- result.stderr_lines == []
- name: Execute in a present container (cat with stdin)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- cat
stdin: Hello world!
strip_empty_ends: false
register: result
- assert:
that:
- result.rc == 0
- result.stdout == 'Hello world!\n'
- result.stdout_lines == ['Hello world!']
- result.stderr == ''
- result.stderr_lines == []
- name: Execute in a present container (cat with stdin, no newline)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- cat
stdin: Hello world!
stdin_add_newline: false
strip_empty_ends: false
register: result
- assert:
that:
- result.rc == 0
- result.stdout == 'Hello world!'
- result.stdout_lines == ['Hello world!']
- result.stderr == ''
- result.stderr_lines == []
- name: Execute in a present container (cat with stdin, newline but stripping)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- cat
stdin: Hello world!
stdin_add_newline: true
strip_empty_ends: true
register: result
- assert:
that:
- result.rc == 0
- result.stdout == 'Hello world!'
- result.stdout_lines == ['Hello world!']
- result.stderr == ''
- result.stderr_lines == []
- name: Prepare long string
set_fact:
very_long_string: "{{ 'something long ' * 10000 }}"
very_long_string2: "{{ 'something else ' * 5000 }}"
- name: Execute in a present container (long stdin)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- cat
stdin: |-
{{ very_long_string }}
{{ very_long_string2 }}
register: result
- assert:
that:
- result is changed
- result.rc == 0
- result.stdout == (very_long_string ~ '\n' ~ very_long_string2)
- result.stdout_lines == [very_long_string, very_long_string2]
- result.stderr == ''
- result.stderr_lines == []
- "'exec_id' not in result"
- name: Execute in a present container (detached)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- sleep 1m
detach: true
register: result
- debug: var=result
- assert:
that:
- result is changed
- "'rc' not in result"
- "'stdout' not in result"
- "'stderr' not in result"
- result.exec_id is string
- name: Execute in a present container (environment variable)
docker_container_exec:
container: "{{ cname }}"
argv:
- /bin/sh
- '-c'
- 'echo "$FOO" ; echo $FOO > /dev/stderr'
env:
FOO: |-
bar
baz
register: result
- assert:
that:
- result.rc == 0
- result.stdout == 'bar\nbaz'
- result.stdout_lines == ['bar', 'baz']
- result.stderr == 'bar baz'
- result.stderr_lines == ['bar baz']
always:
- name: Cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
when: docker_api_version is version('1.25', '>=')
- fail: msg="Too old docker / docker-py version to run docker_container_exec tests!"
when: not(docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

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/5
destructive

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_docker
- setup_docker_python_deps

View File

@ -0,0 +1,84 @@
---
# 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
####################################################################
# WARNING: These are designed specifically for Ansible tests #
# and should not be used as examples of how to write Ansible roles #
####################################################################
- block:
- name: Create random container name
set_fact:
cname: "{{ 'ansible-docker-test-%0x' % ((2**32) | random) }}"
- name: Make sure container is not there
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
- name: Inspect a non-present container
docker_container_info:
name: "{{ cname }}"
register: result
- assert:
that:
- "not result.exists"
- "'container' in result"
- "result.container is none"
- name: Make sure container exists
docker_container:
name: "{{ cname }}"
image: "{{ docker_test_image_alpine }}"
command: '/bin/sh -c "sleep 10m"'
state: started
force_kill: true
- name: Inspect a present container
docker_container_info:
name: "{{ cname }}"
register: result
- name: Dump docker_container_info result
debug: var=result
- name: "Comparison: use 'docker inspect'"
command: docker inspect "{{ cname }}"
register: docker_inspect
ignore_errors: true
- block:
- set_fact:
docker_inspect_result: "{{ docker_inspect.stdout | from_json }}"
- name: Dump docker inspect result
debug: var=docker_inspect_result
when: docker_inspect is not failed
- assert:
that:
- result.exists
- "'container' in result"
- "result.container is truthy"
- assert:
that:
- "result.container == docker_inspect_result[0]"
when: docker_inspect is not failed
- assert:
that:
- "'is too new. Maximum supported API version is' in docker_inspect.stderr"
when: docker_inspect is failed
always:
- name: Cleanup
docker_container:
name: "{{ cname }}"
state: absent
force_kill: true
when: docker_api_version is version('1.25', '>=')
- fail: msg="Too old docker / docker-py version to run docker_container_info tests!"
when: not(docker_api_version is version('1.25', '>=')) and (ansible_distribution != 'CentOS' or ansible_distribution_major_version|int > 6)

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/5
destructive

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