diff --git a/Justfile b/Justfile index 575a877..1929e78 100644 --- a/Justfile +++ b/Justfile @@ -627,9 +627,15 @@ penca-up profile="dev" db="": vm-gc # `seaweedfs` (the in-stack S3 gateway + its bucket-init job) is dropped # for stack profiles whose cold tier is a real bucket — it would idle with # nothing to serve, and its bucket-init job would assert a bucket the - # deployment never reads. Add future real-bucket profiles to this case. + # deployment never reads. Those profiles also need compose.s3.yml, which + # relaxes the servicers' dependency on the now-absent seaweedfs-init; see + # that file for why the relaxation is opt-in rather than the default. + # Add future real-bucket profiles to this case. case "{{profile}}" in - s3) profiles="--profile infra --profile penca-backend" ;; + s3) + compose_files="$compose_files -f docker/compose.s3.yml" + profiles="--profile infra --profile penca-backend" + ;; *) profiles="--profile infra --profile seaweedfs --profile penca-backend" ;; esac diff --git a/docker/compose.s3.yml b/docker/compose.s3.yml new file mode 100644 index 0000000..fc7bacd --- /dev/null +++ b/docker/compose.s3.yml @@ -0,0 +1,40 @@ +# Override fragment for the `s3` stack profile, layered on docker/compose.yml: +# +# docker compose -f docker/compose.yml -f docker/compose.s3.yml \ +# --env-file docker/s3.env --profile infra --profile penca-backend up -d +# +# `just penca-up --profile s3` adds it automatically. +# +# Sole purpose: relax the servicers' dependency on `seaweedfs-init`. The `s3` +# profile points the cold tier at a real bucket, so the `seaweedfs` compose +# profile is off and that service is not part of the project at all — without +# `required: false` compose refuses the whole project with `service "query" +# depends on undefined service "seaweedfs-init": invalid compose project`. +# +# This lives here rather than in compose.yml because `required: false` is NOT +# scoped to "the dependency is absent". It equally downgrades a dependency that +# IS present but exited non-zero: compose logs a warning and starts the +# dependents anyway, exiting 0. In the base file it would therefore delete the +# fail-fast that seaweedfs-init's `s3.bucket.list` assertion exists to provide +# (CHA-542) for the dev and test profiles, letting the servicers boot against a +# nonexistent bucket and fail later as opaque NoSuchBucket errors. +# +# So the safe behavior is the default and the relaxation is opt-in, scoped to +# the one profile that cannot have the dependency. dev/test, and any raw +# `docker compose -f docker/compose.yml` invocation, keep the hard gate. +services: + query: + depends_on: + seaweedfs-init: + condition: service_completed_successfully + required: false + write: + depends_on: + seaweedfs-init: + condition: service_completed_successfully + required: false + lifecycle: + depends_on: + seaweedfs-init: + condition: service_completed_successfully + required: false diff --git a/docker/compose.yml b/docker/compose.yml index f5646c0..e1a62b5 100644 --- a/docker/compose.yml +++ b/docker/compose.yml @@ -127,6 +127,15 @@ services: # errors. Asserting the end state restores the fail-fast `mc mb` gave us. # `awk` on an exact field rather than `grep -w`, which treats `-` as a word # boundary and would accept a stray `penca-foo` as proof of `penca`. CHA-542. + # + # The bucket name comes from the same OBJECT_STORAGE_BUCKET the servicers + # read (see the x-object-storage anchor). Hardcoding `penca` here while they + # interpolate would let the gateway create one bucket and the servicers open + # another — and since the shell environment outranks `--env-file` for + # interpolation, a stray export was enough to cause it. That divergence + # surfaces as an empty table rather than an error, which is the failure mode + # the anchor exists to prevent; this is the fourth participant it has to + # cover. seaweedfs-init: image: chrislusf/seaweedfs:4.33 profiles: [seaweedfs] @@ -137,9 +146,9 @@ services: - /bin/sh - -c - | - echo 's3.bucket.create -name penca' | timeout 60 weed shell -master=seaweedfs:9333 + echo 's3.bucket.create -name ${OBJECT_STORAGE_BUCKET:-penca}' | timeout 60 weed shell -master=seaweedfs:9333 echo 's3.bucket.list' | timeout 60 weed shell -master=seaweedfs:9333 \ - | awk '$1 == "penca" { found = 1 } END { exit !found }' + | awk -v want='${OBJECT_STORAGE_BUCKET:-penca}' '$1 == want { found = 1 } END { exit !found }' # One-shot init job: seeds global Penca tables + the default catalog # row that SQL_SERVER_DEFAULT_CATALOG resolves against. All runtime @@ -180,11 +189,6 @@ services: condition: service_healthy seaweedfs-init: condition: service_completed_successfully - # `required: false` so the servicers still start when the `seaweedfs` - # profile is off (the `s3` profile points them at a real bucket, where - # the in-stack gateway has nothing to serve). When the profile IS on, - # this is still a hard wait on the bucket-create job. - required: false bootstrap-init: condition: service_completed_successfully environment: @@ -231,11 +235,6 @@ services: condition: service_healthy seaweedfs-init: condition: service_completed_successfully - # `required: false` so the servicers still start when the `seaweedfs` - # profile is off (the `s3` profile points them at a real bucket, where - # the in-stack gateway has nothing to serve). When the profile IS on, - # this is still a hard wait on the bucket-create job. - required: false bootstrap-init: condition: service_completed_successfully # CHA-273 rework: the write path connects to the lifecycle service at @@ -276,11 +275,6 @@ services: condition: service_healthy seaweedfs-init: condition: service_completed_successfully - # `required: false` so the servicers still start when the `seaweedfs` - # profile is off (the `s3` profile points them at a real bucket, where - # the in-stack gateway has nothing to serve). When the profile IS on, - # this is still a hard wait on the bucket-create job. - required: false bootstrap-init: condition: service_completed_successfully environment: