c3a1a8b4a1
Capture current PCP architecture notes, service-map prototypes, TGU operations UI/map work, local configuration updates, database helper scripts, and request/sample JSON artifacts.
286 lines
6.6 KiB
Bash
Executable File
286 lines
6.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
NAMESPACE="${NAMESPACE:-pcp-dev}"
|
|
POSTGRES_SERVICE="${POSTGRES_SERVICE:-pcp-postgres}"
|
|
CLUSTER_DOMAIN="${CLUSTER_DOMAIN:-altum.local}"
|
|
POSTGRES_IMAGE="${POSTGRES_IMAGE:-postgis/postgis:15-3.3}"
|
|
ADMIN_SECRET="${ADMIN_SECRET:-pcp-postgres-admin}"
|
|
ADMIN_PASSWORD_KEY="${ADMIN_PASSWORD_KEY:-POSTGRES_PASSWORD}"
|
|
|
|
POSTGRES_HOST="${POSTGRES_SERVICE}.${NAMESPACE}.svc.${CLUSTER_DOMAIN}"
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
Usage:
|
|
$0 <db_name> [db_name...]
|
|
|
|
$0 -f databases.txt
|
|
|
|
Examples:
|
|
$0 pcp_satellite_catalog pcp_ballistics
|
|
|
|
cat > databases.txt <<LIST
|
|
pcp_satellite_catalog
|
|
pcp_ballistics
|
|
pcp_requests
|
|
LIST
|
|
|
|
$0 -f databases.txt
|
|
|
|
Environment overrides:
|
|
NAMESPACE=pcp-dev
|
|
POSTGRES_SERVICE=pcp-postgres
|
|
CLUSTER_DOMAIN=altum.local
|
|
POSTGRES_IMAGE=postgis/postgis:15-3.3
|
|
EOF
|
|
}
|
|
|
|
require_command() {
|
|
if ! command -v "$1" >/dev/null 2>&1; then
|
|
echo "ERROR: required command not found: $1" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
validate_db_name() {
|
|
local db_name="$1"
|
|
|
|
if [[ ! "$db_name" =~ ^[a-z][a-z0-9_]*$ ]]; then
|
|
echo "ERROR: invalid database name: $db_name" >&2
|
|
echo "Allowed format: lowercase letters, digits, underscore; must start with a letter." >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
secret_name_for_db() {
|
|
local db_name="$1"
|
|
local dashed="${db_name//_/-}"
|
|
echo "${dashed}-db"
|
|
}
|
|
|
|
user_name_for_db() {
|
|
local db_name="$1"
|
|
echo "${db_name}_user"
|
|
}
|
|
|
|
make_pod_name() {
|
|
local prefix="$1"
|
|
local db_name="$2"
|
|
local safe="${db_name//_/-}"
|
|
safe="${safe:0:35}"
|
|
echo "${prefix}-${safe}-${RANDOM}${RANDOM}"
|
|
}
|
|
|
|
get_admin_password() {
|
|
kubectl -n "$NAMESPACE" get secret "$ADMIN_SECRET" \
|
|
-o jsonpath="{.data.${ADMIN_PASSWORD_KEY}}" | base64 -d
|
|
}
|
|
|
|
get_or_create_service_password() {
|
|
local secret_name="$1"
|
|
|
|
if kubectl -n "$NAMESPACE" get secret "$secret_name" >/dev/null 2>&1; then
|
|
kubectl -n "$NAMESPACE" get secret "$secret_name" \
|
|
-o jsonpath='{.data.SPRING_DATASOURCE_PASSWORD}' | base64 -d
|
|
else
|
|
openssl rand -base64 32 | tr -d '/+=' | head -c 32
|
|
fi
|
|
}
|
|
|
|
create_database_and_user() {
|
|
local db_name="$1"
|
|
local db_user="$2"
|
|
local db_password="$3"
|
|
local admin_password="$4"
|
|
|
|
local pod_name
|
|
pod_name="$(make_pod_name "psql-create" "$db_name")"
|
|
|
|
echo "Creating/updating database and user:"
|
|
echo " database: $db_name"
|
|
echo " user: $db_user"
|
|
|
|
kubectl -n "$NAMESPACE" run --rm -i "$pod_name" \
|
|
--image="$POSTGRES_IMAGE" \
|
|
--restart=Never \
|
|
--env="PGPASSWORD=$admin_password" \
|
|
--command -- psql \
|
|
-h "$POSTGRES_HOST" \
|
|
-U postgres \
|
|
-d postgres <<SQL
|
|
DO \$\$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT FROM pg_roles WHERE rolname = '$db_user'
|
|
) THEN
|
|
CREATE ROLE "$db_user" LOGIN PASSWORD '$db_password';
|
|
ELSE
|
|
ALTER ROLE "$db_user" WITH LOGIN PASSWORD '$db_password';
|
|
END IF;
|
|
END
|
|
\$\$;
|
|
|
|
SELECT 'CREATE DATABASE "$db_name" OWNER "$db_user"'
|
|
WHERE NOT EXISTS (
|
|
SELECT FROM pg_database WHERE datname = '$db_name'
|
|
) \gexec
|
|
|
|
ALTER DATABASE "$db_name" OWNER TO "$db_user";
|
|
GRANT CONNECT ON DATABASE "$db_name" TO "$db_user";
|
|
SQL
|
|
}
|
|
|
|
enable_postgis() {
|
|
local db_name="$1"
|
|
local db_user="$2"
|
|
local admin_password="$3"
|
|
|
|
local pod_name
|
|
pod_name="$(make_pod_name "psql-postgis" "$db_name")"
|
|
|
|
echo "Enabling PostGIS:"
|
|
echo " database: $db_name"
|
|
|
|
kubectl -n "$NAMESPACE" run --rm -i "$pod_name" \
|
|
--image="$POSTGRES_IMAGE" \
|
|
--restart=Never \
|
|
--env="PGPASSWORD=$admin_password" \
|
|
--command -- psql \
|
|
-h "$POSTGRES_HOST" \
|
|
-U postgres \
|
|
-d "$db_name" <<SQL
|
|
CREATE EXTENSION IF NOT EXISTS postgis;
|
|
CREATE EXTENSION IF NOT EXISTS postgis_topology;
|
|
|
|
ALTER SCHEMA public OWNER TO "$db_user";
|
|
GRANT USAGE, CREATE ON SCHEMA public TO "$db_user";
|
|
SQL
|
|
}
|
|
|
|
create_or_update_secret() {
|
|
local db_name="$1"
|
|
local db_user="$2"
|
|
local db_password="$3"
|
|
local secret_name="$4"
|
|
|
|
echo "Creating/updating Kubernetes Secret:"
|
|
echo " secret: $secret_name"
|
|
|
|
kubectl -n "$NAMESPACE" create secret generic "$secret_name" \
|
|
--from-literal=SPRING_DATASOURCE_USERNAME="$db_user" \
|
|
--from-literal=SPRING_DATASOURCE_PASSWORD="$db_password" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
}
|
|
|
|
check_database() {
|
|
local db_name="$1"
|
|
local db_user="$2"
|
|
local db_password="$3"
|
|
|
|
local pod_name
|
|
pod_name="$(make_pod_name "psql-check" "$db_name")"
|
|
|
|
echo "Checking database:"
|
|
echo " database: $db_name"
|
|
echo " user: $db_user"
|
|
|
|
kubectl -n "$NAMESPACE" run --rm -i "$pod_name" \
|
|
--image="$POSTGRES_IMAGE" \
|
|
--restart=Never \
|
|
--env="PGPASSWORD=$db_password" \
|
|
--command -- psql \
|
|
-h "$POSTGRES_HOST" \
|
|
-U "$db_user" \
|
|
-d "$db_name" \
|
|
-c 'select current_database(), current_user, postgis_full_version();'
|
|
}
|
|
|
|
read_db_list() {
|
|
if [[ $# -eq 0 ]]; then
|
|
usage
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
|
usage
|
|
exit 0
|
|
fi
|
|
|
|
if [[ "$1" == "-f" || "$1" == "--file" ]]; then
|
|
if [[ $# -ne 2 ]]; then
|
|
echo "ERROR: file path is required after $1" >&2
|
|
exit 1
|
|
fi
|
|
|
|
local file="$2"
|
|
|
|
if [[ ! -f "$file" ]]; then
|
|
echo "ERROR: file not found: $file" >&2
|
|
exit 1
|
|
fi
|
|
|
|
grep -vE '^\s*($|#)' "$file"
|
|
else
|
|
printf '%s\n' "$@"
|
|
fi
|
|
}
|
|
|
|
main() {
|
|
require_command kubectl
|
|
require_command openssl
|
|
require_command base64
|
|
|
|
echo "Namespace: $NAMESPACE"
|
|
echo "Postgres host: $POSTGRES_HOST"
|
|
echo "Postgres image: $POSTGRES_IMAGE"
|
|
echo "Admin secret: $ADMIN_SECRET"
|
|
echo
|
|
|
|
local admin_password
|
|
admin_password="$(get_admin_password)"
|
|
|
|
mapfile -t databases < <(read_db_list "$@")
|
|
|
|
if [[ "${#databases[@]}" -eq 0 ]]; then
|
|
echo "ERROR: database list is empty" >&2
|
|
exit 1
|
|
fi
|
|
|
|
for db_name in "${databases[@]}"; do
|
|
validate_db_name "$db_name"
|
|
|
|
local db_user
|
|
local secret_name
|
|
local db_password
|
|
local jdbc_url
|
|
|
|
db_user="$(user_name_for_db "$db_name")"
|
|
secret_name="$(secret_name_for_db "$db_name")"
|
|
db_password="$(get_or_create_service_password "$secret_name")"
|
|
jdbc_url="jdbc:postgresql://${POSTGRES_HOST}:5432/${db_name}"
|
|
|
|
echo
|
|
echo "============================================================"
|
|
echo "Processing database: $db_name"
|
|
echo "============================================================"
|
|
|
|
create_database_and_user "$db_name" "$db_user" "$db_password" "$admin_password"
|
|
enable_postgis "$db_name" "$db_user" "$admin_password"
|
|
create_or_update_secret "$db_name" "$db_user" "$db_password" "$secret_name"
|
|
check_database "$db_name" "$db_user" "$db_password"
|
|
|
|
echo
|
|
echo "Done:"
|
|
echo " database: $db_name"
|
|
echo " user: $db_user"
|
|
echo " secret: $secret_name"
|
|
echo " JDBC URL: $jdbc_url"
|
|
done
|
|
|
|
echo
|
|
echo "All databases processed successfully."
|
|
}
|
|
|
|
main "$@"
|