feat: jpa, messaging, notification, mongo, graphql 어댑터터 리펙토링
This commit is contained in:
Executable
+72
@@ -0,0 +1,72 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# Install capability schema streams. Installation only — promotion is a separate step, and a
|
||||
# separate container, because it is a separate decision.
|
||||
#
|
||||
# Capability streams are not one Flyway run. Each of db/migration/jpa/* declares its own V1 and keeps
|
||||
# its own history table, so pointing a single Flyway at all of them fails with "Found more than one
|
||||
# migration with version 1" — which is what nine Compose lanes discovered the moment Wave 2 stopped
|
||||
# PostgreSqlPersistenceConfig from discarding spring.flyway.locations.
|
||||
#
|
||||
# Two things come first regardless of what was requested, and the order between them is not a
|
||||
# preference:
|
||||
#
|
||||
# 1. db/migration/postgresql, the application's own stream, into the default flyway_schema_history.
|
||||
# The application ships baseline-on-migrate: false as policy (FLYWAY-C6, and re-enabling it under
|
||||
# prod is a boot failure), so it refuses to start against a schema that has tables but no history
|
||||
# table of its own. Installing any capability stream before this one produces exactly that state:
|
||||
# the lane's first run applied ten notification migrations and then the application refused with
|
||||
# "Found non-empty schema(s) but no schema history table" — correctly.
|
||||
# 2. db/migration/jpa/core, which creates capability_schema_registry, the table every other stream
|
||||
# registers itself into.
|
||||
#
|
||||
# Each stream registers itself INSTALLED_INACTIVE. Nothing here promotes anything — a table existing
|
||||
# is not the same as a capability being sanctioned to use it, and this image has no psql to blur the
|
||||
# two with even if that were wanted. infra/postgres/promote-capability-streams.sh is the operator
|
||||
# half, and NotificationSchemaActivation refusing startup until it has run is the fail-closed third.
|
||||
#
|
||||
# CAPABILITY_STREAMS is a space-separated list of directory names under db/migration/jpa.
|
||||
# =============================================================================
|
||||
set -eu
|
||||
|
||||
: "${PGHOST:?PGHOST is required}"
|
||||
: "${PGUSER:?PGUSER is required}"
|
||||
: "${PGDATABASE:?PGDATABASE is required}"
|
||||
: "${CAPABILITY_STREAMS:=}"
|
||||
|
||||
MIGRATIONS=/flyway/sql
|
||||
JDBC="jdbc:postgresql://${PGHOST}:${PGPORT:-5432}/${PGDATABASE}"
|
||||
|
||||
run_flyway() {
|
||||
location="$1"
|
||||
history="$2"
|
||||
baseline="$3"
|
||||
[ -d "${MIGRATIONS}/${location}" ] || {
|
||||
echo "capability-streams: no such stream 'db/migration/${location}'" >&2
|
||||
exit 1
|
||||
}
|
||||
echo "capability-streams: applying ${location} into ${history}"
|
||||
# shellcheck disable=SC2086
|
||||
flyway \
|
||||
-url="${JDBC}" -user="${PGUSER}" -password="${PGPASSWORD:-}" \
|
||||
-locations="filesystem:${MIGRATIONS}/${location}" \
|
||||
-table="${history}" \
|
||||
${baseline} \
|
||||
migrate
|
||||
}
|
||||
|
||||
apply_stream() {
|
||||
stream="$1"
|
||||
# Capability streams baseline at 0 because each is installed into a database the core stream has
|
||||
# already put tables in; the application's own stream must not, for the reason above.
|
||||
run_flyway "jpa/${stream}" "flyway_jpa_$(echo "${stream}" | tr '-' '_')_history" \
|
||||
"-baselineOnMigrate=true -baselineVersion=0"
|
||||
}
|
||||
|
||||
run_flyway postgresql flyway_schema_history ""
|
||||
apply_stream core
|
||||
for stream in ${CAPABILITY_STREAMS}; do
|
||||
apply_stream "${stream}"
|
||||
done
|
||||
|
||||
echo "capability-streams: installed postgresql, core, [${CAPABILITY_STREAMS}]; none promoted"
|
||||
Executable
+37
@@ -0,0 +1,37 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# Installs the server certificate where PostgreSQL will accept it, then hands over.
|
||||
#
|
||||
# PostgreSQL refuses to start if the private key is group- or world-readable, and it reads the key
|
||||
# as the `postgres` user — uid 70 in the Alpine image. The certificate is generated on the host by
|
||||
# the qualification wrapper, so it arrives owned by whoever ran the script; a bind mount preserves
|
||||
# that ownership, and the two facts together mean a mounted key is either unreadable by postgres or
|
||||
# too permissive for it. Neither is fixable from the outside.
|
||||
#
|
||||
# So the key is copied, once, at the only moment this container is still root: before the official
|
||||
# entrypoint gosu's down to postgres. The copy lives on the container filesystem, not on the mount,
|
||||
# and the mount stays read-only.
|
||||
#
|
||||
# The same problem, the same shape as the Keycloak client secret and the MinIO smoke client. It is
|
||||
# worth stating plainly: bind-mounted credentials and per-image uids do not compose, and every
|
||||
# service that needs one has to say how it bridges them.
|
||||
# =============================================================================
|
||||
set -eu
|
||||
|
||||
TLS_SOURCE="${POSTGRES_TLS_DIR:-/opt/postgres-tls}"
|
||||
TLS_TARGET=/etc/postgresql-tls
|
||||
|
||||
if [ -f "${TLS_SOURCE}/server.key" ] && [ -f "${TLS_SOURCE}/server.crt" ]; then
|
||||
mkdir -p "${TLS_TARGET}"
|
||||
cp "${TLS_SOURCE}/server.key" "${TLS_TARGET}/server.key"
|
||||
cp "${TLS_SOURCE}/server.crt" "${TLS_TARGET}/server.crt"
|
||||
chown -R postgres:postgres "${TLS_TARGET}"
|
||||
chmod 0700 "${TLS_TARGET}"
|
||||
chmod 0600 "${TLS_TARGET}/server.key"
|
||||
chmod 0644 "${TLS_TARGET}/server.crt"
|
||||
else
|
||||
echo "postgres-entrypoint: no certificate at ${TLS_SOURCE}; refusing to start a TLS lane without one" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exec docker-entrypoint.sh "$@"
|
||||
Executable
+55
@@ -0,0 +1,55 @@
|
||||
#!/bin/sh
|
||||
# =============================================================================
|
||||
# The operator half: sanction installed capability schemas for use.
|
||||
#
|
||||
# apply-capability-streams.sh installs; this promotes. They are two scripts on two images because
|
||||
# they are two decisions, and because the Flyway image ships no psql — so a promotion that lived
|
||||
# inside the migration step could not have run at all, which is how this split was found.
|
||||
#
|
||||
# Promotion is an UPDATE rather than a migration on purpose. A stream that promoted itself would make
|
||||
# "the tables exist" and "an operator sanctioned this capability" indistinguishable, and the second is
|
||||
# the one NotificationSchemaActivation refuses to start without.
|
||||
#
|
||||
# CAPABILITY_STREAMS is a space-separated list of directory names under db/migration/jpa; the ids are
|
||||
# mapped explicitly below because they are not derivable from the directory names.
|
||||
# =============================================================================
|
||||
set -eu
|
||||
|
||||
: "${PGHOST:?PGHOST is required}"
|
||||
: "${PGUSER:?PGUSER is required}"
|
||||
: "${PGDATABASE:?PGDATABASE is required}"
|
||||
: "${CAPABILITY_STREAMS:=}"
|
||||
|
||||
promote() {
|
||||
capability="$1"
|
||||
echo "capability-streams: promoting ${capability}"
|
||||
# A promotion that matched no row would leave the capability inactive and be reported as success,
|
||||
# so the row count is checked rather than the exit status. That failure mode is the whole reason
|
||||
# this step exists: it would surface much later as a startup refusal about a capability the lane
|
||||
# believed it had promoted.
|
||||
updated="$(psql -v ON_ERROR_STOP=1 -qtAX -h "${PGHOST}" -U "${PGUSER}" -d "${PGDATABASE}" -c \
|
||||
"update capability_schema_registry set lifecycle_state = 'ACTIVE'
|
||||
where capability_id = '${capability}' returning capability_id" | wc -l)"
|
||||
if [ "${updated}" -ne 1 ]; then
|
||||
echo "capability-streams: ${capability} is not installed; nothing was promoted" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
promote jpa-flyway-migration
|
||||
|
||||
# Capability ids are not derivable from directory names — jpa/notification-platform registers
|
||||
# jpa-notification-platform-v4 — so each stream a lane asks for is named here rather than guessed. An
|
||||
# unmapped stream fails loudly instead of being installed and left inactive.
|
||||
for stream in ${CAPABILITY_STREAMS}; do
|
||||
case "${stream}" in
|
||||
notification-platform) promote jpa-notification-platform-v4 ;;
|
||||
fileserver) promote jpa-fileserver-metadata-v1 ;;
|
||||
*)
|
||||
echo "capability-streams: no promotion mapping for stream '${stream}'" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo "capability-streams: promoted core [${CAPABILITY_STREAMS}]"
|
||||
@@ -0,0 +1,5 @@
|
||||
# Generated per qualification run and removed on teardown: a CA, a server keypair for the host name
|
||||
# `db`, and nothing that outlives the lane. A committed test certificate is still a private key in
|
||||
# Git, and "it is only for smoke tests" is not something a scanner or a fork can tell.
|
||||
*
|
||||
!.gitignore
|
||||
Reference in New Issue
Block a user