12199: Merge branch 'master' into 12199-dispatch-to-node-type
[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 PG_DATA="$ARVBOX_DATA/postgres"
54 VAR_DATA="$ARVBOX_DATA/var"
55 PASSENGER="$ARVBOX_DATA/passenger"
56 GEMS="$ARVBOX_DATA/gems"
57 PIPCACHE="$ARVBOX_DATA/pip"
58 NPMCACHE="$ARVBOX_DATA/npm"
59 GOSTUFF="$ARVBOX_DATA/gopath"
60 RLIBS="$ARVBOX_DATA/Rlibs"
61
62 getip() {
63     docker inspect $ARVBOX_CONTAINER | grep \"IPAddress\" | head -n1 | tr -d ' ":,\n' | cut -c10-
64 }
65
66 gethost() {
67     set +e
68     OVERRIDE=$(docker exec -i $ARVBOX_CONTAINER cat /var/run/localip_override 2>/dev/null)
69     CODE=$?
70     set -e
71     if test "$CODE" = 0 ; then
72        echo $OVERRIDE
73     else
74         getip
75     fi
76 }
77
78 updateconf() {
79     if test -f ~/.config/arvados/$ARVBOX_CONTAINER.conf ; then
80         sed "s/ARVADOS_API_HOST=.*/ARVADOS_API_HOST=$(gethost):8000/" <$HOME/.config/arvados/$ARVBOX_CONTAINER.conf >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf.tmp
81         mv ~/.config/arvados/$ARVBOX_CONTAINER.conf.tmp ~/.config/arvados/$ARVBOX_CONTAINER.conf
82     else
83         mkdir -p $HOME/.config/arvados
84         cat >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf <<EOF
85 ARVADOS_API_HOST=$(gethost):8000
86 ARVADOS_API_TOKEN=
87 ARVADOS_API_HOST_INSECURE=true
88 EOF
89     fi
90 }
91
92 wait_for_arvbox() {
93     FF=/tmp/arvbox-fifo-$$
94     mkfifo $FF
95     docker logs -f $ARVBOX_CONTAINER > $FF &
96     LOGPID=$!
97     while read line ; do
98         echo $line
99         if echo $line | grep "Workbench is running at" >/dev/null ; then
100             kill $LOGPID
101         fi
102     done < $FF
103     rm $FF
104     echo
105     if test -n "$localip" ; then
106         echo "export ARVADOS_API_HOST=$localip:8000"
107     else
108         echo "export ARVADOS_API_HOST=$(gethost):8000"
109     fi
110 }
111
112 run() {
113     CONFIG=$1
114     TAG=$2
115
116     shift
117
118     need_setup=1
119
120     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
121         if test "$CONFIG" = test ; then
122             need_setup=0
123         else
124             echo "Container $ARVBOX_CONTAINER is already running"
125             exit 0
126         fi
127     fi
128
129     if test $need_setup = 1 ; then
130         if docker ps -a | grep -E "$ARVBOX_CONTAINER$" -q ; then
131             echo "Container $ARVBOX_CONTAINER already exists but is not running; use restart or reboot"
132             exit 1
133         fi
134     fi
135
136     if test -n "$TAG"
137     then
138         if test $(echo $TAG | cut -c1-1) != '-' ; then
139             TAG=":$TAG"
140             shift
141         else
142             unset TAG
143         fi
144     fi
145
146     if echo "$CONFIG" | grep '^public' ; then
147         if test -n "$ARVBOX_PUBLISH_IP" ; then
148             localip=$ARVBOX_PUBLISH_IP
149         else
150             defaultdev=$(/sbin/ip route|awk '/default/ { print $5 }')
151             localip=$(ip addr show $defaultdev | grep 'inet ' | sed 's/ *inet \(.*\)\/.*/\1/')
152         fi
153         iptemp=$(tempfile)
154         echo $localip > $iptemp
155         chmod og+r $iptemp
156         PUBLIC="--volume=$iptemp:/var/run/localip_override
157               --publish=80:80
158               --publish=8000:8000
159               --publish=8900:8900
160               --publish=9001:9001
161               --publish=9002:9002
162               --publish=25100:25100
163               --publish=25107:25107
164               --publish=25108:25108
165               --publish=8001:8001
166               --publish=8002:8002"
167     else
168         PUBLIC=""
169     fi
170
171     if echo "$CONFIG" | grep 'demo$' ; then
172         if test -d "$ARVBOX_DATA" ; then
173             echo "It looks like you already have a development container named $ARVBOX_CONTAINER."
174             echo "Set ARVBOX_CONTAINER to set a different name for your demo container"
175             exit 1
176         fi
177
178         if ! (docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q) ; then
179             docker create -v /var/lib/postgresql -v /var/lib/arvados --name $ARVBOX_CONTAINER-data arvados/arvbox-demo /bin/true
180         fi
181
182         docker run \
183                --detach \
184                --name=$ARVBOX_CONTAINER \
185                --privileged \
186                --volumes-from $ARVBOX_CONTAINER-data \
187                $PUBLIC \
188                arvados/arvbox-demo$TAG
189         updateconf
190         wait_for_arvbox
191     else
192         mkdir -p "$PG_DATA" "$VAR_DATA" "$PASSENGER" "$GEMS" "$PIPCACHE" "$NPMCACHE" "$GOSTUFF" "$RLIBS"
193
194
195         if ! test -d "$ARVADOS_ROOT" ; then
196             git clone https://github.com/curoverse/arvados.git "$ARVADOS_ROOT"
197         fi
198         if ! test -d "$SSO_ROOT" ; then
199             git clone https://github.com/curoverse/sso-devise-omniauth-provider.git "$SSO_ROOT"
200         fi
201         if ! test -d "$COMPOSER_ROOT" ; then
202             git clone https://github.com/curoverse/composer.git "$COMPOSER_ROOT"
203         fi
204
205         if test "$CONFIG" = test ; then
206
207             mkdir -p $VAR_DATA/test
208
209             if test "$need_setup" = 1 ; then
210                 docker run \
211                        --detach \
212                        --name=$ARVBOX_CONTAINER \
213                        --privileged \
214                        "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
215                        "--volume=$SSO_ROOT:/usr/src/sso:rw" \
216                        "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
217                        "--volume=$PG_DATA:/var/lib/postgresql:rw" \
218                        "--volume=$VAR_DATA:/var/lib/arvados:rw" \
219                        "--volume=$PASSENGER:/var/lib/passenger:rw" \
220                        "--volume=$GEMS:/var/lib/gems:rw" \
221                        "--volume=$PIPCACHE:/var/lib/pip:rw" \
222                        "--volume=$NPMCACHE:/var/lib/npm:rw" \
223                        "--volume=$GOSTUFF:/var/lib/gopath:rw" \
224                        "--volume=$RLIBS:/var/lib/Rlibs:rw" \
225                        "--env=SVDIR=/etc/test-service" \
226                        arvados/arvbox-dev$TAG
227
228                 docker exec -ti \
229                        $ARVBOX_CONTAINER \
230                        /usr/local/lib/arvbox/runsu.sh \
231                        /usr/local/lib/arvbox/waitforpostgres.sh
232
233                 docker exec -ti \
234                        $ARVBOX_CONTAINER \
235                        /usr/local/lib/arvbox/runsu.sh \
236                        /var/lib/arvbox/service/sso/run-service --only-setup
237
238                 docker exec -ti \
239                        $ARVBOX_CONTAINER \
240                        /usr/local/lib/arvbox/runsu.sh \
241                        /var/lib/arvbox/service/api/run-service --only-setup
242             fi
243
244             docker exec -ti \
245                    $ARVBOX_CONTAINER \
246                    /usr/local/lib/arvbox/runsu.sh \
247                    /usr/src/arvados/build/run-tests.sh \
248                    --temp /var/lib/arvados/test \
249                    WORKSPACE=/usr/src/arvados \
250                    GEM_HOME=/var/lib/gems \
251                    "$@"
252         elif echo "$CONFIG" | grep 'dev$' ; then
253             docker run \
254                    --detach \
255                    --name=$ARVBOX_CONTAINER \
256                    --privileged \
257                    "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
258                    "--volume=$SSO_ROOT:/usr/src/sso:rw" \
259                    "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
260                    "--volume=$PG_DATA:/var/lib/postgresql:rw" \
261                    "--volume=$VAR_DATA:/var/lib/arvados:rw" \
262                    "--volume=$PASSENGER:/var/lib/passenger:rw" \
263                    "--volume=$GEMS:/var/lib/gems:rw" \
264                    "--volume=$PIPCACHE:/var/lib/pip:rw" \
265                    "--volume=$NPMCACHE:/var/lib/npm:rw" \
266                    "--volume=$GOSTUFF:/var/lib/gopath:rw" \
267                    "--volume=$RLIBS:/var/lib/Rlibs:rw" \
268                    $PUBLIC \
269                    arvados/arvbox-dev$TAG
270             updateconf
271             wait_for_arvbox
272             echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
273         else
274             echo "Unknown configuration '$CONFIG'"
275         fi
276     fi
277 }
278
279 stop() {
280     if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
281         docker stop $ARVBOX_CONTAINER
282     fi
283
284     VOLUMES=--volumes=true
285     if docker ps -a --filter "status=created" | grep -E "$ARVBOX_CONTAINER$" -q ; then
286         docker rm $VOLUMES $ARVBOX_CONTAINER
287     fi
288     if docker ps -a --filter "status=exited" | grep -E "$ARVBOX_CONTAINER$" -q ; then
289         docker rm $VOLUMES $ARVBOX_CONTAINER
290     fi
291 }
292
293 build() {
294     if ! test -f "$ARVBOX_DOCKER/Dockerfile.base" ;  then
295         echo "Could not find Dockerfile (expected it at $ARVBOX_DOCKER/Dockerfile.base)"
296         exit 1
297     fi
298     if docker --version |grep " 1\.[0-9]\." ; then
299         # Docker version prior 1.10 require -f flag
300         # -f flag removed in Docker 1.12
301         FORCE=-f
302     fi
303     GITHEAD=$(cd $ARVBOX_DOCKER && git log --format=%H -n1 HEAD)
304     docker build --build-arg=arvados_version=$GITHEAD $NO_CACHE -t arvados/arvbox-base:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.base" "$ARVBOX_DOCKER"
305     docker tag $FORCE arvados/arvbox-base:$GITHEAD arvados/arvbox-base:latest
306     if test "$1" = localdemo -o "$1" = publicdemo ; then
307         docker build $NO_CACHE -t arvados/arvbox-demo:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.demo" "$ARVBOX_DOCKER"
308         docker tag $FORCE arvados/arvbox-demo:$GITHEAD arvados/arvbox-demo:latest
309     else
310         docker build $NO_CACHE -t arvados/arvbox-dev:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.dev" "$ARVBOX_DOCKER"
311         docker tag $FORCE arvados/arvbox-dev:$GITHEAD arvados/arvbox-dev:latest
312     fi
313 }
314
315 check() {
316     case "$1" in
317         localdemo|publicdemo|dev|publicdev|test)
318             true
319             ;;
320         *)
321             echo "Argument to $subcmd must be one of localdemo, publicdemo, dev, publicdev, test"
322             exit 1
323         ;;
324     esac
325 }
326
327 subcmd="$1"
328 if test -n "$subcmd" ; then
329     shift
330 fi
331 case "$subcmd" in
332     build)
333         check $@
334         build $@
335         ;;
336
337     rebuild)
338         check $@
339         NO_CACHE=--no-cache build $@
340         ;;
341
342     start|run)
343         check $@
344         run $@
345         ;;
346
347     sh*)
348         exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM GEM_HOME=/var/lib/gems /bin/bash
349         ;;
350
351     pipe)
352         exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash -
353         ;;
354
355     stop)
356         stop
357         ;;
358
359     restart)
360         check $@
361         stop
362         run $@
363         ;;
364
365     reboot)
366         check $@
367         stop
368         build $@
369         run $@
370         ;;
371
372     ip)
373         getip
374         ;;
375
376     host)
377         gethost
378         ;;
379
380     open)
381         exec xdg-open http://$(gethost)
382         ;;
383
384     status)
385         echo "Selected: $ARVBOX_CONTAINER"
386         if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
387             echo "Status: running"
388             echo "Container IP: $(getip)"
389             echo "Published host: $(gethost)"
390         else
391             echo "Status: not running"
392         fi
393         if test -d "$ARVBOX_DATA" ; then
394             echo "Data: $ARVBOX_DATA"
395         elif docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q ; then
396             echo "Data: $ARVBOX_CONTAINER-data"
397         else
398             echo "Data: none"
399         fi
400         ;;
401
402     reset|destroy)
403         stop
404         if test -d "$ARVBOX_DATA" ; then
405             if test "$subcmd" = destroy ; then
406                 if test "$1" != -f ; then
407                     echo "WARNING!  This will delete your entire arvbox ($ARVBOX_DATA)."
408                     echo "Use destroy -f if you really mean it."
409                     exit 1
410                 fi
411                 set -x
412                 rm -rf "$ARVBOX_DATA"
413             else
414                 if test "$1" != -f ; then
415                     echo "WARNING!  This will delete your arvbox data ($ARVBOX_DATA)."
416                     echo "Code and downloaded packages will be preserved."
417                     echo "Use reset -f if you really mean it."
418                     exit 1
419                 fi
420                 set -x
421                 rm -rf "$ARVBOX_DATA/postgres"
422                 rm -rf "$ARVBOX_DATA/var"
423             fi
424         else
425             if test "$1" != -f ; then
426                 echo "WARNING!  This will delete your data container $ARVBOX_CONTAINER-data.  Use -f if you really mean it."
427                 exit 1
428             fi
429             set -x
430             docker rm "$ARVBOX_CONTAINER-data"
431         fi
432         ;;
433
434     log)
435         if test -n "$1" ; then
436             exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM less --follow-name -R +GF "/etc/service/$1/log/main/current"
437         else
438             exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM tail $(docker exec -ti $ARVBOX_CONTAINER find -L /etc -path '/etc/service/*/log/main/current' -printf " %p")
439         fi
440         ;;
441
442     cat)
443         if test -n "$1" ; then
444             exec docker exec $ARVBOX_CONTAINER cat "$@"
445         else
446             echo "Usage: $0 $subcmd <files>"
447         fi
448         ;;
449
450     ls)
451         exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM ls "$@"
452         ;;
453
454     sv)
455         if test -n "$1" -a -n "$2" ; then
456             exec docker exec -ti $ARVBOX_CONTAINER sv "$@"
457         else
458             echo "Usage: $0 $subcmd <start|stop|restart> <service>"
459             echo "Available services:"
460             exec docker exec -ti $ARVBOX_CONTAINER ls /etc/service
461         fi
462         ;;
463
464     clone)
465         if test -n "$2" ; then
466             cp -r "$ARVBOX_BASE/$1" "$ARVBOX_BASE/$2"
467             echo "Created new arvbox $2"
468             echo "export ARVBOX_CONTAINER=$2"
469         else
470             echo "clone <from> <to>   clone an arvbox"
471             echo "available arvboxes: $(ls $ARVBOX_BASE)"
472         fi
473         ;;
474
475     *)
476         echo "Arvados-in-a-box                      http://arvados.org"
477         echo
478         echo "build   <config>      build arvbox Docker image"
479         echo "rebuild <config>      build arvbox Docker image, no layer cache"
480         echo "start|run <config> [tag]  start $ARVBOX_CONTAINER container"
481         echo "open       open arvbox workbench in a web browser"
482         echo "shell      enter arvbox shell"
483         echo "ip         print arvbox docker container ip address"
484         echo "host       print arvbox published host"
485         echo "status     print some information about current arvbox"
486         echo "stop       stop arvbox container"
487         echo "restart <config>  stop, then run again"
488         echo "reboot  <config>  stop, build arvbox Docker image, run"
489         echo "reset      delete arvbox arvados data (be careful!)"
490         echo "destroy    delete all arvbox code and data (be careful!)"
491         echo "log <service> tail log of specified service"
492         echo "ls <options>  list directories inside arvbox"
493         echo "cat <files>   get contents of files inside arvbox"
494         echo "pipe       run a bash script piped in from stdin"
495         echo "sv <start|stop|restart> <service> change state of service inside arvbox"
496         echo "clone <from> <to>   clone an arvbox"
497         ;;
498 esac