Compare commits

4 Commits
main ... pinp

Author SHA1 Message Date
uumas
db651723b2 Add pinp support and make windmill use it 2026-03-26 03:04:44 +02:00
uumas
1d180106d6 service: Use saner defaults for additional containers 2026-03-25 19:29:30 +02:00
uumas
9eaa306aa4 lint 2026-03-25 19:28:40 +02:00
uumas
f8e67b12d7 windmill: fix entrypoint script 2026-03-24 19:56:09 +02:00
11 changed files with 323 additions and 82 deletions

View File

@@ -17,6 +17,7 @@ service_container_devices: []
service_container_secrets: [] service_container_secrets: []
service_container_env: {} service_container_env: {}
service_container_add_capabilities: [] service_container_add_capabilities: []
service_container_pinp: false
service_database_type: none service_database_type: none
service_database_additional_networks: [] service_database_additional_networks: []

View File

@@ -277,6 +277,22 @@ argument_specs:
default: [] default: []
elements: str elements: str
service_container_pinp:
description:
- If true, runs the container with podman in podman
- This starts a podman service inside the outer container
- The podman socket is exposed to the inner container at /var/run/docker.sock
- >-
This allows the container to manage other containers, which are run inside the
same outer container
- >-
The inner containers use host networking, so they share the network namespace
with the outer container and each other.
- This support is experimental and may not work with all images or configurations.
type: bool
required: false
default: false
service_database_type: service_database_type:
description: description:
- Database type to set up. - Database type to set up.
@@ -340,9 +356,10 @@ argument_specs:
service_additional_containers: service_additional_containers:
description: description:
- List of additional containers for the service. - List of additional containers for the service.
- > - >-
Will inherit most options from main service container. All options can be overridden If image is not specified, will use service container image and
per-container. inherit most options from main service container.
- All options can be overridden per-container.
type: list type: list
required: false required: false
default: [] default: []
@@ -363,26 +380,30 @@ argument_specs:
required: false required: false
default: "{{ service_container_image }}" default: "{{ service_container_image }}"
user: user:
description: The UID to run as inside the container description:
- The UID to run as inside the container.
- Defaults to <service_container_user> if same image, "" otherwise.
type: str type: str
required: false required: false
default: "{{ service_container_user }}"
command: command:
description: Command to start the container with. description:
- Command to start the container with.
- Defaults to <service_container_command> if same image, [] otherwise.
type: list type: list
required: false required: false
default: []
elements: str elements: str
entrypoint: entrypoint:
description: Entrypoint to use in the container description:
- Entrypoint to use in the container
- Defaults to <service_container_entrypoint> if same image, "" otherwise.
type: str type: str
required: false required: false
default: ""
mounts: mounts:
description: List of bind mounts or volumes to be mounted inside the container. description:
- List of bind mounts or volumes to be mounted inside the container.
- Defaults to <service_container_mounts> if same image, [] otherwise.
type: list type: list
required: false required: false
default: "{{ service_container_mounts }}"
elements: dict elements: dict
options: options:
type: type:
@@ -450,10 +471,11 @@ argument_specs:
required: false required: false
default: [] default: []
devices: devices:
description: List of devices to be added inside the container. description:
- List of devices to be added inside the container.
- Defaults to <service_container_devices> if same image, [] otherwise.
type: list type: list
required: false required: false
default: "{{ service_container_devices }}"
elements: dict elements: dict
options: options:
source: source:
@@ -506,15 +528,17 @@ argument_specs:
type: int type: int
required: false required: false
env: env:
description: A dict of environment variables for the container description:
- A dict of environment variables for the container
- Defaults to <service_container_env> if same image, {} otherwise.
type: dict type: dict
required: false required: false
default: "{{ service_container_env }}"
add_capabilities: add_capabilities:
description: List of capabilities to add to the container description:
- List of capabilities to add to the container
- Defaults to <service_container_add_capabilities> if same image, [] otherwise.
type: list type: list
required: false required: false
default: "{{ service_container_add_capabilities }}"
elements: str elements: str
secrets: secrets:
description: description:
@@ -525,9 +549,9 @@ argument_specs:
A dict of secrets and their values (including autogenerated values) is available as A dict of secrets and their values (including autogenerated values) is available as
`service_podman_secrets` for use in templates. This should only be used if the `service_podman_secrets` for use in templates. This should only be used if the
container doesn't support reading the secret from file or environment variable. container doesn't support reading the secret from file or environment variable.
- Defaults to <service_container_secrets> if same image, [] otherwise.
type: list type: list
required: false required: false
default: "{{ service_container_secrets }}"
elements: dict elements: dict
options: options:
name: name:
@@ -563,6 +587,21 @@ argument_specs:
the name of the environment variable. Defaults to secret name. the name of the environment variable. Defaults to secret name.
type: str type: str
required: false required: false
pinp:
description:
- If true, runs the container with podman in podman
- This starts a podman service inside the outer container
- The podman socket is exposed to the inner container at /var/run/docker.sock
- >-
This allows the container to manage other containers, which are run inside the
same outer container
- >-
The inner containers use host networking, so they share the network namespace
with the outer container and each other.
- This support is experimental and may not work with all images or configurations.
type: bool
required: false
default: false
service_requires: service_requires:
description: List of systemd units this service container depends on. description: List of systemd units this service container depends on.

