#!/bin/bash

# This script performs managing of Muster environment.

CSDAC_VERSION='2.2.0'
S3BUCKET_HOST="https://csdac-cosign.s3.us-west-1.amazonaws.com"
PUBLIC_KEY_PATH="release"
COSIGN_IMAGE_PATH="public.ecr.aws/e6e4t5f5/csdac_cosign:windriver-latest"
COLOR_GREEN=$'\u2714'
COLOR_RED=$'\u274c'
PUBLIC_KEY_FILE='PublicKey.pem'
# Get working dir of script
real_script_name="${0}"
while test -L "${real_script_name}"; do
        real_script_name="$(readlink "${real_script_name}")"
done
SCRIPT_DIR=$(dirname ${real_script_name})

# Import .env values
if [ -f .env ]
then
  export $(xargs < .env)
fi

help() {
    cat <<EOF
Control and monitor Muster services.
Usage:
  $(basename $0) [COMMAND]
Commands:
  help          Get this help
  core-logs     View output from core services
  debug-off     Turn off debug
  debug-on      Turn on debug
  dyn-logs      View output from dynamically created services (connectors and adapters)
  start         Start services
  status        Services status
  stop          Stop services
  versions      Show app version for each container
  ts-gen        Generate trouble-shoot bundle
EOF
}


image_verification() {

echo "Image verification for signed images... STARTED..."

image_status=()
NL=$'\n'
COLOR_GREEN=$'\u2714'
COLOR_RED=$'\u274c'
PUBLIC_KEY_FILE='PublicKey.pem'
# get all images from config
images=($(grep -r docker_image -h ./config --include=\*.json | sed -rn 's/ *"docker_image" *: *"([^"]+)".*/\1/p'))
# get all images from docker-compose yaml
images+=($(grep -r image: docker-compose.yml | sed 's/^.* //'))
# download public key for verification
wget --tries=3 --timeout=30 -q -nv -O ${PUBLIC_KEY_FILE}  ${S3BUCKET_HOST}/${PUBLIC_KEY_PATH}/${PUBLIC_KEY_FILE}
STATUS=$?
if [ $STATUS -ne 0 ]; then
    echo "Failed to download verification public key with exit $STATUS. Please check the path and permission."
    exit $STATUS
fi

# Verify cosign image itself
for i in {0..2}
do
  docker run --rm -v ${PWD}:/home -v ~/.docker:/root/.docker --entrypoint /bin/bash $COSIGN_IMAGE_PATH -c "cosign verify --key /home/$PUBLIC_KEY_FILE $COSIGN_IMAGE_PATH"
  STATUS=$?
  if [ $STATUS == 0 ]; then
    break
  else
    echo "Previous attempt to verify sign cosign image was unsuccessful. Retry $((i+1))/3"
    sleep $((i+1))
  fi
done
if [ $STATUS == 0 ]; then
  image_status+=("$COSIGN_IMAGE_PATH     ${COLOR_GREEN} ${NL}")
else
  image_status+=("$COSIGN_IMAGE_PATH  ${COLOR_RED} ${NL}")
fi

# iterating images array for verification
for image in ${images[*]}
do
CONTAINER_FULL_TAG=$image
for i in {0..2}
do
  docker run --rm -v ${PWD}:/home -v ~/.docker:/root/.docker --entrypoint /bin/bash $COSIGN_IMAGE_PATH -c "cosign verify --key /home/$PUBLIC_KEY_FILE $CONTAINER_FULL_TAG"
  STATUS=$?
  if [ $STATUS == 0 ]; then
    break
  else
    echo "Previous attempt to verify sign ${image} was unsuccessful. Retry $((i+1))/3"
    sleep $((i+1))
  fi
done
if [ $STATUS == 0 ]; then
  image_status+=("$CONTAINER_FULL_TAG  ${COLOR_GREEN} ${NL}")
else
  image_status+=("$CONTAINER_FULL_TAG  ${COLOR_RED} ${NL}")
fi
done

echo "------------------------------"
echo " Image Verification Status"
echo "------------------------------"

if [[ " ${image_status[*]} " =~ " ${COLOR_RED} " ]]; then
   ERROR_LOG="One or more image verification failed. Please contact Cisco Support.\n\n ${image_status[*]}"
   printf "$ERROR_LOG"
   return 1
fi
echo "${image_status[*]}"
echo "------------------------------"

}

