From 89edf33c2c47aae379b40b1c72dc8b268bdc7507 Mon Sep 17 00:00:00 2001 From: Stephan D Date: Mon, 16 Mar 2026 19:50:05 +0100 Subject: [PATCH] [infra] vault + chsettle + aurora for dev --- .woodpecker/gateway_aurora.yml | 99 +++++++++++ .woodpecker/gateway_chsettle.yml | 99 +++++++++++ .woodpecker/gateway_mntx.yml | 6 + .woodpecker/gateway_tgsettle.yml | 6 + .woodpecker/vault.yml | 31 ++++ README.md | 10 +- ci/devserver/.env.runtime | 31 ++++ ci/prod/compose/aurora_gateway.dockerfile | 37 ++++ ci/prod/compose/aurora_gateway.yml | 46 +++++ ci/prod/compose/bff.dockerfile | 3 +- ci/prod/compose/bff.yml | 4 +- ci/prod/compose/callbacks.dockerfile | 3 +- ci/prod/compose/callbacks.yml | 4 +- ci/prod/compose/chain_gateway.dockerfile | 3 +- ci/prod/compose/chain_gateway.yml | 2 +- ci/prod/compose/chsettle_gateway.dockerfile | 37 ++++ ci/prod/compose/chsettle_gateway.yml | 46 +++++ ci/prod/compose/db.dev.yml | 163 ++++++++++++++++++ ci/prod/compose/tron_gateway.dockerfile | 3 +- ci/prod/compose/tron_gateway.yml | 2 +- ci/prod/compose/vault-agent/bff.hcl | 3 +- ci/prod/compose/vault-agent/callbacks.hcl | 2 +- ci/prod/compose/vault-agent/chain-gateway.hcl | 2 +- ci/prod/compose/vault-agent/tron-gateway.hcl | 2 +- ci/prod/compose/vault-server/config.hcl | 12 ++ ci/prod/compose/vault.yml | 37 ++++ ci/prod/compose/vault/agent.dev.hcl | 29 ++++ ci/prod/scripts/deploy/aurora_gateway.sh | 156 +++++++++++++++++ ci/prod/scripts/deploy/bff.sh | 13 +- ci/prod/scripts/deploy/callbacks.sh | 13 +- ci/prod/scripts/deploy/chain_gateway.sh | 13 +- ci/prod/scripts/deploy/chsettle_gateway.sh | 156 +++++++++++++++++ ci/prod/scripts/deploy/db.sh | 10 +- ci/prod/scripts/deploy/tron_gateway.sh | 13 +- ci/prod/scripts/deploy/vault.sh | 140 +++++++++++++++ ci/scripts/aurora/build-image.sh | 94 ++++++++++ ci/scripts/aurora/deploy.sh | 50 ++++++ ci/scripts/bff/build-image.sh | 5 + ci/scripts/bff/deploy.sh | 12 +- ci/scripts/callbacks/build-image.sh | 5 + ci/scripts/callbacks/deploy.sh | 12 +- ci/scripts/chain_gateway/build-image.sh | 5 + ci/scripts/chain_gateway/deploy.sh | 12 +- ci/scripts/chsettle/build-image.sh | 94 ++++++++++ ci/scripts/chsettle/deploy.sh | 50 ++++++ ci/scripts/common/backend_modules.sh | 12 ++ ci/scripts/common/runtime_env.sh | 10 ++ ci/scripts/tron_gateway/build-image.sh | 5 + ci/scripts/tron_gateway/deploy.sh | 12 +- ci/scripts/vault/deploy.sh | 38 ++++ ci/vlt | 16 +- 51 files changed, 1606 insertions(+), 62 deletions(-) create mode 100644 .woodpecker/gateway_aurora.yml create mode 100644 .woodpecker/gateway_chsettle.yml create mode 100644 .woodpecker/vault.yml create mode 100644 ci/prod/compose/aurora_gateway.dockerfile create mode 100644 ci/prod/compose/aurora_gateway.yml create mode 100644 ci/prod/compose/chsettle_gateway.dockerfile create mode 100644 ci/prod/compose/chsettle_gateway.yml create mode 100644 ci/prod/compose/db.dev.yml create mode 100644 ci/prod/compose/vault-server/config.hcl create mode 100644 ci/prod/compose/vault.yml create mode 100644 ci/prod/compose/vault/agent.dev.hcl create mode 100644 ci/prod/scripts/deploy/aurora_gateway.sh create mode 100644 ci/prod/scripts/deploy/chsettle_gateway.sh create mode 100644 ci/prod/scripts/deploy/vault.sh create mode 100644 ci/scripts/aurora/build-image.sh create mode 100644 ci/scripts/aurora/deploy.sh create mode 100644 ci/scripts/chsettle/build-image.sh create mode 100644 ci/scripts/chsettle/deploy.sh create mode 100644 ci/scripts/vault/deploy.sh diff --git a/.woodpecker/gateway_aurora.yml b/.woodpecker/gateway_aurora.yml new file mode 100644 index 00000000..e84f21a3 --- /dev/null +++ b/.woodpecker/gateway_aurora.yml @@ -0,0 +1,99 @@ +matrix: + include: + - AURORA_GATEWAY_IMAGE_PATH: gateway/aurora + AURORA_GATEWAY_DOCKERFILE: ci/prod/compose/aurora_gateway.dockerfile + AURORA_GATEWAY_MONGO_SECRET_PATH: sendico/db + AURORA_GATEWAY_NATS_SECRET_PATH: sendico/nats + +labels: + platform: linux/amd64 + +when: + - event: push + branch: main + path: + include: + - api/gateway/aurora/** + - api/gateway/common/** + - api/proto/** + - api/pkg/** + - ci/** + - .woodpecker/gateway_aurora.yml + +steps: + - name: version + image: alpine:latest + commands: + - set -euo pipefail 2>/dev/null || set -eu + - apk add --no-cache git + - GIT_REV="$(git rev-parse --short HEAD)" + - BUILD_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + - APP_V="$(cat version)" + - BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + - BUILD_USER="${WOODPECKER_MACHINE:-woodpecker}" + - printf "GIT_REV=%s\nBUILD_BRANCH=%s\nAPP_V=%s\nBUILD_DATE=%s\nBUILD_USER=%s\n" \ + "$GIT_REV" "$BUILD_BRANCH" "$APP_V" "$BUILD_DATE" "$BUILD_USER" | tee .env.version + + - name: proto + image: golang:alpine + depends_on: [ version ] + commands: + - set -eu + - apk add --no-cache bash git build-base protoc protobuf-dev + - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + - export PATH="$(go env GOPATH)/bin:$PATH" + - bash ci/scripts/proto/generate.sh + + - name: backend-lint + image: golang:alpine + depends_on: [ proto ] + commands: + - set -eu + - apk add --no-cache bash git build-base + - CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + - export PATH="$(go env GOPATH)/bin:$PATH" + - sh ci/scripts/common/run_backend_lint.sh gateway_aurora + + - name: backend-tests + image: golang:alpine + depends_on: [ proto ] + commands: + - set -eu + - apk add --no-cache bash git build-base + - sh ci/scripts/common/run_backend_tests.sh gateway_aurora + + - name: secrets + image: alpine:latest + depends_on: [ version ] + environment: + VAULT_ADDR: { from_secret: VAULT_ADDR } + VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } + VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } + commands: + - set -euo pipefail + - apk add --no-cache bash coreutils openssh-keygen curl sed python3 + - sh ci/scripts/common/fetch_deploy_ssh_key.sh secrets/SSH_KEY + - ./ci/vlt kv_get kv registry user > secrets/REGISTRY_USER + - ./ci/vlt kv_get kv registry password > secrets/REGISTRY_PASSWORD + + - name: build-image + image: gcr.io/kaniko-project/executor:debug + depends_on: [ backend-lint, backend-tests, secrets ] + commands: + - '[ "$(uname -m)" = "x86_64" ] || { echo "image build requires an amd64 runner"; exit 1; }' + - sh ci/scripts/aurora/build-image.sh + + - name: deploy + image: alpine:latest + depends_on: [ secrets, build-image ] + environment: + VAULT_ADDR: { from_secret: VAULT_ADDR } + VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } + VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } + commands: + - set -euo pipefail + - apk add --no-cache bash openssh-client rsync coreutils curl sed python3 + - mkdir -p /root/.ssh + - install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa + - sh ci/scripts/aurora/deploy.sh diff --git a/.woodpecker/gateway_chsettle.yml b/.woodpecker/gateway_chsettle.yml new file mode 100644 index 00000000..5e62f04f --- /dev/null +++ b/.woodpecker/gateway_chsettle.yml @@ -0,0 +1,99 @@ +matrix: + include: + - CHSETTLE_GATEWAY_IMAGE_PATH: gateway/chsettle + CHSETTLE_GATEWAY_DOCKERFILE: ci/prod/compose/chsettle_gateway.dockerfile + CHSETTLE_GATEWAY_MONGO_SECRET_PATH: sendico/db + CHSETTLE_GATEWAY_NATS_SECRET_PATH: sendico/nats + +labels: + platform: linux/amd64 + +when: + - event: push + branch: main + path: + include: + - api/gateway/chsettle/** + - api/gateway/common/** + - api/proto/** + - api/pkg/** + - ci/** + - .woodpecker/gateway_chsettle.yml + +steps: + - name: version + image: alpine:latest + commands: + - set -euo pipefail 2>/dev/null || set -eu + - apk add --no-cache git + - GIT_REV="$(git rev-parse --short HEAD)" + - BUILD_BRANCH="$(git rev-parse --abbrev-ref HEAD)" + - APP_V="$(cat version)" + - BUILD_DATE="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + - BUILD_USER="${WOODPECKER_MACHINE:-woodpecker}" + - printf "GIT_REV=%s\nBUILD_BRANCH=%s\nAPP_V=%s\nBUILD_DATE=%s\nBUILD_USER=%s\n" \ + "$GIT_REV" "$BUILD_BRANCH" "$APP_V" "$BUILD_DATE" "$BUILD_USER" | tee .env.version + + - name: proto + image: golang:alpine + depends_on: [ version ] + commands: + - set -eu + - apk add --no-cache bash git build-base protoc protobuf-dev + - go install google.golang.org/protobuf/cmd/protoc-gen-go@latest + - go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest + - export PATH="$(go env GOPATH)/bin:$PATH" + - bash ci/scripts/proto/generate.sh + + - name: backend-lint + image: golang:alpine + depends_on: [ proto ] + commands: + - set -eu + - apk add --no-cache bash git build-base + - CGO_ENABLED=0 go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest + - export PATH="$(go env GOPATH)/bin:$PATH" + - sh ci/scripts/common/run_backend_lint.sh gateway_chsettle + + - name: backend-tests + image: golang:alpine + depends_on: [ proto ] + commands: + - set -eu + - apk add --no-cache bash git build-base + - sh ci/scripts/common/run_backend_tests.sh gateway_chsettle + + - name: secrets + image: alpine:latest + depends_on: [ version ] + environment: + VAULT_ADDR: { from_secret: VAULT_ADDR } + VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } + VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } + commands: + - set -euo pipefail + - apk add --no-cache bash coreutils openssh-keygen curl sed python3 + - sh ci/scripts/common/fetch_deploy_ssh_key.sh secrets/SSH_KEY + - ./ci/vlt kv_get kv registry user > secrets/REGISTRY_USER + - ./ci/vlt kv_get kv registry password > secrets/REGISTRY_PASSWORD + + - name: build-image + image: gcr.io/kaniko-project/executor:debug + depends_on: [ backend-lint, backend-tests, secrets ] + commands: + - '[ "$(uname -m)" = "x86_64" ] || { echo "image build requires an amd64 runner"; exit 1; }' + - sh ci/scripts/chsettle/build-image.sh + + - name: deploy + image: alpine:latest + depends_on: [ secrets, build-image ] + environment: + VAULT_ADDR: { from_secret: VAULT_ADDR } + VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } + VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } + commands: + - set -euo pipefail + - apk add --no-cache bash openssh-client rsync coreutils curl sed python3 + - mkdir -p /root/.ssh + - install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa + - sh ci/scripts/chsettle/deploy.sh diff --git a/.woodpecker/gateway_mntx.yml b/.woodpecker/gateway_mntx.yml index 3bb0eef4..a3595c94 100644 --- a/.woodpecker/gateway_mntx.yml +++ b/.woodpecker/gateway_mntx.yml @@ -69,6 +69,8 @@ steps: - name: secrets image: alpine:latest depends_on: [ version ] + when: + - event: tag environment: VAULT_ADDR: { from_secret: VAULT_ADDR } VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } @@ -83,6 +85,8 @@ steps: - name: build-image image: gcr.io/kaniko-project/executor:debug depends_on: [ backend-lint, backend-tests, secrets ] + when: + - event: tag commands: - '[ "$(uname -m)" = "x86_64" ] || { echo "image build requires an amd64 runner"; exit 1; }' - sh ci/scripts/mntx/build-image.sh @@ -90,6 +94,8 @@ steps: - name: deploy image: alpine:latest depends_on: [ secrets, build-image ] + when: + - event: tag environment: VAULT_ADDR: { from_secret: VAULT_ADDR } VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } diff --git a/.woodpecker/gateway_tgsettle.yml b/.woodpecker/gateway_tgsettle.yml index 4093bb51..96d23e5e 100644 --- a/.woodpecker/gateway_tgsettle.yml +++ b/.woodpecker/gateway_tgsettle.yml @@ -67,6 +67,8 @@ steps: - name: secrets image: alpine:latest depends_on: [ version ] + when: + - event: tag environment: VAULT_ADDR: { from_secret: VAULT_ADDR } VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } @@ -81,6 +83,8 @@ steps: - name: build-image image: gcr.io/kaniko-project/executor:debug depends_on: [ backend-lint, backend-tests, secrets ] + when: + - event: tag commands: - '[ "$(uname -m)" = "x86_64" ] || { echo "image build requires an amd64 runner"; exit 1; }' - sh ci/scripts/tgsettle/build-image.sh @@ -88,6 +92,8 @@ steps: - name: deploy image: alpine:latest depends_on: [ secrets, build-image ] + when: + - event: tag environment: VAULT_ADDR: { from_secret: VAULT_ADDR } VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } diff --git a/.woodpecker/vault.yml b/.woodpecker/vault.yml new file mode 100644 index 00000000..ce9a5540 --- /dev/null +++ b/.woodpecker/vault.yml @@ -0,0 +1,31 @@ +when: + - event: push + branch: main + path: + exclude: ['**'] + ignore_message: '[infra]' + +steps: + - name: secrets + image: alpine:latest + environment: + VAULT_ADDR: { from_secret: VAULT_ADDR } + VAULT_ROLE_ID: { from_secret: VAULT_APP_ROLE } + VAULT_SECRET_ID: { from_secret: VAULT_SECRET_ID } + commands: + - set -euo pipefail + - apk add --no-cache bash coreutils openssh-keygen curl sed python3 + - sh ci/scripts/common/fetch_deploy_ssh_key.sh secrets/SSH_KEY + + - name: deploy + image: alpine:latest + depends_on: [ secrets ] + commands: + - set -euo pipefail + - apk add --no-cache bash openssh-client rsync coreutils curl sed python3 + - mkdir -p /root/.ssh + - install -m 600 secrets/SSH_KEY /root/.ssh/id_rsa + - . ./ci/scripts/common/runtime_env.sh + - load_runtime_env_bundle "$(resolve_runtime_env_name)" + - bash ci/prod/scripts/bootstrap/network.sh + - sh ci/scripts/vault/deploy.sh diff --git a/README.md b/README.md index 8b92d3ef..a423744b 100644 --- a/README.md +++ b/README.md @@ -114,7 +114,15 @@ make test-frontend # Run Flutter tests only - Pushes to `main` build and deploy changed application services to the dev server. - Tags matching `v*` trigger a full production rebuild and deployment from that exact tagged revision. -- Infrastructure workflows for `db` and `nats` remain separately controlled. +- Infrastructure workflows for `db`, `nats`, and the dev `vault` remain separately controlled via `[infra]`. + +First-time dev bootstrap: + +```bash +# merge a PR whose commit message includes [infra] +# this deploys dev db, nats, and dev vault +# after infra is green, merge normal app changes to main +``` Recommended release preparation: diff --git a/ci/devserver/.env.runtime b/ci/devserver/.env.runtime index ee869578..8ccb7bef 100644 --- a/ci/devserver/.env.runtime +++ b/ci/devserver/.env.runtime @@ -8,3 +8,34 @@ WS_PROTOCOL=wss SSH_HOST=178.57.67.136 SSH_USER=cloud DEPLOY_SSH_KEY_PATH=ops/deploy/dev_ssh_key +DB_COMPOSE_FILE=db.dev.yml +APP_VAULT_ADDR=http://dev-vault:8200 +VAULT_DIR=vault +VAULT_COMPOSE_PROJECT=sendico-vault +VAULT_HTTP_PORT=8200 + +# Aurora gateway replaces Monetix on the dev host. +AURORA_GATEWAY_DIR=aurora_gateway +AURORA_GATEWAY_COMPOSE_PROJECT=sendico-aurora-gateway +AURORA_GATEWAY_SERVICE_NAME=sendico_aurora_gateway +AURORA_GATEWAY_GRPC_PORT=50075 +AURORA_GATEWAY_METRICS_PORT=9405 +AURORA_GATEWAY_HTTP_PORT=8084 +AURORA_GATEWAY_MONGO_HOST=sendico_db1 +AURORA_GATEWAY_MONGO_PORT=27017 +AURORA_GATEWAY_MONGO_DATABASE=aurora_gateway +AURORA_GATEWAY_MONGO_AUTH_SOURCE=admin +AURORA_GATEWAY_MONGO_REPLICA_SET=sendico-rs + +# ChimeraSettle replaces TGSettle on the dev host. +CHSETTLE_GATEWAY_DIR=chsettle_gateway +CHSETTLE_GATEWAY_COMPOSE_PROJECT=sendico-chsettle-gateway +CHSETTLE_GATEWAY_SERVICE_NAME=sendico_chsettle_gateway +CHSETTLE_GATEWAY_GRPC_PORT=50080 +CHSETTLE_GATEWAY_METRICS_PORT=9406 +CHSETTLE_GATEWAY_CHAT_ID= +CHSETTLE_GATEWAY_MONGO_HOST=sendico_db1 +CHSETTLE_GATEWAY_MONGO_PORT=27017 +CHSETTLE_GATEWAY_MONGO_DATABASE=chsettle_gateway +CHSETTLE_GATEWAY_MONGO_AUTH_SOURCE=admin +CHSETTLE_GATEWAY_MONGO_REPLICA_SET=sendico-rs diff --git a/ci/prod/compose/aurora_gateway.dockerfile b/ci/prod/compose/aurora_gateway.dockerfile new file mode 100644 index 00000000..40151a7d --- /dev/null +++ b/ci/prod/compose/aurora_gateway.dockerfile @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1.7 + +ARG TARGETOS=linux +ARG TARGETARCH=amd64 + +FROM golang:alpine AS build +ARG APP_VERSION=dev +ARG GIT_REV=unknown +ARG BUILD_BRANCH=unknown +ARG BUILD_DATE=unknown +ARG BUILD_USER=ci +ENV GO111MODULE=on +ENV PATH="/go/bin:${PATH}" +WORKDIR /src +COPY . . +RUN apk add --no-cache git build-base +WORKDIR /src/api/gateway/aurora +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -trimpath -ldflags "\ + -s -w \ + -X github.com/tech/sendico/gateway/aurora/internal/appversion.Version=${APP_VERSION} \ + -X github.com/tech/sendico/gateway/aurora/internal/appversion.Revision=${GIT_REV} \ + -X github.com/tech/sendico/gateway/aurora/internal/appversion.Branch=${BUILD_BRANCH} \ + -X github.com/tech/sendico/gateway/aurora/internal/appversion.BuildUser=${BUILD_USER} \ + -X github.com/tech/sendico/gateway/aurora/internal/appversion.BuildDate=${BUILD_DATE}" \ + -o /out/aurora-gateway . + +FROM alpine:latest AS runtime +RUN apk add --no-cache ca-certificates tzdata wget +WORKDIR /app +COPY api/gateway/aurora/config.yml /app/config.yml +COPY --from=build /out/aurora-gateway /app/aurora-gateway +EXPOSE 50075 9404 8084 +ENTRYPOINT ["/app/aurora-gateway"] +CMD ["--config.file", "/app/config.yml"] diff --git a/ci/prod/compose/aurora_gateway.yml b/ci/prod/compose/aurora_gateway.yml new file mode 100644 index 00000000..729e7829 --- /dev/null +++ b/ci/prod/compose/aurora_gateway.yml @@ -0,0 +1,46 @@ +# Compose v2 - Aurora Gateway + +x-common-env: &common-env + env_file: + - ../env/.env.runtime + - ../env/.env.version + +networks: + sendico-net: + external: true + name: sendico-net + +services: + sendico_aurora_gateway: + <<: *common-env + container_name: sendico-aurora-gateway + platform: linux/amd64 + restart: unless-stopped + image: ${REGISTRY_URL}/gateway/aurora:${IMAGE_TAG:-${APP_V}} + pull_policy: always + environment: + AURORA_GATEWAY_MONGO_HOST: ${AURORA_GATEWAY_MONGO_HOST} + AURORA_GATEWAY_MONGO_PORT: ${AURORA_GATEWAY_MONGO_PORT} + AURORA_GATEWAY_MONGO_DATABASE: ${AURORA_GATEWAY_MONGO_DATABASE} + AURORA_GATEWAY_MONGO_USER: ${AURORA_GATEWAY_MONGO_USER} + AURORA_GATEWAY_MONGO_PASSWORD: ${AURORA_GATEWAY_MONGO_PASSWORD} + AURORA_GATEWAY_MONGO_AUTH_SOURCE: ${AURORA_GATEWAY_MONGO_AUTH_SOURCE} + AURORA_GATEWAY_MONGO_REPLICA_SET: ${AURORA_GATEWAY_MONGO_REPLICA_SET} + NATS_URL: ${NATS_URL} + NATS_HOST: ${NATS_HOST} + NATS_PORT: ${NATS_PORT} + NATS_USER: ${NATS_USER} + NATS_PASSWORD: ${NATS_PASSWORD} + command: ["--config.file", "/app/config.yml"] + ports: + - "0.0.0.0:${AURORA_GATEWAY_GRPC_PORT}:50075" + - "0.0.0.0:${AURORA_GATEWAY_METRICS_PORT}:9404" + - "0.0.0.0:${AURORA_GATEWAY_HTTP_PORT}:8084" + healthcheck: + test: ["CMD-SHELL","wget -qO- http://localhost:9404/health | grep -q '\"status\":\"ok\"'"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + networks: + - sendico-net diff --git a/ci/prod/compose/bff.dockerfile b/ci/prod/compose/bff.dockerfile index fd6c56c4..f78ed1c3 100644 --- a/ci/prod/compose/bff.dockerfile +++ b/ci/prod/compose/bff.dockerfile @@ -28,9 +28,10 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /out/bff . FROM alpine:latest AS runtime +ARG APP_CONFIG_PATH=api/edge/bff/config.yml RUN apk add --no-cache ca-certificates tzdata wget WORKDIR /app -COPY api/edge/bff/config.yml /app/config.yml +COPY ${APP_CONFIG_PATH} /app/config.yml COPY api/edge/bff/assets /app/assets COPY api/edge/bff/env /app/env COPY api/pkg/auth/internal/casbin/models/auth.conf /app/env/permissions_model.conf diff --git a/ci/prod/compose/bff.yml b/ci/prod/compose/bff.yml index b6e5a332..85862589 100644 --- a/ci/prod/compose/bff.yml +++ b/ci/prod/compose/bff.yml @@ -60,7 +60,7 @@ services: PERMISSION_COLLECTION: ${PERMISSION_COLLECTION} PERMISSION_TIMEOUT: ${PERMISSION_TIMEOUT} PERMISSION_IS_FILTERED: ${PERMISSION_IS_FILTERED} - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} VAULT_TOKEN_FILE: /run/vault/token ports: - "0.0.0.0:${BFF_HTTP_PORT}:8081" @@ -86,7 +86,7 @@ services: pull_policy: always cap_add: ["IPC_LOCK"] environment: - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} BFF_VAULT_ROLE_ID: ${BFF_VAULT_ROLE_ID} BFF_VAULT_SECRET_ID: ${BFF_VAULT_SECRET_ID} command: > diff --git a/ci/prod/compose/callbacks.dockerfile b/ci/prod/compose/callbacks.dockerfile index 0a7079cf..6903791a 100644 --- a/ci/prod/compose/callbacks.dockerfile +++ b/ci/prod/compose/callbacks.dockerfile @@ -28,9 +28,10 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /out/callbacks . FROM alpine:latest AS runtime +ARG APP_CONFIG_PATH=api/edge/callbacks/config.yml RUN apk add --no-cache ca-certificates tzdata wget WORKDIR /app -COPY api/edge/callbacks/config.yml /app/config.yml +COPY ${APP_CONFIG_PATH} /app/config.yml COPY api/edge/callbacks/entrypoint.sh /app/entrypoint.sh COPY --from=build /out/callbacks /app/callbacks RUN chmod +x /app/entrypoint.sh diff --git a/ci/prod/compose/callbacks.yml b/ci/prod/compose/callbacks.yml index da596040..0ae50024 100644 --- a/ci/prod/compose/callbacks.yml +++ b/ci/prod/compose/callbacks.yml @@ -40,7 +40,7 @@ services: NATS_USER: ${NATS_USER} NATS_PASSWORD: ${NATS_PASSWORD} CALLBACKS_METRICS_PORT: ${CALLBACKS_METRICS_PORT} - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} VAULT_TOKEN_FILE: /run/vault/token command: ["--config.file", "/app/config.yml"] ports: @@ -67,7 +67,7 @@ services: pull_policy: always cap_add: ["IPC_LOCK"] environment: - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} CALLBACKS_VAULT_ROLE_ID: ${CALLBACKS_VAULT_ROLE_ID} CALLBACKS_VAULT_SECRET_ID: ${CALLBACKS_VAULT_SECRET_ID} command: > diff --git a/ci/prod/compose/chain_gateway.dockerfile b/ci/prod/compose/chain_gateway.dockerfile index 3455fdec..91dad251 100644 --- a/ci/prod/compose/chain_gateway.dockerfile +++ b/ci/prod/compose/chain_gateway.dockerfile @@ -28,9 +28,10 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /out/chain-gateway . FROM alpine:latest AS runtime +ARG APP_CONFIG_PATH=api/gateway/chain/config.yml RUN apk add --no-cache ca-certificates tzdata wget WORKDIR /app -COPY api/gateway/chain/config.yml /app/config.yml +COPY ${APP_CONFIG_PATH} /app/config.yml COPY api/gateway/chain/env /app/env COPY api/gateway/chain/entrypoint.sh /app/entrypoint.sh COPY --from=build /out/chain-gateway /app/chain-gateway diff --git a/ci/prod/compose/chain_gateway.yml b/ci/prod/compose/chain_gateway.yml index 2f69e95f..087dd18c 100644 --- a/ci/prod/compose/chain_gateway.yml +++ b/ci/prod/compose/chain_gateway.yml @@ -75,7 +75,7 @@ services: pull_policy: always cap_add: ["IPC_LOCK"] environment: - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} CHAIN_GATEWAY_VAULT_ROLE_ID: ${CHAIN_GATEWAY_VAULT_ROLE_ID} CHAIN_GATEWAY_VAULT_SECRET_ID: ${CHAIN_GATEWAY_VAULT_SECRET_ID} command: > diff --git a/ci/prod/compose/chsettle_gateway.dockerfile b/ci/prod/compose/chsettle_gateway.dockerfile new file mode 100644 index 00000000..45872e4a --- /dev/null +++ b/ci/prod/compose/chsettle_gateway.dockerfile @@ -0,0 +1,37 @@ +# syntax=docker/dockerfile:1.7 + +ARG TARGETOS=linux +ARG TARGETARCH=amd64 + +FROM golang:alpine AS build +ARG APP_VERSION=dev +ARG GIT_REV=unknown +ARG BUILD_BRANCH=unknown +ARG BUILD_DATE=unknown +ARG BUILD_USER=ci +ENV GO111MODULE=on +ENV PATH="/go/bin:${PATH}" +WORKDIR /src +COPY . . +RUN apk add --no-cache git build-base +WORKDIR /src/api/gateway/chsettle +RUN --mount=type=cache,target=/root/.cache/go-build \ + --mount=type=cache,target=/go/pkg/mod \ + CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \ + go build -trimpath -ldflags "\ + -s -w \ + -X github.com/tech/sendico/gateway/chsettle/internal/appversion.Version=${APP_VERSION} \ + -X github.com/tech/sendico/gateway/chsettle/internal/appversion.Revision=${GIT_REV} \ + -X github.com/tech/sendico/gateway/chsettle/internal/appversion.Branch=${BUILD_BRANCH} \ + -X github.com/tech/sendico/gateway/chsettle/internal/appversion.BuildUser=${BUILD_USER} \ + -X github.com/tech/sendico/gateway/chsettle/internal/appversion.BuildDate=${BUILD_DATE}" \ + -o /out/chsettle-gateway . + +FROM alpine:latest AS runtime +RUN apk add --no-cache ca-certificates tzdata wget +WORKDIR /app +COPY api/gateway/chsettle/config.yml /app/config.yml +COPY --from=build /out/chsettle-gateway /app/chsettle-gateway +EXPOSE 50080 9406 +ENTRYPOINT ["/app/chsettle-gateway"] +CMD ["--config.file", "/app/config.yml"] diff --git a/ci/prod/compose/chsettle_gateway.yml b/ci/prod/compose/chsettle_gateway.yml new file mode 100644 index 00000000..36b357d8 --- /dev/null +++ b/ci/prod/compose/chsettle_gateway.yml @@ -0,0 +1,46 @@ +# Compose v2 - ChimeraSettle Gateway + +x-common-env: &common-env + env_file: + - ../env/.env.runtime + - ../env/.env.version + +networks: + sendico-net: + external: true + name: sendico-net + +services: + sendico_chsettle_gateway: + <<: *common-env + container_name: sendico-chsettle-gateway + platform: linux/amd64 + restart: unless-stopped + image: ${REGISTRY_URL}/gateway/chsettle:${IMAGE_TAG:-${APP_V}} + pull_policy: always + environment: + CHSETTLE_GATEWAY_MONGO_HOST: ${CHSETTLE_GATEWAY_MONGO_HOST} + CHSETTLE_GATEWAY_MONGO_PORT: ${CHSETTLE_GATEWAY_MONGO_PORT} + CHSETTLE_GATEWAY_MONGO_DATABASE: ${CHSETTLE_GATEWAY_MONGO_DATABASE} + CHSETTLE_GATEWAY_MONGO_USER: ${CHSETTLE_GATEWAY_MONGO_USER} + CHSETTLE_GATEWAY_MONGO_PASSWORD: ${CHSETTLE_GATEWAY_MONGO_PASSWORD} + CHSETTLE_GATEWAY_MONGO_AUTH_SOURCE: ${CHSETTLE_GATEWAY_MONGO_AUTH_SOURCE} + CHSETTLE_GATEWAY_MONGO_REPLICA_SET: ${CHSETTLE_GATEWAY_MONGO_REPLICA_SET} + NATS_URL: ${NATS_URL} + NATS_HOST: ${NATS_HOST} + NATS_PORT: ${NATS_PORT} + NATS_USER: ${NATS_USER} + NATS_PASSWORD: ${NATS_PASSWORD} + CHSETTLE_GATEWAY_CHAT_ID: ${CHSETTLE_GATEWAY_CHAT_ID} + command: ["--config.file", "/app/config.yml"] + ports: + - "0.0.0.0:${CHSETTLE_GATEWAY_GRPC_PORT}:50080" + - "0.0.0.0:${CHSETTLE_GATEWAY_METRICS_PORT}:9406" + healthcheck: + test: ["CMD-SHELL","wget -qO- http://localhost:9406/health | grep -q '\"status\":\"ok\"'"] + interval: 30s + timeout: 10s + retries: 3 + start_period: 60s + networks: + - sendico-net diff --git a/ci/prod/compose/db.dev.yml b/ci/prod/compose/db.dev.yml new file mode 100644 index 00000000..a9f05308 --- /dev/null +++ b/ci/prod/compose/db.dev.yml @@ -0,0 +1,163 @@ +# Compose v2 - Dev DB stack without PBM + +x-common-env: &common-env + env_file: + - ../env/.env.runtime + +volumes: + mongo1_data: {} + mongo2_data: {} + mongo3_data: {} + # In-memory store for secrets/material rendered by Vault Agent (no host persistence) + vault_secrets: + driver: local + driver_opts: + type: tmpfs + device: tmpfs + o: size=32m,uid=999,gid=999,mode=0750 + +networks: + sendico-net: + external: true + name: sendico-net + +services: + vault-agent-sendico: + <<: *common-env + image: hashicorp/vault:latest + container_name: vault-agent-sendico + restart: unless-stopped + cap_add: ["IPC_LOCK"] + environment: + VAULT_ADDR: ${VAULT_ADDR} + VAULT_ROLE_ID: ${VAULT_ROLE_ID} + VAULT_SECRET_ID: ${VAULT_SECRET_ID} + volumes: + - ./vault/agent.dev.hcl:/etc/vault/agent.hcl:ro + - ./vault/templates:/etc/vault/templates:ro + - vault_secrets:/vault/secrets:rw + command: > + sh -lc 'set -euo pipefail; umask 077; + : "$${VAULT_ADDR:?}"; : "$${VAULT_ROLE_ID:?}"; : "$${VAULT_SECRET_ID:?}"; + printf "%s" "$${VAULT_ROLE_ID}" > /vault/secrets/role_id; + printf "%s" "$${VAULT_SECRET_ID}" > /vault/secrets/secret_id; + unset VAULT_ROLE_ID VAULT_SECRET_ID; + exec vault agent -config=/etc/vault/agent.hcl' + healthcheck: + test: ["CMD-SHELL","test -s /vault/secrets/MONGO_INITDB_ROOT_USERNAME -a -s /vault/secrets/MONGO_INITDB_ROOT_PASSWORD -a -s /vault/secrets/mongo.kf"] + interval: 5s + timeout: 3s + retries: 30 + start_period: 5s + networks: + - sendico-net + + sendico_db1: + <<: *common-env + image: docker.io/library/mongo:latest + container_name: sendico_db1 + restart: unless-stopped + depends_on: + vault-agent-sendico: + condition: service_healthy + entrypoint: ["/usr/local/bin/mongo-entrypoint-wrapper.sh"] + command: > + mongod --replSet ${MONGO_REPLICA_SET} --bind_ip_all --auth + --keyFile /vault/secrets/mongo.kf --port ${MONGO_PORT} + volumes: + - mongo1_data:/data/db + - vault_secrets:/vault/secrets:ro + - ./ops/mongo-entrypoint.sh:/usr/local/bin/mongo-entrypoint-wrapper.sh:ro + healthcheck: + test: ["CMD-SHELL","mongosh --quiet --host localhost --port ${MONGO_PORT} --eval 'db.runCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + ports: [ "0.0.0.0:${MONGO_PORT}:${MONGO_PORT}" ] + networks: + - sendico-net + + sendico_db2: + <<: *common-env + image: docker.io/library/mongo:latest + container_name: sendico_db2 + restart: unless-stopped + depends_on: + vault-agent-sendico: + condition: service_healthy + entrypoint: ["/usr/local/bin/mongo-entrypoint-wrapper.sh"] + command: > + mongod --replSet ${MONGO_REPLICA_SET} --bind_ip_all --auth + --keyFile /vault/secrets/mongo.kf --port ${MONGO_PORT} + volumes: + - mongo2_data:/data/db + - vault_secrets:/vault/secrets:ro + - ./ops/mongo-entrypoint.sh:/usr/local/bin/mongo-entrypoint-wrapper.sh:ro + healthcheck: + test: ["CMD-SHELL","mongosh --quiet --host localhost --port ${MONGO_PORT} --eval 'db.runCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + networks: + - sendico-net + + sendico_db3: + <<: *common-env + image: docker.io/library/mongo:latest + container_name: sendico_db3 + restart: unless-stopped + depends_on: + vault-agent-sendico: + condition: service_healthy + entrypoint: ["/usr/local/bin/mongo-entrypoint-wrapper.sh"] + command: > + mongod --replSet ${MONGO_REPLICA_SET} --bind_ip_all --auth + --keyFile /vault/secrets/mongo.kf --port ${MONGO_PORT} + volumes: + - mongo3_data:/data/db + - vault_secrets:/vault/secrets:ro + - ./ops/mongo-entrypoint.sh:/usr/local/bin/mongo-entrypoint-wrapper.sh:ro + healthcheck: + test: ["CMD-SHELL","mongosh --quiet --host localhost --port ${MONGO_PORT} --eval 'db.runCommand({ ping: 1 }).ok' || exit 1"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + networks: + - sendico-net + + mongo_setup: + <<: *common-env + image: docker.io/library/mongo:latest + depends_on: + sendico_db1: { condition: service_healthy } + sendico_db2: { condition: service_healthy } + sendico_db3: { condition: service_healthy } + volumes: + - vault_secrets:/vault/secrets:ro + entrypoint: | + bash -c ' + set -euo pipefail + u=$(cat /vault/secrets/MONGO_INITDB_ROOT_USERNAME) + p=$(cat /vault/secrets/MONGO_INITDB_ROOT_PASSWORD) + until mongosh --quiet --host sendico_db1 --port ${MONGO_PORT} --eval "db.adminCommand({ ping: 1 })"; do + echo "waiting for MongoDB…"; sleep 2; + done + mongosh --host sendico_db1 --port ${MONGO_PORT} -u "$$u" -p "$$p" --authenticationDatabase admin <<'EOJS' + try { rs.status() } catch (e) { + rs.initiate({ + _id: "${MONGO_REPLICA_SET}", + members: [ + { _id: 0, host: "sendico_db1:${MONGO_PORT}", priority: 2 }, + { _id: 1, host: "sendico_db2:${MONGO_PORT}", priority: 1 }, + { _id: 2, host: "sendico_db3:${MONGO_PORT}", priority: 1 } + ] + }) + } + EOJS + ' + restart: "no" + networks: + - sendico-net diff --git a/ci/prod/compose/tron_gateway.dockerfile b/ci/prod/compose/tron_gateway.dockerfile index ce0ad1a2..f564536d 100644 --- a/ci/prod/compose/tron_gateway.dockerfile +++ b/ci/prod/compose/tron_gateway.dockerfile @@ -28,9 +28,10 @@ RUN --mount=type=cache,target=/root/.cache/go-build \ -o /out/tron-gateway . FROM alpine:latest AS runtime +ARG APP_CONFIG_PATH=api/gateway/tron/config.yml RUN apk add --no-cache ca-certificates tzdata wget WORKDIR /app -COPY api/gateway/tron/config.yml /app/config.yml +COPY ${APP_CONFIG_PATH} /app/config.yml COPY api/gateway/tron/env /app/env COPY api/gateway/tron/entrypoint.sh /app/entrypoint.sh COPY --from=build /out/tron-gateway /app/tron-gateway diff --git a/ci/prod/compose/tron_gateway.yml b/ci/prod/compose/tron_gateway.yml index 1de76192..9153bd59 100644 --- a/ci/prod/compose/tron_gateway.yml +++ b/ci/prod/compose/tron_gateway.yml @@ -79,7 +79,7 @@ services: pull_policy: always cap_add: ["IPC_LOCK"] environment: - VAULT_ADDR: ${VAULT_ADDR} + VAULT_ADDR: ${APP_VAULT_ADDR:-${VAULT_ADDR}} TRON_GATEWAY_VAULT_ROLE_ID: ${TRON_GATEWAY_VAULT_ROLE_ID} TRON_GATEWAY_VAULT_SECRET_ID: ${TRON_GATEWAY_VAULT_SECRET_ID} command: > diff --git a/ci/prod/compose/vault-agent/bff.hcl b/ci/prod/compose/vault-agent/bff.hcl index 4dc97ad3..996e74dd 100644 --- a/ci/prod/compose/vault-agent/bff.hcl +++ b/ci/prod/compose/vault-agent/bff.hcl @@ -1,5 +1,5 @@ vault { - address = "https://vault.sendico.io" + address = "{{ env `VAULT_ADDR` }}" } auto_auth { @@ -18,4 +18,3 @@ auto_auth { } } } - diff --git a/ci/prod/compose/vault-agent/callbacks.hcl b/ci/prod/compose/vault-agent/callbacks.hcl index 3dbfc804..996e74dd 100644 --- a/ci/prod/compose/vault-agent/callbacks.hcl +++ b/ci/prod/compose/vault-agent/callbacks.hcl @@ -1,5 +1,5 @@ vault { - address = "https://vault.sendico.io" + address = "{{ env `VAULT_ADDR` }}" } auto_auth { diff --git a/ci/prod/compose/vault-agent/chain-gateway.hcl b/ci/prod/compose/vault-agent/chain-gateway.hcl index 3dbfc804..996e74dd 100644 --- a/ci/prod/compose/vault-agent/chain-gateway.hcl +++ b/ci/prod/compose/vault-agent/chain-gateway.hcl @@ -1,5 +1,5 @@ vault { - address = "https://vault.sendico.io" + address = "{{ env `VAULT_ADDR` }}" } auto_auth { diff --git a/ci/prod/compose/vault-agent/tron-gateway.hcl b/ci/prod/compose/vault-agent/tron-gateway.hcl index 3dbfc804..996e74dd 100644 --- a/ci/prod/compose/vault-agent/tron-gateway.hcl +++ b/ci/prod/compose/vault-agent/tron-gateway.hcl @@ -1,5 +1,5 @@ vault { - address = "https://vault.sendico.io" + address = "{{ env `VAULT_ADDR` }}" } auto_auth { diff --git a/ci/prod/compose/vault-server/config.hcl b/ci/prod/compose/vault-server/config.hcl new file mode 100644 index 00000000..1db1e9bf --- /dev/null +++ b/ci/prod/compose/vault-server/config.hcl @@ -0,0 +1,12 @@ +storage "file" { + path = "/vault/file" +} + +listener "tcp" { + address = "0.0.0.0:8200" + tls_disable = true +} + +api_addr = "http://dev-vault:8200" +ui = true +disable_mlock = true diff --git a/ci/prod/compose/vault.yml b/ci/prod/compose/vault.yml new file mode 100644 index 00000000..089a66ba --- /dev/null +++ b/ci/prod/compose/vault.yml @@ -0,0 +1,37 @@ +# Compose v2 - Dev Vault + +x-common-env: &common-env + env_file: + - ../env/.env.runtime + +volumes: + dev_vault_data: {} + +networks: + sendico-net: + external: true + name: sendico-net + +services: + dev_vault: + <<: *common-env + image: hashicorp/vault:latest + container_name: dev-vault + restart: unless-stopped + cap_add: ["IPC_LOCK"] + environment: + VAULT_ADDR: http://127.0.0.1:8200 + command: vault server -config=/vault/config/vault.hcl + volumes: + - dev_vault_data:/vault/file + - ./vault-server/config.hcl:/vault/config/vault.hcl:ro + ports: + - "0.0.0.0:${VAULT_HTTP_PORT}:8200" + healthcheck: + test: ["CMD-SHELL","export VAULT_ADDR=http://127.0.0.1:8200; vault status >/dev/null 2>&1; rc=$?; [ \"$rc\" -eq 0 ] || [ \"$rc\" -eq 2 ]"] + interval: 10s + timeout: 5s + retries: 12 + start_period: 10s + networks: + - sendico-net diff --git a/ci/prod/compose/vault/agent.dev.hcl b/ci/prod/compose/vault/agent.dev.hcl new file mode 100644 index 00000000..911e472d --- /dev/null +++ b/ci/prod/compose/vault/agent.dev.hcl @@ -0,0 +1,29 @@ +# Vault Agent for the dev DB stack. AppRole creds are files on the host. +pid_file = "/tmp/vault-agent.pid" + +auto_auth { + method "approle" { + mount_path = "auth/approle" + config = { + role_id_file_path = "/vault/secrets/role_id" + secret_id_file_path = "/vault/secrets/secret_id" + } + } + sink "file" { config = { path = "/vault/token" } } +} + +vault { address = "{{ env `VAULT_ADDR` }}" } + +template { + source = "/etc/vault/templates/mongo/user.ctmpl" + destination = "/vault/secrets/MONGO_INITDB_ROOT_USERNAME" +} +template { + source = "/etc/vault/templates/mongo/pass.ctmpl" + destination = "/vault/secrets/MONGO_INITDB_ROOT_PASSWORD" +} +template { + source = "/etc/vault/templates/mongo/keyfile.ctmpl" + destination = "/vault/secrets/mongo.kf" + command = "sh -lc 'chown 999:999 /vault/secrets/mongo.kf && chmod 0400 /vault/secrets/mongo.kf'" +} diff --git a/ci/prod/scripts/deploy/aurora_gateway.sh b/ci/prod/scripts/deploy/aurora_gateway.sh new file mode 100644 index 00000000..310ff821 --- /dev/null +++ b/ci/prod/scripts/deploy/aurora_gateway.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && set -x +trap 'echo "[deploy-aurora-gateway] error at line $LINENO" >&2' ERR + +: "${REMOTE_BASE:?missing REMOTE_BASE}" +: "${SSH_USER:?missing SSH_USER}" +: "${SSH_HOST:?missing SSH_HOST}" +: "${AURORA_GATEWAY_DIR:?missing AURORA_GATEWAY_DIR}" +: "${AURORA_GATEWAY_COMPOSE_PROJECT:?missing AURORA_GATEWAY_COMPOSE_PROJECT}" +: "${AURORA_GATEWAY_SERVICE_NAME:?missing AURORA_GATEWAY_SERVICE_NAME}" + +REMOTE_DIR="${REMOTE_BASE%/}/${AURORA_GATEWAY_DIR}" +REMOTE_TARGET="${SSH_USER}@${SSH_HOST}" +RUNTIME_ENV_FILE="${RUNTIME_ENV_FILE:-ci/prod/.env.runtime}" +COMPOSE_FILE="aurora_gateway.yml" +SERVICE_NAMES="${AURORA_GATEWAY_SERVICE_NAME}" + +REQUIRED_SECRETS=( + AURORA_GATEWAY_MONGO_USER + AURORA_GATEWAY_MONGO_PASSWORD + NATS_USER + NATS_PASSWORD + NATS_URL +) + +for var in "${REQUIRED_SECRETS[@]}"; do + if [[ -z "${!var:-}" ]]; then + echo "missing required secret env: ${var}" >&2 + exit 65 + fi +done + +if [[ ! -s .env.version ]]; then + echo ".env.version is missing; run version step first" >&2 + exit 66 +fi + +b64enc() { + printf '%s' "$1" | base64 | tr -d '\n' +} + +AURORA_GATEWAY_MONGO_USER_B64="$(b64enc "${AURORA_GATEWAY_MONGO_USER}")" +AURORA_GATEWAY_MONGO_PASSWORD_B64="$(b64enc "${AURORA_GATEWAY_MONGO_PASSWORD}")" +NATS_USER_B64="$(b64enc "${NATS_USER}")" +NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" +NATS_URL_B64="$(b64enc "${NATS_URL}")" + +SSH_OPTS=( + -i /root/.ssh/id_rsa + -o StrictHostKeyChecking=no + -o UserKnownHostsFile=/dev/null + -o LogLevel=ERROR + -o BatchMode=yes + -o PreferredAuthentications=publickey + -o ConnectTimeout=10 + -q +) +if [[ "${DEBUG_DEPLOY:-0}" = "1" ]]; then + SSH_OPTS=("${SSH_OPTS[@]/-q/}" -vv) +fi + +RSYNC_FLAGS=(-az --delete) +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && RSYNC_FLAGS=(-avz --delete) + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{compose,env}" + +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/" +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${RUNTIME_ENV_FILE}" "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime" +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" .env.version "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.version" + +SERVICES_LINE="${SERVICE_NAMES}" + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \ + REMOTE_DIR="$REMOTE_DIR" \ + COMPOSE_FILE="$COMPOSE_FILE" \ + COMPOSE_PROJECT="$AURORA_GATEWAY_COMPOSE_PROJECT" \ + SERVICES_LINE="$SERVICES_LINE" \ + AURORA_GATEWAY_MONGO_USER_B64="$AURORA_GATEWAY_MONGO_USER_B64" \ + AURORA_GATEWAY_MONGO_PASSWORD_B64="$AURORA_GATEWAY_MONGO_PASSWORD_B64" \ + NATS_USER_B64="$NATS_USER_B64" \ + NATS_PASSWORD_B64="$NATS_PASSWORD_B64" \ + NATS_URL_B64="$NATS_URL_B64" \ + bash -s <<'EOSSH' +set -euo pipefail +cd "${REMOTE_DIR}/compose" +set -a +. ../env/.env.runtime +load_kv_file() { + local file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + if printf '%s' "$line" | grep -Eq '^[[:alpha:]_][[:alnum:]_]*='; then + local key="${line%%=*}" + local value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + if [[ -n "$key" ]]; then + export "$key=$value" + fi + fi + done <"$file" +} +load_kv_file ../env/.env.version +set +a + +IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" +export IMAGE_TAG + +if base64 -d >/dev/null 2>&1 <<<'AA=='; then + BASE64_DECODE_FLAG='-d' +else + BASE64_DECODE_FLAG='--decode' +fi + +decode_b64() { + val="$1" + if [[ -z "$val" ]]; then + printf '' + return + fi + printf '%s' "$val" | base64 "${BASE64_DECODE_FLAG}" +} + +AURORA_GATEWAY_MONGO_USER="$(decode_b64 "$AURORA_GATEWAY_MONGO_USER_B64")" +AURORA_GATEWAY_MONGO_PASSWORD="$(decode_b64 "$AURORA_GATEWAY_MONGO_PASSWORD_B64")" +NATS_USER="$(decode_b64 "$NATS_USER_B64")" +NATS_PASSWORD="$(decode_b64 "$NATS_PASSWORD_B64")" +NATS_URL="$(decode_b64 "$NATS_URL_B64")" + +export AURORA_GATEWAY_MONGO_USER AURORA_GATEWAY_MONGO_PASSWORD +export NATS_USER NATS_PASSWORD NATS_URL + +COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" +export COMPOSE_PROJECT_NAME +read -r -a SERVICES <<<"${SERVICES_LINE}" + +pull_cmd=(docker compose -f "$COMPOSE_FILE" pull) +up_cmd=(docker compose -f "$COMPOSE_FILE" up -d --remove-orphans) +ps_cmd=(docker compose -f "$COMPOSE_FILE" ps) +if [[ "${#SERVICES[@]}" -gt 0 ]]; then + pull_cmd+=("${SERVICES[@]}") + up_cmd+=("${SERVICES[@]}") + ps_cmd+=("${SERVICES[@]}") +fi + +"${pull_cmd[@]}" +"${up_cmd[@]}" +"${ps_cmd[@]}" + +date -Is > .last_deploy +logger -t "deploy-${COMPOSE_PROJECT_NAME}" "${COMPOSE_PROJECT_NAME} deployed at $(date -Is) in ${REMOTE_DIR}" +EOSSH diff --git a/ci/prod/scripts/deploy/bff.sh b/ci/prod/scripts/deploy/bff.sh index d6338c85..422f075a 100755 --- a/ci/prod/scripts/deploy/bff.sh +++ b/ci/prod/scripts/deploy/bff.sh @@ -20,8 +20,6 @@ REQUIRED_SECRETS=( MONGO_USER MONGO_PASSWORD API_ENDPOINT_SECRET - BFF_VAULT_ROLE_ID - BFF_VAULT_SECRET_ID NATS_USER NATS_PASSWORD NATS_URL @@ -46,8 +44,8 @@ b64enc() { MONGO_USER_B64="$(b64enc "${MONGO_USER}")" MONGO_PASSWORD_B64="$(b64enc "${MONGO_PASSWORD}")" API_ENDPOINT_SECRET_B64="$(b64enc "${API_ENDPOINT_SECRET}")" -BFF_VAULT_ROLE_ID_B64="$(b64enc "${BFF_VAULT_ROLE_ID}")" -BFF_VAULT_SECRET_ID_B64="$(b64enc "${BFF_VAULT_SECRET_ID}")" +BFF_VAULT_ROLE_ID_B64="$(b64enc "${BFF_VAULT_ROLE_ID:-}")" +BFF_VAULT_SECRET_ID_B64="$(b64enc "${BFF_VAULT_SECRET_ID:-}")" NATS_USER_B64="$(b64enc "${NATS_USER}")" NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" NATS_URL_B64="$(b64enc "${NATS_URL}")" @@ -114,6 +112,9 @@ load_kv_file() { done <"$file" } load_kv_file ../env/.env.version +if [[ -f ../env/vault.env ]]; then + load_kv_file ../env/vault.env +fi set +a IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" @@ -146,6 +147,10 @@ NATS_URL="$(decode_b64 "$NATS_URL_B64")" export MONGO_USER MONGO_PASSWORD API_ENDPOINT_SECRET export BFF_VAULT_ROLE_ID BFF_VAULT_SECRET_ID export NATS_USER NATS_PASSWORD NATS_URL +if [[ -z "${BFF_VAULT_ROLE_ID:-}" || -z "${BFF_VAULT_SECRET_ID:-}" ]]; then + echo "missing required secret env: BFF_VAULT_ROLE_ID/BFF_VAULT_SECRET_ID" >&2 + exit 65 +fi COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" export COMPOSE_PROJECT_NAME read -r -a SERVICES <<<"${SERVICES_LINE}" diff --git a/ci/prod/scripts/deploy/callbacks.sh b/ci/prod/scripts/deploy/callbacks.sh index 20dece45..03848040 100755 --- a/ci/prod/scripts/deploy/callbacks.sh +++ b/ci/prod/scripts/deploy/callbacks.sh @@ -19,8 +19,6 @@ SERVICE_NAMES="${CALLBACKS_SERVICE_NAME}" REQUIRED_SECRETS=( CALLBACKS_MONGO_USER CALLBACKS_MONGO_PASSWORD - CALLBACKS_VAULT_ROLE_ID - CALLBACKS_VAULT_SECRET_ID NATS_USER NATS_PASSWORD NATS_URL @@ -44,8 +42,8 @@ b64enc() { CALLBACKS_MONGO_USER_B64="$(b64enc "${CALLBACKS_MONGO_USER}")" CALLBACKS_MONGO_PASSWORD_B64="$(b64enc "${CALLBACKS_MONGO_PASSWORD}")" -CALLBACKS_VAULT_ROLE_ID_B64="$(b64enc "${CALLBACKS_VAULT_ROLE_ID}")" -CALLBACKS_VAULT_SECRET_ID_B64="$(b64enc "${CALLBACKS_VAULT_SECRET_ID}")" +CALLBACKS_VAULT_ROLE_ID_B64="$(b64enc "${CALLBACKS_VAULT_ROLE_ID:-}")" +CALLBACKS_VAULT_SECRET_ID_B64="$(b64enc "${CALLBACKS_VAULT_SECRET_ID:-}")" NATS_USER_B64="$(b64enc "${NATS_USER}")" NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" NATS_URL_B64="$(b64enc "${NATS_URL}")" @@ -111,6 +109,9 @@ load_kv_file() { done <"$file" } load_kv_file ../env/.env.version +if [[ -f ../env/vault.env ]]; then + load_kv_file ../env/vault.env +fi set +a IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" @@ -142,6 +143,10 @@ NATS_URL="$(decode_b64 "$NATS_URL_B64")" export CALLBACKS_MONGO_USER CALLBACKS_MONGO_PASSWORD export CALLBACKS_VAULT_ROLE_ID CALLBACKS_VAULT_SECRET_ID export NATS_USER NATS_PASSWORD NATS_URL +if [[ -z "${CALLBACKS_VAULT_ROLE_ID:-}" || -z "${CALLBACKS_VAULT_SECRET_ID:-}" ]]; then + echo "missing required secret env: CALLBACKS_VAULT_ROLE_ID/CALLBACKS_VAULT_SECRET_ID" >&2 + exit 65 +fi COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" export COMPOSE_PROJECT_NAME read -r -a SERVICES <<<"${SERVICES_LINE}" diff --git a/ci/prod/scripts/deploy/chain_gateway.sh b/ci/prod/scripts/deploy/chain_gateway.sh index 9ba00185..7ee5889e 100755 --- a/ci/prod/scripts/deploy/chain_gateway.sh +++ b/ci/prod/scripts/deploy/chain_gateway.sh @@ -22,8 +22,6 @@ REQUIRED_SECRETS=( CHAIN_GATEWAY_RPC_URL CHAIN_GATEWAY_SERVICE_WALLET_KEY CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS - CHAIN_GATEWAY_VAULT_ROLE_ID - CHAIN_GATEWAY_VAULT_SECRET_ID NATS_USER NATS_PASSWORD NATS_URL @@ -50,8 +48,8 @@ CHAIN_GATEWAY_MONGO_PASSWORD_B64="$(b64enc "${CHAIN_GATEWAY_MONGO_PASSWORD}")" CHAIN_GATEWAY_RPC_URL_B64="$(b64enc "${CHAIN_GATEWAY_RPC_URL}")" CHAIN_GATEWAY_SERVICE_WALLET_KEY_B64="$(b64enc "${CHAIN_GATEWAY_SERVICE_WALLET_KEY}")" CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS_B64="$(b64enc "${CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS}")" -CHAIN_GATEWAY_VAULT_ROLE_ID_B64="$(b64enc "${CHAIN_GATEWAY_VAULT_ROLE_ID}")" -CHAIN_GATEWAY_VAULT_SECRET_ID_B64="$(b64enc "${CHAIN_GATEWAY_VAULT_SECRET_ID}")" +CHAIN_GATEWAY_VAULT_ROLE_ID_B64="$(b64enc "${CHAIN_GATEWAY_VAULT_ROLE_ID:-}")" +CHAIN_GATEWAY_VAULT_SECRET_ID_B64="$(b64enc "${CHAIN_GATEWAY_VAULT_SECRET_ID:-}")" NATS_USER_B64="$(b64enc "${NATS_USER}")" NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" NATS_URL_B64="$(b64enc "${NATS_URL}")" @@ -120,6 +118,9 @@ load_kv_file() { done <"$file" } load_kv_file ../env/.env.version +if [[ -f ../env/vault.env ]]; then + load_kv_file ../env/vault.env +fi set +a IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" @@ -156,6 +157,10 @@ export CHAIN_GATEWAY_RPC_URL export CHAIN_GATEWAY_SERVICE_WALLET_KEY CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS export CHAIN_GATEWAY_VAULT_ROLE_ID CHAIN_GATEWAY_VAULT_SECRET_ID export NATS_USER NATS_PASSWORD NATS_URL +if [[ -z "${CHAIN_GATEWAY_VAULT_ROLE_ID:-}" || -z "${CHAIN_GATEWAY_VAULT_SECRET_ID:-}" ]]; then + echo "missing required secret env: CHAIN_GATEWAY_VAULT_ROLE_ID/CHAIN_GATEWAY_VAULT_SECRET_ID" >&2 + exit 65 +fi COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" export COMPOSE_PROJECT_NAME diff --git a/ci/prod/scripts/deploy/chsettle_gateway.sh b/ci/prod/scripts/deploy/chsettle_gateway.sh new file mode 100644 index 00000000..83761a25 --- /dev/null +++ b/ci/prod/scripts/deploy/chsettle_gateway.sh @@ -0,0 +1,156 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && set -x +trap 'echo "[deploy-chsettle-gateway] error at line $LINENO" >&2' ERR + +: "${REMOTE_BASE:?missing REMOTE_BASE}" +: "${SSH_USER:?missing SSH_USER}" +: "${SSH_HOST:?missing SSH_HOST}" +: "${CHSETTLE_GATEWAY_DIR:?missing CHSETTLE_GATEWAY_DIR}" +: "${CHSETTLE_GATEWAY_COMPOSE_PROJECT:?missing CHSETTLE_GATEWAY_COMPOSE_PROJECT}" +: "${CHSETTLE_GATEWAY_SERVICE_NAME:?missing CHSETTLE_GATEWAY_SERVICE_NAME}" + +REMOTE_DIR="${REMOTE_BASE%/}/${CHSETTLE_GATEWAY_DIR}" +REMOTE_TARGET="${SSH_USER}@${SSH_HOST}" +RUNTIME_ENV_FILE="${RUNTIME_ENV_FILE:-ci/prod/.env.runtime}" +COMPOSE_FILE="chsettle_gateway.yml" +SERVICE_NAMES="${CHSETTLE_GATEWAY_SERVICE_NAME}" + +REQUIRED_SECRETS=( + CHSETTLE_GATEWAY_MONGO_USER + CHSETTLE_GATEWAY_MONGO_PASSWORD + NATS_USER + NATS_PASSWORD + NATS_URL +) + +for var in "${REQUIRED_SECRETS[@]}"; do + if [[ -z "${!var:-}" ]]; then + echo "missing required secret env: ${var}" >&2 + exit 65 + fi +done + +if [[ ! -s .env.version ]]; then + echo ".env.version is missing; run version step first" >&2 + exit 66 +fi + +b64enc() { + printf '%s' "$1" | base64 | tr -d '\n' +} + +CHSETTLE_GATEWAY_MONGO_USER_B64="$(b64enc "${CHSETTLE_GATEWAY_MONGO_USER}")" +CHSETTLE_GATEWAY_MONGO_PASSWORD_B64="$(b64enc "${CHSETTLE_GATEWAY_MONGO_PASSWORD}")" +NATS_USER_B64="$(b64enc "${NATS_USER}")" +NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" +NATS_URL_B64="$(b64enc "${NATS_URL}")" + +SSH_OPTS=( + -i /root/.ssh/id_rsa + -o StrictHostKeyChecking=no + -o UserKnownHostsFile=/dev/null + -o LogLevel=ERROR + -o BatchMode=yes + -o PreferredAuthentications=publickey + -o ConnectTimeout=10 + -q +) +if [[ "${DEBUG_DEPLOY:-0}" = "1" ]]; then + SSH_OPTS=("${SSH_OPTS[@]/-q/}" -vv) +fi + +RSYNC_FLAGS=(-az --delete) +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && RSYNC_FLAGS=(-avz --delete) + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{compose,env}" + +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/" +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${RUNTIME_ENV_FILE}" "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime" +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" .env.version "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.version" + +SERVICES_LINE="${SERVICE_NAMES}" + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \ + REMOTE_DIR="$REMOTE_DIR" \ + COMPOSE_FILE="$COMPOSE_FILE" \ + COMPOSE_PROJECT="$CHSETTLE_GATEWAY_COMPOSE_PROJECT" \ + SERVICES_LINE="$SERVICES_LINE" \ + CHSETTLE_GATEWAY_MONGO_USER_B64="$CHSETTLE_GATEWAY_MONGO_USER_B64" \ + CHSETTLE_GATEWAY_MONGO_PASSWORD_B64="$CHSETTLE_GATEWAY_MONGO_PASSWORD_B64" \ + NATS_USER_B64="$NATS_USER_B64" \ + NATS_PASSWORD_B64="$NATS_PASSWORD_B64" \ + NATS_URL_B64="$NATS_URL_B64" \ + bash -s <<'EOSSH' +set -euo pipefail +cd "${REMOTE_DIR}/compose" +set -a +. ../env/.env.runtime +load_kv_file() { + local file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + if printf '%s' "$line" | grep -Eq '^[[:alpha:]_][[:alnum:]_]*='; then + local key="${line%%=*}" + local value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + if [[ -n "$key" ]]; then + export "$key=$value" + fi + fi + done <"$file" +} +load_kv_file ../env/.env.version +set +a + +IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" +export IMAGE_TAG + +if base64 -d >/dev/null 2>&1 <<<'AA=='; then + BASE64_DECODE_FLAG='-d' +else + BASE64_DECODE_FLAG='--decode' +fi + +decode_b64() { + val="$1" + if [[ -z "$val" ]]; then + printf '' + return + fi + printf '%s' "$val" | base64 "${BASE64_DECODE_FLAG}" +} + +CHSETTLE_GATEWAY_MONGO_USER="$(decode_b64 "$CHSETTLE_GATEWAY_MONGO_USER_B64")" +CHSETTLE_GATEWAY_MONGO_PASSWORD="$(decode_b64 "$CHSETTLE_GATEWAY_MONGO_PASSWORD_B64")" +NATS_USER="$(decode_b64 "$NATS_USER_B64")" +NATS_PASSWORD="$(decode_b64 "$NATS_PASSWORD_B64")" +NATS_URL="$(decode_b64 "$NATS_URL_B64")" + +export CHSETTLE_GATEWAY_MONGO_USER CHSETTLE_GATEWAY_MONGO_PASSWORD +export NATS_USER NATS_PASSWORD NATS_URL + +COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" +export COMPOSE_PROJECT_NAME +read -r -a SERVICES <<<"${SERVICES_LINE}" + +pull_cmd=(docker compose -f "$COMPOSE_FILE" pull) +up_cmd=(docker compose -f "$COMPOSE_FILE" up -d --remove-orphans) +ps_cmd=(docker compose -f "$COMPOSE_FILE" ps) +if [[ "${#SERVICES[@]}" -gt 0 ]]; then + pull_cmd+=("${SERVICES[@]}") + up_cmd+=("${SERVICES[@]}") + ps_cmd+=("${SERVICES[@]}") +fi + +"${pull_cmd[@]}" +"${up_cmd[@]}" +"${ps_cmd[@]}" + +date -Is > .last_deploy +logger -t "deploy-${COMPOSE_PROJECT_NAME}" "${COMPOSE_PROJECT_NAME} deployed at $(date -Is) in ${REMOTE_DIR}" +EOSSH diff --git a/ci/prod/scripts/deploy/db.sh b/ci/prod/scripts/deploy/db.sh index a7435ab5..39b745ee 100755 --- a/ci/prod/scripts/deploy/db.sh +++ b/ci/prod/scripts/deploy/db.sh @@ -16,6 +16,7 @@ trap 'echo "[deploy-db] error at line $LINENO" >&2' ERR REMOTE_DIR="${REMOTE_BASE%/}/${DB_DIR}" REMOTE_TARGET="${SSH_USER}@${SSH_HOST}" RUNTIME_ENV_FILE="${RUNTIME_ENV_FILE:-ci/prod/.env.runtime}" +COMPOSE_FILE="${DB_COMPOSE_FILE:-db.yml}" # SSH options: quiet by default; add -vv in debug mode SSH_OPTS=( @@ -47,6 +48,7 @@ rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${RUNTIME_ENV_FILE}" "$REMOTE # The vault-agent container writes them into tmpfs and unsets them internally. ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \ REMOTE_DIR="$REMOTE_DIR" \ + COMPOSE_FILE="$COMPOSE_FILE" \ VAULT_ROLE_ID="$VAULT_ROLE_ID" \ VAULT_SECRET_ID="$VAULT_SECRET_ID" \ bash -s <<'EOSSH' @@ -56,12 +58,12 @@ set -a; . ../env/.env.runtime; set +a COMPOSE_PROJECT_NAME="${DB_COMPOSE_PROJECT:-sendico-db}" export COMPOSE_PROJECT_NAME # Run with ephemeral AppRole env (scoped only to these commands) -VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml pull --quiet 2>/dev/null || \ -VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml pull +VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f "${COMPOSE_FILE}" pull --quiet 2>/dev/null || \ +VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f "${COMPOSE_FILE}" pull -VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f db.yml up -d --remove-orphans +VAULT_ROLE_ID="${VAULT_ROLE_ID}" VAULT_SECRET_ID="${VAULT_SECRET_ID}" docker compose -f "${COMPOSE_FILE}" up -d --remove-orphans -docker compose -f db.yml ps +docker compose -f "${COMPOSE_FILE}" ps date -Is > .last_deploy logger -t deploy-db "db deployed at $(date -Is) in ${REMOTE_DIR}" EOSSH diff --git a/ci/prod/scripts/deploy/tron_gateway.sh b/ci/prod/scripts/deploy/tron_gateway.sh index 817db54d..fb7a4e27 100755 --- a/ci/prod/scripts/deploy/tron_gateway.sh +++ b/ci/prod/scripts/deploy/tron_gateway.sh @@ -22,8 +22,6 @@ REQUIRED_SECRETS=( TRON_GATEWAY_RPC_URL TRON_GATEWAY_SERVICE_WALLET_KEY TRON_GATEWAY_SERVICE_WALLET_ADDRESS - TRON_GATEWAY_VAULT_ROLE_ID - TRON_GATEWAY_VAULT_SECRET_ID NATS_USER NATS_PASSWORD NATS_URL @@ -52,8 +50,8 @@ TRON_GATEWAY_GRPC_URL_B64="$(b64enc "${TRON_GATEWAY_GRPC_URL:-}")" TRON_GATEWAY_GRPC_TOKEN_B64="$(b64enc "${TRON_GATEWAY_GRPC_TOKEN:-}")" TRON_GATEWAY_SERVICE_WALLET_KEY_B64="$(b64enc "${TRON_GATEWAY_SERVICE_WALLET_KEY}")" TRON_GATEWAY_SERVICE_WALLET_ADDRESS_B64="$(b64enc "${TRON_GATEWAY_SERVICE_WALLET_ADDRESS}")" -TRON_GATEWAY_VAULT_ROLE_ID_B64="$(b64enc "${TRON_GATEWAY_VAULT_ROLE_ID}")" -TRON_GATEWAY_VAULT_SECRET_ID_B64="$(b64enc "${TRON_GATEWAY_VAULT_SECRET_ID}")" +TRON_GATEWAY_VAULT_ROLE_ID_B64="$(b64enc "${TRON_GATEWAY_VAULT_ROLE_ID:-}")" +TRON_GATEWAY_VAULT_SECRET_ID_B64="$(b64enc "${TRON_GATEWAY_VAULT_SECRET_ID:-}")" NATS_USER_B64="$(b64enc "${NATS_USER}")" NATS_PASSWORD_B64="$(b64enc "${NATS_PASSWORD}")" NATS_URL_B64="$(b64enc "${NATS_URL}")" @@ -124,6 +122,9 @@ load_kv_file() { done <"$file" } load_kv_file ../env/.env.version +if [[ -f ../env/vault.env ]]; then + load_kv_file ../env/vault.env +fi set +a IMAGE_TAG="${IMAGE_TAG:-${APP_V}-${GIT_REV}}" @@ -162,6 +163,10 @@ export TRON_GATEWAY_RPC_URL TRON_GATEWAY_GRPC_URL TRON_GATEWAY_GRPC_TOKEN export TRON_GATEWAY_SERVICE_WALLET_KEY TRON_GATEWAY_SERVICE_WALLET_ADDRESS export TRON_GATEWAY_VAULT_ROLE_ID TRON_GATEWAY_VAULT_SECRET_ID export NATS_USER NATS_PASSWORD NATS_URL +if [[ -z "${TRON_GATEWAY_VAULT_ROLE_ID:-}" || -z "${TRON_GATEWAY_VAULT_SECRET_ID:-}" ]]; then + echo "missing required secret env: TRON_GATEWAY_VAULT_ROLE_ID/TRON_GATEWAY_VAULT_SECRET_ID" >&2 + exit 65 +fi COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" export COMPOSE_PROJECT_NAME diff --git a/ci/prod/scripts/deploy/vault.sh b/ci/prod/scripts/deploy/vault.sh new file mode 100644 index 00000000..2d72e7f6 --- /dev/null +++ b/ci/prod/scripts/deploy/vault.sh @@ -0,0 +1,140 @@ +#!/usr/bin/env bash +set -euo pipefail +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && set -x +trap 'echo "[deploy-vault] error at line $LINENO" >&2' ERR + +: "${REMOTE_BASE:?missing REMOTE_BASE}" +: "${SSH_USER:?missing SSH_USER}" +: "${SSH_HOST:?missing SSH_HOST}" +: "${VAULT_DIR:?missing VAULT_DIR}" +: "${VAULT_COMPOSE_PROJECT:?missing VAULT_COMPOSE_PROJECT}" + +REMOTE_DIR="${REMOTE_BASE%/}/${VAULT_DIR}" +REMOTE_TARGET="${SSH_USER}@${SSH_HOST}" +RUNTIME_ENV_FILE="${RUNTIME_ENV_FILE:-ci/prod/.env.runtime}" +COMPOSE_FILE="vault.yml" + +SSH_OPTS=( + -i /root/.ssh/id_rsa + -o StrictHostKeyChecking=no + -o UserKnownHostsFile=/dev/null + -o LogLevel=ERROR + -o BatchMode=yes + -o PreferredAuthentications=publickey + -o ConnectTimeout=10 + -q +) +if [[ "${DEBUG_DEPLOY:-0}" = "1" ]]; then + SSH_OPTS=("${SSH_OPTS[@]/-q/}" -vv) +fi + +RSYNC_FLAGS=(-az --delete) +[[ "${DEBUG_DEPLOY:-0}" = "1" ]] && RSYNC_FLAGS=(-avz --delete) + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" "mkdir -p ${REMOTE_DIR}/{compose,env}" + +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" ci/prod/compose/ "$REMOTE_TARGET:${REMOTE_DIR}/compose/" +rsync "${RSYNC_FLAGS[@]}" -e "ssh ${SSH_OPTS[*]}" "${RUNTIME_ENV_FILE}" "$REMOTE_TARGET:${REMOTE_DIR}/env/.env.runtime" + +ssh "${SSH_OPTS[@]}" "$REMOTE_TARGET" \ + REMOTE_BASE="$REMOTE_BASE" \ + REMOTE_DIR="$REMOTE_DIR" \ + COMPOSE_FILE="$COMPOSE_FILE" \ + COMPOSE_PROJECT="$VAULT_COMPOSE_PROJECT" \ + bash -s <<'EOSSH' +set -euo pipefail +cd "${REMOTE_DIR}/compose" +set -a +. ../env/.env.runtime +set +a + +COMPOSE_PROJECT_NAME="$COMPOSE_PROJECT" +export COMPOSE_PROJECT_NAME + +docker compose -f "$COMPOSE_FILE" pull --quiet 2>/dev/null || docker compose -f "$COMPOSE_FILE" pull +docker compose -f "$COMPOSE_FILE" up -d --remove-orphans + +status_json="" +for _ in $(seq 1 30); do + status_json="$(docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault status -format=json' 2>/dev/null || true)" + if [[ -n "$status_json" ]]; then + break + fi + sleep 2 +done + +if [[ -z "$status_json" ]]; then + echo "vault status did not become available" >&2 + exit 65 +fi + +INIT_FILE="../env/vault-init.json" +if printf '%s' "$status_json" | grep -Eq '"initialized"[[:space:]]*:[[:space:]]*false'; then + docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault operator init -format=json -key-shares=1 -key-threshold=1' >"$INIT_FILE" + chmod 600 "$INIT_FILE" + status_json="$(docker exec dev-vault sh -lc 'export VAULT_ADDR=http://127.0.0.1:8200; vault status -format=json')" +fi + +if [[ ! -s "$INIT_FILE" ]]; then + echo "vault init file is missing: $INIT_FILE" >&2 + exit 66 +fi + +INIT_JSON_COMPACT="$(tr -d '\r\n\t ' <"$INIT_FILE")" +UNSEAL_KEY="$(printf '%s' "$INIT_JSON_COMPACT" | sed -n 's/.*"unseal_keys_b64":\["\([^"]*\)".*/\1/p')" +ROOT_TOKEN="$(printf '%s' "$INIT_JSON_COMPACT" | sed -n 's/.*"root_token":"\([^"]*\)".*/\1/p')" + +if [[ -z "$UNSEAL_KEY" || -z "$ROOT_TOKEN" ]]; then + echo "failed to extract vault init credentials" >&2 + exit 67 +fi + +if printf '%s' "$status_json" | grep -Eq '"sealed"[[:space:]]*:[[:space:]]*true'; then + docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200; vault operator unseal '${UNSEAL_KEY}' >/dev/null" +fi + +docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault auth list -format=json | grep -q '\"approle/\"' || vault auth enable approle >/dev/null" +docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault secrets list -format=json | grep -q '\"kv/\"' || vault secrets enable -path=kv kv-v2 >/dev/null" + +docker exec -i dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault policy write sendico-dev-apps -" <<'EOF' +path "kv/data/*" { + capabilities = ["create", "read", "update", "delete", "list"] +} + +path "kv/metadata/*" { + capabilities = ["create", "read", "update", "delete", "list"] +} +EOF + +docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault write auth/approle/role/sendico-dev-apps token_policies='sendico-dev-apps' token_ttl='24h' token_max_ttl='720h' >/dev/null" + +APPROLE_ROLE_ID="$(docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault read -field=role_id auth/approle/role/sendico-dev-apps/role-id")" +APPROLE_SECRET_ID="$(docker exec dev-vault sh -lc "export VAULT_ADDR=http://127.0.0.1:8200 VAULT_TOKEN='${ROOT_TOKEN}'; vault write -force -field=secret_id auth/approle/role/sendico-dev-apps/secret-id")" + +if [[ -z "$APPROLE_ROLE_ID" || -z "$APPROLE_SECRET_ID" ]]; then + echo "failed to create dev approle credentials" >&2 + exit 68 +fi + +write_vault_env() { + local service_dir="$1" + local role_var="$2" + local secret_var="$3" + local env_dir="${REMOTE_BASE%/}/${service_dir}/env" + mkdir -p "$env_dir" + cat >"${env_dir}/vault.env" < .last_deploy +logger -t "deploy-${COMPOSE_PROJECT_NAME}" "${COMPOSE_PROJECT_NAME} deployed at $(date -Is) in ${REMOTE_DIR}" +EOSSH diff --git a/ci/scripts/aurora/build-image.sh b/ci/scripts/aurora/build-image.sh new file mode 100644 index 00000000..90253405 --- /dev/null +++ b/ci/scripts/aurora/build-image.sh @@ -0,0 +1,94 @@ +#!/bin/sh +set -eu + +if ! set -o pipefail 2>/dev/null; then + : +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "${REPO_ROOT}" + +sh ci/scripts/common/ensure_env_version.sh +. ci/scripts/common/runtime_env.sh + +normalize_env_file() { + file="$1" + tmp="${file}.tmp.$$" + tr -d '\r' <"$file" >"$tmp" + mv "$tmp" "$file" +} + +load_env_file() { + file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + export "$key=$value" + done <"$file" +} + +AURORA_GATEWAY_ENV_NAME="${AURORA_GATEWAY_ENV:-$(resolve_runtime_env_name)}" +load_runtime_env_bundle "${AURORA_GATEWAY_ENV_NAME}" + +IMAGE_TAG="$(compute_image_tag)" + +REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" +APP_V="${APP_V:?missing APP_V}" +AURORA_GATEWAY_DOCKERFILE="${AURORA_GATEWAY_DOCKERFILE:?missing AURORA_GATEWAY_DOCKERFILE}" +AURORA_GATEWAY_IMAGE_PATH="${AURORA_GATEWAY_IMAGE_PATH:?missing AURORA_GATEWAY_IMAGE_PATH}" + +REGISTRY_HOST="${REGISTRY_URL#http://}" +REGISTRY_HOST="${REGISTRY_HOST#https://}" +REGISTRY_USER="$(cat secrets/REGISTRY_USER)" +REGISTRY_PASSWORD="$(cat secrets/REGISTRY_PASSWORD)" +: "${REGISTRY_USER:?missing registry user}" +: "${REGISTRY_PASSWORD:?missing registry password}" + +mkdir -p /kaniko/.docker +AUTH_B64="$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASSWORD" | base64 | tr -d '\n')" +cat </kaniko/.docker/config.json +{ + "auths": { + "https://${REGISTRY_HOST}": { "auth": "${AUTH_B64}" } + } +} +EOF + +BUILD_CONTEXT="${AURORA_GATEWAY_BUILD_CONTEXT:-${WOODPECKER_WORKSPACE:-${CI_WORKSPACE:-${PWD:-/workspace}}}}" +if [ ! -d "${BUILD_CONTEXT}" ]; then + BUILD_CONTEXT="/workspace" +fi + +# Gateway modules use a local replace (../common); ensure build context contains shared code. +if [ ! -d "${BUILD_CONTEXT}/api/gateway/common" ] || [ ! -f "${BUILD_CONTEXT}/${AURORA_GATEWAY_DOCKERFILE}" ]; then + if [ -d "${REPO_ROOT}/api/gateway/common" ] && [ -f "${REPO_ROOT}/${AURORA_GATEWAY_DOCKERFILE}" ]; then + echo "[aurora-gateway-build] build context ${BUILD_CONTEXT} is incomplete; falling back to ${REPO_ROOT}" >&2 + BUILD_CONTEXT="${REPO_ROOT}" + fi +fi + +if [ ! -d "${BUILD_CONTEXT}/api/gateway/common" ]; then + echo "[aurora-gateway-build] build context ${BUILD_CONTEXT} missing api/gateway/common" >&2 + exit 67 +fi +if [ ! -f "${BUILD_CONTEXT}/${AURORA_GATEWAY_DOCKERFILE}" ]; then + echo "[aurora-gateway-build] dockerfile not found in build context: ${AURORA_GATEWAY_DOCKERFILE}" >&2 + exit 68 +fi + +/kaniko/executor \ + --context "${BUILD_CONTEXT}" \ + --dockerfile "${AURORA_GATEWAY_DOCKERFILE}" \ + --destination "${REGISTRY_URL}/${AURORA_GATEWAY_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_VERSION="${APP_V}" \ + --build-arg GIT_REV="${GIT_REV}" \ + --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ + --build-arg BUILD_DATE="${BUILD_DATE}" \ + --build-arg BUILD_USER="${BUILD_USER}" \ + --single-snapshot diff --git a/ci/scripts/aurora/deploy.sh b/ci/scripts/aurora/deploy.sh new file mode 100644 index 00000000..6ed89d38 --- /dev/null +++ b/ci/scripts/aurora/deploy.sh @@ -0,0 +1,50 @@ +#!/bin/sh +set -eu + +if ! set -o pipefail 2>/dev/null; then + : +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "${REPO_ROOT}" + +sh ci/scripts/common/ensure_env_version.sh +. ci/scripts/common/runtime_env.sh +. ci/scripts/common/nats_env.sh + +normalize_env_file() { + file="$1" + tmp="${file}.tmp.$$" + tr -d '\r' <"$file" >"$tmp" + mv "$tmp" "$file" +} + +load_env_file() { + file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + export "$key=$value" + done <"$file" +} + +AURORA_GATEWAY_ENV_NAME="${AURORA_GATEWAY_ENV:-$(resolve_runtime_env_name)}" +load_runtime_env_bundle "${AURORA_GATEWAY_ENV_NAME}" + +AURORA_GATEWAY_MONGO_SECRET_PATH="${AURORA_GATEWAY_MONGO_SECRET_PATH:?missing AURORA_GATEWAY_MONGO_SECRET_PATH}" +AURORA_GATEWAY_NATS_SECRET_PATH="${AURORA_GATEWAY_NATS_SECRET_PATH:-sendico/nats}" + +export AURORA_GATEWAY_MONGO_USER="$(./ci/vlt kv_get kv "${AURORA_GATEWAY_MONGO_SECRET_PATH}" user)" +export AURORA_GATEWAY_MONGO_PASSWORD="$(./ci/vlt kv_get kv "${AURORA_GATEWAY_MONGO_SECRET_PATH}" password)" + +NATS_SECRET_PATH="${AURORA_GATEWAY_NATS_SECRET_PATH}" load_nats_env + +bash ci/prod/scripts/bootstrap/network.sh +sh ci/scripts/common/ensure_remote_registry_login.sh +bash ci/prod/scripts/deploy/aurora_gateway.sh diff --git a/ci/scripts/bff/build-image.sh b/ci/scripts/bff/build-image.sh index 28c79b6a..c2848c4a 100755 --- a/ci/scripts/bff/build-image.sh +++ b/ci/scripts/bff/build-image.sh @@ -37,6 +37,10 @@ BFF_ENV_NAME="${BFF_ENV:-$(resolve_runtime_env_name)}" load_runtime_env_bundle "${BFF_ENV_NAME}" IMAGE_TAG="$(compute_image_tag)" +BFF_CONFIG_PATH="api/edge/bff/config.yml" +if [ "${CI_RUNTIME_ENV_NAME:-prod}" = "devserver" ] && [ -f "${REPO_ROOT}/api/edge/bff/config.dev.yml" ]; then + BFF_CONFIG_PATH="api/edge/bff/config.dev.yml" +fi REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" APP_V="${APP_V:?missing APP_V}" @@ -69,6 +73,7 @@ fi --context "${BUILD_CONTEXT}" \ --dockerfile "${BFF_DOCKERFILE}" \ --destination "${REGISTRY_URL}/${BFF_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_CONFIG_PATH="${BFF_CONFIG_PATH}" \ --build-arg APP_VERSION="${APP_V}" \ --build-arg GIT_REV="${GIT_REV}" \ --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ diff --git a/ci/scripts/bff/deploy.sh b/ci/scripts/bff/deploy.sh index 17f5f53e..bee05bac 100755 --- a/ci/scripts/bff/deploy.sh +++ b/ci/scripts/bff/deploy.sh @@ -46,11 +46,13 @@ export MONGO_USER="$(./ci/vlt kv_get kv "${BFF_MONGO_SECRET_PATH}" user)" export MONGO_PASSWORD="$(./ci/vlt kv_get kv "${BFF_MONGO_SECRET_PATH}" password)" export API_ENDPOINT_SECRET="$(./ci/vlt kv_get kv "${BFF_API_SECRET_PATH}" secret)" -export BFF_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${BFF_VAULT_SECRET_PATH}" role_id)" -export BFF_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${BFF_VAULT_SECRET_PATH}" secret_id)" -if [ -z "${BFF_VAULT_ROLE_ID}" ] || [ -z "${BFF_VAULT_SECRET_ID}" ]; then - echo "[bff-deploy] vault approle creds are empty for path ${BFF_VAULT_SECRET_PATH}" >&2 - exit 1 +if [ "${CI_RUNTIME_ENV_NAME:-prod}" != "devserver" ]; then + export BFF_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${BFF_VAULT_SECRET_PATH}" role_id)" + export BFF_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${BFF_VAULT_SECRET_PATH}" secret_id)" + if [ -z "${BFF_VAULT_ROLE_ID}" ] || [ -z "${BFF_VAULT_SECRET_ID}" ]; then + echo "[bff-deploy] vault approle creds are empty for path ${BFF_VAULT_SECRET_PATH}" >&2 + exit 1 + fi fi load_nats_env diff --git a/ci/scripts/callbacks/build-image.sh b/ci/scripts/callbacks/build-image.sh index 805d1a61..9c6cf7d1 100755 --- a/ci/scripts/callbacks/build-image.sh +++ b/ci/scripts/callbacks/build-image.sh @@ -37,6 +37,10 @@ CALLBACKS_ENV_NAME="${CALLBACKS_ENV:-$(resolve_runtime_env_name)}" load_runtime_env_bundle "${CALLBACKS_ENV_NAME}" IMAGE_TAG="$(compute_image_tag)" +CALLBACKS_CONFIG_PATH="api/edge/callbacks/config.yml" +if [ "${CI_RUNTIME_ENV_NAME:-prod}" = "devserver" ] && [ -f "${REPO_ROOT}/api/edge/callbacks/config.dev.yml" ]; then + CALLBACKS_CONFIG_PATH="api/edge/callbacks/config.dev.yml" +fi REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" APP_V="${APP_V:?missing APP_V}" @@ -69,6 +73,7 @@ fi --context "${BUILD_CONTEXT}" \ --dockerfile "${CALLBACKS_DOCKERFILE}" \ --destination "${REGISTRY_URL}/${CALLBACKS_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_CONFIG_PATH="${CALLBACKS_CONFIG_PATH}" \ --build-arg APP_VERSION="${APP_V}" \ --build-arg GIT_REV="${GIT_REV}" \ --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ diff --git a/ci/scripts/callbacks/deploy.sh b/ci/scripts/callbacks/deploy.sh index 31840d71..d2408b56 100755 --- a/ci/scripts/callbacks/deploy.sh +++ b/ci/scripts/callbacks/deploy.sh @@ -43,11 +43,13 @@ CALLBACKS_VAULT_SECRET_PATH="${CALLBACKS_VAULT_SECRET_PATH:?missing CALLBACKS_VA export CALLBACKS_MONGO_USER="$(./ci/vlt kv_get kv "${CALLBACKS_MONGO_SECRET_PATH}" user)" export CALLBACKS_MONGO_PASSWORD="$(./ci/vlt kv_get kv "${CALLBACKS_MONGO_SECRET_PATH}" password)" -export CALLBACKS_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${CALLBACKS_VAULT_SECRET_PATH}" role_id)" -export CALLBACKS_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${CALLBACKS_VAULT_SECRET_PATH}" secret_id)" -if [ -z "${CALLBACKS_VAULT_ROLE_ID}" ] || [ -z "${CALLBACKS_VAULT_SECRET_ID}" ]; then - echo "[callbacks-deploy] vault approle creds are empty for path ${CALLBACKS_VAULT_SECRET_PATH}" >&2 - exit 1 +if [ "${CI_RUNTIME_ENV_NAME:-prod}" != "devserver" ]; then + export CALLBACKS_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${CALLBACKS_VAULT_SECRET_PATH}" role_id)" + export CALLBACKS_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${CALLBACKS_VAULT_SECRET_PATH}" secret_id)" + if [ -z "${CALLBACKS_VAULT_ROLE_ID}" ] || [ -z "${CALLBACKS_VAULT_SECRET_ID}" ]; then + echo "[callbacks-deploy] vault approle creds are empty for path ${CALLBACKS_VAULT_SECRET_PATH}" >&2 + exit 1 + fi fi load_nats_env diff --git a/ci/scripts/chain_gateway/build-image.sh b/ci/scripts/chain_gateway/build-image.sh index f352aac7..8382a4bd 100755 --- a/ci/scripts/chain_gateway/build-image.sh +++ b/ci/scripts/chain_gateway/build-image.sh @@ -37,6 +37,10 @@ CHAIN_GATEWAY_ENV_NAME="${CHAIN_GATEWAY_ENV:-$(resolve_runtime_env_name)}" load_runtime_env_bundle "${CHAIN_GATEWAY_ENV_NAME}" IMAGE_TAG="$(compute_image_tag)" +CHAIN_GATEWAY_CONFIG_PATH="api/gateway/chain/config.yml" +if [ "${CI_RUNTIME_ENV_NAME:-prod}" = "devserver" ] && [ -f "${REPO_ROOT}/api/gateway/chain/config.dev.yml" ]; then + CHAIN_GATEWAY_CONFIG_PATH="api/gateway/chain/config.dev.yml" +fi REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" APP_V="${APP_V:?missing APP_V}" @@ -86,6 +90,7 @@ fi --context "${BUILD_CONTEXT}" \ --dockerfile "${CHAIN_GATEWAY_DOCKERFILE}" \ --destination "${REGISTRY_URL}/${CHAIN_GATEWAY_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_CONFIG_PATH="${CHAIN_GATEWAY_CONFIG_PATH}" \ --build-arg APP_VERSION="${APP_V}" \ --build-arg GIT_REV="${GIT_REV}" \ --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ diff --git a/ci/scripts/chain_gateway/deploy.sh b/ci/scripts/chain_gateway/deploy.sh index 4b2ba259..c58356cf 100755 --- a/ci/scripts/chain_gateway/deploy.sh +++ b/ci/scripts/chain_gateway/deploy.sh @@ -51,11 +51,13 @@ export CHAIN_GATEWAY_RPC_URL="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_RPC_SECRET_P export CHAIN_GATEWAY_SERVICE_WALLET_KEY="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_WALLET_SECRET_PATH}" private_key)" export CHAIN_GATEWAY_SERVICE_WALLET_ADDRESS="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_WALLET_SECRET_PATH}" address || true)" -export CHAIN_GATEWAY_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_VAULT_SECRET_PATH}" role_id)" -export CHAIN_GATEWAY_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_VAULT_SECRET_PATH}" secret_id)" -if [ -z "${CHAIN_GATEWAY_VAULT_ROLE_ID}" ] || [ -z "${CHAIN_GATEWAY_VAULT_SECRET_ID}" ]; then - echo "[chain-gateway-deploy] vault approle creds are empty for path ${CHAIN_GATEWAY_VAULT_SECRET_PATH}" >&2 - exit 1 +if [ "${CI_RUNTIME_ENV_NAME:-prod}" != "devserver" ]; then + export CHAIN_GATEWAY_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_VAULT_SECRET_PATH}" role_id)" + export CHAIN_GATEWAY_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${CHAIN_GATEWAY_VAULT_SECRET_PATH}" secret_id)" + if [ -z "${CHAIN_GATEWAY_VAULT_ROLE_ID}" ] || [ -z "${CHAIN_GATEWAY_VAULT_SECRET_ID}" ]; then + echo "[chain-gateway-deploy] vault approle creds are empty for path ${CHAIN_GATEWAY_VAULT_SECRET_PATH}" >&2 + exit 1 + fi fi load_nats_env diff --git a/ci/scripts/chsettle/build-image.sh b/ci/scripts/chsettle/build-image.sh new file mode 100644 index 00000000..4b71c291 --- /dev/null +++ b/ci/scripts/chsettle/build-image.sh @@ -0,0 +1,94 @@ +#!/bin/sh +set -eu + +if ! set -o pipefail 2>/dev/null; then + : +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "${REPO_ROOT}" + +sh ci/scripts/common/ensure_env_version.sh +. ci/scripts/common/runtime_env.sh + +normalize_env_file() { + file="$1" + tmp="${file}.tmp.$$" + tr -d '\r' <"$file" >"$tmp" + mv "$tmp" "$file" +} + +load_env_file() { + file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + export "$key=$value" + done <"$file" +} + +CHSETTLE_GATEWAY_ENV_NAME="${CHSETTLE_GATEWAY_ENV:-$(resolve_runtime_env_name)}" +load_runtime_env_bundle "${CHSETTLE_GATEWAY_ENV_NAME}" + +IMAGE_TAG="$(compute_image_tag)" + +REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" +APP_V="${APP_V:?missing APP_V}" +CHSETTLE_GATEWAY_DOCKERFILE="${CHSETTLE_GATEWAY_DOCKERFILE:?missing CHSETTLE_GATEWAY_DOCKERFILE}" +CHSETTLE_GATEWAY_IMAGE_PATH="${CHSETTLE_GATEWAY_IMAGE_PATH:?missing CHSETTLE_GATEWAY_IMAGE_PATH}" + +REGISTRY_HOST="${REGISTRY_URL#http://}" +REGISTRY_HOST="${REGISTRY_HOST#https://}" +REGISTRY_USER="$(cat secrets/REGISTRY_USER)" +REGISTRY_PASSWORD="$(cat secrets/REGISTRY_PASSWORD)" +: "${REGISTRY_USER:?missing registry user}" +: "${REGISTRY_PASSWORD:?missing registry password}" + +mkdir -p /kaniko/.docker +AUTH_B64="$(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASSWORD" | base64 | tr -d '\n')" +cat </kaniko/.docker/config.json +{ + "auths": { + "https://${REGISTRY_HOST}": { "auth": "${AUTH_B64}" } + } +} +EOF + +BUILD_CONTEXT="${CHSETTLE_GATEWAY_BUILD_CONTEXT:-${WOODPECKER_WORKSPACE:-${CI_WORKSPACE:-${PWD:-/workspace}}}}" +if [ ! -d "${BUILD_CONTEXT}" ]; then + BUILD_CONTEXT="/workspace" +fi + +# Gateway modules use a local replace (../common); ensure build context contains shared code. +if [ ! -d "${BUILD_CONTEXT}/api/gateway/common" ] || [ ! -f "${BUILD_CONTEXT}/${CHSETTLE_GATEWAY_DOCKERFILE}" ]; then + if [ -d "${REPO_ROOT}/api/gateway/common" ] && [ -f "${REPO_ROOT}/${CHSETTLE_GATEWAY_DOCKERFILE}" ]; then + echo "[chsettle-gateway-build] build context ${BUILD_CONTEXT} is incomplete; falling back to ${REPO_ROOT}" >&2 + BUILD_CONTEXT="${REPO_ROOT}" + fi +fi + +if [ ! -d "${BUILD_CONTEXT}/api/gateway/common" ]; then + echo "[chsettle-gateway-build] build context ${BUILD_CONTEXT} missing api/gateway/common" >&2 + exit 67 +fi +if [ ! -f "${BUILD_CONTEXT}/${CHSETTLE_GATEWAY_DOCKERFILE}" ]; then + echo "[chsettle-gateway-build] dockerfile not found in build context: ${CHSETTLE_GATEWAY_DOCKERFILE}" >&2 + exit 68 +fi + +/kaniko/executor \ + --context "${BUILD_CONTEXT}" \ + --dockerfile "${CHSETTLE_GATEWAY_DOCKERFILE}" \ + --destination "${REGISTRY_URL}/${CHSETTLE_GATEWAY_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_VERSION="${APP_V}" \ + --build-arg GIT_REV="${GIT_REV}" \ + --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ + --build-arg BUILD_DATE="${BUILD_DATE}" \ + --build-arg BUILD_USER="${BUILD_USER}" \ + --single-snapshot diff --git a/ci/scripts/chsettle/deploy.sh b/ci/scripts/chsettle/deploy.sh new file mode 100644 index 00000000..c4b2b441 --- /dev/null +++ b/ci/scripts/chsettle/deploy.sh @@ -0,0 +1,50 @@ +#!/bin/sh +set -eu + +if ! set -o pipefail 2>/dev/null; then + : +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "${REPO_ROOT}" + +sh ci/scripts/common/ensure_env_version.sh +. ci/scripts/common/runtime_env.sh +. ci/scripts/common/nats_env.sh + +normalize_env_file() { + file="$1" + tmp="${file}.tmp.$$" + tr -d '\r' <"$file" >"$tmp" + mv "$tmp" "$file" +} + +load_env_file() { + file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + export "$key=$value" + done <"$file" +} + +CHSETTLE_GATEWAY_ENV_NAME="${CHSETTLE_GATEWAY_ENV:-$(resolve_runtime_env_name)}" +load_runtime_env_bundle "${CHSETTLE_GATEWAY_ENV_NAME}" + +CHSETTLE_GATEWAY_MONGO_SECRET_PATH="${CHSETTLE_GATEWAY_MONGO_SECRET_PATH:?missing CHSETTLE_GATEWAY_MONGO_SECRET_PATH}" +CHSETTLE_GATEWAY_NATS_SECRET_PATH="${CHSETTLE_GATEWAY_NATS_SECRET_PATH:-sendico/nats}" + +export CHSETTLE_GATEWAY_MONGO_USER="$(./ci/vlt kv_get kv "${CHSETTLE_GATEWAY_MONGO_SECRET_PATH}" user)" +export CHSETTLE_GATEWAY_MONGO_PASSWORD="$(./ci/vlt kv_get kv "${CHSETTLE_GATEWAY_MONGO_SECRET_PATH}" password)" + +NATS_SECRET_PATH="${CHSETTLE_GATEWAY_NATS_SECRET_PATH}" load_nats_env + +bash ci/prod/scripts/bootstrap/network.sh +sh ci/scripts/common/ensure_remote_registry_login.sh +bash ci/prod/scripts/deploy/chsettle_gateway.sh diff --git a/ci/scripts/common/backend_modules.sh b/ci/scripts/common/backend_modules.sh index 5532a1b0..eda07aec 100755 --- a/ci/scripts/common/backend_modules.sh +++ b/ci/scripts/common/backend_modules.sh @@ -57,6 +57,18 @@ EOF cat <<'EOF' api/pkg api/gateway/mntx +EOF + ;; + gateway_aurora) + cat <<'EOF' +api/pkg +api/gateway/aurora +EOF + ;; + gateway_chsettle) + cat <<'EOF' +api/pkg +api/gateway/chsettle EOF ;; gateway_tgsettle) diff --git a/ci/scripts/common/runtime_env.sh b/ci/scripts/common/runtime_env.sh index df9fcf91..c9176ffb 100644 --- a/ci/scripts/common/runtime_env.sh +++ b/ci/scripts/common/runtime_env.sh @@ -86,6 +86,16 @@ load_runtime_env_bundle() { env_name="$1" runtime_file="$(resolve_runtime_env_file "${env_name}")" + if [ -n "${VAULT_ADDR:-}" ] && [ -z "${CI_VAULT_ADDR:-}" ]; then + export CI_VAULT_ADDR="${VAULT_ADDR}" + fi + if [ -n "${VAULT_ROLE_ID:-}" ] && [ -z "${CI_VAULT_ROLE_ID:-}" ]; then + export CI_VAULT_ROLE_ID="${VAULT_ROLE_ID}" + fi + if [ -n "${VAULT_SECRET_ID:-}" ] && [ -z "${CI_VAULT_SECRET_ID:-}" ]; then + export CI_VAULT_SECRET_ID="${VAULT_SECRET_ID}" + fi + normalize_env_file "${runtime_file}" normalize_env_file ./.env.version diff --git a/ci/scripts/tron_gateway/build-image.sh b/ci/scripts/tron_gateway/build-image.sh index bd0a7ab4..ff93fbb9 100755 --- a/ci/scripts/tron_gateway/build-image.sh +++ b/ci/scripts/tron_gateway/build-image.sh @@ -37,6 +37,10 @@ TRON_GATEWAY_ENV_NAME="${TRON_GATEWAY_ENV:-$(resolve_runtime_env_name)}" load_runtime_env_bundle "${TRON_GATEWAY_ENV_NAME}" IMAGE_TAG="$(compute_image_tag)" +TRON_GATEWAY_CONFIG_PATH="api/gateway/tron/config.yml" +if [ "${CI_RUNTIME_ENV_NAME:-prod}" = "devserver" ] && [ -f "${REPO_ROOT}/api/gateway/tron/config.dev.yml" ]; then + TRON_GATEWAY_CONFIG_PATH="api/gateway/tron/config.dev.yml" +fi REGISTRY_URL="${REGISTRY_URL:?missing REGISTRY_URL}" APP_V="${APP_V:?missing APP_V}" @@ -86,6 +90,7 @@ fi --context "${BUILD_CONTEXT}" \ --dockerfile "${TRON_GATEWAY_DOCKERFILE}" \ --destination "${REGISTRY_URL}/${TRON_GATEWAY_IMAGE_PATH}:${IMAGE_TAG}" \ + --build-arg APP_CONFIG_PATH="${TRON_GATEWAY_CONFIG_PATH}" \ --build-arg APP_VERSION="${APP_V}" \ --build-arg GIT_REV="${GIT_REV}" \ --build-arg BUILD_BRANCH="${BUILD_BRANCH}" \ diff --git a/ci/scripts/tron_gateway/deploy.sh b/ci/scripts/tron_gateway/deploy.sh index 8b083404..b7503037 100755 --- a/ci/scripts/tron_gateway/deploy.sh +++ b/ci/scripts/tron_gateway/deploy.sh @@ -53,11 +53,13 @@ export TRON_GATEWAY_GRPC_TOKEN="$(./ci/vlt kv_get kv "${TRON_GATEWAY_RPC_SECRET_ export TRON_GATEWAY_SERVICE_WALLET_KEY="$(./ci/vlt kv_get kv "${TRON_GATEWAY_WALLET_SECRET_PATH}" private_key)" export TRON_GATEWAY_SERVICE_WALLET_ADDRESS="$(./ci/vlt kv_get kv "${TRON_GATEWAY_WALLET_SECRET_PATH}" address || true)" -export TRON_GATEWAY_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${TRON_GATEWAY_VAULT_SECRET_PATH}" role_id)" -export TRON_GATEWAY_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${TRON_GATEWAY_VAULT_SECRET_PATH}" secret_id)" -if [ -z "${TRON_GATEWAY_VAULT_ROLE_ID}" ] || [ -z "${TRON_GATEWAY_VAULT_SECRET_ID}" ]; then - echo "[tron-gateway-deploy] vault approle creds are empty for path ${TRON_GATEWAY_VAULT_SECRET_PATH}" >&2 - exit 1 +if [ "${CI_RUNTIME_ENV_NAME:-prod}" != "devserver" ]; then + export TRON_GATEWAY_VAULT_ROLE_ID="$(./ci/vlt kv_get kv "${TRON_GATEWAY_VAULT_SECRET_PATH}" role_id)" + export TRON_GATEWAY_VAULT_SECRET_ID="$(./ci/vlt kv_get kv "${TRON_GATEWAY_VAULT_SECRET_PATH}" secret_id)" + if [ -z "${TRON_GATEWAY_VAULT_ROLE_ID}" ] || [ -z "${TRON_GATEWAY_VAULT_SECRET_ID}" ]; then + echo "[tron-gateway-deploy] vault approle creds are empty for path ${TRON_GATEWAY_VAULT_SECRET_PATH}" >&2 + exit 1 + fi fi load_nats_env diff --git a/ci/scripts/vault/deploy.sh b/ci/scripts/vault/deploy.sh new file mode 100644 index 00000000..fdb4b247 --- /dev/null +++ b/ci/scripts/vault/deploy.sh @@ -0,0 +1,38 @@ +#!/bin/sh +set -eu + +if ! set -o pipefail 2>/dev/null; then + : +fi + +REPO_ROOT="$(cd "$(dirname "$0")/../../.." && pwd)" +cd "${REPO_ROOT}" + +. ci/scripts/common/runtime_env.sh + +normalize_env_file() { + file="$1" + tmp="${file}.tmp.$$" + tr -d '\r' <"$file" >"$tmp" + mv "$tmp" "$file" +} + +load_env_file() { + file="$1" + while IFS= read -r line || [ -n "$line" ]; do + case "$line" in + ''|\#*) continue ;; + esac + key="${line%%=*}" + value="${line#*=}" + key="$(printf '%s' "$key" | tr -d '[:space:]')" + value="${value#"${value%%[![:space:]]*}"}" + value="${value%"${value##*[![:space:]]}"}" + export "$key=$value" + done <"$file" +} + +VAULT_ENV_NAME="${VAULT_ENV:-$(resolve_runtime_env_name)}" +load_runtime_env_bundle "${VAULT_ENV_NAME}" + +bash ci/prod/scripts/deploy/vault.sh diff --git a/ci/vlt b/ci/vlt index 4891673a..73563e9e 100755 --- a/ci/vlt +++ b/ci/vlt @@ -3,19 +3,23 @@ # Requires: curl, sed. Uses VAULT_ADDR, VAULT_ROLE_ID, VAULT_SECRET_ID from env. set -euo pipefail -: "${VAULT_ADDR:?missing VAULT_ADDR}" +VLT_VAULT_ADDR="${CI_VAULT_ADDR:-${VAULT_ADDR:-}}" +VLT_VAULT_ROLE_ID="${CI_VAULT_ROLE_ID:-${VAULT_ROLE_ID:-}}" +VLT_VAULT_SECRET_ID="${CI_VAULT_SECRET_ID:-${VAULT_SECRET_ID:-}}" + +: "${VLT_VAULT_ADDR:?missing VAULT_ADDR}" VAULT_TOKEN_FILE="${VAULT_TOKEN_FILE:-.vault_token}" log(){ printf '[vlt] %s\n' "$*" >&2; } login() { - : "${VAULT_ROLE_ID:?missing VAULT_ROLE_ID}" - : "${VAULT_SECRET_ID:?missing VAULT_SECRET_ID}" + : "${VLT_VAULT_ROLE_ID:?missing VAULT_ROLE_ID}" + : "${VLT_VAULT_SECRET_ID:?missing VAULT_SECRET_ID}" log "login approle" resp="$(curl -sfS -X POST -H 'Content-Type: application/json' \ --connect-timeout 5 --max-time 20 \ - -d "{\"role_id\":\"${VAULT_ROLE_ID}\",\"secret_id\":\"${VAULT_SECRET_ID}\"}" \ - "${VAULT_ADDR%/}/v1/auth/approle/login")" + -d "{\"role_id\":\"${VLT_VAULT_ROLE_ID}\",\"secret_id\":\"${VLT_VAULT_SECRET_ID}\"}" \ + "${VLT_VAULT_ADDR%/}/v1/auth/approle/login")" token="$(printf '%s' "$resp" | sed -n 's/.*"client_token"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')" [ -n "$token" ] || { echo "login failed" >&2; exit 1; } printf '%s' "$token" > "$VAULT_TOKEN_FILE" @@ -34,7 +38,7 @@ ensure_token() { kv_get() { mount="$1"; path="$2"; field="$3" ensure_token - url="${VAULT_ADDR%/}/v1/${mount}/data/${path}" + url="${VLT_VAULT_ADDR%/}/v1/${mount}/data/${path}" resp="$(curl -sfS --connect-timeout 5 --max-time 20 -H "X-Vault-Token: ${VAULT_TOKEN}" "$url")" RESP="$resp" python3 - "$field" <<'PY' import json, os, sys -- 2.49.1