From 60342041a3eb2c091916e36e4bf66199937db335 Mon Sep 17 00:00:00 2001 From: Alex Schultz Date: Thu, 7 Nov 2019 16:06:54 -0700 Subject: [PATCH] [TRAIN] Backport tripleo-systemd-wrapper (squash) This is a combination of 4 commits. This is the 1st commit message: Implement tripleo-systemd-wrapper role This patch adds a new role that will be used to manage side containers with systemd instead of docker.socket or nsenter. The main use case here is Neutron, although this role is designed to work with any service. This role will create a series of systemd files to monitor a file which gets mounted into a container. Additionally a wrapper script is generated which is mounted in the container that will provide the arguments that should be used to launch new containers. Blueprint: safe-side-containers Change-Id: I4821b7ca0260e4dfd1717ba976cef700d160f84f Co-Authored-By: Dan Prince Co-Authored-By: Emilien Macchi Co-Authored-By: Alex Schultz (cherry picked from commit 699249f1790dd5646556173bf5331e7e71135ad4) This is the commit message #2: Remove --rm=true from sidecar container sync Neutron uses kill-scripts which remove the container after stopping it. If the container is launched with docker and --rm=true, the container will automatically be cleaned up and the $(CLI) rm in the kill script with error out because the container can't be found. Related-Bug: #1858662 Change-Id: I3d7940cb0816adce58e0fa778469dcec95302f67 (cherry picked from commit 17d97f2618e56be606c9307551efdaadda8dff69) This is the commit message #3: Fix substitution in kill-script In the kill-script there is a string "Unknown action ${SIG} for ${$CT_NAME} ${CT_ID}" which results in a "bad substitution" error, as there is no variable named with what the contents of the CT_NAME environment variable contains. Remove the extraneous '$'. Change-Id: I4c76071083bf5cb4f876d3b78c379822a8bd8db1 Fixes-Bug: #1860155 (cherry picked from commit b45d4c6d219e8e27219bca341acdfd634155d6f6) This is the commit message #4: Add handling of signal 15 in kill script The reason bug #1860155 was triggered was because the kill script did not have a stanza for handling the signal that was passed in, which is signal 15. Since signal 15 is unhandled, keepalived processes will still stick around. Add handling for signal 15. Change-Id: I632a3ef5ec137df10f647335f6354589c2316fd0 Related-bug: #1860155 (cherry picked from commit 06dc258a28784db98e44a3de488c204f01b97613) --- .../roles/role-tripleo-systemd-wrapper.rst | 90 ++++++++++++++ .../tripleo-systemd-wrapper/defaults/main.yml | 22 ++++ .../tripleo-systemd-wrapper/meta/main.yml | 44 +++++++ .../molecule/default/Dockerfile | 37 ++++++ .../molecule/default/molecule.yml | 60 ++++++++++ .../molecule/default/playbook.yml | 29 +++++ .../molecule/default/prepare.yml | 21 ++++ .../molecule/default/verify.yml | 15 +++ .../tripleo-systemd-wrapper/tasks/main.yml | 112 ++++++++++++++++++ .../templates/service.path.j2 | 5 + .../templates/service.service.j2 | 10 ++ .../templates/service_command.j2 | 8 ++ .../templates/service_kill.j2 | 78 ++++++++++++ .../templates/service_sync.j2 | 52 ++++++++ .../templates/service_wrapper.j2 | 17 +++ zuul.d/molecule.yaml | 10 ++ 16 files changed, 610 insertions(+) create mode 100644 doc/source/roles/role-tripleo-systemd-wrapper.rst create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/defaults/main.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/meta/main.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/Dockerfile create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/molecule.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/playbook.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/prepare.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/verify.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/tasks/main.yml create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.path.j2 create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.service.j2 create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_command.j2 create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_kill.j2 create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_sync.j2 create mode 100644 tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_wrapper.j2 diff --git a/doc/source/roles/role-tripleo-systemd-wrapper.rst b/doc/source/roles/role-tripleo-systemd-wrapper.rst new file mode 100644 index 000000000..a8e754a90 --- /dev/null +++ b/doc/source/roles/role-tripleo-systemd-wrapper.rst @@ -0,0 +1,90 @@ +============================== +Role - tripleo-systemd-wrapper +============================== + +An Ansible role to manage systemd wrappers in TripleO. + + +What it does +------------ + +In a nutshell, this role helps to configure systemd so it manages side car +containers (e.g. dnsmasq, HAproxy, keepalived, etc, requested by Neutron +agents). + +Underneath, this role creates four files: + +- /etc/systemd/system/.path + +This file will allow the host to monitor changes to +/var/lib//-processes-timestamp which keeps track of the +service processes in a text file. +-processes-timestamp file is managed by the -wrapper script +with a flock to avoid race conditions. + +- /etc/systemd/system/.service + +This file is the SystemD service that will run the synchronization of +processes. It is run as "Type=oneshot" because we just want the unit to execute +the -process-sync script without keeping active processes. +In this Ansible role, we automatically enable and start this service. + +- /var/lib///wrapper + +Script that wrap the service lifecycle management. It takes care of starting +the side containers everytime the service is called. +Because it's a wrapper, the script has to be bind mounted from the host into +the container. + +e.g.: /var/lib/neutron/neutron-dnsmasq/wrapper:/usr/local/bin/dnsmasq:ro + +So in the case of Neutron DHCP agent, when an operator will create a network, +Neutron will call dnsmasq which will actually call our side container wrapper. + +- /var/lib/neutron//process-sync + +This script helps to keep the list of processes (side containers) up to date, +so we don't create more than one container per namespace. We use flock to avoid +a race condition if at the same time the wrapper is called. The flock protects +the list of processes and also the timestamps. + + +Requirements +------------ + +It requires systemd on the host. This role isn't designed nor tested to run +within a container. + +Role variables +-------------- + +- tripleo_systemd_wrapper_cmd: -- Command to run in the container. +- tripleo_systemd_wrapper_config_bind_mount: -- Bind-mount used for container config. +- tripleo_systemd_wrapper_container_cli: -- Name of the container cli command to use (podman | docker). +- tripleo_systemd_wrapper_docker_additional_sockets: -- Additional docker sockets to use when interacting with docker +- tripleo_systemd_wrapper_image_name: -- Container image name. +- tripleo_systemd_wrapper_service_dir: -- Directory where state files will be created. +- tripleo_systemd_wrapper_service_kill_script: -- Name of the script to create for the kill action +- tripleo_systemd_wrapper_service_name: -- Name of the service to wrap in Systemd. + +Example Playbook +---------------- + +Sample playbook to call the role:: + + - name: Create Neutron dnsmasq systemd wrapper + hosts: all + roles: + - tripleo-systemd-wrapper + vars: + tripleo_systemd_wrapper_cmd: "/usr/sbin/dnsmasq -k" + tripleo_systemd_wrapper_config_bind_mount: "/var/lib/config-data/puppet-generated/neutron/etc/neutron:/etc/neutron:ro" + tripleo_systemd_wrapper_container_cli: podman + tripleo_systemd_wrapper_image_name: "docker.io/tripleomaster/centos-binary-neutron-dhcp-agent:current-tripleo" + tripleo_systemd_wrapper_service_dir: /var/lib/neutron + tripleo_systemd_wrapper_service_kill_script: dnsmasq-kill + tripleo_systemd_wrapper_service_name: neutron-dnsmasq + + +.. ansibleautoplugin:: + :role: tripleo_ansible/roles/tripleo-systemd-wrapper diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/defaults/main.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/defaults/main.yml new file mode 100644 index 000000000..d7050d6e4 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/defaults/main.yml @@ -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. + + +# All variables intended for modification should be placed in this file. + +# All variables within this role should have a prefix of "tripleo_systemd_wrapper" +tripleo_systemd_wrapper_debug: false +tripleo_systemd_wrapper_container_cli: podman diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/meta/main.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/meta/main.yml new file mode 100644 index 000000000..884423f0b --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/meta/main.yml @@ -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-systemd-wrapper + 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: [] diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/Dockerfile b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/Dockerfile new file mode 100644 index 000000000..e0534b4d1 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/Dockerfile @@ -0,0 +1,37 @@ +# Molecule managed +# 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. + + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python sudo bash ca-certificates && apt-get clean; \ + elif [ $(command -v dnf) ]; then dnf makecache && dnf --assumeyes install sudo python*-devel python*-dnf bash {{ item.pkg_extras | default('') }} && dnf clean all; \ + elif [ $(command -v yum) ]; then yum makecache fast && yum install -y python sudo yum-plugin-ovl python-setuptools bash {{ item.pkg_extras | default('') }} && sed -i 's/plugins=0/plugins=1/g' /etc/yum.conf && yum clean all; \ + elif [ $(command -v zypper) ]; then zypper refresh && zypper install -y python sudo bash python-xml {{ item.pkg_extras | default('') }} && zypper clean -a; \ + elif [ $(command -v apk) ]; then apk update && apk add --no-cache python sudo bash ca-certificates {{ item.pkg_extras | default('') }}; \ + elif [ $(command -v xbps-install) ]; then xbps-install -Syu && xbps-install -y python sudo bash ca-certificates {{ item.pkg_extras | default('') }} && xbps-remove -O; fi + +{% for pkg in item.easy_install | default([]) %} +# install pip for centos where there is no python-pip rpm in default repos +RUN easy_install {{ pkg }} +{% endfor %} + + +CMD ["sh", "-c", "while true; do sleep 10000; done"] diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/molecule.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/molecule.yml new file mode 100644 index 000000000..1bbda47e2 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/molecule.yml @@ -0,0 +1,60 @@ +--- +driver: + name: docker + +log: true + +platforms: + - name: centos7 + hostname: centos7 + image: centos:7 + dockerfile: Dockerfile + pkg_extras: python-setuptools + easy_install: + - pip + environment: &env + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + # needed for systemd stuff + command: /sbin/init + capabilities: + - SYS_ADMIN + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + + - name: centos8 + hostname: centos8 + image: centos:8 + dockerfile: Dockerfile + pkg_extras: python*-setuptools + environment: + <<: *env + # needed for systemd stuff + command: /sbin/init + capabilities: + - SYS_ADMIN + volumes: + - /sys/fs/cgroup:/sys/fs/cgroup:ro + +provisioner: + name: ansible + log: true + env: + ANSIBLE_STDOUT_CALLBACK: yaml + +scenario: + test_sequence: + - destroy + - create + - prepare + - converge + - verify + - destroy + +lint: + enabled: false + +verifier: + name: testinfra + lint: + name: flake8 diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/playbook.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/playbook.yml new file mode 100644 index 000000000..bdccf5cb4 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/playbook.yml @@ -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: Converge + hosts: all + roles: + - "tripleo-systemd-wrapper" + vars: + tripleo_systemd_wrapper_cmd: "/usr/sbin/dnsmasq -k" + tripleo_systemd_wrapper_config_bind_mount: "/var/lib/config-data/puppet-generated/neutron/etc/neutron:/etc/neutron:ro" + tripleo_systemd_wrapper_container_cli: podman + tripleo_systemd_wrapper_image_name: "docker.io/tripleomaster/centos-binary-neutron-dhcp-agent:current-tripleo" + tripleo_systemd_wrapper_service_dir: /var/lib/neutron + tripleo_systemd_wrapper_service_kill_script: dnsmasq-kill + tripleo_systemd_wrapper_service_name: neutron-dnsmasq diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/prepare.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/prepare.yml new file mode 100644 index 000000000..ef85c3128 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/prepare.yml @@ -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: Prepare + hosts: all + roles: + - role: test_deps diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/verify.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/verify.yml new file mode 100644 index 000000000..dfd4c7352 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/molecule/default/verify.yml @@ -0,0 +1,15 @@ +--- +# 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. diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/tasks/main.yml b/tripleo_ansible/roles/tripleo-systemd-wrapper/tasks/main.yml new file mode 100644 index 000000000..d335b6f09 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/tasks/main.yml @@ -0,0 +1,112 @@ +--- +# 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-systemd-wrapper" 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: "Ensure {{ tripleo_systemd_wrapper_service_dir }} exists" + become: true + file: + path: "{{ tripleo_systemd_wrapper_service_dir }}" + state: directory + setype: svirt_sandbox_file_t + selevel: s0 + +- name: "Ensure {{ tripleo_systemd_wrapper_service_dir }}/kill_scripts exists" + become: true + file: + path: "{{ tripleo_systemd_wrapper_service_dir }}/kill_scripts" + state: directory + setype: svirt_sandbox_file_t + selevel: s0 + when: tripleo_systemd_wrapper_service_kill_script is defined + +- name: "Ensure {{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }} exists" + become: true + file: + path: "{{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}" + state: directory + setype: svirt_sandbox_file_t + selevel: s0 + mode: '4750' + +- name: "Create {{ tripleo_systemd_wrapper_service_name }} process command script" + become: true + template: + dest: "{{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/command" + src: service_command.j2 + mode: '0750' + +# TODO(emilien) figure out secure permissions & ownership & labeling +- name: "Create {{ tripleo_systemd_wrapper_service_name }} wrapper script" + become: true + template: + dest: "{{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/wrapper" + src: service_wrapper.j2 + mode: '0750' + +# TODO(emilien) figure out secure permissions & ownership & labeling +- name: "Create {{ tripleo_systemd_wrapper_service_name }} process sync script" + become: true + template: + dest: "{{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/sync" + src: service_sync.j2 + mode: '0750' + +- name: "Create {{ tripleo_systemd_wrapper_service_name }} service kill script" + become: true + template: + dest: "{{ tripleo_systemd_wrapper_service_dir }}/kill_scripts/{{ tripleo_systemd_wrapper_service_kill_script }}" + src: service_kill.j2 + mode: '0755' + when: tripleo_systemd_wrapper_service_kill_script is defined + +- name: "Create {{ tripleo_systemd_wrapper_service_name }} systemd path file" + become: true + template: + dest: "/etc/systemd/system/{{ tripleo_systemd_wrapper_service_name }}.path" + src: service.path.j2 + mode: '0644' + +- name: "Create {{ tripleo_systemd_wrapper_service_name }} systemd service file" + become: true + template: + dest: "/etc/systemd/system/{{ tripleo_systemd_wrapper_service_name }}.service" + src: service.service.j2 + mode: '0644' + +- name: "Start {{ tripleo_systemd_wrapper_service_name }} path" + become: true + systemd: + name: "{{ tripleo_systemd_wrapper_service_name }}.path" + enabled: true + state: started + daemon_reload: true diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.path.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.path.j2 new file mode 100644 index 000000000..59676d05b --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.path.j2 @@ -0,0 +1,5 @@ +[Path] +PathModified={{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/processes-timestamp + +[Install] +WantedBy=multi-user.target diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.service.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.service.j2 new file mode 100644 index 000000000..b6cbf43b8 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service.service.j2 @@ -0,0 +1,10 @@ +[Unit] +Description=Tripleo {{ tripleo_systemd_wrapper_service_name }} sync service + +[Service] +{% if tripleo_systemd_wrapper_debug %} +Environment=SYSTEMD_LOG_LEVEL=debug +{% endif %} +Type=oneshot +ExecStart={{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/sync +User=root diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_command.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_command.j2 new file mode 100644 index 000000000..9cae163ea --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_command.j2 @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +# +# We wrap the command in a bash script so we can do complex logic +# to make the command dynamic based on the internals of the container. +# This is necessary for backwards compatibily when commands change their +# args based on version (I'm looking at you haproxy). +# +{{ tripleo_systemd_wrapper_cmd }} $@ diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_kill.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_kill.j2 new file mode 100644 index 000000000..311fe3d00 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_kill.j2 @@ -0,0 +1,78 @@ +#!/bin/bash +{% if tripleo_systemd_wrapper_debug|bool -%} +set -x +{% endif -%} +add_date() { + echo "$(date) $@" +} + +# Set up script logging for debugging purpose. +# It will be taken care of by logrotate since there is the .log +# suffix. +exec 3>&1 4>&2 +trap 'exec 2>&4 1>&3' 0 1 2 3 +exec 1>>/var/log/neutron/kill-script.log 2>&1 + +SIG=$1 +PID=$2 +NETNS=$(ip netns identify ${PID}) + +if [ "x${NETNS}" == "x" ]; then + add_date "No network namespace detected, exiting" + exit 1 +fi + +{% if tripleo_systemd_wrapper_container_cli == 'podman' %} +CLI="nsenter --net=/run/netns/${NETNS} --preserve-credentials -m -t 1 podman" +{% elif tripleo_systemd_wrapper_container_cli == 'docker' %} +{% if tripleo_systemd_wrapper_docker_additional_sockets and tripleo_systemd_wrapper_docker_additional_sockets|length > 0-%} +export DOCKER_HOST=unix://{{ tripleo_systemd_wrapper_docker_additional_sockets[0] }} +{% endif -%} +CLI='docker' +{% else %} +CLI='echo noop' +{% endif %} + +kill_container() { + add_date "Stopping container $1 ($2)" + $CLI stop $2 + add_date "Deleting container $1 ($2)" + $CLI rm $2 +} + +signal_container() { + SIGNAL=$3 + if [ -z "$SIGNAL" ]; then + SIGNAL="HUP" + fi + add_date "Sending signal '$SIGNAL' to $1 ($2)" + $CLI kill --signal $SIGNAL $2 +} + +{% raw -%} +if [ -f /proc/$PID/cgroup ]; then + # Get container ID based on process cgroups + CT_ID=$(awk 'BEGIN {FS="[-.]"} /name=/{print $3}' /proc/$PID/cgroup) + CT_NAME=$($CLI inspect -f '{{.Name}}' $CT_ID) + + case $SIG in + HUP) + signal_container $CT_NAME $CT_ID + ;; + 9) + kill_container $CT_NAME $CT_ID + ;; + 15) + signal_container $CT_NAME $CT_ID 15 + ;; + *) + add_date "Unknown action ${SIG} for ${CT_NAME} ${CT_ID}" + exit 1 + ;; + esac + +else + add_date "No such PID: ${PID}" + exit 1 +fi +{% endraw %} diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_sync.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_sync.j2 new file mode 100644 index 000000000..1d54c7609 --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_sync.j2 @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +{% if tripleo_systemd_wrapper_debug %} +set -x +{% endif %} + +function start_service { + local NETNS=$1 + shift + local NAME="{{ tripleo_systemd_wrapper_service_name }}-${NETNS}" + local CLI='{{ tripleo_systemd_wrapper_container_cli }}' + local CMD="{{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/command" + local CONTAINER_CMD="ip netns exec ${NETNS} ${CMD}" + {% if tripleo_systemd_wrapper_container_cli == 'podman' %} + local LOGGING="--log-driver k8s-file --log-opt path=/var/log/containers/stdouts/${NAME}.log" + {% else %} + local LOGGING='' + {% endif %} + + $CLI stop $NAME &> /dev/null || true + $CLI rm -f $NAME &> /dev/null || true + $CLI run --detach \ + -v "{{ tripleo_systemd_wrapper_config_bind_mount }}" \ + -v "/run/netns:/run/netns:shared" \ + -v "{{ tripleo_systemd_wrapper_service_dir }}:{{ tripleo_systemd_wrapper_service_dir }}:z,shared" \ + -v "/dev/log:/dev/log" $LOGGING \ + --net host \ + --pid host \ + --privileged \ + -u root \ + --name $NAME \ + {{ tripleo_systemd_wrapper_image_name }} \ + $CONTAINER_CMD $@ +} + +exec {lock_fd}>/var/lock/{{ tripleo_systemd_wrapper_service_name }}-processes.lock || exit 1 +# In case service_wrapper script already locked the commands, we just wait. +flock "$lock_fd" + +IFS=$'\n' +for LINE in $(cat {{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/processes); do + NETNS=$(echo $LINE | awk '{ print $1 }') + IFS=$' ' ARGS=$(echo $LINE | sed -e "s|$NETNS ||" | xargs) + # TODO(emilien) investigate if we should rather run docker/podman ps instead of ps on the host + if ! ps -e -o pid,command | grep "$(echo $NETNS | sed 's|^[^-]*\-||')" | grep -v grep &> /dev/null; then + start_service $NETNS $ARGS + fi +done +# truncate the file so we don't start them again +:> {{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/processes + +flock -u "$lock_fd" + diff --git a/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_wrapper.j2 b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_wrapper.j2 new file mode 100644 index 000000000..2ae4eec3a --- /dev/null +++ b/tripleo_ansible/roles/tripleo-systemd-wrapper/templates/service_wrapper.j2 @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +{% if tripleo_systemd_wrapper_debug %} +set -x +{% endif %} + +ARGS="$@" +NETNS=$(ip netns identify) + +exec {lock_fd}>/var/lock/{{ tripleo_systemd_wrapper_service_name }}-processes.lock || exit 1 +# In case service_sync script already locked the commands, we just wait. +flock "$lock_fd" + +echo "$NETNS $ARGS" >> {{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/processes +# only update the timestamp which fires systemd if there was an update +date > {{ tripleo_systemd_wrapper_service_dir }}/{{ tripleo_systemd_wrapper_service_name }}/processes-timestamp + +flock -u "$lock_fd" diff --git a/zuul.d/molecule.yaml b/zuul.d/molecule.yaml index b8a9ef509..88ef37eae 100644 --- a/zuul.d/molecule.yaml +++ b/zuul.d/molecule.yaml @@ -41,6 +41,7 @@ - tripleo-ansible-centos-7-molecule-tripleo-container-manage - tripleo-ansible-centos-7-molecule-tripleo-modules - tripleo-ansible-centos-7-molecule-tripleo-keystone-resources + - tripleo-ansible-centos-7-molecule-tripleo-systemd-wrapper gate: jobs: - tripleo-ansible-centos-7-molecule-aide @@ -82,6 +83,7 @@ - tripleo-ansible-centos-7-molecule-tripleo-container-manage - tripleo-ansible-centos-7-molecule-tripleo-modules - tripleo-ansible-centos-7-molecule-tripleo-keystone-resources + - tripleo-ansible-centos-7-molecule-tripleo-systemd-wrapper name: tripleo-ansible-molecule-jobs - job: files: @@ -367,3 +369,11 @@ parent: tripleo-ansible-centos-7-base vars: tripleo_role_name: tripleo-keystone-resources + +- job: + files: + - ^tripleo_ansible/roles/tripleo-systemd-wrapper/.* + name: tripleo-ansible-centos-7-molecule-tripleo-systemd-wrapper + parent: tripleo-ansible-centos-7-base + vars: + tripleo_role_name: tripleo-systemd-wrapper