precheck() {
    # Public key bucket check
    {
        wget --spider -q ${S3BUCKET_HOST}/${PUBLIC_KEY_PATH}/${PUBLIC_KEY_FILE} &&
        echo "${COLOR_GREEN} Public key is reachable"
    } || {
        echo "${COLOR_RED} Make sure all of the following hosts are reachable from this host:"
        echo " - ${S3BUCKET_HOST}/"
        echo " - https://public.ecr.aws/"
        echo " - https://hub.docker.com/"
        echo "If this host is behind a firewall, make sure there are rules that allow communication; if you have a proxy, make sure it is configured to allow access."
        exit 2
    }
    # public.ecr.aws check
    {
        docker pull ${COSIGN_IMAGE_PATH} 1>/dev/null &&
        echo "${COLOR_GREEN} Public.ecr.aws is reachable"
    } || {
        echo "${COLOR_RED} Make sure all of the following hosts are reachable from this host:"
        echo " - ${S3BUCKET_HOST}/"
        echo " - https://public.ecr.aws/"
        echo " - https://hub.docker.com/"
        echo "If this host is behind a firewall, make sure there are rules that allow communication; if you have a proxy, make sure it is configured to allow access."
        exit 2
    }
    # hub.docker.com check
    {
        wget --spider -q https://hub.docker.com/ &&
        echo "${COLOR_GREEN} Hub.docker.com is reachable"
    } || {
        echo "${COLOR_RED} Make sure all of the following hosts are reachable from this host:"
        echo " - ${S3BUCKET_HOST}/"
        echo " - https://public.ecr.aws/"
        echo " - https://hub.docker.com/"
        echo "If this host is behind a firewall, make sure there are rules that allow communication; if you have a proxy, make sure it is configured to allow access."
        exit 2
    }
}

prepull() {
    #Auth to ECR
    if [ -f .env ]; then
        ECRTOKEN=$(docker run --rm -it -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} -e AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION} public.ecr.aws/aws-cli/aws-cli ecr get-login-password --region=${AWS_DEFAULT_REGION})
        echo $ECRTOKEN | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
    fi
    # Get images for adapters
    adapter_dir="./config/adapters"
    if [ -d $adapter_dir ]; then
        echo "PRE-PULLING IMAGES FOR ADAPTERS" 
        adapter_images=($(grep -r docker_image -h ./config/adapters --include=\*.json | sed -rn 's/ *"docker_image" *: *"([^"]+)".*/\1/p'))
        for image in ${adapter_images[*]}
        do
            echo "Pulling $image ..."
            for i in {0..2}
            do
                docker pull ${image}
                STATUS=$?
                if [ $STATUS == 0 ]; then
                    break
                else
                    echo "Previous attempt to pull ${image} was unsuccessful. Retry $((i+1))/3"
                    sleep $((i+1))
                fi
            done
        done    
    fi 
    # Get images for connectors
    echo "PRE-PULLING IMAGES FOR CONNECTORS"
    connector_images=($(grep -r docker_image -h ./config/connectors --include=\*.json | sed -rn 's/ *"docker_image" *: *"([^"]+)".*/\1/p'))
    for image in ${connector_images[*]}
    do
        echo "Pulling $image ..."
        for i in {0..2}
        do
            docker pull ${image}
            STATUS=$?
            if [ $STATUS == 0 ]; then
                break
            else
                echo "Previous attempt to pull ${image} was unsuccessful. Retry $((i+1))/3"
                sleep $((i+1))
            fi
        done
    done
}

