Files
computer-playbook/roles/web-app-bigbluebutton/tasks/02_administrator.yml
kevinveenbirkenbachandClaude Opus 5 65d226d0d9 fix(container): reach every resolver address through a shell, and lint for it
The container_address lookup does not return a container name in swarm. It
returns a shell fragment, "$(/usr/bin/resolve-container-id <stack> <service>)",
which is only a name once an interpreter evaluates the substitution. The shell
module provides one. The command module does not -- it execs the binary directly,
so the fragment arrives at container exec as literal text and the call dies with
"No such container: $(...)".

Compose hides the entire class: there the same lookup yields a bare name and
command: works. The failure exists only in swarm, and no swarm run had touched
these paths -- both were added by feature commits (b23653adf8, 0421ebeb16) that
wrote the same argv shape independently, which is the argument for a rule rather
than six edits.

Six call sites moved to the shell module: the moodle egress task, the three
moodle AI-gateway tasks, and the two wordpress AI-Engine tasks. The wordpress
pair keeps its stdin, so it uses the dict form with cmd:. Moving off argv exposes
the surrounding values to word splitting, so they now go through quote -- the
address itself must NOT, because single quotes would suppress exactly the
substitution this depends on.

test_argv_exec_needs_shell.py closes the gap that let this through.
container-exec-resolver checks that the resolver is used; it has nothing to say
about whether anything can evaluate the result. The new rule checks that other
half. It reuses the sibling's argv parser, which recognises only container/docker
in first position, so an argv list that does start an interpreter
(argv: [bash, -lc, ...], as joomla does) is correctly not matched.

Run against the tree the rule reported exactly the five sites found by hand plus
bigbluebutton, and passed over joomla and the discourse task, whose address is a
config lookup rather than the resolver. Bigbluebutton is pinned to compose via
compose_mode_force and can only ever see a bare name, so it takes a nocheck.

Five container-exec-resolver markers disappear with the argv form: under shell
the resolver is the ordinary path, not an exception.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 11:39:38 +02:00

39 lines
1.4 KiB
YAML

---
- name: "⏳ Wait until Greenlight is reachable via Nginx"
uri_retry:
url: "{{ lookup('tls', application_id, 'url.base') }}"
validate_certs: true
status_code: 200
return_content: true
register: greenlight_http
until:
- greenlight_http.status == 200
- "'Greenlight' in greenlight_http.content or 'Sign in' in greenlight_http.content"
retries: 30
delay: 5
changed_when: false
- name: "✅ Ensure Greenlight admin (B64-safe, idempotent)"
ansible.builtin.command:
argv:
- container
- exec
- -i
- -e
- "TARGET_NAME={{ lookup('users', 'administrator').username | upper }}"
- -e
- "NEW_NAME={{ lookup('users', 'administrator').username | upper }}"
- -e
- "NEW_EMAIL={{ lookup('users', 'administrator').email }}"
- -e
- "NEW_PASSWORD_B64={{ lookup('users', 'administrator').password | b64encode }}"
- "{{ BBB_GREENLIGHT_CONTAINER_ADDRESS | quote }}" # nocheck: container-address-quote, container-address-needs-shell compose_mode_force role, so the lookup can only yield a bare name
- bash
- -lc
- "bundle exec rails runner -"
stdin: "{{ lookup('ansible.builtin.file', role_path ~ '/files/ruby/greenlight_ensure_admin.rb') }}"
register: greenlight_admin_map
changed_when: "'CHANGED' in greenlight_admin_map.stdout"
failed_when: greenlight_admin_map.rc != 0
no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}"