-
Notifications
You must be signed in to change notification settings - Fork 2
/
preflights.sh
executable file
·443 lines (386 loc) · 11.2 KB
/
preflights.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
#!/bin/bash
# set -x
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
OS="$(uname | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')"
PREFLIGHTS_ROOT_DIR="${HOME}/.local"
PREFLIGHTS_BIN_DIR="${PREFLIGHTS_ROOT_DIR}/bin"
STABLE_CHARTS=("oci://registry.replicated.com/gitguardian/gitguardian" "oci://registry.replicated.com/gitguardian/stable/gitguardian" "oci://registry.replicated.com/gitguardian-seal/gitguardian" "oci://registry.replicated.com/gitguardian-seal/stable/gitguardian")
export PATH="${PREFLIGHTS_BIN_DIR}:${PATH}"
function echo_pass() {
echo -e "\033[1;32mPASS\033[0m"
}
function exit_ko() {
echo -e "\033[1;31mKO\033[0m "
exit 1
}
function exit_error() {
echo -en "\033[1;31m[ERROR]\033[0m " >&2
echo $@ >&2
exit 1
}
function echo_warn() {
echo -en "\033[38;2;255;165;0m[WARN]\033[0m"
echo $@
}
function install_jq() {
local os=$(echo $OS | sed -e 's/darwin/macos/')
echo -e "--- INSTALLING JQ BINARY"
mkdir -p "${PREFLIGHTS_BIN_DIR}"
curl -fsSL https://github.com/jqlang/jq/releases/download/jq-1.7.1/jq-${os}-${ARCH} \
--output "${PREFLIGHTS_BIN_DIR}/jq"
chmod +x "${PREFLIGHTS_BIN_DIR}/jq"
}
function install_preflight() {
echo -e "--- INSTALLING PREFLIGHT PLUGIN"
curl --silent https://krew.sh/preflight | bash >$script_dir/preflights_install.logs 2>&1
}
# Function to check if a string is in an array
function contains_string_in_array() {
local search_string=$1
shift
local array=("$@")
for item in "${array[@]}"; do
if [[ "$item" == "$search_string" ]]; then
return 0 # Found
fi
done
return 1 # Not found
}
function write_results() {
if [[ "$SAVE" == "yes" ]];
then
echo -e "--- SAVING RESULTS TO SECRET gitguardian-preflights-results"
#Used to preserve formatting
cat <<K8SSECRET > $script_dir/preflights-output
$GLOBAL_OUTPUT
K8SSECRET
kubectl create secret generic gitguardian-preflights-results \
--save-config=true \
--dry-run=client \
--from-file="$script_dir/preflights-output" \
-o yaml | \
kubectl apply $NAMESPACE -f -
rm -f $script_dir/preflights-output
#add this for telemetry usage later in backend
kubectl patch secrets gitguardian-preflights-results $NAMESPACE -p='{"stringData":{"STATUS_LOCAL":"'$LOCAL_CHECKS_STATUS'","STATUS_REMOTE":"'$REMOTE_CHECKS_STATUS'"}}'
fi
}
function run_hide_output() {
local cmd="$1"
local flag="$2"
if [[ "$DEBUG_MODE" == "yes" ]];
then
flag="none"
fi
case "$flag" in
stderr)
eval "$cmd 2> /dev/null"
;;
all)
eval "$cmd &> /dev/null"
;;
none)
eval "$cmd"
;;
*)
echo "Invalid flag: $flag"
return 1
;;
esac
return $?
}
function usage() {
cat <<USAGE
Usage:
$(basename $0) [OPTIONS] chart
Description:
Execute Gitguardian Preflight on a cluster.
Chart can be local path or OCI uri.
Dependencies: kubectl, helm, preflight plugin (installable using --install-preflight option)
OPTIONS:
-f FILE
Pass a values file, can be used several times
-n NAMESPACE
Specify the Kubernetes destination namespace
--version
Specify the version of the Helm chart to use (default to latest)
--local
Execute only local tests
--install-jq
Install jq tool
--install-preflight
Install latest preflight plugin using krew for local preflights
--remote
Execute only remote tests
--reuse
Use existing templates if preflights have been played before
--nosave
Do not save results in-cluster (not recommended)
--no-replicated
Use this option for dev, do not render Replicated pull secrets
-h | --help
Display this help message
USAGE
}
trap "write_results" EXIT
#conf
REMOTE_PREFLIGHTS_TEMPLATE="-s templates/on-prem/helm_preflights_remote.yaml"
LOCAL_PREFLIGHTS_TEMPLATE="-s templates/on-prem/helm_preflights_local.yaml"
PULL_SECRETS_OPTION="-s templates/image-pull-secrets.yaml"
PREFLIGHTS_TEMPLATING_OPTION="--dry-run=server --set onPrem.preflightsTemplating.enabled=true"
REMOTE_CRONJOB_NAME="gitguardian-remote-preflights"
#inputs
CHART=""
CHART_VERSION=""
DEVEL=""
NAMESPACE=""
LOCAL_CHECKS="yes"
REMOTE_CHECKS="yes"
VALUES_FILES=""
FORCE="yes"
SAVE="yes"
DEBUG_MODE="no"
INSTALL_JQ="no"
INSTALL_PREFLIGHT="no"
#outputs
GLOBAL_OUTPUT=""
LOCAL_CHECKS_STATUS="empty"
REMOTE_CHECKS_STATUS="empty"
#input parsing
while (("$#")); do
case "$1" in
--local)
REMOTE_CHECKS="no"
shift
;;
--remote)
LOCAL_CHECKS="no"
shift
;;
-n)
shift
NAMESPACE="--namespace $1"
shift
;;
--version)
shift
CHART_VERSION="--version $1"
shift
;;
--reuse)
FORCE="no"
shift
;;
--install-jq)
INSTALL_JQ="yes"
shift
;;
--install-preflight)
INSTALL_PREFLIGHT="yes"
shift
;;
--no-replicated)
PULL_SECRETS_OPTION=""
shift
;;
--nosave)
SAVE="no"
shift
;;
--debug)
DEBUG_MODE="yes"
shift
;;
-f)
shift
VALUES_FILES+="-f $1 "
shift
;;
-h | --help)
usage
exit 0
;;
*) # positional argument
if [ -n "$CHART" ];
then
usage
exit_error "You can have only one chart path, please check your command"
fi
CHART=$1
shift
;;
esac
done
#Checks
if [[ "$LOCAL_CHECKS" == "no" ]] && [[ "$REMOTE_CHECKS" == "no" ]];
then
usage
exit_error "You selected no tests, remove --local and --remote to run all tests"
fi
if [[ -z "$CHART" ]] && [[ "$LOCAL_CHECKS" == "yes" ]];
then
usage
exit_error "You must provide a chart (path or OCI uri)"
fi
if ! which kubectl helm &>/dev/null;
then
exit_error "You need helm and kubectl in your PATH"
fi
# If specified chart is not considered as a stable chart, enable Helm --devel flag
if ! contains_string_in_array "$CHART" "${STABLE_CHARTS[@]}"; then
DEVEL="--devel"
fi
values_array=($VALUES_FILES)
for i in "${!values_array[@]}";
do
if [[ ${values_array[$i]} == "-f" ]]; then
file=${values_array[$((i+1))]}
if [ ! -f $file ];
then
exit_error "The file $file does not exist"
fi
fi
done
#Main
if [ -n "$PULL_SECRETS_OPTION" ] && [[ "$FORCE" == "yes" ]];
then
echo -e "--- TEMPLATING PULL SECRETS"
echo -e "Please wait ..."
if ! run_hide_output "helm template $DEVEL $NAMESPACE $VALUES_FILES $CHART_VERSION $PULL_SECRETS_OPTION $CHART > $script_dir/local_secrets.yaml" "stderr";
then
LOCAL_CHECKS_STATUS="error"
exit_error "Unable to template pull secrets"
elif ! run_hide_output "kubectl $NAMESPACE apply -f $script_dir/local_secrets.yaml" "all";
then
LOCAL_CHECKS_STATUS="error"
exit_error "Unable to apply pull secrets"
fi
rm -f $script_dir/local_secrets.yaml
fi
if [[ "$LOCAL_CHECKS" == "yes" ]];
then
if ! run_hide_output "kubectl preflight version" "all";
then
if [[ "$INSTALL_PREFLIGHT" == "no" ]];
then
exit_error "You need kubectl preflight plugin to run local tests, use --install-preflight to get it"
else
install_preflight
fi
fi
if [[ ! -f "$script_dir/local_preflights.yaml" ]] || [[ "$FORCE" == "yes" ]] ;
then
echo -e "--- TEMPLATING LOCAL TESTS"
echo -e "Please wait ..."
if ! run_hide_output "helm template $DEVEL $NAMESPACE $VALUES_FILES $CHART_VERSION $PREFLIGHTS_TEMPLATING_OPTION $LOCAL_PREFLIGHTS_TEMPLATE $CHART > $script_dir/local_preflights.yaml" "stderr";
then
rm -f $script_dir/local_preflights.yaml
LOCAL_CHECKS_STATUS="error"
exit_error "Unable to template local preflights"
fi
fi
echo -e "--- RUNNING LOCAL TESTS"
output=`run_hide_output "kubectl preflight $NAMESPACE --interactive=false $script_dir/local_preflights.yaml" "stderr"`
retcode=$?
echo -e "$output"
GLOBAL_OUTPUT+="--- RUNNING LOCAL TESTS$output"
if [ $retcode -eq 0 ];
then
LOCAL_CHECKS_STATUS="pass"
echo_pass
elif [ $retcode -eq 4 ];
then
LOCAL_CHECKS_STATUS="warn"
echo_warn "At least, one check is in warn status"
else
LOCAL_CHECKS_STATUS="error"
exit_ko
fi
fi
if [[ "$REMOTE_CHECKS" == "yes" ]];
then
if ! run_hide_output "jq --version" "all";
then
if [[ "$INSTALL_JQ" == "no" ]];
then
exit_error "You need jq tool to run remote tests, use --install-jq to get it"
else
install_jq
fi
fi
if ! `run_hide_output "kubectl get cronjob $REMOTE_CRONJOB_NAME $NAMESPACE" "all"` || [[ "$FORCE" == "yes" ]] ; then
echo -e "--- TEMPLATING REMOTE TESTS"
echo -e "Please wait ..."
if ! run_hide_output "helm template $DEVEL $NAMESPACE $VALUES_FILES $CHART_VERSION $PREFLIGHTS_TEMPLATING_OPTION $REMOTE_PREFLIGHTS_TEMPLATE $CHART > $script_dir/remote_preflights.yaml" "stderr";
then
REMOTE_CHECKS_STATUS="error"
exit_error "Unable to template remote preflights"
else
run_hide_output "kubectl delete $NAMESPACE cronjob $REMOTE_CRONJOB_NAME" "all"
if ! run_hide_output "kubectl apply $NAMESPACE -f $script_dir/remote_preflights.yaml" "all";
then
REMOTE_CHECKS_STATUS="error"
exit_error "Unable to apply remote preflights"
fi
sleep 2
fi
rm -f $script_dir/remote_preflights.yaml
fi
echo -e "--- RUNNING REMOTE TESTS"
echo -e "If this step is too long, please check the pod is running in the accurate namespace"
echo -e "Please wait ..."
#Start job
run_hide_output "kubectl create job $NAMESPACE --from=cronjob/$REMOTE_CRONJOB_NAME $REMOTE_CRONJOB_NAME-`mktemp -u XXXXX | tr '[:upper:]' '[:lower:]'` --dry-run=client -o json | jq 'del(.metadata.ownerReferences)' | kubectl apply -f -" "all"
sleep 5
pod=$(kubectl get pods $NAMESPACE -l gitguardian=remote-preflight --sort-by=.metadata.creationTimestamp -o 'jsonpath={.items[-1].metadata.name}')
while true; do
# Check the status of the pod
pod_status=$(kubectl get pod $pod $NAMESPACE -o jsonpath='{.status.phase}')
# If pod_status is not empty, the pod has reached a terminal state
if [ -n "$pod_status" ]; then
case "$pod_status" in
"Succeeded")
break
;;
"Failed")
break
;;
"Pending")
waiting_pod=$(kubectl get pod $pod $NAMESPACE -o jsonpath='{.status.containerStatuses[-1].state.waiting.reason}')
if [ -n "$waiting_pod" ] && [[ "$waiting_pod" == "ImagePullBackOff" ]];
then
echo ""
exit_error "Unable to pull the test image, are your credentials configured properly?"
fi
;;
esac
fi
echo -n "."
sleep 5
done
# Print preflights output
output=`kubectl logs $NAMESPACE $pod`
echo -e "$output"
GLOBAL_OUTPUT+="
--- RUNNING REMOTE TESTS$output"
retcode=$(kubectl get pods $pod $NAMESPACE -o 'jsonpath={.status.containerStatuses[0].state.terminated.exitCode}')
if [ $retcode -eq 0 ];
then
if [[ "$output" =~ "WARN" ]];
then
REMOTE_CHECKS_STATUS="warn"
echo_warn "At least, one check is in warn status"
elif [[ "$output" =~ "FAIL" ]];
then
REMOTE_CHECKS_STATUS="error"
exit_ko
else
REMOTE_CHECKS_STATUS="pass"
echo_pass
fi
else
REMOTE_CHECKS_STATUS="error"
exit_ko
fi
fi