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