3 # Copyright (C) The Arvados Authors. All rights reserved.
5 # SPDX-License-Identifier: CC-BY-SA-3.0
7 # If you want to test arvados in a single host, you can run this script, which
8 # will install it using salt masterless
9 # This script is run by the Vagrant file when you run it with
15 # capture the directory that the script is running from
16 SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
20 echo >&2 "Usage: ${0} [-h] [-h]"
22 echo >&2 "${0} options:"
23 echo >&2 " -d, --debug Run salt installation in debug mode"
24 echo >&2 " -p <N>, --ssl-port <N> SSL port to use for the web applications"
25 echo >&2 " -c <local.params>, --config <local.params> Path to the local.params config file"
26 echo >&2 " -t, --test Test installation running a CWL workflow"
27 echo >&2 " -r, --roles List of Arvados roles to apply to the host, comma separated"
28 echo >&2 " Possible values are:"
30 echo >&2 " controller"
34 echo >&2 " workbench2"
38 echo >&2 " dispatcher"
39 echo >&2 " Defaults to applying them all"
40 echo >&2 " -h, --help Display this help and exit"
41 echo >&2 " -v, --vagrant Run in vagrant and use the /vagrant shared dir"
46 # NOTE: This requires GNU getopt (part of the util-linux package on Debian-based distros).
47 TEMP=$(getopt -o c:dhp:r:tv \
48 --long config:,debug,help,ssl-port:,roles:,test,vagrant \
51 if [ ${?} != 0 ] ; then echo "GNU getopt missing? Use -h for help"; exit 1 ; fi
52 # Note the quotes around `$TEMP': they are essential!
55 while [ ${#} -ge 1 ]; do
66 CONTROLLER_EXT_SSL_PORT=${2}
72 # Verify the role exists
73 if [[ ! "database,api,controller,keepstore,websocket,keepweb,workbench2,keepproxy,shell,workbench,dispatcher" == *"$i"* ]]; then
74 echo "The role '${i}' is not a valid role"
102 CONFIG_FILE="${SCRIPT_DIR}/local.params"
103 CONFIG_DIR="config_examples/single_host/multiple_hostnames"
105 CONTROLLER_EXT_SSL_PORT=443
111 # Hostnames/IPs used for single-host deploys
113 HOSTNAME_INT="127.0.1.1"
117 INITIAL_USER_EMAIL=""
118 INITIAL_USER_PASSWORD=""
120 CONTROLLER_EXT_SSL_PORT=8000
121 KEEP_EXT_SSL_PORT=25101
122 # Both for collections and downloads
123 KEEPWEB_EXT_SSL_PORT=9002
124 WEBSHELL_EXT_SSL_PORT=4202
125 WEBSOCKET_EXT_SSL_PORT=8002
126 WORKBENCH1_EXT_SSL_PORT=443
127 WORKBENCH2_EXT_SSL_PORT=3001
132 POSTGRES_TAG="v0.41.6"
136 LETSENCRYPT_TAG="v2.1.0"
142 F_DIR="/srv/formulas"
148 if [ -s ${CONFIG_FILE} ]; then
149 source ${CONFIG_FILE}
151 echo >&2 "Please create a '${CONFIG_FILE}' file with initial values, as described in"
152 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
153 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
157 if grep -q 'fixme_or_this_wont_work' ${CONFIG_FILE} ; then
158 echo >&2 "The config file ${CONFIG_FILE} has some parameters that need to be modified."
159 echo >&2 "Please, fix them and re-run the provision script."
163 if ! grep -E '^[[:alnum:]]{5}$' <<<${CLUSTER} ; then
164 echo >&2 "ERROR: <CLUSTER> must be exactly 5 alphanumeric characters long"
165 echo >&2 "Fix the cluster name in the 'local.params' file and re-run the provision script"
170 apt-get install -y curl git jq
172 if which salt-call; then
173 echo "Salt already installed"
175 curl -L https://bootstrap.saltstack.com -o /tmp/bootstrap_salt.sh
176 sh /tmp/bootstrap_salt.sh -XdfP -x python3
177 /bin/systemctl stop salt-minion.service
178 /bin/systemctl disable salt-minion.service
181 # Set salt to masterless mode
182 cat > /etc/salt/minion << EOFSM
194 mkdir -p ${S_DIR} ${F_DIR} ${P_DIR}
196 # Get the formula and dependencies
197 cd ${F_DIR} || exit 1
198 git clone --branch "${ARVADOS_TAG}" https://github.com/arvados/arvados-formula.git
199 git clone --branch "${DOCKER_TAG}" https://github.com/saltstack-formulas/docker-formula.git
200 git clone --branch "${LOCALE_TAG}" https://github.com/saltstack-formulas/locale-formula.git
201 # git clone --branch "${NGINX_TAG}" https://github.com/saltstack-formulas/nginx-formula.git
202 git clone --branch "${NGINX_TAG}" https://github.com/netmanagers/nginx-formula.git
203 git clone --branch "${POSTGRES_TAG}" https://github.com/saltstack-formulas/postgres-formula.git
204 git clone --branch "${LETSENCRYPT_TAG}" https://github.com/saltstack-formulas/letsencrypt-formula.git
206 # If we want to try a specific branch of the formula
207 if [ "x${BRANCH}" != "x" ]; then
208 cd ${F_DIR}/arvados-formula || exit 1
209 git checkout -t origin/"${BRANCH}" -b "${BRANCH}"
213 if [ "x${VAGRANT}" = "xyes" ]; then
214 SOURCE_PILLARS_DIR="/vagrant/${CONFIG_DIR}/pillars"
215 SOURCE_TESTS_DIR="/vagrant/${TESTS_DIR}"
217 SOURCE_PILLARS_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/pillars"
218 SOURCE_TESTS_DIR="${SCRIPT_DIR}/${TESTS_DIR}"
221 SOURCE_STATES_DIR="${EXTRA_STATES_DIR}"
223 # Replace variables (cluster, domain, etc) in the pillars, states and tests
224 # to ease deployment for newcomers
225 for f in "${SOURCE_PILLARS_DIR}"/*; do
226 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
227 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
228 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
229 s#__CLUSTER__#${CLUSTER}#g;
230 s#__DOMAIN__#${DOMAIN}#g;
231 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
232 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
233 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
234 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
235 s#__INITIAL_USER__#${INITIAL_USER}#g;
236 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
237 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
238 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
239 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
240 s#__RELEASE__#${RELEASE}#g;
241 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
242 s#__VERSION__#${VERSION}#g;
243 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
244 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
245 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
246 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
247 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
248 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
249 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
250 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
251 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
252 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
253 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
254 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
255 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
256 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
257 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
258 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
259 "${f}" > "${P_DIR}"/$(basename "${f}")
262 mkdir -p /tmp/cluster_tests
263 # Replace cluster and domain name in the test files
264 for f in "${SOURCE_TESTS_DIR}"/*; do
265 sed "s#__CLUSTER__#${CLUSTER}#g;
266 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
267 s#__DOMAIN__#${DOMAIN}#g;
268 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
269 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
270 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
271 s#__INITIAL_USER__#${INITIAL_USER}#g;
272 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
273 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g" \
274 "${f}" > "/tmp/cluster_tests"/$(basename "${f}")
276 chmod 755 /tmp/cluster_tests/run-test.sh
278 # Replace helper state files that differ from the formula's examples
279 if [ -d "${SOURCE_STATES_DIR}" ]; then
280 mkdir -p "${F_DIR}"/extra/extra
282 for f in "${SOURCE_STATES_DIR}"/*; do
283 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
284 s#__CLUSTER__#${CLUSTER}#g;
285 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
286 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
287 s#__DOMAIN__#${DOMAIN}#g;
288 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
289 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
290 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
291 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
292 s#__INITIAL_USER__#${INITIAL_USER}#g;
293 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
294 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
295 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
296 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
297 s#__RELEASE__#${RELEASE}#g;
298 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
299 s#__VERSION__#${VERSION}#g;
300 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
301 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
302 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
303 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
304 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
305 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
306 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
307 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
308 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
309 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
310 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
311 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
312 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
313 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
314 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
315 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
316 "${f}" > "${F_DIR}/extra/extra"/$(basename "${f}")
320 # Now, we build the SALT states/pillars trees
321 # As we need to separate both states and pillars in case we want specific
322 # roles, we iterate on both at the same time
325 cat > ${S_DIR}/top.sls << EOFTSLS
332 cat > ${P_DIR}/top.sls << EOFPSLS
339 # States, extra states
340 if [ -d "${F_DIR}"/extra/extra ]; then
341 for f in "${F_DIR}"/extra/extra/*.sls; do
342 echo " - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
346 # If we want specific roles for a node, just add the desired states
347 # and its dependencies
348 if [ -z "${ROLES}" ]; then
350 echo " - nginx.passenger" >> ${S_DIR}/top.sls
351 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
352 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
354 echo " - postgres" >> ${S_DIR}/top.sls
355 echo " - docker" >> ${S_DIR}/top.sls
356 echo " - arvados" >> ${S_DIR}/top.sls
359 echo " - docker" >> ${P_DIR}/top.sls
360 echo " - nginx_api_configuration" >> ${P_DIR}/top.sls
361 echo " - nginx_controller_configuration" >> ${P_DIR}/top.sls
362 echo " - nginx_keepproxy_configuration" >> ${P_DIR}/top.sls
363 echo " - nginx_keepweb_configuration" >> ${P_DIR}/top.sls
364 echo " - nginx_passenger" >> ${P_DIR}/top.sls
365 echo " - nginx_websocket_configuration" >> ${P_DIR}/top.sls
366 echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
367 echo " - nginx_workbench2_configuration" >> ${P_DIR}/top.sls
368 echo " - nginx_workbench_configuration" >> ${P_DIR}/top.sls
369 echo " - postgresql" >> ${P_DIR}/top.sls
370 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
371 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
374 # If we add individual roles, make sure we add the repo first
375 echo " - arvados.repo" >> ${S_DIR}/top.sls
376 for R in ${ROLES}; do
380 echo " - postgres" >> ${S_DIR}/top.sls
382 echo ' - postgresql' >> ${P_DIR}/top.sls
386 # FIXME: https://dev.arvados.org/issues/17352
387 grep -q "postgres.client" ${S_DIR}/top.sls || echo " - postgres.client" >> ${S_DIR}/top.sls
388 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
389 ### If we don't install and run LE before arvados-api-server, it fails and breaks everything
390 ### after it so we add this here, as we are, after all, sharing the host for api and controller
391 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
392 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
394 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
396 grep -q "docker" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
397 grep -q "postgresql" ${P_DIR}/top.sls || echo " - postgresql" >> ${P_DIR}/top.sls
398 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
399 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
401 "controller" | "websocket" | "workbench" | "workbench2" | "keepweb" | "keepproxy")
403 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
404 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
405 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
407 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
409 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
410 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
411 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
412 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
413 grep -q "letsencrypt_${R}_configuration" ${P_DIR}/top.sls || echo " - letsencrypt_${R}_configuration" >> ${P_DIR}/top.sls
418 grep -q "docker" ${S_DIR}/top.sls || echo " - docker" >> ${S_DIR}/top.sls
419 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
421 grep -q "" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
422 grep -q "nginx_webshell_configuration" ${P_DIR}/top.sls || echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
426 grep -q "docker" ${S_DIR}/top.sls || echo " - docker" >> ${S_DIR}/top.sls
427 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
429 # ATM, no specific pillar needed
433 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
435 # ATM, no specific pillar needed
438 echo "Unknown role ${R}"
445 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
446 if [ -e /root/.psqlrc ]; then
447 if ! ( grep 'pset pager off' /root/.psqlrc ); then
449 cp /root/.psqlrc /root/.psqlrc.provision.backup
455 echo '\pset pager off' >> /root/.psqlrc
456 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
458 # Now run the install
459 salt-call --local state.apply -l ${LOG_LEVEL}
461 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
462 if [ "x${DELETE_PSQL}" = "xyes" ]; then
463 echo "Removing .psql file"
467 if [ "x${RESTORE_PSQL}" = "xyes" ]; then
468 echo "Restoring .psql file"
469 mv -v /root/.psqlrc.provision.backup /root/.psqlrc
471 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
473 # Leave a copy of the Arvados CA so the user can copy it where it's required
474 echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
475 # If running in a vagrant VM, also add default user to docker group
476 if [ "x${VAGRANT}" = "xyes" ]; then
477 cp /etc/ssl/certs/arvados-snakeoil-ca.pem /vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
479 echo "Adding the vagrant user to the docker group"
480 usermod -a -G docker vagrant
482 cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
485 # Test that the installation finished correctly
486 if [ "x${TEST}" = "xyes" ]; then
487 cd /tmp/cluster_tests