16314: Merge branch 'master'
[arvados.git] / tools / arvbox / bin / arvbox
1 #!/bin/bash
2 # Copyright (C) The Arvados Authors. All rights reserved.
3 #
4 # SPDX-License-Identifier: AGPL-3.0
5
6 set -e
7
8 if ! test -d /sys/fs/cgroup ; then
9      echo "Arvbox requires cgroups to be mounted at /sys/fs/cgroup in order to use"
10      echo "Docker-in-Docker.  Older operating systems that put cgroups in other"
11      echo "places (such as /cgroup) are not supported."
12      exit 1
13 fi
14
15 if ! which docker >/dev/null 2>/dev/null ; then
16   echo "Arvbox requires Docker.  To install, run the following command as root:"
17   echo "curl -sSL https://get.docker.com/ | sh"
18   exit 1
19 fi
20
21 if test -z "$ARVBOX_DOCKER" ; then
22     set +e
23     if which greadlink >/dev/null 2>/dev/null ; then
24         ARVBOX_DOCKER=$(greadlink -f $(dirname $0)/../lib/arvbox/docker)
25     else
26         ARVBOX_DOCKER=$(readlink -f $(dirname $0)/../lib/arvbox/docker)
27     fi
28     set -e
29 fi
30
31 if test -z "$ARVBOX_CONTAINER" ; then
32     ARVBOX_CONTAINER=arvbox
33 fi
34
35 if test -z "$ARVBOX_BASE" ; then
36     ARVBOX_BASE="$HOME/.arvbox"
37 fi
38
39 if test -z "$ARVBOX_DATA" ; then
40     ARVBOX_DATA="$ARVBOX_BASE/$ARVBOX_CONTAINER"
41 fi
42
43 if test -z "$ARVADOS_ROOT" ; then
44     ARVADOS_ROOT="$ARVBOX_DATA/arvados"
45 fi
46
47 if test -z "$COMPOSER_ROOT" ; then
48     COMPOSER_ROOT="$ARVBOX_DATA/composer"
49 fi
50
51 if test -z "$WORKBENCH2_ROOT" ; then
52     WORKBENCH2_ROOT="$ARVBOX_DATA/workbench2"
53 fi
54
55 PG_DATA="$ARVBOX_DATA/postgres"
56 VAR_DATA="$ARVBOX_DATA/var"
57 PASSENGER="$ARVBOX_DATA/passenger"
58 GEMS="$ARVBOX_DATA/gems"
59 PIPCACHE="$ARVBOX_DATA/pip"
60 NPMCACHE="$ARVBOX_DATA/npm"
61 GOSTUFF="$ARVBOX_DATA/gopath"
62 RLIBS="$ARVBOX_DATA/Rlibs"
63
64 getip() {
65     docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' $ARVBOX_CONTAINER
66 }
67
68 gethost() {
69     set +e
70     OVERRIDE=$(docker exec -i $ARVBOX_CONTAINER cat /var/run/localip_override 2>/dev/null)
71     CODE=$?
72     set -e
73     if test "$CODE" = 0 ; then
74        echo $OVERRIDE
75     else
76         getip
77     fi
78 }
79
80 getclusterid() {
81     docker exec $ARVBOX_CONTAINER cat /var/lib/arvados/api_uuid_prefix
82 }
83
84 updateconf() {
85     if test -f ~/.config/arvados/$ARVBOX_CONTAINER.conf ; then
86         sed "s/ARVADOS_API_HOST=.*/ARVADOS_API_HOST=$(gethost):8000/" <$HOME/.config/arvados/$ARVBOX_CONTAINER.conf >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf.tmp
87         mv ~/.config/arvados/$ARVBOX_CONTAINER.conf.tmp ~/.config/arvados/$ARVBOX_CONTAINER.conf
88     else
89         mkdir -p $HOME/.config/arvados
90         cat >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf <<EOF
91 ARVADOS_API_HOST=$(gethost):8000
92 ARVADOS_API_TOKEN=
93 ARVADOS_API_HOST_INSECURE=true
94 EOF
95     fi
96 }
97
98 wait_for_arvbox() {
99     FF=/tmp/arvbox-fifo-$$
100     mkfifo $FF
101     docker logs -f $ARVBOX_CONTAINER > $FF &
102     LOGPID=$!
103     while read line ; do
104         if [[ $line =~ "ok: down: ready:" ]] ; then
105             kill $LOGPID
106             set +e
107             wait $LOGPID 2>/dev/null
108             set -e
109         else
110             echo $line
111         fi
112     done < $FF
113     rm $FF
114     echo
115     if test -n "$localip" ; then
116         echo "export ARVADOS_API_HOST=$localip:8000"
117     else
118         echo "export ARVADOS_API_HOST=$(gethost):8000"
119     fi
120 }
121
122 docker_run_dev() {
123     docker run \
124            "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
125            "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
126            "--volume=$WORKBENCH2_ROOT:/usr/src/workbench2:rw" \
127            "--volume=$PG_DATA:/var/lib/postgresql:rw" \
128            "--volume=$VAR_DATA:/var/lib/arvados:rw" \
129            "--volume=$PASSENGER:/var/lib/passenger:rw" \
130            "--volume=$GEMS:/var/lib/gems:rw" \
131            "--volume=$PIPCACHE:/var/lib/pip:rw" \
132            "--volume=$NPMCACHE:/var/lib/npm:rw" \
133            "--volume=$GOSTUFF:/var/lib/gopath:rw" \
134            "--volume=$RLIBS:/var/lib/Rlibs:rw" \
135            --label "org.arvados.arvbox_config=$CONFIG" \
136            "$@"
137 }
138
139 running_config() {
140     docker inspect $ARVBOX_CONTAINER -f '{{index .Config.Labels "org.arvados.arvbox_config"}}'
141 }
142
143 run() {
144     CONFIG=$1
145     TAG=$2
146
147     shift
148
149     need_setup=1
150
151     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
152         if [[ $(running_config) != "$CONFIG" ]] ; then
153             echo "Container $ARVBOX_CONTAINER is '$(running_config)' config but requested '$CONFIG'; use restart or reboot"
154             return 1
155         fi
156         if test "$CONFIG" = test -o "$CONFIG" = devenv ; then
157             need_setup=0
158         else
159             echo "Container $ARVBOX_CONTAINER is already running"
160             return 0
161         fi
162     fi
163
164     if test $need_setup = 1 ; then
165         if docker ps -a | grep -E "$ARVBOX_CONTAINER$" -q ; then
166             echo "Container $ARVBOX_CONTAINER already exists but is not running; use restart or reboot"
167             return 1
168         fi
169     fi
170
171     if test -n "$TAG"
172     then
173         if test $(echo $TAG | cut -c1-1) != '-' ; then
174             TAG=":$TAG"
175             shift
176         else
177             if [[ $TAG = '-' ]] ; then
178                 shift
179             fi
180             unset TAG
181         fi
182     fi
183
184     if [[ "$CONFIG" =~ ^public ]] ; then
185         if test -n "$ARVBOX_PUBLISH_IP" ; then
186             localip=$ARVBOX_PUBLISH_IP
187         else
188             defaultdev=$(/sbin/ip route|awk '/default/ { print $5 }')
189             localip=$(ip addr show $defaultdev | grep 'inet ' | sed 's/ *inet \(.*\)\/.*/\1/')
190         fi
191         echo "Public arvbox will use address $localip"
192         iptemp=$(mktemp)
193         echo $localip > $iptemp
194         chmod og+r $iptemp
195         PUBLIC="--volume=$iptemp:/var/run/localip_override
196               --publish=443:443
197               --publish=3001:3001
198               --publish=8000:8000
199               --publish=8900:8900
200               --publish=9000:9000
201               --publish=9002:9002
202               --publish=25101:25101
203               --publish=8001:8001
204               --publish=8002:8002
205               --publish=45000-45020:45000-45020"
206     else
207         PUBLIC=""
208     fi
209
210     if [[ "$CONFIG" =~ demo$ ]] ; then
211         if test -d "$ARVBOX_DATA" ; then
212             echo "It looks like you already have a development container named $ARVBOX_CONTAINER."
213             echo "Set environment variable ARVBOX_CONTAINER to set a different name for your demo container"
214             exit 1
215         fi
216
217         if ! (docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q) ; then
218             docker create -v /var/lib/postgresql -v /var/lib/arvados --name $ARVBOX_CONTAINER-data arvados/arvbox-demo /bin/true
219         fi
220
221         docker run \
222                --detach \
223                --name=$ARVBOX_CONTAINER \
224                --privileged \
225                --volumes-from $ARVBOX_CONTAINER-data \
226                --label "org.arvados.arvbox_config=$CONFIG" \
227                $PUBLIC \
228                arvados/arvbox-demo$TAG
229         updateconf
230         wait_for_arvbox
231     else
232         mkdir -p "$PG_DATA" "$VAR_DATA" "$PASSENGER" "$GEMS" "$PIPCACHE" "$NPMCACHE" "$GOSTUFF" "$RLIBS"
233
234         if ! test -d "$ARVADOS_ROOT" ; then
235             git clone https://git.arvados.org/arvados.git "$ARVADOS_ROOT"
236         fi
237         if ! test -d "$COMPOSER_ROOT" ; then
238             git clone https://github.com/arvados/composer.git "$COMPOSER_ROOT"
239             git -C "$COMPOSER_ROOT" checkout arvados-fork
240             git -C "$COMPOSER_ROOT" pull
241         fi
242         if ! test -d "$WORKBENCH2_ROOT" ; then
243             git clone https://github.com/arvados/arvados-workbench2.git "$WORKBENCH2_ROOT"
244         fi
245
246         if [[ "$CONFIG" = test ]] ; then
247
248             mkdir -p $VAR_DATA/test
249
250             if test "$need_setup" = 1 ; then
251                 docker_run_dev \
252                        --detach \
253                        --name=$ARVBOX_CONTAINER \
254                        --privileged \
255                        "--env=SVDIR=/etc/test-service" \
256                        arvados/arvbox-dev$TAG
257
258                 docker exec -ti \
259                        $ARVBOX_CONTAINER \
260                        /usr/local/lib/arvbox/runsu.sh \
261                        /usr/local/lib/arvbox/waitforpostgres.sh
262
263                 docker exec -ti \
264                        $ARVBOX_CONTAINER \
265                        /usr/local/lib/arvbox/runsu.sh \
266                        /var/lib/arvbox/service/api/run-service --only-setup
267             fi
268
269             interactive=""
270             if [[ -z "$@" ]] ; then
271                 interactive=--interactive
272             fi
273
274             docker exec -ti \
275                    -e LINES=$(tput lines) \
276                    -e COLUMNS=$(tput cols) \
277                    -e TERM=$TERM \
278                    -e WORKSPACE=/usr/src/arvados \
279                    -e GEM_HOME=/var/lib/gems \
280                    -e CONFIGSRC=/var/lib/arvados/run_tests \
281                    $ARVBOX_CONTAINER \
282                    /usr/local/lib/arvbox/runsu.sh \
283                    /usr/src/arvados/build/run-tests.sh \
284                    --temp /var/lib/arvados/test \
285                    $interactive \
286                    "$@"
287         elif [[ "$CONFIG" = devenv ]] ; then
288             if [[ $need_setup = 1 ]] ; then
289                 docker_run_dev \
290                     --detach \
291                     --name=${ARVBOX_CONTAINER} \
292                     "--env=SVDIR=/etc/devenv-service" \
293                     "--volume=$HOME:$HOME:rw" \
294                     --volume=/tmp/.X11-unix:/tmp/.X11-unix:rw \
295                     arvados/arvbox-dev$TAG
296             fi
297             exec docker exec --interactive --tty \
298                  -e LINES=$(tput lines) \
299                  -e COLUMNS=$(tput cols) \
300                  -e TERM=$TERM \
301                  -e "ARVBOX_HOME=$HOME" \
302                  -e "DISPLAY=$DISPLAY" \
303                  --workdir=$PWD \
304                  ${ARVBOX_CONTAINER} \
305                  /usr/local/lib/arvbox/devenv.sh "$@"
306         elif [[ "$CONFIG" =~ dev$ ]] ; then
307             docker_run_dev \
308                    --detach \
309                    --name=$ARVBOX_CONTAINER \
310                    --privileged \
311                    $PUBLIC \
312                    arvados/arvbox-dev$TAG
313             updateconf
314             wait_for_arvbox
315             echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
316             echo "The Arvados testing root certificate is $VAR_DATA/root-cert.pem"
317         else
318             echo "Unknown configuration '$CONFIG'"
319         fi
320     fi
321 }
322
323 update() {
324     CONFIG=$1
325     TAG=$2
326
327     if test -n "$TAG"
328     then
329         if test $(echo $TAG | cut -c1-1) != '-' ; then
330             TAG=":$TAG"
331             shift
332         else
333             unset TAG
334         fi
335     fi
336
337     if echo "$CONFIG" | grep 'demo$' ; then
338         docker pull arvados/arvbox-demo$TAG
339     else
340         docker pull arvados/arvbox-dev$TAG
341     fi
342 }
343
344 stop() {
345     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
346         docker stop $ARVBOX_CONTAINER
347     fi
348
349     VOLUMES=--volumes=true
350     if docker ps -a --filter "status=created" | grep -E "$ARVBOX_CONTAINER$" -q ; then
351         docker rm $VOLUMES $ARVBOX_CONTAINER
352     fi
353     if docker ps -a --filter "status=exited" | grep -E "$ARVBOX_CONTAINER$" -q ; then
354         docker rm $VOLUMES $ARVBOX_CONTAINER
355     fi
356 }
357
358 build() {
359     if ! test -f "$ARVBOX_DOCKER/Dockerfile.base" ;  then
360         echo "Could not find Dockerfile (expected it at $ARVBOX_DOCKER/Dockerfile.base)"
361         exit 1
362     fi
363     if docker --version |grep " 1\.[0-9]\." ; then
364         # Docker version prior 1.10 require -f flag
365         # -f flag removed in Docker 1.12
366         FORCE=-f
367     fi
368     GITHEAD=$(cd $ARVBOX_DOCKER && git log --format=%H -n1 HEAD)
369     docker build --build-arg=arvados_version=$GITHEAD $NO_CACHE -t arvados/arvbox-base:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.base" "$ARVBOX_DOCKER"
370     docker tag $FORCE arvados/arvbox-base:$GITHEAD arvados/arvbox-base:latest
371     if test "$1" = localdemo -o "$1" = publicdemo ; then
372         docker build $NO_CACHE -t arvados/arvbox-demo:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.demo" "$ARVBOX_DOCKER"
373         docker tag $FORCE arvados/arvbox-demo:$GITHEAD arvados/arvbox-demo:latest
374     else
375         docker build $NO_CACHE -t arvados/arvbox-dev:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.dev" "$ARVBOX_DOCKER"
376         docker tag $FORCE arvados/arvbox-dev:$GITHEAD arvados/arvbox-dev:latest
377     fi
378 }
379
380 check() {
381     case "$1" in
382         localdemo|publicdemo|dev|publicdev|test|devenv)
383             true
384             ;;
385         *)
386             echo "Argument to $subcmd must be one of localdemo, publicdemo, dev, publicdev, test, devenv"
387             exit 1
388         ;;
389     esac
390 }
391
392 subcmd="$1"
393 if test -n "$subcmd" ; then
394     shift
395 fi
396 case "$subcmd" in
397     build)
398         check $@
399         build $@
400         ;;
401
402     rebuild)
403         check $@
404         NO_CACHE=--no-cache build $@
405         ;;
406
407     start|run)
408         check $@
409         run $@
410         ;;
411
412     sh*)
413         exec docker exec --interactive --tty \
414                -e LINES=$(tput lines) \
415                -e COLUMNS=$(tput cols) \
416                -e TERM=$TERM \
417                -e GEM_HOME=/var/lib/gems \
418                $ARVBOX_CONTAINER /bin/bash
419         ;;
420
421     ash*)
422         exec docker exec --interactive --tty \
423                -e LINES=$(tput lines) \
424                -e COLUMNS=$(tput cols) \
425                -e TERM=$TERM \
426                -e GEM_HOME=/var/lib/gems \
427                -u arvbox \
428                -w /usr/src/arvados \
429                $ARVBOX_CONTAINER /bin/bash --login
430         ;;
431
432     pipe)
433         exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash -
434         ;;
435
436     stop)
437         stop
438         ;;
439
440     restart)
441         check $@
442         stop
443         run $@
444         ;;
445
446     reboot)
447         check $@
448         stop
449         build $@
450         run $@
451         ;;
452
453     update)
454         check $@
455         stop
456         update $@
457         run $@
458         ;;
459
460     ip)
461         getip
462         ;;
463
464     host)
465         gethost
466         ;;
467
468     open)
469         exec xdg-open http://$(gethost)
470         ;;
471
472     status)
473         echo "Container: $ARVBOX_CONTAINER"
474         if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
475             echo "Cluster id: $(getclusterid)"
476             echo "Status: running"
477             echo "Container IP: $(getip)"
478             echo "Published host: $(gethost)"
479         else
480             echo "Status: not running"
481         fi
482         if test -d "$ARVBOX_DATA" ; then
483             echo "Data: $ARVBOX_DATA"
484         elif docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q ; then
485             echo "Data: $ARVBOX_CONTAINER-data"
486         else
487             echo "Data: none"
488         fi
489         ;;
490
491     reset|destroy)
492         stop
493         if test -d "$ARVBOX_DATA" ; then
494             if test "$subcmd" = destroy ; then
495                 if test "$1" != -f ; then
496                     echo "WARNING!  This will delete your entire arvbox ($ARVBOX_DATA)."
497                     echo "Use destroy -f if you really mean it."
498                     exit 1
499                 fi
500                 set -x
501                 chmod -R u+w "$ARVBOX_DATA"
502                 rm -rf "$ARVBOX_DATA"
503             else
504                 if test "$1" != -f ; then
505                     echo "WARNING!  This will delete your arvbox data ($ARVBOX_DATA)."
506                     echo "Code and downloaded packages will be preserved."
507                     echo "Use reset -f if you really mean it."
508                     exit 1
509                 fi
510                 set -x
511                 rm -rf "$ARVBOX_DATA/postgres"
512                 rm -rf "$ARVBOX_DATA/var"
513             fi
514         else
515             if test "$1" != -f ; then
516                 echo "WARNING!  This will delete your data container $ARVBOX_CONTAINER-data.  Use -f if you really mean it."
517                 exit 1
518             fi
519             set -x
520             docker rm "$ARVBOX_CONTAINER-data"
521         fi
522         ;;
523
524     log)
525         if test -n "$1" ; then
526             exec docker exec -ti -e LINES=$(tput lines) -e COLUMNS=$(tput cols) -e TERM=$TERM $ARVBOX_CONTAINER less --follow-name -R +GF "/etc/service/$1/log/main/current"
527         else
528             exec docker exec -ti $ARVBOX_CONTAINER tail $(docker exec -ti $ARVBOX_CONTAINER find -L /etc -path '/etc/service/*/log/main/current' -printf " %p")
529         fi
530         ;;
531
532     cat)
533         if test -n "$1" ; then
534             exec docker exec $ARVBOX_CONTAINER cat "$@"
535         else
536             echo "Usage: $0 $subcmd <files>"
537         fi
538         ;;
539
540     ls)
541         exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM ls "$@"
542         ;;
543
544     sv)
545         if test -n "$1" -a -n "$2" ; then
546             exec docker exec $ARVBOX_CONTAINER sv "$@"
547         else
548             echo "Usage: $0 $subcmd <start|stop|restart> <service>"
549             echo "Available services:"
550             exec docker execa $ARVBOX_CONTAINER ls /etc/service
551         fi
552         ;;
553
554     clone)
555         if test -n "$2" ; then
556             mkdir -p "$ARVBOX_BASE/$2"
557             cp -a "$ARVBOX_BASE/$1/passenger" \
558                "$ARVBOX_BASE/$1/gems" \
559                "$ARVBOX_BASE/$1/pip" \
560                "$ARVBOX_BASE/$1/npm" \
561                "$ARVBOX_BASE/$1/gopath" \
562                "$ARVBOX_BASE/$1/Rlibs" \
563                "$ARVBOX_BASE/$1/arvados" \
564                "$ARVBOX_BASE/$1/composer" \
565                "$ARVBOX_BASE/$1/workbench2" \
566                "$ARVBOX_BASE/$2"
567             echo "Created new arvbox $2"
568             echo "export ARVBOX_CONTAINER=$2"
569         else
570             echo "clone <from> <to>   clone an arvbox"
571             echo "available arvboxes: $(ls $ARVBOX_BASE)"
572         fi
573         ;;
574
575     root-cert)
576         CERT=$PWD/${ARVBOX_CONTAINER}-root-cert.crt
577         if test -n "$1" ; then
578             CERT="$1"
579         fi
580         docker exec $ARVBOX_CONTAINER cat /var/lib/arvados/root-cert.pem > "$CERT"
581         echo "Certificate copied to $CERT"
582         ;;
583
584     psql)
585         exec docker exec -ti $ARVBOX_CONTAINER bash -c 'PGPASSWORD=$(cat /var/lib/arvados/api_database_pw) exec psql --dbname=arvados_development --host=localhost --username=arvados'
586         ;;
587
588     checkpoint)
589         exec docker exec -ti $ARVBOX_CONTAINER bash -c 'PGPASSWORD=$(cat /var/lib/arvados/api_database_pw) exec pg_dump --host=localhost --username=arvados --clean arvados_development > /var/lib/arvados/checkpoint.sql'
590         ;;
591
592     restore)
593         exec docker exec -ti $ARVBOX_CONTAINER bash -c 'PGPASSWORD=$(cat /var/lib/arvados/api_database_pw) exec psql --dbname=arvados_development --host=localhost --username=arvados --quiet --file=/var/lib/arvados/checkpoint.sql'
594         ;;
595
596     hotreset)
597         exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash - <<EOF
598 sv stop api
599 sv stop controller
600 sv stop websockets
601 sv stop keepstore0
602 sv stop keepstore1
603 sv stop keepproxy
604 cd /usr/src/arvados/services/api
605 export DISABLE_DATABASE_ENVIRONMENT_CHECK=1
606 export RAILS_ENV=development
607 bundle exec rake db:drop
608 rm /var/lib/arvados/api_database_setup
609 rm /var/lib/arvados/superuser_token
610 rm /var/lib/arvados/keep0-uuid
611 rm /var/lib/arvados/keep1-uuid
612 rm /var/lib/arvados/keepproxy-uuid
613 sv start api
614 sv start controller
615 sv start websockets
616 sv restart keepstore0
617 sv restart keepstore1
618 sv restart keepproxy
619 EOF
620         ;;
621
622     *)
623         echo "Arvados-in-a-box             https://doc.arvados.org/install/arvbox.html"
624         echo
625         echo "start|run <config> [tag]   start $ARVBOX_CONTAINER container"
626         echo "stop               stop arvbox container"
627         echo "restart <config>   stop, then run again"
628         echo "status             print some information about current arvbox"
629         echo "ip                 print arvbox docker container ip address"
630         echo "host               print arvbox published host"
631         echo "shell              enter shell as root"
632         echo "ashell             enter shell as 'arvbox'"
633         echo "psql               enter postgres console"
634         echo "open               open arvbox workbench in a web browser"
635         echo "root-cert          get copy of root certificate"
636         echo "update  <config>   stop, pull latest image, run"
637         echo "build   <config>   build arvbox Docker image"
638         echo "reboot  <config>   stop, build arvbox Docker image, run"
639         echo "rebuild <config>   build arvbox Docker image, no layer cache"
640         echo "checkpoint         create database backup"
641         echo "restore            restore checkpoint"
642         echo "hotreset           reset database and restart API without restarting container"
643         echo "reset              delete arvbox arvados data (be careful!)"
644         echo "destroy            delete all arvbox code and data (be careful!)"
645         echo "log <service>      tail log of specified service"
646         echo "ls <options>       list directories inside arvbox"
647         echo "cat <files>        get contents of files inside arvbox"
648         echo "pipe               run a bash script piped in from stdin"
649         echo "sv <start|stop|restart> <service> "
650         echo "                   change state of service inside arvbox"
651         echo "clone <from> <to>  clone dev arvbox"
652         ;;
653 esac