#!/bin/bash
#此脚本实现部署本地镜像仓库并拉取ai-deployer项目镜像至该镜像仓库的功能,请配置以下5个参数并执行该脚本.部署要求/data目录可用空间大于100G

#工作目录,ai-deployer源码实际所在路径
BASE=/opt/ai-deployer
#AI平台版本号
VERSION=v3.5
#本地镜像仓库IP地址
HARBOR_NODE_IP=127.0.0.1
###使用域名部署可以解决主机IP变更时需要修改镜像仓库地址的问题,如果不使用域名可以将此参数的值配置为IP地址
HARBOR_DOMAIN=registry.local
###如果镜像仓库需要供局域网外部访问,需要配置为镜像仓库节点的外部IP,如无需要则配置成内网IP即可
HARBOR_EXTERNAL_DOMAIN=127.0.0.1
#本地镜像仓库端口
HARBOR_PORT=38080
#本地镜像仓库admin用户密码
HARBOR_ADMIN_PASSWORD=Harbor12345


#====================================================
MINIO_URL=https://minio.mopaas.com
REGISTRY_URL=https://registry.mopaas.com
REGISTRY_DOMAIN=${REGISTRY_URL#*//}
REGISTRY_LOCAL="$HARBOR_DOMAIN:$HARBOR_PORT"
DOCKER_VER=19.03.14
HARBOR_VER=v1.10.16
REGISTRY_MIRROR=CN




function logger() {
  TIMESTAMP=$(date +'%Y-%m-%d %H:%M:%S')
  case "$1" in
    debug)
      echo -e "$TIMESTAMP \033[36mDEBUG\033[0m $2"
      ;;
    info)
      echo -e "$TIMESTAMP \033[32mINFO\033[0m $2"
      ;;
    warn)
      echo -e "$TIMESTAMP \033[33mWARN\033[0m $2"
      ;;
    error)
      echo -e "$TIMESTAMP \033[31mERROR\033[0m $2"
      ;;
    *)
      ;;
  esac
}


