Skip to content

Commit

Permalink
fix: ensure user linger is enabled and disabled correctly
Browse files Browse the repository at this point in the history
Cause: The role was not always enabling user lingering before
creating resources, and not always canceling lingering after
removing resources.

Consequence: The role would give errors if attempting to create
a secret or other resource requiring lingering, or would leave
lingering enabled after removing resources.

Fix: Centralize linger handling and keep track of users which
may need linger canceling.  Ensure linger is canceled for all
users if all of that user's resources are removed and linger is
no longer needed.

Result: Resources for rootless users are always created properly.
Lingering is always canceled when no longer needed.

Fix issue with toml.j2 - ensure non-string values are written
as non-strings.

Fix idempotency issue where you could not clean up twice.

Allow testing rootless quadlet on EL8 by configuring settings
and kernel parameters and rebooting.

Fix several cleanup issues, and dump journal if there are test errors.

Construct the __params dict to pass to `podman_secret` to fix the JSON
string issue with `data` on both Ansible 2.9 and later.

Signed-off-by: Rich Megginson <[email protected]>
  • Loading branch information
richm committed Feb 7, 2024
1 parent 852c0a4 commit 656ffae
Show file tree
Hide file tree
Showing 15 changed files with 626 additions and 394 deletions.
5 changes: 5 additions & 0 deletions defaults/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,8 @@ podman_pull_image: true
# running the role.
# You can do this on a per-spec basis using continue_if_pull_fails
podman_continue_if_pull_fails: false

# Retry failed pulls
# If true, if a pull attempt fails, it will be retried according
# to the default Ansible `until` behavior.
podman_pull_retry: false
62 changes: 62 additions & 0 deletions tasks/cancel_linger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
# Input:
# * __podman_linger_user - username
- name: Get user information
getent:
database: passwd
key: "{{ __podman_linger_user }}"
fail_key: true
when: "'getent_passwd' not in ansible_facts or
__podman_linger_user not in ansible_facts['getent_passwd']"

- name: Set cancel linger vars
set_fact:
__podman_xdg_runtime_dir: >-
/run/user/{{ ansible_facts["getent_passwd"][__podman_linger_user][1] }}
- name: Stat XDG_RUNTIME_DIR
stat:
path: "{{ __podman_xdg_runtime_dir }}"
register: __podman_xdg_stat

- name: Gather facts for containers
containers.podman.podman_container_info:
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: true
become_user: "{{ __podman_linger_user }}"
when: __podman_xdg_stat.stat.exists
register: __podman_container_info

- name: Gather facts for networks
command: podman network ls -q
register: __podman_networks
changed_when: false
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: true
become_user: "{{ __podman_linger_user }}"
when: __podman_xdg_stat.stat.exists

- name: Gather secrets
command: podman secret ls -n -q
register: __podman_linger_secrets
changed_when: false
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: true
become_user: "{{ __podman_linger_user }}"
when: __podman_xdg_stat.stat.exists

- name: Cancel linger if no more resources are in use
command: loginctl disable-linger {{ __podman_linger_user }}
when:
- __podman_xdg_stat.stat.exists
- __podman_container_info.containers | length == 0
- __podman_networks.stdout_lines | reject("match", "^podman$") |
reject("match", "^podman-default-kube-network$") |
list | length == 0
- __podman_linger_secrets.stdout == ""
changed_when: true
args:
removes: /var/lib/systemd/linger/{{ __podman_user }}
28 changes: 14 additions & 14 deletions tasks/cleanup_kube_spec.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
---
- name: Stat XDG_RUNTIME_DIR
stat:
path: "{{ __podman_xdg_runtime_dir }}"
register: __podman_xdg_stat
when:
- __podman_rootless | bool
- __podman_xdg_runtime_dir | d("") | length > 0

- name: Stop and disable service
systemd:
name: "{{ __podman_service_name.stdout }}"
Expand All @@ -10,6 +18,7 @@
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
register: __podman_service_status
when: not __podman_rootless or __podman_xdg_stat.stat.exists
failed_when:
- __podman_service_status is failed
- not __podman_service_status.stdout is search(__service_error)
Expand All @@ -24,6 +33,7 @@
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"
register: __podman_removed
when: not __podman_rootless or __podman_xdg_stat.stat.exists

- name: Remove kubernetes yaml file
file:
Expand All @@ -39,17 +49,7 @@
when: __podman_removed is changed # noqa no-handler
changed_when: true

- name: Gather facts for all containers
containers.podman.podman_container_info:
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"
register: __podman_container_info

- name: Cancel linger if no more containers are running
command: loginctl disable-linger {{ __podman_user }}
when:
- __podman_rootless | bool
- __podman_container_info.containers | length == 0
changed_when: true
- name: Manage linger
include_tasks: manage_linger.yml
vars:
__podman_item_state: absent
45 changes: 17 additions & 28 deletions tasks/cleanup_quadlet_spec.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
---
# NOTE: Stopping, disabling, and removing units should also stop
# and remove any pods and containers as well.
- name: Stat XDG_RUNTIME_DIR
stat:
path: "{{ __podman_xdg_runtime_dir }}"
register: __podman_xdg_stat
when:
- __podman_rootless | bool
- __podman_xdg_runtime_dir | d("") | length > 0

- name: Stop and disable service
systemd:
name: "{{ __podman_service_name }}"
scope: "{{ __podman_systemd_scope }}"
state: stopped
enabled: false
force: true
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
register: __podman_service_status
when: __podman_service_name | length > 0
when:
- __podman_service_name | length > 0
- not __podman_rootless or __podman_xdg_stat.stat.exists
failed_when:
- __podman_service_status is failed
- not __podman_service_status.msg is search(__service_error)
Expand All @@ -25,6 +36,11 @@
state: absent
register: __podman_file_removed

- name: Manage linger
include_tasks: manage_linger.yml
vars:
__podman_item_state: absent

- name: Cleanup container resources
when: __podman_file_removed is changed # noqa no-handler
block:
Expand Down Expand Up @@ -53,30 +69,3 @@
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"

- name: Gather facts for all containers
containers.podman.podman_container_info:
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"
register: __podman_container_info
no_log: true

- name: Gather facts for networks
command: podman network ls -q
register: __podman_networks
changed_when: false
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"

- name: Cancel linger if no more resources are in use
command: loginctl disable-linger {{ __podman_user }}
when:
- __podman_rootless | bool
- __podman_container_info.containers | length == 0
- __podman_networks.stdout_lines | reject('match', '^podman$') |
list | length == 0
changed_when: true
11 changes: 6 additions & 5 deletions tasks/create_update_kube_spec.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
---
- name: Enable lingering if needed
command: loginctl enable-linger {{ __podman_user }}
when: __podman_rootless | bool
args:
creates: /var/lib/systemd/linger/{{ __podman_user }}
- name: Manage linger
include_tasks: manage_linger.yml
vars:
__podman_item_state: present

- name: Get the host mount volumes
set_fact:
Expand Down Expand Up @@ -47,6 +46,8 @@
password: "{{ container_image_password | default(omit) }}"
register: __podman_image_updated
when: __podman_pull_image | bool
until: __podman_image_updated is success
retries: "{{ podman_pull_retry | ternary(3, 0) }}"
failed_when:
- __podman_image_updated is failed
- not __podman_continue_if_pull_fails
Expand Down
11 changes: 6 additions & 5 deletions tasks/create_update_quadlet_spec.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
---
- name: Enable lingering if needed
command: loginctl enable-linger {{ __podman_user }}
when: __podman_rootless | bool
args:
creates: /var/lib/systemd/linger/{{ __podman_user }}
- name: Manage linger
include_tasks: manage_linger.yml
vars:
__podman_item_state: present

- name: Create host directories
file: "{{ __defaults | combine(podman_host_directories[__hostitem])
Expand Down Expand Up @@ -31,6 +30,8 @@
password: "{{ container_image_password | default(omit) }}"
register: __podman_image_updated
when: __podman_pull_image | bool
until: __podman_image_updated is success
retries: "{{ podman_pull_retry | ternary(3, 0) }}"
failed_when:
- __podman_image_updated is failed
- not __podman_continue_if_pull_fails
Expand Down
55 changes: 35 additions & 20 deletions tasks/handle_secret.yml
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
# SPDX-License-Identifier: MIT
---
- name: Set variables part 0
set_fact:
__podman_secret: "{{ __podman_secret_item |
dict2items | rejectattr('key', 'match', __del_params) |
list | items2dict }}"
vars:
__del_params: "^(run_as_user)$"
no_log: true

