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