-
Notifications
You must be signed in to change notification settings - Fork 179
/
Copy pathutils.sh
executable file
·364 lines (327 loc) · 10.5 KB
/
utils.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
#!/usr/bin/env bash
LIBDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" || exit; pwd)"
# shellcheck source=prow/scripts/lib/log.sh
source "${LIBDIR}"/log.sh
# utils::check_required_vars checks if all provided variables are initialized
#
# Arguments
# $1 - list of variables
function utils::check_required_vars() {
local discoverUnsetVar=false
for var in "$@"; do
if [ -z "${!var}" ] ; then
echo "ERROR: $var is not set"
discoverUnsetVar=true
fi
done
if [ "${discoverUnsetVar}" = true ] ; then
exit 1
fi
}
# utils::generate_self_signed_cert generates self-signed certificate for the given domain
#
# Optional exported variables
# CERT_VALID_DAYS - days when the certificate is valid
# Arguments
# $1 - domain name
function utils::generate_self_signed_cert() {
if [ -z "$1" ]; then
echo "Domain name is empty. Exiting..."
exit 1
fi
local DOMAIN
DOMAIN=$1
local CERT_PATH
local KEY_PATH
tmpDir=$(mktemp -d)
CERT_PATH="${tmpDir}/cert.pem"
KEY_PATH="${tmpDir}/key.pem"
CERT_VALID_DAYS=${CERT_VALID_DAYS:-5}
openssl req -x509 -nodes -days "${CERT_VALID_DAYS}" -newkey rsa:4069 \
-subj "/CN=${DOMAIN}" \
-reqexts SAN -extensions SAN \
-config <(cat /etc/ssl/openssl.cnf \
<(printf "\\n[SAN]\\nsubjectAltName=DNS:*.%s" "${DOMAIN}")) \
-keyout "${KEY_PATH}" \
-out "${CERT_PATH}"
TLS_CERT=$(base64 "${CERT_PATH}" | tr -d '\n')
TLS_KEY=$(base64 "${KEY_PATH}" | tr -d '\n')
echo "${TLS_CERT}"
echo "${TLS_KEY}"
rm "${KEY_PATH}"
rm "${CERT_PATH}"
}
# utils::generate_letsencrypt_cert generates let's encrypt certificate for the given domain
#
# Expected exported variables
# GOOGLE_APPLICATION_CREDENTIALS
#
# Arguments
# $1 - domain name
function utils::generate_letsencrypt_cert() {
if [ -z "$1" ]; then
echo "Domain name is empty. Exiting..."
exit 1
fi
local DOMAIN
DOMAIN=$1
log::info "Generate lets encrypt certificate"
mkdir -p ./letsencrypt
cp "${GOOGLE_APPLICATION_CREDENTIALS}" letsencrypt
docker run --name certbot \
--rm \
-v "$(pwd)/letsencrypt:/etc/letsencrypt" \
-v "$(pwd)/certbot-log:/var/log/letsencrypt" \
-v "/prow-tools:/prow-tools" \
-e "GOOGLE_APPLICATION_CREDENTIALS=/etc/letsencrypt/service-account.json" \
certbot/certbot \
certonly \
-m "[email protected]" \
--agree-tos \
--no-eff-email \
--server https://acme-v02.api.letsencrypt.org/directory \
--manual \
--preferred-challenges dns \
--manual-auth-hook /prow-tools/certbotauthenticator \
--manual-cleanup-hook "/prow-tools/certbotauthenticator -D" \
-d "*.${DOMAIN}"
TLS_CERT=$(base64 -i ./letsencrypt/live/"${DOMAIN}"/fullchain.pem | tr -d '\n')
export TLS_CERT
TLS_KEY=$(base64 -i ./letsencrypt/live/"${DOMAIN}"/privkey.pem | tr -d '\n')
export TLS_KEY
}
# utils::receive_from_vm receives file(s) from Google Compute Platform over scp
#
# Arguments
# $1 - compute zone
# $2 - remote name
# $3 - remote path
# $4 - local path
function utils::receive_from_vm() {
if [ -z "$1" ]; then
echo "Zone is empty. Exiting..."
exit 1
fi
if [ -z "$2" ]; then
echo "Remote name is empty. Exiting..."
exit 1
fi
if [ -z "$3" ]; then
echo "Remote path is empty. Exiting..."
exit 1
fi
if [ -z "$4" ]; then
echo "Local path is empty. Exiting..."
exit 1
fi
local ZONE=$1
local REMOTE_NAME=$2
local REMOTE_PATH=$3
local LOCAL_PATH=$4
for i in $(seq 1 5); do
[[ ${i} -gt 1 ]] && log::info 'Retrying in 15 seconds..' && sleep 15;
gcloud compute scp --strict-host-key-checking=no --quiet --recurse --zone="${ZONE}" "${REMOTE_NAME}":"${REMOTE_PATH}" "${LOCAL_PATH}" && break;
[[ ${i} -ge 5 ]] && log::error "Failed after $i attempts." && exit 1
done;
}
# utils::send_to_vm sends file(s) to Google Compute Platform over scp
#
# Arguments
# $1 - compute zone
# $2 - remote name
# $3 - local path
# $4 - remote path
function utils::send_to_vm() {
if [ -z "$1" ]; then
echo "Zone is empty. Exiting..."
exit 1
fi
if [ -z "$2" ]; then
echo "Remote name is empty. Exiting..."
exit 1
fi
if [ -z "$3" ]; then
echo "Local path is empty. Exiting..."
exit 1
fi
if [ -z "$4" ]; then
echo "Remote path is empty. Exiting..."
exit 1
fi
local ZONE=$1
local REMOTE_NAME=$2
local LOCAL_PATH=$3
local REMOTE_PATH=$4
for i in $(seq 1 5); do
[[ ${i} -gt 1 ]] && log::info 'Retrying in 15 seconds..' && sleep 15;
gcloud compute scp --strict-host-key-checking=no --quiet --recurse --zone="${ZONE}" "${LOCAL_PATH}" "${REMOTE_NAME}":"${REMOTE_PATH}" && break;
[[ ${i} -ge 5 ]] && log::error "Failed after $i attempts." && exit 1
done;
}
# utils::compress_send_to_vm compresses and sends file(s) to Google Compute Platform over scp
#
# Arguments
# $1 - compute zone
# $2 - remote name
# $3 - local path
# $4 - remote path
function utils::compress_send_to_vm() {
if [ -z "$1" ]; then
echo "Zone is empty. Exiting..."
exit 1
fi
if [ -z "$2" ]; then
echo "Remote name is empty. Exiting..."
exit 1
fi
if [ -z "$3" ]; then
echo "Local path is empty. Exiting..."
exit 1
fi
if [ -z "$4" ]; then
echo "Remote path is empty. Exiting..."
exit 1
fi
local ZONE=$1
local REMOTE_NAME=$2
local LOCAL_PATH=$3
local REMOTE_PATH=$4
TMP_DIRECTORY=$(mktemp -d)
tar -czf "${TMP_DIRECTORY}/pack.tar.gz" -C "${LOCAL_PATH}" "."
#shellcheck disable=SC2088
utils::send_to_vm "${ZONE}" "${REMOTE_NAME}" "${TMP_DIRECTORY}/pack.tar.gz" "~/"
gcloud compute ssh --strict-host-key-checking=no --quiet --zone="${ZONE}" --command="mkdir ${REMOTE_PATH} && tar -xf ~/pack.tar.gz -C ${REMOTE_PATH}" --ssh-flag="-o ServerAliveInterval=30" "${REMOTE_NAME}"
rm -rf "${TMP_DIRECTORY}"
}
# utils::deprovision_gardener_cluster deprovisions a Gardener cluster
#
# Arguments
# $1 - Gardener project name
# $2 - Gardener cluster name
# $3 - path to kubeconfig
function utils::deprovision_gardener_cluster() {
if [ -z "$1" ]; then
echo "Project name is empty. Exiting..."
exit 1
fi
if [ -z "$2" ]; then
echo "Cluster name is empty. Exiting..."
exit 1
fi
if [ -z "$3" ]; then
echo "Kubeconfig path is empty. Exiting..."
exit 1
fi
GARDENER_PROJECT_NAME=$1
GARDENER_CLUSTER_NAME=$2
GARDENER_CREDENTIALS=$3
local NAMESPACE="garden-${GARDENER_PROJECT_NAME}"
kubectl --kubeconfig "${GARDENER_CREDENTIALS}" -n "${NAMESPACE}" annotate shoot "${GARDENER_CLUSTER_NAME}" confirmation.gardener.cloud/deletion=true --overwrite
kubectl --kubeconfig "${GARDENER_CREDENTIALS}" -n "${NAMESPACE}" delete shoot "${GARDENER_CLUSTER_NAME}" --wait=false
}
# utils::save_psp_list generates pod-security-policy list and saves it to json file
#
# Arguments
# $1 - Name of the output json file
function utils::save_psp_list() {
if [ -z "$1" ]; then
echo "File name is empty. Exiting..."
exit 1
fi
local output_path=$1
# this is false-positive as we need to use single-quotes for jq
# shellcheck disable=SC2016
PSP_LIST=$(kubectl get pods --all-namespaces -o json | jq '{ pods: [ .items[] | .metadata.ownerReferences[0].name as $owner | .metadata.annotations."kubernetes.io\/psp" as $psp | { name: .metadata.name, namespace: .metadata.namespace, owner: $owner, psp: $psp} ] | group_by(.name) | map({ name: .[0].name, namespace: .[0].namespace, owner: .[0].owner, psp: .[0].psp }) | sort_by(.psp, .name)}' )
echo "${PSP_LIST}" > "${output_path}"
}
# utils::save_env_file creates a .env file with all provided variables
#
# Arguments
# $1 - list of variables
function utils::save_env_file() {
touch .env
for var in "$@"; do
if [ -z "${!var}" ] ; then
echo "INFO: $var is not set"
continue
fi
echo "${var}"=\""$(printenv "${var}")"\" >> .env
done
}
function utils::describe_nodes() {
log::info "Describe nodes"
{
kubectl describe nodes
kubectl top nodes
kubectl top pods --all-namespaces
} > "${ARTIFACTS}/describe_nodes.txt"
grep "System OOM encountered" "${ARTIFACTS}/describe_nodes.txt"
last=$?
if [[ $last -eq 0 ]]; then
log::banner "OOM event found"
fi
}
function utils::oom_get_output() {
if [ ! -e "${ARTIFACTS}/describe_nodes.txt" ]; then
utils::describe_nodes
fi
log::info "Download OOM events details"
pods=$(kubectl get pod -l "name=oom-debug" -o=jsonpath='{.items[*].metadata.name}')
for pod in $pods; do
kubectl logs "$pod" -c oom-debug > "${ARTIFACTS}/$pod.txt"
done
debugFiles=$(ls -1 "${ARTIFACTS}"/oom-debug-*.txt)
for debugFile in $debugFiles; do
grep "OOM event received" "$debugFile" > /dev/null
last=$?
if [[ $last -eq 0 ]]; then
log::info "Print OOM events details"
cat "$debugFile"
else
rm "$debugFile"
fi
done
}
function utils::debug_oom() {
# run oom debug pod
kubectl apply -f "${TEST_INFRA_SOURCES_DIR}/prow/scripts/resources/debug-container.yaml"
}
# utils::kubeaudit_create_report downlaods kubeaudit if necessary and checks for privileged containers
# Arguments
# $1 - Name of the output log file
function utils::kubeaudit_create_report() {
if [ -z "$1" ]; then
echo "Kubeuadit file path is empty. Exiting..."
exit 1
fi
local kubeaudit_file=$1
log::info "Gather Kubeaudit logs"
if ! [[ -x "$(command -v ./kubeaudit)" ]]; then
curl -sL https://github.com/Shopify/kubeaudit/releases/download/v0.11.8/kubeaudit_0.11.8_linux_amd64.tar.gz | tar -xzO kubeaudit > ./kubeaudit
chmod +x ./kubeaudit
fi
# kubeaudit returns non-zero exit code when it finds issues
# In the context of this job we just want to grab the logs
# It should not break the execution of this script
./kubeaudit privileged privesc -p json > "$kubeaudit_file" || true
}
# utils::kubeaudit_check_report analyzes kubeaudit.log file and returns list of non-compliant resources in kyma-system namespace
# Arguments
# $1 - Name of the input log file
# S2 - optional, name of the resource namespace. Defaults to "kyma-system"
function utils::kubeaudit_check_report() {
if [ -z "$1" ]; then
echo "Kubeuadit file path is empty. Exiting..."
exit 1
fi
local kubeaudit_file=$1
incompliant_resources=$(jq -c 'select( .ResourceNamespace == "kyma-system" )' < "$kubeaudit_file")
compliant=$(echo "$incompliant_resources" | jq -r -s 'if length == 0 then "true" else "false" end')
if [[ "$compliant" == "true" ]]; then
log::info "All resources are compliant"
else
log::error "Not all resources are compliant:"
echo "$incompliant_resources"
exit 1
fi
}