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