#!/bin/sh
# Copyright (C) The Arvados Authors. All rights reserved.
#
# SPDX-License-Identifier: AGPL-3.0

set -e

if ! test -d /sys/fs/cgroup ; then
     echo "Arvbox requires cgroups to be mounted at /sys/fs/cgroup in order to use"
     echo "Docker-in-Docker.  Older operating systems that put cgroups in other"
     echo "places (such as /cgroup) are not supported."
     exit 1
fi

if ! which docker >/dev/null 2>/dev/null ; then
  echo "Arvbox requires Docker.  To install, run the following command as root:"
  echo "curl -sSL https://get.docker.com/ | sh"
  exit 1
fi

if test -z "$ARVBOX_DOCKER" ; then
    if which greadlink >/dev/null 2>/dev/null ; then
        ARVBOX_DOCKER=$(greadlink -f $(dirname $0)/../lib/arvbox/docker)
    else
        ARVBOX_DOCKER=$(readlink -f $(dirname $0)/../lib/arvbox/docker)
    fi
fi

if test -z "$ARVBOX_CONTAINER" ; then
    ARVBOX_CONTAINER=arvbox
fi

if test -z "$ARVBOX_BASE" ; then
    ARVBOX_BASE="$HOME/.arvbox"
fi

if test -z "$ARVBOX_DATA" ; then
    ARVBOX_DATA="$ARVBOX_BASE/$ARVBOX_CONTAINER"
fi

if test -z "$ARVADOS_ROOT" ; then
    ARVADOS_ROOT="$ARVBOX_DATA/arvados"
fi

if test -z "$SSO_ROOT" ; then
    SSO_ROOT="$ARVBOX_DATA/sso-devise-omniauth-provider"
fi

if test -z "$COMPOSER_ROOT" ; then
    COMPOSER_ROOT="$ARVBOX_DATA/composer"
fi

PG_DATA="$ARVBOX_DATA/postgres"
VAR_DATA="$ARVBOX_DATA/var"
PASSENGER="$ARVBOX_DATA/passenger"
GEMS="$ARVBOX_DATA/gems"
PIPCACHE="$ARVBOX_DATA/pip"
NPMCACHE="$ARVBOX_DATA/npm"
GOSTUFF="$ARVBOX_DATA/gopath"
RLIBS="$ARVBOX_DATA/Rlibs"

getip() {
    docker inspect $ARVBOX_CONTAINER | grep \"IPAddress\" | head -n1 | tr -d ' ":,\n' | cut -c10-
}

gethost() {
    set +e
    OVERRIDE=$(docker exec -i $ARVBOX_CONTAINER cat /var/run/localip_override 2>/dev/null)
    CODE=$?
    set -e
    if test "$CODE" = 0 ; then
       echo $OVERRIDE
    else
        getip
    fi
}

getclusterid() {
    docker exec $ARVBOX_CONTAINER cat /var/lib/arvados/api_uuid_prefix
}

updateconf() {
    if test -f ~/.config/arvados/$ARVBOX_CONTAINER.conf ; then
        sed "s/ARVADOS_API_HOST=.*/ARVADOS_API_HOST=$(gethost):8000/" <$HOME/.config/arvados/$ARVBOX_CONTAINER.conf >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf.tmp
        mv ~/.config/arvados/$ARVBOX_CONTAINER.conf.tmp ~/.config/arvados/$ARVBOX_CONTAINER.conf
    else
        mkdir -p $HOME/.config/arvados
        cat >$HOME/.config/arvados/$ARVBOX_CONTAINER.conf <<EOF
ARVADOS_API_HOST=$(gethost):8000
ARVADOS_API_TOKEN=
ARVADOS_API_HOST_INSECURE=true
EOF
    fi
}

wait_for_arvbox() {
    FF=/tmp/arvbox-fifo-$$
    mkfifo $FF
    docker logs -f $ARVBOX_CONTAINER > $FF &
    LOGPID=$!
    while read line ; do
        echo $line
        if echo $line | grep "Workbench is running at" >/dev/null ; then
            kill $LOGPID
        fi
    done < $FF
    rm $FF
    echo
    if test -n "$localip" ; then
        echo "export ARVADOS_API_HOST=$localip:8000"
    else
        echo "export ARVADOS_API_HOST=$(gethost):8000"
    fi
}

run() {
    CONFIG=$1
    TAG=$2

    shift

    need_setup=1

    if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
        if test "$CONFIG" = test ; then
            need_setup=0
        else
            echo "Container $ARVBOX_CONTAINER is already running"
            exit 0
        fi
    fi

    if test $need_setup = 1 ; then
        if docker ps -a | grep -E "$ARVBOX_CONTAINER$" -q ; then
            echo "Container $ARVBOX_CONTAINER already exists but is not running; use restart or reboot"
            exit 1
        fi
    fi

    if test -n "$TAG"
    then
        if test $(echo $TAG | cut -c1-1) != '-' ; then
	    TAG=":$TAG"
            shift
        else
            unset TAG
        fi
    fi

    if echo "$CONFIG" | grep '^public' ; then
        if test -n "$ARVBOX_PUBLISH_IP" ; then
            localip=$ARVBOX_PUBLISH_IP
        else
            defaultdev=$(/sbin/ip route|awk '/default/ { print $5 }')
            localip=$(ip addr show $defaultdev | grep 'inet ' | sed 's/ *inet \(.*\)\/.*/\1/')
        fi
        iptemp=$(tempfile)
        echo $localip > $iptemp
        chmod og+r $iptemp
        PUBLIC="--volume=$iptemp:/var/run/localip_override
              --publish=80:80
              --publish=8000:8000
              --publish=8900:8900
              --publish=9001:9001
              --publish=9002:9002
              --publish=25100:25100
              --publish=25107:25107
              --publish=25108:25108
              --publish=8001:8001
              --publish=8002:8002"
    else
        PUBLIC=""
    fi

    if echo "$CONFIG" | grep 'demo$' ; then
        if test -d "$ARVBOX_DATA" ; then
            echo "It looks like you already have a development container named $ARVBOX_CONTAINER."
            echo "Set ARVBOX_CONTAINER to set a different name for your demo container"
            exit 1
        fi

        if ! (docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q) ; then
            docker create -v /var/lib/postgresql -v /var/lib/arvados --name $ARVBOX_CONTAINER-data arvados/arvbox-demo /bin/true
        fi

        docker run \
               --detach \
               --name=$ARVBOX_CONTAINER \
               --privileged \
               --volumes-from $ARVBOX_CONTAINER-data \
               $PUBLIC \
               arvados/arvbox-demo$TAG
        updateconf
        wait_for_arvbox
    else
        mkdir -p "$PG_DATA" "$VAR_DATA" "$PASSENGER" "$GEMS" "$PIPCACHE" "$NPMCACHE" "$GOSTUFF" "$RLIBS"


        if ! test -d "$ARVADOS_ROOT" ; then
            git clone https://github.com/curoverse/arvados.git "$ARVADOS_ROOT"
        fi
        if ! test -d "$SSO_ROOT" ; then
            git clone https://github.com/curoverse/sso-devise-omniauth-provider.git "$SSO_ROOT"
        fi
        if ! test -d "$COMPOSER_ROOT" ; then
            git clone https://github.com/curoverse/composer.git "$COMPOSER_ROOT"
        fi

        if test "$CONFIG" = test ; then

            mkdir -p $VAR_DATA/test

            if test "$need_setup" = 1 ; then
                docker run \
                       --detach \
                       --name=$ARVBOX_CONTAINER \
                       --privileged \
                       "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
                       "--volume=$SSO_ROOT:/usr/src/sso:rw" \
                       "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
                       "--volume=$PG_DATA:/var/lib/postgresql:rw" \
                       "--volume=$VAR_DATA:/var/lib/arvados:rw" \
                       "--volume=$PASSENGER:/var/lib/passenger:rw" \
                       "--volume=$GEMS:/var/lib/gems:rw" \
                       "--volume=$PIPCACHE:/var/lib/pip:rw" \
                       "--volume=$NPMCACHE:/var/lib/npm:rw" \
                       "--volume=$GOSTUFF:/var/lib/gopath:rw" \
                       "--volume=$RLIBS:/var/lib/Rlibs:rw" \
		       "--env=SVDIR=/etc/test-service" \
                       arvados/arvbox-dev$TAG

                docker exec -ti \
                       $ARVBOX_CONTAINER \
                       /usr/local/lib/arvbox/runsu.sh \
                       /usr/local/lib/arvbox/waitforpostgres.sh

                docker exec -ti \
                       $ARVBOX_CONTAINER \
                       /usr/local/lib/arvbox/runsu.sh \
                       /var/lib/arvbox/service/sso/run-service --only-setup

                docker exec -ti \
                       $ARVBOX_CONTAINER \
                       /usr/local/lib/arvbox/runsu.sh \
                       /var/lib/arvbox/service/api/run-service --only-setup
            fi

            docker exec -ti \
                   $ARVBOX_CONTAINER \
                   /usr/local/lib/arvbox/runsu.sh \
                   /usr/src/arvados/build/run-tests.sh \
                   --temp /var/lib/arvados/test \
                   WORKSPACE=/usr/src/arvados \
                   GEM_HOME=/var/lib/gems \
                   "$@"
        elif echo "$CONFIG" | grep 'dev$' ; then
            docker run \
                   --detach \
                   --name=$ARVBOX_CONTAINER \
                   --privileged \
                   "--volume=$ARVADOS_ROOT:/usr/src/arvados:rw" \
                   "--volume=$SSO_ROOT:/usr/src/sso:rw" \
                   "--volume=$COMPOSER_ROOT:/usr/src/composer:rw" \
                   "--volume=$PG_DATA:/var/lib/postgresql:rw" \
                   "--volume=$VAR_DATA:/var/lib/arvados:rw" \
                   "--volume=$PASSENGER:/var/lib/passenger:rw" \
                   "--volume=$GEMS:/var/lib/gems:rw" \
                   "--volume=$PIPCACHE:/var/lib/pip:rw" \
                   "--volume=$NPMCACHE:/var/lib/npm:rw" \
                   "--volume=$GOSTUFF:/var/lib/gopath:rw" \
                   "--volume=$RLIBS:/var/lib/Rlibs:rw" \
                   $PUBLIC \
                   arvados/arvbox-dev$TAG
            updateconf
            wait_for_arvbox
            echo "The Arvados source code is checked out at: $ARVADOS_ROOT"
        else
            echo "Unknown configuration '$CONFIG'"
        fi
    fi
}

