120 lines
5.4 KiB
Bash
120 lines
5.4 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
: "${DEPLOYMENT_ENVIRONMENT:?DEPLOYMENT_ENVIRONMENT is required}"
|
|
: "${KUBE_NAMESPACE:?KUBE_NAMESPACE is required}"
|
|
: "${PUBLIC_API_ORIGIN:?PUBLIC_API_ORIGIN is required}"
|
|
: "${MONGODB_CONNECTION_STRING:?MONGODB_CONNECTION_STRING is required}"
|
|
: "${AUTH_JWT_KEY:?AUTH_JWT_KEY is required}"
|
|
: "${INTERNAL_PURGE_SECRET:?INTERNAL_PURGE_SECRET is required}"
|
|
: "${STRIPE_SECRET_KEY:?STRIPE_SECRET_KEY is required}"
|
|
: "${STRIPE_WEBHOOK_SECRET:?STRIPE_WEBHOOK_SECRET is required}"
|
|
: "${STRIPE_ACCESS_WEBHOOK_SECRET:?STRIPE_ACCESS_WEBHOOK_SECRET is required}"
|
|
|
|
NODES="${NODES:-192.168.86.72 192.168.86.73 192.168.86.74}"
|
|
BUILD_TAG="$(git rev-parse HEAD)"
|
|
API_IMAGE="step3-questions:${BUILD_TAG}"
|
|
WEB_IMAGE="step3-web:${BUILD_TAG}"
|
|
TEST_IMAGE="step3-tests:${BUILD_TAG}"
|
|
API_TAR="/tmp/step3-questions-${DEPLOYMENT_ENVIRONMENT}.tar"
|
|
WEB_TAR="/tmp/step3-web-${DEPLOYMENT_ENVIRONMENT}.tar"
|
|
|
|
cleanup() {
|
|
rm -f "${API_TAR}" "${WEB_TAR}"
|
|
docker image rm "${API_IMAGE}" "${WEB_IMAGE}" "${TEST_IMAGE}" 2>/dev/null || true
|
|
docker image prune -f >/dev/null || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
rm -f "${API_TAR}" "${WEB_TAR}"
|
|
docker container prune -f
|
|
docker image prune -f
|
|
docker image prune -af --filter "until=24h"
|
|
docker builder prune -af || true
|
|
docker system df
|
|
df -h
|
|
|
|
docker build -f api/QuestionsApi.Tests/Dockerfile -t "${TEST_IMAGE}" .
|
|
docker build --build-arg BUILD_REVISION="${BUILD_TAG}" -f api/QuestionsApi/Dockerfile -t "${API_IMAGE}" .
|
|
docker build \
|
|
--build-arg BUILD_REVISION="${BUILD_TAG}" \
|
|
--build-arg DEPLOYMENT_ENVIRONMENT="${DEPLOYMENT_ENVIRONMENT}" \
|
|
--build-arg PUBLIC_API_ORIGIN="${PUBLIC_API_ORIGIN}" \
|
|
-f web/Dockerfile -t "${WEB_IMAGE}" .
|
|
|
|
docker save "${API_IMAGE}" -o "${API_TAR}"
|
|
docker save "${WEB_IMAGE}" -o "${WEB_TAR}"
|
|
for node in ${NODES}; do
|
|
scp -o StrictHostKeyChecking=no "${API_TAR}" hz@"${node}":/tmp/step3-questions.tar
|
|
scp -o StrictHostKeyChecking=no "${WEB_TAR}" hz@"${node}":/tmp/step3-web.tar
|
|
ssh -o StrictHostKeyChecking=no hz@"${node}" \
|
|
"sudo ctr -n k8s.io images import /tmp/step3-questions.tar && sudo ctr -n k8s.io images import /tmp/step3-web.tar && rm -f /tmp/step3-questions.tar /tmp/step3-web.tar"
|
|
done
|
|
|
|
kubectl apply -f "k8s/overlays/${DEPLOYMENT_ENVIRONMENT}/namespace.yaml"
|
|
kubectl create secret generic step3-runtime-secrets \
|
|
--from-literal=mongodb-connection-string="${MONGODB_CONNECTION_STRING}" \
|
|
--from-literal=jwt-key="${AUTH_JWT_KEY}" \
|
|
--from-literal=purge-secret="${INTERNAL_PURGE_SECRET}" \
|
|
--from-literal=stripe-secret-key="${STRIPE_SECRET_KEY}" \
|
|
--from-literal=stripe-webhook-secret="${STRIPE_WEBHOOK_SECRET}" \
|
|
--from-literal=stripe-access-webhook-secret="${STRIPE_ACCESS_WEBHOOK_SECRET}" \
|
|
--from-literal=google-client-id="${GOOGLE_CLIENT_ID:-}" \
|
|
--from-literal=google-client-secret="${GOOGLE_CLIENT_SECRET:-}" \
|
|
--dry-run=client -o yaml | kubectl apply -n "${KUBE_NAMESPACE}" -f -
|
|
|
|
kubectl kustomize "k8s/overlays/${DEPLOYMENT_ENVIRONMENT}" \
|
|
| sed "s|step3-questions:latest|${API_IMAGE}|g; s|step3-web:latest|${WEB_IMAGE}|g" \
|
|
| kubectl apply -f -
|
|
|
|
# Secret and ConfigMap values are consumed as environment variables at pod startup.
|
|
# Force a rollout so rerunning an unchanged revision still applies rotated secrets.
|
|
kubectl rollout restart deployment/step3-api -n "${KUBE_NAMESPACE}"
|
|
kubectl rollout status deployment/step3-api -n "${KUBE_NAMESPACE}" --timeout=240s
|
|
kubectl get --raw "/api/v1/namespaces/${KUBE_NAMESPACE}/services/step3-api:http/proxy/readyz" >/dev/null
|
|
|
|
if [ "${DEPLOYMENT_ENVIRONMENT}" = "uat" ]; then
|
|
# A completed old replica can briefly remain visible immediately after a
|
|
# rollout. Select the newest pod that is both Running and container-ready
|
|
# instead of relying on the first item returned by Kubernetes.
|
|
PROMOTION_SUCCEEDED=0
|
|
for attempt in 1 2 3; do
|
|
API_POD="$(kubectl get pods -n "${KUBE_NAMESPACE}" -l app=step3-api \
|
|
--field-selector=status.phase=Running \
|
|
--sort-by=.metadata.creationTimestamp \
|
|
-o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.containerStatuses[0].ready}{"\n"}{end}' \
|
|
| awk '$2 == "true" { name = $1 } END { print name }')"
|
|
if [ -n "${API_POD}" ] \
|
|
&& kubectl wait -n "${KUBE_NAMESPACE}" --for=condition=Ready "pod/${API_POD}" --timeout=60s \
|
|
&& kubectl exec -n "${KUBE_NAMESPACE}" "${API_POD}" -- \
|
|
dotnet QuestionsApi.dll promote-dynamic-questions \
|
|
--source /app/content/dynamic_questions \
|
|
--apply; then
|
|
PROMOTION_SUCCEEDED=1
|
|
break
|
|
fi
|
|
echo "Dynamic question promotion attempt ${attempt} did not find a stable ready API pod; retrying." >&2
|
|
sleep 2
|
|
done
|
|
if [ "${PROMOTION_SUCCEEDED}" -ne 1 ]; then
|
|
echo "Dynamic question promotion failed after 3 attempts." >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
kubectl rollout status deployment/step3-web -n "${KUBE_NAMESPACE}" --timeout=240s
|
|
kubectl get --raw "/api/v1/namespaces/${KUBE_NAMESPACE}/services/step3-web:http/proxy/healthz" >/dev/null
|
|
|
|
if [ "${DEPLOYMENT_ENVIRONMENT}" = "uat" ]; then
|
|
# The images are already imported on every Kubernetes node. Reclaim runner
|
|
# space before pulling the Playwright browser image.
|
|
docker image rm "${API_IMAGE}" "${WEB_IMAGE}" "${TEST_IMAGE}" 2>/dev/null || true
|
|
docker builder prune -af >/dev/null || true
|
|
UAT_API_ORIGIN="${PUBLIC_API_ORIGIN}" \
|
|
UAT_WEB_ORIGIN="${PUBLIC_WEB_ORIGIN:-https://ustep3.ranaze.com}" \
|
|
EXPECTED_REVISION="${BUILD_TAG}" \
|
|
bash .gitea/scripts/run-uat-smoke.sh
|
|
fi
|
|
|
|
echo "Deployed ${DEPLOYMENT_ENVIRONMENT} revision ${BUILD_TAG}."
|