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"
31 echo >&2 " dispatcher"
39 echo >&2 " workbench2"
40 echo >&2 " Defaults to applying them all"
41 echo >&2 " -h, --help Display this help and exit"
42 echo >&2 " -v, --vagrant Run in vagrant and use the /vagrant shared dir"
47 # NOTE: This requires GNU getopt (part of the util-linux package on Debian-based distros).
48 TEMP=$(getopt -o c:dhp:r:tv \
49 --long config:,debug,help,ssl-port:,roles:,test,vagrant \
52 if [ ${?} != 0 ] ; then echo "GNU getopt missing? Use -h for help"; exit 1 ; fi
53 # Note the quotes around `$TEMP': they are essential!
56 while [ ${#} -ge 1 ]; do
67 CONTROLLER_EXT_SSL_PORT=${2}
73 # Verify the role exists
74 if [[ ! "database,api,controller,keepstore,websocket,keepweb,workbench2,webshell,keepproxy,shell,workbench,dispatcher" == *"$i"* ]]; then
75 echo "The role '${i}' is not a valid role"
103 CONFIG_FILE="${SCRIPT_DIR}/local.params"
104 CONFIG_DIR="local_config_dir"
106 CONTROLLER_EXT_SSL_PORT=443
112 # Hostnames/IPs used for single-host deploys
114 HOSTNAME_INT="127.0.1.1"
118 INITIAL_USER_EMAIL=""
119 INITIAL_USER_PASSWORD=""
121 CONTROLLER_EXT_SSL_PORT=8000
122 KEEP_EXT_SSL_PORT=25101
123 # Both for collections and downloads
124 KEEPWEB_EXT_SSL_PORT=9002
125 WEBSHELL_EXT_SSL_PORT=4202
126 WEBSOCKET_EXT_SSL_PORT=8002
127 WORKBENCH1_EXT_SSL_PORT=443
128 WORKBENCH2_EXT_SSL_PORT=3001
135 POSTGRES_TAG="v0.41.6"
136 NGINX_TAG="temp-fix-missing-statements-in-pillar"
139 LETSENCRYPT_TAG="v2.1.0"
145 F_DIR="/srv/formulas"
151 if [ -s ${CONFIG_FILE} ]; then
152 source ${CONFIG_FILE}
154 echo >&2 "Please create a '${CONFIG_FILE}' file with initial values, as described in"
155 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
156 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
160 if [ ! -d ${CONFIG_DIR} ]; then
161 echo >&2 "Please create a '${CONFIG_DIR}' with initial values, as described in"
162 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
163 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
167 if grep -q 'fixme_or_this_wont_work' ${CONFIG_FILE} ; then
168 echo >&2 "The config file ${CONFIG_FILE} has some parameters that need to be modified."
169 echo >&2 "Please, fix them and re-run the provision script."
173 if ! grep -E '^[[:alnum:]]{5}$' <<<${CLUSTER} ; then
174 echo >&2 "ERROR: <CLUSTER> must be exactly 5 alphanumeric characters long"
175 echo >&2 "Fix the cluster name in the 'local.params' file and re-run the provision script"
179 # Only used in single_host/single_name deploys
180 if [ "x${HOSTNAME_EXT}" = "x" ] ; then
181 HOSTNAME_EXT="${CLUSTER}.${DOMAIN}"
185 apt-get install -y curl git jq
187 if which salt-call; then
188 echo "Salt already installed"
190 curl -L https://bootstrap.saltstack.com -o /tmp/bootstrap_salt.sh
191 sh /tmp/bootstrap_salt.sh -XdfP -x python3
192 /bin/systemctl stop salt-minion.service
193 /bin/systemctl disable salt-minion.service
196 # Set salt to masterless mode
197 cat > /etc/salt/minion << EOFSM
209 mkdir -p ${S_DIR} ${F_DIR} ${P_DIR}
211 # Get the formula and dependencies
212 cd ${F_DIR} || exit 1
213 git clone --branch "${ARVADOS_TAG}" https://github.com/arvados/arvados-formula.git
214 git clone --branch "${DOCKER_TAG}" https://github.com/saltstack-formulas/docker-formula.git
215 git clone --branch "${LOCALE_TAG}" https://github.com/saltstack-formulas/locale-formula.git
216 # git clone --branch "${NGINX_TAG}" https://github.com/saltstack-formulas/nginx-formula.git
217 git clone --branch "${NGINX_TAG}" https://github.com/netmanagers/nginx-formula.git
218 git clone --branch "${POSTGRES_TAG}" https://github.com/saltstack-formulas/postgres-formula.git
219 git clone --branch "${LETSENCRYPT_TAG}" https://github.com/saltstack-formulas/letsencrypt-formula.git
221 # If we want to try a specific branch of the formula
222 if [ "x${BRANCH}" != "x" ]; then
223 cd ${F_DIR}/arvados-formula || exit 1
224 git checkout -t origin/"${BRANCH}" -b "${BRANCH}"
228 if [ "x${VAGRANT}" = "xyes" ]; then
229 EXTRA_STATES_DIR="/home/vagrant/${CONFIG_DIR}/states"
230 SOURCE_PILLARS_DIR="/home/vagrant/${CONFIG_DIR}/pillars"
231 SOURCE_TESTS_DIR="/home/vagrant/${TESTS_DIR}"
233 EXTRA_STATES_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/states"
234 SOURCE_PILLARS_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/pillars"
235 SOURCE_TESTS_DIR="${SCRIPT_DIR}/${TESTS_DIR}"
238 SOURCE_STATES_DIR="${EXTRA_STATES_DIR}"
240 # Replace variables (cluster, domain, etc) in the pillars, states and tests
241 # to ease deployment for newcomers
242 if [ ! -d "${SOURCE_PILLARS_DIR}" ]; then
243 echo "${SOURCE_PILLARS_DIR} does not exist or is not a directory. Exiting."
246 for f in $(ls "${SOURCE_PILLARS_DIR}"/*); do
247 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
248 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
249 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
250 s#__CLUSTER__#${CLUSTER}#g;
251 s#__DOMAIN__#${DOMAIN}#g;
252 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
253 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
254 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
255 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
256 s#__INITIAL_USER__#${INITIAL_USER}#g;
257 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
258 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
259 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
260 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
261 s#__RELEASE__#${RELEASE}#g;
262 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
263 s#__VERSION__#${VERSION}#g;
264 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
265 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
266 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
267 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
268 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
269 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
270 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
271 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
272 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
273 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
274 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
275 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
276 s#__SHELL_INT_IP__#${SHELL_INT_IP}#g;
277 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
278 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
279 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
280 s#__SHELL_INT_IP__#${SHELL_INT_IP}#g;
281 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
282 "${f}" > "${P_DIR}"/$(basename "${f}")
285 if [ "x${TEST}" = "xyes" ] && [ ! -d "${SOURCE_TESTS_DIR}" ]; then
286 echo "You requested to run tests, but ${SOURCE_TESTS_DIR} does not exist or is not a directory. Exiting."
289 mkdir -p /tmp/cluster_tests
290 # Replace cluster and domain name in the test files
291 for f in $(ls "${SOURCE_TESTS_DIR}"/*); do
292 sed "s#__CLUSTER__#${CLUSTER}#g;
293 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
294 s#__DOMAIN__#${DOMAIN}#g;
295 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
296 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
297 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
298 s#__INITIAL_USER__#${INITIAL_USER}#g;
299 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
300 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g" \
301 "${f}" > "/tmp/cluster_tests"/$(basename "${f}")
303 chmod 755 /tmp/cluster_tests/run-test.sh
305 # Replace helper state files that differ from the formula's examples
306 if [ -d "${SOURCE_STATES_DIR}" ]; then
307 mkdir -p "${F_DIR}"/extra/extra
309 for f in $(ls "${SOURCE_STATES_DIR}"/*); do
310 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
311 s#__CLUSTER__#${CLUSTER}#g;
312 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
313 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
314 s#__DOMAIN__#${DOMAIN}#g;
315 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
316 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
317 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
318 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
319 s#__INITIAL_USER__#${INITIAL_USER}#g;
320 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
321 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
322 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
323 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
324 s#__RELEASE__#${RELEASE}#g;
325 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
326 s#__VERSION__#${VERSION}#g;
327 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
328 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
329 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
330 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
331 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
332 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
333 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
334 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
335 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
336 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
337 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
338 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
339 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
340 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
341 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
342 s#__SHELL_INT_IP__#${SHELL_INT_IP}#g;
343 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
344 "${f}" > "${F_DIR}/extra/extra"/$(basename "${f}")
348 # Now, we build the SALT states/pillars trees
349 # As we need to separate both states and pillars in case we want specific
350 # roles, we iterate on both at the same time
353 cat > ${S_DIR}/top.sls << EOFTSLS
360 cat > ${P_DIR}/top.sls << EOFPSLS
367 # States, extra states
368 if [ -d "${F_DIR}"/extra/extra ]; then
369 for f in $(ls "${F_DIR}"/extra/extra/*.sls); do
370 echo " - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
374 # If we want specific roles for a node, just add the desired states
375 # and its dependencies
376 if [ -z "${ROLES}" ]; then
378 echo " - nginx.passenger" >> ${S_DIR}/top.sls
379 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
380 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
382 echo " - postgres" >> ${S_DIR}/top.sls
383 echo " - docker.software" >> ${S_DIR}/top.sls
384 echo " - arvados" >> ${S_DIR}/top.sls
387 echo " - docker" >> ${P_DIR}/top.sls
388 echo " - nginx_api_configuration" >> ${P_DIR}/top.sls
389 echo " - nginx_controller_configuration" >> ${P_DIR}/top.sls
390 echo " - nginx_keepproxy_configuration" >> ${P_DIR}/top.sls
391 echo " - nginx_keepweb_configuration" >> ${P_DIR}/top.sls
392 echo " - nginx_passenger" >> ${P_DIR}/top.sls
393 echo " - nginx_websocket_configuration" >> ${P_DIR}/top.sls
394 echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
395 echo " - nginx_workbench2_configuration" >> ${P_DIR}/top.sls
396 echo " - nginx_workbench_configuration" >> ${P_DIR}/top.sls
397 echo " - postgresql" >> ${P_DIR}/top.sls
398 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
399 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
402 # If we add individual roles, make sure we add the repo first
403 echo " - arvados.repo" >> ${S_DIR}/top.sls
404 for R in ${ROLES}; do
408 echo " - postgres" >> ${S_DIR}/top.sls
410 echo ' - postgresql' >> ${P_DIR}/top.sls
414 # FIXME: https://dev.arvados.org/issues/17352
415 grep -q "postgres.client" ${S_DIR}/top.sls || echo " - postgres.client" >> ${S_DIR}/top.sls
416 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
417 ### If we don't install and run LE before arvados-api-server, it fails and breaks everything
418 ### after it so we add this here, as we are, after all, sharing the host for api and controller
419 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
420 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
422 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
424 grep -q "docker" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
425 grep -q "postgresql" ${P_DIR}/top.sls || echo " - postgresql" >> ${P_DIR}/top.sls
426 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
427 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
429 "controller" | "websocket" | "workbench" | "workbench2" | "webshell" | "keepweb" | "keepproxy")
431 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
432 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
433 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
435 # webshell role is just a nginx vhost, so it has no state
436 if [ "${R}" != "webshell" ]; then
437 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
440 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
441 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
442 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
443 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
444 grep -q "letsencrypt_${R}_configuration" ${P_DIR}/top.sls || echo " - letsencrypt_${R}_configuration" >> ${P_DIR}/top.sls
449 grep -q "docker" ${S_DIR}/top.sls || echo " - docker.software" >> ${S_DIR}/top.sls
450 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
452 grep -q "" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
456 grep -q "docker" ${S_DIR}/top.sls || echo " - docker.software" >> ${S_DIR}/top.sls
457 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
459 # ATM, no specific pillar needed
463 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
465 # ATM, no specific pillar needed
468 echo "Unknown role ${R}"
475 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
476 if [ -e /root/.psqlrc ]; then
477 if ! ( grep 'pset pager off' /root/.psqlrc ); then
479 cp /root/.psqlrc /root/.psqlrc.provision.backup
485 echo '\pset pager off' >> /root/.psqlrc
486 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
488 # Now run the install
489 salt-call --local state.apply -l ${LOG_LEVEL}
491 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
492 if [ "x${DELETE_PSQL}" = "xyes" ]; then
493 echo "Removing .psql file"
497 if [ "x${RESTORE_PSQL}" = "xyes" ]; then
498 echo "Restoring .psql file"
499 mv -v /root/.psqlrc.provision.backup /root/.psqlrc
501 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
503 # Leave a copy of the Arvados CA so the user can copy it where it's required
504 echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
505 # If running in a vagrant VM, also add default user to docker group
506 if [ "x${VAGRANT}" = "xyes" ]; then
507 cp /etc/ssl/certs/arvados-snakeoil-ca.pem /vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
509 echo "Adding the vagrant user to the docker group"
510 usermod -a -G docker vagrant
512 cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
515 # Test that the installation finished correctly
516 if [ "x${TEST}" = "xyes" ]; then
517 cd /tmp/cluster_tests