Repository navigation
Conversation
uid, gid and mode on a service-level configs:/secrets: reference are applied when the object is copied into the container (content: and environment: sources) and dropped when it is bind-mounted (file: sources). That is a property of how the container is created, not of the compose file, and an engine able to set ownership on bind mounts would change the answer. Report them from create(), next to the other creation-time warnings, once per requested service, instead of from the load-time unsupported-attribute check. The false positive on inline and environment sources goes away without post-filtering findings against the loaded project. Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
Lower-confidence findings (not posted inline)
-
[low]
pkg/compose/secrets.go:100 —fileReferenceOverridesIgnoredreturnstruefor missing/undefined config objects via zero-value map lookup (confidence: weak 52/100). In practice compose-go validates all references at load time so a non-emptySourceabsent from the project map should be impossible at runtime; the anonymous (emptySource) case is intentional and tested. -
[low]
pkg/compose/run.go:194 —warnIgnoredFileReferencesmay double-warn for the run-target service duringcompose runifWithServicesDisabledkeeps the service inServiceNames()(confidence: weak 45/100). If the compose-go implementation excludes disabled services fromServiceNames(), this is a non-issue; the e2e test would not catch duplicates since it usesOutputContains.
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟡 NEEDS ATTENTION
| } | ||
|
|
||
| // the run target is created here, not by create(), which only handles its dependencies | ||
| warnIgnoredFileReferences(project, []string{service.Name}) |
There was a problem hiding this comment.
[medium] Possible duplicate uid/gid/mode warning for the run target service
prepareRun calls warnIgnoredFileReferences(project, []string{service.Name}) at line 194 after startDependencies returns. Inside startDependencies, project is locally reassigned via project = project.WithServicesDisabled(options.Service) (a new value, not a mutation of the caller's *types.Project), and then s.create(ctx, project, ...) is invoked on that disabled copy — which in turn calls warnIgnoredFileReferences(project, project.ServiceNames()).
Whether the run-target service produces warnings from both call sites depends on WithServicesDisabled: if it sets the service's Scale to 0 but keeps it in the Services map, project.ServiceNames() on the disabled copy still includes the run target, so create() warns for it and then prepareRun warns for it a second time. Only if WithServicesDisabled removes the service from the map entirely is there no duplication.
If duplication occurs, a user running docker compose run web would see uid/gid/mode warnings for web twice.
To harden this: verify the exact semantics of WithServicesDisabled, and if needed, guard the explicit prepareRun call with a check that create() did not already cover the run target — or rely solely on the create() call and remove the explicit one if WithServicesDisabled keeps the service in the map (since then create() already warned for it).
| Confidence | Score |
|---|---|
| 🟡 moderate | 57/100 |
There was a problem hiding this comment.
No duplicate: Project.WithServicesDisabled moves the service from Services to DisabledServices and deletes it from Services, so project.ServiceNames() in create() (called from startDependencies on the disabled copy) does not include the run target. Only prepareRun warns for it. Leaving the code as is.
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
docker-agent
left a comment
There was a problem hiding this comment.
Assessment: 🟢 APPROVE
This review covers only the commits pushed since
93ccf6abc129.
The single change in this incremental diff — adding || service.GetScale() == 0 to the guard in warnIgnoredFileReferences — is a correct and minimal fix for the previously-identified spurious warnings for scaled-to-zero services. GetScale() returns 1 when Scale is nil (the default), so the guard only suppresses warnings for services explicitly scaled to 0, where no container is created. No bugs introduced by the new lines.
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
glours
left a comment
There was a problem hiding this comment.
One regression to fix before merge, see the inline comment on run.go: docker compose run on a service scaled to zero no longer warns.
| } | ||
|
|
||
| // the run target is created here, not by create(), which only handles its dependencies | ||
| warnIgnoredFileReferences(project, []string{service.Name}) |
There was a problem hiding this comment.
warnIgnoredFileReferences(project, []string{service.Name}) re-reads project.Services[service.Name], but the container is created from the local service copy, where service.Scale = &one has been forced a few lines above. For a run target declared with scale: 0 or deploy.replicas: 0, the GetScale() == 0 skip fires and the warning is silently dropped, even though a container is created and its uid/gid/mode are ignored. Before this PR run did warn for these services, so it's a regression in that corner. The scale-zero skip is right for create/up, just not for the run target.
Repro (app.conf is any file):
services:
tool: # run-only service
image: alpine
scale: 0
command: ["true"]
configs:
- source: app-config
uid: "1000"
mode: 0o440
tool-replicas:
image: alpine
deploy:
replicas: 0
command: ["true"]
configs:
- source: app-config
uid: "1000"
mode: 0o440
control: # default scale
image: alpine
command: ["true"]
configs:
- source: app-config
uid: "1000"
mode: 0o440
configs:
app-config:
file: ./app.confdocker compose run --rm control warns about uid/mode. docker compose run --rm tool and docker compose run --rm tool-replicas create the container with no warning. On the PR's base commit all three warn.
Minimal fix: keep the skip in warnIgnoredFileReferences, move the per-service body into a helper taking the service itself, and call that helper from prepareRun with the local copy:
func warnIgnoredFileReferences(project *types.Project, services []string) {
for _, name := range slices.Sorted(slices.Values(services)) {
service, ok := project.Services[name]
// a service scaled to zero gets no container, so nothing is ignored
if !ok || service.GetScale() == 0 {
continue
}
warnServiceFileReferences(project, service)
}
}
func warnServiceFileReferences(project *types.Project, service types.ServiceConfig) {
for _, ref := range service.Configs {
warnIgnoredFileReference(service.Name, "configs", types.FileReferenceConfig(ref), types.FileObjectConfig(project.Configs[ref.Source]))
}
for _, ref := range service.Secrets {
warnIgnoredFileReference(service.Name, "secrets", types.FileReferenceConfig(ref), types.FileObjectConfig(project.Secrets[ref.Source]))
}
}// run.go
warnServiceFileReferences(project, service)No double warning: startDependencies disables the target before calling create(), so it is not in project.ServiceNames(). A unit case with a service whose project entry has scale: 0 but is passed to warnServiceFileReferences with Scale=1 would cover it.
|
Confirming this also affects secrets:
tls_certificate_key:
environment: TLS_CERTIFICATE_KEY
services:
web:
secrets:
- source: tls_certificate_key
target: /etc/nginx/ssl/nginx.key
mode: 0400
|
What this PR does, in one sentence
Warns that
uid,gidandmodeon a service-levelconfigs:/secrets:reference are ignored when the container is created, and only when they really are ignored (afile:source), instead of at load time for every source.Context
Compose currently reports, while loading the project, that these three attributes are "not supported outside Swarm mode and will be ignored". For a
file:source that is true: the file is bind-mounted as-is. For acontent:orenvironment:source it is a false positive, because the value is copied into the container and the ownership and mode are applied (#14288, #14283; #14297 filters those findings out after the load).The underlying issue is that the loader cannot know the answer. Whether the attributes are honored depends on how the object reaches the container, and that is settled at creation. It is also not a fixed property of Compose: moby/moby#53368 lets the engine id-map bind mounts, which will make ownership on a bind-mounted file possible on newer daemons. A decision taken from the compose file alone would then be wrong for them.
What the PR brings
content:/environment:sources, which fixes the false positive.file:sources is kept, with the same message, but now comes fromcreate/up/run— once per service, including dependencies and theruntarget — rather than from every command that loads the project.docker compose configno longer prints it.This is an alternative to #14297 for the same issues. It trades the post-load filtering for a decision taken where the information exists.
Fixes #14288
Fixes #14283
🤖 Generated with Claude Code