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