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
133 ARVADOS_TAG="v.1.1.4"
134 POSTGRES_TAG="v0.41.6"
135 NGINX_TAG="temp-fix-missing-statements-in-pillar"
138 LETSENCRYPT_TAG="v2.1.0"
144 F_DIR="/srv/formulas"
150 if [ -s ${CONFIG_FILE} ]; then
151 source ${CONFIG_FILE}
153 echo >&2 "Please create a '${CONFIG_FILE}' file with initial values, as described in"
154 echo >&2 " * https://doc.arvados.org/install/salt-single-host.html#single_host, or"
155 echo >&2 " * https://doc.arvados.org/install/salt-multi-host.html#multi_host_multi_hostnames"
159 if grep -q 'fixme_or_this_wont_work' ${CONFIG_FILE} ; then
160 echo >&2 "The config file ${CONFIG_FILE} has some parameters that need to be modified."
161 echo >&2 "Please, fix them and re-run the provision script."
165 if ! grep -E '^[[:alnum:]]{5}$' <<<${CLUSTER} ; then
166 echo >&2 "ERROR: <CLUSTER> must be exactly 5 alphanumeric characters long"
167 echo >&2 "Fix the cluster name in the 'local.params' file and re-run the provision script"
172 apt-get install -y curl git jq
174 if which salt-call; then
175 echo "Salt already installed"
177 curl -L https://bootstrap.saltstack.com -o /tmp/bootstrap_salt.sh
178 sh /tmp/bootstrap_salt.sh -XdfP -x python3
179 /bin/systemctl stop salt-minion.service
180 /bin/systemctl disable salt-minion.service
183 # Set salt to masterless mode
184 cat > /etc/salt/minion << EOFSM
196 mkdir -p ${S_DIR} ${F_DIR} ${P_DIR}
198 # Get the formula and dependencies
199 cd ${F_DIR} || exit 1
200 git clone --branch "${ARVADOS_TAG}" https://github.com/arvados/arvados-formula.git
201 git clone --branch "${DOCKER_TAG}" https://github.com/saltstack-formulas/docker-formula.git
202 git clone --branch "${LOCALE_TAG}" https://github.com/saltstack-formulas/locale-formula.git
203 # git clone --branch "${NGINX_TAG}" https://github.com/saltstack-formulas/nginx-formula.git
204 git clone --branch "${NGINX_TAG}" https://github.com/netmanagers/nginx-formula.git
205 git clone --branch "${POSTGRES_TAG}" https://github.com/saltstack-formulas/postgres-formula.git
206 git clone --branch "${LETSENCRYPT_TAG}" https://github.com/saltstack-formulas/letsencrypt-formula.git
208 # If we want to try a specific branch of the formula
209 if [ "x${BRANCH}" != "x" ]; then
210 cd ${F_DIR}/arvados-formula || exit 1
211 git checkout -t origin/"${BRANCH}" -b "${BRANCH}"
215 if [ "x${VAGRANT}" = "xyes" ]; then
216 SOURCE_PILLARS_DIR="/vagrant/${CONFIG_DIR}/pillars"
217 SOURCE_TESTS_DIR="/vagrant/${TESTS_DIR}"
219 SOURCE_PILLARS_DIR="${SCRIPT_DIR}/${CONFIG_DIR}/pillars"
220 SOURCE_TESTS_DIR="${SCRIPT_DIR}/${TESTS_DIR}"
223 SOURCE_STATES_DIR="${EXTRA_STATES_DIR}"
225 # Replace variables (cluster, domain, etc) in the pillars, states and tests
226 # to ease deployment for newcomers
227 for f in "${SOURCE_PILLARS_DIR}"/*; do
228 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
229 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
230 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
231 s#__CLUSTER__#${CLUSTER}#g;
232 s#__DOMAIN__#${DOMAIN}#g;
233 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
234 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
235 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
236 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
237 s#__INITIAL_USER__#${INITIAL_USER}#g;
238 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
239 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
240 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
241 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
242 s#__RELEASE__#${RELEASE}#g;
243 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
244 s#__VERSION__#${VERSION}#g;
245 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
246 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
247 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
248 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
249 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
250 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
251 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
252 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
253 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
254 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
255 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
256 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
257 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
258 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
259 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
260 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
261 "${f}" > "${P_DIR}"/$(basename "${f}")
264 mkdir -p /tmp/cluster_tests
265 # Replace cluster and domain name in the test files
266 for f in "${SOURCE_TESTS_DIR}"/*; do
267 sed "s#__CLUSTER__#${CLUSTER}#g;
268 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
269 s#__DOMAIN__#${DOMAIN}#g;
270 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
271 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
272 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g
273 s#__INITIAL_USER__#${INITIAL_USER}#g;
274 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
275 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g" \
276 "${f}" > "/tmp/cluster_tests"/$(basename "${f}")
278 chmod 755 /tmp/cluster_tests/run-test.sh
280 # Replace helper state files that differ from the formula's examples
281 if [ -d "${SOURCE_STATES_DIR}" ]; then
282 mkdir -p "${F_DIR}"/extra/extra
284 for f in "${SOURCE_STATES_DIR}"/*; do
285 sed "s#__ANONYMOUS_USER_TOKEN__#${ANONYMOUS_USER_TOKEN}#g;
286 s#__CLUSTER__#${CLUSTER}#g;
287 s#__BLOB_SIGNING_KEY__#${BLOB_SIGNING_KEY}#g;
288 s#__CONTROLLER_EXT_SSL_PORT__#${CONTROLLER_EXT_SSL_PORT}#g;
289 s#__DOMAIN__#${DOMAIN}#g;
290 s#__HOSTNAME_EXT__#${HOSTNAME_EXT}#g;
291 s#__HOSTNAME_INT__#${HOSTNAME_INT}#g;
292 s#__INITIAL_USER_EMAIL__#${INITIAL_USER_EMAIL}#g;
293 s#__INITIAL_USER_PASSWORD__#${INITIAL_USER_PASSWORD}#g;
294 s#__INITIAL_USER__#${INITIAL_USER}#g;
295 s#__DATABASE_PASSWORD__#${DATABASE_PASSWORD}#g;
296 s#__KEEPWEB_EXT_SSL_PORT__#${KEEPWEB_EXT_SSL_PORT}#g;
297 s#__KEEP_EXT_SSL_PORT__#${KEEP_EXT_SSL_PORT}#g;
298 s#__MANAGEMENT_TOKEN__#${MANAGEMENT_TOKEN}#g;
299 s#__RELEASE__#${RELEASE}#g;
300 s#__SYSTEM_ROOT_TOKEN__#${SYSTEM_ROOT_TOKEN}#g;
301 s#__VERSION__#${VERSION}#g;
302 s#__CLUSTER_INT_CIDR__#${CLUSTER_INT_CIDR}#g;
303 s#__CONTROLLER_INT_IP__#${CONTROLLER_INT_IP}#g;
304 s#__WEBSOCKET_INT_IP__#${WEBSOCKET_INT_IP}#g;
305 s#__KEEP_INT_IP__#${KEEP_INT_IP}#g;
306 s#__KEEPSTORE0_INT_IP__#${KEEPSTORE0_INT_IP}#g;
307 s#__KEEPSTORE1_INT_IP__#${KEEPSTORE1_INT_IP}#g;
308 s#__KEEPWEB_INT_IP__#${KEEPWEB_INT_IP}#g;
309 s#__WEBSHELL_INT_IP__#${WEBSHELL_INT_IP}#g;
310 s#__WORKBENCH1_INT_IP__#${WORKBENCH1_INT_IP}#g;
311 s#__WORKBENCH2_INT_IP__#${WORKBENCH2_INT_IP}#g;
312 s#__DATABASE_INT_IP__#${DATABASE_INT_IP}#g;
313 s#__WEBSHELL_EXT_SSL_PORT__#${WEBSHELL_EXT_SSL_PORT}#g;
314 s#__WEBSOCKET_EXT_SSL_PORT__#${WEBSOCKET_EXT_SSL_PORT}#g;
315 s#__WORKBENCH1_EXT_SSL_PORT__#${WORKBENCH1_EXT_SSL_PORT}#g;
316 s#__WORKBENCH2_EXT_SSL_PORT__#${WORKBENCH2_EXT_SSL_PORT}#g;
317 s#__WORKBENCH_SECRET_KEY__#${WORKBENCH_SECRET_KEY}#g" \
318 "${f}" > "${F_DIR}/extra/extra"/$(basename "${f}")
322 # Now, we build the SALT states/pillars trees
323 # As we need to separate both states and pillars in case we want specific
324 # roles, we iterate on both at the same time
327 cat > ${S_DIR}/top.sls << EOFTSLS
334 cat > ${P_DIR}/top.sls << EOFPSLS
341 # States, extra states
342 if [ -d "${F_DIR}"/extra/extra ]; then
343 for f in "${F_DIR}"/extra/extra/*.sls; do
344 echo " - extra.$(basename ${f} | sed 's/.sls$//g')" >> ${S_DIR}/top.sls
348 # If we want specific roles for a node, just add the desired states
349 # and its dependencies
350 if [ -z "${ROLES}" ]; then
352 echo " - nginx.passenger" >> ${S_DIR}/top.sls
353 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
354 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
356 echo " - postgres" >> ${S_DIR}/top.sls
357 echo " - docker" >> ${S_DIR}/top.sls
358 echo " - arvados" >> ${S_DIR}/top.sls
361 echo " - docker" >> ${P_DIR}/top.sls
362 echo " - nginx_api_configuration" >> ${P_DIR}/top.sls
363 echo " - nginx_controller_configuration" >> ${P_DIR}/top.sls
364 echo " - nginx_keepproxy_configuration" >> ${P_DIR}/top.sls
365 echo " - nginx_keepweb_configuration" >> ${P_DIR}/top.sls
366 echo " - nginx_passenger" >> ${P_DIR}/top.sls
367 echo " - nginx_websocket_configuration" >> ${P_DIR}/top.sls
368 echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
369 echo " - nginx_workbench2_configuration" >> ${P_DIR}/top.sls
370 echo " - nginx_workbench_configuration" >> ${P_DIR}/top.sls
371 echo " - postgresql" >> ${P_DIR}/top.sls
372 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
373 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
376 # If we add individual roles, make sure we add the repo first
377 echo " - arvados.repo" >> ${S_DIR}/top.sls
378 for R in ${ROLES}; do
382 echo " - postgres" >> ${S_DIR}/top.sls
384 echo ' - postgresql' >> ${P_DIR}/top.sls
388 # FIXME: https://dev.arvados.org/issues/17352
389 grep -q "postgres.client" ${S_DIR}/top.sls || echo " - postgres.client" >> ${S_DIR}/top.sls
390 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
391 ### If we don't install and run LE before arvados-api-server, it fails and breaks everything
392 ### after it so we add this here, as we are, after all, sharing the host for api and controller
393 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
394 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
396 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
398 grep -q "docker" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
399 grep -q "postgresql" ${P_DIR}/top.sls || echo " - postgresql" >> ${P_DIR}/top.sls
400 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
401 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
403 "controller" | "websocket" | "workbench" | "workbench2" | "keepweb" | "keepproxy")
405 grep -q "nginx.passenger" ${S_DIR}/top.sls || echo " - nginx.passenger" >> ${S_DIR}/top.sls
406 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
407 grep -q "letsencrypt" ${S_DIR}/top.sls || echo " - letsencrypt" >> ${S_DIR}/top.sls
409 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
411 grep -q "nginx_passenger" ${P_DIR}/top.sls || echo " - nginx_passenger" >> ${P_DIR}/top.sls
412 grep -q "nginx_${R}_configuration" ${P_DIR}/top.sls || echo " - nginx_${R}_configuration" >> ${P_DIR}/top.sls
413 if [ "x${USE_LETSENCRYPT}" = "xyes" ]; then
414 grep -q "letsencrypt" ${P_DIR}/top.sls || echo " - letsencrypt" >> ${P_DIR}/top.sls
415 grep -q "letsencrypt_${R}_configuration" ${P_DIR}/top.sls || echo " - letsencrypt_${R}_configuration" >> ${P_DIR}/top.sls
420 grep -q "docker" ${S_DIR}/top.sls || echo " - docker" >> ${S_DIR}/top.sls
421 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
423 grep -q "" ${P_DIR}/top.sls || echo " - docker" >> ${P_DIR}/top.sls
424 grep -q "nginx_webshell_configuration" ${P_DIR}/top.sls || echo " - nginx_webshell_configuration" >> ${P_DIR}/top.sls
428 grep -q "docker" ${S_DIR}/top.sls || echo " - docker" >> ${S_DIR}/top.sls
429 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
431 # ATM, no specific pillar needed
435 grep -q "arvados.${R}" ${S_DIR}/top.sls || echo " - arvados.${R}" >> ${S_DIR}/top.sls
437 # ATM, no specific pillar needed
440 echo "Unknown role ${R}"
447 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
448 if [ -e /root/.psqlrc ]; then
449 if ! ( grep 'pset pager off' /root/.psqlrc ); then
451 cp /root/.psqlrc /root/.psqlrc.provision.backup
457 echo '\pset pager off' >> /root/.psqlrc
458 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
460 # Now run the install
461 salt-call --local state.apply -l ${LOG_LEVEL}
463 # FIXME! #16992 Temporary fix for psql call in arvados-api-server
464 if [ "x${DELETE_PSQL}" = "xyes" ]; then
465 echo "Removing .psql file"
469 if [ "x${RESTORE_PSQL}" = "xyes" ]; then
470 echo "Restoring .psql file"
471 mv -v /root/.psqlrc.provision.backup /root/.psqlrc
473 # END FIXME! #16992 Temporary fix for psql call in arvados-api-server
475 # Leave a copy of the Arvados CA so the user can copy it where it's required
476 echo "Copying the Arvados CA certificate to the installer dir, so you can import it"
477 # If running in a vagrant VM, also add default user to docker group
478 if [ "x${VAGRANT}" = "xyes" ]; then
479 cp /etc/ssl/certs/arvados-snakeoil-ca.pem /vagrant/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
481 echo "Adding the vagrant user to the docker group"
482 usermod -a -G docker vagrant
484 cp /etc/ssl/certs/arvados-snakeoil-ca.pem ${SCRIPT_DIR}/${CLUSTER}.${DOMAIN}-arvados-snakeoil-ca.pem
487 # Test that the installation finished correctly
488 if [ "x${TEST}" = "xyes" ]; then
489 cd /tmp/cluster_tests