fix(pipelock): write yaml to /etc/pipelock.yaml since image lacks /etc/pipelock
The pipelock image is distroless and does not contain /etc/pipelock/, so docker cp to /etc/pipelock/pipelock.yaml fails with "Could not find the file /etc/pipelock in container" — docker cp does not create missing intermediate parent directories when targeting a stopped container, and no shell is available in the image for a mkdir shim. Move the config file to /etc/pipelock.yaml (directly under /etc, which always exists) and update the --config argv to match. Also surface docker cp stderr in the die message so future failures of this sort are debuggable. Assisted-by: Claude Code
This commit is contained in:
+20
-29
@@ -271,7 +271,7 @@ pipelock_write_yaml() {
|
||||
# service name. The image runs `pipelock` as its CMD; we override
|
||||
# with `run --config <path>` and the listen address.
|
||||
# 2. `docker cp` the YAML config from the host mktemp dir into the
|
||||
# container at /etc/pipelock/pipelock.yaml.
|
||||
# container at /etc/pipelock.yaml.
|
||||
#
|
||||
# We use docker cp rather than `-v <host>:<container>` because Docker
|
||||
# Desktop bind mounts have ownership / case-sensitivity quirks on
|
||||
@@ -308,29 +308,15 @@ pipelock_start() {
|
||||
die "pipelock yaml not found at ${host_yaml}; pipelock_write_yaml must run first"
|
||||
fi
|
||||
|
||||
# Container layout: pipelock reads its config from /etc/pipelock/pipelock.yaml.
|
||||
# We bring the container up with a `sleep` shim so we can `docker cp`
|
||||
# the config in, then restart with the real command — this avoids a
|
||||
# bind mount entirely and keeps the host file off the container's
|
||||
# filesystem after the cp completes.
|
||||
#
|
||||
# Two-phase boot:
|
||||
# phase 1: `docker run -d --entrypoint sh ... -c 'mkdir -p /etc/pipelock && sleep infinity'`
|
||||
# so we can cp the YAML in before pipelock starts and
|
||||
# tries to read it. We do NOT attach to internal_network
|
||||
# here; we'll connect after the config is in place so the
|
||||
# real pipelock process never sees a half-configured
|
||||
# agent on the wire.
|
||||
# phase 2: docker cp + docker restart with the real command via
|
||||
# --entrypoint reset (handled below).
|
||||
# Container layout: pipelock reads its config from /etc/pipelock.yaml.
|
||||
# We `docker create` the sidecar, `docker cp` the YAML into the
|
||||
# writable layer, then `docker start` it — no bind mount, no shell
|
||||
# shim. The image is distroless (no `sh`), and `docker cp` to a
|
||||
# stopped container does NOT create intermediate parent directories,
|
||||
# so the YAML lives directly under /etc rather than in a /etc/pipelock
|
||||
# subdirectory.
|
||||
info "starting pipelock sidecar ${name} on network ${internal_network}"
|
||||
|
||||
# We cannot easily restart a container with a different command
|
||||
# using `docker restart`. Instead, run the container in two stages:
|
||||
# boot it with `sh -c 'mkdir + sleep'`, cp the file in, then start
|
||||
# the real pipelock by docker exec'ing it as PID-N. A simpler
|
||||
# approach: `docker create` + `docker cp` + `docker start`. Use that.
|
||||
#
|
||||
# Sidecar argv verification (PR #1 review). The pinned digest
|
||||
# (CLAUDE_BOTTLE_PIPELOCK_IMAGE above) has:
|
||||
# ENTRYPOINT ["/pipelock"]
|
||||
@@ -348,18 +334,23 @@ pipelock_start() {
|
||||
--name "$name" \
|
||||
--network "$internal_network" \
|
||||
"$CLAUDE_BOTTLE_PIPELOCK_IMAGE" \
|
||||
run --config /etc/pipelock/pipelock.yaml --listen "0.0.0.0:${CLAUDE_BOTTLE_PIPELOCK_PORT}" \
|
||||
run --config /etc/pipelock.yaml --listen "0.0.0.0:${CLAUDE_BOTTLE_PIPELOCK_PORT}" \
|
||||
>/dev/null 2>&1; then
|
||||
die "failed to create pipelock sidecar ${name}"
|
||||
fi
|
||||
|
||||
# `docker cp` to a created-but-not-started container creates parent
|
||||
# dirs as needed and works without the container running, since the
|
||||
# cp is done against the writable layer directly.
|
||||
if ! docker cp "$host_yaml" "${name}:/etc/pipelock/pipelock.yaml" >/dev/null 2>&1; then
|
||||
# `docker cp` to a created-but-not-started container writes into the
|
||||
# writable layer directly. The parent directory must already exist in
|
||||
# the image — docker cp does NOT create missing intermediate dirs to
|
||||
# a stopped container, contrary to a common assumption. The pipelock
|
||||
# image is distroless (no `sh`), so we cannot prepopulate dirs with a
|
||||
# shell shim either. We therefore put the config in /etc/pipelock.yaml
|
||||
# (file directly under /etc) rather than /etc/pipelock/pipelock.yaml.
|
||||
local cp_err
|
||||
cp_err="$(docker cp "$host_yaml" "${name}:/etc/pipelock.yaml" 2>&1)" || {
|
||||
docker rm -f "$name" >/dev/null 2>&1 || true
|
||||
die "failed to copy pipelock yaml into ${name}"
|
||||
fi
|
||||
die "failed to copy pipelock yaml into ${name}: ${cp_err}"
|
||||
}
|
||||
|
||||
# Attach to a per-agent user-defined bridge network for upstream
|
||||
# egress. The internal network has no gateway by definition, so
|
||||
|
||||
Reference in New Issue
Block a user