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