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