Merge remote-tracking branch 'origin/master' into 14645-fuse-operations-reporting
[arvados.git] / tools / arvbox / bin / arvbox
1 #!/bin/sh
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 $ARVBOX_CONTAINER | grep \"IPAddress\" | head -n1 | tr -d ' ":,\n' | cut -c10-
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 echo $line | grep "ok: down: ready:" >/dev/null ; then
107             kill $LOGPID
108         else
109             echo $line
110         fi
111     done < $FF
112     rm $FF
113     echo
114     if test -n "$localip" ; then
115         echo "export ARVADOS_API_HOST=$localip:8000"
116     else
117         echo "export ARVADOS_API_HOST=$(gethost):8000"
118     fi
119 }
120
121 docker_run_dev() {
122     docker run \
123            "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
124            "--volume=$SSO_ROOT:/usr/src/sso: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            "$@"
136 }
137
138 run() {
139     CONFIG=$1
140     TAG=$2
141
142     shift
143
144     need_setup=1
145
146     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
147         if test "$CONFIG" = test ; then
148             need_setup=0
149         else
150             echo "Container $ARVBOX_CONTAINER is already running"
151             exit 0
152         fi
153     fi
154
155     if test $need_setup = 1 ; then
156         if docker ps -a | grep -E "$ARVBOX_CONTAINER$" -q ; then
157             echo "Container $ARVBOX_CONTAINER already exists but is not running; use restart or reboot"
158             exit 1
159         fi
160     fi
161
162     if test -n "$TAG"
163     then
164         if test $(echo $TAG | cut -c1-1) != '-' ; then
165             TAG=":$TAG"
166             shift
167         else
168             unset TAG
169         fi
170     fi
171
172     if echo "$CONFIG" | grep '^public' ; then
173         if test -n "$ARVBOX_PUBLISH_IP" ; then
174             localip=$ARVBOX_PUBLISH_IP
175         else
176             defaultdev=$(/sbin/ip route|awk '/default/ { print $5 }')
177             localip=$(ip addr show $defaultdev | grep 'inet ' | sed 's/ *inet \(.*\)\/.*/\1/')
178         fi
179         iptemp=$(tempfile)
180         echo $localip > $iptemp
181         chmod og+r $iptemp
182         PUBLIC="--volume=$iptemp:/var/run/localip_override
183               --publish=443:443
184               --publish=3001:3001
185               --publish=8000:8000
186               --publish=8900:8900
187               --publish=9001:9001
188               --publish=9002:9002
189               --publish=25100:25100
190               --publish=25107:25107
191               --publish=25108:25108
192               --publish=8001:8001
193               --publish=8002:8002"
194     else
195         PUBLIC=""
196     fi
197
198     if echo "$CONFIG" | grep 'demo$' ; then
199         if test -d "$ARVBOX_DATA" ; then
200             echo "It looks like you already have a development container named $ARVBOX_CONTAINER."
201             echo "Set ARVBOX_CONTAINER to set a different name for your demo container"
202             exit 1
203         fi
204
205         if ! (docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q) ; then
206             docker create -v /var/lib/postgresql -v /var/lib/arvados --name $ARVBOX_CONTAINER-data arvados/arvbox-demo /bin/true
207         fi
208
209         docker run \
210                --detach \
211                --name=$ARVBOX_CONTAINER \
212                --privileged \
213                --volumes-from $ARVBOX_CONTAINER-data \
214                $PUBLIC \
215                arvados/arvbox-demo$TAG
216         updateconf
217         wait_for_arvbox
218     else
219         mkdir -p "$PG_DATA" "$VAR_DATA" "$PASSENGER" "$GEMS" "$PIPCACHE" "$NPMCACHE" "$GOSTUFF" "$RLIBS"
220
221
222         if ! test -d "$ARVADOS_ROOT" ; then
223             git clone https://github.com/curoverse/arvados.git "$ARVADOS_ROOT"
224         fi
225         if ! test -d "$SSO_ROOT" ; then
226             git clone https://github.com/curoverse/sso-devise-omniauth-provider.git "$SSO_ROOT"
227         fi
228         if ! test -d "$COMPOSER_ROOT" ; then
229             git clone https://github.com/curoverse/composer.git "$COMPOSER_ROOT"
230         fi
231         if ! test -d "$WORKBENCH2_ROOT" ; then
232             git clone https://github.com/curoverse/arvados-workbench2.git "$WORKBENCH2_ROOT"
233         fi
234
235         if test "$CONFIG" = test ; then
236
237             mkdir -p $VAR_DATA/test
238
239             if test "$need_setup" = 1 ; then
240                 docker_run_dev \
241                        --detach \
242                        --name=$ARVBOX_CONTAINER \
243                        --privileged \
244                        "--env=SVDIR=/etc/test-service" \
245                        arvados/arvbox-dev$TAG
246
247                 docker exec -ti \
248                        $ARVBOX_CONTAINER \
249                        /usr/local/lib/arvbox/runsu.sh \
250                        /usr/local/lib/arvbox/waitforpostgres.sh
251
252                 docker exec -ti \
253                        $ARVBOX_CONTAINER \
254                        /usr/local/lib/arvbox/runsu.sh \
255                        /var/lib/arvbox/service/sso/run-service --only-setup
256
257                 docker exec -ti \
258                        $ARVBOX_CONTAINER \
259                        /usr/local/lib/arvbox/runsu.sh \
260                        /var/lib/arvbox/service/api/run-service --only-setup
261             fi
262
263             docker exec -ti \
264                    $ARVBOX_CONTAINER \
265                    /usr/local/lib/arvbox/runsu.sh \
266                    /usr/src/arvados/build/run-tests.sh \
267                    --temp /var/lib/arvados/test \
268                    WORKSPACE=/usr/src/arvados \
269                    GEM_HOME=/var/lib/gems \
270                    "$@"
271         elif echo "$CONFIG" | grep 'dev$' ; then
272             docker_run_dev \
273                    --detach \
274                    --name=$ARVBOX_CONTAINER \
275                    --privileged \
276                    $PUBLIC \
277                    arvados/arvbox-dev$TAG
278             updateconf
279             wait_for_arvbox
280             echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
281             echo "The Arvados testing root certificate is $VAR_DATA/root-cert.pem"
282         else
283             echo "Unknown configuration '$CONFIG'"
284         fi
285     fi
286 }
287
288 stop() {
289     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
290         docker stop $ARVBOX_CONTAINER
291     fi
292
293     VOLUMES=--volumes=true
294     if docker ps -a --filter "status=created" | grep -E "$ARVBOX_CONTAINER$" -q ; then
295         docker rm $VOLUMES $ARVBOX_CONTAINER
296     fi
297     if docker ps -a --filter "status=exited" | grep -E "$ARVBOX_CONTAINER$" -q ; then
298         docker rm $VOLUMES $ARVBOX_CONTAINER
299     fi
300 }
301
302 build() {
303     if ! test -f "$ARVBOX_DOCKER/Dockerfile.base" ;  then
304         echo "Could not find Dockerfile (expected it at $ARVBOX_DOCKER/Dockerfile.base)"
305         exit 1
306     fi
307     if docker --version |grep " 1\.[0-9]\." ; then
308         # Docker version prior 1.10 require -f flag
309         # -f flag removed in Docker 1.12
310         FORCE=-f
311     fi
312     GITHEAD=$(cd $ARVBOX_DOCKER && git log --format=%H -n1 HEAD)
313     docker build --build-arg=arvados_version=$GITHEAD $NO_CACHE -t arvados/arvbox-base:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.base" "$ARVBOX_DOCKER"
314     docker tag $FORCE arvados/arvbox-base:$GITHEAD arvados/arvbox-base:latest
315     if test "$1" = localdemo -o "$1" = publicdemo ; then
316         docker build $NO_CACHE -t arvados/arvbox-demo:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.demo" "$ARVBOX_DOCKER"
317         docker tag $FORCE arvados/arvbox-demo:$GITHEAD arvados/arvbox-demo:latest
318     else
319         docker build $NO_CACHE -t arvados/arvbox-dev:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.dev" "$ARVBOX_DOCKER"
320         docker tag $FORCE arvados/arvbox-dev:$GITHEAD arvados/arvbox-dev:latest
321     fi
322 }
323
324 check() {
325     case "$1" in
326         localdemo|publicdemo|dev|publicdev|test)
327             true
328             ;;
329         *)
330             echo "Argument to $subcmd must be one of localdemo, publicdemo, dev, publicdev, test"
331             exit 1
332         ;;
333     esac
334 }
335
336 subcmd="$1"
337 if test -n "$subcmd" ; then
338     shift
339 fi
340 case "$subcmd" in
341     build)
342         check $@
343         build $@
344         ;;
345
346     rebuild)
347         check $@
348         NO_CACHE=--no-cache build $@
349         ;;
350
351     start|run)
352         check $@
353         run $@
354         ;;
355
356     sh*)
357         exec docker exec -ti \
358                -e LINES=$(tput lines) \
359                -e COLUMNS=$(tput cols) \
360                -e TERM=$TERM \
361                -e GEM_HOME=/var/lib/gems \
362                $ARVBOX_CONTAINER /bin/bash
363         ;;
364
365     pipe)
366         exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash -
367         ;;
368
369     stop)
370         stop
371         ;;
372
373     restart)
374         check $@
375         stop
376         run $@
377         ;;
378
379     reboot)
380         check $@
381         stop
382         build $@
383         run $@
384         ;;
385
386     ip)
387         getip
388         ;;
389
390     host)
391         gethost
392         ;;
393
394     open)
395         exec xdg-open http://$(gethost)
396         ;;
397
398     status)
399         echo "Container: $ARVBOX_CONTAINER"
400         if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
401             echo "Cluster id: $(getclusterid)"
402             echo "Status: running"
403             echo "Container IP: $(getip)"
404             echo "Published host: $(gethost)"
405         else
406             echo "Status: not running"
407         fi
408         if test -d "$ARVBOX_DATA" ; then
409             echo "Data: $ARVBOX_DATA"
410         elif docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q ; then
411             echo "Data: $ARVBOX_CONTAINER-data"
412         else
413             echo "Data: none"
414         fi
415         ;;
416
417     reset|destroy)
418         stop
419         if test -d "$ARVBOX_DATA" ; then
420             if test "$subcmd" = destroy ; then
421                 if test "$1" != -f ; then
422                     echo "WARNING!  This will delete your entire arvbox ($ARVBOX_DATA)."
423                     echo "Use destroy -f if you really mean it."
424                     exit 1
425                 fi
426                 set -x
427                 rm -rf "$ARVBOX_DATA"
428             else
429                 if test "$1" != -f ; then
430                     echo "WARNING!  This will delete your arvbox data ($ARVBOX_DATA)."
431                     echo "Code and downloaded packages will be preserved."
432                     echo "Use reset -f if you really mean it."
433                     exit 1
434                 fi
435                 set -x
436                 rm -rf "$ARVBOX_DATA/postgres"
437                 rm -rf "$ARVBOX_DATA/var"
438             fi
439         else
440             if test "$1" != -f ; then
441                 echo "WARNING!  This will delete your data container $ARVBOX_CONTAINER-data.  Use -f if you really mean it."
442                 exit 1
443             fi
444             set -x
445             docker rm "$ARVBOX_CONTAINER-data"
446         fi
447         ;;
448
449     log)
450         if test -n "$1" ; then
451             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"
452         else
453             exec docker exec -ti $ARVBOX_CONTAINER tail $(docker exec -ti $ARVBOX_CONTAINER find -L /etc -path '/etc/service/*/log/main/current' -printf " %p")
454         fi
455         ;;
456
457     cat)
458         if test -n "$1" ; then
459             exec docker exec $ARVBOX_CONTAINER cat "$@"
460         else
461             echo "Usage: $0 $subcmd <files>"
462         fi
463         ;;
464
465     ls)
466         exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM ls "$@"
467         ;;
468
469     sv)
470         if test -n "$1" -a -n "$2" ; then
471             exec docker exec $ARVBOX_CONTAINER sv "$@"
472         else
473             echo "Usage: $0 $subcmd <start|stop|restart> <service>"
474             echo "Available services:"
475             exec docker execa $ARVBOX_CONTAINER ls /etc/service
476         fi
477         ;;
478
479     clone)
480         if test -n "$2" ; then
481             cp -r "$ARVBOX_BASE/$1" "$ARVBOX_BASE/$2"
482             echo "Created new arvbox $2"
483             echo "export ARVBOX_CONTAINER=$2"
484         else
485             echo "clone <from> <to>   clone an arvbox"
486             echo "available arvboxes: $(ls $ARVBOX_BASE)"
487         fi
488         ;;
489
490     install-root-cert)
491         set -x
492         sudo cp $VAR_DATA/root-cert.pem /usr/local/share/ca-certificates/${ARVBOX_CONTAINER}-testing-cert.crt
493         sudo update-ca-certificates
494         ;;
495
496     devenv)
497         set -x
498         if docker ps -a --filter "status=exited" | grep -E "${ARVBOX_CONTAINER}-devenv$" -q ; then
499             docker start ${ARVBOX_CONTAINER}-devenv
500         elif ! (docker ps -a --filter "status=running" | grep -E "${ARVBOX_CONTAINER}-devenv$" -q) ; then
501             docker_run_dev \
502                  --detach \
503                  --name=${ARVBOX_CONTAINER}-devenv \
504                  "--env=SVDIR=/etc/devenv-service" \
505                  "--volume=$HOME:$HOME:rw" \
506                  --volume=/tmp/.X11-unix:/tmp/.X11-unix:rw \
507                  arvados/arvbox-dev$TAG
508         fi
509
510         exec docker exec --interactive --tty \
511              -e LINES=$(tput lines) \
512              -e COLUMNS=$(tput cols) \
513              -e TERM=$TERM \
514              -e "ARVBOX_HOME=$HOME" \
515              -e "DISPLAY=$DISPLAY" \
516              --workdir=$PWD \
517              ${ARVBOX_CONTAINER}-devenv \
518              /usr/local/lib/arvbox/devenv.sh "$@"
519         ;;
520
521     devenv-stop)
522         docker stop ${ARVBOX_CONTAINER}-devenv
523         ;;
524
525     devenv-reset)
526         docker stop ${ARVBOX_CONTAINER}-devenv
527         docker rm ${ARVBOX_CONTAINER}-devenv
528         ;;
529
530     *)
531         echo "Arvados-in-a-box                      http://arvados.org"
532         echo
533         echo "build   <config>      build arvbox Docker image"
534         echo "rebuild <config>      build arvbox Docker image, no layer cache"
535         echo "start|run <config> [tag]  start $ARVBOX_CONTAINER container"
536         echo "open       open arvbox workbench in a web browser"
537         echo "shell      enter arvbox shell"
538         echo "ip         print arvbox docker container ip address"
539         echo "host       print arvbox published host"
540         echo "status     print some information about current arvbox"
541         echo "stop       stop arvbox container"
542         echo "restart <config>  stop, then run again"
543         echo "reboot  <config>  stop, build arvbox Docker image, run"
544         echo "reset      delete arvbox arvados data (be careful!)"
545         echo "destroy    delete all arvbox code and data (be careful!)"
546         echo "log <service> tail log of specified service"
547         echo "ls <options>  list directories inside arvbox"
548         echo "cat <files>   get contents of files inside arvbox"
549         echo "pipe       run a bash script piped in from stdin"
550         echo "sv <start|stop|restart> <service> change state of service inside arvbox"
551         echo "clone <from> <to>   clone an arvbox"
552         ;;
553 esac