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="local_config_dir"
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
129 # For a stable release, change RELEASE "production" and VERSION to the
130 # package version of the release.
131 RELEASE="development"
134 # The arvados-formula version. For a stable release, change the tag
135 # or X.Y-dev branch of the release.
138 # Other formula versions we depend on
139 POSTGRES_TAG="v0.41.6"
140 NGINX_TAG="temp-fix-missing-statements-in-pillar"
143 LETSENCRYPT_TAG="v2.1.0"
149 F_DIR="/srv/formulas"
155 if [ -s ${CONFIG_FILE} ]; then
156 source ${CONFIG_FILE}
158 echo >&2 "Please create a '${CONFIG_FILE}' file with initial values, as described in"
159 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
160 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
164 if [ ! -d ${CONFIG_DIR} ]; then
165 echo >&2 "Please create a '${CONFIG_DIR}' with initial values, as described in"
166 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
167 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
171 if grep -q 'fixme_or_this_wont_work' ${CONFIG_FILE} ; then
172 echo >&2 "The config file ${CONFIG_FILE} has some parameters that need to be modified."
173 echo >&2 "Please, fix them and re-run the provision script."
177 if ! grep -E '^[[:alnum:]]{5}$' <<<${CLUSTER} ; then
178 echo >&2 "ERROR: <CLUSTER> must be exactly 5 alphanumeric characters long"
179 echo >&2 "Fix the cluster name in the 'local.params' file and re-run the provision script"
183 # Only used in single_host/single_name deploys
184 if [ "x${HOSTNAME_EXT}" = "x" ] ; then
185 HOSTNAME_EXT="${CLUSTER}.${DOMAIN}"
189 apt-get install -y curl git jq
191 if which salt-call; then
192 echo "Salt already installed"
194 curl -L https://bootstrap.saltstack.com -o /tmp/bootstrap_salt.sh
195 sh /tmp/bootstrap_salt.sh -XdfP -x python3
196 /bin/systemctl stop salt-minion.service
197 /bin/systemctl disable salt-minion.service
200 # Set salt to masterless mode
201 cat > /etc/salt/minion << EOFSM
213 mkdir -p ${S_DIR} ${F_DIR} ${P_DIR}
215 # Get the formula and dependencies
216 cd ${F_DIR} || exit 1
217 git clone --branch "${ARVADOS_TAG}" https://github.com/arvados/arvados-formula.git
218 git clone --branch "${DOCKER_TAG}" https://github.com/saltstack-formulas/docker-formula.git
219 git clone --branch "${LOCALE_TAG}" https://github.com/saltstack-formulas/locale-formula.git
220 # git clone --branch "${NGINX_TAG}" https://github.com/saltstack-formulas/nginx-formula.git
221 git clone --branch "${NGINX_TAG}" https://github.com/netmanagers/nginx-formula.git
222 git clone --branch "${POSTGRES_TAG}" https://github.com/saltstack-formulas/postgres-formula.git
223 git clone --branch "${LETSENCRYPT_TAG}" https://github.com/saltstack-formulas/letsencrypt-formula.git
225 # If we want to try a specific branch of the formula
226 if [ "x${BRANCH}" != "x" ]; then
227 cd ${F_DIR}/arvados-formula || exit 1
228 git checkout -t origin/"${BRANCH}" -b "${BRANCH}"
232 if [ "x${VAGRANT}" = "xyes" ]; then
233 EXTRA_STATES_DIR="/home/vagrant/${CONFIG_DIR}/states"
234 SOURCE_PILLARS_DIR="/home/vagrant/${CONFIG_DIR}/pillars"
235 SOURCE_TESTS_DIR="/home/vagrant/${TESTS_DIR}"
237 EXTRA_STATES_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/states"
238 SOURCE_PILLARS_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/pillars"
239 SOURCE_TESTS_DIR="${SCRIPT_DIR}/${TESTS_DIR}"
242 SOURCE_STATES_DIR="${EXTRA_STATES_DIR}"
244 # Replace variables (cluster, domain, etc) in the pillars, states and tests
245 # to ease deployment for newcomers
246 if [ ! -d "${SOURCE_PILLARS_DIR}" ]; then
247 echo "${SOURCE_PILLARS_DIR} does not exist or is not a directory. Exiting."
250 for f in $(ls "${SOURCE_PILLARS_DIR}"/*); do
251 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
252 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
253 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
254 s#__CLUSTER__#${CLUSTER}#g;
255 s#__DOMAIN__#${DOMAIN}#g;
256 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
257 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
258 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
259 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
260 s#__INITIAL_USER__#${INITIAL_USER}#g;
261 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
262 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
263 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
264 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
265 s#__RELEASE__#${RELEASE}#g;
266 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
267 s#__VERSION__#${VERSION}#g;
268 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
269 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
270 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
271 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
272 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
273 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
274 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
275 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
276 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
277 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
278 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
279 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
280 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
281 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
282 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
283 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
284 "${f}" > "${P_DIR}"/$(basename "${f}")
287 if [ "x${TEST}" = "xyes" ] && [ ! -d "${SOURCE_TESTS_DIR}" ]; then
288 echo "You requested to run tests, but ${SOURCE_TESTS_DIR} does not exist or is not a directory. Exiting."
291 mkdir -p /tmp/cluster_tests
292 # Replace cluster and domain name in the test files
293 for f in $(ls "${SOURCE_TESTS_DIR}"/*); do
294 sed "s#__CLUSTER__#${CLUSTER}#g;
295 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
296 s#__DOMAIN__#${DOMAIN}#g;
297 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
298 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
299 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
300 s#__INITIAL_USER__#${INITIAL_USER}#g;
301 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
302 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g" \
303 "${f}" > "/tmp/cluster_tests"/$(basename "${f}")
305 chmod 755 /tmp/cluster_tests/run-test.sh
307 # Replace helper state files that differ from the formula's examples
308 if [ -d "${SOURCE_STATES_DIR}" ]; then
309 mkdir -p "${F_DIR}"/extra/extra
311 for f in $(ls "${SOURCE_STATES_DIR}"/*); do
312 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
313 s#__CLUSTER__#${CLUSTER}#g;
314 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
315 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
316 s#__DOMAIN__#${DOMAIN}#g;
317 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
318 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
319 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
320 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
321 s#__INITIAL_USER__#${INITIAL_USER}#g;
322 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
323 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
324 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
325 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
326 s#__RELEASE__#${RELEASE}#g;
327 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
328 s#__VERSION__#${VERSION}#g;
329 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
330 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
331 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
332 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
333 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
334 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
335 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
336 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
337 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
338 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
339 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
340 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
341 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
342 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
343 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
344 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
345 "${f}" > "${F_DIR}/extra/extra"/$(basename "${f}")
349 # Now, we build the SALT states/pillars trees
350 # As we need to separate both states and pillars in case we want specific
351 # roles, we iterate on both at the same time
354 cat > ${S_DIR}/top.sls << EOFTSLS
361 cat > ${P_DIR}/top.sls << EOFPSLS
368 # States, extra states
369 if [ -d "${F_DIR}"/extra/extra ]; then
370 for f in $(ls "${F_DIR}"/extra/extra/*.sls); do
371 echo " - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
375 # If we want specific roles for a node, just add the desired states
376 # and its dependencies
377 if [ -z "${ROLES}" ]; then
379 echo " - nginx.passenger" >> ${S_DIR}/top.sls
380 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
381 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
383 echo " - postgres" >> ${S_DIR}/top.sls
384 echo " - docker.software" >> ${S_DIR}/top.sls
385 echo " - arvados" >> ${S_DIR}/top.sls
388 echo " - docker" >> ${P_DIR}/top.sls
389 echo " - nginx_api_configuration" >> ${P_DIR}/top.sls
390 echo " - nginx_controller_configuration" >> ${P_DIR}/top.sls
391 echo " - nginx_keepproxy_configuration" >> ${P_DIR}/top.sls
392 echo " - nginx_keepweb_configuration" >> ${P_DIR}/top.sls
393 echo " - nginx_passenger" >> ${P_DIR}/top.sls
394 echo " - nginx_websocket_configuration" >> ${P_DIR}/top.sls
395 echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
396 echo " - nginx_workbench2_configuration" >> ${P_DIR}/top.sls
397 echo " - nginx_workbench_configuration" >> ${P_DIR}/top.sls
398 echo " - postgresql" >> ${P_DIR}/top.sls
399 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
400 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
403 # If we add individual roles, make sure we add the repo first
404 echo " - arvados.repo" >> ${S_DIR}/top.sls
405 for R in ${ROLES}; do
409 echo " - postgres" >> ${S_DIR}/top.sls
411 echo ' - postgresql' >> ${P_DIR}/top.sls
415 # FIXME: https://dev.arvados.org/issues/17352
416 grep -q "postgres.client" ${S_DIR}/top.sls || echo " - postgres.client" >> ${S_DIR}/top.sls
417 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
418 ### If we don't install and run LE before arvados-api-server, it fails and breaks everything
419 ### after it so we add this here, as we are, after all, sharing the host for api and controller
420 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
421 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
423 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
425 grep -q "docker" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
426 grep -q "postgresql" ${P_DIR}/top.sls || echo " - postgresql" >> ${P_DIR}/top.sls
427 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
428 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
430 "controller" | "websocket" | "workbench" | "workbench2" | "keepweb" | "keepproxy")
432 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
433 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
434 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
436 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
438 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
439 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
440 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
441 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
442 grep -q "letsencrypt_${R}_configuration" ${P_DIR}/top.sls || echo " - letsencrypt_${R}_configuration" >> ${P_DIR}/top.sls
447 grep -q "docker" ${S_DIR}/top.sls || echo " - docker.software" >> ${S_DIR}/top.sls
448 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
450 grep -q "" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
451 grep -q "nginx_webshell_configuration" ${P_DIR}/top.sls || echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
455 grep -q "docker" ${S_DIR}/top.sls || echo " - docker.software" >> ${S_DIR}/top.sls
456 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
458 # ATM, no specific pillar needed
462 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
464 # ATM, no specific pillar needed
467 echo "Unknown role ${R}"
474 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
475 if [ -e /root/.psqlrc ]; then
476 if ! ( grep 'pset pager off' /root/.psqlrc ); then
478 cp /root/.psqlrc /root/.psqlrc.provision.backup
484 echo '\pset pager off' >> /root/.psqlrc
485 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
487 # Now run the install
488 salt-call --local state.apply -l ${LOG_LEVEL}
490 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
491 if [ "x${DELETE_PSQL}" = "xyes" ]; then
492 echo "Removing .psql file"
496 if [ "x${RESTORE_PSQL}" = "xyes" ]; then
497 echo "Restoring .psql file"
498 mv -v /root/.psqlrc.provision.backup /root/.psqlrc
500 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
502 # Leave a copy of the Arvados CA so the user can copy it where it's required
503 echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
504 # If running in a vagrant VM, also add default user to docker group
505 if [ "x${VAGRANT}" = "xyes" ]; then
506 cp /etc/ssl/certs/arvados-snakeoil-ca.pem /vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
508 echo "Adding the vagrant user to the docker group"
509 usermod -a -G docker vagrant
511 cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
514 # Test that the installation finished correctly
515 if [ "x${TEST}" = "xyes" ]; then
516 cd /tmp/cluster_tests