stop() {
    if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
        docker stop $ARVBOX_CONTAINER
    fi

    VOLUMES=--volumes=true
    if docker ps -a --filter "status=created" | grep -E "$ARVBOX_CONTAINER$" -q ; then
        docker rm $VOLUMES $ARVBOX_CONTAINER
    fi
    if docker ps -a --filter "status=exited" | grep -E "$ARVBOX_CONTAINER$" -q ; then
        docker rm $VOLUMES $ARVBOX_CONTAINER
    fi
}

build() {
    if ! test -f "$ARVBOX_DOCKER/Dockerfile.base" ;  then
        echo "Could not find Dockerfile (expected it at $ARVBOX_DOCKER/Dockerfile.base)"
        exit 1
    fi
    if docker --version |grep " 1\.[0-9]\." ; then
        # Docker version prior 1.10 require -f flag
        # -f flag removed in Docker 1.12
        FORCE=-f
    fi
    GITHEAD=$(cd $ARVBOX_DOCKER && git log --format=%H -n1 HEAD)
    docker build --build-arg=arvados_version=$GITHEAD $NO_CACHE -t arvados/arvbox-base:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.base" "$ARVBOX_DOCKER"
    docker tag $FORCE arvados/arvbox-base:$GITHEAD arvados/arvbox-base:latest
    if test "$1" = localdemo -o "$1" = publicdemo ; then
        docker build $NO_CACHE -t arvados/arvbox-demo:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.demo" "$ARVBOX_DOCKER"
        docker tag $FORCE arvados/arvbox-demo:$GITHEAD arvados/arvbox-demo:latest
    else
        docker build $NO_CACHE -t arvados/arvbox-dev:$GITHEAD -f "$ARVBOX_DOCKER/Dockerfile.dev" "$ARVBOX_DOCKER"
        docker tag $FORCE arvados/arvbox-dev:$GITHEAD arvados/arvbox-dev:latest
    fi
}

check() {
    case "$1" in
        localdemo|publicdemo|dev|publicdev|test)
            true
            ;;
        *)
            echo "Argument to $subcmd must be one of localdemo, publicdemo, dev, publicdev, test"
            exit 1
        ;;
    esac
}

subcmd="$1"
if test -n "$subcmd" ; then
    shift
