Merge "tripleo-container-manage: use async"

This commit is contained in:
Zuul 2019-11-12 23:07:01 +00:00 committed by Gerrit Code Review
commit 7787a68c8d
7 changed files with 195 additions and 85 deletions

View File

@ -25,7 +25,8 @@ class FilterModule(object):
return {
'singledict': self.singledict,
'subsort': self.subsort,
'needs_delete': self.needs_delete
'needs_delete': self.needs_delete,
'haskey': self.haskey
}
def subsort(self, dict_to_sort, attribute, null_value=None):
@ -115,7 +116,12 @@ class FilterModule(object):
for c in container_infos if c_name == c['Name']]
except KeyError:
continue
c_facts = c_facts[0] if len(c_facts) == 1 else {}
# Build c_facts so it can be compared later with config_data;
# both will be json.dumps objects.
c_facts = json.dumps(
json.loads(c_facts[0]).get(c_name)
) if len(c_facts) == 1 else {}
# 0 was picked since it's the null_value for the subsort filter.
# When a container config doesn't provide the start_order, it'll be
@ -131,3 +137,35 @@ class FilterModule(object):
continue
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

View File

@ -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: "Get {{ lookup('dict', container_data).value.command.0 }} container status"
set_fact:
container_running: >-
{{ podman_containers.containers | selectattr('Name', 'equalto', lookup('dict', container_data).value.command.0) |
map(attribute='State.Running') | first | default(false) }}
- name: "Fail if {{ lookup('dict', container_data).key }} is not running"
fail:
msg: "Can't run container exec for {{ lookup('dict', container_data).key }}, {{ lookup('dict', container_data).value.command.0 }} is not running"
when:
- not container_running|default(false)|bool

View File

@ -14,67 +14,62 @@
# License for the specific language governing permissions and limitations
# under the License.
- name: Prepare container facts
set_fact:
container_name: "{{ batched_container_data.0.keys() | first }}"
container_data: "{{ batched_container_data.0[batched_container_data.0.keys()|first] }}"
- name: "{{ container_name }} : Run execute task"
include_tasks: exec.yml
when:
- container_data.action is defined
- container_data.action == 'exec'
- name: "{{ container_name }} : Manage container"
when: container_data.action is not defined
# TODO(emilien) Re-add podman_container.changed from async
- name: "Async container create/run"
async: 300
poll: 0
register: create_async_results
loop: "{{ batched_container_data | haskey(attribute='action', reverse=True) }}"
loop_control:
loop_var: container_data
podman_container:
cap_add: "{{ container_data.cap_add | default(omit) }}"
cap_drop: "{{ container_data.cap_drop | default(omit) }}"
command: "{{ container_data.command | default(omit) }}"
conmon_pidfile: "/var/run/{{ container_name }}.pid"
cpu_shares: "{{ container_data.cpu_shares | default(omit) | int }}"
# cpuset_cpus: "{{ container_data.cpuset_cpus | default(omit) }}"
detach: "{{ container_data.detach | default(true) }}"
env: "{{ container_data.environment | default({}, true) }}"
env_file: "{{ container_data.env_file | default(omit) }}"
etc_hosts: "{{ container_data.extra_hosts | default(omit) }}"
group_add: "{{ container_data.group_add | default(omit) }}"
hostname: "{{ container_data.hostname | default(omit) }}"
image: "{{ container_data.image }}"
interactive: "{{ container_data.interactive | default(false) }}"
ipc: "{{ container_data.ipc | default(omit) }}"
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) }}"
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: "{{ container_name }}"
container_name: "{{ lookup('dict', container_data).key }}"
managed_by: tripleo_ansible
config_data: "{{ container_data | to_json }}"
log_driver: 'k8s-file'
log_opt: "path={{ tripleo_container_manage_log_path }}/{{ container_name }}.log"
memory: "{{ container_data.mem_limit | default(omit) }}"
memory_swap: "{{ container_data.mem_swappiness | default(omit) }}"
name: "{{ container_name }}"
net: "{{ container_data.net | default('none') }}"
pid: "{{ container_data.pid | default(omit) }}"
privileged: "{{ container_data.privileged | default(false) }}"
rm: "{{ container_data.remove | default(false) }}"
security_opt: "{{ container_data.security_opt | default(omit) }}"
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: "{{ container_data.stop_signal | default(omit) }}"
stop_timeout: "{{ container_data.stop_grace_period | default(omit) | int }}"
tty: "{{ container_data.tty | default(false) }}"
ulimit: "{{ container_data.ulimit | default(omit) }}"
user: "{{ container_data.user | default(omit) }}"
uts: "{{ container_data.uts | default(omit) }}"
volume: "{{ container_data.volumes | default(omit) }}"
volumes_from: "{{ container_data.volumes_from | default(omit) }}"
register: podman_container
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: "{{ container_name }} : Manage systemd service }}"
include_tasks: systemd.yml
when:
- container_data.action is not defined
- container_data.restart is defined
# 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.
- container_data.restart == 'always' or container_data.restart == 'unless-stopped'
- 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
when: not ansible_check_mode|bool

View File

@ -14,28 +14,36 @@
# License for the specific language governing permissions and limitations
# under the License.
- name: "Execute a command within a running container for {{ container_name }}"
check_mode: false
block:
- name: "Check if {{ container_data.command.0 }} container is running"
block:
- name: Get container status
set_fact:
container_running: >-
{{ podman_containers.containers | selectattr('Name', 'equalto', container_data.command.0 ) |
map(attribute='State.Running') | first | default(false) }}
- name: "Fail if {{ container_data.command.0 }} is not running"
fail:
msg: "Can't run container exec for {{ container_name }}, {{ container_data.command.0 }} is not running"
when:
- not container_running|bool
- name: "Prepare the exec command for {{ container_name }}"
set_fact:
cmd_template:
- "{{ tripleo_container_manage_cli }}"
- "exec"
- "-u"
- "{{ container_data.user if container_data.user is defined else 'root' }}"
- name: "Run the container exec for {{ container_name }}"
command:
argv: "{{ cmd_template + container_data.command }}"
- 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_data
- name: "Run actions async"
command:
argv: "{{ cmd_template + lookup('dict', container_data).value.command }}"
vars:
cmd_template:
- "{{ tripleo_container_manage_cli }}"
- "exec"
- "-u"
- "{{ lookup('dict', container_data).value.user if lookup('dict', container_data).value.user is defined else 'root' }}"
async: 60
poll: 0
register: exec_async_results
loop: "{{ batched_container_data | haskey(attribute='action', value='exec') }}"
loop_control:
loop_var: container_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

View File

@ -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

View File

@ -21,7 +21,7 @@
- tripleo_container_manage_cli == 'podman'
- name: "Batching items for start_order {{ order }}"
include_tasks: podman/create.yml
include_tasks: podman/manage.yml
loop: "{{ data | batch(tripleo_container_manage_concurrency) | list }}"
loop_control:
loop_var: batched_container_data

View File

@ -13,15 +13,23 @@
# 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: Set container_name and container_data facts
set_fact:
container_name: "{{ lookup('dict', container_config).key }}"
container_data: "{{ lookup('dict', container_config).value }}"
- name: "Start systemd service for {{ container_name }}"
block:
- name: "Remove trailing .requires for {{ container_name }}"
@ -44,7 +52,8 @@
enabled: true
daemon_reload: true
when:
- systemd_file.changed or podman_container.changed
# Re-add podman_container.changed from async
- systemd_file.changed
- name: "Manage systemd healthcheck for {{ container_name }}"
when:
- not tripleo_container_manage_healthcheck_disabled