Repository navigation
Conversation
There was a problem hiding this comment.
🟡 Changes recommended
Restart-policy handling incorrectly rejects unrestricted retries expressed as max_attempts: 0 and fails to propagate restart settings to LoadModelWithContext consumers.
2 open issues
What changed in this PR
This PR centralizes restart-policy reconciliation in the Compose loader, reducing policy-merging work for consumers and rejecting contradictory service settings.
Changes:
- Carries compatible
restartanddeploy.restart_policyvalues intorestartafter merging. - Adds contradiction checks that respect
SkipConsistencyCheck. - Adds tests for reconciliation, conflicts, reloads, overrides, and inheritance.
| File | Description |
|---|---|
loader/validate.go |
Integrates restart-policy contradiction checks. |
loader/tests/restart_test.go |
Covers reconciliation and loader-option behavior. |
loader/restart.go |
Implements policy mapping, merging, and conflict detection. |
loader/loader.go |
Applies reconciliation during normalized project conversion. |
Review effort: Balanced
|
Demo of what this enables on the Docker Compose side: docker/compose#14461 (draft, go.mod |
…tory values restart and deploy.restart_policy are two spellings of one policy: condition is its name and max_attempts its retry count. Each may complete the other, and normalization writes the outcome in restart, so a restart_policy made of a delay alone (docker/compose#14383) leaves restart as it is instead of clobbering it. Where both say something it must be the same thing: the consistency check now rejects them with 'can't set distinct values on', as it does for scale and deploy.replicas, instead of letting one silently win. A max_attempts of zero is no limit, as for on-failure:0, so it contradicts nothing but a retry count. Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
860ac4a to
9652c7f
Compare
|
Why an error rather than a warning here? The spec defines the precedence: "If Rejecting at load time is stricter than the spec and breaks files that load today ( The carry into WDYT @ndeloof? |

What this PR does, in one sentence
restartanddeploy.restart_policyare now reconciled by the loader: each may complete the other, the outcome is carried inrestart, and a contradiction between the two is rejected instead of being settled silently.Context
A service can express its restart policy twice: with
restart(on-failure:3) and withdeploy.restart_policy(condition: on-failure,max_attempts: 3). The spec saysrestart_policywins when it is set, and every consumer has to implement that on its own. Docker Compose does, by replacing the whole policy computed fromrestart, which has two bad outcomes (docker/compose#14383):restart_policythat does not set acondition(adelayalone, say) wipes out the policy ofrestart, and the engine then silently creates a container that never restarts;restart: unless-stoppedwithcondition: on-failure) are not reported: one of them is dropped without a word.What the PR brings
conditionis the name of the policy andmax_attemptsits retry count, sorestart: on-failurewithmax_attempts: 3ison-failure:3, anddeploy.restart_policy.condition: anyalone isrestart: always. The outcome is written inrestart, on the merged model (an override file or anextendsmay still change either attribute until then), for every service. An attribute that is not set says nothing: arestart_policymade of adelayalone leavesrestartas it is.deploy.restart_policyitself is left untouched.scale/deploy.replicas:services.foo: can't set distinct values on 'restart' and 'deploy.restart_policy.condition'(or.max_attempts). That covers another policy, another retry count, and a retry count given to a policy that has none (restart: alwayswithmax_attempts: 3). As for the other consistency checks it is skipped withSkipConsistencyCheck, and the model is then kept as written.on-failure:abc), and a retry count withany/noneand norestart(the engine rejects it today, and it is norestartvalue, so nothing is carried).Compatibility
A service that sets both
restartanddeploy.restart_policywith values that disagree is now rejected at load time, where one of the two used to be dropped without notice. The error names the two attributes to align, andSkipConsistencyCheckskips the check. Services that set only one of the two, or both with the same policy, load as before, with the policy now also carried inrestart. Worth a line in the release notes.What comes next
Once released, docker/compose can read the policy from
restartonly, instead of merging the two attributes ingetRestartPolicy(docker/compose#14459 does the field-by-field merge in the meantime).🤖 Generated with Claude Code