#!/bin/bash
# Copyright 2014 - Rackspace Hosting
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.


# Solum Build Script for Docker and lp-cedarish


SCRIPT_START_TIME=$(date +"%s")

IMAGE_STORAGE=${IMAGE_STORAGE:-null}
ASSEMBLY_ID=${ASSEMBLY_ID:-null}
PROJECT_ID=${PROJECT_ID:-null}
BUILD_ID=${BUILD_ID:-null}
TASKNAME=build
REUSE_IMAGES_IF_REPO_UNCHANGED=${REUSE_IMAGES_IF_REPO_UNCHANGED:="0"}
USER_PARAMS=${USER_PARAMS:-null}
SOLUM_PARAMS=${SOLUM_PARAMS:-null}
GIT_PRIVATE_KEY=${REPO_DEPLOY_KEYS:-''}
RUN_CMD=${RUN_CMD:-''}
DELETE_LOCAL_CACHE=${DELETE_LOCAL_CACHE:-null}
LP_ACCESS=${ACCESS:-null}

OS_AUTH_TOKEN=${OS_AUTH_TOKEN:-null}
OS_REGION_NAME=${OS_REGION_NAME:-null}
OS_STORAGE_URL=${OS_STORAGE_URL:-null}

TEMP_URL_SECRET=${TEMP_URL_SECRET:-null}
TEMP_URL_PROTOCOL=${TEMP_URL_PROTOCOL:-null}
TEMP_URL_TTL=${TEMP_URL_TTL:-null}

OPR_LP_DOWNLOAD_STRATEGY=${OPR_LP_DOWNLOAD_STRATEGY:-null}
OPER_AUTH_TOKEN=${OPER_AUTH_TOKEN:-null}
OPER_OS_STORAGE_URL=${OPER_OS_STORAGE_URL:-null}

# TLOG, PRUN, etc. defined in common/utils
HERE=$(dirname $0)
source $HERE/../../common/utils

LOG_FILE=$(GET_LOGFILE)

# Get the image_id of the image named $1
function app_glance_id () {
  glance --os-image-api-version 2 --os-auth-token $OS_AUTH_TOKEN image-list --sort-key updated_at --sort-dir asc | grep $1 | grep -v "+--" | tail -1 | cut -d'|' -f2
}

function cleanup_on_exit () {
  if [[ $DELETE_LOCAL_CACHE == "true" ]]; then
    # Not Deleting languagepack image because we found it conflicts with docker load in
    # performance tests, which might be due to a concurrency bug in docker load and rmi.
    #if [[ -n $BASE_IMG ]]; then
    #  sudo docker rmi -f $BASE_IMG
    #fi

    # Delete DU image after uploading to backend
    if [[ -n $DU_IMG_TAG ]]; then
      sudo docker rmi $DU_IMG_TAG
    fi

    # Delete temp output files
    if [[ -n $APP_DU_FILE ]] && [[ -e $APP_DU_FILE ]]; then
      rm -f $APP_DU_FILE
    fi

    if [[ -n $OUTPUT ]] && [[ -e $OUTPUT ]]; then
      rm -f $OUTPUT
    fi

    # Delete the cloned git repo
    if [[ -n $APP_DIR ]]; then
      rm -rf $APP_DIR
    fi

    if [[ -n $TMP_APP_DIR ]]; then
      rm -rf $TMP_APP_DIR
    fi

    remove_ssh_creds "$GIT_PRIVATE_KEY"
  fi
}

TLOG ===== Starting Build Script $0 $*

# Make sure tenant auth credentials were passed in.
if [[ -z "$OS_AUTH_TOKEN" ]]; then
  TLOG OpenStack credentials not passed via ENV. && exit 1
fi

