-
Notifications
You must be signed in to change notification settings - Fork 164
/
otomi
executable file
·399 lines (372 loc) · 11.9 KB
/
otomi
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
#!/usr/bin/env bash
#####################################################################################
##
## NOTE:
## This is a command line tool to operate on otomi-core.
## All commands are executed in docker container.
## Keep this file as simple as possible:
## - do not depend on any external files.
## - do not use any non standard tooling.
## - only Docker is needed to run otomi-core image
## If you need to use any extra binaries then most probably you want to add them to the otomi/tools image.
##
#####################################################################################
set -e
command=$1
[ "$ENV_DIR" = "" ] && env_unset=1
ENV_DIR=${ENV_DIR:-$PWD}
[ "$ENV_DIR" = "/home/app/stack" ] && ENV_DIR=$ENV_DIR/env
. bin/common.sh
function verbose_env() {
echo "has_docker=$has_docker"
echo "ENV_DIR=$ENV_DIR"
echo "stack_dir=$stack_dir"
echo "helm_config=$helm_config"
echo "cmd_image=$cmd_image"
echo "check_kube_context=$check_kube_context"
echo "K8S_CONTEXT=$K8S_CONTEXT"
echo "IN_DOCKER=$IN_DOCKER"
echo "command=$command"
}
# VERBOSE - export this varibale to run this script in verbose mode
VERBOSE=${VERBOSE:-'true'}
# EXIT_FAST - export this varaibale to exit the script on error
EXIT_FAST=${EXIT_FAST:-'true'}
# check_kube_context - a flag to indicate to use kube context and to refresh kube access token before running command in docker
check_kube_context=1
# A path to directory with values directory
has_docker=0
dind=0
mount_stack_dir=0
stack_dir='/home/app/stack'
set +e
customer=$(customer_name || $CUSTOMER || "unknown")
set -e
cmd_image=''
docker_terminal_params='-ti'
helm_config=''
readme_url='https://github.com/redkubes/otomi-core'
function set_env_and_stack_dir() {
local cwd=$(basename "$PWD")
[ "$VERBOSE" = "1" ] && echo "CWD: $cwd"
if [ "$cwd" = "otomi-core" ]; then
[ "$env_unset" = "1" ] && echo "Error: The ENV_DIR environment variable is not set" >&2 && exit 1
[ "$VERBOSE" = "1" ] && echo "Mounting otomi-core dir"
stack_dir=$PWD
mount_stack_dir=1
fi
return 0
}
function set_helm_config() {
helm_config="$HOME/.config/helm"
uname -a | grep -i darwin >/dev/null && helm_config="$HOME/Library/Preferences/helm"
return 0
}
function show_usage() {
echo "otomi usage:
apply apply k8s resources (use labels like '-l name=prometheus-operator')
bash run interactive bash shell in otomi-core container
bootstrap bootstrap values repo with artifacts corresponding to the cluster's stack version
commit wrapper for encrypt > generate pipelines > git commit changed files
console starts Otomi Console (add any arg to quit) on http://127.0.0.1:3000 (licensed, so pull secret required for api)
decrypt decrypts file(s) given as arguments (relative to env folder), or all env/*.secrets.yaml to env/*.secrets.yaml.dec files
deploy execute otomi-core deploy script
diff diff k8s resources (use labels like '-l name=prometheus-operator')
encrypt encrypts file(s) given as arguments (relative to env folder), or all env/*.secrets.yaml files
help print this help
hf run helmfile with CLUSTER and CLOUD already set
pull wrapper for git pull && bootstrap
switch switches context to the target cluster and boostraps
template export k8s resources (use labels like '-l name=prometheus-operator')
test runs some tests againts the target cluster
values show helmfile values for CLOUD and CLUSTER
validate-templates validate generated manifests against supported k8s versions/CRDs, best practices, and defined OPA rules, (provide any arg to limit to the selected cluster)
validate-values validate values for each cluster against JSON schema
x execute command in container
Env flags:
VERBOSE=1; Run otomi CLI in verbose mode
EXIT_FAST=1 Exit the script after first error
"
}
function check_docker_running() {
set +e
docker --version &>/dev/null
set -e
if [ "$?" = "0" ]; then
has_docker=1
else
echo "Otomi CLI expects a running docker daemon!" >&2 && exit 1
fi
}
function evaluate_k8s_context() {
local env_path="${ENV_DIR}/env/clouds/${CLOUD}/${CLUSTER}/.env"
[ ! -f $env_path ] && echo "Error: The file '${env_path}' does not exist" >&2 && exit 1
source $env_path
[[ -z "$K8S_CONTEXT" ]] && echo "Error: The K8S_CONTEXT env is not defined in $env_path" >&2 && exit 1
return 0
}
function validate_k8s_context() {
local context=$(kubectl config current-context)
if [[ "$K8S_CONTEXT" != "$context" ]]; then
echo "Warning: Your current kubernetes context does not match target context: $K8S_CONTEXT"
echo ""
read -p "Would you like to switch kube context to target first? Yn" oki
if [ "${oki:-y}" = "y" ]; then
kubectl config use $K8S_CONTEXT
drun bin/bootstrap.sh 1
else
exit
fi
fi
return 0
}
function set_otomi_image() {
cmd_image="otomi/core:$(otomi_image_tag)"
return 0
}
evaluate_secrets() {
[ ! -f "$ENV_DIR/.sops.yaml" ] && {
echo "Info: The 'secrets.*.yaml files' are not decrypted, because $ENV_DIR/.sops.yaml file is not present"
return 0
}
if [ "$CI" = "" ]; then
local secrets_path="$ENV_DIR/.secrets"
[[ ! -r "$secrets_path" ]] && echo "Error: Unable to find the '$secrets_path' file.\nPlease follow to documentation: $readme_url" >&2 && exit 2
source $secrets_path
fi
prepare_crypt
return 0
}
validate_cluster_env() {
evaluate_secrets
local err
[[ -z "$CLOUD" ]] && echo "Error: The CLOUD environment variable is not set" >&2 && err=1
[[ -z "$CLUSTER" ]] && echo "Error: The CLUSTER environment variable is not set" >&2 && err=1
[[ -n "$err" ]] && exit 2
return 0
}
is_repo() {
[[ "$(basename "$PWD")" = "otomi-core" ]] && echo "Error: Should not be ran from otomi-core" >&2 && exit 2
return 0
}
run_console() {
[ "$1" != "" ] && docker-compose down --remove-orphans && exit
# this will create /tmp/otomi-env:
drun bin/check-console.sh
# now source the fresh env we created for docker-compose
. /tmp/otomi-env
# try to login with the pull secret
set -o pipefail
pass=$(echo "$PULL_SECRET" | base64 --decode | jq '.auths["eu.gcr.io"].password|fromjson')
docker login -u _json_key -p "$pass" "eu.gcr.io"
echo "Starting Otomi Console at http://127.0.0.1:3000"
sh -c "sleep 7 && open http://127.0.0.1:3000" &
docker-compose up
rm /tmp/otomi-env
}
function drun() {
local command=$@
local tmp_volume=''
local stack_volume=''
local socket_volume=''
if [ "$CI" != "" ]; then
[ "$VERBOSE" = "1" ] && echo "Running in CI: $CI"
check_kube_context=0
else
socket_volume="-v /var/run/docker.sock:/var/run/docker.sock"
fi
# execute any kubectl command to refresh access token
if [ $check_kube_context -eq 1 ]; then
evaluate_k8s_context
validate_k8s_context
kubectl version >/dev/null
fi
if [ $mount_stack_dir -eq 1 ]; then
stack_volume="-v ${stack_dir}:${stack_dir}"
fi
if [ "$VERBOSE" = "1" ]; then
verbose_env
fi
# use docker run if has_docker AND either:
# - not in docker
# - in docker AND force docker
if [[ ("$CI" = "") && $has_docker -eq 1 && ("$IN_DOCKER" != "1" || $dind -eq 1) ]]; then
[ "$VERBOSE" = "1" ] && echo "Running dockerized version of command: $command"
docker run $docker_terminal_params --rm \
$stack_volume $socket_volume -v /tmp:/tmp \
-v $GOOGLE_APPLICATION_CREDENTIALS:$GOOGLE_APPLICATION_CREDENTIALS \
-v ${HOME}/.kube/config:/home/app/.kube/config \
-v ${HOME}/.ssh:/home/app/.ssh \
-v ${helm_config}:/home/app/.config/helm \
-v ${HOME}/.config/gcloud:/home/app/.config/gcloud \
-v ${HOME}/.aws:/home/app/.aws \
-v ${HOME}/.azure:/home/app/.azure \
-v ${ENV_DIR}:${stack_dir}/env \
-e GOOGLE_APPLICATION_CREDENTIALS=$GOOGLE_APPLICATION_CREDENTIALS \
-e CUSTOMER="$customer" \
-e VERBOSE="$VERBOSE" \
-e CLOUD="$CLOUD" \
-e IN_DOCKER="1" \
-e GCLOUD_SERVICE_KEY="$GCLOUD_SERVICE_KEY" \
-e CLUSTER="$CLUSTER" \
-e K8S_CONTEXT="$K8S_CONTEXT" \
-e EXIT_FAST="$EXIT_FAST" \
-w $stack_dir \
$cmd_image \
$command
else
[ "$VERBOSE" = "1" ] && echo "Running native version of command: $command"
$command
fi
}
function execute() {
shift 2
case $command in
values)
check_kube_context=0
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER -f helmfile.tpl/helmfile-dump.yaml build | grep -Ev $helmfileOutputHide | sed -e $replacePathsPattern
;;
test)
validate_cluster_env
drun bin/test.sh
;;
hf)
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER $@ | grep -Ev $helmfileOutputHide
;;
apply)
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER $@ apply --skip-deps | grep -Ev $helmfileOutputHide
;;
bash)
check_kube_context=0
docker_terminal_params='-it'
drun bash
;;
bootstrap)
check_kube_context=0
validate_cluster_env
drun bin/bootstrap.sh 1
;;
console)
check_kube_context=0
evaluate_secrets
run_console $@
;;
decrypt)
check_sops_file
check_kube_context=0
evaluate_secrets
if [ "$@" != "" ]; then
for f in $@; do
echo "Decrypting $f"
drun helm secrets dec ./env/$f >/dev/null
done
exit
else
drun bin/crypt.sh decrypt
fi
;;
deploy)
validate_cluster_env
drun bin/deploy.sh | grep -Ev $helmfileOutputHide
;;
diff)
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER $@ diff --skip-deps | grep -Ev $helmfileOutputHide
;;
encrypt)
check_sops_file
check_kube_context=0
evaluate_secrets
if [ "$@" != "" ]; then
for f in $@; do
echo "Encrypting $f"
drun helm secrets enc ./env/$f >/dev/null
done
exit
else
drun bin/crypt.sh encrypt
fi
;;
sync)
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER $@ sync --skip-deps | grep -Ev $helmfileOutputHide
;;
template)
check_kube_context=0
validate_cluster_env
set -o pipefail
drun helmfile -e $CLOUD-$CLUSTER --quiet $@ template --skip-deps | grep -Ev $helmfileOutputHideTpl
;;
rotate-keys)
check_sops_file
check_kube_context=0
evaluate_secrets
drun bin/crypt.sh rotate
;;
switch)
evaluate_k8s_context
kubectl config use $K8S_CONTEXT
drun bin/bootstrap.sh 1
;;
gen-drone)
check_kube_context=0
evaluate_secrets
drun bin/gen-drone.sh
;;
test)
validate_cluster_env
evaluate_secrets
drun bin/test.sh
;;
validate-templates)
if [ "$@" != "" ]; then
validate_cluster_env
fi
evaluate_secrets
drun bin/validate-templates.sh $@
;;
x)
check_kube_context=0
drun $@
;;
commit)
check_kube_context=0
is_repo
evaluate_secrets
git -C $ENV_DIR pull
drun bin/validate-values.sh
drun bin/pre-commit.sh
git add . && git commit -m 'Manual commit' --no-verify
;;
pull)
check_kube_context=0
is_repo
evaluate_secrets
git -C $ENV_DIR pull
drun bin/bootstrap.sh 1
;;
validate-values)
check_kube_context=0
evaluate_secrets
drun bin/validate-values.sh
;;
*)
show_usage
[ "$1" != "" ] && echo "Error: Unknown command: $@" >&2 && exit 1
;;
esac
}
[[ -z "$command" ]] && echo "Error: Missing command argument" >&2 && show_usage && exit 2
set_env_and_stack_dir
set_otomi_image
set_helm_config
check_docker_running
execute $command $@