19215: Polishing install process
[arvados.git] / tools / salt-install / installer.sh
1 #!/bin/bash
2
3 # Copyright (C) The Arvados Authors. All rights reserved.
4 #
5 # SPDX-License-Identifier: CC-BY-SA-3.0
6
7 #
8 # installer.sh
9 #
10 # Helps manage the configuration in a git repository, and then deploy
11 # nodes by pushing a copy of the git repository to each node and
12 # running the provision script to do the actual installation and
13 # configuration.
14 #
15
16 set -eu
17
18 # The parameter file
19 declare CONFIG_FILE=local.params
20
21 # The salt template directory
22 declare CONFIG_DIR=local_config_dir
23
24 # The 5-character Arvados cluster id
25 # This will be populated by loadconfig()
26 declare CLUSTER
27
28 # The parent domain (not including the cluster id)
29 # This will be populated by loadconfig()
30 declare DOMAIN
31
32 # A bash associative array listing each node and mapping to the roles
33 # that should be provisioned on those nodes.
34 # This will be populated by loadconfig()
35 declare -A NODES
36
37 # The ssh user we'll use
38 # This will be populated by loadconfig()
39 declare DEPLOY_USER
40
41 # The git repository that we'll push to on all the nodes
42 # This will be populated by loadconfig()
43 declare GITTARGET
44
45 checktools() {
46     local MISSING=''
47     for a in git ip ; do
48         if ! which $a ; then
49             MISSING="$MISSING $a"
50         fi
51     done
52     if [[ -n "$MISSING" ]] ; then
53         echo "Some tools are missing, please make sure you have the 'git' and 'iproute2' packages installed"
54         exit 1
55     fi
56 }
57
58 sync() {
59     local NODE=$1
60     local BRANCH=$2
61
62     # Synchronizes the configuration by creating a git repository on
63     # each node, pushing our branch, and updating the checkout.
64
65     if [[ "$NODE" != localhost ]] ; then
66         if ! ssh $DEPLOY_USER@$NODE test -d ${GITTARGET}.git ; then
67
68             # Initialize the git repository (1st time case).  We're
69             # actually going to make two repositories here because git
70             # will complain if you try to push to a repository with a
71             # checkout. So we're going to create a "bare" repository
72             # and then clone a regular repository (with a checkout)
73             # from that.
74
75             ssh $DEPLOY_USER@$NODE git init --bare ${GITTARGET}.git
76             if ! git remote add $NODE $DEPLOY_USER@$NODE:${GITTARGET}.git ; then
77                         git remote set-url $NODE $DEPLOY_USER@$NODE:${GITTARGET}.git
78             fi
79             git push $NODE $BRANCH
80             ssh $DEPLOY_USER@$NODE git clone ${GITTARGET}.git ${GITTARGET}
81         fi
82
83         # The update case.
84         #
85         # Push to the bare repository on the remote node, then in the
86         # remote node repository with the checkout, pull the branch
87         # from the bare repository.
88
89         git push $NODE $BRANCH
90         ssh $DEPLOY_USER@$NODE "git -C ${GITTARGET} checkout ${BRANCH} && git -C ${GITTARGET} pull"
91     fi
92 }
93
94 deploynode() {
95     local NODE=$1
96     local ROLES=$2
97
98     # Deploy a node.  This runs the provision script on the node, with
99     # the appropriate roles.
100
101     if [[ -z "$ROLES" ]] ; then
102         echo "No roles specified for $NODE, will deploy all roles"
103     else
104         ROLES="--roles ${ROLES}"
105     fi
106
107     logfile=deploy-${NODE}-$(date -Iseconds).log
108
109     if [[ "$NODE" = localhost ]] ; then
110         SUDO=''
111         if [[ $(whoami) != 'root' ]] ; then
112             SUDO=sudo
113         fi
114         $SUDO ./provision.sh --config ${CONFIG_FILE} ${ROLES} 2>&1 | tee $logfile
115     else
116         ssh $DEPLOY_USER@$NODE "cd ${GITTARGET} && sudo ./provision.sh --config ${CONFIG_FILE} ${ROLES}" 2>&1 | tee $logfile
117     fi
118 }
119
120 loadconfig() {
121     if [[ ! -s $CONFIG_FILE ]] ; then
122         echo "Must be run from initialized setup dir, maybe you need to 'initialize' first?"
123     fi
124     source ${CONFIG_FILE}
125     GITTARGET=arvados-deploy-config-${CLUSTER}
126 }
127
128 set +u
129 subcmd="$1"
130 set -u
131
132 if [[ -n "$subcmd" ]] ; then
133     shift
134 fi
135 case "$subcmd" in
136     initialize)
137         if [[ ! -f provision.sh ]] ; then
138             echo "Must be run from arvados/tools/salt-install"
139             exit
140         fi
141
142         checktools
143
144         set +u
145         SETUPDIR=$1
146         PARAMS=$2
147         SLS=$3
148         TERRAFORM=$4
149         set -u
150
151         err=
152         if [[ -z "$PARAMS" || ! -f local.params.example.$PARAMS ]] ; then
153             echo "Not found: local.params.example.$PARAMS"
154             echo "Expected one of multiple_hosts, single_host_multiple_hostnames, single_host_single_hostname"
155             err=1
156         fi
157
158         if [[ -z "$SLS" || ! -d config_examples/$SLS ]] ; then
159             echo "Not found: config_examples/$SLS"
160             echo "Expected one of multi_host/aws, single_host/multiple_hostnames, single_host/single_hostname"
161             err=1
162         fi
163
164         if [[ -z "$SETUPDIR" || -z "$PARAMS" || -z "$SLS" ]]; then
165             echo "installer.sh <setup dir to initialize> <params template> <config template>"
166             err=1
167         fi
168
169         if [[ -n "$err" ]] ; then
170             exit 1
171         fi
172
173         echo "Initializing $SETUPDIR"
174         git init $SETUPDIR
175         cp -r *.sh tests $SETUPDIR
176
177         cp local.params.example.$PARAMS $SETUPDIR/${CONFIG_FILE}
178         cp -r config_examples/$SLS $SETUPDIR/${CONFIG_DIR}
179
180         if [[ -n "$TERRAFORM" ]] ; then
181             mkdir $SETUPDIR/terraform
182             cp -r $TERRAFORM/* $SETUPDIR/terraform/
183         fi
184
185         cd $SETUPDIR
186         echo '*.log' > .gitignore
187
188         git add *.sh ${CONFIG_FILE} ${CONFIG_DIR} tests .gitignore
189         git commit -m"initial commit"
190
191         echo
192         echo "Setup directory $SETUPDIR initialized."
193         if [[ -n "$TERRAFORM" ]] ; then
194             (cd $SETUPDIR/terraform/vpc && terraform init)
195             (cd $SETUPDIR/terraform/data-storage && terraform init)
196             (cd $SETUPDIR/terraform/services && terraform init)
197             echo "Now go to $SETUPDIR, customize 'terraform/vpc/terraform.tfvars' as needed, then run 'installer.sh terraform'"
198         else
199             echo "Now go to $SETUPDIR, customize '${CONFIG_FILE}' and '${CONFIG_DIR}' as needed, then run 'installer.sh deploy'"
200         fi
201         ;;
202
203     terraform)
204         logfile=terraform-$(date -Iseconds).log
205         (cd terraform/vpc && terraform apply) 2>&1 | tee -a $logfile
206         (cd terraform/data-storage && terraform apply) 2>&1 | tee -a $logfile
207         (cd terraform/services && terraform apply) 2>&1 | tee -a $logfile
208         ;;
209
210     generate-tokens)
211         for i in BLOB_SIGNING_KEY MANAGEMENT_TOKEN SYSTEM_ROOT_TOKEN ANONYMOUS_USER_TOKEN WORKBENCH_SECRET_KEY DATABASE_PASSWORD; do
212             echo ${i}=$(tr -dc A-Za-z0-9 </dev/urandom | head -c 32 ; echo '')
213         done
214         ;;
215
216     deploy)
217         set +u
218         NODE=$1
219         set -u
220
221         checktools
222
223         loadconfig
224
225         if grep -rni 'fixme' ${CONFIG_FILE} ${CONFIG_DIR} ; then
226             echo
227             echo "Some parameters still need to be updated.  Please fix them and then re-run deploy."
228             exit 1
229         fi
230
231         BRANCH=$(git branch --show-current)
232
233         set -x
234
235         git add -A
236         if ! git diff --cached --exit-code ; then
237             git commit -m"prepare for deploy"
238         fi
239
240         if [[ -z "$NODE" ]]; then
241             for NODE in "${!NODES[@]}"
242             do
243                 # First, push the git repo to each node.  This also
244                 # confirms that we have git and can log into each
245                 # node.
246                 sync $NODE $BRANCH
247             done
248
249             for NODE in "${!NODES[@]}"
250             do
251                 # Do 'database' role first,
252                 if [[ "${NODES[$NODE]}" =~ database ]] ; then
253                     deploynode $NODE "${NODES[$NODE]}"
254                     unset NODES[$NODE]
255                 fi
256             done
257
258             for NODE in "${!NODES[@]}"
259             do
260                 # then  'api' or 'controller' roles
261                 if [[ "${NODES[$NODE]}" =~ (api|controller) ]] ; then
262                     deploynode $NODE "${NODES[$NODE]}"
263                     unset NODES[$NODE]
264                 fi
265             done
266
267             for NODE in "${!NODES[@]}"
268             do
269                 # Everything else (we removed the nodes that we
270                 # already deployed from the list)
271                 deploynode $NODE "${NODES[$NODE]}"
272             done
273         else
274             # Just deploy the node that was supplied on the command line.
275             sync $NODE $BRANCH
276             deploynode $NODE ""
277         fi
278
279         set +x
280         echo
281         echo "Completed deploy, run 'installer.sh diagnostics' to verify the install"
282
283         ;;
284
285     diagnostics)
286         loadconfig
287
288         set +u
289         declare LOCATION=$1
290         set -u
291
292         if ! which arvados-client ; then
293             echo "arvados-client not found, install 'arvados-client' package with 'apt-get' or 'yum'"
294             exit 1
295         fi
296
297         if [[ -z "$LOCATION" ]] ; then
298             echo "Need to provide '-internal-client' or '-external-client'"
299             echo
300             echo "-internal-client    You are running this on the same private network as the Arvados cluster (e.g. on one of the Arvados nodes)"
301             echo "-external-client    You are running this outside the private network of the Arvados cluster (e.g. your workstation)"
302             exit 1
303         fi
304
305         export ARVADOS_API_HOST="${CLUSTER}.${DOMAIN}:${CONTROLLER_EXT_SSL_PORT}"
306         export ARVADOS_API_TOKEN="$SYSTEM_ROOT_TOKEN"
307
308         arvados-client diagnostics $LOCATION
309         ;;
310
311     *)
312         echo "Arvados installer"
313         echo ""
314         echo "initialize        initialize the setup directory for configuration"
315         echo "terraform         create cloud resources using terraform"
316         echo "generate-tokens   generate random values for tokens"
317         echo "deploy            deploy the configuration from the setup directory"
318         echo "diagnostics       check your install using diagnostics"
319         ;;
320 esac