start() {
    precheck
    docker volume create muster-status
    docker volume create muster-secret
    docker volume create muster-etcd-data

    docker network create -d bridge -o com.docker.network.bridge.enable_icc=true muster-net || true
    [ "$MUSTER_SKIP_VERIFICATION" == "1" ] || image_verification
    # Auth to ECR if needed
    if ([ -f .env ] && [ -z "$ECRTOKEN" ]); then
        ECRTOKEN=$(docker run --rm -it -e AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} -e AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} -e AWS_DEFAULT_REGION=${AWS_DEFAULT_REGION} public.ecr.aws/aws-cli/aws-cli ecr get-login-password --region=${AWS_DEFAULT_REGION})
        echo $ECRTOKEN | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
    fi
    prepull
    for i in {0..2}
    do
        docker-compose pull
        STATUS=$?
        if [ $STATUS == 0 ]; then
            break
        else
            echo "Previous attempt to pull core images was unsuccessful. Retry $((i+1))/3"
            sleep $((i+1))
        fi
    done
    docker-compose up -d
}

stop() {
    # Stop dynamically created containers
    docker exec -it muster-ui-backend docker-compose -f /app/muster-gen/docker-compose.yml down

    docker-compose stop
}

status() {
    echo "=============================================== CORE SERVICES ==============================================="
    docker-compose ps
    echo "=========================== CONNECTORS AND ADAPTERS ==========================="
    docker exec -it muster-ui-backend docker-compose -f /app/muster-gen/docker-compose.yml ps
}

versions_core() {
    containers=''
    containers=$(docker-compose --log-level ERROR images | awk '{if (NR>2) {print $1}}')
    if [ "${containers}" != "" ]
    then
        printf "%-30s | %-20s | %-30s\n" "CONTAINER" "APP VERSION" "COMMIT"
        printf "=%.0s" {1..79}; printf "\n"
        for container in ${containers}; do
            version=$(docker inspect ${container} --format='{{index .Config.Labels "version"}}')
            commit=$(docker inspect ${container} --format='{{index .Config.Labels "commit"}}')
            if [ "${version}" = "" ]; then version='NOT SUPPORTED'; fi
            if [ "${commit}" = "" ]; then commit='NOT SUPPORTED'; fi
            printf "%-30s | %-20s | %-30s\n" "${container}" "${version}" "${commit}"
        done
    else
        echo "No created containers found. Please check that you executed 'muster-cli start' command"
        exit 1
    fi
}

versions_dyn() {
    containers=''
    containers=$(docker exec -it muster-ui-backend docker-compose --log-level ERROR -f /app/muster-gen/docker-compose.yml images | awk '{if (NR>2) {print $1}}')
    if [ "${containers}" != "" ]
    then
        for container in ${containers}; do
            version=$(docker inspect ${container} --format='{{index .Config.Labels "version"}}')
            commit=$(docker inspect ${container} --format='{{index .Config.Labels "commit"}}')
            if [ "${version}" = "" ]; then version='NOT SUPPORTED'; fi
            if [ "${commit}" = "" ]; then commit='NOT SUPPORTED'; fi
            printf "%-30s | %-20s | %-30s\n" "${container}" "${version}" "${commit}"
        done
    else
        echo "No created adapters or connectors found. Please check that you created any adapter or connector"
    fi
}

versions() {
    echo "CSDAC version: ${CSDAC_VERSION}"
    echo "CONTAINERS VERSIONS"
    versions_core
    versions_dyn
}

core_logs() {
    docker-compose logs -f
}

dyn_logs() {
    docker exec -it muster-ui-backend docker-compose -f /app/muster-gen/docker-compose.yml logs -f
}

debug() {
    local flag=$1
    if [ $flag == "on" ] ; then
        local from="INFO"
        local to="DEBUG"
    else
        local from="DEBUG"
        local to="INFO"
    fi

    if [ -f ./docker-compose.yml ] ; then
        local current_time=$(date "+%Y.%m.%d-%H.%M.%S")
        cp -f docker-compose.yml docker-compose.yml.$current_time
        sed "s|MUSTER_LOG_LEVEL=${from}|MUSTER_LOG_LEVEL=${to}|" \
            docker-compose.yml.$current_time > docker-compose.yml
        docker-compose up -d
    else
        printf "Cannot find docker-compose.yml in current directory\n"
        exit 1
    fi
}

