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