diff --git a/Docker-AIO/.env.example b/Docker-AIO/.env.example index 00041f5..26852f4 100644 --- a/Docker-AIO/.env.example +++ b/Docker-AIO/.env.example @@ -33,6 +33,31 @@ WARP_VERSION=latest WARP_AIO_ADMIN=true WARP_AIO_WEB=true +# ------------------------------------------------------------- seaweedfs + +# docker-compose.pocketbase.seaweedfs*.yml only; set WARP_PB_S3_ACCESS_KEY +# and WARP_PB_S3_SECRET below as well. +# +# The S3 gateway's host port, on loopback unless the bind address says +# otherwise. The homeserver reaches it inside the container; this is for +# aws-cli and the like. +SEAWEED_S3_BIND=127.0.0.1 +SEAWEED_S3_PORT=8333 + +# The split file only: SeaweedFS's admin UI. It can mint credentials for +# the bucket, so the password is required. Put it behind a reverse proxy +# before binding it to 0.0.0.0. The read-only login is optional. +SEAWEED_ADMIN_BIND=127.0.0.1 +SEAWEED_ADMIN_PORT=23646 +SEAWEED_ADMIN_USER=admin +SEAWEED_ADMIN_PASSWORD= +SEAWEED_ADMIN_READONLY_USER= +SEAWEED_ADMIN_READONLY_PASSWORD= + +# Building only: the SeaweedFS release to bake in. Leave it commented out to +# keep the Dockerfile's pin; an empty value breaks the build. +# SEAWEED_VERSION=4.45 + # ------------------------------------------------------------ homeserver # Part of every user and room ID: it cannot be changed later. @@ -89,6 +114,11 @@ WARP_DB_AUTO_MIGRATE= # docker-compose.pocketbase*.yml only: keep media in an S3 bucket instead # of pb_data/storage. Bucket, region, endpoint and keys are all required # when it is on. +# +# The pocketbase.seaweedfs files turn it on themselves, against the +# SeaweedFS in the container; there only the access key and secret are +# needed (required: they are the gateway's credentials too), and the bucket +# defaults to warp. WARP_PB_S3_ENABLED=false WARP_PB_S3_BUCKET= WARP_PB_S3_REGION= diff --git a/Docker-AIO/Dockerfile.PocketBase.SeaweedFS b/Docker-AIO/Dockerfile.PocketBase.SeaweedFS new file mode 100644 index 0000000..358cba7 --- /dev/null +++ b/Docker-AIO/Dockerfile.PocketBase.SeaweedFS @@ -0,0 +1,147 @@ +# warp-aio-pocketbase-seaweedfs — the homeserver, the admin panel and the +# web client on PocketBase, with SeaweedFS in the same image as the media +# store: one `weed server -s3` process, master, volume, filer and S3 gateway +# in one. The homeserver keeps its database in /data/pb_data and its media +# in the bucket WARP_PB_S3_BUCKET (default warp) on the gateway at +# 127.0.0.1:8333. For per-role processes and SeaweedFS's admin UI, see +# Dockerfile.PocketBase.SeaweedFS.split. +# +# Build from the repository root: +# +# docker build -t warp-aio-pocketbase-seaweedfs -f Docker-AIO/Dockerfile.PocketBase.SeaweedFS . +# docker run -p 8008:8008 -p 8009:8009 -p 8080:8080 -p 127.0.0.1:8333:8333 \ +# -v warp-data:/data -v seaweed-data:/seaweed/data \ +# -e WARP_SERVER_NAME=warp.example \ +# -e WARP_PB_S3_ACCESS_KEY=warp -e WARP_PB_S3_SECRET=change-me \ +# warp-aio-pocketbase-seaweedfs +# +# WARP_PB_S3_ACCESS_KEY and WARP_PB_S3_SECRET are required: they are the +# gateway's only identity and what the homeserver signs its requests with. +# +# Ports inside the container (publish 8333 on loopback at most; never 8081, +# the volume server, which serves content by id with no authentication): +# +# 8008 homeserver 8333 SeaweedFS S3 gateway +# 8009 admin panel 8888 SeaweedFS filer +# 8080 web client 9333 SeaweedFS master +# 8081 SeaweedFS volume (not its usual 8080, which is the web client) + +# SeaweedFS release to bake in, pinned so a rebuild later brings up the same +# one; --build-arg SEAWEED_VERSION=... to upgrade. Global, because a FROM uses +# it. +ARG SEAWEED_VERSION="4.45" + +FROM node:22-alpine AS web + +WORKDIR /src +RUN corepack enable + +# Lockfile and manifests first, so the package store survives source changes. +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY apps/ui/package.json ./apps/ui/ +COPY apps/admin/package.json ./apps/admin/ +COPY apps/web/package.json ./apps/web/ +RUN pnpm install --frozen-lockfile --filter @warp/admin... --filter @warp/web... + +COPY apps/ui ./apps/ui +COPY apps/admin ./apps/admin +COPY apps/web ./apps/web +RUN pnpm --filter @warp/admin build && pnpm --filter @warp/web build + + +FROM golang:1.26-alpine AS build + +ARG VERSION=0.0.0-dev +ARG COMMIT=unknown +ARG BUILD_DATE=unknown +ENV CGO_ENABLED=0 + +WORKDIR /src + +# The homeserver. Dependencies first, so the module cache survives source +# changes. +COPY server/go.mod server/go.sum ./server/ +RUN cd server && go mod download +COPY server ./server +RUN cd server && go build -trimpath \ + -ldflags "-s -w \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Version=${VERSION} \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Commit=${COMMIT} \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.BuildDate=${BUILD_DATE}" \ + -o /out/warp ./cmd/warp + +# The admin panel and the web client, each with its frontend embedded. +COPY apps/admin/go.mod ./apps/admin/ +COPY apps/admin/cmd ./apps/admin/cmd +COPY apps/admin/internal ./apps/admin/internal +COPY --from=web /src/apps/admin/internal/panel/build/dist ./apps/admin/internal/panel/build/dist +RUN cd apps/admin && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o /out/warp-admin ./cmd/warp-admin + +COPY apps/web/go.mod ./apps/web/ +COPY apps/web/cmd ./apps/web/cmd +COPY apps/web/host ./apps/web/host +COPY apps/web/client/client.go ./apps/web/client/ +COPY --from=web /src/apps/web/client/build/dist ./apps/web/client/build/dist +RUN cd apps/web && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o /out/warp-web ./cmd/warp-web + + +# The one static weed binary from the official image. +FROM chrislusf/seaweedfs:${SEAWEED_VERSION} AS seaweed + + +FROM alpine:3.21 + +# tini is PID 1 so signals reach the entrypoint and zombies are reaped. +RUN apk add --no-cache ca-certificates tzdata curl tini && \ + adduser -D -u 10001 -h /data warp + +COPY --from=build /out/warp /out/warp-admin /out/warp-web /usr/local/bin/ +COPY --from=seaweed /usr/bin/weed /usr/local/bin/weed +COPY warp.yaml.example /etc/warp/warp.yaml.example +COPY --chmod=755 Docker-AIO/entrypoint.sh /usr/local/bin/warp-aio + +# /data: the database, signing key and anything stored before S3 was on. +# /seaweed/data: everything SeaweedFS keeps. +# Mount a volume on each; a fresh named volume inherits the ownership. +RUN mkdir -p /data /seaweed/data && chown -R warp:warp /data /seaweed +WORKDIR /data +USER warp + +# Media goes to the bucket on the gateway inside this image: a fixed +# loopback address, path style because a self-hosted gateway has no +# per-bucket DNS, and a region SeaweedFS ignores. Media stored on the +# filesystem before S3 was on is not moved into the bucket. +ENV WARP_LISTEN_ADDRESS=":8008" \ + WARP_PRIVATE_KEY="/data/signing.key" \ + WARP_DB_DRIVER="pocketbase" \ + WARP_DB_CONNECTION_STRING="/data/pb_data" \ + WARP_PB_S3_ENABLED="true" \ + WARP_PB_S3_ENDPOINT="http://127.0.0.1:8333" \ + WARP_PB_S3_BUCKET="warp" \ + WARP_PB_S3_REGION="us-east-1" \ + WARP_PB_S3_FORCE_PATH_STYLE="true" \ + WARP_LOG_FORMAT="json" \ + WARP_ADMIN_LISTEN=":8009" \ + WARP_ADMIN_HOMESERVER="http://127.0.0.1:8008" \ + WARP_ADMIN_LOG_FORMAT="json" \ + WARP_WEB_LISTEN=":8080" \ + WARP_WEB_HOMESERVER="http://127.0.0.1:8008" \ + WARP_WEB_LOG_FORMAT="json" \ + WARP_AIO_ADMIN="true" \ + WARP_AIO_WEB="true" \ + WARP_AIO_EMBEDDED_POSTGRES="false" \ + WARP_AIO_SEAWEEDFS="server" + +EXPOSE 8008 8009 8080 8333 + +# Every process must answer, so a wedged one shows in `docker ps`. +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS -o /dev/null http://127.0.0.1:9333/cluster/status && \ + curl -fsS -o /dev/null http://127.0.0.1:8333/healthz && \ + curl -fsS -o /dev/null http://127.0.0.1:8008/healthz && \ + { [ "$WARP_AIO_ADMIN" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8009/healthz; } && \ + { [ "$WARP_AIO_WEB" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8080/healthz; } || exit 1 + +ENTRYPOINT ["/sbin/tini", "--", "warp-aio"] diff --git a/Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split b/Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split new file mode 100644 index 0000000..828c8d7 --- /dev/null +++ b/Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split @@ -0,0 +1,159 @@ +# warp-aio-pocketbase-seaweedfs-split — the homeserver, the admin panel and +# the web client on PocketBase, with SeaweedFS in the same image as the +# media store, split into its roles: master, volume, filer, S3 gateway and +# admin UI, one process each. Over Dockerfile.PocketBase.SeaweedFS that buys +# per-role restart, per-role metrics ports and the admin UI, where S3 +# identities are minted and revoked under Object Store → Users. +# +# The homeserver's identity lives in the filer's IAM store, seeded on every +# boot, the same store the admin UI writes; so a key added there never +# displaces the homeserver's. /seaweed/data has the layout `weed server -dir` +# writes, so it moves between the two SeaweedFS images unchanged. +# +# Build from the repository root: +# +# docker build -t warp-aio-pocketbase-seaweedfs-split -f Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split . +# docker run -p 8008:8008 -p 8009:8009 -p 8080:8080 \ +# -p 127.0.0.1:8333:8333 -p 127.0.0.1:23646:23646 \ +# -v warp-data:/data -v seaweed-data:/seaweed/data -v seaweed-admin:/seaweed/admin \ +# -e WARP_SERVER_NAME=warp.example \ +# -e WARP_PB_S3_ACCESS_KEY=warp -e WARP_PB_S3_SECRET=change-me \ +# -e WEED_ADMIN_PASSWORD=change-me \ +# warp-aio-pocketbase-seaweedfs-split +# +# WARP_PB_S3_ACCESS_KEY, WARP_PB_S3_SECRET and WEED_ADMIN_PASSWORD are +# required; the container refuses to start an open gateway or admin UI. +# +# Ports inside the container (publish 8333 and 23646 on loopback at most; +# master, volume and filer listen on loopback only): +# +# 8008 homeserver 8333 SeaweedFS S3 gateway +# 8009 admin panel 8888 SeaweedFS filer +# 8080 web client 9333 SeaweedFS master +# 8081 SeaweedFS volume 23646 SeaweedFS admin UI +# 9324-9328 SeaweedFS metrics: master, volume, filer, S3, admin + +# SeaweedFS release to bake in, pinned so a rebuild later brings up the same +# one; --build-arg SEAWEED_VERSION=... to upgrade. Global, because a FROM uses +# it. +ARG SEAWEED_VERSION="4.45" + +FROM node:22-alpine AS web + +WORKDIR /src +RUN corepack enable + +# Lockfile and manifests first, so the package store survives source changes. +COPY package.json pnpm-workspace.yaml pnpm-lock.yaml ./ +COPY apps/ui/package.json ./apps/ui/ +COPY apps/admin/package.json ./apps/admin/ +COPY apps/web/package.json ./apps/web/ +RUN pnpm install --frozen-lockfile --filter @warp/admin... --filter @warp/web... + +COPY apps/ui ./apps/ui +COPY apps/admin ./apps/admin +COPY apps/web ./apps/web +RUN pnpm --filter @warp/admin build && pnpm --filter @warp/web build + + +FROM golang:1.26-alpine AS build + +ARG VERSION=0.0.0-dev +ARG COMMIT=unknown +ARG BUILD_DATE=unknown +ENV CGO_ENABLED=0 + +WORKDIR /src + +# The homeserver. Dependencies first, so the module cache survives source +# changes. +COPY server/go.mod server/go.sum ./server/ +RUN cd server && go mod download +COPY server ./server +RUN cd server && go build -trimpath \ + -ldflags "-s -w \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Version=${VERSION} \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.Commit=${COMMIT} \ + -X github.com/dariuszjaniak/warp/server/internal/buildinfo.BuildDate=${BUILD_DATE}" \ + -o /out/warp ./cmd/warp + +# The admin panel and the web client, each with its frontend embedded. +COPY apps/admin/go.mod ./apps/admin/ +COPY apps/admin/cmd ./apps/admin/cmd +COPY apps/admin/internal ./apps/admin/internal +COPY --from=web /src/apps/admin/internal/panel/build/dist ./apps/admin/internal/panel/build/dist +RUN cd apps/admin && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o /out/warp-admin ./cmd/warp-admin + +COPY apps/web/go.mod ./apps/web/ +COPY apps/web/cmd ./apps/web/cmd +COPY apps/web/host ./apps/web/host +COPY apps/web/client/client.go ./apps/web/client/ +COPY --from=web /src/apps/web/client/build/dist ./apps/web/client/build/dist +RUN cd apps/web && GOWORK=off go build -trimpath -ldflags "-s -w -X main.version=${VERSION}" \ + -o /out/warp-web ./cmd/warp-web + + +# The one static weed binary from the official image. +FROM chrislusf/seaweedfs:${SEAWEED_VERSION} AS seaweed + + +FROM alpine:3.21 + +# tini is PID 1 so signals reach the entrypoint and zombies are reaped. +RUN apk add --no-cache ca-certificates tzdata curl tini && \ + adduser -D -u 10001 -h /data warp + +COPY --from=build /out/warp /out/warp-admin /out/warp-web /usr/local/bin/ +COPY --from=seaweed /usr/bin/weed /usr/local/bin/weed +COPY warp.yaml.example /etc/warp/warp.yaml.example +COPY --chmod=755 Docker-AIO/entrypoint.sh /usr/local/bin/warp-aio + +# /data: the database, signing key and anything stored before S3 was on. +# /seaweed/data: master, volume and filer, sharing it. +# /seaweed/admin: the admin UI's session key and maintenance state. +# Mount a volume on each; a fresh named volume inherits the ownership. +RUN mkdir -p /data /seaweed/data /seaweed/admin && chown -R warp:warp /data /seaweed +WORKDIR /data +USER warp + +# Media goes to the bucket on the gateway inside this image: a fixed +# loopback address, path style because a self-hosted gateway has no +# per-bucket DNS, and a region SeaweedFS ignores. Media stored on the +# filesystem before S3 was on is not moved into the bucket. +ENV WARP_LISTEN_ADDRESS=":8008" \ + WARP_PRIVATE_KEY="/data/signing.key" \ + WARP_DB_DRIVER="pocketbase" \ + WARP_DB_CONNECTION_STRING="/data/pb_data" \ + WARP_PB_S3_ENABLED="true" \ + WARP_PB_S3_ENDPOINT="http://127.0.0.1:8333" \ + WARP_PB_S3_BUCKET="warp" \ + WARP_PB_S3_REGION="us-east-1" \ + WARP_PB_S3_FORCE_PATH_STYLE="true" \ + WARP_LOG_FORMAT="json" \ + WARP_ADMIN_LISTEN=":8009" \ + WARP_ADMIN_HOMESERVER="http://127.0.0.1:8008" \ + WARP_ADMIN_LOG_FORMAT="json" \ + WARP_WEB_LISTEN=":8080" \ + WARP_WEB_HOMESERVER="http://127.0.0.1:8008" \ + WARP_WEB_LOG_FORMAT="json" \ + WARP_AIO_ADMIN="true" \ + WARP_AIO_WEB="true" \ + WARP_AIO_EMBEDDED_POSTGRES="false" \ + WARP_AIO_SEAWEEDFS="split" \ + WEED_ADMIN_USER="admin" + +EXPOSE 8008 8009 8080 8333 23646 + +# Every process must answer, so a wedged one shows in `docker ps`. +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=3 \ + CMD curl -fsS -o /dev/null http://127.0.0.1:9333/cluster/status && \ + curl -fsS -o /dev/null http://127.0.0.1:8081/healthz && \ + curl -fsS -o /dev/null http://127.0.0.1:8888/healthz && \ + curl -fsS -o /dev/null http://127.0.0.1:8333/healthz && \ + curl -fsS -o /dev/null http://127.0.0.1:23646/health && \ + curl -fsS -o /dev/null http://127.0.0.1:8008/healthz && \ + { [ "$WARP_AIO_ADMIN" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8009/healthz; } && \ + { [ "$WARP_AIO_WEB" != true ] || curl -fsS -o /dev/null http://127.0.0.1:8080/healthz; } || exit 1 + +ENTRYPOINT ["/sbin/tini", "--", "warp-aio"] diff --git a/Docker-AIO/README.md b/Docker-AIO/README.md index 9662fe1..00da281 100644 --- a/Docker-AIO/README.md +++ b/Docker-AIO/README.md @@ -10,15 +10,21 @@ policy brings the set back. | [`Dockerfile`](Dockerfile) | `warp-aio` | SQLite, `/data/warp.db` | | [`Dockerfile.PocketBase`](Dockerfile.PocketBase) | `warp-aio-pocketbase` | PocketBase (embedded driver), `/data/pb_data` | | [`Dockerfile.PostgreSQL`](Dockerfile.PostgreSQL) | `warp-aio-postgresql` | PostgreSQL 17 in the container, `/data/postgres` | +| [`Dockerfile.PocketBase.SeaweedFS`](Dockerfile.PocketBase.SeaweedFS) | `warp-aio-pocketbase-seaweedfs` | PocketBase, media in SeaweedFS (`weed server -s3`) in the container | +| [`Dockerfile.PocketBase.SeaweedFS.split`](Dockerfile.PocketBase.SeaweedFS.split) | `warp-aio-pocketbase-seaweedfs-split` | PocketBase, media in SeaweedFS split into its roles, with its admin UI | | Compose file | Image | | --- | --- | | `docker-compose.sqlite.yml` | `warp-aio`, built here | | `docker-compose.pocketbase.yml` | `warp-aio-pocketbase`, built here | | `docker-compose.postgresql.yml` | `warp-aio-postgresql`, built here | +| `docker-compose.pocketbase.seaweedfs.yml` | `warp-aio-pocketbase-seaweedfs`, built here | +| `docker-compose.pocketbase.seaweedfs.split.yml` | `warp-aio-pocketbase-seaweedfs-split`, built here | | `docker-compose.*.prod.yml` | the same images, pulled, never built | -Everything lives in the `warp-data` volume, mounted on `/data`. +Everything lives in the `warp-data` volume, mounted on `/data`; the +SeaweedFS images add `seaweed-data` on `/seaweed/data` (and the split one +`seaweed-admin` on `/seaweed/admin`). ```sh cd Docker-AIO @@ -34,9 +40,48 @@ inside the container, so it needs no password and is not reachable from outside. To point that image at a PostgreSQL elsewhere instead, set `WARP_AIO_EMBEDDED_POSTGRES=false` and `WARP_DB_CONNECTION_STRING`. -The three Dockerfiles share their build stages line for line; only the -header and the final stage differ. Keep the build stages in step when -changing one. +The Dockerfiles share their build stages line for line; only the header, +the SeaweedFS stage and the final stage differ. Keep the build stages in +step when changing one. + +## SeaweedFS + +The two SeaweedFS images keep the PocketBase database in `/data/pb_data` +and put media in an S3 bucket (`WARP_PB_S3_BUCKET`, default `warp`) on a +SeaweedFS inside the same container. The compose files point the +homeserver at it; `.env` only has to give it credentials: + +```sh +cp .env.example .env # set WARP_PB_S3_ACCESS_KEY, WARP_PB_S3_SECRET + # (and SEAWEED_ADMIN_PASSWORD for split) +docker compose -f docker-compose.pocketbase.seaweedfs.yml up -d --build +``` + +- **`Dockerfile.PocketBase.SeaweedFS`** runs `weed server -s3`: master, + volume, filer and S3 gateway in one process. The gateway has exactly + one identity, the homeserver's key pair. +- **`Dockerfile.PocketBase.SeaweedFS.split`** runs each role as its own + process, plus SeaweedFS's admin UI on port 23646, where further S3 + identities are created and revoked under *Object Store → Users*. The + homeserver's identity is written into the filer's IAM store on every + boot (the store the admin UI writes too), so a key added there never + displaces it, and a rotated `WARP_PB_S3_SECRET` lands on the next start. + +Both refuse to start without the key pair, and the split one without +`SEAWEED_ADMIN_PASSWORD`: either would otherwise serve an open gateway or +an open panel that can mint credentials. The S3 gateway and the admin UI +are published on 127.0.0.1 only; the volume server, which serves content +by id with no authentication, is never published. Inside, it is on 8081, +since 8080 is the web client. + +Both write `/seaweed/data` in the layout `weed server -dir` uses, and their +compose files share a project and volumes, so switching between them +needs no migration. + +Media already stored on the filesystem is not moved into the bucket when +S3 is turned on, so start these on a fresh `warp-data` or copy the files +across first. The SeaweedFS release is pinned in the Dockerfiles +(`SEAWEED_VERSION`, 4.45); upgrading it means a rebuild. ## Settings @@ -62,7 +107,10 @@ from the repository root: docker build -t $WARP_REGISTRY/warp-aio:$WARP_VERSION -f Docker-AIO/Dockerfile . docker build -t $WARP_REGISTRY/warp-aio-pocketbase:$WARP_VERSION -f Docker-AIO/Dockerfile.PocketBase . docker build -t $WARP_REGISTRY/warp-aio-postgresql:$WARP_VERSION -f Docker-AIO/Dockerfile.PostgreSQL . -for i in warp-aio warp-aio-pocketbase warp-aio-postgresql; do +docker build -t $WARP_REGISTRY/warp-aio-pocketbase-seaweedfs:$WARP_VERSION -f Docker-AIO/Dockerfile.PocketBase.SeaweedFS . +docker build -t $WARP_REGISTRY/warp-aio-pocketbase-seaweedfs-split:$WARP_VERSION -f Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split . +for i in warp-aio warp-aio-pocketbase warp-aio-postgresql \ + warp-aio-pocketbase-seaweedfs warp-aio-pocketbase-seaweedfs-split; do docker push $WARP_REGISTRY/$i:$WARP_VERSION done ``` diff --git a/Docker-AIO/docker-compose.pocketbase.seaweedfs.prod.yml b/Docker-AIO/docker-compose.pocketbase.seaweedfs.prod.yml new file mode 100644 index 0000000..f0a2c9a --- /dev/null +++ b/Docker-AIO/docker-compose.pocketbase.seaweedfs.prod.yml @@ -0,0 +1,64 @@ +# Warp all-in-one on PocketBase with SeaweedFS: the homeserver, the admin +# panel, the web client and SeaweedFS (one `weed server -s3` process) in one +# container pulled from a registry (the image is built from +# Dockerfile.PocketBase.SeaweedFS). +# +# cd Docker-AIO +# cp .env.example .env # then edit it; set WARP_PB_S3_ACCESS_KEY +# # and WARP_PB_S3_SECRET +# docker compose -f docker-compose.pocketbase.seaweedfs.prod.yml up -d +# +# The database stays in /data/pb_data (warp-data); media goes to the bucket +# WARP_PB_S3_BUCKET in SeaweedFS (seaweed-data), whose gateway has exactly +# one identity: the homeserver's. Media stored before S3 was on is not moved +# into the bucket. +# +# Homeserver http://localhost:8008, admin panel http://localhost:8009, web +# client http://localhost:8080. The S3 gateway (8333) is published on +# loopback, for aws-cli and the like; SEAWEED_S3_BIND=0.0.0.0 exposes it. +# +# This and docker-compose.pocketbase.seaweedfs.split.yml share their project +# and volumes: SeaweedFS's data has the same layout either way, so switching +# between them needs no migration. +# +# Create the first administrator once the server is up: +# +# docker compose -f docker-compose.pocketbase.seaweedfs.prod.yml exec warp warp create-account --username admin --admin +# +# Nothing is built here: the image comes from WARP_REGISTRY (default +# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin +# WARP_VERSION to a release in production, and update with +# +# docker compose -f docker-compose.pocketbase.seaweedfs.prod.yml pull && docker compose -f docker-compose.pocketbase.seaweedfs.prod.yml up -d + +name: warp-aio-pocketbase-seaweedfs + +services: + warp: + image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-aio-pocketbase-seaweedfs:${WARP_VERSION:-latest} + pull_policy: always + restart: unless-stopped + env_file: .env + environment: + # The gateway is in the container at a fixed address; these override + # whatever .env says about S3. + WARP_PB_S3_ENABLED: "true" + WARP_PB_S3_ENDPOINT: http://127.0.0.1:8333 + WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp} + WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-us-east-1} + WARP_PB_S3_FORCE_PATH_STYLE: "true" + # The gateway's only identity, and what the homeserver signs with. + WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env} + WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env} + volumes: + - warp-data:/data + - seaweed-data:/seaweed/data + ports: + - "${WARP_PORT:-8008}:8008" + - "${WARP_ADMIN_PORT:-8009}:8009" + - "${WARP_WEB_PORT:-8080}:8080" + - "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333" + +volumes: + warp-data: + seaweed-data: diff --git a/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.prod.yml b/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.prod.yml new file mode 100644 index 0000000..849726d --- /dev/null +++ b/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.prod.yml @@ -0,0 +1,76 @@ +# Warp all-in-one on PocketBase with SeaweedFS split into its roles: the +# homeserver, the admin panel, the web client, and SeaweedFS's master, +# volume, filer, S3 gateway and admin UI, all in one container pulled from +# a registry (the image is built from Dockerfile.PocketBase.SeaweedFS.split). +# +# cd Docker-AIO +# cp .env.example .env # then edit it; set WARP_PB_S3_ACCESS_KEY, +# # WARP_PB_S3_SECRET and SEAWEED_ADMIN_PASSWORD +# docker compose -f docker-compose.pocketbase.seaweedfs.split.prod.yml up -d +# +# The database stays in /data/pb_data (warp-data); media goes to the bucket +# WARP_PB_S3_BUCKET in SeaweedFS (seaweed-data). The homeserver's S3 identity +# is seeded into the filer's IAM store on every boot, beside any added in +# the admin UI under Object Store → Users. Media stored before S3 was on is +# not moved into the bucket. +# +# Homeserver http://localhost:8008, admin panel http://localhost:8009, web +# client http://localhost:8080. The S3 gateway (8333) and the SeaweedFS admin +# UI (23646) are published on loopback; SEAWEED_S3_BIND and +# SEAWEED_ADMIN_BIND=0.0.0.0 expose them, the admin UI only behind a proxy. +# +# This and docker-compose.pocketbase.seaweedfs.yml share their project and +# volumes: SeaweedFS's data has the same layout either way, so switching +# between them needs no migration. +# +# Create the first administrator once the server is up: +# +# docker compose -f docker-compose.pocketbase.seaweedfs.split.prod.yml exec warp warp create-account --username admin --admin +# +# Nothing is built here: the image comes from WARP_REGISTRY (default +# ghcr.io/dariuszjaniak) at tag WARP_VERSION (default latest). Pin +# WARP_VERSION to a release in production, and update with +# +# docker compose -f docker-compose.pocketbase.seaweedfs.split.prod.yml pull && docker compose -f docker-compose.pocketbase.seaweedfs.split.prod.yml up -d + +name: warp-aio-pocketbase-seaweedfs + +services: + warp: + image: ${WARP_REGISTRY:-ghcr.io/dariuszjaniak}/warp-aio-pocketbase-seaweedfs-split:${WARP_VERSION:-latest} + pull_policy: always + restart: unless-stopped + env_file: .env + environment: + # The gateway is in the container at a fixed address; these override + # whatever .env says about S3. + WARP_PB_S3_ENABLED: "true" + WARP_PB_S3_ENDPOINT: http://127.0.0.1:8333 + WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp} + WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-us-east-1} + WARP_PB_S3_FORCE_PATH_STYLE: "true" + # The homeserver's identity in SeaweedFS, and what it signs with. + WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env} + WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env} + # The admin UI can mint bucket credentials, so it needs a password. + WEED_ADMIN_USER: ${SEAWEED_ADMIN_USER:-admin} + WEED_ADMIN_PASSWORD: ${SEAWEED_ADMIN_PASSWORD:?set SEAWEED_ADMIN_PASSWORD in .env} + WEED_ADMIN_READONLY_USER: ${SEAWEED_ADMIN_READONLY_USER:-} + WEED_ADMIN_READONLY_PASSWORD: ${SEAWEED_ADMIN_READONLY_PASSWORD:-} + volumes: + - warp-data:/data + - seaweed-data:/seaweed/data + - seaweed-admin:/seaweed/admin + ports: + - "${WARP_PORT:-8008}:8008" + - "${WARP_ADMIN_PORT:-8009}:8009" + - "${WARP_WEB_PORT:-8080}:8080" + - "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333" + - "${SEAWEED_ADMIN_BIND:-127.0.0.1}:${SEAWEED_ADMIN_PORT:-23646}:23646" + +volumes: + warp-data: + # Master, volume and filer share it, as `weed server -dir` lays it out. + seaweed-data: + # The admin UI's session key and maintenance state; no object data. + seaweed-admin: diff --git a/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.yml b/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.yml new file mode 100644 index 0000000..2dcb946 --- /dev/null +++ b/Docker-AIO/docker-compose.pocketbase.seaweedfs.split.yml @@ -0,0 +1,75 @@ +# Warp all-in-one on PocketBase with SeaweedFS split into its roles: the +# homeserver, the admin panel, the web client, and SeaweedFS's master, +# volume, filer, S3 gateway and admin UI, all in one container built from +# this checkout (Dockerfile.PocketBase.SeaweedFS.split). +# +# cd Docker-AIO +# cp .env.example .env # then edit it; set WARP_PB_S3_ACCESS_KEY, +# # WARP_PB_S3_SECRET and SEAWEED_ADMIN_PASSWORD +# docker compose -f docker-compose.pocketbase.seaweedfs.split.yml up -d --build +# +# The database stays in /data/pb_data (warp-data); media goes to the bucket +# WARP_PB_S3_BUCKET in SeaweedFS (seaweed-data). The homeserver's S3 identity +# is seeded into the filer's IAM store on every boot, beside any added in +# the admin UI under Object Store → Users. Media stored before S3 was on is +# not moved into the bucket. +# +# Homeserver http://localhost:8008, admin panel http://localhost:8009, web +# client http://localhost:8080. The S3 gateway (8333) and the SeaweedFS admin +# UI (23646) are published on loopback; SEAWEED_S3_BIND and +# SEAWEED_ADMIN_BIND=0.0.0.0 expose them, the admin UI only behind a proxy. +# +# This and docker-compose.pocketbase.seaweedfs.yml share their project and +# volumes: SeaweedFS's data has the same layout either way, so switching +# between them needs no migration. +# +# Create the first administrator once the server is up: +# +# docker compose -f docker-compose.pocketbase.seaweedfs.split.yml exec warp warp create-account --username admin --admin + +name: warp-aio-pocketbase-seaweedfs + +services: + warp: + build: + context: .. + dockerfile: Docker-AIO/Dockerfile.PocketBase.SeaweedFS.split + args: + # Passed only when set, so the Dockerfile's pin stays otherwise. + - SEAWEED_VERSION + image: warp-aio-pocketbase-seaweedfs-split:latest + restart: unless-stopped + env_file: .env + environment: + # The gateway is in the container at a fixed address; these override + # whatever .env says about S3. + WARP_PB_S3_ENABLED: "true" + WARP_PB_S3_ENDPOINT: http://127.0.0.1:8333 + WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp} + WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-us-east-1} + WARP_PB_S3_FORCE_PATH_STYLE: "true" + # The homeserver's identity in SeaweedFS, and what it signs with. + WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env} + WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env} + # The admin UI can mint bucket credentials, so it needs a password. + WEED_ADMIN_USER: ${SEAWEED_ADMIN_USER:-admin} + WEED_ADMIN_PASSWORD: ${SEAWEED_ADMIN_PASSWORD:?set SEAWEED_ADMIN_PASSWORD in .env} + WEED_ADMIN_READONLY_USER: ${SEAWEED_ADMIN_READONLY_USER:-} + WEED_ADMIN_READONLY_PASSWORD: ${SEAWEED_ADMIN_READONLY_PASSWORD:-} + volumes: + - warp-data:/data + - seaweed-data:/seaweed/data + - seaweed-admin:/seaweed/admin + ports: + - "${WARP_PORT:-8008}:8008" + - "${WARP_ADMIN_PORT:-8009}:8009" + - "${WARP_WEB_PORT:-8080}:8080" + - "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333" + - "${SEAWEED_ADMIN_BIND:-127.0.0.1}:${SEAWEED_ADMIN_PORT:-23646}:23646" + +volumes: + warp-data: + # Master, volume and filer share it, as `weed server -dir` lays it out. + seaweed-data: + # The admin UI's session key and maintenance state; no object data. + seaweed-admin: diff --git a/Docker-AIO/docker-compose.pocketbase.seaweedfs.yml b/Docker-AIO/docker-compose.pocketbase.seaweedfs.yml new file mode 100644 index 0000000..abe31e3 --- /dev/null +++ b/Docker-AIO/docker-compose.pocketbase.seaweedfs.yml @@ -0,0 +1,62 @@ +# Warp all-in-one on PocketBase with SeaweedFS: the homeserver, the admin +# panel, the web client and SeaweedFS (one `weed server -s3` process) in one +# container built from this checkout (Dockerfile.PocketBase.SeaweedFS). +# +# cd Docker-AIO +# cp .env.example .env # then edit it; set WARP_PB_S3_ACCESS_KEY +# # and WARP_PB_S3_SECRET +# docker compose -f docker-compose.pocketbase.seaweedfs.yml up -d --build +# +# The database stays in /data/pb_data (warp-data); media goes to the bucket +# WARP_PB_S3_BUCKET in SeaweedFS (seaweed-data), whose gateway has exactly +# one identity: the homeserver's. Media stored before S3 was on is not moved +# into the bucket. +# +# Homeserver http://localhost:8008, admin panel http://localhost:8009, web +# client http://localhost:8080. The S3 gateway (8333) is published on +# loopback, for aws-cli and the like; SEAWEED_S3_BIND=0.0.0.0 exposes it. +# +# This and docker-compose.pocketbase.seaweedfs.split.yml share their project +# and volumes: SeaweedFS's data has the same layout either way, so switching +# between them needs no migration. +# +# Create the first administrator once the server is up: +# +# docker compose -f docker-compose.pocketbase.seaweedfs.yml exec warp warp create-account --username admin --admin + +name: warp-aio-pocketbase-seaweedfs + +services: + warp: + build: + context: .. + dockerfile: Docker-AIO/Dockerfile.PocketBase.SeaweedFS + args: + # Passed only when set, so the Dockerfile's pin stays otherwise. + - SEAWEED_VERSION + image: warp-aio-pocketbase-seaweedfs:latest + restart: unless-stopped + env_file: .env + environment: + # The gateway is in the container at a fixed address; these override + # whatever .env says about S3. + WARP_PB_S3_ENABLED: "true" + WARP_PB_S3_ENDPOINT: http://127.0.0.1:8333 + WARP_PB_S3_BUCKET: ${WARP_PB_S3_BUCKET:-warp} + WARP_PB_S3_REGION: ${WARP_PB_S3_REGION:-us-east-1} + WARP_PB_S3_FORCE_PATH_STYLE: "true" + # The gateway's only identity, and what the homeserver signs with. + WARP_PB_S3_ACCESS_KEY: ${WARP_PB_S3_ACCESS_KEY:?set WARP_PB_S3_ACCESS_KEY in .env} + WARP_PB_S3_SECRET: ${WARP_PB_S3_SECRET:?set WARP_PB_S3_SECRET in .env} + volumes: + - warp-data:/data + - seaweed-data:/seaweed/data + ports: + - "${WARP_PORT:-8008}:8008" + - "${WARP_ADMIN_PORT:-8009}:8009" + - "${WARP_WEB_PORT:-8080}:8080" + - "${SEAWEED_S3_BIND:-127.0.0.1}:${SEAWEED_S3_PORT:-8333}:8333" + +volumes: + warp-data: + seaweed-data: diff --git a/Docker-AIO/entrypoint.sh b/Docker-AIO/entrypoint.sh index d0c56ba..ac980f2 100644 --- a/Docker-AIO/entrypoint.sh +++ b/Docker-AIO/entrypoint.sh @@ -1,10 +1,12 @@ #!/bin/sh -# warp-aio: runs the homeserver, the admin panel and the web client (and, -# with WARP_AIO_EMBEDDED_POSTGRES=true, PostgreSQL) in one container. +# warp-aio: runs the homeserver, the admin panel and the web client in one +# container, with whatever the image adds: PostgreSQL, or SeaweedFS as the +# media store. # -# If any of them exits, the others are stopped and the container exits, so -# the restart policy brings the whole set back. SIGTERM stops the clients -# first, then the homeserver, then PostgreSQL. +# If any process exits, the others are stopped and the container exits, so +# the restart policy brings the whole set back. SIGTERM stops them in the +# reverse of the order they started: the clients, the homeserver, then +# SeaweedFS and PostgreSQL underneath it. # # With arguments, runs them instead: `docker run warp-aio warp version`. # @@ -14,6 +16,11 @@ # $PGDATA (/data/postgres); the default # in warp-aio-postgresql, the only image # that has PostgreSQL +# WARP_AIO_SEAWEEDFS=server SeaweedFS as one `weed server -s3` +# WARP_AIO_SEAWEEDFS=split SeaweedFS as master, volume, filer, S3 +# gateway and admin UI, one process each; +# set by the warp-aio-pocketbase-seaweedfs +# images, the only ones with SeaweedFS set -eu @@ -23,36 +30,53 @@ fi log() { echo "warp-aio: $*" >&2; } -pg_pid="" -warp_pid="" -admin_pid="" -web_pid="" +# "name:pid" of every process started, newest first, so shutdown walks it in +# reverse start order. +procs="" -# stop PID...: SIGTERM, then wait for each to exit. -stop() { - for pid in "$@"; do - [ -n "$pid" ] && kill -TERM "$pid" 2>/dev/null || true - done - for pid in "$@"; do - [ -n "$pid" ] && wait "$pid" 2>/dev/null || true - done +# run NAME COMMAND...: start COMMAND in the background and track it. +run() { + name=$1 + shift + "$@" & + procs="$name:$! $procs" } shutdown() { trap '' TERM INT - stop "$web_pid" "$admin_pid" - stop "$warp_pid" - if [ -n "$pg_pid" ]; then + for entry in $procs; do + name=${entry%%:*} + pid=${entry#*:} # SIGINT is PostgreSQL's fast shutdown: it does not wait for - # clients, and the homeserver is gone by now anyway. - kill -INT "$pg_pid" 2>/dev/null || true - wait "$pg_pid" 2>/dev/null || true - fi + # clients, and everything that used it is gone by now. + if [ "$name" = postgres ]; then sig=INT; else sig=TERM; fi + kill -"$sig" "$pid" 2>/dev/null || true + wait "$pid" 2>/dev/null || true + done exit "${1:-0}" } trap 'log "stopping"; shutdown 0' TERM INT -if [ "$WARP_AIO_EMBEDDED_POSTGRES" = true ]; then +# check: stop everything if any process has exited. +check() { + for entry in $procs; do + if ! kill -0 "${entry#*:}" 2>/dev/null; then + log "${entry%%:*} exited; stopping the rest" + shutdown 1 + fi + done +} + +# wait_http NAME URL: wait until URL answers, as long as nothing has died. +wait_http() { + until curl -fsS -o /dev/null "$2" 2>/dev/null; do + check + log "waiting for $1" + sleep 1 + done +} + +if [ "${WARP_AIO_EMBEDDED_POSTGRES:-false}" = true ]; then if ! command -v postgres >/dev/null; then log "WARP_AIO_EMBEDDED_POSTGRES=true, but this image has no PostgreSQL; use warp-aio-postgresql" exit 1 @@ -64,10 +88,9 @@ if [ "$WARP_AIO_EMBEDDED_POSTGRES" = true ]; then # a socket inside this container. initdb -D "$PGDATA" -U warp --auth=trust --encoding=UTF8 --locale=C >/dev/null fi - postgres -D "$PGDATA" -c listen_addresses='' -c unix_socket_directories=/run/postgresql & - pg_pid=$! + run postgres postgres -D "$PGDATA" -c listen_addresses='' -c unix_socket_directories=/run/postgresql until pg_isready -q -h /run/postgresql -U warp; do - kill -0 "$pg_pid" 2>/dev/null || { log "PostgreSQL failed to start"; exit 1; } + check sleep 1 done if [ "$(psql -h /run/postgresql -U warp -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname = 'warp'")" != 1 ]; then @@ -75,34 +98,105 @@ if [ "$WARP_AIO_EMBEDDED_POSTGRES" = true ]; then fi fi +# SeaweedFS keeps everything under /seaweed/data, in the layout `weed server +# -dir` writes, which the split roles share as well: switching between the +# two images needs no migration. Every role advertises 127.0.0.1; the volume +# server serves file content by id with no authentication at all, so only +# the S3 gateway and the admin UI are meant to be published. The volume +# server is on 8081 because 8080 is the web client. +seaweedfs="${WARP_AIO_SEAWEEDFS:-}" +if [ -n "$seaweedfs" ]; then + if ! command -v weed >/dev/null; then + log "WARP_AIO_SEAWEEDFS=$seaweedfs, but this image has no SeaweedFS; use warp-aio-pocketbase-seaweedfs" + exit 1 + fi + # One pair of credentials: the gateway's identity and what the + # homeserver signs its requests with. An empty pair would leave the + # gateway in its allow-anyone default, so refuse instead. + if [ -z "${WARP_PB_S3_ACCESS_KEY:-}" ] || [ -z "${WARP_PB_S3_SECRET:-}" ]; then + log "WARP_PB_S3_ACCESS_KEY and WARP_PB_S3_SECRET are required; refusing to start an unauthenticated S3 gateway" + exit 1 + fi + bucket="${WARP_PB_S3_BUCKET:-warp}" + mkdir -p /seaweed/data +fi + +case "$seaweedfs" in +"") ;; +server) + # Master, volume, filer and gateway as goroutines of one process. With + # no -s3.config, the gateway takes its single identity from + # AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY, given to it alone. + run seaweedfs env AWS_ACCESS_KEY_ID="$WARP_PB_S3_ACCESS_KEY" AWS_SECRET_ACCESS_KEY="$WARP_PB_S3_SECRET" \ + weed server -dir=/seaweed/data -ip=127.0.0.1 -ip.bind=0.0.0.0 \ + -volume.port=8081 -volume.max=0 -master.volumeSizeLimitMB=1024 -s3 -s3.port=8333 + wait_http "SeaweedFS S3 gateway" http://127.0.0.1:8333/healthz + # The homeserver never creates its bucket. Creating one that exists is + # a no-op. + echo "s3.bucket.create -name $bucket" | weed shell -master=127.0.0.1:9333 >/dev/null || true + ;; +split) + # The admin UI can mint credentials for the bucket, and weed serves it + # with no authentication when the password is empty. + if [ -z "${WEED_ADMIN_PASSWORD:-}" ]; then + log "WEED_ADMIN_PASSWORD is empty; refusing to serve an unauthenticated SeaweedFS admin UI" + exit 1 + fi + mkdir -p /seaweed/admin + run seaweedfs-master weed master -ip=127.0.0.1 -ip.bind=127.0.0.1 -port=9333 \ + -mdir=/seaweed/data -volumeSizeLimitMB=1024 -metricsPort=9324 + wait_http "SeaweedFS master" http://127.0.0.1:9333/cluster/status + run seaweedfs-volume weed volume -master=127.0.0.1:9333 -ip=127.0.0.1 -ip.bind=127.0.0.1 \ + -port=8081 -dir=/seaweed/data -max=0 -metricsPort=9325 + wait_http "SeaweedFS volume server" http://127.0.0.1:8081/healthz + run seaweedfs-filer weed filer -master=127.0.0.1:9333 -ip=127.0.0.1 -ip.bind=127.0.0.1 \ + -port=8888 -defaultStoreDir=/seaweed/data -metricsPort=9326 + wait_http "SeaweedFS filer" http://127.0.0.1:8888/healthz + + # The bucket, and the homeserver's identity in the filer's IAM store, + # both re-applied on every boot; that is how a rotated secret lands. + # The store is the one the admin UI writes, so the gateway runs with no + # -config and no AWS_* variables: those are lower-priority sources that + # the first identity added in the admin UI would silently displace. + shell() { weed shell -master=127.0.0.1:9333 -filer=127.0.0.1:8888; } + printf '%s\n' \ + "s3.bucket.create -name $bucket" \ + "s3.configure -user warp -access_key $WARP_PB_S3_ACCESS_KEY -secret_key $WARP_PB_S3_SECRET -actions Admin -apply" | + shell >/dev/null || true + # An empty IAM store would leave the gateway open to anyone. + if ! echo "s3.configure" | shell 2>/dev/null | grep -q "$WARP_PB_S3_ACCESS_KEY"; then + log "the homeserver's S3 identity is not in the filer's IAM store; refusing to start the gateway" + shutdown 1 + fi + + run seaweedfs-s3 weed s3 -filer=127.0.0.1:8888 -ip.bind=0.0.0.0 -port=8333 -metricsPort=9327 + run seaweedfs-admin weed admin -port=23646 -master=127.0.0.1:9333 -dataDir=/seaweed/admin -metricsPort=9328 + wait_http "SeaweedFS S3 gateway" http://127.0.0.1:8333/healthz + ;; +*) + log "WARP_AIO_SEAWEEDFS must be server, split or empty, not $seaweedfs" + exit 1 + ;; +esac + # The signing key is created on first start and kept in the volume: losing # it means other servers no longer recognise this one. if [ ! -f "$WARP_PRIVATE_KEY" ]; then warp generate-keys --output "$WARP_PRIVATE_KEY" fi -warp serve & -warp_pid=$! - -if [ "$WARP_AIO_ADMIN" = true ]; then - warp-admin & - admin_pid=$! +run warp warp serve +if [ "${WARP_AIO_ADMIN:-true}" = true ]; then + run warp-admin warp-admin fi -if [ "$WARP_AIO_WEB" = true ]; then - warp-web & - web_pid=$! +if [ "${WARP_AIO_WEB:-true}" = true ]; then + run warp-web warp-web fi # Busybox sh has no `wait -n`, so check on the processes every second. The # sleep runs in the background so a signal is handled at once. while :; do - for entry in "postgres:$pg_pid" "warp:$warp_pid" "warp-admin:$admin_pid" "warp-web:$web_pid"; do - pid="${entry#*:}" - if [ -n "$pid" ] && ! kill -0 "$pid" 2>/dev/null; then - log "${entry%%:*} exited; stopping the rest" - shutdown 1 - fi - done + check sleep 1 & wait $! || true done