View File

@@ -4,18 +4,18 @@
name: container name: container
vars: vars:
container_name: "{{ _service_additional_container.name }}" container_name: "{{ _service_additional_container.name }}"
container_image: "{{ _service_additional_container.image | default(service_container_image) }}" container_image: "{{ _service_additional_container_final.image }}"
container_command: "{{ _service_additional_container.command | default([]) }}" container_entrypoint: "{{ _service_additional_container_final.entrypoint }}"
container_entrypoint: "{{ _service_additional_container.entrypoint | default('') }}" container_command: "{{ _service_additional_container_final.command }}"
container_user: "{{ _service_additional_container.user | default(service_container_user) }}" container_user: "{{ _service_additional_container_final.user }}"
container_mounts: "{{ _service_additional_container_mounts }}" container_mounts: "{{ _service_additional_container_final.mounts }}"
container_devices: "{{ _service_additional_container.devices | default(service_container_devices) }}" container_devices: "{{ _service_additional_container.devices }}"
container_publish_ports: "{{ _service_additional_container_publish_ports }}" container_publish_ports: "{{ _service_additional_container_publish_ports }}"
container_networks: "{{ _service_additional_container_networks }}" container_networks: "{{ _service_additional_container_networks }}"
container_hostname: "{{ _service_additional_container.name | regex_replace('^' ~ service_name ~ '-', '') }}" container_hostname: "{{ _service_additional_container.name | regex_replace('^' ~ service_name ~ '-', '') }}"
container_secrets: "{{ _service_additional_container_secrets }}" container_secrets: "{{ _service_additional_container_secrets }}"
container_env: "{{ _service_additional_container.env | default(service_container_env) }}" container_env: "{{ _service_additional_container_final.env }}"
container_add_capabilities: "{{ _service_additional_container.add_capabilities | default(service_container_add_capabilities) }}" container_add_capabilities: "{{ _service_additional_container.add_capabilities }}"
container_requires: "{{ _service_container_requires }}" container_requires: "{{ _service_container_requires }}"
container_wants: "{{ _service_additional_container_wants }}" container_wants: "{{ _service_additional_container_wants }}"
container_auto_update: "{{ service_auto_update }}" container_auto_update: "{{ service_auto_update }}"

View File