# Check command line arguments
if [[ $# -lt 6 ]]; then
  TLOG Usage: $0 git_url commit_sha appname project_id img_external_ref lp_img_tag && exit 1
fi

PRUN silent sudo docker ps
[[ $? != 0 ]] && TLOG cannot talk to docker. && exit 1


GIT=$1
shift
COMMIT_ID=$1
shift
APP=$1
shift
TENANT=$1
shift
IMG_EXTERNAL_REF=$1
shift
LP_IMG_TAG=$1

if ! (test_public_repo $GIT); then
    TLOG Could not reach $GIT with curl. Failing. && exit 1
fi

BASE_DIR=/dev/shm
GIT_CHECKSUM=$(git ls-remote $GIT | head -1 | awk '{print $1}')
TS=$(date +"%Y%m%dt%H%M%S%N")
APP_DIR="$BASE_DIR/apps/$TENANT/$ASSEMBLY_ID"
TMP_APP_DIR="/tmp/apps/$TENANT/$ASSEMBLY_ID"
mkdir -p $APP_DIR
mkdir -p $TMP_APP_DIR

add_ssh_creds "$GIT_PRIVATE_KEY" "$APP_DIR"
[[ $? != 0 ]] && TLOG FAILED to register ssh key with ssh-agent && exit 1

trap cleanup_on_exit EXIT

if [[ -d "$APP_DIR/build" ]]; then
  cd $APP_DIR/build
  OUT=$(git pull | grep -c 'Already up-to-date')
  # Check to see if this is the same as last build, and don't rebuild if allowed to skip
  if [[ "$OUT" != "0" ]]; then
    if [[ "$REUSE_IMAGES_IF_REPO_UNCHANGED" -eq "1" ]]; then
      image_id=$(app_glance_id $APP)
      if [[ ${#image_id} == 36 ]]; then # uuid4 is 36 characters
        TLOG Repo is unchanged. Reusing image $image_id.
        TLOG created_image_id=$image_id
        # Need stdout for solum-worker to parse the image_id
        echo created_image_id=$image_id
        TOTAL_TIME=$(elapsed $SCRIPT_START_TIME)
        TLOG ===== Total elapsed time: $TOTAL_TIME sec
        exit 0
      fi
    fi
  fi
else
  git_clone_with_commit_sha_retry $GIT $APP_DIR/build $COMMIT_ID
  [[ $? != 0 ]] && TLOG Git clone failed. Check repo $GIT && exit 1
fi

cd $APP_DIR/build
if [[ "$COMMIT_ID" == "" ]]; then
  COMMIT_ID=$(git log -1 --pretty=%H)
fi

# if $RUN_CMD contains a run script in repo ($APP_DIR/build)
# and not executable, make it executable
# docker ADD and COPY will inherit permissions
run_cmd_prefix="./"
if [[ -n $RUN_CMD ]] && [[ "$RUN_CMD" == $run_cmd_prefix* ]]; then
  script_name=${RUN_CMD:2}
  if [[ -e "$script_name" ]] && [[ ! -x "$script_name" ]]; then
    chmod +x "$script_name"
  fi
fi

# If languagepack is 'auto', build the application slug
if [[ $IMG_EXTERNAL_REF == "auto" ]]; then
  TLOG "===>" Building App
  BUILD_ID=$(git archive master | sudo docker run -i -a stdin \
             -v /opt/solum/cache:/tmp/cache:rw  \
             -v /opt/solum/buildpacks:/tmp/buildpacks:rw  \
             solum/slugbuilder)

  PRUN sudo docker logs --tail=all -f $BUILD_ID

  cd $APP_DIR
  PRUN sudo docker cp $BUILD_ID:/tmp/slug.tgz $APP_DIR/
  if [[ ! -f "$APP_DIR/slug.tgz" ]]; then
    TLOG Slug build failed see container: $BUILD_ID && exit 1
  fi
  sudo docker rm -f $BUILD_ID
else
  # download base image (languagepack) if it is not 'auto'
  TLOG downloading LP image from $IMAGE_STORAGE
  if [[ $IMAGE_STORAGE == "glance" ]]; then
    OUTPUT="$TMP_APP_DIR/$LP_IMG_TAG"
    PRUN silent glance --os-image-api-version 2 --os-auth-token $OS_AUTH_TOKEN image-list
    if [[ $? != 0 ]]; then
      TLOG Cannot talk to Glance. Check your OpenStack credentials. && exit 1
    fi

    glance --os-image-api-version 2 --os-auth-token $OS_AUTH_TOKEN image-download --file $OUTPUT $IMG_EXTERNAL_REF
    if [[ $? != 0 ]]; then
      TLOG Failed to download image $IMG_EXTERNAL_REF from glance. && exit 1
    fi

    docker_load_with_retry $OUTPUT
    if [[ $? != 0 ]]; then
      OUTPUT_FILE_SIZE=$(stat -c%s "$OUTPUT")
      TLOG Failed docker load, file size $OUTPUT_FILE_SIZE && exit 1
    fi

    BASE_IMG=$LP_IMG_TAG
  elif [[ $IMAGE_STORAGE == "swift" ]]; then
    OUTPUT="$TMP_APP_DIR/$LP_IMG_TAG"
    if [[ $LP_ACCESS == "custom" ]]; then
      python $HERE/swift-handler.py $OS_REGION_NAME $OS_AUTH_TOKEN $OS_STORAGE_URL download solum_lp $IMG_EXTERNAL_REF $OUTPUT \
      > >(while read ALINE; do TLOG $ALINE; done)
    elif [[ $LP_ACCESS == "operator" ]]; then
      if [[ $OPR_LP_DOWNLOAD_STRATEGY == "swift-client" ]]; then
        python $HERE/swift-handler.py $OS_REGION_NAME $OPER_AUTH_TOKEN $OPER_OS_STORAGE_URL download solum_lp $IMG_EXTERNAL_REF $OUTPUT \
        > >(while read ALINE; do TLOG $ALINE; done)
      elif [[ $OPR_LP_DOWNLOAD_STRATEGY == "wget" ]]; then
        wget -q "$IMG_EXTERNAL_REF" --output-document=$OUTPUT
      fi
    fi
    if [[ $? != 0 ]]; then
      TLOG Failed to download image $IMG_EXTERNAL_REF from swift. && exit 1
    fi

    docker_load_with_retry $OUTPUT
    if [[ $? != 0 ]]; then
      OUTPUT_FILE_SIZE=$(stat -c%s "$OUTPUT")
      TLOG Failed docker load, file size $OUTPUT_FILE_SIZE && exit 1
    fi

    BASE_IMG=$LP_IMG_TAG
  elif [[ $IMAGE_STORAGE == "docker_registry" ]]; then
    sudo docker pull $IMG_EXTERNAL_REF
    if [[ $? != 0 ]]; then
      TLOG Failed to download image $IMG_EXTERNAL_REF from docker registry. && exit 1
    fi

    BASE_IMG=$IMG_EXTERNAL_REF
  else
    TLOG Unsupported Image storage backend - $IMAGE_STORAGE && exit 1
  fi
fi

DOCKER_RUN_CMD=$RUN_CMD

# copy params to the working dir
EXT=$(mktemp -u XXX | head -n 1)
mkdir $APP_DIR/build/params_$EXT
if [[ $USER_PARAMS != null ]]; then
  cp $USER_PARAMS $APP_DIR/build/params_$EXT/user_params
  DOCKER_RUN_CMD="[\"/bin/bash\", \"-c\", \"source params_$EXT/user_params && $RUN_CMD\"]"
fi
if [[ $SOLUM_PARAMS != null ]]; then
  cp $SOLUM_PARAMS $APP_DIR/build/params_$EXT/solum_params
fi

if [[ $IMG_EXTERNAL_REF == "auto" ]]; then
# Build the application image by injecting slug into runner
  TLOG Creating Dockerfile
  cat << EOF > $APP_DIR/Dockerfile
# SOLUM APP BUILDER
FROM solum/slugrunner
ADD slug.tgz /app
COPY build/params_$EXT /root/params
ENTRYPOINT ["/runner/init"]
CMD ["start","web"]
EOF
else
  TLOG Creating Dockerfile
  cat << EOF > $APP_DIR/Dockerfile
FROM $BASE_IMG
COPY build /app
WORKDIR /solum/bin
RUN chmod +x build.sh
RUN ./build.sh
WORKDIR /app
CMD $DOCKER_RUN_CMD
EOF
fi

cd $APP_DIR
DU_IMG_TAG="$TENANT-$APP-$TS-$COMMIT_ID"
STORAGE_OBJ_NAME="$APP-$TS-$COMMIT_ID"

echo "build/.git" > .dockerignore

if [[ $IMAGE_STORAGE == "glance" ]]; then

  docker_build_with_retry $DU_IMG_TAG .
  [[ $? != 0 ]] && TLOG Docker build failed. && exit 1

  glance_upload_with_retry $DU_IMG_TAG
  image_id="$(app_glance_id $DU_IMG_TAG)"
  TLOG ===== finished uploading DU to $IMAGE_STORAGE
elif [[ $IMAGE_STORAGE == "docker_registry" ]]; then

  DOCKER_REGISTRY=${DOCKER_REGISTRY:-'10.0.2.15:5042'}
  APP_NAME=$DOCKER_REGISTRY/$DU_IMG_TAG

  docker_build_with_retry $APP_NAME .
  [[ $? != 0 ]] && TLOG Docker build failed. && exit 1

  sudo docker push $APP_NAME
  [[ $? != 0 ]] && TLOG Docker push failed. && exit 1

  # just to make worker/shell easier to process
  image_id="${APP_NAME}"
  sudo docker rmi -f $APP_NAME
  TLOG ===== finished uploading DU to $IMAGE_STORAGE
elif [[ $IMAGE_STORAGE == "swift" ]]; then
  docker_build_with_retry $DU_IMG_TAG .
  if [[ $? != 0 ]]; then
    TLOG Docker build failed. && exit 1
  fi

  APP_DU_FILE="$TMP_APP_DIR/$STORAGE_OBJ_NAME"
  docker_save_with_retry $APP_DU_FILE $DU_IMG_TAG
  if [[ $? != 0 ]]; then
    TLOG Docker save failed. && exit 1
  fi

  #TODO(devkulkarni): Read the SECRET and TTL from config file
  SECRET=secret
  TTL=604800

  python $HERE/swift-handler.py $OS_REGION_NAME $OS_AUTH_TOKEN $OS_STORAGE_URL upload solum_du $STORAGE_OBJ_NAME $APP_DU_FILE \
  > >(while read ALINE; do TLOG $ALINE; done)
  if [[ $? != 0 ]]; then
    TLOG Swift upload failed. && exit 1
  fi

  ACCOUNT=$(echo $OS_STORAGE_URL | sed 's/\// /'g | awk '{print $4}')
  TLOG "ACCOUNT=$ACCOUNT"
  STORAGE_HOST=$(echo $OS_STORAGE_URL | sed 's/\// /'g | awk '{print $2}')
  TLOG "STORAGE_HOST=$STORAGE_HOST"
  curl -i -X POST -H X-Auth-Token:$OS_AUTH_TOKEN -H X-Account-Meta-Temp-URL-Key:$TEMP_URL_SECRET $TEMP_URL_PROTOCOL://$STORAGE_HOST/v1/$ACCOUNT
  TLOG "HERE:$HERE"

  TEMP_URL=$(python $HERE/get-temp-url.py $STORAGE_HOST solum_du $STORAGE_OBJ_NAME $ACCOUNT $TEMP_URL_SECRET $TEMP_URL_TTL $TEMP_URL_PROTOCOL)
  TLOG "TEMP_URL:$TEMP_URL"

  image_id="${TEMP_URL}"
  TLOG ===== finished uploading DU to $IMAGE_STORAGE
else
  TLOG Unsupported Image storage backend - $IMAGE_STORAGE && exit 1
fi

TOTAL_TIME=$(elapsed $SCRIPT_START_TIME)
TLOG ===== Total elapsed time: $TOTAL_TIME sec

TLOG created_image_id=$image_id
TLOG docker_image_name=$DU_IMG_TAG

# Need stdout for solum-worker to parse the image_id
echo created_image_id=$image_id
echo docker_image_name=$DU_IMG_TAG

exit 0
