fix(global_defaults): validate system.users as the documented dict on both paths

This commit is contained in:
2026-10-05 16:54:28 +02:00
parent 9aad42a4c2
commit 74caca915e
5 changed files with 213 additions and 22 deletions
+62
View File
@@ -0,0 +1,62 @@
---
# Run: ansible-playbook tests/users/test_users.yml
# One host per fixture: set_fact state cannot be cleared between loop iterations.
- name: Register one host per users fixture
hosts: localhost
gather_facts: false
connection: local
vars_files:
- users_fixtures.yml
tasks:
- name: Add the fixture host
ansible.builtin.add_host:
name: "{{ uf.name }}"
groups: users_fixtures
ansible_connection: local
ansible_python_interpreter: "{{ ansible_playbook_python }}"
expect: "{{ uf.expect }}"
system: "{{ _system }}"
system_cfg: "{{ _system if uf.precomputed | default(false) else omit }}"
vars:
_system: "{{ users_fixture_system | combine(uf.system, {'name': uf.name}, recursive=True) }}"
loop: "{{ users_fixtures }}"
loop_control:
loop_var: uf
label: "{{ uf.name }}"
- name: Run global_defaults against each users fixture
hosts: users_fixtures
gather_facts: false
vars:
hypervisor:
type: libvirt
url: qemu:///system
storage: default
boot_iso: /var/lib/libvirt/images/fixture.iso
tasks:
- name: Normalize and validate
block:
- name: Load global defaults
ansible.builtin.include_role:
name: global_defaults
- name: Record the pass
ansible.builtin.set_fact:
users_outcome: pass
rescue:
- name: Record the failure
ansible.builtin.set_fact:
users_outcome: >-
{{ ([ansible_failed_result.msg | default('')]
+ (ansible_failed_result.results | default([]) | map(attribute='msg', default='') | list))
| join(' ') }}
- name: Assert the expected outcome
ansible.builtin.assert:
that:
- >-
(users_outcome == 'pass' and bootstrap_primary_user.name == expect.primary)
if expect.primary is defined
else (expect.fail in users_outcome)
fail_msg: "[{{ inventory_hostname }}] expected {{ expect }}, got: {{ users_outcome }}"
success_msg: "[{{ inventory_hostname }}] OK"
+123
View File
@@ -0,0 +1,123 @@
---
users_fixture_system:
type: virtual
os: debian
version: "13"
cpus: 2
memory: 2048
disks:
- size: 30
network:
interfaces:
- bridge: br0
ip: 192.0.2.10
prefix: 24
gateway: 192.0.2.1
root:
password: rootpw
# precomputed: hand system_cfg over the way deploy_iac does, skipping the raw input checks.
users_fixtures:
- name: dict-first-password-wins
system:
users:
keyonly:
keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFixture keyonly
blank:
password: ""
ops:
password: pw
sudo: true
later:
password: pw2
expect:
primary: ops
- name: dict-first-password-wins-precomputed
precomputed: true
system:
users:
keyonly:
keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFixture keyonly
ops:
password: pw
expect:
primary: ops
- name: list-rejected
system:
users:
- name: ops
password: pw
expect:
fail: system.users must be a dict keyed by username
- name: list-rejected-precomputed
precomputed: true
system:
users:
- name: ops
password: pw
expect:
fail: system.users must be a dict keyed by username
- name: scalar-entry-rejected
system:
users:
ops: pw
expect:
fail: system.users.ops must be a dict
- name: scalar-entry-rejected-precomputed
precomputed: true
system:
users:
ops: pw
expect:
fail: system.users.ops must be a dict
- name: keys-string-rejected
system:
users:
ops:
password: pw
keys: ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFixture ops
expect:
fail: its keys a list of SSH public keys
- name: no-password-rejected
system:
users:
ops:
keys:
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFixture ops
expect:
fail: needs a non-empty password
- name: autologin-user-accepted
system:
users:
ops:
password: pw
features:
desktop:
enabled: true
environment: gnome
autologin: ops
expect:
primary: ops
- name: autologin-unknown-user-rejected
system:
users:
ops:
password: pw
features:
desktop:
enabled: true
environment: gnome
autologin: ghost
expect:
fail: desktop.autologin must be false or a username