debug_on() {
    debug "on"
}

debug_off() {
    debug "off"
}

ts_gen() {
    echo "Generating trouble-shoot bundle."
    echo
    echo "This may take a while.  Please be patient..."
    echo
    local current_time=$(date "+%Y.%m.%d-%H.%M.%S")
    echo "Start Time : $current_time"
    local ts_name="ts-bundle-$current_time"
    local ts_dir=$(mktemp -d "/tmp/$ts_name-XXX")

    # general status
    echo "Collecting general status..."
    local status_log="$ts_dir/status.log"
    printf "===== IMAGE VERSIONS =====\n" > $status_log
    versions >> $status_log
    printf "\n\n===== IMAGE STATUS =====\n" >> $status_log
    status >> $status_log
    printf "\n\n===== IMAGE DETAILS =====\n" >> $status_log
    docker ps -a >> $status_log
    printf "\n\n===== IMAGE RESOURCES =====\n" >> $status_log
    docker stats --no-stream >> $status_log
    printf "\n\n===== IMAGE PROCESSES =====\n" >> $status_log
    docker-compose top >> $status_log
    printf "\n" >> $status_log
    docker exec -it muster-ui-backend \
           docker-compose --log-level ERROR \
           -f /app/muster-gen/docker-compose.yml top >> $status_log
    gzip -9 $status_log

    # go through each images and collect any additional info
    echo "Collecting internal info..."
    mkdir -p $ts_dir/info
    local images=''
    local images=$(docker-compose --log-level ERROR images \
                       | awk '{if (NR>2) {print $1}}')
    images+=" "$(docker exec -it muster-ui-backend \
                        docker-compose --log-level ERROR \
                        -f /app/muster-gen/docker-compose.yml images \
                     | awk '{if (NR>2) {print $1}}')
    for img in $images ; do
        if docker exec $img [ -f "/app/collect-info.sh" ] ; then
            docker exec -it $img /app/collect-info.sh \
                | gzip -9 > $ts_dir/info/$img.log.gz
            if docker exec $img [ -e "/tmp/saved_db" ] ; then
                docker cp -L $img:/tmp/saved_db $ts_dir/info/$img-saved-db
            fi
        fi
    done

    echo "Collecting logs..."
    # collect logs from each image
    mkdir -p $ts_dir/logs
    for img in $images ; do
        docker logs $img --since 24h 2>&1 | gzip -9 > $ts_dir/logs/$img-docker.log.gz
    done

    # collect journald logs
    local username=$(id -u -n)
    if groups $username | grep -q -e "\badm\b" -e "\bsystemd-journal\b" ; then
        journalctl --list-boots > $ts_dir/logs/journald-boots.log
        journalctl --since "24 hours ago" | gzip -9 > $ts_dir/logs/journald-day.log.gz
    else
        printf "WARNING: $username is not in group systemd-journal: journald logs skipped.\n"
        printf "    (Use 'sudo usermod -a -G systemd-journal $username' and logout/login to enable)\n"
    fi

    local file_name="$ts_name.tar"
    mv -f $ts_dir "/tmp/$ts_name"
    tar cf $file_name -C /tmp $ts_name
    echo "Trouble-shoot bundle $file_name generated."
    rm -rf "/tmp/$ts_name"
    echo "Finish Time : $(date "+%Y.%m.%d-%H.%M.%S")"
}

##### main() #####
case $1 in
    help | "") help ;;
    core-logs) core_logs ;;
    debug-off) debug_off ;;
    debug-on) debug_on ;;
    dyn-logs) dyn_logs ;;
    start) start ;;
    status) status ;;
    stop) stop ;;
    versions) versions ;;
    ts-gen) ts_gen ;;
    verify) image_verification ;;
    *)
        echo "Unknown command: $1"
        echo
        help
        exit 1
        ;;
esac
exit 0
