workflow:
  rules:
    - if: >-
        $CI_COMMIT_BRANCH == "staging"
        || ($CI_COMMIT_BRANCH == "main" && $CI_COMMIT_TITLE =~ /Merge branch 'staging' into 'main'/)
      when: always
    - when: never

variables:
  DOCKER_VERSION: '20.10.17'
  MONGO_VERSION: '4.4'
  K8S_NAMESPACE: 'todoapp-devops'
  GOOGLE_CLOUD_SDK_IMAGE_VERSION: '400.0.0'
  GCP_PROJECT_ID: ss22-devops-app
  GCP_COMPUTE_REGION: 'europe-west4'
  CONTAINER_TAG: '${CI_REGISTRY_IMAGE}:${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}'

stages:
  - test
  - build
  - release
  - deploy
  - upgrade-connection

.gke-initialization:
  image: google/cloud-sdk:${GOOGLE_CLOUD_SDK_IMAGE_VERSION}
  before_script:
    # BASE64_GOOGLE_CREDENTIALS is a masked CI/CD GitLab variable
    - echo $BASE64_GOOGLE_CREDENTIALS | base64 -d > ~/service_account.json
    - gcloud auth activate-service-account --key-file ~/service_account.json
    - gcloud config set project ${GCP_PROJECT_ID}
    - gcloud config set compute/zone ${GCP_COMPUTE_REGION}
    - gcloud container clusters get-credentials ${GCP_PROJECT_ID}-gke

run-tests:
  stage: test
  tags:
    - docker-privileged
  image: docker:${DOCKER_VERSION}
  services:
    - name: docker:${DOCKER_VERSION}-dind
      alias: localhost
  variables:
    MONGODB_URL_TEST: mongodb://localhost:27017/todo-app
  script:
    - docker run -p 27017:27017
      --detach
      --name mongo-container
      --rm
      --network=host
      mongo:${MONGO_VERSION}
    - docker build
      --tag "${CONTAINER_TAG}-test"
      --network=host
      --build-arg MONGODB_URL_TEST=$MONGODB_URL_TEST
      --build-arg JWT_SECRET_TEST=$JWT_SECRET
      --file "./app/Dockerfile.test"
      "./app"
  after_script:
    - docker stop mongo-container
    - docker images -a | grep "mongo" | awk '{print $3}' | xargs docker rmi --force
    - docker image rm --force "${CONTAINER_TAG}-test"

build-image:
  stage: build
  tags:
    - docker-privileged
  image: docker:${DOCKER_VERSION}
  services:
    - name: docker:${DOCKER_VERSION}-dind
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker image build
      --tag "${CONTAINER_TAG}"
      --target=build
      "./app"
    - docker push "${CONTAINER_TAG}"
  after_script:
    - docker image rm --force "${CONTAINER_TAG}"

create-release-image:
  stage: release
  rules:
    - if: $CI_COMMIT_REF_NAME =~ /main/
      when: always
    - when: never
  tags:
    - docker-privileged
  image: docker:${DOCKER_VERSION}
  services:
    - name: docker:${DOCKER_VERSION}-dind
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
    - docker pull "${CONTAINER_TAG}"
  script:
    - VERSION=$(grep '"version"' ./app/app-version.json | cut -d '"' -f 4 | head -n 1)
    - docker tag "${CONTAINER_TAG}" "${CI_REGISTRY_IMAGE}:${VERSION}"
    - docker push "${CI_REGISTRY_IMAGE}:${VERSION}"
  after_script:
    - docker image rm --force
        $(docker images --format '{{.Repository}}{{.Tag}}' | grep '${CI_REGISTRY_IMAGE}')

create-release-tag:
  stage: release
  needs:
    - 'create-release-image'
  rules:
    - if: $CI_COMMIT_REF_NAME =~ /main/
      when: 'always'
    - when: never
  tags:
    - docker-privileged
  image: docker:${DOCKER_VERSION}-git
  services:
    - name: docker:${DOCKER_VERSION}-dind
  before_script:
    - PROJECT_URL=$(echo $CI_PROJECT_URL | sed 's/https:\/\///')
    - git remote set-url origin https://oauth2:${APP_CI_ACCESS_TOKEN}@${PROJECT_URL}
  script:
    - VERSION=$(grep '"version"' ./app/app-version.json | cut -d '"' -f 4 | head -n 1)
    - echo ${VERSION}
    - TAG=v${VERSION}
    - git tag $TAG && git push origin $TAG
  allow_failure: true

