-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathrun_kc_system.sh
executable file
·452 lines (371 loc) · 14.6 KB
/
run_kc_system.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
#!/usr/bin/env bash
# Exit immediately if a command exits with a non-zero status,
# treat unset variables as an error, and ensure pipeline commands fail if any command fails.
set -euo pipefail
# Initialize DEBUG to 0 (non-debug)
DEBUG=0
# Output Prefixes for Readability
INF="[INFO] "
DBG="[DEBUG] "
ERR="[ERROR] "
WRN="[WARNING] "
ADD="[ADD] "
DEL="[DELETE] "
GEN="[GENERATE] "
DEP="[DEPLOY] "
CRE="[CREATE] "
WAI="[WAIT] "
# Parse command-line arguments to detect the --debug flag
for arg in "$@"; do
case $arg in
--debug)
DEBUG=1
shift
;;
*)
;;
esac
done
# -----------------------------
# Logging Functions
# -----------------------------
# Display routine informational messages
log_info() {
echo -e "${INF} $1"
}
# Display warning messages
log_warn() {
echo -e "${WRN} $1"
}
# Display error messages
log_error() {
echo -e "${ERR} $1" >&2
}
# Display detailed debugging information (only in debug mode)
log_debug() {
if [ "$DEBUG" -eq 1 ]; then
echo -e "${DBG} $1"
fi
}
# -----------------------------
# Command Execution Functions
# -----------------------------
# Executes routine commands with suppressed output unless in debug mode
run_quiet() {
if [ "$DEBUG" -eq 1 ]; then
"$@"
else
"$@" >/dev/null 2>&1
fi
}
# Executes sensitive commands without displaying outputs, ensuring secrets remain hidden
run_sensitive() {
if [ "$DEBUG" -eq 1 ]; then
"$@"
else
"$@" >/dev/null 2>&1
fi
}
# Executes commands with full outputs only in debug mode
run_debug() {
if [ "$DEBUG" -eq 1 ]; then
"$@"
else
"$@" >/dev/null 2>&1
fi
}
# -----------------------------
# Variables Configuration
# -----------------------------
# Namespaces
namespace="hbr-keycloak"
certmgr_ns="cert-manager"
sealed_secrets_ns="sealed-secrets"
# Deployment and Service Name for Sealed Secrets Controller
sealed_secrets_deployment="sealed-secrets"
sealed_secrets_service="sealed-secrets"
# Admin Passwords (Set via environment variables for security)
kc_admin_pwd="${KEYCLOAK_ADMIN_PWD:-password}" # Default to 'password' if not set
# Helm Values Files
kc_values="keycloak-values.yaml"
pg_values="postgresql-values.yaml"
sealed_secrets_values="sealed-secrets-values.yaml"
# Readiness Parameters
readiness_timeout=300 # Total timeout in seconds
readiness_interval=10 # Interval between checks in seconds
# -----------------------------
# Utility Functions
# -----------------------------
# Function to check if a namespace exists
namespace_exists() {
kubectl get namespace "$1" >/dev/null 2>&1
}
# Function to wait for a Secret to exist
wait_for_secret() {
local secret_name="$1"
local namespace="$2"
local timeout=60
local interval=5
local elapsed=0
while [ $elapsed -lt $timeout ]; do
if kubectl get secret "$secret_name" -n "$namespace" >/dev/null 2>&1; then
log_info "Secret '$secret_name' in namespace '$namespace' is available."
return 0
fi
echo "Waiting for secret '$secret_name' in namespace '$namespace'..."
sleep "$interval"
elapsed=$((elapsed + interval))
done
log_error "Secret '$secret_name' not found in namespace '$namespace' after $timeout seconds."
return 1
}
# Function to wait for a pod with a specific selector to be ready
wait_for_pods() {
local namespace="$1"
local selector="$2"
local component="$3"
echo -n "${WAI} Waiting for $component pods to be ready..."
if ! kubectl wait --namespace "$namespace" \
--for=condition=ready pod \
--selector="$selector" \
--timeout=300s; then
log_warn "Failed to wait for $component pods to be ready within the timeout."
log_warn "Proceeding with the deployment of other components."
else
echo "${INF} OK"
fi
}
# -----------------------------
# Core Functions
# -----------------------------
# Function to install Sealed Secrets Controller via Helm
install_sealed_secrets_via_helm() {
log_info "Installing Sealed Secrets Controller via Helm"
run_quiet helm install sealed-secrets oci://registry-1.docker.io/bitnamicharts/sealed-secrets \
-f "${sealed_secrets_values}" \
--namespace "$sealed_secrets_ns" \
--create-namespace \
--wait \
--timeout "${readiness_timeout}s"
log_info "Sealed Secrets Controller installed via Helm."
}
# Function to install Sealed Secrets Controller via YAML (Alternative Method)
install_sealed_secrets_via_yaml() {
log_info "Installing Sealed Secrets Controller via YAML"
run_quiet kubectl apply -f "$sealed_secrets_controller_yaml"
log_info "Waiting for Sealed Secrets Controller to be ready..."
local elapsed=0
while [ $elapsed -lt $readiness_timeout ]; do
# Fetch the number of ready replicas
ready_replicas=$(kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
desired_replicas=$(kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" -o jsonpath='{.spec.replicas}' 2>/dev/null || echo 1)
if [ "$ready_replicas" -ge "$desired_replicas" ]; then
log_info "Sealed Secrets Controller is ready."
return 0
fi
echo "Sealed Secrets Controller not ready yet. ($ready_replicas/$desired_replicas)"
sleep "$readiness_interval"
elapsed=$((elapsed + readiness_interval))
done
log_error "Sealed Secrets Controller deployment '$sealed_secrets_deployment' in namespace '$sealed_secrets_ns' did not become ready within $readiness_timeout seconds."
log_info "Current deployment status:"
run_quiet kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" || true
log_info "Current pod statuses:"
run_quiet kubectl get pods -n "$sealed_secrets_ns" -l app.kubernetes.io/name=sealed-secrets || true
exit 1
}
# Function to install Sealed Secrets Controller (Choose Method)
install_sealed_secrets() {
# Choose installation method: Helm or YAML
# Uncomment the preferred method and comment out the other
# Method 1: Install via Helm (Recommended)
install_sealed_secrets_via_helm
# Method 2: Install via YAML
# install_sealed_secrets_via_yaml
}
# Function to create a Sealed Secret
create_sealed_secret() {
local secret_name="$1"
local secret_namespace="$2"
local secret_file="$3"
log_info "Creating Sealed Secret for '$secret_name'"
# Ensure the Sealed Secrets Controller is ready
if ! namespace_exists "$sealed_secrets_ns" || ! kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" >/dev/null 2>&1; then
log_info "Sealed Secrets Controller is not deployed yet. Installing..."
install_sealed_secrets
fi
# Display Secret YAML in debug mode only
if [ "$DEBUG" -eq 1 ]; then
log_debug "=== $secret_file ==="
cat "$secret_file"
log_debug "=== End of $secret_file ==="
fi
# Generate Sealed Secret using a pipeline
# In debug mode, display the outputs
# In non-debug mode, suppress stderr but allow the pipeline to function correctly
if [ "$DEBUG" -eq 1 ]; then
kubectl create -f "$secret_file" --dry-run=client -o json | \
kubeseal --controller-name "$sealed_secrets_service" --controller-namespace "$sealed_secrets_ns" --format=yaml > "${secret_file}.sealed.yaml"
else
kubectl create -f "$secret_file" --dry-run=client -o json 2>/dev/null | \
kubeseal --controller-name "$sealed_secrets_service" --controller-namespace "$sealed_secrets_ns" --format=yaml > "${secret_file}.sealed.yaml" 2>/dev/null
fi
log_info "Applying Sealed Secret '$secret_name'"
run_quiet kubectl apply -f "${secret_file}.sealed.yaml"
# Wait for the underlying Secret to be created
wait_for_secret "$secret_name" "$secret_namespace"
# Verify Secret Data in debug mode only
if [ "$DEBUG" -eq 1 ]; then
log_debug "Verifying Secret '$secret_name' in namespace '$secret_namespace'"
kubectl get secret "$secret_name" -n "$secret_namespace" -o yaml
fi
# Clean up temporary sealed secret file
rm -f "${secret_file}.sealed.yaml"
}
# -----------------------------
# Main Deployment Steps
# -----------------------------
log_info "Starting Keycloak and PostgreSQL-HA deployment..."
# Cleanup existing namespaces if they exist
if namespace_exists "$namespace"; then
log_info "Namespace '$namespace' exists. Deleting..."
run_quiet kubectl delete namespace "$namespace"
fi
if namespace_exists "$certmgr_ns"; then
log_info "Namespace '$certmgr_ns' exists. Deleting..."
run_quiet kubectl delete namespace "$certmgr_ns"
fi
# Create necessary namespaces
log_info "Creating namespace '$namespace'"
run_quiet kubectl create namespace "$namespace"
log_info "Creating namespace '$sealed_secrets_ns'"
run_quiet kubectl create namespace "$sealed_secrets_ns"
# Install Sealed Secrets Controller if not present
if ! namespace_exists "$sealed_secrets_ns" || ! kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" >/dev/null 2>&1; then
install_sealed_secrets
else
log_info "Sealed Secrets Controller already installed."
log_info "Ensuring Sealed Secrets Controller is ready..."
local elapsed=0
while [ $elapsed -lt $readiness_timeout ]; do
ready_replicas=$(kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo 0)
desired_replicas=$(kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" -o jsonpath='{.spec.replicas}' 2>/dev/null || echo 1)
if [ "$ready_replicas" -ge "$desired_replicas" ]; then
log_info "Sealed Secrets Controller is ready."
break
fi
echo "Sealed Secrets Controller not ready yet. ($ready_replicas/$desired_replicas)"
sleep "$readiness_interval"
elapsed=$((elapsed + readiness_interval))
done
if [ "$ready_replicas" -lt "$desired_replicas" ]; then
log_error "Sealed Secrets Controller deployment '$sealed_secrets_deployment' in namespace '$sealed_secrets_ns' did not become ready within $readiness_timeout seconds."
log_info "Current deployment status:"
run_quiet kubectl get deployment "$sealed_secrets_deployment" -n "$sealed_secrets_ns" || true
log_info "Current pod statuses:"
run_quiet kubectl get pods -n "$sealed_secrets_ns" -l app.kubernetes.io/name=sealed-secrets || true
exit 1
fi
fi
# Add Bitnami Helm repo and update it
log_info "Adding Bitnami Helm repo and updating it"
run_quiet helm repo add bitnami https://charts.bitnami.com/bitnami
run_quiet helm repo update
# Generate Keycloak admin password
log_info "Generating Keycloak admin password"
KEYCLOAK_ADMIN_PASSWORD=$(echo -n "$kc_admin_pwd")
# Display Keycloak Admin Password in debug mode only
if [ "$DEBUG" -eq 1 ]; then
log_debug "KEYCLOAK_ADMIN_PASSWORD: '$KEYCLOAK_ADMIN_PASSWORD'"
fi
# Create Keycloak admin Secret YAML
cat <<EOF > keycloak-admin-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: keycloak-admin-password
namespace: $namespace
type: Opaque
data:
password: $(echo -n "$KEYCLOAK_ADMIN_PASSWORD" | base64)
EOF
# Create Sealed Secret for Keycloak admin password
create_sealed_secret "keycloak-admin-password" "$namespace" "keycloak-admin-secret.yaml"
# Generate PostgreSQL and PgPool passwords
log_info "Generating passwords and secrets for PostgreSQL, RepMgr, and PgPool admin"
POSTGRES_ADMIN_PASSWORD=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16)
POSTGRES_USER_PASSWORD=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16)
REPMGR_PASSWORD=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16)
PGPOOL_ADMIN_PASSWORD=$(openssl rand -base64 12 | tr -dc 'a-zA-Z0-9' | head -c 16)
# Display Generated Passwords in debug mode only
if [ "$DEBUG" -eq 1 ]; then
log_debug "POSTGRES_ADMIN_PASSWORD: '$POSTGRES_ADMIN_PASSWORD'"
log_debug "POSTGRES_USER_PASSWORD: '$POSTGRES_USER_PASSWORD'"
log_debug "REPMGR_PASSWORD: '$REPMGR_PASSWORD'"
log_debug "PGPOOL_ADMIN_PASSWORD: '$PGPOOL_ADMIN_PASSWORD'"
fi
# Create PostgreSQL Secret YAML
cat <<EOF > postgresql-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: postgresql-secret
namespace: $namespace
type: Opaque
data:
password: $(echo -n "$POSTGRES_USER_PASSWORD" | base64)
postgres-password: $(echo -n "$POSTGRES_ADMIN_PASSWORD" | base64)
repmgr-password: $(echo -n "$REPMGR_PASSWORD" | base64)
EOF
# Create Sealed Secret for PostgreSQL
create_sealed_secret "postgresql-secret" "$namespace" "postgresql-secret.yaml"
# Create PgPool Secret YAML
cat <<EOF > pgpool-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: pgpool-secret
namespace: $namespace
type: Opaque
data:
admin-password: $(echo -n "$PGPOOL_ADMIN_PASSWORD" | base64)
EOF
# Create Sealed Secret for PgPool
create_sealed_secret "pgpool-secret" "$namespace" "pgpool-secret.yaml"
# Deploy PostgreSQL with Helm using Sealed Secrets
log_info "Deploying PostgreSQL-HA with Helm and '$pg_values'"
run_quiet helm install postgresql-ha oci://registry-1.docker.io/bitnamicharts/postgresql-ha \
--namespace "$namespace" \
-f "$pg_values"
# Wait for PostgreSQL-HA pods to be ready
wait_for_pods "$namespace" "app.kubernetes.io/component=pgpool" "PostgreSQL-HA"
# Deploy Keycloak with Helm using Sealed Secrets
log_info "Deploying Keycloak with Helm and '$kc_values'"
run_quiet helm install keycloak oci://registry-1.docker.io/bitnamicharts/keycloak \
--namespace "$namespace" \
-f "$kc_values"
# Install Cert-Manager
log_info "Creating namespace '$certmgr_ns'"
run_quiet kubectl create namespace "$certmgr_ns"
log_info "Adding Jetstack Helm repo and updating it"
run_quiet helm repo add jetstack https://charts.jetstack.io
run_quiet helm repo update
log_info "Installing Cert-Manager CRDs"
certmgr_crds="https://github.com/cert-manager/cert-manager/releases/download/v1.11.1/cert-manager.crds.yaml"
run_quiet kubectl apply -f "$certmgr_crds"
log_info "Deploying Cert-Manager with Helm"
run_quiet helm install cert-manager jetstack/cert-manager \
--namespace "$certmgr_ns" \
--version v1.11.1
log_info "Applying ClusterIssuer"
run_quiet kubectl apply -f cluster-issuer-selfsigned.yaml \
-n "$namespace"
log_info "Applying Self-Signed Certificate"
run_quiet kubectl apply -f selfsigned-certificate.yaml \
-n "$namespace"
log_info "Deploying NGINX Ingress Controller with Cert-Manager Integration"
run_quiet kubectl apply -f keycloak-ingress-with-cert-manager.yaml \
-n "$namespace"
# Wait for Keycloak pods to be ready
wait_for_pods "$namespace" "app.kubernetes.io/component=keycloak" "Keycloak"
log_info "Keycloak and PostgreSQL-HA are ready!"