-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathscript.sh
executable file
·279 lines (229 loc) · 7.69 KB
/
script.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
#!/bin/bash
set -e
# debug
# set -x
if [ ! -f ~/.kube/config ]; then
echo "~/.kube/config missing"
echo "if running github action, action kubernetes_set_env must be run before"
echo "this is needed to read/write db password in Kubernetes secret"
exit 1
fi
# Checking envvars
if [ -z "$ACTION" ]; then
echo "ACTION env var is missing"
exit=1
fi
if [ -z "$DBMS" ]; then
echo "DBMS env var is missing"
exit=1
fi
if [ -z "$CLUSTER" ]; then
echo "CLUSTER env var is missing"
exit=1
fi
if [ -z "$PREFIX_NAME" ]; then
echo "PREFIX_NAME env var is missing"
exit=1
fi
if [ -z "$DUMP_FILENAME" ]; then
echo "DUMP_FILENAME env var is missing"
exit=1
fi
if [ -z "$DUMP_SUBSTITUTIONS" ]; then
echo "DUMP_SUBSTITUTIONS env var is missing"
exit=1
fi
if [ -n "$exit" ]; then
exit 1
fi
CLUSTER=${CLUSTER}
CLUSTER_UC=$(echo $CLUSTER | tr a-z A-Z)
# If in Github actions context
if [ ! -z "$SECRETS_JSON" ]; then
function get_input() {
local key=$1
jq -r ".$key // empty" <<EOF
${SECRETS_JSON}
EOF
}
SQL_INSTANCE="$(get_input SQL_INSTANCE_${CLUSTER_UC})"
SQL_PROXY_AUTH_BASE64="$(get_input SQL_PROXY_AUTH_BASE64_${CLUSTER_UC})"
SQL_ADMIN_USER="$(get_input SQL_ADMIN_USER_${CLUSTER_UC})"
SQL_ADMIN_PASSWORD="$(get_input SQL_ADMIN_PASSWORD_${CLUSTER_UC})"
if [ -z "$SQL_PROXY_AUTH_BASE64" ]; then
echo "SQL_PROXY_AUTH_BASE64_${CLUSTER_UC} secret is missing"
exit=1
fi
if [ -z "$SQL_INSTANCE" ]; then
echo "SQL_INSTANCE_${CLUSTER_UC} secret is missing"
exit=1
fi
if [ -z "$SQL_ADMIN_USER" ]; then
echo "SQL_ADMIN_USER_${CLUSTER_UC} secret is missing"
exit=1
fi
if [ -z "$SQL_ADMIN_PASSWORD" ]; then
echo "SQL_ADMIN_PASSWORD_${CLUSTER_UC} secret is missing"
exit=1
fi
if [ -n "$exit" ]; then
exit 1
fi
# Else in a local docker context
else
# Vars should be provided by env
if [ -z "$SQL_INSTANCE" ]; then
echo "SQL_INSTANCE env var is missing"
exit=1
fi
if [ -z "$SQL_PROXY_AUTH_BASE64" ]; then
echo "SQL_PROXY_AUTH_BASE64 env var is missing"
exit=1
fi
if [ -z "$SQL_ADMIN_USER" ]; then
echo "SQL_ADMIN_USER env var is missing"
exit=1
fi
if [ -z "$SQL_ADMIN_PASSWORD" ]; then
echo "SQL_ADMIN_PASSWORD env var is missing"
exit=1
fi
if [ -z "$DB_NAME" ]; then
echo "DB_NAME env var is missing"
exit=1
fi
if [ -z "$DB_USER" ]; then
echo "DB_USER env var is missing"
exit=1
fi
if [ -n "$exit" ]; then
exit 1
fi
fi
AUTH_FILE=/tmp/gce-cloudsql-proxy-key.json
# create sql auth config
base64 -d <<EOF > $AUTH_FILE
$SQL_PROXY_AUTH_BASE64
EOF
if [[ $DBMS == 'mysql' ]]; then
DBMS_PORT=3306
CONNPARAMS="-h 127.0.0.1 -P $DBMS_PORT -u $SQL_ADMIN_USER -p$SQL_ADMIN_PASSWORD"
MYSQL_OPTIONS="${CONNPARAMS} --connect-timeout=1"
MYSQLDUMP_OPTIONS="${CONNPARAMS} --opt $DB_NAME --set-gtid-purged=OFF --single-transaction"
mysql_q="mysql $MYSQL_OPTIONS -e"
else
DBMS_PORT=5432
# Don't set $DB_NAME in CONNPARAMS because we need to be connected to another one (e.g. postgres) to create/delete $DB_NAME
CONNPARAMS="--dbname=postgresql://${SQL_ADMIN_USER}:${SQL_ADMIN_PASSWORD}@127.0.0.1:${DBMS_PORT}"
psql_q="psql $CONNPARAMS --tuples-only --command"
fi
# launch proxy
./cloud_sql_proxy \
-instances=${SQL_INSTANCE}=tcp:0.0.0.0:$DBMS_PORT \
-credential_file $AUTH_FILE \
&
# test db connection via proxy
for i in $(seq 1 10); do
if [[ $DBMS == 'mysql' ]]; then
mysql $MYSQL_OPTIONS -e "select 1;" && break;
else
pg_isready $CONNPARAMS && break;
fi
if [ $i -eq 10 ]; then
echo no connection to $DBMS after $i retries
exit 1
fi
sleep 1
done
# this function replace all search/replace from the DUMP_SUBSTITUTIONS input
# this can be reverted for db exports
function dump_substitute() {
FILE=$1
REVERT=$2
for i in $(seq 0 $(expr $(jq ". | length" substitutions.json) - 1 )); do
if [ -z "$REVERT" ]; then
sed -i -e 's#'$(jq -r ".[$i].search" substitutions.json)'#'$(jq -r ".[$i].replace" substitutions.json)'#g' $FILE
else
sed -i -e 's#'$(jq -r ".[$i].replace" substitutions.json)'#'$(jq -r ".[$i].search" substitutions.json)'#g' $FILE
fi
done
}
cat > substitutions.json <<EOF
$DUMP_SUBSTITUTIONS
EOF
if [ "${ACTION}" == "create" ]; then
# get or set db password in Kubernetes Secret
# if secret exist, read password, else create it
if kubectl get secret ${PREFIX_NAME}-${DBMS}; then
if [[ $DBMS == 'mysql' ]]; then
JSON_PWD_PATH='{.data.mysql-password}';
else
JSON_PWD_PATH='{.data.postgresql-password}';
fi
DB_PASSWORD=$(kubectl get secret ${PREFIX_NAME}-$DBMS -o=jsonpath=$JSON_PWD_PATH | base64 -d)
else
DB_PASSWORD=$(openssl rand -base64 48 | tr -dc A-Za-z0-9 | head -c14)
kubectl create secret generic ${PREFIX_NAME}-${DBMS} \
--from-literal=${DBMS}-password=$DB_PASSWORD
fi
if [[ $DBMS == 'mysql' ]]; then
# create user if not exists
$mysql_q "create user if not exists '$DB_USER'@'%' identified by '$DB_PASSWORD';"
# create database if not exists
$mysql_q "create database if not exists \`$DB_NAME\`;"
# grant privileges
$mysql_q "grant all privileges on \`$DB_NAME\`.* TO '$DB_USER'@'%';"
else
# create user if not exists
$psql_q "SELECT 1 FROM pg_roles WHERE rolname = '${DB_USER}'" | grep -q 1 || $psql_q "CREATE ROLE ${DB_USER} WITH PASSWORD '${DB_PASSWORD}'; GRANT ${DB_USER} TO \"${SQL_ADMIN_USER}\";"
# create database if not exists
$psql_q "SELECT 1 FROM pg_database WHERE datname = '${DB_NAME}'" | grep -q 1 || $psql_q "CREATE DATABASE $DB_NAME OWNER ${DB_USER};"
fi
fi
if [ "${ACTION}" == "import" ]; then
# import database
if [ ! -f "${DUMP_FILENAME}.sql.gz" ]; then
echo "Nothing to import, ${DUMP_FILENAME}.sql.gz does not exist"
else
echo "some tables are excluded for ci/cd performance"
gunzip --keep ${DUMP_FILENAME}.sql.gz
# call DUMP_SUBSTITUTIONS replacement function
dump_substitute ${DUMP_FILENAME}.sql
if [[ $DBMS == 'mysql' ]]; then
cat ${DUMP_FILENAME}.sql | mysql $MYSQL_OPTIONS $DB_NAME
else
psql $CONNPARAMS/${DB_NAME} < ${DUMP_FILENAME}.sql
fi
fi
fi
if [ "${ACTION}" == "export" ] ;then
DATEDJ=$(date +%F)
if [[ $DBMS == 'mysql' ]]; then
mysqldump $MYSQLDUMP_OPTIONS > ${DATEDJ}-dump.sql
else
pg_dump $CONNPARAMS/${DB_NAME} > ${DATEDJ}-dump.sql
fi
# call DUMP_SUBSTITUTIONS replacement function, revert mode during export
dump_substitute ${DATEDJ}-dump.sql revert
gzip ${DATEDJ}-dump.sql
fi
if [ "${ACTION}" == "delete" ]; then
if [ "${PREFIX_NAME}" == "main" -o "${PREFIX_NAME}" == "prod" ]; then
echo "Cannot delete production dbs"
exit 1
else
if [[ $DBMS == 'mysql' ]]; then
$mysql_q "drop database if exists \`$DB_NAME\`;"
$mysql_q "drop user if exists '$DB_USER'@'%';"
else
# drop database if it exists
# WITH (FORCE) remove database even if user is connected to it
$psql_q "SELECT 1 FROM pg_database WHERE datname = '${DB_NAME}'" | grep -q 1 && $psql_q "DROP DATABASE $DB_NAME WITH (FORCE);"
# drop user if it exists
$psql_q "SELECT 1 FROM pg_roles WHERE rolname = '${DB_USER}'" | grep -q 1 && $psql_q "DROP USER $DB_USER;"
fi
kubectl delete secret ${PREFIX_NAME}-${DBMS}
fi
fi
# stop sql proxy
kill %1