deploy-new-staging-version:
  extends: .gke-initialization
  stage: deploy
  needs:
    - 'build-image'
  tags:
    - docker
  rules:
    - if: $CI_COMMIT_REF_NAME =~ /staging/
      when: 'always'
      variables:
        ENVIRONMENT_NAME: staging
        STATIC_IP: staging-todoapp-ip
        VERSION: ${CI_PIPELINE_IID}-${CI_COMMIT_SHORT_SHA}
    - when: never
  script:
    - IMAGE="${CI_REGISTRY_IMAGE}:$VERSION"
    - cd "./k8s-manifests"
    - kubectl apply -f gitlab-registry-credentials.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    # JWT_SECRET is a masked CI/CD GitLab variable
    - SECRETS_MANIFEST=$(sed -e "s,{{jwtSecret}},${JWT_SECRET},g" ./secrets.yaml.tmpl)
    - echo "${SECRETS_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
    - kubectl apply -f configmap.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    - DEPLOYMENT_MANIFEST=$(sed -e "s,{{image}},${IMAGE},g" ./deployment.yaml.tmpl)
    - echo "${DEPLOYMENT_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
    - kubectl apply -f service.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    - INGRESS_MANIFEST=$(sed -e "s,{{static-ip-name}},${STATIC_IP},g" -e "s,{{allowHttpFlag}},true,g" -e "s,{{portNumber}},80,g" -e "s,{{suffix}},${ENVIRONMENT_NAME},g" ./ingress.yaml.tmpl)
    - echo "${INGRESS_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -

deploy-new-release:
  extends: .gke-initialization
  stage: deploy
  needs:
    - 'create-release-image'
  rules:
    - if: $CI_COMMIT_REF_NAME =~ /main/
      when: 'always'
      variables:
        ENVIRONMENT_NAME: production
        STATIC_IP: todoapp-ip
    - when: never
  tags:
    - docker
  script:
    - VERSION=$(grep '"version"' ./app/app-version.json | cut -d '"' -f 4 | head -n 1)
    - IMAGE="${CI_REGISTRY_IMAGE}:$VERSION"
    - cd "./k8s-manifests"
    - kubectl apply -f gitlab-registry-credentials.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    # JWT_SECRET is a masked CI/CD GitLab variable
    - SECRETS_MANIFEST=$(sed -e "s,{{jwtSecret}},${JWT_SECRET},g" ./secrets.yaml.tmpl)
    - echo "${SECRETS_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
    - kubectl apply -f configmap.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    - DEPLOYMENT_MANIFEST=$(sed -e "s,{{image}},${IMAGE},g" ./deployment.yaml.tmpl)
    - echo "${DEPLOYMENT_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
    - kubectl apply -f service.yaml --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}"
    - INGRESS_MANIFEST=$(sed -e "s,{{static-ip-name}},${STATIC_IP},g" -e "s,{{allowHttpFlag}},true,g" -e "s,{{portNumber}},80,g"  -e "s,{{suffix}},${ENVIRONMENT_NAME},g" ./ingress.yaml.tmpl)
    - echo "${INGRESS_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -

switch-to-https:
  extends: .gke-initialization
  stage: upgrade-connection
  rules:
    - if: $CI_COMMIT_REF_NAME =~ /staging/
      when: manual
      variables:
        STATIC_IP: staging-todoapp-ip
        ENVIRONMENT_NAME: staging
    - if: $CI_COMMIT_REF_NAME =~ /main/
      when: manual
      variables:
        STATIC_IP: todoapp-ip
        ENVIRONMENT_NAME: production
  tags:
    - docker
  script:
    - INGRESS_MANIFEST=$(sed -e "s,{{static-ip-name}},${STATIC_IP},g" -e "s,{{allowHttpFlag}},false,g" -e "s,{{portNumber}},443,g" -e "s,{{suffix}},${ENVIRONMENT_NAME},g" ./k8s-manifests/ingress.yaml.tmpl)
    - echo "${INGRESS_MANIFEST}" | kubectl apply --namespace "${ENVIRONMENT_NAME}-${K8S_NAMESPACE}" --filename -
  when: manual