fi
case "$subcmd" in
    build)
        check $@
        build $@
        ;;

    rebuild)
        check $@
        NO_CACHE=--no-cache build $@
        ;;

    start|run)
        check $@
        run $@
        ;;

    sh*)
        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
        ;;

    pipe)
        exec docker exec -i $ARVBOX_CONTAINER /usr/bin/env GEM_HOME=/var/lib/gems /bin/bash -
        ;;

    stop)
        stop
        ;;

    restart)
        check $@
        stop
        run $@
        ;;

    reboot)
        check $@
        stop
        build $@
        run $@
        ;;

    ip)
        getip
        ;;

    host)
        gethost
        ;;

    open)
        exec xdg-open http://$(gethost)
        ;;

    status)
        echo "Container: $ARVBOX_CONTAINER"
        if docker ps -a --filter "status=running" | grep -E "$ARVBOX_CONTAINER$" -q ; then
	    echo "Cluster id: $(getclusterid)"
            echo "Status: running"
            echo "Container IP: $(getip)"
            echo "Published host: $(gethost)"
        else
            echo "Status: not running"
        fi
        if test -d "$ARVBOX_DATA" ; then
            echo "Data: $ARVBOX_DATA"
        elif docker ps -a | grep -E "$ARVBOX_CONTAINER-data$" -q ; then
            echo "Data: $ARVBOX_CONTAINER-data"
        else
            echo "Data: none"
        fi
        ;;

    reset|destroy)
        stop
        if test -d "$ARVBOX_DATA" ; then
            if test "$subcmd" = destroy ; then
                if test "$1" != -f ; then
                    echo "WARNING!  This will delete your entire arvbox ($ARVBOX_DATA)."
                    echo "Use destroy -f if you really mean it."
                    exit 1
                fi
                set -x
                rm -rf "$ARVBOX_DATA"
            else
                if test "$1" != -f ; then
                    echo "WARNING!  This will delete your arvbox data ($ARVBOX_DATA)."
                    echo "Code and downloaded packages will be preserved."
                    echo "Use reset -f if you really mean it."
                    exit 1
                fi
                set -x
                rm -rf "$ARVBOX_DATA/postgres"
                rm -rf "$ARVBOX_DATA/var"
            fi
        else
            if test "$1" != -f ; then
                echo "WARNING!  This will delete your data container $ARVBOX_CONTAINER-data.  Use -f if you really mean it."
                exit 1
            fi
            set -x
            docker rm "$ARVBOX_CONTAINER-data"
        fi
        ;;

    log)
        if test -n "$1" ; then
            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"
        else
            exec docker exec -ti $ARVBOX_CONTAINER tail $(docker exec -ti $ARVBOX_CONTAINER find -L /etc -path '/etc/service/*/log/main/current' -printf " %p")
        fi
        ;;

    cat)
        if test -n "$1" ; then
            exec docker exec $ARVBOX_CONTAINER cat "$@"
        else
            echo "Usage: $0 $subcmd <files>"
        fi
        ;;

    ls)
        exec docker exec -ti $ARVBOX_CONTAINER /usr/bin/env TERM=$TERM ls "$@"
        ;;

    sv)
        if test -n "$1" -a -n "$2" ; then
            exec docker exec $ARVBOX_CONTAINER sv "$@"
        else
            echo "Usage: $0 $subcmd <start|stop|restart> <service>"
            echo "Available services:"
            exec docker execa $ARVBOX_CONTAINER ls /etc/service
        fi
        ;;

    clone)
        if test -n "$2" ; then
            cp -r "$ARVBOX_BASE/$1" "$ARVBOX_BASE/$2"
            echo "Created new arvbox $2"
            echo "export ARVBOX_CONTAINER=$2"
        else
            echo "clone <from> <to>   clone an arvbox"
            echo "available arvboxes: $(ls $ARVBOX_BASE)"
        fi
        ;;

    *)
        echo "Arvados-in-a-box                      http://arvados.org"
        echo
        echo "build   <config>      build arvbox Docker image"
        echo "rebuild <config>      build arvbox Docker image, no layer cache"
        echo "start|run <config> [tag]  start $ARVBOX_CONTAINER container"
        echo "open       open arvbox workbench in a web browser"
        echo "shell      enter arvbox shell"
        echo "ip         print arvbox docker container ip address"
        echo "host       print arvbox published host"
        echo "status     print some information about current arvbox"
        echo "stop       stop arvbox container"
        echo "restart <config>  stop, then run again"
        echo "reboot  <config>  stop, build arvbox Docker image, run"
        echo "reset      delete arvbox arvados data (be careful!)"
        echo "destroy    delete all arvbox code and data (be careful!)"
        echo "log <service> tail log of specified service"
        echo "ls <options>  list directories inside arvbox"
        echo "cat <files>   get contents of files inside arvbox"
        echo "pipe       run a bash script piped in from stdin"
        echo "sv <start|stop|restart> <service> change state of service inside arvbox"
        echo "clone <from> <to>   clone an arvbox"
        ;;
esac