Write the context root into the bundle tar #23
No reviewers
Labels
No labels
⏳in-progress
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Grey.ooo/someones.computer_agent!23
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/context-root-tar-entry"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Every
sc deployof a Dockerfile that doesCOPY . <existing-dir>produced an image thatcould not serve. Found by running the deploy path end to end against the local stack rather
than by reading it: the build reported success, the push reported success, the deployment
reported running, and the service sat at 0/1 restarting for ever.
The platform builds with kaniko, and kaniko takes the mode of an existing destination
directory from the context tar's root entry, defaulting it to
0750owned by root when thetar has none.
docker buildinstead leaves an existing directory exactly as the base imagehad it.
packContextskipped"."in its walk, so there was never a root entry.Same Dockerfile, two builders:
/appdocker build755 app:appsc deploy→ kaniko750 root:rootA base image that serves as a de-privileged user then cannot traverse its own application
directory.
matthewbaggett/phpserves as uid 1000app, so nginx answeredthe healthcheck never passed, and Swarm restarted the task on a loop —
task: non-zero exit (111): dockerexec: unhealthy container.Isolating it
Two tars differing only in a
./member, same Dockerfile(
FROM matthewbaggett/php:8.5+WORKDIR /app+COPY . /app), straight through kaniko:WORKDIRalone accounts for the ownership change (kaniko makes it root where docker keepsapp:app); the mode drop to0750, which is the part that actually breaks serving, isentirely the missing root entry.
The change
One entry, written outside the walk:
.dockerignore, and a pattern matching"."would
fs.SkipDirthe whole context.0755, not the local directory's. This is the one entry whose mode landson a directory the base image already created, so whether a built image can be served must
not depend on the umask of whoever ran
sc deploy.chowns whatever its runtime user needs to write.
This changes every context digest, since the root entry is content. Nothing in the agent
caches on it; worth a thought on the platform side if anything does.
Test plan
go test ./...— all packages pass.TestPackContextWritesTheContextRootFirst: asserts the archive's first entry is./, adirectory, mode
0755, owner0:0— with the local directory deliberatelychmod 0700, soa tight umask escaping into the image would fail the test. Asserted on the tar headers
rather than on the hash, because the hash only says something changed, not that it is right.
TestPackContextRootEntryIsDeterministic: the root entry does not move when the localdirectory's mode does.
.dockerignoretests unchanged and passing.Verified end to end
Against the local stack,
make sc-deployofexample-app/:grey-ooo-test-webserver_app0/1, restarting, with thePermission deniedabove indocker service logs./appis755 root:root, the service reaches 1/1 Running, and answersHTTP 200 both inside the container and through the Swarm ingress on
:8080.Follow-up, not in this PR
The pin bump in
someones.computeris a separate change in that repo.Every `sc deploy` of a Dockerfile that does `COPY . <existing-dir>` produced an image that could not serve. The platform builds with kaniko, and kaniko takes the mode of an existing destination directory from the context tar's **root entry** — defaulting it to 0750 owned by root when the tar has none. `docker build` instead leaves an existing directory exactly as the base image had it. packContext skipped "." in its walk, so there was never a root entry. The result, against the same Dockerfile: docker build /app 755 app:app sc deploy /app 750 root:root A base image that serves as a de-privileged user then cannot traverse its own application directory. `matthewbaggett/php` serves as uid 1000 `app`, so nginx answered stat() "/app/public/index.php" failed (13: Permission denied) the healthcheck never passed, and Swarm restarted the task for ever — after a build and push that had both reported success. The root entry carries a fixed 0755 rather than the local directory's mode: it is the one entry whose mode lands on a directory the base image already created, and whether a built image can be served must not depend on the umask of whoever ran `sc deploy`. Ownership is root, as for every other entry. It is written outside the walk because the walk applies .dockerignore, and a pattern matching "." would prune the whole context. Note this changes every context digest, since the root entry is content. Verified end to end against the local stack: example-app now builds, pushes, and reaches 1/1 Running, answering HTTP 200 through the Swarm ingress. Before the change the same deploy sat at 0/1 restarting.