[SQUASH] backport tripleo-container-manage to stable/train
This is a squash of 32 commits to facilitate the backport of tripleo-container-manage and its dependencies. Introduce tripleo-container-manage role This is a first ieration of the role, but there is still a long TODO, that will come later in separated patches: - Add molecule testing - In podman.yaml, add cpuset_cpus with parity of what is in paunch - Remove containers that are: - managed by tripleo-ansible (using the container_label flag) - not in the container-startup-config - Print stdout when containers start as it was done with paunch Story: 2006732 Task: 37165 Co-Authored-By: Kevin Carter <kecarter@redhat.com> Co-Authored-By: Alex Schultz <aschultz@redhat.com> Depends-On: https://review.opendev.org/#/c/702144/ Change-Id: I2f88caa8e1c230dfe846a8a0dd9f939b98992cd5 (cherry picked from commita191a2d600
) tripleo-container-manage: set some defaults Set defaults that are needed to use the role outside of THT more easily. Change-Id: Id67cf06c85a2a6b50e6494b1a66f534ccb06c4a7 (cherry picked from commit609d7895a1
) Move the filters plugin to the core plugins location This change is a workaround for a zuul issue which moves the nested ansible role plugin to the core plugins directory so that it is not creating a gate conflict. Change-Id: I9f959803381063502b4d15980b14c3416ffa153f Signed-off-by: Kevin Carter <kecarter@redhat.com> (cherry picked from commite2719131db
) Revert "Workaround for ansible-lint installation failure" Backport note: this is a second backport of the same patch since now it includes the change in tripleo-container-manage role that is being backported to stable/train. This reverts the disabling of the ansible-lint test from commitcffd4fc9d4
and updates ansible-lint to the fixed version. Included are fixes for ansible-lint test failures which got merged as part of I2f88caa8e1c230dfe846a8a0dd9f939b98992cd5 while the lint test was disabled. Change-Id: I37100f5e1764a5cd2cb8df82ae963e673ca0a8da (cherry picked from commit28e105c056
) tripleo-container-manage: few improvements - Add and use variables to make the role more flexible: tripleo_container_manage_config, tripleo_container_manage_config_id tripleo_container_manage_debug tripleo_container_manage_config_pattern (and rename hashed_files var) With these vars, the role can pretty much be used outside of TripleO. - Show logs of config data generation if debug is enabled - Do not run the "podman exec" tasks in check mode - Remove the dependency on the "step" variable Change-Id: I28ee31b723f27c392f880676aaae9368906cf45f (cherry picked from commit9c69840640
) Adds new molecule testing for tripleo-container-manage This test ensure the "create" part is working fine with an easy and simple container. The container-create and default scenario have been consoldated so its running one complete test for now. Change-Id: I9139c7b63c15739a1a95d913acb1128af299ce97 Co-Authored-By: Emilien Macchi <emilien@redhat.com> Signed-off-by: Kevin Carter <kecarter@redhat.com> (cherry picked from commit414f47cc32
) tripleo-container-manage: add check tasks to the molecule playbook Verify that the "fedora" container exists and has the right infos. Change-Id: If78a254564cff502b49769b0b401d2efdac8cb23 (cherry picked from commitc20ab42015
) Molecule job for testing plugins and modules and remove ansible_facts from podman_container_info module Change-Id: I0c768bc6168363fa3758562f9f053aa9ab85236b (cherry picked from commit2d42082737
) tripleo-container-manage - first support for idempotency - Implements a new helper to figure out if the existing containers on the hosts need to be removed (and re-created later). The helper will remove the container if: - the container is managed by tripleo_ansible (+ other conditions later) - a running container isn't in the config - the container has no config_data Label - the config_data changed for the container - Restart the systemd service for the container if the podman_container module reported as changed (note the podman_container isn't yet idempotent but we're working on it in a separated patch) - Fix the healthcheck & timer systemd files to be attached to the right service (with tripleo_ prefix) Story: #2006732 Task: #37163 Change-Id: I5081c918b47dcb9f3629a3649fdf33d17668c1ff (cherry picked from commit7e41e0642d
) tripleo-container-manage: include_tasks to speed things up Change-Id: I79d48fef7552f72c8fc0ebbad35d98cfc618b114 (cherry picked from commit57411934b7
) tripleo-container-manage: introduce concurrency Co-Authored-By: Alex Schultz <aschultz@redhat.com> Change-Id: I1e5e941558c492b050e4db542703e322707dbbbd (cherry picked from commit2f8f0fc027
) tripleo-container-manage: fix log_opt Put the right variables to have proper logging. Change-Id: I60a14721ed978dddbebfd28f830fb2c50f326ce0 (cherry picked from commite7f71c352e
) tripleo-container-manage: some nits fixed Change-Id: I4006db3f5e3092aefeb8a0e50819dda11931630b (cherry picked from commit73e49888f7
) tripleo-container-manage: fix exec check Fix the task which check if the container where the exec happens is actually running. Also podman_containers needs to be refreshed at every start_order to get new container informations status. So moving podman_containers from main, and putting it where we need it: - before processing the list of containers to delete - when start_order playbook starts Co-Authored-By: Sagi Shnaidman <sshnaidm@redhat.com> Change-Id: I2afb71288208c8b97763caa832c94e06e1b9457c (cherry picked from commite68d4b42a2
) tripleo-container-manage: port paunch-services paunch-services used to be useful for container start/stop ordering, when on the host some containers are managed by Pacemaker and some others by Paunch. We need to keep that feature so we are now porting these scripts and services into tripleo-ansible. These files where managed by Paunch: %{_libexecdir}/paunch-container-shutdown %{_libexecdir}/paunch-start-podman-container %{_unitdir}/paunch-container-shutdown.service %{_presetdir}/91-paunch-container-shutdown.preset %{_unitdir}/netns-placeholder.service %{_presetdir}/91-netns-placeholder.preset Now we handle them via Ansible now, and cleanup the Paunch version. It creates the exact same files from: https://github.com/rdo-packages/paunch-distgit This feature is disabled by default and will be explicitely enabled by THT later. Molecule tests enable it though for testing coverage. Story: 2006732 Task: 37382 Change-Id: I4f79429baab50bc0199fb65fe84641908d83935d (cherry picked from commit6e76f444df
) tripleo-container-manage: use async - Add a new filter 'haskey' which returns container data with a specific config key. The filter takes a list of dicts and returns the dicts which have a certain key given in parameter with 'attribute'. If 'reverse' is set to True, the returned list won't contains dicts which have the 'attribute' value. If 'any' is set to True, the returned list will match any value in the list of 'value' parameter which has to be a list. - Make the exec and create playbook using ansible "async". (WIP is to re-add the podman_container.changed in systemd playbook). Note: we don't manage Systemd resources with async, since it exposes race conditions at the systemd level. Change-Id: Ice92cd5f90039e685c171e9035f929349a67ff2c (cherry picked from commite26f817597
) tripleo-container-manage: fix duplicated loop vars So we don't have the Ansible warning saying the loop var is already used for another loop. Change-Id: Ie28857cb712d2133aad4a67ae6942fcbbb1a6aee (cherry picked from commit80e0476f78
) tripleo-container-manage: skip some tasks in check mode Change-Id: I5e68fdb2d872c741f00a1a24bac33112ba630f69 (cherry picked from commitf506dd6994
) tripleo-container-manage: add no_log to podman create So we don't leak confidential informations like passwords and others in the logs. Note that if debug is enabled, the infos are displayed in the ansible logs. Change-Id: I8473c9118dbce2b04eeb2c01bcc1e55232325a67 (cherry picked from commitac8cff4ebb
) Improve molecule tests for tripleo-container-manage (systemd) Change-Id: Id14ad4876bdf7dcc979d5ecf4fe2b4751c635b88 (cherry picked from commit484faa7e80
) fix typo Change-Id: I0a970ae73f01c6ff181a67367cf668091748fc9c Signed-off-by: Kevin Carter <kecarter@redhat.com> (cherry picked from commit3720b41a7f
) tripleo-container-manage: restart systemd service if container changed If a podman_container resource changed, it'll be added to a fact, that later will be used when it comes to figure our if a systemd service needs to be restarted or not. It introduces a new filter: list_of_keys. This filter takes in input a list of dictionaries and for each of them it will add the key to list_of_keys and returns it. Change-Id: I0285b006c015f6cd223615ebdad52286f7683a87 (cherry picked from commit0a037c65f7
) tripleo-container-manage: some improvements - Improve logging in some tasks, to display what container is managed - Use no_log for the tasks which leak all infos about containers (config_data). If debug is enabled, the logs will show all the config_data. - Move the /etc/sysconfig/podman_drop_in tasks into shutdown.yaml so it runs once and it's staying close to the other tasks related to why we have this file (manage shutdown/start order when reboot). Change-Id: I56896f92d58db900fd2ea06281d89f75e8d53a17 (cherry picked from commit6743f75863
) tripleo-container-manage: create ansible-managed dropin file If a deployment has containers managed by tripleo-ansible and not paunch, and an operator would try to run paunch on the host, we want to send a warning because paunch will remove all containers managed by tripleo-ansible and then redeploy the containers with managed_by=paunch. We could change the default of manage_by in Paunch (currently managed_by=paunch) to something else e.g. tripleo, which would also be the managed_by of tripleo-ansible; so both paunch and tripleo-ansible could be used on the same host. However, we decided that when tripleo-ansible is used to deploy the containers, paunch could not be used anymore on that host. Therefore, we create a file that will be checked by paunch and if present, paunch CLI will show a warning. Change-Id: I722cb8faa3b7eee81b418da83451bf802351dd79 (cherry picked from commitdb1b13c962
) tripleo-container-manage: remove no_log for create/exec tasks 1. Remove no_log for the create and exec tasks. It's always useful to see what is being run. 2. Enable podman_container debug, so we see what commands are being run. Change-Id: If728788293dd64622cf95da840b60e271197e9a0 (cherry picked from commit89e4adf2ae
) Add missing ExecReload in container service unit file It may happen that we want to just reload the container. Before this patch, it was a "stop and start", while podman has the "podman kill" available, accepting the HUP signal. Doing so allows other automated tools to actually just "reload" the container as we would do for a standard service. Change-Id: I35eff80f7637b013d3a1a831289ec9b1e0f81431 (cherry picked from commit2a2bed6f5c
) Introduce unit tests structure for tripleo-ansible filters Change-Id: Ie2fea14d2cbfb2c0b78cdc3064df0a558fa28a4c (cherry picked from commitf90a6d42b3
) Fix case in filters when Labels is None When Labels is None it's not managed by tripleo container, then skip it. Change-Id: Ib82c4d28c462abb3f1a5ccb7d5137ec6059b2665 (cherry picked from commit1acc95211d
) tripleo-container-manage: include more arguments for container exec Container execs can be more complex than just a user and a command. It can also use --env and --privileged. - container_exec_cmd is a new filter that will help to build the container exec command from the container data. - list_or_dict_arg is an utility taken from openstack/paunch which allows to build a command and its arguments with list or dict data. This patch will allow the container exec to work fine when they have environment variables, like it's the case for Keystone bootstrap. Change-Id: I15e0b518936b37e26799dbda9677a248cf17ff3c (cherry picked from commit55d5363b4a
) Add SyslogIdenfier to healthcheck systemd unit Adding this new field will allow to filter all healthcheck logs using the Idenfier value. For instance, using journalctl, you would be able to run this: `journalctl -t healthcheck_collectd' It will also allow to get a dedicated file out of (r)syslog if needed. Change-Id: Icdc5caf4cedc46291a807c39c0a31c74955a4a74 Closes-Bug: #1856573 (cherry picked from commit49858c5265
) Allow to run tripleo-container-manage in check mode Change-Id: I3350a43805b4a148f64de393716c26b0158fcff4 (cherry picked from commit6513a4bed8
) tripleo-container-manage: fix config_data in Config/Labels config_data should not contain the container name, just the actual config data which is the value of the container_data dict. It removes the add of start_order into the compare, this isn't necessary and breaks idempotency for containers which don't have start_order in their config_data. Also adds more unit tests to cover all situations handled by the filter. Change-Id: I0b64bf34c8f7498128b3b1fceb7c727f8544cec6 (cherry picked from commit19c5d7e77e
) tripleo-container-manage: search container configs recursively If the user doesn't override the default value for tripleo_container_manage_config, which is set to '/var/lib/tripleo-config/', to something deeper, we want Ansible to recursively search for the container config until it finds it. Change-Id: I7d23fec91ffb813f0ab6f11b85d811ef3897f9e0 (cherry picked from commit205f7c9b0c
) Idempotency for podman_container Introduce partial idepmpotency for running podman_container - Replace all options by their defaults it they're not set - Force lower case on all podman inspection data - Add a class with methods for every parameter to check its value and compare it with module argument - Add check_mode support and podman_actions Change-Id: I1ae93dff1e10a1a696bb171996a75a0db6c34fa3 (cherry picked from commit1212544a28
) Fix case where there're no effective caps When no effective caps in container, it should be list, not None Change-Id: I5412edc844ad43223c4b3bda35662a7f7ee43f3a (cherry picked from commit3a6690c904
) Add idempotency for networks parameter in podman_container Change-Id: Ib7235d1cb64ad0f42e7a0201008536e1a35bd696 (cherry picked from commit25f8abbc5a
) use version parsing from distutils use LooseVersion for version comparisons Change-Id: I609920a96c725c49f1623f60f8295d89ae4f3141 (cherry picked from commit8c83219fbb
) Improve idempotency for podman containers module Fix some pep8 issues Change-Id: If4233e57edeec10ccac965d61c78f30688cd5531 (cherry picked from commit0609c16d10
) Improve idempotency for podman container module Strip all registry values from "image" parameter in input. Add this case to test. Change-Id: I78656e48cf85a1a39f873ee40193765eecf02c56 (cherry picked from commit87d9e9d9a6
)
This commit is contained in:
parent
655f7e027f
commit
ec4351c566
|
@ -11,6 +11,10 @@ cover
|
|||
nosetests.xml
|
||||
.testrepository
|
||||
.venv
|
||||
.stestr
|
||||
tripleo_ansible.egg-info/
|
||||
__pycache__
|
||||
build
|
||||
|
||||
# Editors
|
||||
*~
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
[DEFAULT]
|
||||
test_path=${TEST_PATH:-./tripleo_ansible/tests/}
|
||||
top_dir=./
|
|
@ -0,0 +1 @@
|
|||
ansible>=2.8
|
|
@ -19,7 +19,9 @@ git [platform:rpm]
|
|||
libffi-devel [platform:rpm]
|
||||
openssl-devel [platform:rpm]
|
||||
python-devel [platform:rpm !platform:rhel-8 !platform:centos-8]
|
||||
python3-devel [platform:rpm !platform:rhel-7 !platform:centos-7]
|
||||
python3-devel [platform:rpm !platform:rhel-7 !platform:centos-7]
|
||||
PyYAML [platform:rpm !platform:rhel-8 !platform:centos-8]
|
||||
python3-pyyaml [platform:rpm !platform:rhel-7 !platform:centos-7]
|
||||
python2-dnf [platform:fedora]
|
||||
|
||||
# For SELinux
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
===============================
|
||||
Role - tripleo-container-manage
|
||||
===============================
|
||||
|
||||
.. ansibleautoplugin::
|
||||
:role: tripleo_ansible/roles/tripleo-container-manage
|
|
@ -1,2 +1,5 @@
|
|||
pre-commit # MIT
|
||||
netaddr # BSD
|
||||
mock>=2.0.0 # BSD
|
||||
stestr>=2.0.0 # Apache-2.0
|
||||
oslotest>=3.2.0 # Apache-2.0
|
||||
|
|
16
tox.ini
16
tox.ini
|
@ -26,8 +26,13 @@ setenv =
|
|||
PYTHONWARNINGS=ignore:DEPRECATION::pip._internal.cli.base_command,ignore::UserWarning
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
sitepackages = True
|
||||
deps = -r {toxinidir}/test-requirements.txt
|
||||
whitelist_externals = bash
|
||||
deps =
|
||||
-r {toxinidir}/test-requirements.txt
|
||||
-r {toxinidir}/ansible-requirements.txt
|
||||
commands = stestr run {posargs}
|
||||
whitelist_externals =
|
||||
bash
|
||||
tox
|
||||
|
||||
[testenv:bindep]
|
||||
# Do not install any requirements. We want this to be fast and work even if
|
||||
|
@ -123,3 +128,10 @@ commands =
|
|||
echo -e '\n\nNo molecule tests have been executed\nSee https://docs.openstack.org/tripleo-ansible/latest/contributing.html#local-testing-of-new-roles\n\n'; \
|
||||
fi"
|
||||
{[testenv:linters]commands}
|
||||
|
||||
[testenv:modules]
|
||||
deps=
|
||||
{[testenv:linters]deps}
|
||||
commands =
|
||||
bash -c "cd {toxinidir}/tripleo_ansible/ansible_plugins/tests; molecule test --all;"
|
||||
{[testenv:linters]commands}
|
||||
|
|
|
@ -0,0 +1,227 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import ast
|
||||
import json
|
||||
import six
|
||||
|
||||
from collections import OrderedDict
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
# cmp() doesn't exist on python3
|
||||
if six.PY3:
|
||||
def cmp(a, b):
|
||||
return 0 if a == b else 1
|
||||
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'singledict': self.singledict,
|
||||
'subsort': self.subsort,
|
||||
'needs_delete': self.needs_delete,
|
||||
'haskey': self.haskey,
|
||||
'list_of_keys': self.list_of_keys,
|
||||
'container_exec_cmd': self.container_exec_cmd
|
||||
}
|
||||
|
||||
def subsort(self, dict_to_sort, attribute, null_value=0):
|
||||
"""Sort a hash from a sub-element.
|
||||
|
||||
This filter will return an dictionary ordered by the attribute
|
||||
part of each item.
|
||||
"""
|
||||
for k, v in dict_to_sort.items():
|
||||
if attribute not in v:
|
||||
dict_to_sort[k][attribute] = null_value
|
||||
|
||||
data = {}
|
||||
for d in dict_to_sort.items():
|
||||
if d[1][attribute] not in data:
|
||||
data[d[1][attribute]] = []
|
||||
data[d[1][attribute]].append({d[0]: d[1]})
|
||||
|
||||
sorted_list = sorted(
|
||||
data.items(),
|
||||
key=lambda x: x[0]
|
||||
)
|
||||
ordered_dict = {}
|
||||
for o, v in sorted_list:
|
||||
ordered_dict[o] = v
|
||||
return ordered_dict
|
||||
|
||||
def singledict(self, list_to_convert):
|
||||
"""Generate a single dictionary from a list of dictionaries.
|
||||
|
||||
This filter will return a single dictionary from a list of
|
||||
dictionaries.
|
||||
"""
|
||||
return_dict = {}
|
||||
for i in list_to_convert:
|
||||
return_dict.update(i)
|
||||
return return_dict
|
||||
|
||||
def needs_delete(self, container_infos, config, config_id):
|
||||
"""Returns a list of containers which need to be removed.
|
||||
|
||||
This filter will check which containers need to be removed for these
|
||||
reasons: no config_data, updated config_data or container not
|
||||
part of the global config.
|
||||
"""
|
||||
to_delete = []
|
||||
to_skip = []
|
||||
installed_containers = []
|
||||
for c in container_infos:
|
||||
c_name = c['Name']
|
||||
installed_containers.append(c_name)
|
||||
|
||||
# Don't delete containers not managed by tripleo-ansible
|
||||
if (c['Config']['Labels'] is None
|
||||
or c['Config']['Labels'].get(
|
||||
'managed_by') != 'tripleo_ansible'):
|
||||
to_skip += [c_name]
|
||||
continue
|
||||
|
||||
# Only remove containers managed in this config_id
|
||||
if (c['Config']['Labels'] is None
|
||||
or c['Config']['Labels'].get('config_id') != config_id):
|
||||
to_skip += [c_name]
|
||||
continue
|
||||
|
||||
# Remove containers with no config_data
|
||||
# e.g. broken config containers
|
||||
if (c['Config']['Labels'] is not None
|
||||
and 'config_data' not in c['Config']['Labels']):
|
||||
to_delete += [c_name]
|
||||
continue
|
||||
|
||||
# Remove containers managed by tripleo-ansible that aren't in
|
||||
# config e.g. containers not needed anymore and removed by an
|
||||
# upgrade. Note: we don't cleanup paunch-managed containers.
|
||||
if c_name not in config:
|
||||
to_delete += [c_name]
|
||||
continue
|
||||
|
||||
for c_name, config_data in config.items():
|
||||
# don't try to remove a container which doesn't exist
|
||||
if c_name not in installed_containers:
|
||||
continue
|
||||
|
||||
# already tagged to be removed
|
||||
if c_name in to_delete:
|
||||
continue
|
||||
|
||||
if c_name in to_skip:
|
||||
continue
|
||||
|
||||
# Remove containers managed by tripleo-ansible when config_data
|
||||
# changed. Since we already cleaned the containers not in config,
|
||||
# this check needs to be in that loop.
|
||||
# e.g. new TRIPLEO_CONFIG_HASH during a minor update
|
||||
try:
|
||||
c_facts = [c['Config']['Labels']['config_data']
|
||||
for c in container_infos if c_name == c['Name']]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
# Build c_facts so it can be compared later with config_data
|
||||
c_facts = ast.literal_eval(c_facts[0]) if (
|
||||
len(c_facts)) == 1 else dict()
|
||||
|
||||
if cmp(c_facts, config_data) != 0:
|
||||
to_delete += [c_name]
|
||||
|
||||
return to_delete
|
||||
|
||||
def haskey(self, batched_container_data, attribute, value=None,
|
||||
reverse=False, any=False):
|
||||
"""Return container data with a specific config key.
|
||||
|
||||
This filter will take a list of dictionaries (batched_container_data)
|
||||
and will return the dictionnaries which have a certain key given
|
||||
in parameter with 'attribute'.
|
||||
If reverse is set to True, the returned list won't contain dictionaries
|
||||
which have the attribute.
|
||||
If any is set to True, the returned list will match any value in
|
||||
the list of values for "value" parameter which has to be a list.
|
||||
"""
|
||||
return_list = []
|
||||
for container in batched_container_data:
|
||||
for k, v in json.loads(json.dumps(container)).items():
|
||||
if attribute in v and not reverse:
|
||||
if value is None:
|
||||
return_list.append({k: v})
|
||||
else:
|
||||
if isinstance(value, list) and any:
|
||||
if v[attribute] in value:
|
||||
return_list.append({k: v})
|
||||
elif any:
|
||||
raise TypeError("value has to be a list if any is "
|
||||
"set to True.")
|
||||
else:
|
||||
if v[attribute] == value:
|
||||
return_list.append({k: v})
|
||||
if attribute not in v and reverse:
|
||||
return_list.append({k: v})
|
||||
return return_list
|
||||
|
||||
def list_of_keys(self, keys_to_list):
|
||||
"""Return a list of keys from a list of dictionaries.
|
||||
|
||||
This filter takes in input a list of dictionaries and for each of them
|
||||
it will add the key to list_of_keys and returns it.
|
||||
"""
|
||||
list_of_keys = []
|
||||
for i in keys_to_list:
|
||||
for k, v in i.items():
|
||||
list_of_keys.append(k)
|
||||
return list_of_keys
|
||||
|
||||
def list_or_dict_arg(self, data, cmd, key, arg):
|
||||
"""Utility to build a command and its argument with list or dict data.
|
||||
|
||||
The key can be a dictionary or a list, the returned arguments will be
|
||||
a list where each item is the argument name and the item data.
|
||||
"""
|
||||
if key not in data:
|
||||
return
|
||||
value = data[key]
|
||||
if isinstance(value, dict):
|
||||
for k, v in sorted(value.items()):
|
||||
if v:
|
||||
cmd.append('%s=%s=%s' % (arg, k, v))
|
||||
elif k:
|
||||
cmd.append('%s=%s' % (arg, k))
|
||||
elif isinstance(value, list):
|
||||
for v in value:
|
||||
if v:
|
||||
cmd.append('%s=%s' % (arg, v))
|
||||
|
||||
def container_exec_cmd(self, data, cli='podman'):
|
||||
"""Return a list of all the arguments to execute a container exec.
|
||||
|
||||
This filter takes in input the container exec data and the cli name
|
||||
to return the full command in a list of arguments that will be used
|
||||
by Ansible command module.
|
||||
"""
|
||||
cmd = [cli, 'exec']
|
||||
cmd.append('--user=%s' % data.get('user', 'root'))
|
||||
if 'privileged' in data:
|
||||
cmd.append('--privileged=%s' % str(data['privileged']).lower())
|
||||
self.list_or_dict_arg(data, cmd, 'environment', '--env')
|
||||
cmd.extend(data['command'])
|
||||
return cmd
|
|
@ -21,9 +21,10 @@ from __future__ import absolute_import, division, print_function
|
|||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
from distutils.version import LooseVersion
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule, env_fallback
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
from ansible.module_utils._text import to_bytes, to_native
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
|
@ -101,7 +102,7 @@ options:
|
|||
(Not available for remote commands) You can also override the default
|
||||
path of the authentication file by setting the ``REGISTRY_AUTH_FILE``
|
||||
environment variable. ``export REGISTRY_AUTH_FILE=path``
|
||||
type: str
|
||||
type: path
|
||||
blkio_weight:
|
||||
description:
|
||||
- Block IO weight (relative weight) accepts a weight value between 10 and
|
||||
|
@ -128,7 +129,22 @@ options:
|
|||
If the path is not absolute, the path is considered to be relative to
|
||||
the cgroups path of the init process. Cgroups will be created if they
|
||||
do not already exist.
|
||||
type: path
|
||||
cgroupns:
|
||||
description:
|
||||
- Path to cgroups under which the cgroup for the container will be
|
||||
created.
|
||||
type: str
|
||||
cgroups:
|
||||
description:
|
||||
- Determines whether the container will create CGroups.
|
||||
Valid values are enabled and disabled, which the default being enabled.
|
||||
The disabled option will force the container to not create CGroups,
|
||||
and thus conflicts with CGroup options cgroupns and cgroup-parent.
|
||||
type: str
|
||||
choices:
|
||||
- default
|
||||
- disabled
|
||||
cidfile:
|
||||
description:
|
||||
- Write the container ID to the file
|
||||
|
@ -154,9 +170,17 @@ options:
|
|||
description:
|
||||
- Limit the CPU real-time period in microseconds
|
||||
type: int
|
||||
cpu_rt_period:
|
||||
description:
|
||||
- Limit the CPU real-time period in microseconds.
|
||||
Limit the container's Real Time CPU usage. This flag tell the kernel to
|
||||
restrict the container's Real Time CPU usage to the period you specify.
|
||||
type: int
|
||||
cpu_rt_runtime:
|
||||
description:
|
||||
- Limit the CPU real-time runtime in microseconds
|
||||
- Limit the CPU real-time runtime in microseconds.
|
||||
This flag tells the kernel to limit the amount of time in a given CPU
|
||||
period Real Time tasks may consume.
|
||||
type: int
|
||||
cpu_shares:
|
||||
description:
|
||||
|
@ -180,6 +204,11 @@ options:
|
|||
- Run container in detach mode
|
||||
type: bool
|
||||
default: True
|
||||
debug:
|
||||
description:
|
||||
- Return additional information which can be helpful for investigations.
|
||||
type: bool
|
||||
default: False
|
||||
detach_keys:
|
||||
description:
|
||||
- Override the key sequence for detaching a container. Format is a single
|
||||
|
@ -190,27 +219,28 @@ options:
|
|||
- Add a host device to the container.
|
||||
The format is <device-on-host>[:<device-on-container>][:<permissions>]
|
||||
(e.g. device /dev/sdc:/dev/xvdc:rwm)
|
||||
type: str
|
||||
type: list
|
||||
elements: str
|
||||
device_read_bps:
|
||||
description:
|
||||
- Limit read rate (bytes per second) from a device
|
||||
(e.g. device-read-bps /dev/sda:1mb)
|
||||
type: str
|
||||
type: list
|
||||
device_read_iops:
|
||||
description:
|
||||
- Limit read rate (IO per second) from a device
|
||||
(e.g. device-read-iops /dev/sda:1000)
|
||||
type: str
|
||||
type: list
|
||||
device_write_bps:
|
||||
description:
|
||||
- Limit write rate (bytes per second) to a device
|
||||
(e.g. device-write-bps /dev/sda:1mb)
|
||||
type: str
|
||||
type: list
|
||||
device_write_iops:
|
||||
description:
|
||||
- Limit write rate (IO per second) to a device
|
||||
(e.g. device-write-iops /dev/sda:1000)
|
||||
type: str
|
||||
type: list
|
||||
dns:
|
||||
description:
|
||||
- Set custom DNS servers
|
||||
|
@ -240,6 +270,11 @@ options:
|
|||
description:
|
||||
- Read in a line delimited file of environment variables
|
||||
type: path
|
||||
env_host:
|
||||
description:
|
||||
- Use all current host environment variables in container.
|
||||
Defaults to false.
|
||||
type: bool
|
||||
etc_hosts:
|
||||
description:
|
||||
- Dict of host-to-IP mappings, where each host name is a key in the
|
||||
|
@ -271,7 +306,7 @@ options:
|
|||
group_add:
|
||||
description:
|
||||
- Add additional groups to run as
|
||||
type: str
|
||||
type: list
|
||||
healthcheck:
|
||||
description:
|
||||
- Set or alter a healthcheck command for a container.
|
||||
|
@ -356,6 +391,7 @@ options:
|
|||
description:
|
||||
- Kernel memory limit
|
||||
(format <number>[<unit>], where unit = b, k, m or g)
|
||||
Note - idempotency is supported for integers only.
|
||||
type: str
|
||||
label:
|
||||
description:
|
||||
|
@ -385,10 +421,12 @@ options:
|
|||
memory:
|
||||
description:
|
||||
- Memory limit (format 10k, where unit = b, k, m or g)
|
||||
Note - idempotency is supported for integers only.
|
||||
type: str
|
||||
memory_reservation:
|
||||
description:
|
||||
- Memory soft limit (format 100m, where unit = b, k, m or g)
|
||||
Note - idempotency is supported for integers only.
|
||||
type: str
|
||||
memory_swap:
|
||||
description:
|
||||
|
@ -396,6 +434,7 @@ options:
|
|||
(--memory) flag.
|
||||
The swap LIMIT should always be larger than -m (--memory) value.
|
||||
By default, the swap LIMIT will be set to double the value of --memory
|
||||
Note - idempotency is supported for integers only.
|
||||
type: str
|
||||
memory_swappiness:
|
||||
description:
|
||||
|
@ -419,7 +458,8 @@ options:
|
|||
* ns:<path> path to a network namespace to join
|
||||
* slirp4netns use slirp4netns to create a user network stack.
|
||||
This is the default for rootless containers
|
||||
type: str
|
||||
type: list
|
||||
elements: str
|
||||
aliases:
|
||||
- net
|
||||
no_hosts:
|
||||
|
@ -512,7 +552,8 @@ options:
|
|||
security_opt:
|
||||
description:
|
||||
- Security Options. For example security_opt "seccomp=unconfined"
|
||||
type: str
|
||||
type: list
|
||||
elements: str
|
||||
shm_size:
|
||||
description:
|
||||
- Size of /dev/shm. The format is <number><unit>. number must be greater
|
||||
|
@ -530,7 +571,7 @@ options:
|
|||
stop_signal:
|
||||
description:
|
||||
- Signal to stop a container. Default is SIGTERM.
|
||||
type: str
|
||||
type: int
|
||||
stop_timeout:
|
||||
description:
|
||||
- Timeout (in seconds) to stop a container. Default is 10.
|
||||
|
@ -813,9 +854,12 @@ class PodmanModuleParams:
|
|||
params {dict} -- dictionary of module parameters
|
||||
|
||||
"""
|
||||
def __init__(self, action, params):
|
||||
|
||||
def __init__(self, action, params, podman_version, module):
|
||||
self.params = params
|
||||
self.action = action
|
||||
self.podman_version = podman_version
|
||||
self.module = module
|
||||
|
||||
def construct_command_from_params(self):
|
||||
"""Create a podman command from given module parameters.
|
||||
|
@ -828,8 +872,8 @@ class PodmanModuleParams:
|
|||
if self.action in ['create', 'run']:
|
||||
cmd = [self.action, '--name', self.params['name']]
|
||||
all_param_methods = [func for func in dir(self)
|
||||
if callable(getattr(self, func)) and
|
||||
func.startswith("addparam")]
|
||||
if callable(getattr(self, func))
|
||||
and func.startswith("addparam")]
|
||||
params_set = (i for i in self.params if self.params[i] is not None)
|
||||
for param in params_set:
|
||||
func_name = "_".join(["addparam", param])
|
||||
|
@ -853,19 +897,26 @@ class PodmanModuleParams:
|
|||
cmd = ['rm', '-f', self.params['name']]
|
||||
return [to_bytes(i, errors='surrogate_or_strict') for i in cmd]
|
||||
|
||||
def addparam_detach(self, c):
|
||||
return c + ['--detach=%s' % self.params['detach']]
|
||||
|
||||
def addparam_etc_hosts(self, c):
|
||||
for host_ip in self.params['etc_hosts'].items():
|
||||
c += ['--add-host', ':'.join(host_ip)]
|
||||
return c
|
||||
def check_version(self, param, minv=None, maxv=None):
|
||||
if minv and LooseVersion(minv) > LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported from podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
if maxv and LooseVersion(maxv) < LooseVersion(
|
||||
self.podman_version):
|
||||
self.module.fail_json(msg="Parameter %s is supported till podman "
|
||||
"version %s only! Current version is %s" % (
|
||||
param, minv, self.podman_version))
|
||||
|
||||
def addparam_annotation(self, c):
|
||||
for annotate in self.params['annotation'].items():
|
||||
c += ['--annotation', '='.join(annotate)]
|
||||
return c
|
||||
|
||||
def addparam_authfile(self, c):
|
||||
return c + ['--authfile', self.params['authfile']]
|
||||
|
||||
def addparam_blkio_weight(self, c):
|
||||
return c + ['--blkio-weight', self.params['blkio_weight']]
|
||||
|
||||
|
@ -884,6 +935,14 @@ class PodmanModuleParams:
|
|||
c += ['--cap-drop', cap_drop]
|
||||
return c
|
||||
|
||||
def addparam_cgroups(self, c):
|
||||
self.check_version('--cgroups', minv='1.6.0')
|
||||
return c + ['--cgroups=%s' % self.params['cgroups']]
|
||||
|
||||
def addparam_cgroupns(self, c):
|
||||
self.check_version('--cgroupns', minv='1.6.2')
|
||||
return c + ['--cgroupns=%s' % self.params['cgroupns']]
|
||||
|
||||
def addparam_cgroup_parent(self, c):
|
||||
return c + ['--cgroup-parent', self.params['cgroup_parent']]
|
||||
|
||||
|
@ -896,6 +955,9 @@ class PodmanModuleParams:
|
|||
def addparam_cpu_period(self, c):
|
||||
return c + ['--cpu-period', self.params['cpu_period']]
|
||||
|
||||
def addparam_cpu_rt_period(self, c):
|
||||
return c + ['--cpu-rt-period', self.params['cpu_rt_period']]
|
||||
|
||||
def addparam_cpu_rt_runtime(self, c):
|
||||
return c + ['--cpu-rt-runtime', self.params['cpu_rt_runtime']]
|
||||
|
||||
|
@ -911,23 +973,36 @@ class PodmanModuleParams:
|
|||
def addparam_cpuset_mems(self, c):
|
||||
return c + ['--cpuset-mems', self.params['cpuset_mems']]
|
||||
|
||||
def addparam_detach(self, c):
|
||||
return c + ['--detach=%s' % self.params['detach']]
|
||||
|
||||
def addparam_detach_keys(self, c):
|
||||
return c + ['--detach-keys', self.params['detach_keys']]
|
||||
|
||||
def addparam_device(self, c):
|
||||
return c + ['--device', self.params['device']]
|
||||
for dev in self.params['device']:
|
||||
c += ['--device', dev]
|
||||
return c
|
||||
|
||||
def addparam_device_read_bps(self, c):
|
||||
return c + ['--device-read-bps', self.params['device_read_bps']]
|
||||
for dev in self.params['device_read_bps']:
|
||||
c += ['--device-read-bps', dev]
|
||||
return c
|
||||
|
||||
def addparam_device_read_iops(self, c):
|
||||
return c + ['--device-read-iops', self.params['device_read_iops']]
|
||||
for dev in self.params['device_read_iops']:
|
||||
c += ['--device-read-iops', dev]
|
||||
return c
|
||||
|
||||
def addparam_device_write_bps(self, c):
|
||||
return c + ['--device-write-bps', self.params['device_write_bps']]
|
||||
for dev in self.params['device_write_bps']:
|
||||
c += ['--device-write-bps', dev]
|
||||
return c
|
||||
|
||||
def addparam_device_write_iops(self, c):
|
||||
return c + ['--device-write-iops', self.params['device_write_iops']]
|
||||
for dev in self.params['device_write_iops']:
|
||||
c += ['--device-write-iops', dev]
|
||||
return c
|
||||
|
||||
def addparam_dns(self, c):
|
||||
return c + ['--dns', ','.join(self.params['dns'])]
|
||||
|
@ -951,6 +1026,15 @@ class PodmanModuleParams:
|
|||
def addparam_env_file(self, c):
|
||||
return c + ['--env-file', self.params['env_file']]
|
||||
|
||||
def addparam_env_host(self, c):
|
||||
self.check_version('--env-host', minv='1.5.0')
|
||||
return c + ['--env-host=%s' % self.params['env_host']]
|
||||
|
||||
def addparam_etc_hosts(self, c):
|
||||
for host_ip in self.params['etc_hosts'].items():
|
||||
c += ['--add-host', ':'.join(host_ip)]
|
||||
return c
|
||||
|
||||
def addparam_expose(self, c):
|
||||
for exp in self.params['expose']:
|
||||
c += ['--expose', exp]
|
||||
|
@ -960,7 +1044,9 @@ class PodmanModuleParams:
|
|||
return c + ['--gidmap', self.params['gidmap']]
|
||||
|
||||
def addparam_group_add(self, c):
|
||||
return c + ['--group-add', self.params['group_add']]
|
||||
for g in self.params['group_add']:
|
||||
c += ['--group-add', g]
|
||||
return c
|
||||
|
||||
def addparam_healthcheck(self, c):
|
||||
return c + ['--healthcheck', self.params['healthcheck']]
|
||||
|
@ -1010,7 +1096,8 @@ class PodmanModuleParams:
|
|||
|
||||
def addparam_label(self, c):
|
||||
for label in self.params['label'].items():
|
||||
c += ['--label', '='.join(label)]
|
||||
c += ['--label', b'='.join([to_bytes(l, errors='surrogate_or_strict')
|
||||
for l in label])]
|
||||
return c
|
||||
|
||||
def addparam_label_file(self, c):
|
||||
|
@ -1038,7 +1125,7 @@ class PodmanModuleParams:
|
|||
return c + ['--mount', self.params['mount']]
|
||||
|
||||
def addparam_network(self, c):
|
||||
return c + ['--network', self.params['network']]
|
||||
return c + ['--network', ",".join(self.params['network'])]
|
||||
|
||||
def addparam_no_hosts(self, c):
|
||||
return c + ['--no-hosts=%s' % self.params['no_hosts']]
|
||||
|
@ -1085,7 +1172,9 @@ class PodmanModuleParams:
|
|||
return c + ['--rootfs=%s' % self.params['rootfs']]
|
||||
|
||||
def addparam_security_opt(self, c):
|
||||
return c + ['--security-opt', self.params['security_opt']]
|
||||
for secopt in self.params['security_opt']:
|
||||
c += ['--security-opt', secopt]
|
||||
return c
|
||||
|
||||
def addparam_shm_size(self, c):
|
||||
return c + ['--shm-size', self.params['shm_size']]
|
||||
|
@ -1161,6 +1250,436 @@ class PodmanModuleParams:
|
|||
return c + self.params['cmd_args']
|
||||
|
||||
|
||||
class PodmanDefaults:
|
||||
def __init__(self, module, podman_version):
|
||||
self.module = module
|
||||
self.version = podman_version
|
||||
self.defaults = {
|
||||
"blkio_weight": 0,
|
||||
"cgroups": "default",
|
||||
"cgroup_parent": "",
|
||||
"cidfile": "",
|
||||
"cpus": 0.0,
|
||||
"cpu_shares": 0,
|
||||
"cpu_quota": 0,
|
||||
"cpu_period": 0,
|
||||
"cpu_rt_runtime": 0,
|
||||
"cpu_rt_period": 0,
|
||||
"cpuset_cpus": "",
|
||||
"cpuset_mems": "",
|
||||
"detach": True,
|
||||
"device": [],
|
||||
"env_host": False,
|
||||
"etc_hosts": {},
|
||||
"group_add": [],
|
||||
"ipc": "",
|
||||
"kernelmemory": "0",
|
||||
"log_driver": "k8s-file",
|
||||
"memory": "0",
|
||||
"memory_swap": "0",
|
||||
"memory_reservation": "0",
|
||||
# "memory_swappiness": -1,
|
||||
"no_hosts": False,
|
||||
# libpod issue with networks in inspection
|
||||
"network": ["default"],
|
||||
"oom_score_adj": 0,
|
||||
"pid": "",
|
||||
"privileged": False,
|
||||
"rm": False,
|
||||
"security_opt": [],
|
||||
"stop_signal": 15,
|
||||
"tty": False,
|
||||
"user": "",
|
||||
"uts": "",
|
||||
"volume": [],
|
||||
"workdir": "/",
|
||||
}
|
||||
|
||||
def default_dict(self):
|
||||
# make here any changes to self.defaults related to podman version
|
||||
return self.defaults
|
||||
|
||||
|
||||
class PodmanContainerDiff:
|
||||
def __init__(self, module, info, podman_version):
|
||||
self.module = module
|
||||
self.version = podman_version
|
||||
self.default_dict = None
|
||||
self.info = yaml.safe_load(json.dumps(info).lower())
|
||||
self.params = self.defaultize()
|
||||
self.diff = {'before': {}, 'after': {}}
|
||||
self.non_idempotent = {
|
||||
'env_file',
|
||||
'env_host',
|
||||
"ulimit", # Defaults depend on user and platform, impossible to guess
|
||||
}
|
||||
|
||||
def defaultize(self):
|
||||
params_with_defaults = {}
|
||||
self.default_dict = PodmanDefaults(
|
||||
self.module, self.version).default_dict()
|
||||
for p in self.module.params:
|
||||
if self.module.params[p] is None and p in self.default_dict:
|
||||
params_with_defaults[p] = self.default_dict[p]
|
||||
else:
|
||||
params_with_defaults[p] = self.module.params[p]
|
||||
return params_with_defaults
|
||||
|
||||
def _diff_update_and_compare(self, param_name, before, after):
|
||||
if before != after:
|
||||
self.diff['before'].update({param_name: before})
|
||||
self.diff['after'].update({param_name: after})
|
||||
return True
|
||||
return False
|
||||
|
||||
def diffparam_annotation(self):
|
||||
before = self.info['config']['annotations'] or {}
|
||||
after = before.copy()
|
||||
if self.module.params['annotation'] is not None:
|
||||
after.update(self.params['annotation'])
|
||||
return self._diff_update_and_compare('annotation', before, after)
|
||||
|
||||
def diffparam_env_host(self):
|
||||
# It's impossible to get from inspest, recreate it if not default
|
||||
before = False
|
||||
after = self.params['env_host']
|
||||
return self._diff_update_and_compare('env_host', before, after)
|
||||
|
||||
def diffparam_blkio_weight(self):
|
||||
before = self.info['hostconfig']['blkioweight']
|
||||
after = self.params['blkio_weight']
|
||||
return self._diff_update_and_compare('blkio_weight', before, after)
|
||||
|
||||
def diffparam_blkio_weight_device(self):
|
||||
before = self.info['hostconfig']['blkioweightdevice']
|
||||
if before == [] and self.module.params['blkio_weight_device'] is None:
|
||||
after = []
|
||||
else:
|
||||
after = self.params['blkio_weight_device']
|
||||
return self._diff_update_and_compare('blkio_weight_device', before, after)
|
||||
|
||||
def diffparam_cap_add(self):
|
||||
before = self.info['effectivecaps'] or []
|
||||
after = []
|
||||
if self.module.params['cap_add'] is not None:
|
||||
after += ["cap_" + i.lower()
|
||||
for i in self.module.params['cap_add']]
|
||||
after += before
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('cap_add', before, after)
|
||||
|
||||
def diffparam_cap_drop(self):
|
||||
before = self.info['effectivecaps'] or []
|
||||
after = before[:]
|
||||
if self.module.params['cap_drop'] is not None:
|
||||
for c in ["cap_" + i.lower() for i in self.module.params['cap_drop']]:
|
||||
if c in after:
|
||||
after.remove(c)
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('cap_drop', before, after)
|
||||
|
||||
def diffparam_cgroup_parent(self):
|
||||
before = self.info['hostconfig']['cgroupparent']
|
||||
after = self.params['cgroup_parent']
|
||||
return self._diff_update_and_compare('cgroup_parent', before, after)
|
||||
|
||||
def diffparam_cgroups(self):
|
||||
# Cgroups output is not supported in all versions
|
||||
if 'cgroups' in self.info['hostconfig']:
|
||||
before = self.info['hostconfig']['cgroups']
|
||||
after = self.params['cgroups']
|
||||
return self._diff_update_and_compare('cgroups', before, after)
|
||||
return False
|
||||
|
||||
def diffparam_cidfile(self):
|
||||
before = self.info['hostconfig']['containeridfile']
|
||||
after = self.params['cidfile']
|
||||
return self._diff_update_and_compare('cidfile', before, after)
|
||||
|
||||
def diffparam_command(self):
|
||||
before = self.info['config']['cmd']
|
||||
after = self.params['command']
|
||||
if isinstance(after, str):
|
||||
after = [i.lower() for i in after.split()]
|
||||
elif isinstance(after, list):
|
||||
after = [i.lower() for i in after]
|
||||
return self._diff_update_and_compare('command', before, after)
|
||||
|
||||
def diffparam_conmon_pidfile(self):
|
||||
before = self.info['conmonpidfile']
|
||||
if self.module.params['conmon_pidfile'] is None:
|
||||
after = before
|
||||
else:
|
||||
after = self.params['conmon_pidfile']
|
||||
return self._diff_update_and_compare('conmon_pidfile', before, after)
|
||||
|
||||
def diffparam_cpu_period(self):
|
||||
before = self.info['hostconfig']['cpuperiod']
|
||||
after = self.params['cpu_period']
|
||||
return self._diff_update_and_compare('cpu_period', before, after)
|
||||
|
||||
def diffparam_cpu_rt_period(self):
|
||||
before = self.info['hostconfig']['cpurealtimeperiod']
|
||||
after = self.params['cpu_rt_period']
|
||||
return self._diff_update_and_compare('cpu_rt_period', before, after)
|
||||
|
||||
def diffparam_cpu_rt_runtime(self):
|
||||
before = self.info['hostconfig']['cpurealtimeruntime']
|
||||
after = self.params['cpu_rt_runtime']
|
||||
return self._diff_update_and_compare('cpu_rt_runtime', before, after)
|
||||
|
||||
def diffparam_cpu_shares(self):
|
||||
before = self.info['hostconfig']['cpushares']
|
||||
after = self.params['cpu_shares']
|
||||
return self._diff_update_and_compare('cpu_shares', before, after)
|
||||
|
||||
def diffparam_cpus(self):
|
||||
before = int(self.info['hostconfig']['nanocpus']) / 1000000000
|
||||
after = self.params['cpus']
|
||||
return self._diff_update_and_compare('cpus', before, after)
|
||||
|
||||
def diffparam_cpuset_cpus(self):
|
||||
before = self.info['hostconfig']['cpusetcpus']
|
||||
after = self.params['cpuset_cpus']
|
||||
return self._diff_update_and_compare('cpuset_cpus', before, after)
|
||||
|
||||
def diffparam_cpuset_mems(self):
|
||||
before = self.info['hostconfig']['cpusetmems']
|
||||
after = self.params['cpuset_mems']
|
||||
return self._diff_update_and_compare('cpuset_mems', before, after)
|
||||
|
||||
def diffparam_device(self):
|
||||
before = [":".join([i['pathonhost'], i['pathincontainer']])
|
||||
for i in self.info['hostconfig']['devices']]
|
||||
after = [":".join(i.split(":")[:2]) for i in self.params['device']]
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('devices', before, after)
|
||||
|
||||
def diffparam_device_read_bps(self):
|
||||
before = self.info['hostconfig']['blkiodevicereadbps'] or []
|
||||
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
|
||||
after = self.params['device_read_bps'] or []
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('device_read_bps', before, after)
|
||||
|
||||
def diffparam_device_read_iops(self):
|
||||
before = self.info['hostconfig']['blkiodevicereadiops'] or []
|
||||
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
|
||||
after = self.params['device_read_iops'] or []
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('device_read_iops', before, after)
|
||||
|
||||
def diffparam_device_write_bps(self):
|
||||
before = self.info['hostconfig']['blkiodevicewritebps'] or []
|
||||
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
|
||||
after = self.params['device_write_bps'] or []
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('device_write_bps', before, after)
|
||||
|
||||
def diffparam_device_write_iops(self):
|
||||
before = self.info['hostconfig']['blkiodevicewriteiops'] or []
|
||||
before = ["%s:%s" % (i['path'], i['rate']) for i in before]
|
||||
after = self.params['device_write_iops'] or []
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('device_write_iops', before, after)
|
||||
|
||||
# Limited idempotency, it can't guess default values
|
||||
def diffparam_env(self):
|
||||
env_before = self.info['config']['env'] or {}
|
||||
before = {i.split("=")[0]: i.split("=")[1] for i in env_before}
|
||||
after = before.copy()
|
||||
if self.params['env']:
|
||||
after.update({
|
||||
str(k).lower(): str(v).lower()
|
||||
for k, v in self.params['env'].items()
|
||||
})
|
||||
return self._diff_update_and_compare('env', before, after)
|
||||
|
||||
def diffparam_etc_hosts(self):
|
||||
if self.info['hostconfig']['extrahosts']:
|
||||
before = dict([i.split(":") for i in self.info['hostconfig']['extrahosts']])
|
||||
else:
|
||||
before = {}
|
||||
after = self.params['etc_hosts']
|
||||
return self._diff_update_and_compare('etc_hosts', before, after)
|
||||
|
||||
def diffparam_group_add(self):
|
||||
before = self.info['hostconfig']['groupadd']
|
||||
after = self.params['group_add']
|
||||
return self._diff_update_and_compare('group_add', before, after)
|
||||
|
||||
# Because of hostname is random generated, this parameter has partial idempotency only.
|
||||
def diffparam_hostname(self):
|
||||
before = self.info['config']['hostname']
|
||||
after = self.params['hostname'] or before
|
||||
return self._diff_update_and_compare('hostname', before, after)
|
||||
|
||||
def diffparam_image(self):
|
||||
before = self.info['config']['image'].replace(
|
||||
"docker.io/library/", "").replace(
|
||||
"docker.io/", "").replace(
|
||||
":latest", "")
|
||||
after = self.params['image'].replace(
|
||||
"docker.io/library/", "").replace(
|
||||
"docker.io/", "").replace(
|
||||
":latest", "")
|
||||
return self._diff_update_and_compare('image', before, after)
|
||||
|
||||
def diffparam_ipc(self):
|
||||
before = self.info['hostconfig']['ipcmode']
|
||||
after = self.params['ipc']
|
||||
return self._diff_update_and_compare('ipc', before, after)
|
||||
|
||||
def diffparam_label(self):
|
||||
before = self.info['config']['labels'] or {}
|
||||
after = before.copy()
|
||||
if self.params['label']:
|
||||
after.update({
|
||||
str(k).lower(): str(v).lower()
|
||||
for k, v in self.params['label'].items()
|
||||
})
|
||||
return self._diff_update_and_compare('label', before, after)
|
||||
|
||||
def diffparam_log_driver(self):
|
||||
before = self.info['hostconfig']['logconfig']['type']
|
||||
after = self.params['log_driver']
|
||||
return self._diff_update_and_compare('log_driver', before, after)
|
||||
|
||||
# Parameter has limited idempotency, unable to guess the default log_path
|
||||
def diffparam_log_opt(self):
|
||||
before = self.info['logpath']
|
||||
if self.module.params['log_opt'] in [None, '']:
|
||||
after = before
|
||||
else:
|
||||
after = self.params['log_opt'].split("=")[1]
|
||||
return self._diff_update_and_compare('log_opt', before, after)
|
||||
|
||||
def diffparam_memory(self):
|
||||
before = str(self.info['hostconfig']['memory'])
|
||||
after = self.params['memory']
|
||||
return self._diff_update_and_compare('memory', before, after)
|
||||
|
||||
def diffparam_memory_swap(self):
|
||||
# By default it's twice memory parameter
|
||||
before = str(self.info['hostconfig']['memoryswap'])
|
||||
after = self.params['memory_swap']
|
||||
if (self.module.params['memory_swap'] is None
|
||||
and self.params['memory'] != 0
|
||||
and self.params['memory'].isdigit()):
|
||||
after = str(int(self.params['memory']) * 2)
|
||||
return self._diff_update_and_compare('memory_swap', before, after)
|
||||
|
||||
def diffparam_memory_reservation(self):
|
||||
before = str(self.info['hostconfig']['memoryreservation'])
|
||||
after = self.params['memory_reservation']
|
||||
return self._diff_update_and_compare('memory_reservation', before, after)
|
||||
|
||||
def diffparam_network(self):
|
||||
before = [self.info['hostconfig']['networkmode']]
|
||||
after = self.params['network']
|
||||
return self._diff_update_and_compare('network', before, after)
|
||||
|
||||
def diffparam_no_hosts(self):
|
||||
before = not bool(self.info['hostspath'])
|
||||
after = self.params['no_hosts']
|
||||
if self.params['network'] == ['none']:
|
||||
after = True
|
||||
return self._diff_update_and_compare('no_hosts', before, after)
|
||||
|
||||
def diffparam_oom_score_adj(self):
|
||||
before = self.info['hostconfig']['oomscoreadj']
|
||||
after = self.params['oom_score_adj']
|
||||
return self._diff_update_and_compare('oom_score_adj', before, after)
|
||||
|
||||
def diffparam_privileged(self):
|
||||
before = self.info['hostconfig']['privileged']
|
||||
after = self.params['privileged']
|
||||
return self._diff_update_and_compare('privileged', before, after)
|
||||
|
||||
def diffparam_pid(self):
|
||||
before = self.info['hostconfig']['pidmode']
|
||||
after = self.params['pid']
|
||||
return self._diff_update_and_compare('pid', before, after)
|
||||
|
||||
def diffparam_rm(self):
|
||||
before = self.info['hostconfig']['autoremove']
|
||||
after = self.params['rm']
|
||||
return self._diff_update_and_compare('rm', before, after)
|
||||
|
||||
def diffparam_security_opt(self):
|
||||
before = self.info['hostconfig']['securityopt']
|
||||
after = self.params['security_opt']
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('security_opt', before, after)
|
||||
|
||||
def diffparam_stop_signal(self):
|
||||
before = self.info['config']['stopsignal']
|
||||
after = self.params['stop_signal']
|
||||
return self._diff_update_and_compare('stop_signal', before, after)
|
||||
|
||||
def diffparam_tty(self):
|
||||
before = self.info['config']['tty']
|
||||
after = self.params['tty']
|
||||
return self._diff_update_and_compare('tty', before, after)
|
||||
|
||||
def diffparam_user(self):
|
||||
before = self.info['config']['user']
|
||||
if self.module.params['user'] is None and before:
|
||||
after = before
|
||||
else:
|
||||
after = self.params['user']
|
||||
return self._diff_update_and_compare('user', before, after)
|
||||
|
||||
def diffparam_uts(self):
|
||||
before = self.info['hostconfig']['utsmode']
|
||||
after = self.params['uts']
|
||||
return self._diff_update_and_compare('uts', before, after)
|
||||
|
||||
def diffparam_volume(self):
|
||||
before = self.info['mounts']
|
||||
if before:
|
||||
volumes = []
|
||||
for m in before:
|
||||
if m['type'] == 'volume':
|
||||
volumes.append([m['name'], m['destination']])
|
||||
else:
|
||||
volumes.append([m['source'], m['destination']])
|
||||
before = [":".join(v) for v in volumes]
|
||||
# Ignore volumes option for idempotency
|
||||
after = [":".join(v.split(":")[:2]) for v in self.params['volume']]
|
||||
before, after = sorted(list(set(before))), sorted(list(set(after)))
|
||||
return self._diff_update_and_compare('volume', before, after)
|
||||
|
||||
def diffparam_volumes_from(self):
|
||||
before = self.info['hostconfig']['volumesfrom'] or []
|
||||
after = self.params['volumes_from'] or []
|
||||
return self._diff_update_and_compare('volumes_from', before, after)
|
||||
|
||||
def diffparam_workdir(self):
|
||||
before = self.info['config']['workingdir']
|
||||
after = self.params['workdir']
|
||||
return self._diff_update_and_compare('workdir', before, after)
|
||||
|
||||
def is_different(self):
|
||||
diff_func_list = [func for func in dir(self)
|
||||
if callable(getattr(self, func)) and func.startswith(
|
||||
"diffparam")]
|
||||
fail_fast = not bool(self.module._diff)
|
||||
different = False
|
||||
for func_name in diff_func_list:
|
||||
dff_func = getattr(self, func_name)
|
||||
if dff_func():
|
||||
if fail_fast:
|
||||
return True
|
||||
else:
|
||||
different = True
|
||||
# Check non idempotent parameters
|
||||
for p in self.non_idempotent:
|
||||
if self.module.params[p] is not None and self.module.params[p] not in [{}, [], '']:
|
||||
different = True
|
||||
return different
|
||||
|
||||
|
||||
def ensure_image_exists(module, image):
|
||||
"""If image is passed, ensure it exists, if not - pull it or fail.
|
||||
|
||||
|
@ -1205,6 +1724,9 @@ class PodmanContainer:
|
|||
self.name = name
|
||||
self.stdout, self.stderr = '', ''
|
||||
self.info = self.get_info()
|
||||
self.version = self._get_podman_version()
|
||||
self.diff = {}
|
||||
self.actions = []
|
||||
|
||||
@property
|
||||
def exists(self):
|
||||
|
@ -1214,9 +1736,17 @@ class PodmanContainer:
|
|||
@property
|
||||
def different(self):
|
||||
"""Check if container is different."""
|
||||
# TODO(sshnaidm): implement difference calculation between input vars
|
||||
# and current container to understand if we need to recreate it
|
||||
return True
|
||||
diffcheck = PodmanContainerDiff(self.module, self.info, self.version)
|
||||
is_different = diffcheck.is_different()
|
||||
diffs = diffcheck.diff
|
||||
if self.module._diff and is_different and diffs['before'] and diffs['after']:
|
||||
self.diff['before'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['before'].items())]) + "\n"
|
||||
self.diff['after'] = "\n".join(
|
||||
["%s - %s" % (k, v) for k, v in sorted(
|
||||
diffs['after'].items())]) + "\n"
|
||||
return is_different
|
||||
|
||||
@property
|
||||
def running(self):
|
||||
|
@ -1234,6 +1764,13 @@ class PodmanContainer:
|
|||
[self.module.params['executable'], b'container', b'inspect', self.name])
|
||||
return json.loads(out)[0] if rc == 0 else {}
|
||||
|
||||
def _get_podman_version(self):
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'--version'])
|
||||
if rc != 0 or not out or "version" not in out:
|
||||
self.module.fail_json(msg="%s run failed!" % self.module.params['executable'])
|
||||
return out.split("version")[1].strip()
|
||||
|
||||
def _perform_action(self, action):
|
||||
"""Perform action with container.
|
||||
|
||||
|
@ -1241,19 +1778,25 @@ class PodmanContainer:
|
|||
action {str} -- action to perform - start, create, stop, run,
|
||||
delete
|
||||
"""
|
||||
b_command = PodmanModuleParams(action, self.module.params
|
||||
b_command = PodmanModuleParams(action,
|
||||
self.module.params,
|
||||
self.version,
|
||||
self.module,
|
||||
).construct_command_from_params()
|
||||
self.module.log("PODMAN-CONTAINER-DEBUG: " +
|
||||
"%s" % " ".join([to_native(i) for i in b_command]))
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'container'] + b_command,
|
||||
expand_user_and_vars=False)
|
||||
self.stdout = out
|
||||
self.stderr = err
|
||||
if rc != 0:
|
||||
self.module.fail_json(
|
||||
msg="Can't %s container %s" % (action, self.name),
|
||||
stdout=out, stderr=err)
|
||||
full_cmd = " ".join([self.module.params['executable']]
|
||||
+ [to_native(i) for i in b_command])
|
||||
self.module.log("PODMAN-CONTAINER-DEBUG: %s" % full_cmd)
|
||||
self.actions.append(full_cmd)
|
||||
if not self.module.check_mode:
|
||||
rc, out, err = self.module.run_command(
|
||||
[self.module.params['executable'], b'container'] + b_command,
|
||||
expand_user_and_vars=False)
|
||||
self.stdout = out
|
||||
self.stderr = err
|
||||
if rc != 0:
|
||||
self.module.fail_json(
|
||||
msg="Can't %s container %s" % (action, self.name),
|
||||
stdout=out, stderr=err)
|
||||
|
||||
def run(self):
|
||||
"""Run the container."""
|
||||
|
@ -1326,11 +1869,15 @@ class PodmanManager:
|
|||
changed {bool} -- whether any action was performed
|
||||
(default: {True})
|
||||
"""
|
||||
facts = self.container.get_info()
|
||||
facts = self.container.get_info() if changed else self.container.info
|
||||
out, err = self.container.stdout, self.container.stderr
|
||||
self.results.update({'changed': changed, 'container': facts,
|
||||
'ansible_facts': {'podman_container': facts}},
|
||||
stdout=out, stderr=err)
|
||||
'podman_actions': self.container.actions},
|
||||
stdout=out, stderr=err)
|
||||
if self.container.diff:
|
||||
self.results.update({'diff': self.container.diff})
|
||||
if self.module.params['debug']:
|
||||
self.results.update({'podman_version': self.container.version})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def make_started(self):
|
||||
|
@ -1347,7 +1894,7 @@ class PodmanManager:
|
|||
self.results['actions'].append('restarted %s' %
|
||||
self.container.name)
|
||||
self.update_container_result()
|
||||
self.module.exit_json(**self.results)
|
||||
self.update_container_result(changed=False)
|
||||
elif not self.container.exists:
|
||||
self.container.run()
|
||||
self.results['actions'].append('started %s' % self.container.name)
|
||||
|
@ -1365,7 +1912,7 @@ class PodmanManager:
|
|||
def make_stopped(self):
|
||||
"""Run actions if desired state is 'stopped'."""
|
||||
if not self.container.exists and not self.image:
|
||||
self.module.fail_json(msg='Cannot create container when image' +
|
||||
self.module.fail_json(msg='Cannot create container when image'
|
||||
' is not specified!')
|
||||
if not self.container.exists:
|
||||
self.container.create()
|
||||
|
@ -1387,7 +1934,7 @@ class PodmanManager:
|
|||
self.results['actions'].append('deleted %s' % self.container.name)
|
||||
self.results.update({'changed': True})
|
||||
self.results.update({'container': {},
|
||||
'ansible_facts': {'podman_container': {}}})
|
||||
'podman_actions': self.container.actions})
|
||||
self.module.exit_json(**self.results)
|
||||
|
||||
def execute(self):
|
||||
|
@ -1400,7 +1947,7 @@ class PodmanManager:
|
|||
}
|
||||
process_action = states_map[self.state]
|
||||
process_action()
|
||||
self.module.fail_json(msg="Unexpected logic error happened, " +
|
||||
self.module.fail_json(msg="Unexpected logic error happened, "
|
||||
"please contact maintainers ASAP!")
|
||||
|
||||
|
||||
|
@ -1411,6 +1958,7 @@ def main():
|
|||
['no_hosts', 'etc_hosts'],
|
||||
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
# work on input vars
|
||||
if module.params['state'] in ['started', 'present'] and \
|
||||
|
|
|
@ -13,54 +13,43 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import absolute_import, division, print_function
|
||||
__metaclass__ = type
|
||||
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
import ansible.module_utils.six as six
|
||||
|
||||
six.add_metaclass(type)
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.0',
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
||||
DOCUMENTATION = """
|
||||
---
|
||||
module: podman_container_info
|
||||
author:
|
||||
- Sagi Shnaidman (@sshnaidm)
|
||||
- Emilien Macchi (@EmilienM)
|
||||
version_added: '2.9'
|
||||
version_added: '2.10'
|
||||
short_description: Gather facts about containers using podman
|
||||
notes:
|
||||
- Podman may required elevated privileges in order to run properly.
|
||||
- Podman may require elevated privileges in order to run properly.
|
||||
description:
|
||||
- Gather facts about containers using C(podman)
|
||||
requirements:
|
||||
- "Podman installed on host"
|
||||
options:
|
||||
executable:
|
||||
description:
|
||||
- Path to C(podman) executable if it is not in the C($PATH) on the
|
||||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- List of tags or UID to gather facts about. If no name is given
|
||||
return facts about all containers.
|
||||
type: list
|
||||
elements: str
|
||||
|
||||
name:
|
||||
description:
|
||||
- List of container names to gather facts about. If no name is given
|
||||
return facts about all containers.
|
||||
type: list
|
||||
elements: str
|
||||
executable:
|
||||
description:
|
||||
- Path to C(podman) executable if it is not in the C($PATH) on the
|
||||
machine running C(podman)
|
||||
default: 'podman'
|
||||
type: str
|
||||
"""
|
||||
|
||||
EXAMPLES = """
|
||||
- name: Gather facts for all containers
|
||||
podman_container_info:
|
||||
|
@ -75,273 +64,274 @@ EXAMPLES = """
|
|||
- redis
|
||||
- web1
|
||||
"""
|
||||
|
||||
RETURN = """
|
||||
containers:
|
||||
description: Facts from all or specificed containers
|
||||
returned: always
|
||||
type: dict
|
||||
type: list
|
||||
sample: [
|
||||
{
|
||||
"Id": "c5c39f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f",
|
||||
"Created": "2019-10-01T12:51:00.233106443Z",
|
||||
"Path": "dumb-init",
|
||||
"Args": [
|
||||
"--single-child",
|
||||
"--",
|
||||
"kolla_start"
|
||||
],
|
||||
"State": {
|
||||
"OciVersion": "1.0.1-dev",
|
||||
"Status": "configured",
|
||||
"Running": false,
|
||||
"Paused": false,
|
||||
"Restarting": false,
|
||||
"OOMKilled": false,
|
||||
"Dead": false,
|
||||
"Pid": 0,
|
||||
"ExitCode": 0,
|
||||
"Error": "",
|
||||
"StartedAt": "0001-01-01T00:00:00Z",
|
||||
"FinishedAt": "0001-01-01T00:00:00Z",
|
||||
"Healthcheck": {
|
||||
"Status": "",
|
||||
"FailingStreak": 0,
|
||||
"Log": null
|
||||
}
|
||||
},
|
||||
"Image": "0e267acda67d0ebd643e900d820a91b961d859743039e620191ca1",
|
||||
"ImageName": "docker.io/tripleomaster/centos-haproxy:latest",
|
||||
"Rootfs": "",
|
||||
"Pod": "",
|
||||
"ResolvConfPath": "",
|
||||
"HostnamePath": "",
|
||||
"HostsPath": "",
|
||||
"OCIRuntime": "runc",
|
||||
"Name": "haproxy",
|
||||
"RestartCount": 0,
|
||||
"Driver": "overlay",
|
||||
"MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c78,c866",
|
||||
"ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c785,c866",
|
||||
"AppArmorProfile": "",
|
||||
"EffectiveCaps": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE"
|
||||
],
|
||||
"BoundingCaps": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE"
|
||||
],
|
||||
"ExecIDs": [],
|
||||
"GraphDriver": {
|
||||
"Name": "overlay",
|
||||
}
|
||||
},
|
||||
"Mounts": [],
|
||||
"Dependencies": [],
|
||||
"NetworkSettings": {
|
||||
"Bridge": "",
|
||||
"SandboxID": "",
|
||||
"HairpinMode": false,
|
||||
"LinkLocalIPv6Address": "",
|
||||
"LinkLocalIPv6PrefixLen": 0,
|
||||
"Ports": [],
|
||||
"SandboxKey": "",
|
||||
"SecondaryIPAddresses": null,
|
||||
"SecondaryIPv6Addresses": null,
|
||||
"EndpointID": "",
|
||||
"Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAddress": "",
|
||||
"IPPrefixLen": 0,
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": ""
|
||||
},
|
||||
"ExitCommand": [
|
||||
"/usr/bin/podman",
|
||||
"--root",
|
||||
"/var/lib/containers/storage",
|
||||
"--runroot",
|
||||
"/var/run/containers/storage",
|
||||
"--log-level",
|
||||
"error",
|
||||
"--cgroup-manager",
|
||||
"systemd",
|
||||
"--tmpdir",
|
||||
"/var/run/libpod",
|
||||
"--runtime",
|
||||
"runc",
|
||||
"--storage-driver",
|
||||
"overlay",
|
||||
"--events-backend",
|
||||
"journald",
|
||||
"container",
|
||||
"cleanup",
|
||||
"c9e813703f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f"
|
||||
],
|
||||
"Namespace": "",
|
||||
"IsInfra": false,
|
||||
"Config": {
|
||||
"Hostname": "c5c39e813703",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": false,
|
||||
"AttachStderr": false,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": [
|
||||
"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"TERM=xterm",
|
||||
"HOSTNAME=",
|
||||
"container=oci",
|
||||
"KOLLA_INSTALL_METATYPE=rdo",
|
||||
"KOLLA_BASE_DISTRO=centos",
|
||||
"KOLLA_INSTALL_TYPE=binary",
|
||||
"KOLLA_DISTRO_PYTHON_VERSION=2.7",
|
||||
"KOLLA_BASE_ARCH=x86_64"
|
||||
],
|
||||
"Cmd": [
|
||||
{
|
||||
"Id": "c5c39f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f",
|
||||
"Created": "2019-10-01T12:51:00.233106443Z",
|
||||
"Path": "dumb-init",
|
||||
"Args": [
|
||||
"--single-child",
|
||||
"--",
|
||||
"kolla_start"
|
||||
],
|
||||
"Image": "docker.io/tripleomaster/centos-haproxy:latest",
|
||||
"Volumes": null,
|
||||
"WorkingDir": "/",
|
||||
"Entrypoint": "dumb-init --single-child --",
|
||||
"OnBuild": null,
|
||||
"Labels": {
|
||||
"build-date": "20190919",
|
||||
"kolla_version": "8.1.0",
|
||||
"name": "haproxy",
|
||||
"org.label-schema.build-date": "20190801",
|
||||
"org.label-schema.license": "GPLv2",
|
||||
"org.label-schema.name": "CentOS Base Image",
|
||||
"org.label-schema.schema-version": "1.0",
|
||||
"org.label-schema.vendor": "CentOS"
|
||||
},
|
||||
"Annotations": {
|
||||
"io.kubernetes.cri-o.ContainerType": "sandbox",
|
||||
"io.kubernetes.cri-o.TTY": "false",
|
||||
"io.podman.annotations.autoremove": "FALSE",
|
||||
"io.podman.annotations.init": "FALSE",
|
||||
"io.podman.annotations.privileged": "FALSE",
|
||||
"io.podman.annotations.publish-all": "FALSE"
|
||||
},
|
||||
"StopSignal": 15
|
||||
},
|
||||
"HostConfig": {
|
||||
"Binds": [],
|
||||
"ContainerIDFile": "",
|
||||
"LogConfig": {
|
||||
"Type": "k8s-file",
|
||||
"Config": null
|
||||
},
|
||||
"NetworkMode": "default",
|
||||
"PortBindings": {},
|
||||
"RestartPolicy": {
|
||||
"Name": "",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
"AutoRemove": false,
|
||||
"VolumeDriver": "",
|
||||
"VolumesFrom": null,
|
||||
"CapAdd": [],
|
||||
"CapDrop": [],
|
||||
"Dns": [],
|
||||
"DnsOptions": [],
|
||||
"DnsSearch": [],
|
||||
"ExtraHosts": [],
|
||||
"GroupAdd": [],
|
||||
"IpcMode": "",
|
||||
"Cgroup": "",
|
||||
"Links": null,
|
||||
"OomScoreAdj": 0,
|
||||
"PidMode": "",
|
||||
"Privileged": false,
|
||||
"PublishAllPorts": false,
|
||||
"ReadonlyRootfs": false,
|
||||
"SecurityOpt": [],
|
||||
"Tmpfs": {},
|
||||
"UTSMode": "",
|
||||
"UsernsMode": "",
|
||||
"ShmSize": 65536000,
|
||||
"Runtime": "oci",
|
||||
"ConsoleSize": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Isolation": "",
|
||||
"CpuShares": 0,
|
||||
"Memory": 0,
|
||||
"NanoCpus": 0,
|
||||
"CgroupParent": "",
|
||||
"BlkioWeight": 0,
|
||||
"BlkioWeightDevice": null,
|
||||
"BlkioDeviceReadBps": null,
|
||||
"BlkioDeviceWriteBps": null,
|
||||
"BlkioDeviceReadIOps": null,
|
||||
"BlkioDeviceWriteIOps": null,
|
||||
"CpuPeriod": 0,
|
||||
"CpuQuota": 0,
|
||||
"CpuRealtimePeriod": 0,
|
||||
"CpuRealtimeRuntime": 0,
|
||||
"CpusetCpus": "",
|
||||
"CpusetMems": "",
|
||||
"Devices": [],
|
||||
"DiskQuota": 0,
|
||||
"KernelMemory": 0,
|
||||
"MemoryReservation": 0,
|
||||
"MemorySwap": 0,
|
||||
"MemorySwappiness": -1,
|
||||
"OomKillDisable": false,
|
||||
"PidsLimit": 0,
|
||||
"Ulimits": [
|
||||
{
|
||||
"Name": "RLIMIT_NOFILE",
|
||||
"Soft": 1048576,
|
||||
"Hard": 1048576
|
||||
},
|
||||
{
|
||||
"Name": "RLIMIT_NPROC",
|
||||
"Soft": 1048576,
|
||||
"Hard": 1048576
|
||||
"State": {
|
||||
"OciVersion": "1.0.1-dev",
|
||||
"Status": "configured",
|
||||
"Running": false,
|
||||
"Paused": false,
|
||||
"Restarting": false,
|
||||
"OOMKilled": false,
|
||||
"Dead": false,
|
||||
"Pid": 0,
|
||||
"ExitCode": 0,
|
||||
"Error": "",
|
||||
"StartedAt": "0001-01-01T00:00:00Z",
|
||||
"FinishedAt": "0001-01-01T00:00:00Z",
|
||||
"Healthcheck": {
|
||||
"Status": "",
|
||||
"FailingStreak": 0,
|
||||
"Log": null
|
||||
}
|
||||
},
|
||||
"Image": "0e267acda67d0ebd643e900d820a91b961d859743039e620191ca1",
|
||||
"ImageName": "docker.io/tripleomaster/centos-haproxy:latest",
|
||||
"Rootfs": "",
|
||||
"Pod": "",
|
||||
"ResolvConfPath": "",
|
||||
"HostnamePath": "",
|
||||
"HostsPath": "",
|
||||
"OCIRuntime": "runc",
|
||||
"Name": "haproxy",
|
||||
"RestartCount": 0,
|
||||
"Driver": "overlay",
|
||||
"MountLabel": "system_u:object_r:svirt_sandbox_file_t:s0:c78,c866",
|
||||
"ProcessLabel": "system_u:system_r:svirt_lxc_net_t:s0:c785,c866",
|
||||
"AppArmorProfile": "",
|
||||
"EffectiveCaps": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE"
|
||||
],
|
||||
"CpuCount": 0,
|
||||
"CpuPercent": 0,
|
||||
"IOMaximumIOps": 0,
|
||||
"IOMaximumBandwidth": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
"BoundingCaps": [
|
||||
"CAP_CHOWN",
|
||||
"CAP_DAC_OVERRIDE",
|
||||
"CAP_FSETID",
|
||||
"CAP_FOWNER",
|
||||
"CAP_MKNOD",
|
||||
"CAP_NET_RAW",
|
||||
"CAP_SETGID",
|
||||
"CAP_SETUID",
|
||||
"CAP_SETFCAP",
|
||||
"CAP_SETPCAP",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
"CAP_SYS_CHROOT",
|
||||
"CAP_KILL",
|
||||
"CAP_AUDIT_WRITE"
|
||||
],
|
||||
"ExecIDs": [],
|
||||
"GraphDriver": {
|
||||
"Name": "overlay"
|
||||
},
|
||||
"Mounts": [],
|
||||
"Dependencies": [],
|
||||
"NetworkSettings": {
|
||||
"Bridge": "",
|
||||
"SandboxID": "",
|
||||
"HairpinMode": false,
|
||||
"LinkLocalIPv6Address": "",
|
||||
"LinkLocalIPv6PrefixLen": 0,
|
||||
"Ports": [],
|
||||
"SandboxKey": "",
|
||||
"SecondaryIPAddresses": null,
|
||||
"SecondaryIPv6Addresses": null,
|
||||
"EndpointID": "",
|
||||
"Gateway": "",
|
||||
"GlobalIPv6Address": "",
|
||||
"GlobalIPv6PrefixLen": 0,
|
||||
"IPAddress": "",
|
||||
"IPPrefixLen": 0,
|
||||
"IPv6Gateway": "",
|
||||
"MacAddress": ""
|
||||
},
|
||||
"ExitCommand": [
|
||||
"/usr/bin/podman",
|
||||
"--root",
|
||||
"/var/lib/containers/storage",
|
||||
"--runroot",
|
||||
"/var/run/containers/storage",
|
||||
"--log-level",
|
||||
"error",
|
||||
"--cgroup-manager",
|
||||
"systemd",
|
||||
"--tmpdir",
|
||||
"/var/run/libpod",
|
||||
"--runtime",
|
||||
"runc",
|
||||
"--storage-driver",
|
||||
"overlay",
|
||||
"--events-backend",
|
||||
"journald",
|
||||
"container",
|
||||
"cleanup",
|
||||
"c9e813703f9b80a6ea2ad665aa9946435934e478a0c5322da835f3883872f"
|
||||
],
|
||||
"Namespace": "",
|
||||
"IsInfra": false,
|
||||
"Config": {
|
||||
"Hostname": "c5c39e813703",
|
||||
"Domainname": "",
|
||||
"User": "",
|
||||
"AttachStdin": false,
|
||||
"AttachStdout": false,
|
||||
"AttachStderr": false,
|
||||
"Tty": false,
|
||||
"OpenStdin": false,
|
||||
"StdinOnce": false,
|
||||
"Env": [
|
||||
"PATH=/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"TERM=xterm",
|
||||
"HOSTNAME=",
|
||||
"container=oci",
|
||||
"KOLLA_INSTALL_METATYPE=rdo",
|
||||
"KOLLA_BASE_DISTRO=centos",
|
||||
"KOLLA_INSTALL_TYPE=binary",
|
||||
"KOLLA_DISTRO_PYTHON_VERSION=2.7",
|
||||
"KOLLA_BASE_ARCH=x86_64"
|
||||
],
|
||||
"Cmd": [
|
||||
"kolla_start"
|
||||
],
|
||||
"Image": "docker.io/tripleomaster/centos-haproxy:latest",
|
||||
"Volumes": null,
|
||||
"WorkingDir": "/",
|
||||
"Entrypoint": "dumb-init --single-child --",
|
||||
"OnBuild": null,
|
||||
"Labels": {
|
||||
"build-date": "20190919",
|
||||
"kolla_version": "8.1.0",
|
||||
"name": "haproxy",
|
||||
"org.label-schema.build-date": "20190801",
|
||||
"org.label-schema.license": "GPLv2",
|
||||
"org.label-schema.name": "CentOS Base Image",
|
||||
"org.label-schema.schema-version": "1.0",
|
||||
"org.label-schema.vendor": "CentOS"
|
||||
},
|
||||
"Annotations": {
|
||||
"io.kubernetes.cri-o.ContainerType": "sandbox",
|
||||
"io.kubernetes.cri-o.TTY": "false",
|
||||
"io.podman.annotations.autoremove": "FALSE",
|
||||
"io.podman.annotations.init": "FALSE",
|
||||
"io.podman.annotations.privileged": "FALSE",
|
||||
"io.podman.annotations.publish-all": "FALSE"
|
||||
},
|
||||
"StopSignal": 15
|
||||
},
|
||||
"HostConfig": {
|
||||
"Binds": [],
|
||||
"ContainerIDFile": "",
|
||||
"LogConfig": {
|
||||
"Type": "k8s-file",
|
||||
"Config": null
|
||||
},
|
||||
"NetworkMode": "default",
|
||||
"PortBindings": {},
|
||||
"RestartPolicy": {
|
||||
"Name": "",
|
||||
"MaximumRetryCount": 0
|
||||
},
|
||||
"AutoRemove": false,
|
||||
"VolumeDriver": "",
|
||||
"VolumesFrom": null,
|
||||
"CapAdd": [],
|
||||
"CapDrop": [],
|
||||
"Dns": [],
|
||||
"DnsOptions": [],
|
||||
"DnsSearch": [],
|
||||
"ExtraHosts": [],
|
||||
"GroupAdd": [],
|
||||
"IpcMode": "",
|
||||
"Cgroup": "",
|
||||
"Links": null,
|
||||
"OomScoreAdj": 0,
|
||||
"PidMode": "",
|
||||
"Privileged": false,
|
||||
"PublishAllPorts": false,
|
||||
"ReadonlyRootfs": false,
|
||||
"SecurityOpt": [],
|
||||
"Tmpfs": {},
|
||||
"UTSMode": "",
|
||||
"UsernsMode": "",
|
||||
"ShmSize": 65536000,
|
||||
"Runtime": "oci",
|
||||
"ConsoleSize": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"Isolation": "",
|
||||
"CpuShares": 0,
|
||||
"Memory": 0,
|
||||
"NanoCpus": 0,
|
||||
"CgroupParent": "",
|
||||
"BlkioWeight": 0,
|
||||
"BlkioWeightDevice": null,
|
||||
"BlkioDeviceReadBps": null,
|
||||
"BlkioDeviceWriteBps": null,
|
||||
"BlkioDeviceReadIOps": null,
|
||||
"BlkioDeviceWriteIOps": null,
|
||||
"CpuPeriod": 0,
|
||||
"CpuQuota": 0,
|
||||
"CpuRealtimePeriod": 0,
|
||||
"CpuRealtimeRuntime": 0,
|
||||
"CpusetCpus": "",
|
||||
"CpusetMems": "",
|
||||
"Devices": [],
|
||||
"DiskQuota": 0,
|
||||
"KernelMemory": 0,
|
||||
"MemoryReservation": 0,
|
||||
"MemorySwap": 0,
|
||||
"MemorySwappiness": -1,
|
||||
"OomKillDisable": false,
|
||||
"PidsLimit": 0,
|
||||
"Ulimits": [
|
||||
{
|
||||
"Name": "RLIMIT_NOFILE",
|
||||
"Soft": 1048576,
|
||||
"Hard": 1048576
|
||||
},
|
||||
{
|
||||
"Name": "RLIMIT_NPROC",
|
||||
"Soft": 1048576,
|
||||
"Hard": 1048576
|
||||
}
|
||||
],
|
||||
"CpuCount": 0,
|
||||
"CpuPercent": 0,
|
||||
"IOMaximumIOps": 0,
|
||||
"IOMaximumBandwidth": 0
|
||||
}
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
import json
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def get_containers_facts(module, executable, name):
|
||||
if not name:
|
||||
|
@ -353,30 +343,32 @@ def get_containers_facts(module, executable, name):
|
|||
command = [executable, 'container', 'inspect']
|
||||
command.extend(name)
|
||||
rc, out, err = module.run_command(command)
|
||||
if not out or rc != 0:
|
||||
if rc != 0:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all containers', err))
|
||||
if not out or json.loads(out) is None:
|
||||
return [], out, err
|
||||
return json.loads(out), out, err
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='list', elements='str')
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.params['executable']
|
||||
name = module.params.get('name')
|
||||
executable = module.get_bin_path(executable, required=True)
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
|
||||
inspect_results, out, err = get_containers_facts(module, executable, name)
|
||||
|
||||
results = dict(
|
||||
changed=False,
|
||||
containers=inspect_results,
|
||||
ansible_facts=dict(podman_containers=inspect_results),
|
||||
stdout=out,
|
||||
stderr=err
|
||||
)
|
||||
results = {
|
||||
"changed": False,
|
||||
"containers": inspect_results,
|
||||
"stderr": err
|
||||
}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
|
|
@ -15,17 +15,11 @@
|
|||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import json
|
||||
import yaml
|
||||
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
ANSIBLE_METADATA = {
|
||||
'metadata_version': '1.0',
|
||||
'metadata_version': '1.1',
|
||||
'status': ['preview'],
|
||||
'supported_by': 'community'
|
||||
}
|
||||
|
@ -34,7 +28,7 @@ DOCUMENTATION = """
|
|||
module: podman_volume_info
|
||||
author:
|
||||
- "Sagi Shnaidman (@sshnaidm)"
|
||||
version_added: '2.9'
|
||||
version_added: '2.10'
|
||||
short_description: Gather info about podman volumes
|
||||
notes: []
|
||||
description:
|
||||
|
@ -65,21 +59,22 @@ RETURN = """
|
|||
volumes:
|
||||
description: Facts from all or specified volumes
|
||||
returned: always
|
||||
type: dict
|
||||
sample:
|
||||
[
|
||||
{
|
||||
"name": "testvolume",
|
||||
"labels": {},
|
||||
"mountPoint": "/home/ansible/.local/share/testvolume/_data",
|
||||
"driver": "local",
|
||||
"options": {},
|
||||
"scope": "local"
|
||||
}
|
||||
]
|
||||
|
||||
type: list
|
||||
sample: [
|
||||
{
|
||||
"name": "testvolume",
|
||||
"labels": {},
|
||||
"mountPoint": "/home/ansible/.local/share/testvolume/_data",
|
||||
"driver": "local",
|
||||
"options": {},
|
||||
"scope": "local"
|
||||
}
|
||||
]
|
||||
"""
|
||||
|
||||
import json
|
||||
from ansible.module_utils.basic import AnsibleModule
|
||||
|
||||
|
||||
def get_volume_info(module, executable, name):
|
||||
command = [executable, 'volume', 'inspect']
|
||||
|
@ -88,31 +83,32 @@ def get_volume_info(module, executable, name):
|
|||
else:
|
||||
command.append("--all")
|
||||
rc, out, err = module.run_command(command)
|
||||
if not out or rc != 0:
|
||||
if rc != 0 or 'no such volume' in err:
|
||||
module.fail_json(msg="Unable to gather info for %s: %s" % (name or 'all volumes', err))
|
||||
if not out or json.loads(out) is None:
|
||||
return [], out, err
|
||||
return json.loads(out), out, err
|
||||
|
||||
|
||||
def main():
|
||||
module = AnsibleModule(
|
||||
argument_spec=yaml.safe_load(DOCUMENTATION)['options'],
|
||||
argument_spec=dict(
|
||||
executable=dict(type='str', default='podman'),
|
||||
name=dict(type='str')
|
||||
),
|
||||
supports_check_mode=True,
|
||||
)
|
||||
|
||||
executable = module.params['executable']
|
||||
name = module.params['name']
|
||||
executable = module.get_bin_path(executable, required=True)
|
||||
executable = module.get_bin_path(module.params['executable'], required=True)
|
||||
|
||||
inspect_results, out, err = get_volume_info(module, executable, name)
|
||||
|
||||
results = dict(
|
||||
changed=False,
|
||||
volume=inspect_results,
|
||||
stdout=out,
|
||||
stderr=err
|
||||
)
|
||||
if name:
|
||||
results.update({"exists": bool(inspect_results)})
|
||||
results = {
|
||||
"changed": False,
|
||||
"volumes": inspect_results,
|
||||
"stderr": err
|
||||
}
|
||||
|
||||
module.exit_json(**results)
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
# Based on ansible-lint config
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
braces:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
brackets:
|
||||
max-spaces-inside: 1
|
||||
level: error
|
||||
colons:
|
||||
max-spaces-after: -1
|
||||
level: error
|
||||
commas:
|
||||
max-spaces-after: -1
|
||||
level: error
|
||||
comments: disable
|
||||
comments-indentation: disable
|
||||
document-start: disable
|
||||
empty-lines:
|
||||
max: 3
|
||||
level: error
|
||||
hyphens:
|
||||
level: error
|
||||
indentation: disable
|
||||
key-duplicates: enable
|
||||
line-length: disable
|
||||
new-line-at-end-of-file: disable
|
||||
new-lines:
|
||||
type: unix
|
||||
trailing-spaces: disable
|
||||
truthy: disable
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
driver:
|
||||
name: delegated
|
||||
options:
|
||||
managed: false
|
||||
login_cmd_template: >-
|
||||
ssh
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o Compression=no
|
||||
-o TCPKeepAlive=yes
|
||||
-o VerifyHostKeyDNS=no
|
||||
-o ForwardX11=no
|
||||
-o ForwardAgent=no
|
||||
{instance}
|
||||
ansible_connection_options:
|
||||
ansible_connection: ssh
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: instance
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
fact_caching: jsonfile
|
||||
fact_caching_connection: /tmp/molecule/facts
|
||||
inventory:
|
||||
hosts:
|
||||
all:
|
||||
hosts:
|
||||
instance:
|
||||
ansible_host: localhost
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_ROLES_PATH: "${ANSIBLE_ROLES_PATH:-/usr/share/ansible/roles}:${HOME}/zuul-jobs/roles"
|
||||
ANSIBLE_LIBRARY: "${ANSIBLE_LIBRARY:-/usr/share/ansible/plugins/modules}"
|
||||
ANSIBLE_FILTER_PLUGINS: "${ANSIBLE_FILTER_PLUGINS:-/usr/share/ansible/plugins/filter}"
|
||||
|
||||
scenario:
|
||||
name: podman_container
|
||||
test_sequence:
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
|
||||
lint:
|
||||
enabled: false
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
|
@ -0,0 +1,393 @@
|
|||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Test podman_container
|
||||
become: true
|
||||
block:
|
||||
- name: Delete all container leftovers from tests
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "alpine:3.7"
|
||||
- "container"
|
||||
- "container2"
|
||||
|
||||
- name: Test no image with default action
|
||||
podman_container:
|
||||
name: container
|
||||
ignore_errors: true
|
||||
register: no_image
|
||||
|
||||
- name: Test no image with state 'started'
|
||||
podman_container:
|
||||
name: container
|
||||
state: started
|
||||
ignore_errors: true
|
||||
register: no_image1
|
||||
|
||||
- name: Test no image with state 'present'
|
||||
podman_container:
|
||||
name: container
|
||||
state: present
|
||||
ignore_errors: true
|
||||
register: no_image2
|
||||
|
||||
- name: Check no image
|
||||
assert:
|
||||
that:
|
||||
- no_image is failed
|
||||
- no_image1 is failed
|
||||
- no_image2 is failed
|
||||
- no_image.msg == "State 'started' required image to be configured!"
|
||||
- no_image1.msg == "State 'started' required image to be configured!"
|
||||
- no_image2.msg == "State 'present' required image to be configured!"
|
||||
fail_msg: No image test failed!
|
||||
success_msg: No image test passed!
|
||||
|
||||
- name: Ensure image doesn't exist
|
||||
podman_image:
|
||||
name: alpine:3.7
|
||||
state: absent
|
||||
|
||||
- name: Check pulling image
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: image
|
||||
|
||||
- name: Check using already pulled image
|
||||
podman_container:
|
||||
name: container2
|
||||
image: alpine:3.7
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: image2
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- image is changed
|
||||
- image.container is defined
|
||||
- image.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' in image.actions"
|
||||
- "'started container' in image.actions"
|
||||
- image2 is changed
|
||||
- image2.container is defined
|
||||
- image2.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' not in image2.actions"
|
||||
- "'started container2' in image2.actions"
|
||||
fail_msg: Pulling image test failed!
|
||||
success_msg: Pulling image test passed!
|
||||
|
||||
- name: Check failed image pull
|
||||
podman_container:
|
||||
name: container
|
||||
image: ineverneverneverexist
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: imagefail
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- imagefail is failed
|
||||
- imagefail.msg == "Can't pull image ineverneverneverexist"
|
||||
|
||||
|
||||
- name: Force container recreate
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 1d
|
||||
recreate: true
|
||||
register: recreated
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- recreated is changed
|
||||
- recreated.container is defined
|
||||
- recreated.container['State']['Running']
|
||||
- "'recreated container' in recreated.actions"
|
||||
fail_msg: Force recreate test failed!
|
||||
success_msg: Force recreate test passed!
|
||||
|
||||
- name: Stop container
|
||||
podman_container:
|
||||
name: container
|
||||
state: stopped
|
||||
register: stopped
|
||||
|
||||
- name: Stop the same container again (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: stopped
|
||||
register: stopped_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- stopped is changed
|
||||
- stopped.container is defined
|
||||
- not stopped.container['State']['Running']
|
||||
- "'stopped container' in stopped.actions"
|
||||
- stopped_again is not changed
|
||||
- stopped_again.container is defined
|
||||
- not stopped_again.container['State']['Running']
|
||||
- stopped_again.actions == []
|
||||
fail_msg: Stopping container test failed!
|
||||
success_msg: Stopping container test passed!
|
||||
|
||||
- name: Delete stopped container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted
|
||||
|
||||
- name: Delete again container (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- deleted is changed
|
||||
- deleted.container is defined
|
||||
- deleted.container == {}
|
||||
- "'deleted container' in deleted.actions"
|
||||
- deleted_again is not changed
|
||||
- deleted_again.container is defined
|
||||
- deleted_again.container == {}
|
||||
- deleted_again.actions == []
|
||||
fail_msg: Deleting stopped container test failed!
|
||||
success_msg: Deleting stopped container test passed!
|
||||
|
||||
- name: Create container, but don't run
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: stopped
|
||||
command: sleep 1d
|
||||
register: created
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- created is changed
|
||||
- created.container is defined
|
||||
- created.container != {}
|
||||
- not created.container['State']['Running']
|
||||
- "'created container' in created.actions"
|
||||
fail_msg: "Creating stopped container test failed!"
|
||||
success_msg: "Creating stopped container test passed!"
|
||||
|
||||
- name: Delete created container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
|
||||
- name: Start container that was deleted
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: started
|
||||
command: sleep 1d
|
||||
register: started
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- started is changed
|
||||
- started.container is defined
|
||||
- started.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' not in started.actions"
|
||||
|
||||
- name: Delete started container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted
|
||||
|
||||
- name: Delete again container (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- deleted is changed
|
||||
- deleted.container is defined
|
||||
- deleted.container == {}
|
||||
- "'deleted container' in deleted.actions"
|
||||
- deleted_again is not changed
|
||||
- deleted_again.container is defined
|
||||
- deleted_again.container == {}
|
||||
- deleted_again.actions == []
|
||||
fail_msg: Deleting started container test failed!
|
||||
success_msg: Deleting started container test passed!
|
||||
|
||||
- name: Recreate container with parameters
|
||||
podman_container:
|
||||
name: container
|
||||
image: docker.io/alpine:3.7
|
||||
state: started
|
||||
command: sleep 1d
|
||||
recreate: true
|
||||
etc_hosts:
|
||||
host1: 127.0.0.1
|
||||
host2: 127.0.0.1
|
||||
annotation:
|
||||
this: "annotation_value"
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.4.4
|
||||
dns_search: example.com
|
||||
cap_add:
|
||||
- SYS_TIME
|
||||
- NET_ADMIN
|
||||
publish:
|
||||
- "9000:80"
|
||||
- "9001:8000"
|
||||
workdir: "/bin"
|
||||
env:
|
||||
FOO: bar
|
||||
BAR: foo
|
||||
TEST: 1
|
||||
BOOL: false
|
||||
group_add: "somegroup"
|
||||
label:
|
||||
somelabel: labelvalue
|
||||
otheralbe: othervalue
|
||||
volumes:
|
||||
- /tmp:/data
|
||||
register: test
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- test is changed
|
||||
- test.container is defined
|
||||
- test.container != {}
|
||||
- test.container['State']['Running']
|
||||
# test capabilities
|
||||
- "'CAP_SYS_TIME' in test.container['BoundingCaps']"
|
||||
- "'CAP_NET_ADMIN' in test.container['BoundingCaps']"
|
||||
# test annotations
|
||||
- test.container['Config']['Annotations']['this'] is defined
|
||||
- test.container['Config']['Annotations']['this'] == "annotation_value"
|
||||
# test DNS
|
||||
- >-
|
||||
(test.container['HostConfig']['Dns'] is defined and
|
||||
test.container['HostConfig']['Dns'] == ['1.1.1.1', '8.8.4.4']) or
|
||||
(test.container['HostConfig']['DNS'] is defined and
|
||||
test.container['HostConfig']['DNS'] == ['1.1.1.1', '8.8.4.4'])
|
||||
# test ports
|
||||
- test.container['NetworkSettings']['Ports']|length == 2
|
||||
# test working dir
|
||||
- test.container['Config']['WorkingDir'] == "/bin"
|
||||
# test dns search
|
||||
- >-
|
||||
(test.container['HostConfig']['DnsSearch'] is defined and
|
||||
test.container['HostConfig']['DnsSearch'] == ['example.com']) or
|
||||
(test.container['HostConfig']['DNSSearch'] is defined and
|
||||
test.container['HostConfig']['DNSSearch'] == ['example.com'])
|
||||
# test environment variables
|
||||
- "'FOO=bar' in test.container['Config']['Env']"
|
||||
- "'BAR=foo' in test.container['Config']['Env']"
|
||||
- "'TEST=1' in test.container['Config']['Env']"
|
||||
- "'BOOL=False' in test.container['Config']['Env']"
|
||||
# test labels
|
||||
- test.container['Config']['Labels'] | length == 2
|
||||
- test.container['Config']['Labels']['somelabel'] == "labelvalue"
|
||||
- test.container['Config']['Labels']['otheralbe'] == "othervalue"
|
||||
# test mounts
|
||||
- >-
|
||||
(test.container['Mounts'][0]['Destination'] is defined and
|
||||
'/data' in test.container['Mounts'] | map(attribute='Destination') | list) or
|
||||
(test.container['Mounts'][0]['destination'] is defined and
|
||||
'/data' in test.container['Mounts'] | map(attribute='destination') | list)
|
||||
- >-
|
||||
(test.container['Mounts'][0]['Source'] is defined and
|
||||
'/tmp' in test.container['Mounts'] | map(attribute='Source') | list) or
|
||||
(test.container['Mounts'][0]['source'] is defined and
|
||||
'/tmp' in test.container['Mounts'] | map(attribute='source') | list)
|
||||
fail_msg: Parameters container test failed!
|
||||
success_msg: Parameters container test passed!
|
||||
|
||||
- name: Check basic idempotency of running container
|
||||
podman_container:
|
||||
name: testidem
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 20m
|
||||
|
||||
- name: Check basic idempotency of running container - run it again
|
||||
podman_container:
|
||||
name: testidem
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 20m
|
||||
register: idem
|
||||
|
||||
- name: Check that nothing was changed
|
||||
assert:
|
||||
that:
|
||||
- not idem.changed
|
||||
|
||||
- name: Run changed container (with tty enabled)
|
||||
podman_container:
|
||||
name: testidem
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 20m
|
||||
tty: true
|
||||
register: idem1
|
||||
|
||||
- name: Check that container is recreated when changed
|
||||
assert:
|
||||
that:
|
||||
- idem1 is changed
|
||||
|
||||
- name: Run changed container without specifying an option, use defaults
|
||||
podman_container:
|
||||
name: testidem
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 20m
|
||||
register: idem2
|
||||
|
||||
- name: Check that container is recreated when changed to default value
|
||||
assert:
|
||||
that:
|
||||
- idem2 is changed
|
||||
|
||||
- name: Remove container
|
||||
podman_container:
|
||||
name: testidem
|
||||
state: absent
|
||||
register: remove
|
||||
|
||||
- name: Check podman_actions
|
||||
assert:
|
||||
that:
|
||||
- "'podman rm -f testidem' in remove.podman_actions"
|
||||
|
||||
always:
|
||||
- name: Delete all container leftovers from tests
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "alpine:3.7"
|
||||
- "container"
|
||||
- "container2"
|
|
@ -0,0 +1,39 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
gather_facts: true
|
||||
roles:
|
||||
- role: test_deps
|
||||
test_deps_extra_packages:
|
||||
- podman
|
||||
|
||||
post_tasks:
|
||||
- name: Check podman version
|
||||
command: podman version
|
||||
register: p_ver
|
||||
changed_when: false
|
||||
|
||||
- name: Print podman version
|
||||
debug:
|
||||
msg: |
|
||||
podman version:
|
||||
{{ p_ver.stdout }}
|
||||
|
||||
Testing with ansible {{ ansible_version.full }}
|
||||
with python {{ ansible_python_version }}
|
||||
on host {{ ansible_distribution }} {{ ansible_distribution_version }}
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
driver:
|
||||
name: delegated
|
||||
options:
|
||||
managed: false
|
||||
login_cmd_template: >-
|
||||
ssh
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o Compression=no
|
||||
-o TCPKeepAlive=yes
|
||||
-o VerifyHostKeyDNS=no
|
||||
-o ForwardX11=no
|
||||
-o ForwardAgent=no
|
||||
{instance}
|
||||
ansible_connection_options:
|
||||
ansible_connection: ssh
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: instance
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
fact_caching: jsonfile
|
||||
fact_caching_connection: /tmp/molecule/facts
|
||||
inventory:
|
||||
hosts:
|
||||
all:
|
||||
hosts:
|
||||
instance:
|
||||
ansible_host: localhost
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_ROLES_PATH: "${ANSIBLE_ROLES_PATH:-/usr/share/ansible/roles}:${HOME}/zuul-jobs/roles"
|
||||
ANSIBLE_LIBRARY: "${ANSIBLE_LIBRARY:-/usr/share/ansible/plugins/modules}"
|
||||
ANSIBLE_FILTER_PLUGINS: "${ANSIBLE_FILTER_PLUGINS:-/usr/share/ansible/plugins/filter}"
|
||||
|
||||
scenario:
|
||||
name: podman_container_info
|
||||
test_sequence:
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
|
||||
lint:
|
||||
enabled: false
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
|
@ -0,0 +1,70 @@
|
|||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Test podman_container_info
|
||||
become: true
|
||||
block:
|
||||
- name: Generate random value for container name
|
||||
set_fact:
|
||||
container_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}"
|
||||
|
||||
- name: Make sure container doesn't exist
|
||||
command: podman container rm -f {{ container_name }}
|
||||
ignore_errors: true
|
||||
|
||||
- name: Get missing container info
|
||||
podman_container_info:
|
||||
name: "{{ container_name }}"
|
||||
register: nonexist
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check results
|
||||
assert:
|
||||
that:
|
||||
- "'containers' not in nonexist"
|
||||
- nonexist is failed
|
||||
|
||||
- name: Make sure container exists
|
||||
command: podman container run -d --name {{ container_name }} alpine sleep 15m
|
||||
|
||||
- name: Get existing container info
|
||||
podman_container_info:
|
||||
name: "{{ container_name }}"
|
||||
register: existing_container
|
||||
|
||||
- name: Get all containers info
|
||||
podman_container_info:
|
||||
register: all_containers
|
||||
|
||||
- name: Dump podman container inspect result
|
||||
debug: var=existing_container
|
||||
|
||||
- name: Comparison with 'podman container inspect'
|
||||
command: podman container inspect "{{ container_name }}"
|
||||
register: podman_inspect
|
||||
|
||||
- name: Convert podman inspect output to JSON
|
||||
set_fact:
|
||||
podman_inspect_result: "{{ podman_inspect.stdout | from_json }}"
|
||||
|
||||
- name: Cleanup
|
||||
command: podman container rm -f {{ container_name }}
|
||||
|
||||
- name: Make checks
|
||||
assert:
|
||||
that:
|
||||
- "'containers' in existing_container"
|
||||
- existing_container.containers
|
||||
- "existing_container.containers == podman_inspect_result"
|
||||
- all_containers.containers == existing_container.containers
|
||||
|
||||
always:
|
||||
- name: Delete all container leftovers from tests
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "alpine:3.7"
|
||||
- "container"
|
||||
- "container2"
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
roles:
|
||||
- role: test_deps
|
||||
test_deps_extra_packages:
|
||||
- podman
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
driver:
|
||||
name: delegated
|
||||
options:
|
||||
managed: false
|
||||
login_cmd_template: >-
|
||||
ssh
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o Compression=no
|
||||
-o TCPKeepAlive=yes
|
||||
-o VerifyHostKeyDNS=no
|
||||
-o ForwardX11=no
|
||||
-o ForwardAgent=no
|
||||
{instance}
|
||||
ansible_connection_options:
|
||||
ansible_connection: ssh
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: instance
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
fact_caching: jsonfile
|
||||
fact_caching_connection: /tmp/molecule/facts
|
||||
inventory:
|
||||
hosts:
|
||||
all:
|
||||
hosts:
|
||||
instance:
|
||||
ansible_host: localhost
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_ROLES_PATH: "${ANSIBLE_ROLES_PATH:-/usr/share/ansible/roles}:${HOME}/zuul-jobs/roles"
|
||||
ANSIBLE_LIBRARY: "${ANSIBLE_LIBRARY:-/usr/share/ansible/plugins/modules}"
|
||||
ANSIBLE_FILTER_PLUGINS: "${ANSIBLE_FILTER_PLUGINS:-/usr/share/ansible/plugins/filter}"
|
||||
|
||||
scenario:
|
||||
name: podman_volume_info
|
||||
test_sequence:
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
|
||||
lint:
|
||||
enabled: false
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
|
@ -0,0 +1,63 @@
|
|||
---
|
||||
- name: Converge
|
||||
hosts: all
|
||||
tasks:
|
||||
- name: Test podman_volume_info
|
||||
become: true
|
||||
block:
|
||||
- name: Print podman version
|
||||
command: podman version
|
||||
|
||||
- name: Generate random value for volume name
|
||||
set_fact:
|
||||
volume_name: "{{ 'ansible-test-podman-%0x' % ((2**32) | random) }}"
|
||||
|
||||
- name: Make sure volume doesn't exist
|
||||
command: podman volume rm {{ volume_name }}
|
||||
ignore_errors: true
|
||||
|
||||
- name: Get missing volume info
|
||||
podman_volume_info:
|
||||
name: "{{ volume_name }}"
|
||||
register: nonexist
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check results
|
||||
assert:
|
||||
that:
|
||||
- "'volumes' not in nonexist"
|
||||
- nonexist is failed
|
||||
|
||||
- name: Make sure volume exists
|
||||
command: podman volume create {{ volume_name }}
|
||||
|
||||
- name: Get existing volume info
|
||||
podman_volume_info:
|
||||
name: "{{ volume_name }}"
|
||||
register: existing_volume
|
||||
|
||||
- name: Dump podman volume inspect result
|
||||
debug: var=existing_volume
|
||||
|
||||
- name: Comparison with 'podman volume inspect'
|
||||
command: podman volume inspect "{{ volume_name }}"
|
||||
register: podman_inspect
|
||||
|
||||
- name: Convert podman inspect output to JSON
|
||||
set_fact:
|
||||
podman_inspect_result: "{{ podman_inspect.stdout | from_json }}"
|
||||
|
||||
- name: Cleanup
|
||||
command: podman volume rm {{ volume_name }}
|
||||
|
||||
- name: Make checks
|
||||
assert:
|
||||
that:
|
||||
- "'volumes' in existing_volume"
|
||||
- existing_volume.volumes
|
||||
- "existing_volume.volumes == podman_inspect_result"
|
||||
always:
|
||||
|
||||
- name: Cleanup
|
||||
command: podman volume rm {{ volume_name }}
|
||||
ignore_errors: true
|
|
@ -0,0 +1,22 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
roles:
|
||||
- role: test_deps
|
||||
test_deps_extra_packages:
|
||||
- podman
|
|
@ -1,351 +0,0 @@
|
|||
---
|
||||
- name: Test podman_image
|
||||
when:
|
||||
- ansible_facts.virtualization_type != 'docker'
|
||||
- ansible_facts.distribution in ['RedHat', 'Fedora', 'CentOS']
|
||||
block:
|
||||
- name: Delete all container leftovers from tests
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "alpine:3.7"
|
||||
- "container"
|
||||
- "container2"
|
||||
|
||||
- name: Test no image with default action
|
||||
podman_container:
|
||||
name: container
|
||||
ignore_errors: true
|
||||
register: no_image
|
||||
|
||||
- name: Test no image with state 'started'
|
||||
podman_container:
|
||||
name: container
|
||||
state: started
|
||||
ignore_errors: true
|
||||
register: no_image1
|
||||
|
||||
- name: Test no image with state 'present'
|
||||
podman_container:
|
||||
name: container
|
||||
state: present
|
||||
ignore_errors: true
|
||||
register: no_image2
|
||||
|
||||
- name: Check no image
|
||||
assert:
|
||||
that:
|
||||
- no_image is failed
|
||||
- no_image1 is failed
|
||||
- no_image2 is failed
|
||||
- no_image.msg == "State 'started' required image to be configured!"
|
||||
- no_image1.msg == "State 'started' required image to be configured!"
|
||||
- no_image2.msg == "State 'present' required image to be configured!"
|
||||
fail_msg: No image test failed!
|
||||
success_msg: No image test passed!
|
||||
|
||||
- name: Ensure image doesn't exist
|
||||
podman_image:
|
||||
name: alpine:3.7
|
||||
state: absent
|
||||
|
||||
- name: Check pulling image
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: image
|
||||
|
||||
- name: Check using already pulled image
|
||||
podman_container:
|
||||
name: container2
|
||||
image: alpine:3.7
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: image2
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- image is changed
|
||||
- image.ansible_facts is defined
|
||||
- image.ansible_facts.podman_container is defined
|
||||
- image.ansible_facts.podman_container['State']['Running']
|
||||
- image.container is defined
|
||||
- image.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' in image.actions"
|
||||
- "'started container' in image.actions"
|
||||
- image2 is changed
|
||||
- image2.ansible_facts is defined
|
||||
- image2.ansible_facts.podman_container is defined
|
||||
- image2.ansible_facts.podman_container['State']['Running']
|
||||
- image2.container is defined
|
||||
- image2.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' not in image2.actions"
|
||||
- "'started container2' in image2.actions"
|
||||
fail_msg: Pulling image test failed!
|
||||
success_msg: Pulling image test passed!
|
||||
|
||||
- name: Check failed image pull
|
||||
podman_container:
|
||||
name: container
|
||||
image: ineverneverneverexist
|
||||
state: present
|
||||
command: sleep 1d
|
||||
register: imagefail
|
||||
ignore_errors: true
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- imagefail is failed
|
||||
- imagefail.msg == "Can't pull image ineverneverneverexist"
|
||||
|
||||
|
||||
- name: Force container recreate
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine
|
||||
state: present
|
||||
command: sleep 1d
|
||||
recreate: true
|
||||
register: recreated
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- recreated is changed
|
||||
- recreated.ansible_facts is defined
|
||||
- recreated.ansible_facts.podman_container is defined
|
||||
- recreated.ansible_facts.podman_container['State']['Running']
|
||||
- "'recreated container' in recreated.actions"
|
||||
fail_msg: Force recreate test failed!
|
||||
success_msg: Force recreate test passed!
|
||||
|
||||
- name: Stop container
|
||||
podman_container:
|
||||
name: container
|
||||
state: stopped
|
||||
register: stopped
|
||||
|
||||
- name: Stop the same container again (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: stopped
|
||||
register: stopped_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- stopped is changed
|
||||
- stopped.ansible_facts is defined
|
||||
- stopped.ansible_facts.podman_container is defined
|
||||
- not stopped.ansible_facts.podman_container['State']['Running']
|
||||
- "'stopped container' in stopped.actions"
|
||||
- stopped_again is not changed
|
||||
- stopped_again.ansible_facts is defined
|
||||
- stopped_again.ansible_facts.podman_container is defined
|
||||
- not stopped_again.ansible_facts.podman_container['State']['Running']
|
||||
- stopped_again.actions == []
|
||||
fail_msg: Stopping container test failed!
|
||||
success_msg: Stopping container test passed!
|
||||
|
||||
- name: Delete stopped container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted
|
||||
|
||||
- name: Delete again container (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- deleted is changed
|
||||
- deleted.ansible_facts is defined
|
||||
- deleted.ansible_facts.podman_container is defined
|
||||
- deleted.ansible_facts.podman_container == {}
|
||||
- "'deleted container' in deleted.actions"
|
||||
- deleted_again is not changed
|
||||
- deleted_again.ansible_facts is defined
|
||||
- deleted_again.ansible_facts.podman_container is defined
|
||||
- deleted_again.ansible_facts.podman_container == {}
|
||||
- deleted_again.actions == []
|
||||
fail_msg: Deleting stopped container test failed!
|
||||
success_msg: Deleting stopped container test passed!
|
||||
|
||||
- name: Create container, but don't run
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: stopped
|
||||
command: sleep 1d
|
||||
register: created
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- created is changed
|
||||
- created.ansible_facts is defined
|
||||
- created.ansible_facts.podman_container is defined
|
||||
- created.ansible_facts.podman_container != {}
|
||||
- not created.ansible_facts.podman_container['State']['Running']
|
||||
- "'created container' in created.actions"
|
||||
fail_msg: "Creating stopped container test failed!"
|
||||
success_msg: "Creating stopped container test passed!"
|
||||
|
||||
- name: Delete created container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
|
||||
- name: Start container that was deleted
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: started
|
||||
command: sleep 1d
|
||||
register: started
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- started is changed
|
||||
- started.ansible_facts is defined
|
||||
- started.ansible_facts.podman_container is defined
|
||||
- started.ansible_facts.podman_container['State']['Running']
|
||||
- started.container is defined
|
||||
- started.container['State']['Running']
|
||||
- "'pulled image alpine:3.7' not in started.actions"
|
||||
|
||||
- name: Delete started container
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted
|
||||
|
||||
- name: Delete again container (idempotency)
|
||||
podman_container:
|
||||
name: container
|
||||
state: absent
|
||||
register: deleted_again
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- deleted is changed
|
||||
- deleted.ansible_facts is defined
|
||||
- deleted.ansible_facts.podman_container is defined
|
||||
- deleted.ansible_facts.podman_container == {}
|
||||
- "'deleted container' in deleted.actions"
|
||||
- deleted_again is not changed
|
||||
- deleted_again.ansible_facts is defined
|
||||
- deleted_again.ansible_facts.podman_container is defined
|
||||
- deleted_again.ansible_facts.podman_container == {}
|
||||
- deleted_again.actions == []
|
||||
fail_msg: Deleting started container test failed!
|
||||
success_msg: Deleting started container test passed!
|
||||
|
||||
- name: Recreate container with parameters
|
||||
podman_container:
|
||||
name: container
|
||||
image: alpine:3.7
|
||||
state: started
|
||||
command: sleep 1d
|
||||
recreate: true
|
||||
etc_hosts:
|
||||
host1: 127.0.0.1
|
||||
host2: 127.0.0.1
|
||||
annotation:
|
||||
this: "annotation_value"
|
||||
dns:
|
||||
- 1.1.1.1
|
||||
- 8.8.4.4
|
||||
dns_search: example.com
|
||||
cap_add:
|
||||
- SYS_TIME
|
||||
- NET_ADMIN
|
||||
publish:
|
||||
- "9000:80"
|
||||
- "9001:8000"
|
||||
workdir: "/bin"
|
||||
env:
|
||||
FOO: bar
|
||||
BAR: foo
|
||||
TEST: 1
|
||||
BOOL: false
|
||||
group_add: "somegroup"
|
||||
label:
|
||||
somelabel: labelvalue
|
||||
otheralbe: othervalue
|
||||
volumes:
|
||||
- /tmp:/data
|
||||
register: test
|
||||
|
||||
- name: Check output is correct
|
||||
assert:
|
||||
that:
|
||||
- test is changed
|
||||
- test.ansible_facts is defined
|
||||
- test.ansible_facts.podman_container is defined
|
||||
- test.ansible_facts.podman_container != {}
|
||||
- test.ansible_facts.podman_container['State']['Running']
|
||||
# test capabilities
|
||||
- "'CAP_SYS_TIME' in test.ansible_facts.podman_container['BoundingCaps']"
|
||||
- "'CAP_NET_ADMIN' in test.ansible_facts.podman_container['BoundingCaps']"
|
||||
# test annotations
|
||||
- test.ansible_facts.podman_container['Config']['Annotations']['this'] is defined
|
||||
- test.ansible_facts.podman_container['Config']['Annotations']['this'] == "annotation_value"
|
||||
# test DNS
|
||||
- >-
|
||||
(test.ansible_facts.podman_container['HostConfig']['Dns'] is defined and
|
||||
test.ansible_facts.podman_container['HostConfig']['Dns'] == ['1.1.1.1', '8.8.4.4']) or
|
||||
(test.ansible_facts.podman_container['HostConfig']['DNS'] is defined and
|
||||
test.ansible_facts.podman_container['HostConfig']['DNS'] == ['1.1.1.1', '8.8.4.4'])
|
||||
# test ports
|
||||
- test.ansible_facts.podman_container['NetworkSettings']['Ports']|length == 2
|
||||
# test working dir
|
||||
- test.ansible_facts.podman_container['Config']['WorkingDir'] == "/bin"
|
||||
# test dns search
|
||||
- >-
|
||||
(test.ansible_facts.podman_container['HostConfig']['DnsSearch'] is defined and
|
||||
test.ansible_facts.podman_container['HostConfig']['DnsSearch'] == ['example.com']) or
|
||||
(test.ansible_facts.podman_container['HostConfig']['DNSSearch'] is defined and
|
||||
test.ansible_facts.podman_container['HostConfig']['DNSSearch'] == ['example.com'])
|
||||
# test environment variables
|
||||
- "'FOO=bar' in test.ansible_facts.podman_container['Config']['Env']"
|
||||
- "'BAR=foo' in test.ansible_facts.podman_container['Config']['Env']"
|
||||
- "'TEST=1' in test.ansible_facts.podman_container['Config']['Env']"
|
||||
- "'BOOL=False' in test.ansible_facts.podman_container['Config']['Env']"
|
||||
# test labels
|
||||
- test.ansible_facts.podman_container['Config']['Labels'] | length == 2
|
||||
- test.ansible_facts.podman_container['Config']['Labels']['somelabel'] == "labelvalue"
|
||||
- test.ansible_facts.podman_container['Config']['Labels']['otheralbe'] == "othervalue"
|
||||
# test mounts
|
||||
- >-
|
||||
(test.ansible_facts.podman_container['Mounts'][0]['Destination'] is defined and
|
||||
'/data' in test.ansible_facts.podman_container['Mounts'] | map(attribute='Destination') | list) or
|
||||
(test.ansible_facts.podman_container['Mounts'][0]['destination'] is defined and
|
||||
'/data' in test.ansible_facts.podman_container['Mounts'] | map(attribute='destination') | list)
|
||||
- >-
|
||||
(test.ansible_facts.podman_container['Mounts'][0]['Source'] is defined and
|
||||
'/tmp' in test.ansible_facts.podman_container['Mounts'] | map(attribute='Source') | list) or
|
||||
(test.ansible_facts.podman_container['Mounts'][0]['source'] is defined and
|
||||
'/tmp' in test.ansible_facts.podman_container['Mounts'] | map(attribute='source') | list)
|
||||
fail_msg: Parameters container test failed!
|
||||
success_msg: Parameters container test passed!
|
||||
always:
|
||||
- name: Delete all container leftovers from tests
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- "alpine:3.7"
|
||||
- "container"
|
||||
- "container2"
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
# All variables intended for modification should place placed in this file.
|
||||
|
||||
# All variables within this role should have a prefix of "tripleo_container_manage"
|
||||
tripleo_container_manage_cli: podman
|
||||
tripleo_container_manage_concurrency: 1
|
||||
tripleo_container_manage_config: "/var/lib/tripleo-config/"
|
||||
tripleo_container_manage_config_id: tripleo
|
||||
tripleo_container_manage_config_patterns: 'hashed-*.json'
|
||||
tripleo_container_manage_debug: false
|
||||
tripleo_container_manage_healthcheck_disabled: false
|
||||
tripleo_container_manage_log_path: '/var/log/containers/stdouts'
|
||||
tripleo_container_manage_systemd_order: false
|
|
@ -0,0 +1 @@
|
|||
enable netns-placeholder.service
|
|
@ -0,0 +1 @@
|
|||
enable tripleo-container-shutdown.service
|
|
@ -0,0 +1,11 @@
|
|||
[Unit]
|
||||
Description=Create netns directory
|
||||
Before=tripleo-container-shutdown.service
|
||||
Wants=network.target
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/sbin/ip netns add placeholder
|
||||
ExecStop=/sbin/ip netns delete placeholder
|
||||
KillMode=process
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,19 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
TIMEOUT=${1:-90}
|
||||
PARALLEL=${2:-10}
|
||||
|
||||
if command -v dnf >/dev/null;then
|
||||
if command -v podman >/dev/null; then
|
||||
containers=$(podman ps --filter label=managed_by=tripleo_ansible --format {{.Names}})
|
||||
for c in $containers; do
|
||||
logger -p warning "WARNING ($c) Container $c managed by tripleo-ansible is not stopped yet"
|
||||
logger -p warning "WARNING ($c) Check systemd logs: journalctl -u tripleo_$c"
|
||||
done
|
||||
fi
|
||||
else
|
||||
if command -v docker >/dev/null; then
|
||||
/usr/bin/docker ps --format \"{{.Names}}\" --filter "label=managed_by=tripleo_ansible" | \
|
||||
/usr/bin/xargs -n 1 -P $PARALLEL /usr/bin/docker stop --time=$TIMEOUT
|
||||
fi
|
||||
fi
|
|
@ -0,0 +1,21 @@
|
|||
[Unit]
|
||||
Description=TripleO Container Shutdown
|
||||
Documentation=https://docs.openstack.org/tripleo-docs/
|
||||
# Note: docker.service will be removed once CentOS8 / RHEL8 will be the default
|
||||
# platform, but for now we keep it for Pacemaker testing.
|
||||
# pacemaker.service is needed here, to make sure that all non-Pacemaker managed
|
||||
# containers are stopped before Pacemaker.
|
||||
After=pacemaker.service docker.service network-online.target iptables.service ip6tables.service
|
||||
Before=shutdown.target
|
||||
RefuseManualStop=yes
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/bin/true
|
||||
RemainAfterExit=yes
|
||||
ExecStop=/usr/libexec/tripleo-container-shutdown
|
||||
# Wait at most 900 seconds for all containers to shutdown
|
||||
TimeoutStopSec=900
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,83 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
PODMAN=/usr/bin/podman
|
||||
|
||||
NAME=$1
|
||||
|
||||
if [ -z "$NAME" ]; then
|
||||
echo "No name provided, cannot start container. Aborting" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Start container. Podman does not fail if container is already started
|
||||
$PODMAN start $NAME
|
||||
rc=$?
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "Error starting podman container $NAME: $rc" >&2
|
||||
exit $rc
|
||||
fi
|
||||
|
||||
# The environment can ben configured to create additional drop-in
|
||||
# dependencies for the scopes associated with the container. This is
|
||||
# done to prevent systemd from stopping the scopes early and break the
|
||||
# configured dependencies in tripleo_*.services
|
||||
# Stop here otherwise.
|
||||
if [ ! -f "/etc/sysconfig/podman_drop_in" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Retrieve the container's ID
|
||||
# Note: currently the only API to retrieve the CID is either
|
||||
# 1) via "podman inspect" but we don't want to use it because it can be
|
||||
# very slow under IO load.
|
||||
# 2) by running "podman start $NAME" but that command only returns the CID
|
||||
# if the container is already running. Otherwise it returns the container
|
||||
# name, which would break us.
|
||||
# The only other means is via "podman ps". ps option "--filter" cannot
|
||||
# enforce full name matches, so use grep instead and stop at first match.
|
||||
CID=$($PODMAN ps --no-trunc --format '{{.ID}} {{.Names}}' | grep -F -w -m1 "$NAME" | cut -d' ' -f1)
|
||||
|
||||
if [ -z "$CID" ]; then
|
||||
echo "Container ID not found for \"$NAME\". Not creating drop-in dependency" 2>&1
|
||||
exit 1
|
||||
else
|
||||
echo "Creating additional drop-in dependency for \"$NAME\" ($CID)"
|
||||
fi
|
||||
|
||||
# Note: a tripleo-ansible container has three systemd files associated with it:
|
||||
# 1. tripleo_*.service - the regular systemd service generated by tripleo-ansible
|
||||
# 2. libpod-conmon*.scope - created dynamically by podman. runs a conmon
|
||||
# process that creates a pidfile for tripleo_*.service and monitor it.
|
||||
# 3. libpod-*.scope - created dynamically by runc. for cgroups accounting
|
||||
#
|
||||
# tripleo-ansible can only set start/stop dependencies on 1., not 2. and 3.
|
||||
# On reboot, systemd is allowed to stop 2. or 3. at any time, which can
|
||||
# cause 1. to stop before its deps as set up by tripleo-ansible.
|
||||
#
|
||||
# To prevent an unexpected stop of 1. from happening, inject a dependency
|
||||
# in 2. and 3. so that systemd is forbidden to stop those scopes
|
||||
# automatically until tripleo-container-shutdown.service is stopped.
|
||||
# That way, when systemd stops 1., the two scopes 2. and 3. will
|
||||
# finish in sequence and tripleo-ansible dependencies will be respected.
|
||||
|
||||
for scope in "libpod-$CID.scope.d" "libpod-conmon-$CID.scope.d"; do
|
||||
if [ $rc -eq 0 ] && [ ! -d /run/systemd/transient/"$scope" ]; then
|
||||
mkdir -p /run/systemd/transient/"$scope" && \
|
||||
echo -e "[Unit]\nBefore=tripleo-container-shutdown.service" > /run/systemd/transient/"$scope"/dep.conf && \
|
||||
chmod ago+r /run/systemd/transient/"$scope" /run/systemd/transient/"$scope"/dep.conf
|
||||
rc=$?
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "Could not create drop-in dependency for \"$NAME\" ($CID)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
systemctl daemon-reload
|
||||
rc=$?
|
||||
if [ $rc -ne 0 ]; then
|
||||
echo "Could not refresh service definition after creating drop-in for \"$NAME\": $rc" >&2
|
||||
exit 1
|
||||
fi
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
galaxy_info:
|
||||
author: OpenStack
|
||||
description: TripleO OpenStack Role -- tripleo-container-manage
|
||||
company: Red Hat
|
||||
license: Apache-2.0
|
||||
min_ansible_version: 2.7
|
||||
#
|
||||
# Provide a list of supported platforms, and for each platform a list of versions.
|
||||
# If you don't wish to enumerate all versions for a particular platform, use 'all'.
|
||||
# To view available platforms and versions (or releases), visit:
|
||||
# https://galaxy.ansible.com/api/v1/platforms/
|
||||
#
|
||||
platforms:
|
||||
- name: Fedora
|
||||
versions:
|
||||
- 28
|
||||
- name: CentOS
|
||||
versions:
|
||||
- 7
|
||||
|
||||
galaxy_tags:
|
||||
- tripleo
|
||||
|
||||
|
||||
# List your role dependencies here, one per line. Be sure to remove the '[]' above,
|
||||
# if you add dependencies to this list.
|
||||
dependencies: []
|
|
@ -0,0 +1,56 @@
|
|||
---
|
||||
driver:
|
||||
name: delegated
|
||||
options:
|
||||
managed: false
|
||||
login_cmd_template: >-
|
||||
ssh
|
||||
-o UserKnownHostsFile=/dev/null
|
||||
-o StrictHostKeyChecking=no
|
||||
-o Compression=no
|
||||
-o TCPKeepAlive=yes
|
||||
-o VerifyHostKeyDNS=no
|
||||
-o ForwardX11=no
|
||||
-o ForwardAgent=no
|
||||
{instance}
|
||||
ansible_connection_options:
|
||||
ansible_connection: ssh
|
||||
|
||||
log: true
|
||||
|
||||
platforms:
|
||||
- name: instance
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
config_options:
|
||||
defaults:
|
||||
fact_caching: jsonfile
|
||||
fact_caching_connection: /tmp/molecule/facts
|
||||
inventory:
|
||||
hosts:
|
||||
all:
|
||||
hosts:
|
||||
instance:
|
||||
ansible_host: localhost
|
||||
log: true
|
||||
env:
|
||||
ANSIBLE_STDOUT_CALLBACK: yaml
|
||||
ANSIBLE_ROLES_PATH: "${ANSIBLE_ROLES_PATH:-/usr/share/ansible/roles}:${HOME}/zuul-jobs/roles"
|
||||
ANSIBLE_LIBRARY: "${ANSIBLE_LIBRARY:-/usr/share/ansible/plugins/modules}"
|
||||
ANSIBLE_FILTER_PLUGINS: "${ANSIBLE_FILTER_PLUGINS:-/usr/share/ansible/plugins/filter}"
|
||||
|
||||
scenario:
|
||||
name: default
|
||||
test_sequence:
|
||||
- prepare
|
||||
- converge
|
||||
- verify
|
||||
|
||||
lint:
|
||||
enabled: false
|
||||
|
||||
verifier:
|
||||
name: testinfra
|
||||
lint:
|
||||
name: flake8
|
|
@ -0,0 +1,62 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
- name: Converge
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
vars:
|
||||
tripleo_container_manage_config: '/tmp/container-configs'
|
||||
tripleo_container_manage_debug: true
|
||||
tripleo_container_manage_config_patterns: '*.json'
|
||||
tripleo_container_manage_systemd_order: true
|
||||
tasks:
|
||||
- include_role:
|
||||
name: tripleo-container-manage
|
||||
post_tasks:
|
||||
- name: Verify that Fedora container was created correctly
|
||||
become: true
|
||||
block:
|
||||
- name: Check for fedora container
|
||||
command: podman container exists fedora
|
||||
- name: Gather facts about fedora container
|
||||
podman_container_info:
|
||||
name: fedora
|
||||
register: fedora_infos
|
||||
- name: Assert that fedora container has the right image
|
||||
assert:
|
||||
that:
|
||||
- "'fedora:latest' in fedora_infos.containers.0.ImageName"
|
||||
fail_msg: 'fedora container has wrong image'
|
||||
success_msg: 'fedora container has the right image'
|
||||
- name: Check if tripleo_fedora systemd service is active
|
||||
command: systemctl is-active --quiet tripleo_fedora
|
||||
register: tripleo_fedora_active_result
|
||||
- name: Assert that tripleo_fedora systemd service is active
|
||||
assert:
|
||||
that:
|
||||
- tripleo_fedora_active_result.rc == 0
|
||||
fail_msg: 'tripleo_fedora systemd service is not active'
|
||||
success_msg: 'tripleo_fedora systemd service is active'
|
||||
- name: Check if tripleo_fedora systemd healthcheck service is active
|
||||
command: systemctl is-active --quiet tripleo_fedora_healthcheck.timer
|
||||
register: tripleo_fedora_healthcheck_active_result
|
||||
- name: Assert that tripleo_fedora systemd healthcheck service is active
|
||||
assert:
|
||||
that:
|
||||
- tripleo_fedora_healthcheck_active_result.rc == 0
|
||||
fail_msg: 'tripleo_fedora systemd healthcheck service is not active'
|
||||
success_msg: 'tripleo_fedora systemd healthcheck service is active'
|
|
@ -0,0 +1,40 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
- name: Prepare
|
||||
hosts: all
|
||||
roles:
|
||||
- role: test_deps
|
||||
test_deps_extra_packages:
|
||||
- podman
|
||||
tasks:
|
||||
- name: Prepare the container configs directory
|
||||
file:
|
||||
path: '/tmp/container-configs'
|
||||
state: directory
|
||||
- name: Create a configuration file for a fedora container
|
||||
copy:
|
||||
content: |
|
||||
{
|
||||
"image": "fedora:latest",
|
||||
"net": "host",
|
||||
"command": "sleep 3600",
|
||||
"restart": "always",
|
||||
"net": "host",
|
||||
"healthcheck": { "test": "echo test" }
|
||||
}
|
||||
dest: '/tmp/container-configs/fedora.json'
|
|
@ -0,0 +1,29 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
- name: "Get {{ lookup('dict', container_exists_data).value.command.0 }} container status"
|
||||
set_fact:
|
||||
container_running: >-
|
||||
{{ podman_containers.containers | selectattr('Name', 'equalto', lookup('dict', container_exists_data).value.command.0) |
|
||||
map(attribute='State.Running') | first | default(false) }}
|
||||
|
||||
- name: "Fail if {{ lookup('dict', container_exists_data).value.command.0 }} is not running"
|
||||
fail:
|
||||
msg: >-
|
||||
Can't run container exec for {{ lookup('dict', container_exists_data).key }},
|
||||
{{ lookup('dict', container_exists_data).value.command.0 }} is not running
|
||||
when:
|
||||
- not container_running|default(false)|bool
|
|
@ -0,0 +1,21 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: "Create containers managed by Podman for {{ tripleo_container_manage_config }}"
|
||||
when:
|
||||
- tripleo_container_manage_cli == 'podman'
|
||||
include: podman/start_order.yml order="{{ item.key }}" data="{{ item.value }}"
|
||||
loop: "{{ all_containers_hash | subsort(attribute='start_order', null_value=0) | dict2items | list }}"
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Gather podman infos
|
||||
podman_container_info: {}
|
||||
register: podman_containers
|
||||
when:
|
||||
- tripleo_container_manage_cli == 'podman'
|
||||
|
||||
- name: "Delete containers managed by Podman for {{ tripleo_container_manage_config }}"
|
||||
when:
|
||||
- tripleo_container_manage_cli == 'podman'
|
||||
include_tasks: podman/delete.yml
|
||||
loop: "{{ podman_containers.containers | needs_delete(config=all_containers_hash, config_id=tripleo_container_manage_config_id) }}"
|
|
@ -0,0 +1,95 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
# "tripleo-container-manage" will search for and load any operating system variable file
|
||||
|
||||
# found within the "vars/" path. If no OS files are found the task will skip.
|
||||
- name: Gather variables for each operating system
|
||||
include_vars: "{{ item }}"
|
||||
with_first_found:
|
||||
- skip: true
|
||||
files:
|
||||
- "{{ ansible_distribution | lower }}-{{ ansible_distribution_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}-{{ ansible_distribution_major_version | lower }}.yml"
|
||||
- "{{ ansible_distribution | lower }}.yml"
|
||||
- "{{ ansible_os_family | lower }}-{{ ansible_distribution_version.split('.')[0] }}.yml"
|
||||
- "{{ ansible_os_family | lower }}.yml"
|
||||
tags:
|
||||
- always
|
||||
|
||||
- name: Create container logs path
|
||||
file:
|
||||
path: "{{ tripleo_container_manage_log_path }}"
|
||||
state: directory
|
||||
owner: root
|
||||
group: root
|
||||
become: true
|
||||
|
||||
- name: Create ansible-managed dropin file
|
||||
copy:
|
||||
dest: "{{ tripleo_container_manage_config | dirname }}/.ansible-managed"
|
||||
content: |
|
||||
Containers are managed by the tripleo-container-manage role in
|
||||
tripleo-ansible project. This file is used by paunch to show a warning
|
||||
if paunch is used against a deployment done with tripleo-container-manage.
|
||||
|
||||
- name: Generate containers configs data
|
||||
no_log: "{{ false if tripleo_container_manage_debug else true }}"
|
||||
block:
|
||||
- name: "Find all matching configs configs for in {{ tripleo_container_manage_config }}"
|
||||
find:
|
||||
paths: "{{ tripleo_container_manage_config }}"
|
||||
patterns: "{{ tripleo_container_manage_config_patterns }}"
|
||||
recurse: true
|
||||
register: matched_files
|
||||
- name: "Read config for each container in {{ tripleo_container_manage_config }}"
|
||||
slurp:
|
||||
src: "{{ item.path }}"
|
||||
register: containers_data
|
||||
loop: "{{ matched_files.files }}"
|
||||
- name: Prepare container hashes from config
|
||||
set_fact:
|
||||
container_hash: "{'{{ item.source|basename|regex_replace('^hashed-','')|regex_replace('.json$','') }}': {{ item.content|b64decode|from_json }} }"
|
||||
register: container_hashes
|
||||
loop: "{{ containers_data['results'] }}"
|
||||
- name: Compile container hashes from results
|
||||
set_fact:
|
||||
container_hash: "{{ item.ansible_facts.container_hash | combine(item.ansible_facts.container_hash) }}"
|
||||
register: container_hashes
|
||||
loop: "{{ container_hashes.results }}"
|
||||
- name: Finalise hashes for all containers
|
||||
set_fact:
|
||||
all_containers_hash: "{{ container_hashes.results | map(attribute='ansible_facts.container_hash') | list | singledict() }}"
|
||||
|
||||
- name: "Manage systemd shutdown files"
|
||||
become: true
|
||||
when:
|
||||
- tripleo_container_manage_systemd_order
|
||||
block:
|
||||
- name: Include tasks for systemd shutdown service
|
||||
include_tasks: shutdown.yml
|
||||
|
||||
- name: "Manage containers from {{ tripleo_container_manage_config }}"
|
||||
when:
|
||||
- tripleo_container_manage_cli == 'podman'
|
||||
become: true
|
||||
block:
|
||||
- name: "Delete containers from {{ tripleo_container_manage_config }}"
|
||||
include_tasks: delete.yml
|
||||
- name: "Create containers from {{ tripleo_container_manage_config }}"
|
||||
include_tasks: create.yml
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: "Async container create/run"
|
||||
async: 300
|
||||
poll: 0
|
||||
register: create_async_results
|
||||
# async and check mode don't work together
|
||||
when:
|
||||
- not ansible_check_mode|bool
|
||||
loop: "{{ batched_container_data | haskey(attribute='action', reverse=True) }}"
|
||||
loop_control:
|
||||
loop_var: container_data
|
||||
podman_container:
|
||||
cap_add: "{{ lookup('dict', container_data).value.cap_add | default(omit) }}"
|
||||
cap_drop: "{{ lookup('dict', container_data).value.cap_drop | default(omit) }}"
|
||||
command: "{{ lookup('dict', container_data).value.command | default(omit) }}"
|
||||
conmon_pidfile: "/var/run/{{ lookup('dict', container_data).key }}.pid"
|
||||
cpu_shares: "{{ lookup('dict', container_data).value.cpu_shares | default(omit) | int }}"
|
||||
# cpuset_cpus: "{{ lookup('dict', container_data).value.cpuset_cpus | default(omit) }}"
|
||||
debug: true
|
||||
detach: "{{ lookup('dict', container_data).value.detach | default(true) }}"
|
||||
env: "{{ lookup('dict', container_data).value.environment | default(omit) }}"
|
||||
env_file: "{{ lookup('dict', container_data).value.env_file | default(omit) }}"
|
||||
etc_hosts: "{{ lookup('dict', container_data).value.extra_hosts | default({}) }}"
|
||||
group_add: "{{ lookup('dict', container_data).value.group_add | default(omit) }}"
|
||||
hostname: "{{ lookup('dict', container_data).value.hostname | default(omit) }}"
|
||||
image: "{{ lookup('dict', container_data).value.image }}"
|
||||
interactive: "{{ lookup('dict', container_data).value.interactive | default(false) }}"
|
||||
ipc: "{{ lookup('dict', container_data).value.ipc | default(omit) }}"
|
||||
label:
|
||||
config_id: "{{ tripleo_container_manage_config_id }}"
|
||||
container_name: "{{ lookup('dict', container_data).key }}"
|
||||
managed_by: tripleo_ansible
|
||||
config_data: "{{ lookup('dict', container_data).value }}"
|
||||
log_driver: 'k8s-file'
|
||||
log_opt: "path={{ tripleo_container_manage_log_path }}/{{ lookup('dict', container_data).key }}.log"
|
||||
memory: "{{ lookup('dict', container_data).value.mem_limit | default(omit) }}"
|
||||
memory_swap: "{{ lookup('dict', container_data).value.mem_swappiness | default(omit) }}"
|
||||
name: "{{ lookup('dict', container_data).key }}"
|
||||
net: "{{ lookup('dict', container_data).value.net | default('none') }}"
|
||||
pid: "{{ lookup('dict', container_data).value.pid | default(omit) }}"
|
||||
privileged: "{{ lookup('dict', container_data).value.privileged | default(false) }}"
|
||||
rm: "{{ lookup('dict', container_data).value.remove | default(false) }}"
|
||||
security_opt: "{{ lookup('dict', container_data).value.security_opt | default(omit) }}"
|
||||
state: present
|
||||
stop_signal: "{{ lookup('dict', container_data).value.stop_signal | default(omit) }}"
|
||||
stop_timeout: "{{ lookup('dict', container_data).value.stop_grace_period | default(omit) | int }}"
|
||||
tty: "{{ lookup('dict', container_data).value.tty | default(false) }}"
|
||||
ulimit: "{{ lookup('dict', container_data).value.ulimit | default(omit) }}"
|
||||
user: "{{ lookup('dict', container_data).value.user | default(omit) }}"
|
||||
uts: "{{ lookup('dict', container_data).value.uts | default(omit) }}"
|
||||
volume: "{{ lookup('dict', container_data).value.volumes | default(omit) }}"
|
||||
volumes_from: "{{ lookup('dict', container_data).value.volumes_from | default([]) }}"
|
||||
|
||||
- name: "Check podman create status"
|
||||
async_status:
|
||||
jid: "{{ create_async_result_item.ansible_job_id }}"
|
||||
loop: "{{ create_async_results.results }}"
|
||||
loop_control:
|
||||
loop_var: "create_async_result_item"
|
||||
register: create_async_poll_results
|
||||
until: create_async_poll_results.finished
|
||||
retries: 30
|
||||
# async and check mode don't work together
|
||||
when:
|
||||
- not ansible_check_mode|bool
|
||||
|
||||
# This fact will be used in systemd playbook to figure out if whether or not
|
||||
# a container managed by systemd needs to be restarted
|
||||
- name: "Create a list of containers which changed"
|
||||
set_fact:
|
||||
containers_changed: >-
|
||||
{{ create_async_results.results | selectattr('changed', 'equalto', true) |
|
||||
map(attribute='container_data') | list | list_of_keys }}
|
||||
|
||||
- name: "Print the list of containers which changed"
|
||||
debug:
|
||||
var: containers_changed
|
||||
when: tripleo_container_manage_debug | bool
|
|
@ -0,0 +1,77 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
# This playbook is a "best effort" way to remove a container from a host.
|
||||
# It'll try to remove the healthcheck, service and then container without
|
||||
# much validation in case things failed in the middle.
|
||||
|
||||
- name: "Remove systemd healthcheck for {{ item }}"
|
||||
block:
|
||||
- name: "Stop and disable systemd timer for {{ item }}"
|
||||
systemd:
|
||||
state: stopped
|
||||
name: "tripleo_{{ item }}_healthcheck.timer"
|
||||
enabled: false
|
||||
ignore_errors: true
|
||||
- name: "Delete systemd timer file for {{ item }}"
|
||||
file:
|
||||
path: "/etc/systemd/system/tripleo_{{ item }}_healthcheck.timer"
|
||||
state: absent
|
||||
register: systemd_timer_deleted
|
||||
- name: "Stop and disable systemd healthcheck for {{ item }}"
|
||||
systemd:
|
||||
state: stopped
|
||||
name: "tripleo_{{ item }}_healthcheck.service"
|
||||
enabled: false
|
||||
ignore_errors: true
|
||||
- name: "Delete systemd healthcheck file for {{ item }}"
|
||||
file:
|
||||
path: "/etc/systemd/system/tripleo_{{ item }}_healthcheck.service"
|
||||
state: absent
|
||||
register: systemd_healthcheck_deleted
|
||||
- name: Force systemd to reread configs
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
when: systemd_timer_deleted.changed or systemd_healthcheck_deleted.changed
|
||||
|
||||
- name: "Stop and disable systemd service for {{ item }}"
|
||||
systemd:
|
||||
state: stopped
|
||||
name: "tripleo_{{ item }}.service"
|
||||
enabled: false
|
||||
ignore_errors: true
|
||||
|
||||
- name: "Delete systemd unit file for {{ item }}"
|
||||
file:
|
||||
path: "/etc/systemd/system/tripleo_{{ item }}.service"
|
||||
state: absent
|
||||
register: systemd_file_deleted
|
||||
|
||||
- name: "Remove trailing .requires for {{ item }}"
|
||||
file:
|
||||
path: "/etc/systemd/system/tripleo_{{ item }}.requires"
|
||||
state: absent
|
||||
register: systemd_requires_deleted
|
||||
|
||||
- name: Force systemd to reread configs
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
when: systemd_file_deleted.changed or systemd_requires_deleted.changed
|
||||
|
||||
- name: "Remove container {{ item }}"
|
||||
podman_container:
|
||||
name: "{{ item }}"
|
||||
state: absent
|
|
@ -0,0 +1,44 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: "Check if containers are running before doing exec"
|
||||
include_tasks: container_running.yml
|
||||
loop: "{{ batched_container_data | haskey(attribute='action', value='exec') }}"
|
||||
loop_control:
|
||||
loop_var: container_exists_data
|
||||
when: not ansible_check_mode|bool
|
||||
|
||||
- name: "Async container exec"
|
||||
command:
|
||||
argv: "{{ lookup('dict', container_exec_data).value | container_exec_cmd(cli=tripleo_container_manage_cli) }}"
|
||||
async: 60
|
||||
poll: 0
|
||||
register: exec_async_results
|
||||
loop: "{{ batched_container_data | haskey(attribute='action', value='exec') }}"
|
||||
loop_control:
|
||||
loop_var: container_exec_data
|
||||
when: not ansible_check_mode|bool
|
||||
|
||||
- name: "Check podman exec status"
|
||||
async_status:
|
||||
jid: "{{ exec_async_result_item.ansible_job_id }}"
|
||||
loop: "{{ exec_async_results.results }}"
|
||||
loop_control:
|
||||
loop_var: "exec_async_result_item"
|
||||
register: exec_async_poll_results
|
||||
until: exec_async_poll_results.finished
|
||||
retries: 30
|
||||
when: not ansible_check_mode|bool
|
|
@ -0,0 +1,33 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Run containers execs asynchronously
|
||||
include_tasks: podman/exec.yml
|
||||
|
||||
- name: Manage containers asynchronously
|
||||
include_tasks: podman/create.yml
|
||||
|
||||
# We don't want to use async for the systemd tasks or we can have startup
|
||||
# errors when systemd has to deal with multiple services trying to start
|
||||
# at the same time. It is more reliable to start them in serial.
|
||||
- name: Manage container systemd services and healthchecks in serial
|
||||
include_tasks: podman/systemd.yml
|
||||
# systemd doesn't have the equivalent of docker unless-stopped.
|
||||
# Let's force 'always' so containers aren't restarted when stopped by
|
||||
# systemd, but restarted when in failure.
|
||||
loop: "{{ batched_container_data | haskey(attribute='restart', value=['always','unless-stopped'], any=True) }}"
|
||||
loop_control:
|
||||
loop_var: container_config
|
|
@ -0,0 +1,27 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Gather podman infos
|
||||
podman_container_info: {}
|
||||
register: podman_containers
|
||||
when:
|
||||
- tripleo_container_manage_cli == 'podman'
|
||||
|
||||
- name: "Batching items for start_order {{ order }}"
|
||||
include_tasks: podman/manage.yml
|
||||
loop: "{{ data | batch(tripleo_container_manage_concurrency) | list }}"
|
||||
loop_control:
|
||||
loop_var: batched_container_data
|
|
@ -0,0 +1,74 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Set container_name and container_sysd facts
|
||||
set_fact:
|
||||
container_sysd_name: "{{ lookup('dict', container_config).key }}"
|
||||
container_sysd_data: "{{ lookup('dict', container_config).value }}"
|
||||
|
||||
- name: "Start systemd service for {{ container_sysd_name }}"
|
||||
block:
|
||||
- name: "Remove trailing .requires for {{ container_sysd_name }}"
|
||||
file:
|
||||
path: "/etc/systemd/system/tripleo_{{ container_sysd_name }}.requires"
|
||||
state: absent
|
||||
- name: "Create systemd unit file for {{ container_sysd_name }} service"
|
||||
template:
|
||||
src: systemd-service.j2
|
||||
dest: "/etc/systemd/system/tripleo_{{ container_sysd_name }}.service"
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
register: systemd_file
|
||||
- name: "Enable and start systemd service for {{ container_sysd_name }}"
|
||||
systemd:
|
||||
# Restart the service if it was already running
|
||||
state: restarted
|
||||
name: "tripleo_{{ container_sysd_name }}.service"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
when:
|
||||
- systemd_file is changed or container_sysd_name in containers_changed
|
||||
- name: "Manage systemd healthcheck for {{ container_sysd_name }}"
|
||||
when:
|
||||
- not tripleo_container_manage_healthcheck_disabled
|
||||
- container_sysd_data.healthcheck is defined
|
||||
block:
|
||||
- name: "Create systemd unit file for {{ container_sysd_name }} healthcheck"
|
||||
template:
|
||||
src: systemd-healthcheck.j2
|
||||
dest: "/etc/systemd/system/tripleo_{{ container_sysd_name }}_healthcheck.service"
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
register: systemd_healthcheck
|
||||
- name: "Create systemd timer for {{ container_sysd_name }} healthcheck"
|
||||
template:
|
||||
src: systemd-timer.j2
|
||||
dest: "/etc/systemd/system/tripleo_{{ container_sysd_name }}_healthcheck.timer"
|
||||
mode: '0644'
|
||||
owner: root
|
||||
group: root
|
||||
register: systemd_timer
|
||||
- name: "Enable and start systemd timer for {{ container_sysd_name }}"
|
||||
systemd:
|
||||
# Restart the timer if it was already running
|
||||
state: restarted
|
||||
name: "tripleo_{{ container_sysd_name }}_healthcheck.timer"
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
when:
|
||||
- systemd_healthcheck.changed or systemd_timer.changed
|
|
@ -0,0 +1,114 @@
|
|||
---
|
||||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
- name: Check if /etc/sysconfig/podman_drop_in exists
|
||||
stat:
|
||||
path: /etc/sysconfig/podman_drop_in
|
||||
register: podman_drop_in
|
||||
|
||||
- name: Set podman_drop_in fact
|
||||
set_fact:
|
||||
podman_drop_in: true
|
||||
when:
|
||||
- podman_drop_in.stat.exists
|
||||
|
||||
- name: Cleanup Paunch services and files
|
||||
block:
|
||||
- name: Check if /usr/lib/systemd/system/paunch-container-shutdown.service exists
|
||||
stat:
|
||||
path: /usr/lib/systemd/system/paunch-container-shutdown.service
|
||||
register: paunch_shutdown
|
||||
- name: Tear-down paunch-container-shutdown
|
||||
when:
|
||||
- paunch_shutdown.stat.exists
|
||||
block:
|
||||
- name: Allow paunch-container-shutdown to be stopped
|
||||
lineinfile:
|
||||
path: /usr/lib/systemd/system/paunch-container-shutdown.service
|
||||
regexp: '^RefuseManualStop'
|
||||
line: 'RefuseManualStop=no'
|
||||
- name: Force systemd to reread configs
|
||||
systemd:
|
||||
daemon_reload: true
|
||||
- name: Disable and stop paunch-container-shutdown service
|
||||
systemd:
|
||||
name: paunch-container-shutdown
|
||||
state: stopped
|
||||
enabled: false
|
||||
# TODO(emilien): this task can be removed later when paunch isn't a
|
||||
# dependency of python-tripleoclient. It'll be replaced by an rpm removal.
|
||||
- name: "Remove paunch files for systemd"
|
||||
file:
|
||||
path: "{{ item }}"
|
||||
state: absent
|
||||
loop:
|
||||
- /usr/libexec/paunch-container-shutdown
|
||||
- /usr/libexec/paunch-start-podman-container
|
||||
- /usr/lib/systemd/system/paunch-container-shutdown.service
|
||||
- /usr/lib/systemd/system-preset/91-paunch-container-shutdown.preset
|
||||
|
||||
- name: Create TripleO Container systemd service
|
||||
block:
|
||||
- name: "Deploy tripleo-container-shutdown and tripleo-start-podman-container"
|
||||
copy:
|
||||
src: "{{ role_path }}/files/{{ item }}"
|
||||
dest: "/usr/libexec/{{ item }}"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
loop:
|
||||
- 'tripleo-container-shutdown'
|
||||
- 'tripleo-start-podman-container'
|
||||
- name: "Create /usr/lib/systemd/system/tripleo-container-shutdown.service"
|
||||
copy:
|
||||
src: "{{ role_path }}/files/tripleo-container-shutdown-service"
|
||||
dest: "/usr/lib/systemd/system/tripleo-container-shutdown.service"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
- name: "Create /usr/lib/systemd/system-preset/91-tripleo-container-shutdown.preset"
|
||||
copy:
|
||||
src: "{{ role_path }}/files/91-tripleo-container-shutdown-preset"
|
||||
dest: "/usr/lib/systemd/system-preset/91-tripleo-container-shutdown.preset"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
- name: Enable and start tripleo-container-shutdown
|
||||
systemd:
|
||||
name: tripleo-container-shutdown
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
||||
- name: "Create /usr/lib/systemd/system/netns-placeholder.service"
|
||||
copy:
|
||||
src: "{{ role_path }}/files/netns-placeholder-service"
|
||||
dest: "/usr/lib/systemd/system/netns-placeholder.service"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
- name: "Create /usr/lib/systemd/system-preset/91-netns-placeholder.preset"
|
||||
copy:
|
||||
src: "{{ role_path }}/files/91-netns-placeholder-preset"
|
||||
dest: "/usr/lib/systemd/system-preset/91-netns-placeholder.preset"
|
||||
mode: '0700'
|
||||
owner: root
|
||||
group: root
|
||||
- name: Enable and start netns-placeholder
|
||||
systemd:
|
||||
name: netns-placeholder
|
||||
state: started
|
||||
enabled: true
|
||||
daemon_reload: true
|
|
@ -0,0 +1,10 @@
|
|||
[Unit]
|
||||
Description=tripleo_{{ container_sysd_name }} healthcheck
|
||||
After=tripleo-container-shutdown.service tripleo_{{ container_sysd_name }}.service
|
||||
Requisite=tripleo_{{ container_sysd_name }}.service
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/bin/podman exec {{ container_sysd_name }} {{ container_sysd_data.healthcheck.test }}
|
||||
SyslogIdentifier=healthcheck_{{ container_sysd_name }}
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,23 @@
|
|||
[Unit]
|
||||
Description={{ container_sysd_name }} container
|
||||
After=tripleo-container-shutdown.service
|
||||
Wants={{ container_sysd_data.depends_on | default([]) | join(',') }}
|
||||
[Service]
|
||||
Restart=always
|
||||
{% if container_sysd_data.depends_on is defined and (container_sysd_data.depends_on | length > 0) and podman_drop_in | default('false') %}
|
||||
ExecStart=/usr/libexec/tripleo-start-podman-container {{ container_sysd_name }}
|
||||
{% else %}
|
||||
ExecStart=/usr/bin/podman start {{ container_sysd_name }}
|
||||
{% endif %}
|
||||
ExecReload=/usr/bin/podman kill --signal HUP {{ container_sysd_name }}
|
||||
ExecStop=/usr/bin/podman stop -t {{ container_sysd_data.stop_grace_period | default(10) | int }} {{ container_sysd_name }}
|
||||
KillMode=none
|
||||
Type=forking
|
||||
PIDFile=/var/run/{{ container_sysd_name }}.pid
|
||||
{% if container_sysd_data.systemd_exec_flags is defined %}
|
||||
{% for s_flag, s_value in container_sysd_data.systemd_exec_flags.items() %}
|
||||
{{ s_flag }}={{ s_value }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -0,0 +1,9 @@
|
|||
[Unit]
|
||||
Description=tripleo_{{ container_sysd_name }} container healthcheck
|
||||
PartOf=tripleo_{{ container_sysd_name }}.service
|
||||
[Timer]
|
||||
OnActiveSec=120
|
||||
OnUnitActiveSec={{ container_sysd_data.check_interval | default(60) }}
|
||||
RandomizedDelaySec={{ 45 if container_sysd_data.check_interval is not defined else (container_sysd_data.check_interval * 3 / 4) | int | abs }}
|
||||
[Install]
|
||||
WantedBy=timers.target
|
|
@ -0,0 +1,21 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from oslotest import base
|
||||
|
||||
|
||||
class TestCase(base.BaseTestCase):
|
||||
|
||||
"""Test case base class for all unit tests."""
|
|
@ -0,0 +1,376 @@
|
|||
# Copyright 2019 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from tripleo_ansible.ansible_plugins.filter import helpers
|
||||
from tripleo_ansible.tests import base as tests_base
|
||||
|
||||
|
||||
class TestHelperFilters(tests_base.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestHelperFilters, self).setUp()
|
||||
self.filters = helpers.FilterModule()
|
||||
|
||||
def test_subsort(self):
|
||||
dict = {
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone'
|
||||
},
|
||||
'haproxy': {
|
||||
'image': 'quay.io/tripleo/haproxy'
|
||||
},
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
expected_ordered_dict = {
|
||||
0: [
|
||||
{'haproxy': {
|
||||
'image': 'quay.io/tripleo/haproxy',
|
||||
'start_order': 0
|
||||
}},
|
||||
{'mysql': {
|
||||
'image': 'quay.io/tripleo/mysql',
|
||||
'start_order': 0
|
||||
}}
|
||||
],
|
||||
1: [
|
||||
{'keystone': {
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'start_order': 1
|
||||
}}
|
||||
]
|
||||
}
|
||||
result = self.filters.subsort(dict_to_sort=dict,
|
||||
attribute='start_order')
|
||||
self.assertEqual(result, expected_ordered_dict)
|
||||
|
||||
def test_subsort_with_null_value(self):
|
||||
dict = {
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone'
|
||||
},
|
||||
'haproxy': {
|
||||
'image': 'quay.io/tripleo/haproxy'
|
||||
},
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
expected_ordered_dict = {
|
||||
0: [
|
||||
{'mysql': {
|
||||
'image': 'quay.io/tripleo/mysql',
|
||||
'start_order': 0
|
||||
}}
|
||||
],
|
||||
1: [
|
||||
{'keystone': {
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'start_order': 1
|
||||
}}
|
||||
],
|
||||
5: [
|
||||
{'haproxy': {
|
||||
'image': 'quay.io/tripleo/haproxy',
|
||||
'start_order': 5
|
||||
}}
|
||||
]
|
||||
}
|
||||
result = self.filters.subsort(dict_to_sort=dict,
|
||||
attribute='start_order', null_value=5)
|
||||
self.assertEqual(result, expected_ordered_dict)
|
||||
|
||||
def test_singledict(self):
|
||||
list = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone'
|
||||
},
|
||||
},
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
]
|
||||
expected_dict = {
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone'
|
||||
},
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
result = self.filters.singledict(list)
|
||||
self.assertEqual(result, expected_dict)
|
||||
|
||||
def test_list_of_keys(self):
|
||||
keys = [
|
||||
{
|
||||
'foo1': 'bar1'
|
||||
},
|
||||
{
|
||||
'foo2': 'bar2'
|
||||
},
|
||||
]
|
||||
expected_list = ['foo1', 'foo2']
|
||||
result = self.filters.list_of_keys(keys)
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_haskey(self):
|
||||
data = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
},
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
]
|
||||
expected_list = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
}
|
||||
]
|
||||
result = self.filters.haskey(batched_container_data=data,
|
||||
attribute='restart', value='always')
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_haskey_reverse(self):
|
||||
data = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
},
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
]
|
||||
expected_list = [
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
},
|
||||
}
|
||||
]
|
||||
result = self.filters.haskey(batched_container_data=data,
|
||||
attribute='restart',
|
||||
value='always',
|
||||
reverse=True)
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_haskey_any(self):
|
||||
data = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
},
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
]
|
||||
expected_list = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
}
|
||||
]
|
||||
result = self.filters.haskey(batched_container_data=data,
|
||||
attribute='restart',
|
||||
any=True)
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_haskey_any_reverse(self):
|
||||
data = [
|
||||
{
|
||||
'keystone': {
|
||||
'start_order': 1,
|
||||
'image': 'quay.io/tripleo/keystone',
|
||||
'restart': 'always'
|
||||
},
|
||||
},
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
}
|
||||
}
|
||||
]
|
||||
expected_list = [
|
||||
{
|
||||
'mysql': {
|
||||
'start_order': 0,
|
||||
'image': 'quay.io/tripleo/mysql'
|
||||
},
|
||||
}
|
||||
]
|
||||
result = self.filters.haskey(batched_container_data=data,
|
||||
attribute='restart',
|
||||
reverse=True,
|
||||
any=True)
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_needs_delete(self):
|
||||
data = [
|
||||
{
|
||||
'Name': 'mysql',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'config_id': 'dontdeleteme',
|
||||
'managed_by': 'triple_ansible',
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'rabbitmq',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'managed_by': 'tripleo_ansible',
|
||||
'config_id': 'tripleo_step1',
|
||||
'container_name': 'rabbitmq',
|
||||
'name': 'rabbitmq',
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'swift',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'managed_by': 'tripleo_ansible',
|
||||
'config_id': 'tripleo_step1',
|
||||
'container_name': 'swift',
|
||||
'name': 'swift',
|
||||
'config_data': "{'foo': 'bar'}",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'heat',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'managed_by': 'tripleo_ansible',
|
||||
'config_id': 'tripleo_step1',
|
||||
'container_name': 'heat',
|
||||
'name': 'heat',
|
||||
'config_data': "{'start_order': 0}",
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'haproxy',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'managed_by': 'tripleo_ansible',
|
||||
'config_id': 'tripleo_step1',
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'tripleo',
|
||||
'Config': {
|
||||
'Labels': {
|
||||
'foo': 'bar'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
'Name': 'none_tripleo',
|
||||
'Config': {
|
||||
'Labels': None,
|
||||
}
|
||||
},
|
||||
]
|
||||
config = {
|
||||
# we don't want that container to be touched: no restart
|
||||
'mysql': '',
|
||||
# container has no Config, therefore no Labels: restart needed
|
||||
'rabbitmq': '',
|
||||
# container has no config_data: restart needed
|
||||
'haproxy': '',
|
||||
# container isn't part of config_id: no restart
|
||||
'tripleo': '',
|
||||
# container isn't in container_infos but not part of config_id:
|
||||
# no restart.
|
||||
'doesnt_exist': '',
|
||||
# config_data didn't change: no restart
|
||||
'swift': {'foo': 'bar'},
|
||||
# config_data changed: restart needed
|
||||
'heat': {'start_order': 1},
|
||||
}
|
||||
expected_list = ['rabbitmq', 'haproxy', 'heat']
|
||||
result = self.filters.needs_delete(container_infos=data,
|
||||
config=config,
|
||||
config_id='tripleo_step1')
|
||||
self.assertEqual(result, expected_list)
|
||||
|
||||
def test_container_exec_cmd(self):
|
||||
data = {
|
||||
"action": "exec",
|
||||
"environment": {
|
||||
"OS_BOOTSTRAP_PASSWORD": "IH7PdaZc5DozbmunSTjMa7",
|
||||
"KOLLA_BOOTSTRAP": True
|
||||
},
|
||||
"start_order": 3,
|
||||
"command": [
|
||||
"keystone",
|
||||
"/usr/bin/bootstrap_host_exec",
|
||||
"keystone",
|
||||
"keystone-manage",
|
||||
"bootstrap"
|
||||
],
|
||||
"user": "root"
|
||||
}
|
||||
expected_cmd = ['podman', 'exec', '--user=root',
|
||||
'--env=KOLLA_BOOTSTRAP=True',
|
||||
'--env=OS_BOOTSTRAP_PASSWORD=IH7PdaZc5DozbmunSTjMa7',
|
||||
'keystone', '/usr/bin/bootstrap_host_exec',
|
||||
'keystone', 'keystone-manage', 'bootstrap']
|
||||
result = self.filters.container_exec_cmd(data=data)
|
||||
self.assertEqual(result, expected_cmd)
|
|
@ -3,6 +3,7 @@
|
|||
templates:
|
||||
- tripleo-ansible-molecule-jobs
|
||||
- release-notes-jobs-python3
|
||||
- openstack-python3-ussuri-jobs
|
||||
check:
|
||||
jobs:
|
||||
- openstack-tox-linters
|
||||
|
|
|
@ -38,6 +38,8 @@
|
|||
- tripleo-ansible-centos-7-molecule-backup-and-restore
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-packages
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-hosts-entries
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-container-manage
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-modules
|
||||
gate:
|
||||
jobs:
|
||||
- tripleo-ansible-centos-7-molecule-aide
|
||||
|
@ -76,6 +78,8 @@
|
|||
- tripleo-ansible-centos-7-molecule-backup-and-restore
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-packages
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-hosts-entries
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-container-manage
|
||||
- tripleo-ansible-centos-7-molecule-tripleo-modules
|
||||
name: tripleo-ansible-molecule-jobs
|
||||
- job:
|
||||
files:
|
||||
|
@ -338,3 +342,18 @@
|
|||
parent: tripleo-ansible-centos-7-base
|
||||
vars:
|
||||
tripleo_role_name: tripleo-hosts-entries
|
||||
- job:
|
||||
files:
|
||||
- ^tripleo_ansible/roles/tripleo-container-manage/.*
|
||||
name: tripleo-ansible-centos-7-molecule-tripleo-container-manage
|
||||
parent: tripleo-ansible-centos-7-base
|
||||
vars:
|
||||
tripleo_role_name: tripleo-container-manage
|
||||
|
||||
- job:
|
||||
files:
|
||||
- ^tripleo_ansible/ansible_plugins/.*$
|
||||
- ^tox.ini
|
||||
- ^molecule-requirements.txt
|
||||
name: tripleo-ansible-centos-7-molecule-tripleo-modules
|
||||
parent: tripleo-ansible-centos-7-base
|
||||
|
|
|
@ -4,10 +4,21 @@
|
|||
environment:
|
||||
ANSIBLE_LOG_PATH: "{{ ansible_user_dir }}/zuul-output/logs/ansible-execution.log"
|
||||
pre_tasks:
|
||||
|
||||
- name: Set project path fact
|
||||
set_fact:
|
||||
tripleo_ansible_project_path: "{{ ansible_user_dir }}/{{ zuul.project.src_dir }}"
|
||||
|
||||
- name: Set role or plugin path fact
|
||||
set_fact:
|
||||
triple_ansible_testdir: "{{ tripleo_ansible_project_path }}/tripleo_ansible/roles/{{ tripleo_role_name }}"
|
||||
when: tripleo_role_name is defined and tripleo_role_name
|
||||
|
||||
- name: Set role or plugin path fact
|
||||
set_fact:
|
||||
triple_ansible_testdir: "{{ tripleo_ansible_project_path }}/tripleo_ansible/ansible_plugins/tests"
|
||||
when: tripleo_role_name is not defined
|
||||
|
||||
- name: Set action plugin path fact
|
||||
set_fact:
|
||||
tripleo_action_plugins_paths:
|
||||
|
@ -25,7 +36,7 @@
|
|||
--ansible-args='{{ tripleo_job_ansible_args | default('') }}' \
|
||||
{{ tripleo_ansible_project_path }}/tests/test_molecule.py
|
||||
args:
|
||||
chdir: "{{ tripleo_ansible_project_path }}/tripleo_ansible/roles/{{ tripleo_role_name }}"
|
||||
chdir: "{{ triple_ansible_testdir }}"
|
||||
executable: /bin/bash
|
||||
environment:
|
||||
ANSIBLE_ACTION_PLUGINS: "{{ tripleo_action_plugins_paths | join(':') }}"
|
||||
|
|
Loading…
Reference in New Issue