function download_docker() {
  if [[ "$REGISTRY_MIRROR" == CN ]];then
    DOCKER_URL="$MINIO_URL/ai-deployer/docker-${DOCKER_VER}.tgz"
  else
    DOCKER_URL="$MINIO_URL/ai-deployer/docker-${DOCKER_VER}.tgz"
  fi

  if [[ -f "$BASE/down/docker-${DOCKER_VER}.tgz" ]];then
    logger info "docker离线包已下载"
  else
    logger info "正在下载docker离线包"
    if [[ -e /usr/bin/curl ]];then
      curl -C- -O --retry 3 "$DOCKER_URL" || { logger error "下载docker离线包失败"; exit 1; }
    else
      wget -c "$DOCKER_URL" || { logger error "下载docker离线包失败"; exit 1; }
    fi
    /bin/mv -f "./docker-$DOCKER_VER.tgz" "$BASE/down"
  fi

  tar zxf "$BASE/down/docker-$DOCKER_VER.tgz" -C "$BASE/down" && \
  /bin/cp -f "$BASE"/down/docker/* "$BASE/bin" && \
  /bin/mv -f "$BASE"/down/docker/* /opt/kube/bin && \
  ln -sf /opt/kube/bin/docker /bin/docker
}

function install_docker() {
  # check if a container runtime is already installed
  systemctl status docker|grep Active|grep -q running && { logger info "docker正在运行中"; return 0; }
  logger debug "生成docker service文件"
  cat > /etc/systemd/system/docker.service << EOF
[Unit]
Description=Docker Application Container Engine
Documentation=http://docs.docker.io
[Service]
Environment="PATH=/opt/kube/bin:/bin:/sbin:/usr/bin:/usr/sbin"
ExecStartPre=/sbin/iptables -F
ExecStartPre=/sbin/iptables -X
ExecStartPre=/sbin/iptables -F -t nat
ExecStartPre=/sbin/iptables -X -t nat
ExecStartPre=/sbin/iptables -F -t raw
ExecStartPre=/sbin/iptables -X -t raw
ExecStartPre=/sbin/iptables -F -t mangle
ExecStartPre=/sbin/iptables -X -t mangle
ExecStart=/opt/kube/bin/dockerd
ExecStartPost=/sbin/iptables -P INPUT ACCEPT
ExecStartPost=/sbin/iptables -P OUTPUT ACCEPT
ExecStartPost=/sbin/iptables -P FORWARD ACCEPT
ExecReload=/bin/kill -s HUP \$MAINPID
Restart=on-failure
RestartSec=5
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
Delegate=yes
KillMode=process
[Install]
WantedBy=multi-user.target
EOF
  
  # configuration for dockerd
  mkdir -p /etc/docker
  logger debug "生成docker配置文件: /etc/docker/daemon.json"
  if [[ "$REGISTRY_MIRROR" == CN ]];then
    cat > /etc/docker/daemon.json << EOF
{
  "registry-mirrors": [
    "https://docker.mirrors.ustc.edu.cn",
    "http://hub-mirror.c.163.com"
  ],
  "insecure-registries": ["__registry__external__domain__", "__registry__local__domain__", "__registry__domain__"],
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF
    sed -i "s/__registry__external__domain__/$HARBOR_EXTERNAL_DOMAIN:$HARBOR_PORT/g" "/etc/docker/daemon.json"
    sed -i "s/__registry__local__domain__/$REGISTRY_LOCAL/g" "/etc/docker/daemon.json"
    sed -i "s/__registry__domain__/$REGISTRY_DOMAIN/g" "/etc/docker/daemon.json"   
  else
    cat > /etc/docker/daemon.json << EOF
{
  "max-concurrent-downloads": 10,
  "log-driver": "json-file",
  "log-level": "warn",
  "log-opts": {
    "max-size": "10m",
    "max-file": "3"
    },
  "data-root": "/var/lib/docker"
}
EOF
  fi

  if [[ -e /etc/centos-release || -e /etc/redhat-release ]]; then
    logger debug "关闭selinux in CentOS/Redhat"
    getenforce|grep Disabled || setenforce 0
    sed -i 's/^SELINUX=.*$/SELINUX=disabled/g' /etc/selinux/config
  fi

  logger debug "enable and start docker"
  systemctl enable docker
  systemctl daemon-reload && systemctl restart docker && sleep 4
}

function download_harbor() {
  if [[ -f /data/harbor-offline-installer-$HARBOR_VER.tgz ]];then
    logger info "harbor离线包已下载!"
  else
    logger info "正在下载harbor离线包"
    HARBOR_URL="$MINIO_URL/ai-deployer/harbor-offline-installer-$HARBOR_VER.tgz"
    curl -C- -O --retry 3 "$HARBOR_URL" || { logger error "下载harbor离线包失败"; exit 1; }
    /bin/mv -f "./harbor-offline-installer-$HARBOR_VER.tgz" "/data"
    tar zxf  "/data/harbor-offline-installer-$HARBOR_VER.tgz" -C "/data"
  fi
}

function install_harbor() {
  if [[ -d /data/registry ]];then
    logger info "harbor已安装!"
  else
    #生成随机数据库密码
    DB_PASSWORD=$(< /dev/urandom tr -dc 0-9A-Za-z-_ | head -c 16)
    #下载docker-compose
    logger info "正在下载docker-compose"
    curl -L $MINIO_URL/ai-deployer/docker-compose > /opt/kube/bin/docker-compose && chmod +x /opt/kube/bin/docker-compose
    #导入harbor离线镜像
    logger info "正在导入harbor离线镜像"
    cd /data/harbor && docker load -i /data/harbor/harbor.$HARBOR_VER.tar.gz
    docker load -i /data/harbor/harbor-core.tar
    docker load -i /data/harbor/harbor-portal.tar

    #准备harbor配置文件
    /bin/cp -f "$BASE/roles/harbor/templates/harbor-v1.10.yml.j2" "/data/harbor/harbor.yml"
    sed -i "s/{{ HARBOR_HOSTNAME }}/$HARBOR_EXTERNAL_DOMAIN/g" /data/harbor/harbor.yml
    sed -i "s/{{ HARBOR_TLS_PORT }}/$HARBOR_PORT/g" /data/harbor/harbor.yml
    sed -i "s/{{ HARBOR_ADMIN_PASSWORD }}/$HARBOR_ADMIN_PASSWORD/g" /data/harbor/harbor.yml
    sed -i "s/{{ db_password_gen.stdout }}/$DB_PASSWORD/g" /data/harbor/harbor.yml
    #准备harbor服务的systemd unit文件
    /bin/cp -f "$BASE/roles/harbor/templates/harbor.service.j2" "/etc/systemd/system/harbor.service"
    systemctl enable harbor
    #重启docker
    systemctl restart docker
    #准备安装harbor
    cd /data/harbor && export PATH=/opt/kube/bin:$PATH && ./prepare --with-chartmuseum --with-clair >> /tmp/harbor-`date +'%Y%m%d%H%M%S'`.log 2>&1
    #修改镜像
    sed -i "s/harbor-core:v1.10.16/harbor-core:v1.10.16-1/g" docker-compose.yml
    sed -i "s/harbor-portal:v1.10.16/harbor-portal:v1.10.16-1/g" docker-compose.yml
    #安装harbor
    /opt/kube/bin/docker-compose -f docker-compose.yml up -d
    #等待harbor运行
    logger info "正在等待harbor运行......"
    harbor_status=$(/opt/kube/bin/docker ps -a|grep nginx-photon)
    until [[ $harbor_status =~ healthy ]]
    do
      harbor_status=$(/opt/kube/bin/docker ps -a|grep nginx-photon)
      if [[ $harbor_status =~ healthy ]];then
        logger info "harbor正在运行中"
      fi
    done

    #创建harbor项目
    curl -s -u "admin:$HARBOR_ADMIN_PASSWORD" -X POST "$HARBOR_DOMAIN:$HARBOR_PORT/api/projects" -H "accept: application/json" -H "Content-Type: application/json" -d '{ "project_name": "frame_system", "metadata": { "public": "true" }}'
    curl -s -u "admin:$HARBOR_ADMIN_PASSWORD" -X POST "$HARBOR_DOMAIN:$HARBOR_PORT/api/projects" -H "accept: application/json" -H "Content-Type: application/json" -d '{ "project_name": "service_system", "metadata": { "public": "true" }}'
    curl -s -u "admin:$HARBOR_ADMIN_PASSWORD" -X POST "$HARBOR_DOMAIN:$HARBOR_PORT/api/projects" -H "accept: application/json" -H "Content-Type: application/json" -d '{ "project_name": "ai", "metadata": { "public": "true" }}'
    curl -s -u "admin:$HARBOR_ADMIN_PASSWORD" -X POST "$HARBOR_DOMAIN:$HARBOR_PORT/api/projects" -H "accept: application/json" -H "Content-Type: application/json" -d '{ "project_name": "ai-deployer", "metadata": { "public": "true" }}'
  fi
}

function get_images() {
  if [[ -f /mnt/got-images ]];then
    logger info "镜像已经拉取到本地仓库"
  else
    logger debug "正在拉取镜像至本地仓库"
    #PROJECT_ID=$(curl -s -X GET "$REGISTRY_URL/api/projects?name=ai-deploy" -H "accept: application/json"|grep "project_id"|awk 'NR<2{print $2}'|cut -d',' -f1)
    #REPOS=$(curl -s -X GET --header 'Accept: application/json' "$REGISTRY_URL/api/repositories?project_id=$PROJECT_ID"|grep "name"|awk -F '"' '{print $4}')
    #登录本地镜像仓库
    docker login $REGISTRY_LOCAL -u admin -p $HARBOR_ADMIN_PASSWORD
    #拉取线上镜像仓库ai-deployer项目下的镜像
    #for repo in $REPOS
    #do
    #  TAGS=$(curl -s -X GET --header 'Accept: application/json' "$REGISTRY_URL/api/repositories/$repo/tags"|grep -A 4 'digest'|grep \"name\"|awk -F '"' '{print $4}'|sort -r)
    #  image=$(echo $repo|awk -F "/" '{print $2}')
    #  for tag in $TAGS
    #  do
    #    logger info "正在拉取$repo:$tag并推送至本地仓库"
    #    docker pull $REGISTRY_DOMAIN/$repo:$tag
    #    docker tag $REGISTRY_DOMAIN/$repo:$tag $REGISTRY_LOCAL/$repo:$tag
    #    docker push $REGISTRY_LOCAL/$repo:$tag
        #echo "$REGISTRY_LOCAL/$repo:$tag flag=0" >> images-tag
        #docker save $REGISTRY_LOCAL/$repo:$tag > /opt/docker/$image-$tag.tar.gz
    #  done
    #  echo "======================================="
    #done

    images=$(cat $BASE/images-list)
    for image in $images
    do
      docker pull $REGISTRY_DOMAIN/$image
      docker tag $REGISTRY_DOMAIN/$image $REGISTRY_LOCAL/$image
      docker push $REGISTRY_LOCAL/$image
    done
    
    #拉取AI平台应用镜像
    docker pull $REGISTRY_DOMAIN/ai/ai-user:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-user:$VERSION $REGISTRY_LOCAL/ai/ai-user:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-user:$VERSION
    
    docker pull $REGISTRY_DOMAIN/ai/ai-mgr-web:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-mgr-web:$VERSION $REGISTRY_LOCAL/ai/ai-mgr-web:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-mgr-web:$VERSION
    
    docker pull $REGISTRY_DOMAIN/ai/ai-mgr:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-mgr:$VERSION $REGISTRY_LOCAL/ai/ai-mgr:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-mgr:$VERSION
    
    docker pull $REGISTRY_DOMAIN/ai/ai-user-web:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-user-web:$VERSION $REGISTRY_LOCAL/ai/ai-user-web:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-user-web:$VERSION
   
    docker pull $REGISTRY_DOMAIN/ai/ai-kop:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-kop:$VERSION $REGISTRY_LOCAL/ai/ai-kop:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-kop:$VERSION

    docker pull $REGISTRY_DOMAIN/ai/ai-kop-web:$VERSION
    docker tag $REGISTRY_DOMAIN/ai/ai-kop-web:$VERSION $REGISTRY_LOCAL/ai/ai-kop-web:$VERSION
    docker push $REGISTRY_LOCAL/ai/ai-kop-web:$VERSION
    
    docker pull $REGISTRY_DOMAIN/ai/page-render:v1
    docker tag $REGISTRY_DOMAIN/ai/page-render:v1 $REGISTRY_LOCAL/ai/page-render:v1
    docker push $REGISTRY_LOCAL/ai/page-render:v1
    
    #拉取AI框架镜像
    TENSORFLOW_GPU_TAG=2.11.0-cuda11.2-py38
    PYTORCH_GPU_TAG=1.13.1-cuda11.4-py38
    PYTORCH_SERVING_GPU_TAG=0.10.0-gpu
    PYTORCH_SERVING_TAG=0.10.0
    AI_EXPLORER_TAG=v1.10
    LABELSTUDIO_TAG=1.7.3
    ADAPT_JOB_TAG=v3.5
    
    docker pull $REGISTRY_DOMAIN/frame_system/tensorflow-gpu:$TENSORFLOW_GPU_TAG
    docker tag $REGISTRY_DOMAIN/frame_system/tensorflow-gpu:$TENSORFLOW_GPU_TAG  $REGISTRY_LOCAL/frame_system/tensorflow-gpu:$TENSORFLOW_GPU_TAG
    docker push $REGISTRY_LOCAL/frame_system/tensorflow-gpu:$TENSORFLOW_GPU_TAG
   
    docker pull $REGISTRY_DOMAIN/frame_system/pytorch-gpu:$PYTORCH_GPU_TAG
    docker tag $REGISTRY_DOMAIN/frame_system/pytorch-gpu:$PYTORCH_GPU_TAG  $REGISTRY_LOCAL/frame_system/pytorch-gpu:$PYTORCH_GPU_TAG
    docker push $REGISTRY_LOCAL/frame_system/pytorch-gpu:$PYTORCH_GPU_TAG

    docker pull $REGISTRY_DOMAIN/frame_system/pytorch-serving:$PYTORCH_SERVING_GPU_TAG
    docker tag $REGISTRY_DOMAIN/frame_system/pytorch-serving:$PYTORCH_SERVING_GPU_TAG $REGISTRY_LOCAL/frame_system/pytorch-serving:$PYTORCH_SERVING_GPU_TAG
    docker push $REGISTRY_LOCAL/frame_system/pytorch-serving:$PYTORCH_SERVING_GPU_TAG

    docker pull $REGISTRY_DOMAIN/frame_system/pytorch-serving:$PYTORCH_SERVING_TAG
    docker tag $REGISTRY_DOMAIN/frame_system/pytorch-serving:$PYTORCH_SERVING_TAG $REGISTRY_LOCAL/frame_system/pytorch-serving:$PYTORCH_SERVING_TAG
    docker push $REGISTRY_LOCAL/frame_system/pytorch-serving:$PYTORCH_SERVING_TAG

    docker pull $REGISTRY_DOMAIN/ai/adapt-job:$ADAPT_JOB_TAG
    docker tag $REGISTRY_DOMAIN/ai/adapt-job:$ADAPT_JOB_TAG $REGISTRY_LOCAL/ai/adapt-job:$ADAPT_JOB_TAG
    docker push $REGISTRY_LOCAL/ai/adapt-job:$ADAPT_JOB_TAG

    docker pull $REGISTRY_DOMAIN/ai/ai-explorer:$AI_EXPLORER_TAG
    docker tag $REGISTRY_DOMAIN/ai/ai-explorer:$AI_EXPLORER_TAG  $REGISTRY_LOCAL/ai/ai-explorer:$AI_EXPLORER_TAG
    docker push $REGISTRY_LOCAL/ai/ai-explorer:$AI_EXPLORER_TAG

    docker pull $REGISTRY_DOMAIN/ai/label-studio:$LABELSTUDIO_TAG
    docker tag $REGISTRY_DOMAIN/ai/label-studio:$LABELSTUDIO_TAG $REGISTRY_LOCAL/ai/label-studio:$LABELSTUDIO_TAG
    docker push $REGISTRY_LOCAL/ai/label-studio:$LABELSTUDIO_TAG
    touch /mnt/got-images
    touch /mnt/got-frame-images
    logger info "已拉取镜像至本地仓库"
  fi
} 

function main() {
  mkdir -p /opt/kube/bin "$BASE/down" "$BASE/bin" /data
  echo "$HARBOR_NODE_IP $HARBOR_DOMAIN" >> /etc/hosts
  download_docker
  install_docker
  download_harbor
  install_harbor
  get_images
}
main "$@"

