forked from GoogleCloudPlatform/emblem
-
Notifications
You must be signed in to change notification settings - Fork 0
/
setup.sh
executable file
·308 lines (260 loc) · 12.4 KB
/
setup.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
#!/usr/bin/env bash
# Copyright 2021 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set -eu
# Should this script error, print out the line that was responsible
_error_report() {
echo >&2 "Exited [$?] at line $(caller):"
cat -n $0 | tail -n+$(($1 - 3)) | head -n7 | sed "4s/^\s*/>>> /"
}
trap '_error_report $LINENO' ERR
# Variable list
# PROD_PROJECT GCP Project ID of the production project
# STAGE_PROJECT GCP Project ID of the staging project
# OPS_PROJECT GCP Project ID of the operations project
# SKIP_TERRAFORM If set, don't set up infrastructure
# SKIP_TRIGGERS If set, don't set up build triggers
# SKIP_AUTH If set, do not prompt to set up auth
# SKIP_BUILD If set, do not build container images
# SKIP_DEPLOY If set, do not deploy services
# SKIP_SEEDING If set, do not seed the database
# USE_DEFAULT_ACCOUNT If set, do not prompt for a GCP Account Name during database seeding
# REGION Default region to deploy resources to. Defaults to 'us-central1'
# Default to empty or default values, avoiding unbound variable errors.
SKIP_TERRAFORM=${SKIP_TERRAFORM:-}
SKIP_TRIGGERS=${SKIP_TRIGGERS:-}
SKIP_AUTH=${SKIP_AUTH:=true}
SKIP_BUILD=${SKIP_BUILD:-}
SKIP_DEPLOY=${SKIP_DEPLOY:-}
SKIP_SEEDING=${SKIP_SEEDING:-}
USE_DEFAULT_ACCOUNT=${USE_DEFAULT_ACCOUNT:-}
export REGION=${REGION:=us-central1}
# Check env variables are not empty strings
if [[ -z "${PROD_PROJECT}" ]]; then
echo "Please set the $(tput bold)PROD_PROJECT$(tput sgr0) variable"
exit 1
elif [[ -z "${STAGE_PROJECT}" ]]; then
echo "Please set the $(tput bold)STAGE_PROJECT$(tput sgr0) variable"
exit 1
elif [[ -z "${OPS_PROJECT}" ]]; then
echo "Please set the $(tput bold)OPS_PROJECT$(tput sgr0) variable"
exit 1
fi
echo "Setting up a new instance of Emblem. There may be a few prompts to guide the process."
#####################
# Initial Ops Setup #
#####################
if [[ -z "$SKIP_TERRAFORM" ]]; then
echo
echo "$(tput bold)Setting up your Cloud resources with Terraform...$(tput sgr0)"
echo
STATE_GCS_BUCKET_NAME="$OPS_PROJECT-tf-states"
# Create remote state bucket if it doesn't exist
if ! gsutil ls gs://${STATE_GCS_BUCKET_NAME} > /dev/null ; then
echo "Creating remote state bucket: " $STATE_GCS_BUCKET_NAME
gsutil mb -p $OPS_PROJECT -l $REGION gs://${STATE_GCS_BUCKET_NAME}
gsutil versioning set on gs://${STATE_GCS_BUCKET_NAME}
fi
# Ops Project
OPS_ENVIRONMENT_DIR=terraform/environments/ops
cat > "${OPS_ENVIRONMENT_DIR}/terraform.tfvars" <<EOF
project_id = "${OPS_PROJECT}"
EOF
terraform -chdir=${OPS_ENVIRONMENT_DIR} init -backend-config="bucket=${STATE_GCS_BUCKET_NAME}" -backend-config="prefix=ops"
terraform -chdir=${OPS_ENVIRONMENT_DIR} apply --auto-approve
fi # $SKIP_TERRAFORM
####################
# Build Containers #
####################
SETUP_IMAGE_TAG="setup"
E2E_RUNNER_TAG="latest"
if [[ -z "$SKIP_BUILD" ]]; then
echo
echo "$(tput bold)Building container images for testing and application hosting...$(tput sgr0)"
echo
API_BUILD_ID=$(gcloud builds submit "content-api" --async \
--config=ops/api-build.cloudbuild.yaml \
--project="$OPS_PROJECT" --substitutions=_REGION="$REGION",_IMAGE_TAG="$SETUP_IMAGE_TAG",_CONTEXT="." --format='value(ID)')
WEB_BUILD_ID=$(gcloud builds submit \
--config=ops/web-build.cloudbuild.yaml --async \
--ignore-file=ops/web-build.gcloudignore \
--project="$OPS_PROJECT" --substitutions=_REGION="$REGION",_IMAGE_TAG="$SETUP_IMAGE_TAG" --format='value(ID)')
E2E_BUILD_ID=$(gcloud builds submit "ops/e2e-runner" --async \
--config=ops/e2e-runner-build.cloudbuild.yaml \
--project="$OPS_PROJECT" --substitutions=_REGION="$REGION",_IMAGE_TAG="$E2E_RUNNER_TAG" --format='value(ID)')
fi # skip build
#####################
# Application Setup #
#####################
if [[ -z "$SKIP_TERRAFORM" ]]; then
# Staging Project
STAGE_ENVIRONMENT_DIR=terraform/environments/staging
cat > "${STAGE_ENVIRONMENT_DIR}/terraform.tfvars" <<EOF
project_id = "${STAGE_PROJECT}"
ops_project_id = "${OPS_PROJECT}"
EOF
terraform -chdir=${STAGE_ENVIRONMENT_DIR} init -backend-config="bucket=${STATE_GCS_BUCKET_NAME}" -backend-config="prefix=stage"
terraform -chdir=${STAGE_ENVIRONMENT_DIR} apply --auto-approve
# Prod Project
# Only deploy to separate project for multi-project setups
if [ "${PROD_PROJECT}" != "${STAGE_PROJECT}" ]; then
PROD_ENVIRONMENT_DIR=terraform/environments/prod
cat > "${PROD_ENVIRONMENT_DIR}/terraform.tfvars" <<EOF
project_id = "${PROD_PROJECT}"
ops_project_id = "${OPS_PROJECT}"
EOF
terraform -chdir=${PROD_ENVIRONMENT_DIR} init -backend-config="bucket=${STATE_GCS_BUCKET_NAME}" -backend-config="prefix=prod"
terraform -chdir=${PROD_ENVIRONMENT_DIR} apply --auto-approve
fi
fi # skip terraform
########################
# Seed Default Content #
########################
if [[ -z "$SKIP_SEEDING" ]]; then
echo
echo "$(tput bold)Seeding default content...$(tput sgr0)"
echo
account=$(gcloud config get-value account 2> /dev/null)
if [[ -z "$USE_DEFAULT_ACCOUNT" ]]; then
read -rp "Please input an email address for an approver. This email will be added to the Firestore database as an 'approver' and will be able to perform privileged API operations from the website frontend: [${account}]: " approver
fi
approver="${approver:-$account}"
gcloud builds submit content-api/data --project="$OPS_PROJECT" --async \
--substitutions=_FIREBASE_PROJECT="${STAGE_PROJECT}",_APPROVER_EMAIL="${approver}" \
--config=./content-api/data/cloudbuild.yaml
if [ "${PROD_PROJECT}" != "${STAGE_PROJECT}" ]; then
gcloud builds submit content-api/data --project="$OPS_PROJECT" --async \
--substitutions=_FIREBASE_PROJECT="${PROD_PROJECT}",_APPROVER_EMAIL="${approver}" \
--config=./content-api/data/cloudbuild.yaml
fi
fi # skip seeding
##################
# Deploy Services #
##################
# This function will wait for job status for the provided build job to update
# from WORKING to FAILURE or SUCCESS. For failed build jobs, the failure info
# will be returned along with the url to the build log. For successful build
# jobs, the provided run command will be executed. For all other statuses,
# url to the build log is returned.
check_for_build_then_run () {
local build_id="${1}"
local run_command="${2}"
# Wait for build to complete.
if [ $(gcloud builds describe $build_id --format='value(status)') == "WORKING" ]; then
log_url=$(gcloud builds describe $build_id --format='value(logUrl)')
echo "Build $build_id still working."
echo "Logs are available at [ $log_url ]."
while [ $(gcloud builds describe $build_id --format='value(status)') == "WORKING" ]
do
echo "Build $build_id still working..."
sleep 10
done
fi
# Return error and build log for failures.
if [ $(gcloud builds describe $build_id --format='value(status)') == "FAILURE" ]; then
build_describe=$(gcloud builds describe $build_id --format='csv(failureInfo.detail,logUrl)[no-heading]')
fail_info=$(echo $build_describe | awk -F',' '{gsub(/""/,"\"");print $1}')
log_url=$(echo $build_describe | awk -F',' '{print $2}')
echo "Build ${build_id} failed. See build log: $log_url"
echo "ERROR: ${fail_info}"
echo "Please re-run setup."
exit 2
# Deploy if build is successful.
elif [ $(gcloud builds describe $build_id --format='value(status)') == "SUCCESS" ]; then
$run_command
# Return build log for all other statuses.
else
log_url=$(gcloud builds describe $build_id --format='value(logUrl)')
echo "Build ${build_id} did not complete."
echo "See build log: $log_url"
echo "Please re-run setup."
exit 2
fi;
}
if [[ -z "$SKIP_DEPLOY" ]]; then
echo
echo "$(tput bold)Deploying Cloud Run services...$(tput sgr0)"
echo
## Staging Services ##
check_for_build_then_run $API_BUILD_ID "gcloud run deploy content-api \
--allow-unauthenticated \
--image "${REGION}-docker.pkg.dev/${OPS_PROJECT}/content-api/content-api:${SETUP_IMAGE_TAG}" \
--service-account "api-manager@${STAGE_PROJECT}.iam.gserviceaccount.com" \
--project "${STAGE_PROJECT}" \
--region "${REGION}""
STAGING_API_URL=$(gcloud run services describe content-api --project "${STAGE_PROJECT}" --region ${REGION} --format 'value(status.url)')
check_for_build_then_run $WEB_BUILD_ID "gcloud run deploy website \
--allow-unauthenticated \
--image "${REGION}-docker.pkg.dev/${OPS_PROJECT}/website/website:${SETUP_IMAGE_TAG}" \
--service-account "website-manager@${STAGE_PROJECT}.iam.gserviceaccount.com" \
--update-env-vars "EMBLEM_SESSION_BUCKET=${STAGE_PROJECT}-sessions" \
--update-env-vars "EMBLEM_API_URL=${STAGING_API_URL}" \
--project "${STAGE_PROJECT}" \
--region "${REGION}" \
--tag "latest""
## Production Services ##
# Only deploy to separate project for multi-project setups
if [ "${PROD_PROJECT}" != "${STAGE_PROJECT}" ]; then
check_for_build_then_run $API_BUILD_ID "gcloud run deploy content-api \
--allow-unauthenticated \
--image "${REGION}-docker.pkg.dev/${OPS_PROJECT}/content-api/content-api:${SETUP_IMAGE_TAG}" \
--service-account "api-manager@${PROD_PROJECT}.iam.gserviceaccount.com" \
--project "${PROD_PROJECT}" \
--region "${REGION}""
PROD_API_URL=$(gcloud run services describe content-api --project "${PROD_PROJECT}" --region ${REGION} --format 'value(status.url)')
check_for_build_then_run $WEB_BUILD_ID "gcloud run deploy website \
--allow-unauthenticated \
--image "${REGION}-docker.pkg.dev/${OPS_PROJECT}/website/website:${SETUP_IMAGE_TAG}" \
--service-account "website-manager@${PROD_PROJECT}.iam.gserviceaccount.com" \
--update-env-vars "EMBLEM_SESSION_BUCKET=${PROD_PROJECT}-sessions" \
--update-env-vars "EMBLEM_API_URL=${PROD_API_URL}" \
--project "${PROD_PROJECT}" \
--region "${REGION}" \
--tag "latest""
fi
fi # skip deploy
###############
# Setup CI/CD #
###############
if [[ -z "$SKIP_TRIGGERS" ]]; then
echo
sh ./scripts/configure_delivery.sh
fi # skip triggers
echo
STAGING_WEBSITE_URL=$(gcloud run services describe website --project "${STAGE_PROJECT}" --region ${REGION} --format 'value(status.url)')
if [ "${PROD_PROJECT}" != "${STAGE_PROJECT}" ]; then
PROD_WEBSITE_URL=$(gcloud run services describe website --project "${PROD_PROJECT}" --region ${REGION} --format 'value(status.url)')
echo "💠 The staging environment is ready! Navigate your browser to ${STAGING_WEBSITE_URL}"
echo "💠 The production environment is ready! Navigate your browser to ${PROD_WEBSITE_URL}"
else
echo "💠 The application is ready! Navigate your browser to ${STAGING_WEBSITE_URL}"
fi
#######################
# User Authentication #
#######################
if [[ -z "$SKIP_AUTH" ]]; then
echo
read -rp "Would you like to configure $(tput bold)$(tput setaf 3)end-user authentication?$(tput sgr0) (y/n) " auth_yesno
if [[ ${auth_yesno} == "y" ]]; then
sh ./scripts/configure_auth.sh
else
echo "Skipping end-user authentication configuration. You can configure it later by running:"
echo
echo " export $(tput bold)PROD_PROJECT$(tput sgr0)=$(tput setaf 6)${PROD_PROJECT}$(tput sgr0)"
echo " export $(tput bold)STAGE_PROJECT$(tput sgr0)=$(tput setaf 6)${STAGE_PROJECT}$(tput sgr0)"
echo " export $(tput bold)OPS_PROJECT$(tput sgr0)=$(tput setaf 6)${OPS_PROJECT}$(tput sgr0)"
echo " $(tput setaf 6)sh scripts/configure_auth.sh$(tput sgr0)"
echo
fi
fi # skip authentication