Last active
September 12, 2025 08:48
-
-
Save jumping/dc32de8909f1e71a926724d6ce548a15 to your computer and use it in GitHub Desktop.
Configure the runtime environment for the setup_step.sh script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
set -Eeuo pipefail | |
log() { | |
echo "[$(date '+%Y-%m-%d %H:%M:%S%z')] $*" | |
} | |
usage() { | |
cat <<'EOF' | |
Usage: run-setup.sh --secret-id <secret_id> [--setup-script </path/to/setup_step.sh>] | |
[--region <aws_region>] [--poll-interval <seconds>] | |
[--s3-region <region>] [--eks-cluster-name <name>] [--efs-id <fs-xxxx>] | |
[--once] | |
Parts: | |
1) Install base tools (awscli, db clients, efs utils, kubectl, eksctl, jq) | |
2) Continuously poll Secrets Manager, printing mysql/postgresql/redis/s3/efs info | |
3) Ensure kubeconfig, then call setup_step.sh with (s3_region, eks_cluster_name, efs_id) | |
Examples: | |
run-setup.sh --secret-id my/workshop/secret --setup-script /opt/aws-workshop/setup_step.sh | |
EOF | |
} | |
SECRET_ID="" | |
SETUP_SCRIPT="/opt/aws-workshop/setup_step.sh" | |
AWS_REGION="${AWS_REGION:-}" | |
POLL_INTERVAL=15 | |
ONCE=0 | |
S3_REGION_OVERRIDE="" | |
EKS_CLUSTER_NAME_OVERRIDE="" | |
EFS_ID_OVERRIDE="" | |
while [[ $# -gt 0 ]]; do | |
case "$1" in | |
--secret-id) SECRET_ID="$2"; shift 2;; | |
--setup-script) SETUP_SCRIPT="$2"; shift 2;; | |
--region) AWS_REGION="$2"; shift 2;; | |
--poll-interval) POLL_INTERVAL="$2"; shift 2;; | |
--s3-region) S3_REGION_OVERRIDE="$2"; shift 2;; | |
--eks-cluster-name) EKS_CLUSTER_NAME_OVERRIDE="$2"; shift 2;; | |
--efs-id) EFS_ID_OVERRIDE="$2"; shift 2;; | |
--once) ONCE=1; shift 1;; | |
-h|--help) usage; exit 0;; | |
*) log "Unknown argument: $1"; usage; exit 1;; | |
esac | |
done | |
if [[ -z "${SECRET_ID}" ]]; then | |
log "ERROR: --secret-id is required" | |
exit 1 | |
fi | |
imds_region() { | |
set +e | |
TOKEN="$(curl -sS -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")" | |
REGION_DOC="$(curl -sS -H "X-aws-ec2-metadata-token: ${TOKEN}" http://169.254.169.254/latest/dynamic/instance-identity/document)" | |
echo "${REGION_DOC}" | jq -r .region 2>/dev/null | |
set -e | |
} | |
# ------------------------- | |
# Part 1: base tools setup | |
# ------------------------- | |
log "Installing base tools and CLIs..." | |
# Include your original commands | |
set -euxo pipefail | |
dnf -y update | |
dnf -y install awscli mariadb1011-client-utils postgresql15 amazon-efs-utils jq redis6 | |
curl -sSL -o /usr/local/bin/kubectl https://s3.us-west-2.amazonaws.com/amazon-eks/1.33.3/2025-08-03/bin/linux/amd64/kubectl | |
chmod +x /usr/local/bin/kubectl | |
curl -sLO https://github.com/eksctl-io/eksctl/releases/latest/download/eksctl_linux_amd64.tar.gz | |
tar -xzf eksctl_linux_amd64.tar.gz -C /usr/local/bin && chmod +x /usr/local/bin/eksctl && rm -f eksctl_linux_amd64.tar.gz | |
set +x | |
# Resolve region if not provided | |
if [[ -z "${AWS_REGION}" ]]; then | |
AWS_REGION="$(imds_region || true)" | |
fi | |
if [[ -z "${AWS_REGION}" ]]; then | |
AWS_REGION="$(aws configure get region || true)" | |
fi | |
if [[ -z "${AWS_REGION}" ]]; then | |
log "WARN: AWS region is not set and couldn't be auto-detected. Some AWS CLI calls may fail." | |
fi | |
log "Using AWS region: ${AWS_REGION:-<unset>}" | |
fetch_secret_json() { | |
set +e | |
local out | |
out="$(aws secretsmanager get-secret-value --secret-id "${SECRET_ID}" ${AWS_REGION:+--region "${AWS_REGION}"} --query SecretString --output text 2>/dev/null)" | |
local rc=$? | |
set -e | |
if [[ $rc -ne 0 || -z "${out}" || "${out}" == "None" ]]; then | |
echo "" | |
return 1 | |
fi | |
echo "${out}" | |
} | |
print_service_info() { | |
local json="$1" | |
echo "---------------- Secrets Snapshot ----------------" | |
echo "Time: $(date '+%F %T%z') Secret: ${SECRET_ID} Region: ${AWS_REGION:-<unset>}" | |
if echo "${json}" | jq -e '.mysql? != null' >/dev/null 2>&1; then | |
echo "[MySQL]" | |
echo "${json}" | jq -r '.mysql | {host, port, username, dbname} | to_entries[] | "\(.key)=\(.value // "")"' | |
fi | |
if echo "${json}" | jq -e '.postgresql? != null' >/dev/null 2>&1; then | |
echo "[PostgreSQL]" | |
echo "${json}" | jq -r '.postgresql | {host, port, username, dbname} | to_entries[] | "\(.key)=\(.value // "")"' | |
fi | |
if echo "${json}" | jq -e '.redis? != null' >/dev/null 2>&1; then | |
echo "[Redis]" | |
echo "${json}" | jq -r '.redis | {endpoint, port} | to_entries[] | "\(.key)=\(.value // "")"' | |
fi | |
if echo "${json}" | jq -e '.s3? != null' >/dev/null 2>&1; then | |
echo "[S3]" | |
echo "${json}" | jq -r '.s3 | {bucket, region} | to_entries[] | "\(.key)=\(.value // "")"' | |
fi | |
if echo "${json}" | jq -e '.efs? != null' >/dev/null 2>&1; then | |
echo "[EFS]" | |
echo "${json}" | jq -r '.efs | {file_system_id, mount_target, dns} | to_entries[] | "\(.key)=\(.value // "")"' | |
fi | |
echo "--------------------------------------------------" | |
} | |
# 连接脚本生成:mysql.sh / psql.sh / redis.sh(运行时再取 Secret,不将密码落盘) | |
WORK_DIR="$(dirname "${SETUP_SCRIPT}" 2>/dev/null || echo /opt/aws-workshop)" | |
mkdir -p "${WORK_DIR}" | |
umask 077 | |
generate_connection_scripts() { | |
local _json="$1" | |
# mysql.sh | |
cat > "${WORK_DIR}/mysql.sh" <<'EOS' | |
#!/usr/bin/env bash | |
set -euo pipefail | |
SECRET_ID="__SECRET_ID__" | |
AWS_REGION="__AWS_REGION__" | |
json="$(aws secretsmanager get-secret-value --secret-id "$SECRET_ID" ${AWS_REGION:+--region "$AWS_REGION"} --query SecretString --output text 2>/dev/null || true)" | |
if [[ -z "${json}" || "${json}" == "None" ]]; then | |
echo "[ERROR] Cannot fetch secret '${SECRET_ID}'." | |
exit 1 | |
fi | |
host="$(echo "$json" | jq -r '.mysql.host // .mysql_host // empty')" | |
port="$(echo "$json" | jq -r '.mysql.port // .mysql_port // 3306')" | |
user="$(echo "$json" | jq -r '.mysql.username // .mysql_username // empty')" | |
pass="$(echo "$json" | jq -r '.mysql.password // .mysql_password // empty')" | |
db="$(echo "$json" | jq -r '.mysql.dbname // .mysql_database // empty')" | |
if ! command -v mysql >/dev/null 2>&1; then | |
echo "[ERROR] mysql client not found. Please install mariadb client." | |
exit 1 | |
fi | |
if [[ -z "$host" || -z "$user" ]]; then | |
echo "[ERROR] host or username missing in secret." | |
exit 1 | |
fi | |
if [[ -n "$pass" ]]; then | |
MYSQL_PWD="$pass" exec mysql -h "$host" -P "$port" -u "$user" ${db:+"$db"} | |
else | |
exec mysql -h "$host" -P "$port" -u "$user" ${db:+"$db"} | |
fi | |
EOS | |
# psql.sh | |
cat > "${WORK_DIR}/psql.sh" <<'EOS' | |
#!/usr/bin/env bash | |
set -euo pipefail | |
SECRET_ID="__SECRET_ID__" | |
AWS_REGION="__AWS_REGION__" | |
json="$(aws secretsmanager get-secret-value --secret-id "$SECRET_ID" ${AWS_REGION:+--region "$AWS_REGION"} --query SecretString --output text 2>/dev/null || true)" | |
if [[ -z "${json}" || "${json}" == "None" ]]; then | |
echo "[ERROR] Cannot fetch secret '${SECRET_ID}'." | |
exit 1 | |
fi | |
host="$(echo "$json" | jq -r '.postgresql.host // .postgres_host // empty')" | |
port="$(echo "$json" | jq -r '.postgresql.port // .postgres_port // 5432')" | |
user="$(echo "$json" | jq -r '.postgresql.username // .postgres_username // empty')" | |
pass="$(echo "$json" | jq -r '.postgresql.password // .postgres_password // empty')" | |
db="$(echo "$json" | jq -r '.postgresql.dbname // .postgres_database // .postgres // .workshop')" | |
if ! command -v psql >/dev/null 2>&1; then | |
echo "[ERROR] psql client not found. Please install postgresql client." | |
exit 1 | |
fi | |
if [[ -n "$pass" ]]; then | |
PGPASSWORD="$pass" exec psql "host=$host port=$port user=$user dbname=${db:-postgres} sslmode=prefer" | |
else | |
exec psql "host=$host port=$port user=$user dbname=${db:-postgres} sslmode=prefer" | |
fi | |
EOS | |
# redis.sh | |
cat > "${WORK_DIR}/redis.sh" <<'EOS' | |
#!/usr/bin/env bash | |
set -euo pipefail | |
SECRET_ID="__SECRET_ID__" | |
AWS_REGION="__AWS_REGION__" | |
json="$(aws secretsmanager get-secret-value --secret-id "$SECRET_ID" ${AWS_REGION:+--region "$AWS_REGION"} --query SecretString --output text 2>/dev/null || true)" | |
if [[ -z "${json}" || "${json}" == "None" ]]; then | |
echo "[ERROR] Cannot fetch secret '${SECRET_ID}'." | |
exit 1 | |
fi | |
host="$(echo "$json" | jq -r '.redis.endpoint // .redis_primary_endpoint // empty')" | |
port="$(echo "$json" | jq -r '.redis.port // .redis_primary_port // 6379')" | |
pass="$(echo "$json" | jq -r '.redis.password // empty')" | |
if ! command -v redis6-cli >/dev/null 2>&1; then | |
echo "[ERROR] redis6-cli not found. Install Redis CLI or connect from another host." | |
exit 1 | |
fi | |
if [[ -n "$pass" ]]; then | |
REDISCLI_AUTH="$pass" exec redis-cli -h "$host" -p "$port" | |
else | |
exec redis6-cli -h "$host" -p "$port" | |
fi | |
EOS | |
# 将 SECRET_ID 和 AWS_REGION 写入连接脚本(不落盘密码) | |
sed -i "s|__SECRET_ID__|${SECRET_ID}|g" "${WORK_DIR}/mysql.sh" "${WORK_DIR}/psql.sh" "${WORK_DIR}/redis.sh" | |
sed -i "s|__AWS_REGION__|${AWS_REGION:-}|g" "${WORK_DIR}/mysql.sh" "${WORK_DIR}/psql.sh" "${WORK_DIR}/redis.sh" | |
chmod +x "${WORK_DIR}/mysql.sh" "${WORK_DIR}/psql.sh" "${WORK_DIR}/redis.sh" | |
log "Connection scripts refreshed at ${WORK_DIR}: mysql.sh, psql.sh, redis.sh" | |
} | |
# ------------------------- | |
# Part 2: secrets polling | |
# ------------------------- | |
log "Starting secrets polling for '${SECRET_ID}' every ${POLL_INTERVAL}s..." | |
SECRETS_JSON_ONCE="$(fetch_secret_json || true)" | |
if [[ -n "${SECRETS_JSON_ONCE}" ]]; then | |
print_service_info "${SECRETS_JSON_ONCE}" | |
generate_connection_scripts "${SECRETS_JSON_ONCE}" | |
else | |
log "Secret '${SECRET_ID}' not available yet." | |
fi | |
if [[ "${ONCE}" -eq 0 ]]; then | |
( | |
while true; do | |
json="$(fetch_secret_json || true)" | |
if [[ -n "${json}" ]]; then | |
print_service_info "${json}" | |
generate_connection_scripts "${json}" | |
else | |
log "Secret '${SECRET_ID}' not ready or cannot be fetched." | |
fi | |
sleep "${POLL_INTERVAL}" | |
done | |
) & | |
SECRETS_WATCH_PID=$! | |
fi | |
# ------------------------- | |
# Part 3: ensure kubeconfig then call setup_step.sh | |
# ------------------------- | |
ensure_kubeconfig() { | |
local eks_cluster_name="$1" | |
local region_for_eks="$2" | |
if [[ -z "${eks_cluster_name}" ]]; then | |
log "ERROR: EKS cluster name is empty; cannot configure kubeconfig." | |
return 1 | |
fi | |
if [[ -z "${region_for_eks}" ]]; then | |
region_for_eks="${AWS_REGION:-}" | |
fi | |
if [[ -z "${region_for_eks}" ]]; then | |
log "WARN: Region for EKS not found; kubeconfig update may fail." | |
fi | |
mkdir -p /root/.kube | |
local max_attempts=10 | |
local attempt=1 | |
local sleep_seconds=6 | |
while (( attempt <= max_attempts )); do | |
log "Updating kubeconfig (attempt ${attempt}/${max_attempts}) for cluster '${eks_cluster_name}' in region '${region_for_eks:-<unset>}''..." | |
set +e | |
aws eks update-kubeconfig --name "${eks_cluster_name}" ${region_for_eks:+--region "${region_for_eks}"} >/tmp/update-kubeconfig.log 2>&1 | |
local rc=$? | |
set -e | |
if [[ $rc -eq 0 ]]; then | |
# Verify cluster connectivity | |
set +e | |
kubectl get nodes >/tmp/kubectl-nodes.log 2>&1 | |
local krc=$? | |
set -e | |
if [[ $krc -eq 0 ]]; then | |
log "Kubeconfig updated and kubectl is able to reach the cluster." | |
return 0 | |
else | |
log "kubectl cannot reach the cluster yet. Log:" | |
sed 's/^/[kubectl] /' /tmp/kubectl-nodes.log || true | |
fi | |
else | |
log "update-kubeconfig failed. Log:" | |
sed 's/^/[update-kubeconfig] /' /tmp/update-kubeconfig.log || true | |
fi | |
attempt=$((attempt + 1)) | |
log "Retrying in ${sleep_seconds}s..." | |
sleep "${sleep_seconds}" | |
done | |
log "ERROR: Failed to configure kubeconfig after ${max_attempts} attempts." | |
return 1 | |
} | |
resolve_values_and_invoke() { | |
local json="$1" | |
local s3_region="" eks_cluster_name="" efs_id="" | |
if [[ -n "${json}" ]]; then | |
s3_region="$(echo "${json}" | jq -r '.s3.region // empty' 2>/dev/null || true)" | |
eks_cluster_name="$(echo "${json}" | jq -r '.eks.cluster_name // empty' 2>/dev/null || true)" | |
efs_id="$(echo "${json}" | jq -r '.efs.file_system_id // empty' 2>/dev/null || true)" | |
fi | |
s3_region="${S3_REGION_OVERRIDE:-${s3_region}}" | |
eks_cluster_name="${EKS_CLUSTER_NAME_OVERRIDE:-${eks_cluster_name}}" | |
efs_id="${EFS_ID_OVERRIDE:-${efs_id}}" | |
if [[ -z "${s3_region}" ]]; then | |
s3_region="${AWS_REGION:-}" | |
fi | |
if [[ -z "${eks_cluster_name}" ]]; then | |
set +e | |
eks_cluster_name="$(aws eks list-clusters ${AWS_REGION:+--region "${AWS_REGION}"} --query 'clusters[0]' --output text 2>/dev/null)" | |
[[ "${eks_cluster_name}" == "None" ]] && eks_cluster_name="" | |
set -e | |
fi | |
if [[ -z "${efs_id}" ]]; then | |
set +e | |
efs_id="$(aws efs describe-file-systems ${AWS_REGION:+--region "${AWS_REGION}"} --query 'FileSystems[0].FileSystemId' --output text 2>/dev/null)" | |
[[ "${efs_id}" == "None" ]] && efs_id="" | |
set -e | |
fi | |
log "Resolved values:" | |
log " s3_region=${s3_region:-<empty>}" | |
log " eks_cluster_name=${eks_cluster_name:-<empty>}" | |
log " efs_id=${efs_id:-<empty>}" | |
if [[ ! -x "${SETUP_SCRIPT}" ]]; then | |
log "ERROR: setup script not found or not executable: ${SETUP_SCRIPT}" | |
return 1 | |
fi | |
# Ensure kubeconfig before invoking setup_step.sh | |
ensure_kubeconfig "${eks_cluster_name}" "${AWS_REGION:-${s3_region:-}}" | |
log "Invoking: ${SETUP_SCRIPT} '${s3_region}' '${eks_cluster_name}' '${efs_id}'" | |
"${SETUP_SCRIPT}" --region "${s3_region}" --cluster-name "${eks_cluster_name}" --efs-id "${efs_id}" | |
} | |
resolve_values_and_invoke "${SECRETS_JSON_ONCE}" | |
if [[ "${ONCE}" -eq 0 && -n "${SECRETS_WATCH_PID:-}" ]]; then | |
log "Secrets polling is running in background with PID ${SECRETS_WATCH_PID}." | |
fi | |
log "run-setup.sh completed initial execution." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment