-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathnubis-deploy
executable file
·329 lines (303 loc) · 12.2 KB
/
nubis-deploy
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
#!/bin/bash
# shellcheck disable=SC1117
MOUNT_VOLUME='/nubis/data'
WORKING_PATH='/nubis/work'
# Make sure we capture failures from pipe commands
set -o pipefail
show_help () {
echo -en "\nUsage:docker run --interactive --tty --env-file ~/.docker_env -v $PWD:/nubis/data nubis-deploy [command]\n\n"
echo -en "Commands:\n"
echo -en " --help Print this help message\n"
echo -en " --domain Set domain for looking up state (Default: nubis.allizom.org)\n"
echo -en " --skip-init Skip Terraform initialization\n"
echo -en " Must be used with a volume mount only after initial run\n"
echo -en " plan Show the deployment plan\n"
echo -en " apply Apply the deployment\n"
echo -en " destroy Destroy the deployment\n"
echo -en " show Show the state of the deployment\n"
echo -en " output Display outputs for the deployment\n"
echo -en " state Work with the state of the deployment\n"
echo -en " account Invoke the account-deploy script\n\n"
exit 0
}
setup-terraform () {
# Test for the existance of the variables file
MOUNT_TERRAFORM_PATH="${MOUNT_VOLUME}/nubis/terraform"
if [ ! -f "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" ]; then
echo -e "\033[1;31mERROR: 'terraform.tfvars' file not found in ${MOUNT_TERRAFORM_PATH}\033[0m"
exit 1
fi
# Test for the existance of state.tf file
if [ ! -f "${MOUNT_TERRAFORM_PATH}/state.tf" ];then
echo -e "\033[0;32mWARNING: 'state.tf' file not found in ${MOUNT_TERRAFORM_PATH}\033[0m"
echo -e "\033[0;32mWARNING: 'state.tf' file should contain the following lines:\033[0m"
echo -e "\033[0;32mWARNING: terraform { backend \"s3\" {} }\033[0m"
echo -e "\033[0;32mWARNING: Adding temporary state.tf file.\033[0m"
# Workaround for TF bug https://github.com/hashicorp/terraform/issues/15761
TF_CONSUL_WORKAROUND="${MOUNT_TERRAFORM_PATH}/nubis-state-${RANDOM}.tf"
echo 'terraform { backend "s3" { } }' > "${TF_CONSUL_WORKAROUND}"
export TF_CONSUL_WORKAROUND
fi
ACCOUNT=$(json2hcl -reverse < "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" | jq -r .account )
# Make sure we got an account name
if [ "${ACCOUNT}" == 'null' ]; then
echo -e "\033[1;31mERROR: 'account' not set ${MOUNT_TERRAFORM_PATH}/terraform.tfvars\033[0m"
exit 1
else
if [ "${ACCOUNT}" != "${NUBIS_ACCOUNT:-$ACCOUNT}" ]; then
echo -e "\033[1;31mERROR: Specified account differs from account in nubis/terraform/terraform.tfvars\033[0m"
echo -e "\033[1;31m'${NUBIS_ACCOUNT:-$ACCOUNT}' != '${ACCOUNT}'\033[0m"
echo -e "\033[1;31mBoth must match. Unable to continue.\033[0m"
exit 1
fi
export ACCOUNT
fi
ARENA=$(json2hcl -reverse < "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" | jq -r .arena )
# Make sure we got an arena
if [ "${ARENA}" == 'null' ]; then
echo -e "\033[0;32mWARNING: 'arena' not set ${MOUNT_TERRAFORM_PATH}/terraform.tfvars\033[0m"
echo -e "\033[0;32mWARNING: Defaulting 'arena' to 'core'\033[0m"
echo -e "\033[0;32mWARNING: 'arena' will be required in a future release\033[0m"
ARENA='core'; export ARENA
else
export ARENA
fi
ENVIRONMENT=$(json2hcl -reverse < "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" | jq -r .environment )
# Make sure we got an arena
if [ "${ENVIRONMENT}" == 'null' ]; then
echo -e "\033[1;31mERROR: 'environment' not set ${MOUNT_TERRAFORM_PATH}/terraform.tfvars\033[0m"
exit 1
else
export ENVIRONMENT
fi
DEPLOYMENT_REGION=$(json2hcl -reverse < "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" | jq -r .region )
# Make sure we got a deployment region
if [ "${DEPLOYMENT_REGION}" == 'null' ]; then
echo -e "\033[1;31mERROR: 'region' not set ${MOUNT_TERRAFORM_PATH}/terraform.tfvars\033[0m"
exit 1
else
export DEPLOYMENT_REGION
fi
SERVICE_NAME=$(json2hcl -reverse < "${MOUNT_TERRAFORM_PATH}/terraform.tfvars" | jq -r .service_name )
# Make sure we got a service name
if [ "${SERVICE_NAME}" == 'null' ]; then
echo -e "\033[1;31mERROR: 'service_name' not set ${MOUNT_TERRAFORM_PATH}/terraform.tfvars\033[0m"
exit 1
else
export SERVICE_NAME
fi
STATE_BUCKET=$(curl -s "http://state.nubis.${ACCOUNT}.${NUBIS_DOMAIN:-nubis.allizom.org}/aws/${DEPLOYMENT_REGION}/${ARENA}.tfstate" | \
jq -r ' .modules[] | select(.path == ["root"]) | .outputs.apps_state_bucket')
# Make sure we have a state bucket
if [ "${STATE_BUCKET}" == 'null' ] || [ -z "${STATE_BUCKET}" ]; then
echo -e "\033[1;32mWARNING: Could not find state bucket in account '${ARENA}.tfstate' file using:\033[0m"
echo -e "\033[1;32mWARNING: 'curl -s \"http://state.nubis.${ACCOUNT}.${NUBIS_DOMAIN:-'nubis.allizom.org'}/aws/${DEPLOYMENT_REGION}/${ARENA}.tfstate\"'\033[0m"
# Default checking state bucket using awscli
echo -e "\033[0;32mWARNING: Defaulting state bucket discovery in S3 using awscli\033[0m"
STATE_BUCKET=$(aws s3 ls | grep nubis-apps-state | awk '{print $3}')
if [ "${STATE_BUCKET}" == 'null' ] || [ -z "${STATE_BUCKET}" ]; then
echo -e "\033[1;31mERROR: Could not find S3 state bucket using:\033[0m"
echo -e "\033[1;31mERROR: 'aws s3 ls | grep nubis-apps-state | awk \"{ print \$3 }\"'\033[0m"
exit 1
else
export STATE_BUCKET
fi
else
export STATE_BUCKET
fi
BUCKET_REGION=$(aws s3api get-bucket-location --bucket "${STATE_BUCKET}" | jq -r '.LocationConstraint // "us-east-1"')
# Make sure we got a state bucket region
if [ "${BUCKET_REGION}" == 'null' ]; then
echo -e "\033[1;31mERROR: Could not look up bucket location using:\033[0m"
echo -e "\033[1;31mERROR: 'aws s3api get-bucket-location --bucket \"${STATE_BUCKET}\"'\033[0m"
exit 1
else
export BUCKET_REGION
fi
# Set up the working terraform path and create the working directory
export TERRAFORM_PATH="${WORKING_PATH}/${SERVICE_NAME}/nubis/terraform"
if [ ! -d "${WORKING_PATH}/${SERVICE_NAME}" ]; then
echo "Creating directory ${WORKING_PATH}/${SERVICE_NAME}"
if ! mkdir "${WORKING_PATH}/${SERVICE_NAME}" ; then
echo -e "\033[1;31mERROR: Failed running 'mkdir ${WORKING_PATH}/${SERVICE_NAME}'\033[0m"
exit 1
fi
fi
# Determine if we have a tty and set input accordingly
if ! tty > /dev/null 2>&1 ; then
echo "Info: setting -input=false"
TTY_INPUT='false'
else
echo "Info: setting -input=true"
TTY_INPUT='true'
fi
export TTY_INPUT
}
setup-deploy-dir () {
# Skip any downloaded terraform submodules.
#+ Terraform modules contain symlinks with full paths that are not valid in
#+ the container.
RSYNC=( 'rsync' '-auz' )
RSYNC_EXCLUDES=( '--exclude=SEC,*.pid' )
RSYNC_EXCLUDES+=('--exclude=.terraform' )
RSYNC_EXCLUDES+=( '--exclude=.git*' )
RSYNC_EXCLUDES+=( '--exclude=.travis.yml' )
RSYNC_EXCLUDES+=( '--exclude=terraform.tfstate*' )
RSYNC_EXCLUDES+=( '--delete-excluded' )
if ! "${RSYNC[@]}" "${RSYNC_EXCLUDES[@]}" -x "${MOUNT_VOLUME}/" "${WORKING_PATH}/${SERVICE_NAME}/" ; then
echo -e "\033[1;31mERROR: Failed to rsync files\033[0m"
exit 1
fi
}
terraform-init () {
if ! cd "${TERRAFORM_PATH}"; then
echo -e "\033[1;31mERROR: Could not cd into '${TERRAFORM_PATH}'\033[0m"
exit 1
fi
if ! terraform init \
-input="${TTY_INPUT}" \
-upgrade=true \
-backend-config="region=${BUCKET_REGION}" \
-backend-config="key=terraform/${DEPLOYMENT_REGION}/${ARENA}/${ENVIRONMENT}/${SERVICE_NAME}" \
-backend-config="bucket=${STATE_BUCKET}"
then
echo -e "\033[1;31mERROR: Could not initialize teraform\033[0m"
exit 1
fi
}
terraform-apply () {
if ! cd "${TERRAFORM_PATH}" ; then
echo -e "\033[1;31mERROR: Could not cd into '${TERRAFORM_PATH}'\033[0m"
exit 1
fi
if ! terraform plan -input="${TTY_INPUT}" -out=".terraform/terraform.plan"; then
echo -e "\033[1;31mERROR: Terraform plan failed. Not applying plan\033[0m"
exit 1
fi
if [ "${#@}" != 0 ]; then
if ! terraform apply -input="${TTY_INPUT}" "${@}" ".terraform/terraform.plan" ; then
echo -e "\033[1;31mERROR: Failed running 'terraform apply -input=${TTY_INPUT} ${*} .terraform/terraform.plan'\033[0m"
exit 1
fi
else
if ! terraform apply -input="${TTY_INPUT}" ".terraform/terraform.plan" ; then
echo -e "\033[1;31mERROR: Failed running 'terraform apply -input=${TTY_INPUT} .terraform/terraform.plan'\033[0m"
exit 1
fi
fi
# Copy Terraform files to the S3 bucket
echo -e "\nUploading Terraform assets to s3"
if ! aws s3 sync --delete --region "${BUCKET_REGION}" --exclude ".terraform*" "${TERRAFORM_PATH}/" "s3://${STATE_BUCKET}/terraform/${DEPLOYMENT_REGION}/${ARENA}/${ENVIRONMENT}/${SERVICE_NAME}-terraform/" ; then
echo -e "\033[1;31mERROR: Failed uploading assets to S3\033[0m"
exit 1
fi
# Clean up temporary file if we created one
if [ -f "${TF_CONSUL_WORKAROUND}" ];then
rm "${TF_CONSUL_WORKAROUND}"
fi
}
terraform-do () {
declare -a _ACTION; _ACTION=( "${@}" )
if ! cd "${TERRAFORM_PATH}" ; then
echo -e "\033[1;31mERROR: Could not cd into '${TERRAFORM_PATH}'\033[0m"
exit 1
fi
if ! terraform "${_ACTION[@]}" ; then
echo -e "\033[1;31mERROR: Failed running 'terraform ${_ACTION[*]}'\033[0m"
exit 1
fi
# Clean up temporary file if we created one
if [ -f "${TF_CONSUL_WORKAROUND}" ];then
rm "${TF_CONSUL_WORKAROUND}"
fi
}
# Grab and setup called options
while [ "$1" != "" ]; do
case $1 in
-x | --debug )
set -x
export TF_LOG='DEBUG'
;;
-h | --help | help )
show_help
;;
--skip-init )
SKIP_INIT='1'
;;
-a | --account | --account-name )
NUBIS_ACCOUNT="$2"
shift
;;
-d | --domain )
NUBIS_DOMAIN="${2}"
shift
;;
account )
shift
if [ "${#@}" == 0 ]; then
account-deploy
else
account-deploy --account "${NUBIS_ACCOUNT}" "${@}"
fi
shift "${#@}"
GOT_COMMAND=1
;;
plan )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-do plan -input="${TTY_INPUT}" "${@}"
GOT_COMMAND=1
;;
apply )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-apply "${@}"
GOT_COMMAND=1
;;
destroy )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-do destroy -input="${TTY_INPUT}" "${@}"
GOT_COMMAND=1
;;
show )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-do show "${@}"
GOT_COMMAND=1
;;
output | outputs )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-do output "${@}"
GOT_COMMAND=1
;;
state )
shift
setup-terraform
setup-deploy-dir
[[ ${SKIP_INIT:-0} == 0 ]] && terraform-init || echo "Skipping terraform-init"
terraform-do state "${@}"
GOT_COMMAND=1
;;
* )
show_help
;;
esac
shift
done
# If we did not get a valid command print the help message
if [ "${GOT_COMMAND:-0}" == 0 ]; then
help
exit 1
fi