mirror of
https://github.com/ansible-collections/community.docker.git
synced 2025-12-18 12:52:37 +00:00
Add docker_compose_v2 module (#739)
* Add docker_compose_v2 module. * Add note on compatibility. * Parse more events. Emit warnings (or things we assume are warnings), and report unparsable messages to the user so they can report them to us.
This commit is contained in:
parent
762ce3e1cf
commit
b774837183
@ -77,7 +77,8 @@ If you use the Ansible package and do not update collections independently, use
|
||||
- community.docker.docker_volume: manage Docker volumes
|
||||
- community.docker.docker_volume_info: retrieve information on Docker volumes
|
||||
* Docker Compose:
|
||||
- community.docker.docker_compose: manage Docker Compose files
|
||||
- community.docker.docker_compose: manage Docker Compose files (legacy Docker Compose v1)
|
||||
- community.docker.docker_compose_v2: manage Docker Compose files (Docker compose CLI plugin)
|
||||
* Docker Swarm:
|
||||
- community.docker.docker_config: manage configurations
|
||||
- community.docker.docker_node: manage Docker Swarm nodes
|
||||
|
||||
@ -7,6 +7,7 @@ requires_ansible: '>=2.11.0'
|
||||
action_groups:
|
||||
docker:
|
||||
- docker_compose
|
||||
- docker_compose_v2
|
||||
- docker_config
|
||||
- docker_container
|
||||
- docker_container_copy_into
|
||||
|
||||
@ -12,12 +12,13 @@ DOCUMENTATION = '''
|
||||
|
||||
module: docker_compose
|
||||
|
||||
short_description: Manage multi-container Docker applications with Docker Compose.
|
||||
short_description: Manage multi-container Docker applications with Docker Compose V1
|
||||
|
||||
author: "Chris Houseknecht (@chouseknecht)"
|
||||
|
||||
description:
|
||||
- Uses Docker Compose to start, shutdown and scale services. B(This module requires docker-compose < 2.0.0.)
|
||||
Use the M(community.docker.docker_compose_v2) module for using the modern Docker compose CLI plugin.
|
||||
- Configuration can be read from a C(docker-compose.yml) or C(docker-compose.yaml) file or inline using the O(definition) option.
|
||||
- See the examples for more details.
|
||||
- Supports check mode.
|
||||
@ -188,6 +189,9 @@ requirements:
|
||||
- "docker-compose >= 1.7.0, < 2.0.0"
|
||||
- "Docker API >= 1.25"
|
||||
- "PyYAML >= 3.11"
|
||||
|
||||
seealso:
|
||||
- module: community.docker.docker_compose_v2
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
|
||||
785
plugins/modules/docker_compose_v2.py
Normal file
785
plugins/modules/docker_compose_v2.py
Normal file
@ -0,0 +1,785 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright (c) 2023, Felix Fontein <felix@fontein.de>
|
||||
# Copyright (c) 2023, Léo El Amri (@lel-amri)
|
||||
# Copyright 2016 Red Hat | Ansible
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
|
||||
DOCUMENTATION = '''
|
||||
|
||||
module: docker_compose_v2
|
||||
|
||||
short_description: Manage multi-container Docker applications with Docker Compose CLI plugin
|
||||
|
||||
version_added: 3.6.0
|
||||
|
||||
description:
|
||||
- Uses Docker Compose to start or shutdown services.
|
||||
|
||||
extends_documentation_fragment:
|
||||
- community.docker.docker.cli_documentation
|
||||
- community.docker.attributes
|
||||
- community.docker.attributes.actiongroup_docker
|
||||
|
||||
attributes:
|
||||
check_mode:
|
||||
support: full
|
||||
diff_mode:
|
||||
support: none
|
||||
|
||||
options:
|
||||
project_src:
|
||||
description:
|
||||
- Path to a directory containing a C(docker-compose.yml) or C(docker-compose.yaml) file.
|
||||
type: path
|
||||
required: true
|
||||
project_name:
|
||||
description:
|
||||
- Provide a project name. If not provided, the project name is taken from the basename of O(project_src).
|
||||
type: str
|
||||
env_files:
|
||||
description:
|
||||
- By default environment files are loaded from a C(.env) file located directly under the O(project_src) directory.
|
||||
- O(env_files) can be used to specify the path of one or multiple custom environment files instead.
|
||||
- The path is relative to the O(project_src) directory.
|
||||
type: list
|
||||
elements: path
|
||||
profiles:
|
||||
description:
|
||||
- List of profiles to enable when starting services.
|
||||
- Equivalent to C(docker compose --profile).
|
||||
type: list
|
||||
elements: str
|
||||
state:
|
||||
description:
|
||||
- Desired state of the project.
|
||||
- V(present) is equivalent to running C(docker compose up).
|
||||
- V(stopped) is equivalent to running C(docker compose stop).
|
||||
- V(absent) is equivalent to running C(docker compose down).
|
||||
- V(restarted) is equivalent to running C(docker compose restart).
|
||||
type: str
|
||||
default: present
|
||||
choices:
|
||||
- absent
|
||||
- stopped
|
||||
- restarted
|
||||
- present
|
||||
dependencies:
|
||||
description:
|
||||
- When O(state) is V(present) or V(restarted), specify whether or not to include linked services.
|
||||
type: bool
|
||||
default: true
|
||||
recreate:
|
||||
description:
|
||||
- By default containers will be recreated when their configuration differs from the service definition.
|
||||
- Setting to V(never) ignores configuration differences and leaves existing containers unchanged.
|
||||
- Setting to V(always) forces recreation of all existing containers.
|
||||
type: str
|
||||
default: auto
|
||||
choices:
|
||||
- always
|
||||
- never
|
||||
- auto
|
||||
remove_images:
|
||||
description:
|
||||
- Use with O(state=absent) to remove all images or only local images.
|
||||
type: str
|
||||
choices:
|
||||
- 'all'
|
||||
- 'local'
|
||||
remove_volumes:
|
||||
description:
|
||||
- Use with O(state=absent) to remove data volumes.
|
||||
type: bool
|
||||
default: false
|
||||
remove_orphans:
|
||||
description:
|
||||
- Remove containers for services not defined in the Compose file.
|
||||
type: bool
|
||||
default: false
|
||||
timeout:
|
||||
description:
|
||||
- Timeout in seconds for container shutdown when attached or when containers are already running.
|
||||
type: int
|
||||
|
||||
requirements:
|
||||
- "Docker CLI with Docker compose plugin 2.18.0 or later"
|
||||
|
||||
author:
|
||||
- Felix Fontein (@felixfontein)
|
||||
|
||||
notes:
|
||||
- |-
|
||||
The Docker compose CLI plugin has no stable output format (see for example U(https://github.com/docker/compose/issues/10872)),
|
||||
and for the main operations also no machine friendly output format. The module tries to accomodate this with various
|
||||
version-dependent behavior adjustments and with testing older and newer versions of the Docker compose CLI plugin.
|
||||
|
||||
Currently the module is tested with multiple plugin versions between 2.18.1 and 2.23.3. The exact list of plugin versions
|
||||
will change over time. New releases of the Docker compose CLI plugin can break this module at any time.
|
||||
|
||||
seealso:
|
||||
- module: community.docker.docker_compose
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
# Examples use the django example at https://docs.docker.com/compose/django. Follow it to create the
|
||||
# flask directory
|
||||
|
||||
- name: Run using a project directory
|
||||
hosts: localhost
|
||||
gather_facts: false
|
||||
tasks:
|
||||
- name: Tear down existing services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: flask
|
||||
state: absent
|
||||
|
||||
- name: Create and start services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: flask
|
||||
register: output
|
||||
|
||||
- name: Show results
|
||||
ansible.builtin.debug:
|
||||
var: output
|
||||
|
||||
- name: Run `docker-compose up` again
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: flask
|
||||
register: output
|
||||
|
||||
- name: Show results
|
||||
ansible.builtin.debug:
|
||||
var: output
|
||||
|
||||
- ansible.builtin.assert:
|
||||
that: not output.changed
|
||||
|
||||
- name: Stop all services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: flask
|
||||
state: stopped
|
||||
register: output
|
||||
|
||||
- name: Show results
|
||||
ansible.builtin.debug:
|
||||
var: output
|
||||
|
||||
- name: Verify that web and db services are not running
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "not output.services.web.flask_web_1.state.running"
|
||||
- "not output.services.db.flask_db_1.state.running"
|
||||
|
||||
- name: Restart services
|
||||
community.docker.docker_compose_v2:
|
||||
project_src: flask
|
||||
state: restarted
|
||||
register: output
|
||||
|
||||
- name: Show results
|
||||
ansible.builtin.debug:
|
||||
var: output
|
||||
|
||||
- name: Verify that web and db services are running
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- "output.services.web.flask_web_1.state.running"
|
||||
- "output.services.db.flask_db_1.state.running"
|
||||
'''
|
||||
|
||||
RETURN = '''
|
||||
containers:
|
||||
description:
|
||||
- A list of containers associated to the service.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
Command:
|
||||
description:
|
||||
- The container's command.
|
||||
type: raw
|
||||
CreatedAt:
|
||||
description:
|
||||
- The timestamp when the container was created.
|
||||
type: str
|
||||
sample: "2024-01-02 12:20:41 +0100 CET"
|
||||
ExitCode:
|
||||
description:
|
||||
- The container's exit code.
|
||||
type: int
|
||||
Health:
|
||||
description:
|
||||
- The container's health check.
|
||||
type: raw
|
||||
ID:
|
||||
description:
|
||||
- The container's ID.
|
||||
type: str
|
||||
sample: "44a7d607219a60b7db0a4817fb3205dce46e91df2cb4b78a6100b6e27b0d3135"
|
||||
Image:
|
||||
description:
|
||||
- The container's image.
|
||||
type: str
|
||||
Labels:
|
||||
description:
|
||||
- Labels for this container.
|
||||
type: dict
|
||||
LocalVolumes:
|
||||
description:
|
||||
- The local volumes count.
|
||||
type: str
|
||||
Mounts:
|
||||
description:
|
||||
- Mounts.
|
||||
type: str
|
||||
Name:
|
||||
description:
|
||||
- The container's primary name.
|
||||
type: str
|
||||
Names:
|
||||
description:
|
||||
- List of names of the container.
|
||||
type: list
|
||||
elements: str
|
||||
Networks:
|
||||
description:
|
||||
- List of networks attached to this container.
|
||||
type: list
|
||||
elements: str
|
||||
Ports:
|
||||
description:
|
||||
- List of port assignments as a string.
|
||||
type: str
|
||||
Publishers:
|
||||
description:
|
||||
- List of port assigments.
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
URL:
|
||||
description:
|
||||
- Interface the port is bound to.
|
||||
type: str
|
||||
TargetPort:
|
||||
description:
|
||||
- The container's port the published port maps to.
|
||||
type: int
|
||||
PublishedPort:
|
||||
description:
|
||||
- The port that is published.
|
||||
type: int
|
||||
Protocol:
|
||||
description:
|
||||
- The protocol.
|
||||
type: str
|
||||
choices:
|
||||
- tcp
|
||||
- udp
|
||||
RunningFor:
|
||||
description:
|
||||
- Amount of time the container runs.
|
||||
type: str
|
||||
Service:
|
||||
description:
|
||||
- The name of the service.
|
||||
type: str
|
||||
Size:
|
||||
description:
|
||||
- The container's size.
|
||||
type: str
|
||||
sample: "0B"
|
||||
State:
|
||||
description:
|
||||
- The container's state.
|
||||
type: str
|
||||
sample: running
|
||||
Status:
|
||||
description:
|
||||
- The container's status.
|
||||
type: str
|
||||
sample: Up About a minute
|
||||
images:
|
||||
description:
|
||||
- A list of images associated to the service.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
ID:
|
||||
description:
|
||||
- The image's ID.
|
||||
type: str
|
||||
sample: sha256:c8bccc0af9571ec0d006a43acb5a8d08c4ce42b6cc7194dd6eb167976f501ef1
|
||||
ContainerName:
|
||||
description:
|
||||
- Name of the conainer this image is used by.
|
||||
type: str
|
||||
Repository:
|
||||
description:
|
||||
- The repository where this image belongs to.
|
||||
type: str
|
||||
Tag:
|
||||
description:
|
||||
- The tag of the image.
|
||||
type: str
|
||||
Size:
|
||||
description:
|
||||
- The image's size in bytes.
|
||||
type: int
|
||||
actions:
|
||||
description:
|
||||
- A list of actions that have been applied.
|
||||
returned: success
|
||||
type: list
|
||||
elements: dict
|
||||
contains:
|
||||
what:
|
||||
description:
|
||||
- What kind of resource was changed.
|
||||
type: str
|
||||
sample: container
|
||||
choices:
|
||||
- network
|
||||
- image
|
||||
- volume
|
||||
- container
|
||||
id:
|
||||
description:
|
||||
- The ID of the resource that was changed.
|
||||
type: str
|
||||
sample: container
|
||||
status:
|
||||
description:
|
||||
- The status change that happened.
|
||||
type: str
|
||||
sample: Created
|
||||
choices:
|
||||
- Started
|
||||
- Exited
|
||||
- Restarted
|
||||
- Created
|
||||
- Stopped
|
||||
- Killed
|
||||
- Removed
|
||||
- Recreated
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import traceback
|
||||
from collections import namedtuple
|
||||
|
||||
from ansible.module_utils.common.text.converters import to_native
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.common_cli import (
|
||||
AnsibleModuleDockerClient,
|
||||
DockerException,
|
||||
)
|
||||
|
||||
from ansible_collections.community.docker.plugins.module_utils.util import DockerBaseClass
|
||||
from ansible_collections.community.docker.plugins.module_utils.version import LooseVersion
|
||||
|
||||
|
||||
DOCKER_COMPOSE_MINIMAL_VERSION = '2.18.0'
|
||||
DOCKER_COMPOSE_FILES = 'docker-compose.yml', 'docker-compose.yaml'
|
||||
DOCKER_STATUS_DONE = frozenset((
|
||||
'Started',
|
||||
'Healthy',
|
||||
'Exited',
|
||||
'Restarted',
|
||||
'Running',
|
||||
'Created',
|
||||
'Stopped',
|
||||
'Killed',
|
||||
'Removed',
|
||||
# An extra, specific to containers
|
||||
'Recreated',
|
||||
))
|
||||
DOCKER_STATUS_WORKING = frozenset((
|
||||
'Creating',
|
||||
'Starting',
|
||||
'Waiting',
|
||||
'Restarting',
|
||||
'Stopping',
|
||||
'Killing',
|
||||
'Removing',
|
||||
# An extra, specific to containers
|
||||
'Recreate',
|
||||
))
|
||||
DOCKER_STATUS_ERROR = frozenset((
|
||||
'Error',
|
||||
))
|
||||
DOCKER_STATUS = frozenset(DOCKER_STATUS_DONE | DOCKER_STATUS_WORKING | DOCKER_STATUS_ERROR)
|
||||
|
||||
|
||||
class ResourceType(object):
|
||||
NETWORK = "network"
|
||||
IMAGE = "image"
|
||||
VOLUME = "volume"
|
||||
CONTAINER = "container"
|
||||
|
||||
@classmethod
|
||||
def from_docker_compose_event(cls, resource_type):
|
||||
# type: (Type[ResourceType], Text) -> Any
|
||||
return {
|
||||
"Network": cls.NETWORK,
|
||||
"Image": cls.IMAGE,
|
||||
"Volume": cls.VOLUME,
|
||||
"Container": cls.CONTAINER,
|
||||
}[resource_type]
|
||||
|
||||
|
||||
ResourceEvent = namedtuple(
|
||||
'ResourceEvent',
|
||||
['resource_type', 'resource_id', 'status', 'msg']
|
||||
)
|
||||
|
||||
|
||||
_RE_RESOURCE_EVENT = re.compile(
|
||||
r'^'
|
||||
r'\s*'
|
||||
r'(?P<resource_type>Network|Image|Volume|Container)'
|
||||
r'\s+'
|
||||
r'(?P<resource_id>\S+)'
|
||||
r'\s+'
|
||||
r'(?P<status>\S(?:|.*\S))'
|
||||
r'\s*'
|
||||
r'$'
|
||||
)
|
||||
|
||||
_RE_RESOURCE_EVENT_DRY_RUN = re.compile(
|
||||
r'^'
|
||||
r'\s*'
|
||||
r'DRY-RUN MODE -'
|
||||
r'\s+'
|
||||
r'(?P<resource_type>Network|Image|Volume|Container)'
|
||||
r'\s+'
|
||||
r'(?P<resource_id>\S+)'
|
||||
r'\s+'
|
||||
r'(?P<status>\S(?:|.*\S))'
|
||||
r'\s*'
|
||||
r'$'
|
||||
)
|
||||
|
||||
|
||||
class ContainerManager(DockerBaseClass):
|
||||
def __init__(self, client):
|
||||
super(ContainerManager, self).__init__()
|
||||
self.client = client
|
||||
self.check_mode = self.client.check_mode
|
||||
parameters = self.client.module.params
|
||||
|
||||
self.project_src = parameters['project_src']
|
||||
self.project_name = parameters['project_name']
|
||||
self.env_files = parameters['env_files']
|
||||
self.profiles = parameters['profiles']
|
||||
self.state = parameters['state']
|
||||
self.dependencies = parameters['dependencies']
|
||||
self.recreate = parameters['recreate']
|
||||
self.remove_images = parameters['remove_images']
|
||||
self.remove_volumes = parameters['remove_volumes']
|
||||
self.remove_orphans = parameters['remove_orphans']
|
||||
self.timeout = parameters['timeout']
|
||||
|
||||
compose = self.client.get_client_plugin_info('compose')
|
||||
if compose is None:
|
||||
self.client.fail('Docker CLI {0} does not have the compose plugin installed'.format(self.client.get_cli()))
|
||||
compose_version = compose['Version'].lstrip('v')
|
||||
self.compose_version = LooseVersion(compose_version)
|
||||
if self.compose_version < LooseVersion(DOCKER_COMPOSE_MINIMAL_VERSION):
|
||||
self.client.fail('Docker CLI {cli} has the compose plugin with version {version}; need version {min_version} or later'.format(
|
||||
cli=self.client.get_cli(),
|
||||
version=compose_version,
|
||||
min_version=DOCKER_COMPOSE_MINIMAL_VERSION,
|
||||
))
|
||||
|
||||
if not os.path.isdir(self.project_src):
|
||||
self.client.fail('"{0}" is not a directory'.format(self.project_src))
|
||||
|
||||
if all(not os.path.isfile(os.path.join(self.project_src, f)) for f in DOCKER_COMPOSE_FILES):
|
||||
self.client.fail('"{0}" does not contain {1}'.format(self.project_src, ' or '.join(DOCKER_COMPOSE_FILES)))
|
||||
|
||||
def get_base_args(self):
|
||||
args = ['compose', '--ansi', 'never']
|
||||
if self.compose_version >= LooseVersion('2.19.0'):
|
||||
# https://github.com/docker/compose/pull/10690
|
||||
args.extend(['--progress', 'plain'])
|
||||
args.extend(['--project-directory', self.project_src])
|
||||
if self.project_name:
|
||||
args.extend(['--project-name', self.project_name])
|
||||
for env_file in self.env_files or []:
|
||||
args.extend(['--env-file', env_file])
|
||||
for profile in self.profiles or []:
|
||||
args.extend(['--profile', profile])
|
||||
return args
|
||||
|
||||
def list_containers_raw(self):
|
||||
args = self.get_base_args() + ['ps', '--format', 'json', '--all']
|
||||
if self.compose_version >= LooseVersion('2.23.0'):
|
||||
# https://github.com/docker/compose/pull/11038
|
||||
args.append('--no-trunc')
|
||||
kwargs = dict(cwd=self.project_src, check_rc=True)
|
||||
if self.compose_version >= LooseVersion('2.21.0'):
|
||||
# Breaking change in 2.21.0: https://github.com/docker/compose/pull/10918
|
||||
dummy, containers, dummy = self.client.call_cli_json_stream(*args, **kwargs)
|
||||
else:
|
||||
dummy, containers, dummy = self.client.call_cli_json(*args, **kwargs)
|
||||
return containers
|
||||
|
||||
def list_containers(self):
|
||||
result = []
|
||||
for container in self.list_containers_raw():
|
||||
labels = {}
|
||||
if container.get('Labels'):
|
||||
for part in container['Labels'].split(','):
|
||||
label_value = part.split('=', 1)
|
||||
labels[label_value[0]] = label_value[1] if len(label_value) > 1 else ''
|
||||
container['Labels'] = labels
|
||||
container['Names'] = container.get('Names', container['Name']).split(',')
|
||||
container['Networks'] = container.get('Networks', '').split(',')
|
||||
container['Publishers'] = container.get('Publishers') or []
|
||||
result.append(container)
|
||||
return result
|
||||
|
||||
def list_images(self):
|
||||
args = self.get_base_args() + ['images', '--format', 'json']
|
||||
kwargs = dict(cwd=self.project_src, check_rc=True)
|
||||
dummy, images, dummy = self.client.call_cli_json(*args, **kwargs)
|
||||
return images
|
||||
|
||||
def run(self):
|
||||
if self.state == 'present':
|
||||
result = self.cmd_up()
|
||||
elif self.state == 'stopped':
|
||||
result = self.cmd_stop()
|
||||
elif self.state == 'restarted':
|
||||
result = self.cmd_restart()
|
||||
elif self.state == 'absent':
|
||||
result = self.cmd_down()
|
||||
|
||||
result['containers'] = self.list_containers()
|
||||
result['images'] = self.list_images()
|
||||
return result
|
||||
|
||||
def parse_events(self, stderr, dry_run=False):
|
||||
events = []
|
||||
for line in stderr.splitlines():
|
||||
line = to_native(line.strip())
|
||||
match = (_RE_RESOURCE_EVENT_DRY_RUN if dry_run else _RE_RESOURCE_EVENT).match(line)
|
||||
if match is not None:
|
||||
status = match.group('status')
|
||||
msg = None
|
||||
if status not in DOCKER_STATUS:
|
||||
status, msg = msg, status
|
||||
events.append(
|
||||
ResourceEvent(
|
||||
ResourceType.from_docker_compose_event(match.group('resource_type')),
|
||||
match.group('resource_id'),
|
||||
status,
|
||||
msg,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# This could be a bug, a change of docker compose's output format, ...
|
||||
# Tell the user to report it to us :-)
|
||||
self.client.warn(
|
||||
'Cannot parse event from line: {0!r}. Please report this at '
|
||||
'https://github.com/ansible-collections/community.docker/issues/new?assignees=&labels=&projects=&template=bug_report.md'
|
||||
.format(line)
|
||||
)
|
||||
return events
|
||||
|
||||
def has_changes(self, events):
|
||||
for event in events:
|
||||
if event.status in DOCKER_STATUS_WORKING:
|
||||
return True
|
||||
return False
|
||||
|
||||
def extract_actions(self, events):
|
||||
actions = []
|
||||
for event in events:
|
||||
if event.status in DOCKER_STATUS_WORKING:
|
||||
actions.append({
|
||||
'what': event.resource_type,
|
||||
'id': event.resource_id,
|
||||
'status': event.status,
|
||||
})
|
||||
return actions
|
||||
|
||||
def emit_warnings(self, events):
|
||||
for event in events:
|
||||
# If a message is present, assume it is a warning
|
||||
if event.status is None and event.msg is not None:
|
||||
self.client.warn('Docker compose: {resource_type} {resource_id}: {msg}'.format(
|
||||
resource_type=event.resource_type,
|
||||
resource_id=event.resource_id,
|
||||
msg=event.msg,
|
||||
))
|
||||
|
||||
def update_failed(self, result, events):
|
||||
errors = []
|
||||
for event in events:
|
||||
if event.status in DOCKER_STATUS_ERROR:
|
||||
errors.append('Error when processing {resource_type} {resource_id}: {status}'.format(
|
||||
resource_type=event.resource_type,
|
||||
resource_id=event.resource_id,
|
||||
status=event.status,
|
||||
))
|
||||
if errors:
|
||||
result['failed'] = True
|
||||
result['msg'] = '\n'.join(errors)
|
||||
|
||||
def get_up_cmd(self, dry_run, no_start=False):
|
||||
args = self.get_base_args() + ['up', '--detach', '--no-color']
|
||||
if self.remove_orphans:
|
||||
args.append('--remove-orphans')
|
||||
if self.recreate == 'always':
|
||||
args.append('--force-recreate')
|
||||
if self.recreate == 'never':
|
||||
args.append('--no-recreate')
|
||||
if not self.dependencies:
|
||||
args.append('--no-deps')
|
||||
if self.timeout is not None:
|
||||
args.extend(['--timeout', '%d' % self.timeout])
|
||||
if no_start:
|
||||
args.append('--no-start')
|
||||
if dry_run:
|
||||
args.append('--dry-run')
|
||||
args.append('--')
|
||||
return args
|
||||
|
||||
def cmd_up(self):
|
||||
result = dict()
|
||||
args = self.get_up_cmd(self.check_mode)
|
||||
dummy, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src, check_rc=True)
|
||||
events = self.parse_events(stderr, dry_run=self.check_mode)
|
||||
self.emit_warnings(events)
|
||||
result['changed'] = self.has_changes(events)
|
||||
result['actions'] = self.extract_actions(events)
|
||||
self.update_failed(result, events)
|
||||
return result
|
||||
|
||||
def get_stop_cmd(self, dry_run):
|
||||
args = self.get_base_args() + ['stop']
|
||||
if self.timeout is not None:
|
||||
args.extend(['--timeout', '%d' % self.timeout])
|
||||
if dry_run:
|
||||
args.append('--dry-run')
|
||||
args.append('--')
|
||||
return args
|
||||
|
||||
def _are_containers_stopped(self):
|
||||
for container in self.list_containers_raw():
|
||||
if container['State'] not in ('created', 'exited', 'stopped', 'killed'):
|
||||
return False
|
||||
return True
|
||||
|
||||
def cmd_stop(self):
|
||||
# Since 'docker compose stop' **always** claims its stopping containers, even if they are already
|
||||
# stopped, we have to do this a bit more complicated.
|
||||
|
||||
result = dict()
|
||||
# Make sure all containers are created
|
||||
args = self.get_up_cmd(self.check_mode, no_start=True)
|
||||
dummy, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src, check_rc=True)
|
||||
events_1 = self.parse_events(stderr, dry_run=self.check_mode)
|
||||
self.emit_warnings(events_1)
|
||||
if not self._are_containers_stopped():
|
||||
# Make sure all containers are stopped
|
||||
args = self.get_stop_cmd(self.check_mode)
|
||||
dummy, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src, check_rc=True)
|
||||
events_2 = self.parse_events(stderr, dry_run=self.check_mode)
|
||||
self.emit_warnings(events_2)
|
||||
else:
|
||||
events_2 = []
|
||||
# Compose result
|
||||
result['changed'] = self.has_changes(events_1) or self.has_changes(events_2)
|
||||
result['actions'] = self.extract_actions(events_1) + self.extract_actions(events_2)
|
||||
self.update_failed(result, events_1 + events_2)
|
||||
return result
|
||||
|
||||
def get_restart_cmd(self, dry_run):
|
||||
args = self.get_base_args() + ['restart']
|
||||
if not self.dependencies:
|
||||
args.append('--no-deps')
|
||||
if self.timeout is not None:
|
||||
args.extend(['--timeout', '%d' % self.timeout])
|
||||
if dry_run:
|
||||
args.append('--dry-run')
|
||||
args.append('--')
|
||||
return args
|
||||
|
||||
def cmd_restart(self):
|
||||
result = dict()
|
||||
args = self.get_restart_cmd(self.check_mode)
|
||||
dummy, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src, check_rc=True)
|
||||
events = self.parse_events(stderr, dry_run=self.check_mode)
|
||||
self.emit_warnings(events)
|
||||
result['changed'] = self.has_changes(events)
|
||||
result['actions'] = self.extract_actions(events)
|
||||
self.update_failed(result, events)
|
||||
return result
|
||||
|
||||
def get_down_cmd(self, dry_run):
|
||||
args = self.get_base_args() + ['down']
|
||||
if self.remove_orphans:
|
||||
args.append('--remove-orphans')
|
||||
if self.remove_images:
|
||||
args.extend(['--rmi', self.remove_images])
|
||||
if self.remove_volumes:
|
||||
args.append('--volumes')
|
||||
if self.timeout is not None:
|
||||
args.extend(['--timeout', '%d' % self.timeout])
|
||||
if dry_run:
|
||||
args.append('--dry-run')
|
||||
args.append('--')
|
||||
return args
|
||||
|
||||
def cmd_down(self):
|
||||
result = dict()
|
||||
args = self.get_down_cmd(self.check_mode)
|
||||
dummy, stdout, stderr = self.client.call_cli(*args, cwd=self.project_src, check_rc=True)
|
||||
events = self.parse_events(stderr, dry_run=self.check_mode)
|
||||
self.emit_warnings(events)
|
||||
result['changed'] = self.has_changes(events)
|
||||
result['actions'] = self.extract_actions(events)
|
||||
self.update_failed(result, events)
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
argument_spec = dict(
|
||||
project_src=dict(type='path', required=True),
|
||||
project_name=dict(type='str'),
|
||||
env_files=dict(type='list', elements='path'),
|
||||
profiles=dict(type='list', elements='str'),
|
||||
state=dict(type='str', default='present', choices=['absent', 'present', 'stopped', 'restarted']),
|
||||
dependencies=dict(type='bool', default=True),
|
||||
recreate=dict(type='str', default='auto', choices=['always', 'never', 'auto']),
|
||||
remove_images=dict(type='str', choices=['all', 'local']),
|
||||
remove_volumes=dict(type='bool', default=False),
|
||||
remove_orphans=dict(type='bool', default=False),
|
||||
timeout=dict(type='int'),
|
||||
)
|
||||
|
||||
client = AnsibleModuleDockerClient(
|
||||
argument_spec=argument_spec,
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
try:
|
||||
result = ContainerManager(client).run()
|
||||
client.module.exit_json(**result)
|
||||
except DockerException as e:
|
||||
client.fail('An unexpected docker error occurred: {0}'.format(to_native(e)), exception=traceback.format_exc())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
6
tests/integration/targets/docker_compose_v2/aliases
Normal file
6
tests/integration/targets/docker_compose_v2/aliases
Normal 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
|
||||
10
tests/integration/targets/docker_compose_v2/meta/main.yml
Normal file
10
tests/integration/targets/docker_compose_v2/meta/main.yml
Normal 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
|
||||
48
tests/integration/targets/docker_compose_v2/tasks/main.yml
Normal file
48
tests/integration/targets/docker_compose_v2/tasks/main.yml
Normal 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
|
||||
|
||||
####################################################################
|
||||
# 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:
|
||||
- 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', '>=')
|
||||
@ -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 }}"
|
||||
@ -0,0 +1,265 @@
|
||||
---
|
||||
# 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: Set up project and container names
|
||||
set_fact:
|
||||
pname: "{{ name_prefix }}"
|
||||
cname: "{{ name_prefix ~ '-hi' }}"
|
||||
|
||||
- name: Registering container name
|
||||
set_fact:
|
||||
cnames: "{{ cnames + [pname ~ '-' ~ cname ~ '-1'] }}"
|
||||
dnetworks: "{{ dnetworks + [pname ~ '_default'] }}"
|
||||
|
||||
- name: Define services
|
||||
set_fact:
|
||||
project_src: "{{ remote_tmp_dir }}/{{ pname }}"
|
||||
test_service: |
|
||||
version: '3'
|
||||
services:
|
||||
{{ cname }}:
|
||||
image: "{{ docker_test_image_alpine }}"
|
||||
command: '/bin/sh -c "sleep 10m"'
|
||||
stop_grace_period: 1s
|
||||
test_service_mod: |
|
||||
version: '3'
|
||||
services:
|
||||
{{ cname }}:
|
||||
image: "{{ docker_test_image_alpine }}"
|
||||
command: '/bin/sh -c "sleep 15m"'
|
||||
stop_grace_period: 1s
|
||||
|
||||
- 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
|
||||
|
||||
- assert:
|
||||
that:
|
||||
- present_1_check is changed
|
||||
- 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_2_check is not changed
|
||||
- present_2 is not changed
|
||||
- present_3_check is changed
|
||||
- present_3 is changed
|
||||
|
||||
####################################################################
|
||||
## 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 is changed
|
||||
- absent_2_check is not changed
|
||||
- absent_2 is not changed
|
||||
|
||||
####################################################################
|
||||
## 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 is changed
|
||||
- present_2_check is not changed
|
||||
- present_2 is not changed
|
||||
- present_3_check is changed
|
||||
- present_3 is changed
|
||||
- present_4_check is not changed
|
||||
- present_4 is not changed
|
||||
- present_5_check is changed
|
||||
- present_5 is changed
|
||||
- present_6_check is changed
|
||||
- present_6 is changed
|
||||
- present_7_check is changed
|
||||
- present_7 is changed
|
||||
@ -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
|
||||
@ -0,0 +1,13 @@
|
||||
---
|
||||
# Copyright (c) Ansible Project
|
||||
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
- name: Compose is available from Alpine 3.15 on
|
||||
set_fact:
|
||||
docker_has_compose: "{{ ansible_facts.distribution_version is version('3.15', '>=') }}"
|
||||
|
||||
- name: Install Docker compose CLI plugin
|
||||
apk:
|
||||
name: docker-cli-compose
|
||||
when: docker_has_compose
|
||||
@ -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
|
||||
|
||||
- name: Install Docker compose CLI plugin
|
||||
community.general.pacman:
|
||||
name: docker-compose
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -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
|
||||
|
||||
- name: For some reason we don't have the buildx plugin
|
||||
set_fact:
|
||||
docker_has_compose: false
|
||||
@ -0,0 +1 @@
|
||||
nothing.yml
|
||||
@ -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
|
||||
|
||||
- name: Compose is available in OpenSuSE 15.5, not sure which versions before
|
||||
set_fact:
|
||||
docker_has_compose: "{{ ansible_facts.distribution_version is version('15.5', '>=') }}"
|
||||
|
||||
- name: Install Docker compose CLI plugin
|
||||
community.general.zypper:
|
||||
name: docker-compose
|
||||
disable_gpg_check: true
|
||||
when: docker_has_compose
|
||||
@ -0,0 +1,65 @@
|
||||
---
|
||||
# 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: Setup Docker
|
||||
when: ansible_facts.distribution ~ ansible_facts.distribution_major_version not in ['CentOS6', 'RedHat6']
|
||||
block:
|
||||
- name:
|
||||
debug:
|
||||
msg: |-
|
||||
OS family: {{ ansible_facts.os_family }}
|
||||
Distribution: {{ ansible_facts.distribution }}
|
||||
Distribution major version: {{ ansible_facts.distribution_major_version }}
|
||||
Distribution full version: {{ ansible_facts.distribution_version }}
|
||||
|
||||
- name: Set facts for Docker features to defaults
|
||||
set_fact:
|
||||
docker_has_compose: true
|
||||
docker_compose_version: "0.0"
|
||||
|
||||
- name: Include distribution specific variables
|
||||
include_vars: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.distribution }}.yml"
|
||||
- "{{ ansible_facts.os_family }}.yml"
|
||||
- default.yml
|
||||
paths:
|
||||
- "{{ role_path }}/vars"
|
||||
|
||||
- name: Include distribution specific tasks
|
||||
include_tasks: "{{ lookup('first_found', params) }}"
|
||||
vars:
|
||||
params:
|
||||
files:
|
||||
- "{{ ansible_facts.distribution }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.os_family }}-{{ ansible_facts.distribution_major_version }}.yml"
|
||||
- "{{ ansible_facts.distribution }}.yml"
|
||||
- "{{ ansible_facts.os_family }}.yml"
|
||||
paths:
|
||||
- "{{ role_path }}/tasks"
|
||||
|
||||
- name: Obtain Docker Compose version
|
||||
when: docker_has_compose
|
||||
block:
|
||||
- command:
|
||||
cmd: docker info --format '{% raw %}{{ json .ClientInfo.Plugins }}{% endraw %}'
|
||||
register: docker_cli_plugins_stdout
|
||||
- set_fact:
|
||||
docker_has_compose: >-
|
||||
{{ docker_cli_plugins_stdout.stdout | from_json | selectattr('Name', 'eq', 'compose') | map(attribute='Version') | length > 0 }}
|
||||
docker_compose_version: >-
|
||||
{{ docker_cli_plugins_stdout.stdout | from_json | selectattr('Name', 'eq', 'compose') | map(attribute='Version') | first | default('0.0') | regex_replace('^v', '') }}
|
||||
|
||||
- debug:
|
||||
msg: "Has Docker compoes plugin: {{ docker_has_compose }}; Docker compose plugin version: {{ docker_compose_version }}"
|
||||
@ -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
|
||||
|
||||
# nothing to do
|
||||
[]
|
||||
@ -0,0 +1,4 @@
|
||||
---
|
||||
# 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
|
||||
@ -7,5 +7,6 @@
|
||||
plugins/modules/current_container_facts.py validate-modules:return-syntax-error
|
||||
plugins/module_utils/module_container/module.py compile-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/module_utils/module_container/module.py import-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/modules/docker_compose_v2.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_container.py import-2.6!skip # Import uses Python 2.7+ syntax
|
||||
plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin
|
||||
|
||||
@ -7,5 +7,6 @@
|
||||
plugins/modules/current_container_facts.py validate-modules:return-syntax-error
|
||||
plugins/module_utils/module_container/module.py compile-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/module_utils/module_container/module.py import-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/modules/docker_compose_v2.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_container.py import-2.6!skip # Import uses Python 2.7+ syntax
|
||||
plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
||||
plugins/modules/current_container_facts.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_compose_v2.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin
|
||||
|
||||
@ -1,2 +1,3 @@
|
||||
.azure-pipelines/scripts/publish-codecov.py replace-urlopen
|
||||
plugins/modules/docker_compose_v2.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin
|
||||
|
||||
@ -6,5 +6,6 @@
|
||||
.azure-pipelines/scripts/publish-codecov.py metaclass-boilerplate
|
||||
plugins/module_utils/module_container/module.py compile-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/module_utils/module_container/module.py import-2.6!skip # Uses Python 2.7+ syntax
|
||||
plugins/modules/docker_compose_v2.py validate-modules:return-syntax-error
|
||||
plugins/modules/docker_container.py import-2.6!skip # Import uses Python 2.7+ syntax
|
||||
plugins/modules/docker_container_copy_into.py validate-modules:undocumented-parameter # _max_file_size_for_diff is used by the action plugin
|
||||
|
||||
Loading…
Reference in New Issue
Block a user