- name: Set variables part 1
set_fact:
__podman_user: "{{ __podman_secret_item['run_as_user'] |
Expand All @@ -20,19 +11,43 @@
__podman_xdg_runtime_dir: >-
/run/user/{{ ansible_facts["getent_passwd"][__podman_user][1] }}
- name: Manage linger
include_tasks: manage_linger.yml
vars:
__podman_item_state: "{{ __podman_secret_item.state | d('present') }}"

- name: Stat XDG_RUNTIME_DIR
stat:
path: "{{ __podman_xdg_runtime_dir }}"
register: __podman_xdg_stat
when:
- __podman_rootless | bool
- __podman_xdg_runtime_dir | d("") | length > 0

# if XDG_RUNTIME_DIR does not exist, this means linger
# was already canceled, which means the user is attempting
# to remove more than once
# We use __params here because the Ansible module code will convert a `data`
# parameter string that looks like JSON e.g. {"test": "string"} to a dict or
# list - there seems to be no way to prevent that - but if we construct the
# parameter dict to pass to podman_secret, it seems to preserve the original
# data types
- name: Manage each secret
containers.podman.podman_secret:
data: "{{ __podman_secret.data | string
if 'data' in __podman_secret else omit }}"
driver: "{{ __podman_secret.driver | d(omit) }}"
driver_opts: "{{ __podman_secret.driver_opts | d(omit) }}"
executable: "{{ __podman_secret.executable | d(omit) }}"
force: "{{ __podman_secret.force | d(omit) }}"
name: "{{ __podman_secret.name }}"
skip_existing: "{{ __podman_secret.skip_existing | d(omit) }}"
state: "{{ __podman_secret.state | d(omit) }}"
containers.podman.podman_secret: "{{ __params }}"
environment:
XDG_RUNTIME_DIR: "{{ __podman_xdg_runtime_dir }}"
become: "{{ __podman_rootless | ternary(true, omit) }}"
become_user: "{{ __podman_rootless | ternary(__podman_user, omit) }}"
no_log: true
when: not __podman_rootless or __podman_xdg_stat.stat.exists
no_log: false
vars:
__params: |
{% set rc = {} %}
{% set supported_params = ['data', 'driver', 'driver_opts', 'executable',
'force', 'name', 'skip_existing', 'state'] %}
{% for key in supported_params %}
{% if key in __podman_secret_item %}
{% set _ = rc.__setitem__(key, __podman_secret_item[key]) %}
{% endif %}
{% endfor %}
{{ rc }}
10 changes: 10 additions & 0 deletions tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@
selinux_ports: "{{ podman_selinux_ports }}"
when: podman_selinux_ports | length > 0

- name: Keep track of users that need to cancel linger
set_fact:
__podman_cancel_user_linger: []

- name: Handle secrets
include_tasks: handle_secret.yml
loop: "{{ podman_secrets }}"
Expand All @@ -124,3 +128,9 @@
loop: "{{ podman_quadlet_specs }}"
loop_control:
loop_var: __podman_quadlet_spec_item

- name: Cancel linger
include_tasks: cancel_linger.yml
loop: "{{ __podman_cancel_user_linger }}"
loop_control:
loop_var: __podman_linger_user
29 changes: 29 additions & 0 deletions tasks/manage_linger.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
# Input:
# * __podman_rootless - true or false
# * __podman_user - name of user
# * __podman_item_state - present or absent
# Globals: __podman_cancel_user_linger
- name: Enable linger if needed
when:
- __podman_rootless | bool
- __podman_item_state | d('present') != 'absent'
block:
- name: Enable linger if needed
command: loginctl enable-linger {{ __podman_user }}
when: __podman_rootless | bool
args:
creates: /var/lib/systemd/linger/{{ __podman_user }}

- name: Mark user as not yet needing to cancel linger
set_fact:
__podman_cancel_user_linger: "{{ __podman_cancel_user_linger |
difference([__podman_user]) }}"

- name: Mark user for possible linger cancel
set_fact:
__podman_cancel_user_linger: "{{ __podman_cancel_user_linger |
union([__podman_user]) }}"
when:
- __podman_rootless | bool
- __podman_item_state | d('present') == 'absent'
2 changes: 2 additions & 0 deletions templates/toml.j2
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
{{ key }}={{ value }}
{% elif value is mapping and value is not string %}
{{ key }} = [{%- for k in value %} "{{k}}={{value[k]}}", {%- endfor %}]
{% elif value is not string %}
{{ key }} = {{ value }}
{% else %}
{{ key }}="{{ value }}"
{% endif %}
Expand Down
Loading

0 comments on commit 656ffae

Please sign in to comment.