#!/usr/bin/env bash
#
# This program provides a convenient utility for "building" a compatible 
# application image from pre-existing application image formats,
# such as Docker images or QEMU VHDs.
#

function print_usage {
    echo "$(basename $0): a useful shell script for converting Docker images into Mystikos cpio archives"
    echo
    echo "Usage: $(basename $0) [-o file] [-i image] [-e extras] [-v] [[-d] file]"
    echo
    echo "  -d file     Build docker image from the named file"
    echo "  -i image    Specify an existing Docker image name to build from"
    echo "  -f format   Specify the output format. Options: dir, cpio. Default: 'dir'"
    echo "  -e extras   Specify the extra options while building the container image"
    echo "  -o      Specify the name of the output file or directory"
    echo "          If not specified, defaults to 'appdir'"
    echo "  -v      Verbose"
    echo "  -h      Print this help message"
    echo
    echo
    echo "Sample Usage:"
    echo
    echo "  Build a Dockerfile without '-d' or '-o' option:"
    echo "    $(basename $0) Dockerfile"
    echo
    echo "  Build Dockerfile with irregular file name"
    echo "    $(basename $0) Dockerfile.release"
    echo
    echo "  Build existing image from remote registry"
    echo "    $(basename $0) -i hello-world"
    echo
    echo "  Build specified Docker image with extra options in verbose"
    echo "    $(basename $0) -d Dockerfile.debug -v -e '--build-arg USERNAME=username --build-arg PASSWORD=password'"
    echo
}


while getopts 'hvd:i:f:o:e:' OPTION; do
    case "$OPTION" in
        d )
            DOCKER_FILE=${OPTARG:-Dockerfile}
            ;;
        f )
            OUTPUT_FORMAT="$OPTARG"
            ;;
        h )
            print_usage;
            exit 0
            ;;
        i )
            IMAGE_NAME="$OPTARG"
            ;;
        o )
            OUTPUT_NAME="$OPTARG"
            ;;
        e )
            EXTRA_ARGS="$OPTARG"
            ;;
        v )
            VERBOSE=true;
            QUIET=false;
            set -x
            ;;
        * )
            print_usage;
            exit 1
            ;;
    esac
done

### defaults

if [ $# -lt 1 ]; then
        echo "Dockerfile or ImageName has not been specified"
        exit 1
fi

# if neither '-i' nor '-d' option is specified, assume the last argument is a Dockerfile name. 
# ImageName/URL must be specified by '-i' option. The default argument can only be a Dockerfile
if [ -z "$DOCKER_FILE" -a -z "$IMAGE_NAME" ]; then
    DOCKER_FILE=${@: -1}
fi

OUTPUT_NAME=${OUTPUT_NAME:-appdir}
DELETE_OUTPUT=${DELETE_OUTPUT:-false}
OUTPUT_FORMAT=${OUTPUT_FORMAT:-dir}
TEMP_IMAGE_IIDFILE=$(mktemp /tmp/myst.XXXXXX)

### end defaults

set -e

if [ ! -z "$DOCKER_FILE" ]; then
    USE_DOCKERFILE=true
else
    USE_DOCKERFILE=false
fi

case "$OUTPUT_FORMAT" in
    dir )
        ;;
    cpio )
        ;;
    ext2 )
        echo "EXT2 format output is not directly supported by this program.";
        exit 1
        ;;
    * )
        echo "Unsupported output format: '$OUTPUT_FORMAT'.";
        print_usage
        ;;
esac

if [ -z $OUTPUT_NAME ]; then
    echo "You must specify an output file name with the -o option."
    echo
    print_usage
    exit 1
fi

# ask before overwriting output target
if [ -d $OUTPUT_NAME -o -f $OUTPUT_NAME ]; then
    echo "Output target '$OUTPUT_NAME' will be overwritten by this operation! Are you sure?"
    select yn in "Yes" "No"; do
	case $yn in
	    Yes ) DELETE_OUTPUT=true ; break ;;
	    No  ) echo "Aborting $(basename $0)"; exit 0 ;;
	esac
    done
fi

if $QUIET; then
    DOCKER_BUILD_MODE="--quiet";
fi

# Download the docker image from dockerhub, or build it locally from a docker file.
get_image()
{   
    if $USE_DOCKERFILE; then
        # in either quiet or none quiet mode, IMAGE id will always be exported to intermediate iidfile
        rm -f $TEMP_IMAGE_IIDFILE
        docker build $DOCKER_BUILD_MODE --iidfile $TEMP_IMAGE_IIDFILE -f $DOCKER_FILE $EXTRA_ARGS .
        if [ ! -f "$TEMP_IMAGE_IIDFILE" ]; then
            echo "failed to build from Docker Image file $TEMP_IMAGE_IIDFILE4";
            exit 1
        fi
        IMAGE_NAME=$(cat $TEMP_IMAGE_IIDFILE)
        rm -f $TEMP_IMAGE_IIDFILE
        DELETE_IMAGE=true
    else
        docker pull $IMAGE_NAME
	    DELETE_IMAGE=false
    fi
}

# export a flattened copy of the container
export_image()
{
    TEMP_FILE=$(mktemp)
    TEMP_DIR=$(mktemp -d)
    APPENV_FILENAME="appenv.json"

    # note that we have to start up the image to flatten it
    TEMP_INAME=$(docker run -d $IMAGE_NAME)
    TEMP_APPENV=$APPENV_FILENAME-$TEMP_INAME
    
    docker image inspect --format='{{json .Config}}' $IMAGE_NAME > $TEMP_APPENV

    docker stop $TEMP_INAME >/dev/null
    docker export $TEMP_INAME -o $TEMP_FILE
    docker rm $TEMP_INAME >/dev/null
    if [ -z $DELETE_IMAGE ]; then
	docker rmi -f $IMAGE_NAME
    fi

    # create the 'appdir' from the docker export
    tar xf $TEMP_FILE -C $TEMP_DIR
    mv $TEMP_APPENV $TEMP_DIR/$APPENV_FILENAME
    rm -rf $OUTPUT_NAME $TEMP_FILE

    if [ "$OUTPUT_FORMAT" == "dir" ]; then
        mv $TEMP_DIR $OUTPUT_NAME
    elif [ "$OUTPUT_FORMAT" == "cpio" ]; then
        myst mkcpio $TEMP_DIR $OUTPUT_NAME
    fi
    rm -rf $TEMP_DIR
}

get_image
export_image

if $VERBOSE; then
    set +x
fi

echo "Success! Application built at ${OUTPUT_NAME}."
