Summary
The assert task that checks the clock never runs, so fail_msg never renders. The operator sees a raw retry failure with no explanation of the cause.
This issue follows #208. In #208, @hyde-repo classified this defect as a bug.
Separately, the dead assert and the Fedora path do qualify as bugs. Please open a dedicated issue for them and feel free to propose the fix.
Scope, as agreed in #208: the fix stays inside the existing tasks, with no restructuring.
Severity: MEDIUM. The defect hides the reason for the failure. The defect does not change whether the deployment fails.
The Fedora faults of the same role are #213. The two issues are independent, and this one needs no decision.
Location
The defect exists in the copy that runs as of 2026-08-27:
| Distribution |
File and lines |
| Ubuntu |
02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/network/ntp_client/ubuntu/systemd_timesyncd.yaml:61-75 |
| Fedora |
02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/network/ntp_client/fedora/systemd_timesyncd.yaml:56-69 |
The defect also exists in both copies inside service.reload.ntp, which no scenario includes. #208 carries the question of which copy is authoritative.
The block below is abridged. The block is dedented, and it omits the success_msg and comment lines.
- name: wait time drift - ubuntu
command: timedatectl show -p NTPSynchronized --value
register: ntp_sync_check
until: ntp_sync_check.stdout == "yes"
retries: 10
delay: 5
changed_when: false
- name: assert - time is sync - ubuntu
assert:
that:
- ntp_sync_check.stdout == "yes"
fail_msg: " clock is not sync!"
Mechanism
When the retries exhaust, the command task fails and aborts the block. The assert task never runs, so fail_msg never renders.
Current output for the operator:
fatal: [r42.admin-all-services-lab]: FAILED! => cmd: timedatectl show -p NTPSynchronized --value, stdout: "no", attempts: 10
Expected output: the text of fail_msg, which names the clock as the cause.
Fix
Add failed_when: false to the retry task, so the assert becomes the failing task.
Verified on ansible-core 2.20.2, with a local reproduction of the three cases:
| Retry task |
Retry loop |
Task result |
assert |
fail_msg |
| current code |
exhausts |
fatal |
never runs |
not rendered |
failed_when: false |
exhausts |
ok |
runs |
rendered |
ignore_errors: true |
exhausts |
fatal, then ignored |
runs |
rendered, with the raw failure |
Not verified on the pinned ansible-core 2.19.1. failed_when: false is the cleaner of the two working options, because ignore_errors still prints the raw retry failure.
The same construct in the bootstrapper
The same construct waits in a commented copy in the bootstrapper repository, at range42/roles/deployer.bootstrap/tasks/00_ntp.yml:39-58. Both tasks are commented there, so nothing is unreachable today. #208 proposes to re-enable that copy. Re-enabling it as written would reproduce this defect.
Reproduction
Run the role on an Ubuntu guest, on a segment where UDP port 123 is filtered. The retry loop exhausts, and the operator sees stdout: "no" with no fail_msg.
Acceptance criteria
#208 leaves the meaning of "with tests" open. These criteria assume acceptance at the playbook level, as proposed in #208.
Out of scope
Summary
The
asserttask that checks the clock never runs, sofail_msgnever renders. The operator sees a raw retry failure with no explanation of the cause.This issue follows #208. In #208, @hyde-repo classified this defect as a bug.
Scope, as agreed in #208: the fix stays inside the existing tasks, with no restructuring.
Severity: MEDIUM. The defect hides the reason for the failure. The defect does not change whether the deployment fails.
The Fedora faults of the same role are #213. The two issues are independent, and this one needs no decision.
Location
The defect exists in the copy that runs as of 2026-08-27:
02_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/network/ntp_client/ubuntu/systemd_timesyncd.yaml:61-7502_ansible_layer/admin/roles/software.install.warmup.basic_packages/tasks/include/network/ntp_client/fedora/systemd_timesyncd.yaml:56-69The defect also exists in both copies inside
service.reload.ntp, which no scenario includes. #208 carries the question of which copy is authoritative.The block below is abridged. The block is dedented, and it omits the
success_msgand comment lines.Mechanism
When the retries exhaust, the
commandtask fails and aborts the block. Theasserttask never runs, sofail_msgnever renders.Current output for the operator:
Expected output: the text of
fail_msg, which names the clock as the cause.Fix
Add
failed_when: falseto the retry task, so theassertbecomes the failing task.Verified on
ansible-core 2.20.2, with a local reproduction of the three cases:assertfail_msgfatalfailed_when: falseokignore_errors: truefatal, then ignoredNot verified on the pinned
ansible-core 2.19.1.failed_when: falseis the cleaner of the two working options, becauseignore_errorsstill prints the raw retry failure.The same construct in the bootstrapper
The same construct waits in a commented copy in the bootstrapper repository, at
range42/roles/deployer.bootstrap/tasks/00_ntp.yml:39-58. Both tasks are commented there, so nothing is unreachable today. #208 proposes to re-enable that copy. Re-enabling it as written would reproduce this defect.Reproduction
Run the role on an Ubuntu guest, on a segment where UDP port 123 is filtered. The retry loop exhausts, and the operator sees
stdout: "no"with nofail_msg.Acceptance criteria
#208 leaves the meaning of "with tests" open. These criteria assume acceptance at the playbook level, as proposed in #208.
fail_msg, instead of a barestdout: "no".assertfails.Out of scope
ansible_virtualization_typeand the retry budget, which all stay in feat: Support configurable NTP server for restricted environments #208.