@@ -34,3 +34,28 @@
directory_mode: "0755" directory_mode: "0755"
notify: Restart container service {{ service_name }} notify: Restart container service {{ service_name }}
loop: "{{ _service_all_copy_mounts | zip(_service_all_copy_mount_host_files) }}" loop: "{{ _service_all_copy_mounts | zip(_service_all_copy_mount_host_files) }}"
- name: Template entrypoint for pinp
ansible.builtin.template:
src: "pinp-entrypoint.sh.j2"
dest: "{{ _service_host_directory }}/mounts/pinp-entrypoint.sh"
mode: "0755"
vars:
pinp_inner_name: "{{ service_name }}"
pinp_inner_image: "{{ service_container_image }}"
pinp_inner_mounts: "{{ _service_container_pinp_inner_mounts }}"
pinp_inner_env: "{{ service_container_env }}"
when: service_container_pinp
- name: Template entrypoint for pinp of additional containers
ansible.builtin.template:
src: "pinp-entrypoint.sh.j2"
dest: "{{ _service_host_directory }}/mounts/{{ _service_additional_container.name }}-pinp-entrypoint.sh"
mode: "0755"
loop: "{{ _service_additional_containers | selectattr('pinp') }}"
loop_control:
loop_var: _service_additional_container
vars:
pinp_inner_name: "{{ _service_additional_container.name }}"
pinp_inner_image: "{{ _service_additional_container.image }}"
pinp_inner_mounts: "{{ _service_additional_container_pinp_inner_mounts }}"
pinp_inner_env: "{{ _service_additional_container.env }}"

View File

@@ -14,9 +14,12 @@
ansible.builtin.include_tasks: secrets.yaml ansible.builtin.include_tasks: secrets.yaml
when: _service_container_secrets | length > 0 when: _service_container_secrets | length > 0
- name: Template mounts for {{ service_name }} - name: Host mounts for {{ service_name }}
ansible.builtin.include_tasks: host_mounts.yaml ansible.builtin.include_tasks: host_mounts.yaml
when: (_service_all_template_mounts + _service_all_copy_mounts) | length > 0 when: >-
(_service_all_template_mounts + _service_all_copy_mounts) | length > 0
or service_container_pinp
or (_service_additional_containers | selectattr('pinp') | length > 0)
- name: Additional containers for {{ service_name }} - name: Additional containers for {{ service_name }}
ansible.builtin.include_tasks: additional.yaml ansible.builtin.include_tasks: additional.yaml
@@ -36,16 +39,16 @@
name: container name: container
vars: vars:
container_name: "{{ service_name }}" container_name: "{{ service_name }}"
container_image: "{{ service_container_image }}" container_image: "{{ _service_container.image }}"
container_command: "{{ service_container_command }}" container_entrypoint: "{{ _service_container.entrypoint }}"
container_entrypoint: "{{ service_container_entrypoint }}" container_command: "{{ _service_container.command }}"
container_user: "{{ service_container_user }}" container_user: "{{ _service_container.user }}"
container_mounts: "{{ _service_container_mounts }}" container_mounts: "{{ _service_container.mounts }}"
container_devices: "{{ service_container_devices }}" container_devices: "{{ service_container_devices }}"
container_publish_ports: "{{ _service_container_publish_ports }}" container_publish_ports: "{{ _service_container_publish_ports }}"
container_networks: "{{ _service_container_networks }}" container_networks: "{{ _service_container_networks }}"
container_secrets: "{{ _service_container_secrets }}" container_secrets: "{{ _service_container_secrets }}"
container_env: "{{ service_container_env }}" container_env: "{{ _service_container.env }}"
container_add_capabilities: "{{ service_container_add_capabilities }}" container_add_capabilities: "{{ service_container_add_capabilities }}"
container_requires: "{{ _service_container_requires }}" container_requires: "{{ _service_container_requires }}"
container_wants: "{{ _service_container_wants }}" container_wants: "{{ _service_container_wants }}"

View File

@@ -0,0 +1,26 @@
#!/bin/bash
# {{ ansible_managed }}
_term() {
echo "Received SIGTERM, stopping all containers"
kill "$child"
}
podman system service -t 0 &
podman run \
--rm \
-v /run/secrets:/run/secrets:ro \
{% for key, value in pinp_inner_env.items() %}
-e {{ key }}={{ value }} \
{% endfor %}
-v /tmp/storage-run-1000/podman/podman.sock:/var/run/docker.sock \
{% for mount in pinp_inner_mounts %}
--mount type={{ mount.type }},source={{ mount.source }},destination={{ mount.destination }}{% if mount.readonly | default(false) %},readonly{% endif %} \
{% endfor %}
--name {{ pinp_inner_name }} \
--network host \
{{ pinp_inner_image }} &
child=$!
trap _term SIGTERM
wait "$!"

View File

