-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathsetup.sh
executable file
·300 lines (253 loc) · 14.3 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
#!/bin/bash
# Copyright 2023 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.
# Run from inside of either CloudShell or a Bastion VM inside of the same GCP project as the GCP cnuc's
PREFIX_DIR=$(dirname -- "$0")
source ${PREFIX_DIR}/scripts/cloud/gce-helper.vars
source ${PREFIX_DIR}/scripts/install-shell-helper.sh
# Results in none or one key for the current Instance Run to download. If empty, no key to down
function get_downloadable_key_name {
# NOTE: This matches Ansible's setting for ssh key download
local SSH_SECRET_NAME_PREFIX="ssh-priv-key-"
# First check if there is a key in the GCP Secret Manager
SECRET_LIST=$(gcloud secrets list --filter="name~${SSH_SECRET_NAME_PREFIX}" --format="value(name)" --project="${PROJECT_ID}" 2> /dev/null)
SECRET_COUNT=${#SECRET_LIST[@]}
num=0
# If no secrets are found, the SECRET_LIST variable will have a single item
# which is an empty string. Catching that condition here
if [ ${SECRET_COUNT} -ge 1 ] && [ ! -z ${SECRET_LIST[0]} ]; then
pretty_print "\nThere are existing SSH private keys in Google Secret Manager for the project ${PROJECT_ID}. \n" "DEBUG"
for index in ${!SECRET_LIST[@]}; do
cluster_name=${SECRET_LIST[$index]#"$SSH_SECRET_NAME_PREFIX"}
num=$(( index + 1 ))
pretty_print " $num) ${cluster_name}" "DEBUG"
done
else
SECRET_COUNT=0
pretty_print "No existing SSH private keys were found in Google Secret Manager
for this project (${PROJECT_ID})" "DEBUG"
fi
# Add option for "create a new key"
pretty_print " $((num+1))) Create a new key-pair" "DEBUG"
pretty_print "\n Ctrl+C to cancel\n" "DEBUG"
# Start capture of decision
read -p "Which of these do you want to use: " sel_key_num
if [[ "${sel_key_num}" =~ ^([1-9][0-9]*)$ ]] && [[ "${sel_key_num}" -le $((SECRET_COUNT+1)) ]] ; then
if [[ "${sel_key_num}" -le SECRET_COUNT ]]; then
pretty_print "\nINFO: Downloading key for ${SECRET_LIST[$index]#}" "INFO"
gcloud secrets versions access latest --secret="${SECRET_LIST[$index]}" >> ./build-artifacts/consumer-edge-machine --project="${PROJECT_ID}"
chmod 600 ./build-artifacts/consumer-edge-machine
pretty_print "INFO: Generate the public key locally ./build-artifacts/consumer-edge-machine.pub" "INFO"
ssh-keygen -f ./build-artifacts/consumer-edge-machine -y >> ./build-artifacts/consumer-edge-machine.pub
else
echo "\nINFO: Creating a new SSH key-pair and pushing to Google Secret Manager for Cluster '${DEFAULT_CLUSTER_NAME}'"
echo "INFO: The new primary key stored at ./build-artifacts/consumer-edge-machine.pub"
ssh-keygen -o -a 100 -t ed25519 -f ./build-artifacts/consumer-edge-machine -N ''
gcloud secrets create ssh-priv-key-${DEFAULT_CLUSTER_NAME} --replication-policy="automatic" > /dev/null 2>&1 # Ignore all issues with this
gcloud secrets versions add ssh-priv-key-${DEFAULT_CLUSTER_NAME} --data-file="build-artifacts/consumer-edge-machine" > /dev/null 2>&1
fi
else
pretty_print "\nERROR: The answer [${sel_key_num}] was not recognized, please re-run.\n" "ERROR"
exit 1
fi
}
# Option to add the cluster name to the script
DEFAULT_CLUSTER_NAME=${1:-cnuc-1}
if [[ "${DEFAULT_CLUSTER_NAME}" =~ ^([a-z]+[0-9a-z-]*)$ ]]; then
pretty_print "INFO: Evaluating setup using cluster name [${DEFAULT_CLUSTER_NAME}]" "INFO"
else
pretty_print "ERROR: Cluster name [$DEFAULT_CLUSTER_NAME] contains characters that cannot be used. Only lowercase alpha-numeric and dashes can be used" "ERROR"
exit 1
fi
# Must currently run as an admin user with org permissions in order to make the changes throughout
# For now this means we will require that the user login via gcloud auth login
# This check will bail if a gserviceaccount is found in use
ACTIVE_USER=$(gcloud auth list --filter=status:ACTIVE --format="value(account)")
GSERVICEACCOUNT=$(echo ${ACTIVE_USER} | grep gserviceaccount)
if [[ ! -z "${GSERVICEACCOUNT}" ]]; then
echo "Detected gcloud is authenticated using a GSA. Please login as a human user account using the following command -- 'gcloud auth login --no-launch-browser'"
echo "Copy/Paste the link into a browser where you are authenticated with admin level permissions for the project!!"
exit 1
fi
# detect Argolis user
ARGOLIS_USER=$(echo ${ACTIVE_USER} | grep "altostrat.com")
IS_ARGOLIS=false
if [[ ! -z "${ARGOLIS_USER}" || ! -z "${ARGOLIS_PROJECT}" ]]; then
pretty_print "INFO: Detected this is an Argolis project and will attempt to modify org policies to enable Consumer Edge." "DEBUG"
IS_ARGOLIS=true
fi
# If PROJECT_ID is not defined, prompt the user to define the project ID to use
if [[ -z "${PROJECT_ID}" ]]; then
# If a project has been configured through gcloud, capture that
GCLOUD_PROJECT=$(gcloud config list --format 'value(core.project)' 2>/dev/null)
read -p "Enter the GCP Project ID to use for this installation [${GCLOUD_PROJECT}]: " selected_project
PROJECT_ID=${selected_project:-$GCLOUD_PROJECT}
fi
pretty_print "\nBeginning installation, using ${PROJECT_ID} as the target GCP Project." "DEFAULT"
# Configure gcloud to use the selected GCP project
gcloud config set project ${PROJECT_ID} --no-user-output-enabled
# Needed for reference
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format="value(projectNumber)")
pretty_print "\nINFO: Enabling GCP services required for project" "INFO"
if [[ ${IS_ARGOLIS} == true ]]; then
source ./scripts/argolis-setup.sh
fi
# Enable any services needed
gcloud services enable \
servicemanagement.googleapis.com \
anthos.googleapis.com \
cloudbuild.googleapis.com \
cloudresourcemanager.googleapis.com \
serviceusage.googleapis.com \
compute.googleapis.com \
secretmanager.googleapis.com \
--quiet --verbosity=critical --no-user-output-enabled
pretty_print "INFO: Adding roles/secretmanager.secretAccessor and roles/storage.objectViewer to default compute service account." "INFO"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}[email protected]" \
--role="roles/secretmanager.secretAccessor" --no-user-output-enabled
pretty_print "INFO: Adding roles/storage.objectViewer to default compute service account." "INFO"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}[email protected]" \
--role="roles/storage.objectViewer" --no-user-output-enabled
pretty_print "INFO: Adding roles/storage.objectViewer to default cloudbuild service account." "INFO"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com" \
--role="roles/storage.objectViewer" --no-user-output-enabled
pretty_print "INFO: Adding roles/artifactregistry.createOnPushWriter to default compute service account." "INFO"
gcloud projects add-iam-policy-binding $PROJECT_ID \
--member="serviceAccount:${PROJECT_NUMBER}[email protected]" \
--role="roles/artifactregistry.createOnPushWriter" --no-user-output-enabled
# Validate that required applications are installed
REQUIRED_APPS=(direnv docker gcloud gettext git jq screen)
MISSING_APPS=()
DOCKER_MISSING=0
echo ""
for app in ${REQUIRED_APPS[@]}; do
if ! command -v $app > /dev/null 2>&1; then
pretty_print "ERROR: ${app} is required, but not installed." "ERROR"
# Docker won't be installed through apt, so catching that so we can manually
# install docker later
[ ${app} != "docker" ] && MISSING_APPS+=(${app}) || DOCKER_MISSING=1
else
pretty_print "PASS: ${app} is required and installed."
fi
done
# If required software is missing, prompt the user to install
if [ ${#MISSING_APPS[@]} -gt 0 ]; then
pretty_print "=========================" "ERROR"
pretty_print "Some or all required dependencies are not met." "INFO"
read -p "Would you like this script to install the required dependencies? (Y/N) " -n 1 -r
echo
if [[ "${REPLY}" =~ ^([yY][eE][sS]|[yY])$ ]]; then
pretty_print "\nInstalling ${MISSING_APPS[@]}.\n"
sudo apt install -y ${MISSING_APPS[@]}
pretty_print "Finished installing dependencies."
pretty_print "\nPLEASE add: eval \"$(direnv hook bashrc)\" (or zsh) to your ~/.bashrc or ~/.zshrc file" "INFO"
else
pretty_print "\nExiting. Please fix dependencies on your own, or re-run this script and select 'Y'" "ERROR"
exit 1
fi
fi
# We're installing Docker through the Docker apt repo not through the distro's
# packages. If Docker is not installed, performing the install here
if [ ${DOCKER_MISSING} -eq 1 ]; then
pretty_print "Installing docker" "INFO"
sudo apt -y remove docker docker-engine docker.io containerd runc
sudo apt -y install ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/debian/gpg | sudo gpg --yes --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt -y install docker-ce docker-ce-cli containerd.io
sudo usermod -aG docker $(whoami)
sudo chmod 666 /var/run/docker.sock
pretty_print "\nFinished Docker setup..." "INFO"
fi
# Enable mouse scroll and scrollback in screen configuration & session
echo "termcapinfo xterm* ti@:te@" > .screenrc
SSH_KEY_LOC="./build-artifacts/consumer-edge-machine"
# Setup SSH Keys on local box (create new or download from GCP Secret Manager)
if [[ ! -f "./build-artifacts/consumer-edge-machine" ]]; then
get_downloadable_key_name
fi
# Print the public key (not sensitive)
PUB_KEY=$(cat ${SSH_KEY_LOC}.pub)
pretty_print "\nINFO: Public key-pair used: ${PUB_KEY}" "INFO"
export QL_PROJECT_ID=$(gcloud config get-value project 2> /dev/null)
if [[ -z "${QL_PROJECT_ID}" ]]; then
pretty_print "ERROR: Project ID not configured for gcloud. Please set project with 'gcloud config set project <project-id>'" "ERROR"
exit 1
else
pretty_print "INFO: Setting PROJECT_ID: ${PROJECT_ID}" "INFO"
export PROJECT_ID="${QL_PROJECT_ID}"
fi
pretty_print ""
pretty_print "==============================="
pretty_print ""
pretty_print "All of the following variables CAN and SHOULD be verified in the generated 'envrc' file following the completion of this script"
pretty_print ""
if [[ -z "${ROOT_REPO_URL}" ]]; then
pretty_print "INFO: Setting default Primary Root Repo: ${ROOT_REPO_URL}" "INFO"
export ROOT_REPO_URL="https://gitlab.com/gcp-solutions-public/retail-edge/primary-root-repo-template.git"
fi
pretty_print "INFO: Setting up docker configuration to use gcloud for gcr.io" "INFO"
yes Y | gcloud auth configure-docker --quiet --verbosity=critical --no-user-output-enabled
if [[ ! -f "build-artifacts/envrc" ]]; then
pretty_print "INFO: Generating 'envrc' properties file" "INFO"
envsubst "${PROJECT_ID}" < templates/envrc-template.sh > build-artifacts/envrc
else
pretty_print "PASS: Using existing envrc file"
fi
direnv allow .
if [[ ! -f "./build-artifacts/provisioning-gsa.json" ]]; then
pretty_print "INFO: Create the provisioning GSAs used during initial setup. JSON key placed in ./build-artifacts/" "INFO"
yes Y | ./scripts/create-gsa.sh
else
pretty_print "PASS: GSA Keys have been created for the provisioning GSA"
fi
# Create docker container for building
CONTAINER_URL=$(gcloud container images list --repository=gcr.io/${PROJECT_ID} --format="value(name)" --filter="name~consumer-edge-install")
if [[ -z "$CONTAINER_URL" ]]; then
pretty_print "INFO: This project uses a Docker image to provision host machines
from. The image has not been detected and a build request has been submitted to
GCP." "INFO"
# This build is being submitted async, so this command will return ~immediately
# while the container is being built by Cloud Build in the background.
gcloud builds submit --config ./docker-build/cloudbuild.yaml ./ --async --quiet --verbosity=critical --no-user-output-enabled
else
pretty_print "\nINFO: Docker build image was found, and will not be rebuilt." INFO
fi
if [[ ! -f "build-artifacts/gcp.yml" ]]; then
pretty_print "INFO: Default GCP Ansible plugin configuration was not found. Generating a new version at build-artifacts/gcp.yml" "INFO"
envsubst < templates/inventory-cloud-example.yaml > build-artifacts/gcp.yml
else
pretty_print "PASS: GCP Inventory file found at build-artifacts/gcp.yml"
fi
if [[ ! -f "inventory/inventory.yaml" ]]; then
pretty_print "INFO: Setting up inventory link to build-artifacts" "INFO"
ln -s ../build-artifacts/inventory.yaml ./inventory/inventory.yaml
fi
pretty_print "\n\nYour project is set up and ready for use. You will need to do a combination of the following options next:\n"
pretty_print "1. Use your editor of choice and check the generated 'build-artifacts/envrc' file to ensure correct Environment Variables were set." "INFO"
pretty_print "2. If you made any changes, save file and run either: 'direnv allow .' or 'source build-artifacts/envrc'"
pretty_print "Cloud-based host machines (default)" "DEBUG"
pretty_print "1. Create GCE instances: run './scripts/cloud/create-cloud-gce-baseline.sh -c 3'" "INFO"
pretty_print "\nPhysical Machine based host machines" "DEBUG"
pretty_print "1. Copy the 3 'edge-X.yaml' files in ./inventory and rename with a hostname of your choice (ie: nucs-1.yaml, nucs-2.yaml, nucs-3.yaml)"
pretty_print "2. If using physical hardware, create an inventory file: envsubst < templates/inventory-physical-example.yaml > build-artifacts/inventory.yaml."
pretty_print " - Modify this file to match your host_var inventory files (see previous step). Comment out 'edge-1,edge-2,edge-3 and replace with your host names"
pretty_print "\n==== Last Step ===\n1. Run: ./install.sh\n"
pretty_print "NOTE: Physical machines require a bit more setup not outlined here. Please ping the https://www.google.com/goto/gdc-consumer-edge:gchat team for more info.\n\n"