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