@@ -1,9 +1,69 @@
--- ---
_service_additional_containers_with_default_image: >-
{{
([{ 'image': service_container_image }] * service_additional_containers | length)
| zip(service_additional_containers)
| map('combine')
}}
_service_additional_container_same_image_defaults:
user: "{{ service_container_user }}"
command: "{{ service_container_command }}"
entrypoint: "{{ service_container_entrypoint }}"
devices: "{{ service_container_devices }}"
env: "{{ service_container_env }}"
add_capabilities: "{{ service_container_add_capabilities }}"
pinp: false
_service_additional_container_different_image_defaults:
user: ""
command: []
entrypoint: ""
mounts: []
devices: []
publish_ports: []
env: {}
add_capabilities: []
secrets: []
pinp: false
_service_additional_same_image_containers: >-
{{
_service_additional_containers_with_default_image
| selectattr('image', '==', service_container_image)
}}
_service_additional_different_image_containers: >-
{{
_service_additional_containers_with_default_image
| selectattr('image', '!=', service_container_image)
}}
_service_additional_containers: >- _service_additional_containers: >-
{{ {{
service_additional_containers (
(
(
[_service_additional_container_same_image_defaults] *
(_service_additional_same_image_containers | length)
)
| zip(_service_additional_same_image_containers)
| map('combine')
) +
(
(
[_service_additional_container_different_image_defaults] *
(_service_additional_different_image_containers | length)
)
| zip(_service_additional_different_image_containers)
| map('combine')
)
)
| zip( | zip(
service_additional_containers (
_service_additional_same_image_containers +
_service_additional_different_image_containers
)
| map(attribute='name') | map(attribute='name')
| map('regex_replace', '^', service_name ~ '-') | map('regex_replace', '^', service_name ~ '-')
| map('community.general.dict_kv', 'name') | map('community.general.dict_kv', 'name')
@@ -11,6 +71,7 @@ _service_additional_containers: >-
| map('combine') | map('combine')
}} }}
_service_additional_container_wants: >- _service_additional_container_wants: >-
{{ {{
service_wants service_wants
@@ -145,3 +206,59 @@ _service_additional_container_mounts: >-
else else
_service_container_mounts _service_container_mounts
}} }}
_service_additional_plain_container:
image: "{{ _service_additional_container.image }}"
entrypoint: "{{ _service_additional_container.entrypoint }}"
command: "{{ _service_additional_container.command }}"
user: "{{ _service_additional_container.user }}"
env: "{{ _service_additional_container.env }}"
mounts: "{{ _service_additional_container_mounts }}"
_service_additional_pinp_container_mounts:
- type: bind
source: "{{ _service_host_directory }}/mounts/{{ _service_additional_container.name }}-entrypoint.sh"
destination: /entrypoint.sh
readonly: true
- type: volume
source: "{{ _service_additional_container.name }}-containers"
destination: /home/podman/.local/share/containers
_service_additional_pinp_container:
image: quay.io/podman/stable:latest
entrypoint: /entrypoint.sh
command: []
user: podman
env: {}
mounts: >-
{{
_service_additional_pinp_container_mounts
+ (
_service_additional_container_mounts
| zip(
_service_additional_container_mounts
| map(attribute='source')
| map('replace', '/', '_')
| map('regex_replace', '^', '/mounts/')
| map('community.general.dict_kv', 'destination')
)
| map('combine')
)
}}
_service_additional_container_final: >-
{{ _service_additional_pinp_container if _service_additional_container.pinp else _service_additional_plain_container }}
_service_additional_container_pinp_inner_mounts: >-
{{
_service_additional_container_mounts
| zip(
_service_additional_container_mounts
| map(attribute='source')
| map('replace', '/', '_')
| map('regex_replace', '^', '/mounts/')
| map('community.general.dict_kv', 'source')
)
| map('combine')
}}

View File

@@ -0,0 +1,55 @@
---
_service_plain_container:
image: "{{ service_container_image }}"
entrypoint: "{{ service_container_entrypoint }}"
command: "{{ service_container_command }}"
user: "{{ service_container_user }}"
env: "{{ service_container_env }}"
mounts: "{{ _service_container_mounts }}"
_service_pinp_container_mounts:
- type: bind
source: "{{ _service_host_directory }}/mounts/entrypoint.sh"
destination: /entrypoint.sh
readonly: true
- type: volume
source: "containers"
destination: /home/podman/.local/share/containers
_service_pinp_container:
image: quay.io/podman/stable:latest
entrypoint: /entrypoint.sh
command: []
user: podman
env: {}
mounts: >-
{{
_service_pinp_container_mounts
+ (
_service_container_mounts
| zip(
_service_container_mounts
| map(attribute='source')
| map('replace', '/', '_')
| map('regex_replace', '^', '/mounts/')
| map('community.general.dict_kv', 'destination')
)
| map('combine')
)
}}
_service_container: >-
{{ _service_pinp_container if service_container_pinp else _service_plain_container }}
_service_container_pinp_inner_mounts: >-
{{
_service_container_mounts
| zip(
_service_container_mounts
| map(attribute='source')
| map('replace', '/', '_')
| map('regex_replace', '^', '/mounts/')
| map('community.general.dict_kv', 'source')
)
| map('combine')
}}

View File

@@ -22,7 +22,12 @@ _service_container_secrets: >-
+ ( + (
[{ [{
'name': _service_database_name ~ '-url', 'name': _service_database_name ~ '-url',
'value': 'postgres://' ~ service_name | replace('-', '_') ~ ':' ~ service_podman_secrets[service_name ~ '-postgres'] ~ '@postgres/' ~ service_name | replace('-', '_') ~ '?sslmode=disable', 'value':
'postgres://'
~ service_name | replace('-', '_')
~ ':' ~ service_podman_secrets[service_name ~ '-postgres']
~ '@postgres/' ~ service_name | replace('-', '_')
~ '?sslmode=disable',
'type': service_database_secret_type, 'type': service_database_secret_type,
'target': service_database_secret_target ~ '-url' 'target': service_database_secret_target ~ '-url'
}] if service_podman_secrets[service_name ~ '-postgres'] is defined else [] }] if service_podman_secrets[service_name ~ '-postgres'] is defined else []

View File

@@ -18,25 +18,21 @@
MODE: server MODE: server
service_additional_containers: service_additional_containers:
- name: worker - name: worker
image: quay.io/podman/stable:latest pinp: true
user: podman
entrypoint: /entrypoint.sh
mounts: mounts:
- type: volume - type: volume
source: worker-logs source: worker-logs
destination: /worker-logs destination: /tmp/windmill/logs
- type: volume - type: volume
source: worker-dependency-cache source: worker-dependency-cache
destination: /worker-dependency-cache destination: /tmp/windmill/cache
- type: template env:
source: worker_entrypoint.sh.j2 DATABASE_URL_FILE: /run/secrets/postgres-url
destination: /entrypoint.sh MODE: worker
mode: "0755" WORKER_GROUP: default
- type: volume ENABLE_UNSHARE_PID: "true"
source: worker-containers UNSHARE_ISOLATION_FLAGS: "--user --map-root-user --pid --fork"
destination: /home/podman/.local/share/containers
publish_ports: []
env: {}
- name: worker-native - name: worker-native
env: env:
DATABASE_URL_FILE: /run/secrets/postgres-url DATABASE_URL_FILE: /run/secrets/postgres-url

View File

@@ -1,26 +0,0 @@
#!/bin/bash
# {{ ansible_managed }}
_term() {
echo "Received SIGTERM, stopping all containers"
kill "$child"
}
podman system service -t 0 &
podman run \
--rm \
-v /run/secrets/postgres-url:/run/secrets/postgres-url:ro \
-e DATABASE_URL_FILE=/run/secrets/postgres-url \
-e MODE=worker \
-e WORKER_GROUP=default \
-e ENABLE_UNSHARE_PID="true" \
-v /tmp/storage-run-1000/podman/podman.sock:/var/run/docker.sock \
-v /worker-logs:/tmp/windmill/logs \
-v /worker-dependency-cache:/tmp/windmill/cache \
--name worker \
--network host \
ghcr.io/windmill-labs/windmill:main &
child=$!
trap _term SIGTERM
wait $!