arvbox creates /var/lib/arvados/run_tests/config.yml
[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             interactive=""
279             if [[ -z "$@" ]] ; then
280                 interactive=--interactive
281             fi
282
283             docker exec -ti \
284                    -e LINES=$(tput lines) \
285                    -e COLUMNS=$(tput cols) \
286                    -e TERM=$TERM \
287                    -e WORKSPACE=/usr/src/arvados \
288                    -e GEM_HOME=/var/lib/gems \
289                    -e CONFIGSRC=/var/lib/arvados/run_tests \
290                    $ARVBOX_CONTAINER \
291                    /usr/local/lib/arvbox/runsu.sh \
292                    /usr/src/arvados/build/run-tests.sh \
293                    --temp /var/lib/arvados/test \
294                    $interactive \
295                    "$@"
296         elif [[ "$CONFIG" = devenv ]] ; then
297             if [[ $need_setup = 1 ]] ; then
298                 docker_run_dev \
299                     --detach \
300                     --name=${ARVBOX_CONTAINER} \
301                     "--env=SVDIR=/etc/devenv-service" \
302                     "--volume=$HOME:$HOME:rw" \
303                     --volume=/tmp/.X11-unix:/tmp/.X11-unix:rw \
304                     arvados/arvbox-dev$TAG
305             fi
306             exec docker exec --interactive --tty \
307                  -e LINES=$(tput lines) \
308                  -e COLUMNS=$(tput cols) \
309                  -e TERM=$TERM \
310                  -e "ARVBOX_HOME=$HOME" \
311                  -e "DISPLAY=$DISPLAY" \
312                  --workdir=$PWD \
313                  ${ARVBOX_CONTAINER} \
314                  /usr/local/lib/arvbox/devenv.sh "$@"
315         elif [[ "$CONFIG" =~ dev$ ]] ; then
316             docker_run_dev \
317                    --detach \
318                    --name=$ARVBOX_CONTAINER \
319                    --privileged \
320                    $PUBLIC \
321                    arvados/arvbox-dev$TAG
322             updateconf
323             wait_for_arvbox
324             echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
325             echo "The Arvados testing root certificate is $VAR_DATA/root-cert.pem"
326         else
327             echo "Unknown configuration '$CONFIG'"
328         fi
329     fi
330 }
331
332 update() {
333     CONFIG=$1
334     TAG=$2
335
336     if test -n "$TAG"
337     then
338         if test $(echo $TAG | cut -c1-1) != '-' ; then
339             TAG=":$TAG"
340             shift
341         else
342             unset TAG
343         fi
344     fi
345
346     if echo "$CONFIG" | grep 'demo$' ; then
347         docker pull arvados/arvbox-demo$TAG
348     else
349         docker pull arvados/arvbox-dev$TAG
350     fi
351 }
352
353 stop() {
354     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
355         docker stop $ARVBOX_CONTAINER
356     fi
357
358     VOLUMES=--volumes=true
359     if docker ps -a --filter "status=created" | grep -E "$ARVBOX_CONTAINER$" -q ; then
360         docker rm $VOLUMES $ARVBOX_CONTAINER
361     fi
362     if docker ps -a --filter "status=exited" | grep -E "$ARVBOX_CONTAINER$" -q ; then
363         docker rm $VOLUMES $ARVBOX_CONTAINER
364     fi
365 }
366
367 build() {
368     if ! test -f "$ARVBOX_DOCKER/Dockerfile.base" ;  then
369         echo "Could not find Dockerfile (expected it at $ARVBOX_DOCKER/Dockerfile.base)"
370         exit 1
371     fi
372     if docker --version |grep " 1\.[0-9]\." ; then
373         # Docker version prior 1.10 require -f flag
374         # -f flag removed in Docker 1.12
375         FORCE=-f
376     fi
377     GITHEAD=$(cd $ARVBOX_DOCKER && git log --format=%H -n1 HEAD)
378     docker build --build-arg=arvados_version=$GITHEAD $NO_CACHE -t arvados/arvbox-base:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.base" "$ARVBOX_DOCKER"
379     docker tag $FORCE arvados/arvbox-base:$GITHEAD arvados/arvbox-base:latest
380     if test "$1" = localdemo -o "$1" = publicdemo ; then
381         docker build $NO_CACHE -t arvados/arvbox-demo:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.demo" "$ARVBOX_DOCKER"
382         docker tag $FORCE arvados/arvbox-demo:$GITHEAD arvados/arvbox-demo:latest
383     else
384         docker build $NO_CACHE -t arvados/arvbox-dev:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.dev" "$ARVBOX_DOCKER"
385         docker tag $FORCE arvados/arvbox-dev:$GITHEAD arvados/arvbox-dev:latest
386     fi
387 }
388
389 check() {
390     case "$1" in
391         localdemo|publicdemo|dev|publicdev|test|devenv)
392             true
393             ;;
394         *)
395             echo "Argument to $subcmd must be one of localdemo, publicdemo, dev, publicdev, test, devenv"
396             exit 1
397         ;;
398     esac
399 }
400
401 subcmd="$1"
402 if test -n "$subcmd" ; then
403     shift
404 fi
405 case "$subcmd" in
406     build)
407         check $@
408         build $@
409         ;;
410
411     rebuild)
412         check $@
413         NO_CACHE=--no-cache build $@
414         ;;
415
416     start|run)
417         check $@
418         run $@
419         ;;
420
421     sh*)
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                $ARVBOX_CONTAINER /bin/bash
428         ;;
429
430     ash*)
431         exec docker exec --interactive --tty \
432                -e LINES=$(tput lines) \
433                -e COLUMNS=$(tput cols) \
434                -e TERM=$TERM \
435                -e GEM_HOME=/var/lib/gems \
436                -u arvbox \
437                -w /usr/src/arvados \
438                $ARVBOX_CONTAINER /bin/bash --login
439         ;;
440
441     pipe)
442         exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash -
443         ;;
444
445     stop)
446         stop
447         ;;
448
449     restart)
450         check $@
451         stop
452         run $@
453         ;;
454
455     reboot)
456         check $@
457         stop
458         build $@
459         run $@
460         ;;
461
462     update)
463         check $@
464         stop
465         update $@
466         run $@
467         ;;
468
469     ip)
470         getip
471         ;;
472
473     host)
474         gethost
475         ;;
476
477     open)
478         exec xdg-open http://$(gethost)
479         ;;
480
481     status)
482         echo "Container: $ARVBOX_CONTAINER"
483         if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
484             echo "Cluster id: $(getclusterid)"
485             echo "Status: running"
486             echo "Container IP: $(getip)"
487             echo "Published host: $(gethost)"
488         else
489             echo "Status: not running"
490         fi
491         if test -d "$ARVBOX_DATA" ; then
492             echo "Data: $ARVBOX_DATA"
493         elif docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q ; then
494             echo "Data: $ARVBOX_CONTAINER-data"
495         else
496             echo "Data: none"
497         fi
498         ;;
499
500     reset|destroy)
501         stop
502         if test -d "$ARVBOX_DATA" ; then
503             if test "$subcmd" = destroy ; then
504                 if test "$1" != -f ; then
505                     echo "WARNING!  This will delete your entire arvbox ($ARVBOX_DATA)."
506                     echo "Use destroy -f if you really mean it."
507                     exit 1
508                 fi
509                 set -x
510                 rm -rf "$ARVBOX_DATA"
511             else
512                 if test "$1" != -f ; then
513                     echo "WARNING!  This will delete your arvbox data ($ARVBOX_DATA)."
514                     echo "Code and downloaded packages will be preserved."
515                     echo "Use reset -f if you really mean it."
516                     exit 1
517                 fi
518                 set -x
519                 rm -rf "$ARVBOX_DATA/postgres"
520                 rm -rf "$ARVBOX_DATA/var"
521             fi
522         else
523             if test "$1" != -f ; then
524                 echo "WARNING!  This will delete your data container $ARVBOX_CONTAINER-data.  Use -f if you really mean it."
525                 exit 1
526             fi
527             set -x
528             docker rm "$ARVBOX_CONTAINER-data"
529         fi
530         ;;
531
532     log)
533         if test -n "$1" ; then
534             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"
535         else
536             exec docker exec -ti $ARVBOX_CONTAINER tail $(docker exec -ti $ARVBOX_CONTAINER find -L /etc -path '/etc/service/*/log/main/current' -printf " %p")
537         fi
538         ;;
539
540     cat)
541         if test -n "$1" ; then
542             exec docker exec $ARVBOX_CONTAINER cat "$@"
543         else
544             echo "Usage: $0 $subcmd <files>"
545         fi
546         ;;
547
548     ls)
549         exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM ls "$@"
550         ;;
551
552     sv)
553         if test -n "$1" -a -n "$2" ; then
554             exec docker exec $ARVBOX_CONTAINER sv "$@"
555         else
556             echo "Usage: $0 $subcmd <start|stop|restart> <service>"
557             echo "Available services:"
558             exec docker execa $ARVBOX_CONTAINER ls /etc/service
559         fi
560         ;;
561
562     clone)
563         if test -n "$2" ; then
564             mkdir -p "$ARVBOX_BASE/$2"
565             cp -a "$ARVBOX_BASE/$1/passenger" \
566                "$ARVBOX_BASE/$1/gems" \
567                "$ARVBOX_BASE/$1/pip" \
568                "$ARVBOX_BASE/$1/npm" \
569                "$ARVBOX_BASE/$1/gopath" \
570                "$ARVBOX_BASE/$1/Rlibs" \
571                "$ARVBOX_BASE/$1/arvados" \
572                "$ARVBOX_BASE/$1/sso-devise-omniauth-provider" \
573                "$ARVBOX_BASE/$1/composer" \
574                "$ARVBOX_BASE/$1/workbench2" \
575                "$ARVBOX_BASE/$2"
576             echo "Created new arvbox $2"
577             echo "export ARVBOX_CONTAINER=$2"
578         else
579             echo "clone <from> <to>   clone an arvbox"
580             echo "available arvboxes: $(ls $ARVBOX_BASE)"
581         fi
582         ;;
583
584     root-cert)
585         CERT=$PWD/${ARVBOX_CONTAINER}-root-cert.crt
586         if test -n "$1" ; then
587             CERT="$1"
588         fi
589         docker exec $ARVBOX_CONTAINER cat /var/lib/arvados/root-cert.pem > "$CERT"
590         echo "Certificate copied to $CERT"
591         ;;
592
593     psql)
594         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'
595         ;;
596
597     *)
598         echo "Arvados-in-a-box             https://doc.arvados.org/install/arvbox.html"
599         echo
600         echo "start|run <config> [tag]   start $ARVBOX_CONTAINER container"
601         echo "stop               stop arvbox container"
602         echo "restart <config>   stop, then run again"
603         echo "status             print some information about current arvbox"
604         echo "ip                 print arvbox docker container ip address"
605         echo "host               print arvbox published host"
606         echo "shell              enter shell as root"
607         echo "ashell             enter shell as 'arvbox'"
608         echo "psql               enter postgres console"
609         echo "open               open arvbox workbench in a web browser"
610         echo "root-cert          get copy of root certificate"
611         echo "update  <config>   stop, pull latest image, run"
612         echo "build   <config>   build arvbox Docker image"
613         echo "reboot  <config>   stop, build arvbox Docker image, run"
614         echo "rebuild <config>   build arvbox Docker image, no layer cache"
615         echo "reset              delete arvbox arvados data (be careful!)"
616         echo "destroy            delete all arvbox code and data (be careful!)"
617         echo "log <service>      tail log of specified service"
618         echo "ls <options>       list directories inside arvbox"
619         echo "cat <files>        get contents of files inside arvbox"
620         echo "pipe               run a bash script piped in from stdin"
621         echo "sv <start|stop|restart> <service> "
622         echo "                   change state of service inside arvbox"
623         echo "clone <from> <to>  clone dev arvbox"
624         ;;
625 esac