From 2df0436f4df191367bad38e0e3b18af55782258a Mon Sep 17 00:00:00 2001 From: Fernando-Granato <117268775+Fernando-Granato@users.noreply.github.com> Date: Wed, 2 Aug 2023 16:05:56 +0200 Subject: [PATCH] feat(.devops) added devops pipeline (#2) * feat: added repository structure * feat: added PULL REQUEST CHANGELOG TEMPLATE * feat: added helm value for dev and uat * feat: changed chart name * feat: added prod helm value * feat: added code review pipeline * feat: added deploy pipeline * feat: added azure template * fix(version):fixed project version * fix(main class reference):fixed main class reference name * fix(Dockerfile): add comment for COPY api-spec api-spec --- .../azure-templates/chart-current-version.yml | 18 + .../azure-templates/gradle-github-release.yml | 153 +++++ .../helm-microservice-chart-deploy.yml | 88 +++ .devops/code-review-pipelines.yml | 62 ++ .devops/deploy-pipelines.yml | 575 ++++++++++++++++++ 5 files changed, 896 insertions(+) create mode 100644 .devops/azure-templates/chart-current-version.yml create mode 100644 .devops/azure-templates/gradle-github-release.yml create mode 100644 .devops/azure-templates/helm-microservice-chart-deploy.yml create mode 100644 .devops/code-review-pipelines.yml create mode 100644 .devops/deploy-pipelines.yml diff --git a/.devops/azure-templates/chart-current-version.yml b/.devops/azure-templates/chart-current-version.yml new file mode 100644 index 0000000..364a92b --- /dev/null +++ b/.devops/azure-templates/chart-current-version.yml @@ -0,0 +1,18 @@ +# Read the current chart version and output it two variables +# -> 'chart_current_version.version' : the Chart version +# -> 'chart_current_version.appVersion': the application version + +steps: + - task: Bash@3 + name: chart_current_version + displayName: 'Read chart current version' + inputs: + targetType: "inline" + script: | + CHART_FILE="helm/Chart.yaml" + version=$(yq -r '.version' $CHART_FILE) + appVersion=$(yq -r '.appVersion' $CHART_FILE) + echo "##vso[task.setvariable variable=version;isOutput=true]$version" + echo "##vso[task.setvariable variable=appVersion;isOutput=true]$appVersion" + failOnStderr: true + \ No newline at end of file diff --git a/.devops/azure-templates/gradle-github-release.yml b/.devops/azure-templates/gradle-github-release.yml new file mode 100644 index 0000000..a0d8104 --- /dev/null +++ b/.devops/azure-templates/gradle-github-release.yml @@ -0,0 +1,153 @@ +# Node Github Relase steps +# Mark a release on the project repository, with version bump and tag, +# and publish a release on Github + +parameters: + + # Versioning parameters + - name: 'semver' + type: string + values: + - major + - minor + - patch + - none + + # Versioning parameters + - name: 'semver_chart' + type: string + values: + - major + - minor + - patch + - none + + # This is the branch in which we will push the release tag. + # It'll be master, but it can be overridden + # Basically, this variable is used to enforce the fact that we use the very same branch in different steps + - name: 'release_branch' + type: string + default: main + + # Github parameters + - name: 'gitUsername' + type: string + - name: 'gitEmail' + type: string + - name: 'gitHubConnection' + type: string + +steps: + # setup git author + - script: | + git config --global user.email "${{ parameters.gitEmail }}" && git config --global user.name "${{ parameters.gitUsername }}" + displayName: 'Git setup' + + # Without this step, changes would be applied to a detached head + - script: | + git checkout ${{ parameters.release_branch }} + displayName: 'Checkout release branch' + - task: JavaToolInstaller@0 + displayName: 'Installing JDK' + inputs: + versionSpec: "17" + jdkArchitectureOption: x64 + jdkSourceOption: 'PreInstalled' + # bump version + - ${{ if ne(parameters['semver'], 'none') }}: + - task: Gradle@3 + displayName: 'Bump version' + name: bump_version + inputs: + tasks: 'help' + options: 'incrementVersion --versionIncrementType=${{ parameters.semver }}' + jdkVersionOption: '1.17' + - task: Bash@3 + name: next_version_app + displayName: 'Set release variables' + inputs: + targetType: "inline" + script: | + version=$(./gradlew printVersion -Psnapshot=false -q | head -n1 | cut -d' ' -f2) + echo "##vso[task.setvariable variable=value;isOutput=true]$version" + git add build.gradle.kts + failOnStderr: true + - task: Bash@3 + displayName: Update Version Values Helm + name: update_version_helm + inputs: + targetType: 'inline' + script: | + for i in helm/values-*.yaml; do + [ -f "$i" ] || break + yq -i ".microservice-chart.image.tag = \"$(next_version_app.value)\"" "$i" + git add "$i" + done + - task: Bash@3 + name: update_app_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + CHART_FILE="helm/Chart.yaml" + if [[ -f "$CHART_FILE" ]]; then + yq -i ".appVersion = \"$(next_version_app.value)\"" "$CHART_FILE" + git add "$CHART_FILE" + fi + + - task: Bash@3 + name: setup_semver_utility + displayName: 'Setup semver utility' + inputs: + targetType: "inline" + script: | + yarn global add semver + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + RELEASE_CHART_SEMVER=${{parameters.semver_chart}} + CHART_FILE="helm/Chart.yaml" + CURRENT_CHART_VERSION=$(yq -r '.version' $CHART_FILE) + if [[ -f "$CHART_FILE" ]]; then + yq -i ".version = \"$(semver $CURRENT_CHART_VERSION -i $RELEASE_CHART_SEMVER )\"" "$CHART_FILE" + git add "$CHART_FILE" + fi + - task: Bash@3 + name: next_version_chart + displayName: 'Set release chart variables' + inputs: + targetType: "inline" + script: | + CHART_FILE="helm/Chart.yaml" + version=$(yq -r '.version' $CHART_FILE) + echo "##vso[task.setvariable variable=value;isOutput=true]$version" + failOnStderr: true + # push new version + - script: | + git commit -m "Bump version [skip ci]" + git push origin ${{ parameters.release_branch }} + displayName: 'Push to the release branch' + + - script: | + HEAD_SHA=$(git rev-parse HEAD) + TAG="$(next_version_chart.value)" + TITLE="Release $(next_version_chart.value)" + echo "##vso[task.setvariable variable=title]$TITLE" + echo "##vso[task.setvariable variable=sha]$HEAD_SHA" + echo "##vso[task.setvariable variable=tag]$TAG" + displayName: 'Set release variables' + + # create new release + - task: GitHubRelease@0 + inputs: + gitHubConnection: ${{ parameters.gitHubConnection }} + repositoryName: $(Build.Repository.Name) + action: create + target: $(sha) + tagSource: manual + tag: $(tag) + title: $(title) + addChangelog: true diff --git a/.devops/azure-templates/helm-microservice-chart-deploy.yml b/.devops/azure-templates/helm-microservice-chart-deploy.yml new file mode 100644 index 0000000..749bb8d --- /dev/null +++ b/.devops/azure-templates/helm-microservice-chart-deploy.yml @@ -0,0 +1,88 @@ +parameters: + # Required + - name: "DO_DEPLOY" + type: boolean + - name: "ENV" + type: string + - name: "KUBERNETES_SERVICE_CONN" + type: string + - name: "NAMESPACE" + type: string + - name: "APP_NAME" + type: string + - name: "VALUE_FILE" + type: string + - name: "GREEN_VERSION" + type: string + # Optional + - name: "DO_BLUE_GREEN_DEPLOY" + type: boolean + default: false + - name: "BLUE_VERSION" + type: string + default: "none" + - name: "CHART_TYPE" + type: string + default: "filepath" + - name: "CHART_PATH" + type: string + default: "helm" + - name: "WAIT_FOR_EXECUTION" + type: boolean + default: true + - name: "ARGUMENTS" + type: string + default: "--timeout 5m0s" + - name: "APPINSIGHTS_SERVICE_CONN" + type: string + default: "none" + - name: "APPINSIGHTS_RESOURCE_ID" + type: string + default: "none" + +steps: + - task: HelmDeploy@0 + displayName: Deploy on ${{ parameters.ENV }} BLUEGREEN + condition: and(succeeded(), eq(${{ parameters.DO_DEPLOY }}, True)) + inputs: + kubernetesServiceEndpoint: ${{ parameters.KUBERNETES_SERVICE_CONN }} + namespace: ${{ parameters.NAMESPACE }} + command: upgrade + chartType: ${{ parameters.CHART_TYPE }} + chartPath: ${{ parameters.CHART_PATH }} + chartName: ${{ parameters.APP_NAME }} + releaseName: ${{ parameters.APP_NAME }} + valueFile: ${{ parameters.VALUE_FILE }} + install: true + waitForExecution: ${{ parameters.WAIT_FOR_EXECUTION }} + arguments: ${{ parameters.ARGUMENTS }} + overrideValues: microservice-chart.image.tag=${{ parameters.GREEN_VERSION }},microservice-chart.canaryDelivery.create=${{ parameters.DO_BLUE_GREEN_DEPLOY }},microservice-chart.canaryDelivery.deployment.image.tag=${{ parameters.BLUE_VERSION }} + - template: ./chart-current-version.yml + - ${{ if ne(parameters['APPINSIGHTS_SERVICE_CONN'], 'none') }}: + - task: AzureCLI@2 + displayName: Release annotations + condition: and(succeeded(), eq(${{ parameters.DO_DEPLOY }}, True)) + inputs: + azureSubscription: '${{ parameters.APPINSIGHTS_SERVICE_CONN }}' + addSpnToEnvironment: true + scriptType: 'bash' + scriptLocation: 'inlineScript' + failOnStandardError: true + inlineScript: | + echo "[INFO] Creating release annotation in Application Insights" + + APPINSIGHTS_ID=${{ parameters.APPINSIGHTS_RESOURCE_ID }} + UUID=$(uuidgen) + releaseName="${{ parameters.APP_NAME }}-${{ parameters.ENV }}" + releaseDescription="$(chart_current_version.appVersion)" + triggerBy="Azure DevOps" + eventTime=$(date -u '+%Y-%m-%dT%H:%M:%S') + category="Deployment" + label="Success" + + body='{ "Id": "'$UUID'", "AnnotationName": "'$releaseName'", "EventTime":"'$eventTime'", "Category":"'$category'", "Properties":"{ \"ReleaseName\":\"'$releaseName'\", \"ReleaseDescription\" : \"'$releaseDescription'\", \"TriggerBy\": \"'$triggerBy'\" }"}' + + # echo "[INFO] body: $body" + # echo "[INFO] APPINSIGHTS_ID: $APPINSIGHTS_ID" + + az rest --method put --uri "$APPINSIGHTS_ID/Annotations?api-version=2015-05-01" --body "$body" -o none \ No newline at end of file diff --git a/.devops/code-review-pipelines.yml b/.devops/code-review-pipelines.yml new file mode 100644 index 0000000..d9e2c48 --- /dev/null +++ b/.devops/code-review-pipelines.yml @@ -0,0 +1,62 @@ +variables: + BRANCH_NAME: $[ replace(variables['System.PullRequest.SourceBranch'], 'refs/heads/', '') ] + GRADLE_USER_HOME: $(Pipeline.Workspace)/.gradle +trigger: none + +pool: + vmImage: ubuntu-latest + +stages: + - stage: BuildEndUnitTest + jobs: + - job: make_buildEndUnitTest + steps: + - task: Cache@2 + inputs: + key: 'gradle | "$(Agent.OS)" | **/build.gradle.kts' # Swap build.gradle.kts for build.gradle when using Groovy + restoreKeys: | + gradle | "$(Agent.OS)" + gradle + path: $(GRADLE_USER_HOME) + displayName: Configure gradle caching + + - task: SonarCloudPrepare@1 + displayName: 'Prepare SonarCloud analysis configuration' + inputs: + SonarCloud: '$(SONARCLOUD_SERVICE_CONN)' + organization: '$(SONARCLOUD_ORG)' + scannerMode: Other + extraProperties: | + sonar.projectKey=$(SONARCLOUD_PROJECT_KEY) + sonar.projectName=$(SONARCLOUD_PROJECT_NAME) + sonar.coverage.exclusions=**/config/*,**/*Mock*,**/model/* + sonar.coverage.jacoco.xmlReportPaths=./build/reports/jacoco/test/jacocoTestReport.xml + sonar.junit.reportPaths=./build/test-results/test + + - task: Gradle@3 + inputs: + gradleWrapperFile: 'gradlew' # string. Alias: wrapperScript. Required. Gradle wrapper. Default: gradlew. + tasks: 'build' # string. Required. Tasks. Default: build. + publishJUnitResults: true + testResultsFiles: '**/TEST-*.xml' # string. Required when publishJUnitResults = true. Test results files. Default: **/TEST-*.xml. + codeCoverageToolOption: 'None' # 'None' | 'Cobertura' | 'JaCoCo'. Alias: codeCoverageTool. Code coverage tool. Default: None. + codeCoverageClassFilesDirectories: 'build/classes/main/' # string. Alias: classFilesDirectories. Required when codeCoverageTool != None. Class files directories. Default: build/classes/main/. + javaHomeOption: 'JDKVersion' # 'JDKVersion' | 'Path'. Alias: javaHomeSelection. Required. Set JAVA_HOME by. Default: JDKVersion. + jdkVersionOption: '1.17' # 'default' | '1.11' | '1.10' | '1.9' | '1.8' | '1.7' | '1.6'. Alias: jdkVersion. Optional. Use when javaHomeSelection = JDKVersion. JDK version. Default: default. + sonarQubeRunAnalysis: true + + - task: SonarCloudPublish@1 + displayName: 'Publish SonarCloud results on build summary' + inputs: + pollingTimeoutSec: '300' + - script: | + # stop the Gradle daemon to ensure no files are left open (impacting the save cache operation later) + ./gradlew --stop + displayName: Gradlew stop + + - task: PublishCodeCoverageResults@1 + inputs: + codeCoverageTool: 'JaCoCo' + summaryFileLocation: 'build/reports/jacoco/test/jacocoTestReport.xml' + reportDirectory: 'build/reports/jacoco/test/html' + displayName: 'Publish Code Coverage on Azure Devops' \ No newline at end of file diff --git a/.devops/deploy-pipelines.yml b/.devops/deploy-pipelines.yml new file mode 100644 index 0000000..498c259 --- /dev/null +++ b/.devops/deploy-pipelines.yml @@ -0,0 +1,575 @@ +# Deploy to Azure Kubernetes Service: +# - DEV +# - UAT -> PROD +# Build and push image to Azure Container Registry; Deploy to Azure Kubernetes Service +# https://docs.microsoft.com/azure/devops/pipelines/languages/docker + +parameters: + - name: 'DEV_DEPLOY' + displayName: 'Deploy on DEV environment' + type: boolean + default: True + values: + - False + - True + - name: 'UAT_PROD_DEPLOY' + displayName: 'Deploy on UAT environment with PROD promotion' + type: boolean + default: False + values: + - False + - True + - name: 'SKIP_BUILD' + displayName: 'Check this flag to skip build and proceed to deploy a docker image previously built' + type: boolean + default: False + values: + - False + - True + - name: 'RELEASE_SEMVER' + displayName: 'When packing a release, define the version bump to apply (release is done automatically when deploying on UAT and skipped on DEV) ' + type: string + values: + - major + - minor + - patch + - none + default: patch + - name: 'RELEASE_CHART_SEMVER' + displayName: 'When upgrading helm chart, define the version bump to apply' + type: string + values: + - major + - minor + - patch + default: patch + - name: "FORCE_REPLACE_DOCKER_IMAGE" + displayName: "Force the existing docker image to be replaced (latest tag)" + type: boolean + default: False + values: + - False + - True + - name: "SKIP_RELEASE" + displayName: "Skip release" + type: boolean + default: False + values: + - False + - True + - name: "UAT_SKIP_BLUE_DEPLOYMENT" + displayName: "Skip blue/green UAT deployment strategy: activating this parameter no blue version will be created and the pipeline proceed building and deploy artifact green version" + type: boolean + default: True + values: + - False + - True + - name: "PROD_SKIP_BLUE_DEPLOYMENT" + displayName: "Skip blue/green PROD deployment strategy: activating this parameter no blue version will be created and the pipeline proceed building and deploy artifact green version" + type: boolean + default: True + values: + - False + - True + +pool: + vmImage: ubuntu-latest + +# --- START Deploy DEV --- # +stages: + + - stage: 'Build_for_DEV' + displayName: 'Build for DEV' + condition: and(succeeded(), not(${{ parameters.SKIP_BUILD }}), eq(${{parameters.DEV_DEPLOY}}, true)) + jobs: + - job: Build_docker + displayName: Build docker with Build.SourceVersion as TAG + steps: + - template: templates/docker-release/template.yaml@pagopaCommons + parameters: + CONTAINER_REGISTRY_SERVICE_CONN: $(DEV_CONTAINER_REGISTRY_SERVICE_CONN) + CONTAINER_REGISTRY_FQDN: $(DEV_CONTAINER_NAMESPACE) + DOCKER_IMAGE_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + DOCKER_IMAGE_TAG: $(Build.SourceVersion) + FORCE_REPLACE_DOCKER_IMAGE: ${{ parameters.FORCE_REPLACE_DOCKER_IMAGE }} + + - stage: 'Deploy_for_DEV' + displayName: 'Deploy DEV' + dependsOn: Build_for_DEV + condition: or(succeeded(), ${{ parameters.SKIP_BUILD }}, eq(${{parameters.DEV_DEPLOY}}, true) ) + jobs: + - deployment: "deploy" + environment: 'DEV' + strategy: + runOnce: + deploy: + steps: + - checkout: self + displayName: "Checkout" + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + helm repo add microservice-chart https://pagopa.github.io/aks-microservice-chart-blueprint + helm dep build helm + - template: azure-templates/helm-microservice-chart-deploy.yml + parameters: + DO_DEPLOY: true + ENV: 'DEV' + KUBERNETES_SERVICE_CONN: $(DEV_KUBERNETES_SERVICE_CONN) + NAMESPACE: ecommerce + APP_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + VALUE_FILE: "helm/values-dev.yaml" + GREEN_VERSION: $(Build.SourceVersion) + # --- END Deploy DEV --- # + + # --- START Deploy UAT --- # + - stage: "Build_release_candidate" + displayName: 'Build release candidate' + dependsOn: [] + condition: + and( + succeeded(), + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/main'), + startsWith(variables['Build.SourceBranch'], 'refs/tags') + ) + ) + jobs: + - job: "build" + displayName: 'Build release candidate docker image' + steps: + - template: templates/docker-release/template.yaml@pagopaCommons + parameters: + CONTAINER_REGISTRY_SERVICE_CONN: $(UAT_CONTAINER_REGISTRY_SERVICE_CONN) + CONTAINER_REGISTRY_FQDN: $(UAT_CONTAINER_NAMESPACE) + DOCKER_IMAGE_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + DOCKER_IMAGE_TAG: $(Build.SourceVersion) + FORCE_REPLACE_DOCKER_IMAGE: ${{ parameters.FORCE_REPLACE_DOCKER_IMAGE }} + - template: azure-templates/chart-current-version.yml + + - stage: "Deploy_UAT_Blue" + displayName: 'UAT blue deployment' + dependsOn: Build_release_candidate + condition: + and( + succeeded(), + eq(${{parameters.UAT_SKIP_BLUE_DEPLOYMENT}}, False) + ) + variables: + green_app_version: $[ stageDependencies.Build_release_candidate.build.outputs['chart_current_version.appVersion'] ] + jobs: + - deployment: "Blue_deployment" + displayName: "Blue deployment" + pool: + name: pagopa-uat-linux + environment: 'UAT' + strategy: + runOnce: + deploy: + steps: + - checkout: self + displayName: "Checkout" + - task: KubectlInstaller@0 + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + helm repo add microservice-chart https://pagopa.github.io/aks-microservice-chart-blueprint + helm dep build helm + - template: azure-templates/helm-microservice-chart-deploy.yml + parameters: + DO_DEPLOY: true + DO_BLUE_GREEN_DEPLOY: true + ENV: 'UAT' + KUBERNETES_SERVICE_CONN: $(UAT_KUBERNETES_SERVICE_CONN) + NAMESPACE: ecommerce + APP_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + VALUE_FILE: "helm/values-uat.yaml" + GREEN_VERSION: $(green_app_version) + BLUE_VERSION: $(Build.SourceVersion) + + - stage: "Bluegreen_WaitForApproval" + displayName: 'UAT green approval deployment' + dependsOn: Deploy_UAT_Blue + variables: + commitUrl: $[ stageDependencies.Build_release_candidate.build.outputs['chart_current_version.commitUrl'] ] + jobs: + - job: Bluegreen_WaitForApproval + displayName: Manual blue deploy approval + pool: server + timeoutInMinutes: 4320 # 3 days + steps: + - task: ManualValidation@0 + timeoutInMinutes: 4320 # 3 days + inputs: + notifyUsers: $(APPROVE_TOUCHPOINT_MAIL) + instructions: "Please approve or reject UAT blue green promotions for $(commitUrl)" + onTimeout: 'reject' + + - stage: Release + ${{ if eq(parameters['UAT_SKIP_BLUE_DEPLOYMENT'], True) }}: + dependsOn: Build_release_candidate + ${{ if eq(parameters['UAT_SKIP_BLUE_DEPLOYMENT'], False) }}: + dependsOn: Bluegreen_WaitForApproval + condition: succeeded() + jobs: + - job: make_release + displayName: Make github release + steps: + - ${{ if eq(parameters['SKIP_RELEASE'], False) }}: + - template: templates/node-job-setup/template.yaml@pagopaCommons + parameters: + persistCredentials: true + - template: azure-templates/maven-github-release.yml + parameters: + gitEmail: $(GIT_EMAIL) + gitUsername: $(GIT_USERNAME) + gitHubConnection: $(GITHUB_CONNECTION) + release_branch: main + semver_chart: '${{ parameters.RELEASE_CHART_SEMVER }}' + semver: '${{ parameters.RELEASE_SEMVER }}' + - template: azure-templates/chart-current-version.yml + + - stage: "tag_docker_release" + displayName: 'Tag Docker image to be release' + dependsOn: Release + condition: + and( + succeeded(), + ne('${{parameters.RELEASE_SEMVER}}', 'none') + ) + variables: + app_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + jobs: + - job: "build" + displayName: 'Build UAT service beta' + steps: + - task: Docker@2 + displayName: "docker login" + inputs: + containerRegistry: $(UAT_CONTAINER_REGISTRY_SERVICE_CONN) + command: "login" + - task: Bash@3 + displayName: "docker tag new version" + inputs: + targetType: "inline" + script: | + docker pull $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(Build.SourceVersion) + docker tag $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(Build.SourceVersion) $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(app_version) + docker push $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(app_version) + + + - stage: "Deploy_UAT_Green" + displayName: 'UAT green deployment' + dependsOn: [tag_docker_release,Release] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + in(dependencies.tag_docker_release.result, 'Succeeded', 'Skipped'), + in(dependencies.Release.result, 'Succeeded', 'Skipped'), + or( + eq(variables['Build.SourceBranch'], 'refs/heads/main'), + startsWith(variables['Build.SourceBranch'], 'refs/tags') + ) + ) + variables: + app_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + jobs: + - deployment: "Green_deployment" + displayName: "Green deployment" + pool: + name: pagopa-uat-linux + environment: 'UAT' + strategy: + runOnce: + deploy: + steps: + - checkout: self + displayName: "Checkout" + - task: KubectlInstaller@0 + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + helm repo add microservice-chart https://pagopa.github.io/aks-microservice-chart-blueprint + helm dep build helm + - template: azure-templates/helm-microservice-chart-deploy.yml + parameters: + DO_DEPLOY: true + DO_BLUE_GREEN_DEPLOY: false + ENV: 'UAT' + KUBERNETES_SERVICE_CONN: $(UAT_KUBERNETES_SERVICE_CONN) + NAMESPACE: ecommerce + APP_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + VALUE_FILE: "helm/values-uat.yaml" + GREEN_VERSION: $(app_version) + # --- END Deploy UAT --- # + + # --- START Deploy PROD --- # + - stage: "Prod_WaitForApproval" + displayName: 'PROD approval deployment' + dependsOn: [Deploy_UAT_Green,Release] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(dependencies.Deploy_UAT_Green.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + variables: + release_url: $[ stageDependencies.Release.make_release.outputs['chart_current_version.releaseUrl'] ] + jobs: + - job: Prod_Approval + displayName: Manual prod deploy approval + pool: server + timeoutInMinutes: 4320 # 3 days + steps: + - task: ManualValidation@0 + timeoutInMinutes: 4320 # 3 days + inputs: + notifyUsers: $(APPROVE_TOUCHPOINT_MAIL) + instructions: "Please approve or reject PROD promotions for release $(release_url)" + onTimeout: 'reject' + + # --- START Deploy PROD --- # + - stage: "Build_PROD_Blue" + displayName: 'PROD blue Build' + dependsOn: [ Prod_WaitForApproval,Release ] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(${{parameters.PROD_SKIP_BLUE_DEPLOYMENT}}, False), + eq(dependencies.Prod_WaitForApproval.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + variables: + app_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + jobs: + - job: "PROD_image" + displayName: 'Build PROD image' + steps: + - task: Docker@2 + displayName: "docker login for UAT" + inputs: + containerRegistry: $(UAT_CONTAINER_REGISTRY_SERVICE_CONN) + command: "login" + - task: Bash@3 + displayName: "docker tag new version for PROD" + inputs: + targetType: "inline" + script: | + docker pull $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(app_version) + docker tag $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(app_version) $(PROD_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(Build.SourceVersion) + - task: Docker@2 + displayName: "docker login for PROD" + inputs: + containerRegistry: $(PROD_CONTAINER_REGISTRY_SERVICE_CONN) + command: "login" + - task: Bash@3 + displayName: "Push new version for PROD" + inputs: + targetType: "inline" + script: | + docker push $(PROD_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(Build.SourceVersion) + - stage: "Deploy_PROD_Blue" + displayName: 'PROD blue deployment' + dependsOn: [Build_PROD_Blue,Release] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(${{parameters.PROD_SKIP_BLUE_DEPLOYMENT}}, False), + eq(dependencies.Build_PROD_Blue.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + variables: + app_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + jobs: + - deployment: "Blue_PROD_deployment" + displayName: "Blue PROD deployment" + pool: + name: pagopa-prod-linux + environment: 'PROD' + strategy: + runOnce: + deploy: + steps: + - checkout: self + displayName: "Checkout" + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + helm repo add microservice-chart https://pagopa.github.io/aks-microservice-chart-blueprint + helm dep build helm + - template: azure-templates/helm-microservice-chart-deploy.yml + parameters: + DO_DEPLOY: true + DO_BLUE_GREEN_DEPLOY: true + ENV: 'PROD' + KUBERNETES_SERVICE_CONN: $(PROD_KUBERNETES_SERVICE_CONN) + NAMESPACE: ecommerce + APP_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + VALUE_FILE: "helm/values-prod.yaml" + GREEN_VERSION: $(app_version) + BLUE_VERSION: $(Build.SourceVersion) + + - stage: "PROD_Green_WaitForApproval" + displayName: 'PROD green approval deployment' + ${{ if eq(parameters['PROD_SKIP_BLUE_DEPLOYMENT'], True) }}: + dependsOn: Prod_WaitForApproval + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + in(dependencies.Prod_WaitForApproval.result, 'Succeeded') + ) + ${{ if eq(parameters['PROD_SKIP_BLUE_DEPLOYMENT'], False) }}: + dependsOn: Deploy_PROD_Blue + variables: + release_url: $[ stageDependencies.Release.make_release.outputs['chart_current_version.releaseUrl'] ] + jobs: + - job: Bluegreen_PROD_WaitForApproval + displayName: Manual green deploy approval + pool: server + timeoutInMinutes: 4320 # 3 days + steps: + - task: ManualValidation@0 + timeoutInMinutes: 4320 # 3 days + inputs: + notifyUsers: $(APPROVE_TOUCHPOINT_MAIL) + instructions: "Please approve or reject PROD green promotions for release $(release_url)" + onTimeout: 'reject' + + - stage: "Build_PROD_Green" + displayName: 'PROD green Build' + dependsOn: [PROD_Green_WaitForApproval,Release] + variables: + prod_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(dependencies.PROD_Green_WaitForApproval.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + jobs: + - job: "PROD_image" + displayName: 'Build PROD image' + steps: + - task: Docker@2 + displayName: "docker login for UAT" + inputs: + containerRegistry: $(UAT_CONTAINER_REGISTRY_SERVICE_CONN) + command: "login" + - task: Bash@3 + displayName: "docker tag new version for PROD" + inputs: + targetType: "inline" + script: | + docker pull $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(prod_version) + - task: Docker@2 + displayName: "docker login for PROD" + inputs: + containerRegistry: $(PROD_CONTAINER_REGISTRY_SERVICE_CONN) + command: "login" + - task: Bash@3 + displayName: "docker tag new version for PROD" + inputs: + targetType: "inline" + script: | + docker tag $(UAT_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(prod_version) $(PROD_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(prod_version) + docker push $(PROD_CONTAINER_NAMESPACE)/$(K8S_IMAGE_REPOSITORY_NAME):$(prod_version) + - stage: "Deploy_PROD_Green" + displayName: 'PROD green deployment' + dependsOn: [Build_PROD_Green,Release] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(dependencies.Build_PROD_Green.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + variables: + prod_version: $[ stageDependencies.Release.make_release.outputs['chart_current_version.appVersion'] ] + jobs: + - deployment: "Green_PROD_deployment" + displayName: "Green PROD deployment" + pool: + name: pagopa-prod-linux + environment: 'PROD' + strategy: + runOnce: + deploy: + steps: + - checkout: self + displayName: "Checkout" + - task: Bash@3 + name: update_chart_version + displayName: 'Setup helm microservice chart' + inputs: + targetType: "inline" + script: | + helm repo add microservice-chart https://pagopa.github.io/aks-microservice-chart-blueprint + helm dep build helm + - template: azure-templates/helm-microservice-chart-deploy.yml + parameters: + DO_DEPLOY: true + DO_BLUE_GREEN_DEPLOY: false + ENV: 'PROD' + KUBERNETES_SERVICE_CONN: $(PROD_KUBERNETES_SERVICE_CONN) + NAMESPACE: ecommerce + APP_NAME: $(K8S_IMAGE_REPOSITORY_NAME) + VALUE_FILE: "helm/values-prod.yaml" + GREEN_VERSION: $(prod_version) + # --- END Deploy PROD --- # + + # --- START ROLLBACK PROD --- # + - stage: "Prod_RollbackForApproval" + displayName: 'PROD ROLLBACK' + dependsOn: [Deploy_PROD_Green,Release] + condition: | + and( + eq(${{parameters.UAT_PROD_DEPLOY}}, true), + eq(dependencies.Deploy_PROD_Green.result, 'Succeeded'), + in(dependencies.Release.result, 'Succeeded', 'Skipped') + ) + variables: + release_url: $[ stageDependencies.Release.make_release.outputs['chart_current_version.releaseUrl'] ] + jobs: + - job: Prod_Rollback_Approval + displayName: Manual prod rollback approval + pool: server + steps: + - task: ManualValidation@0 + timeoutInMinutes: 30 + inputs: + notifyUsers: $(APPROVE_TOUCHPOINT_MAIL) + instructions: "Please approve or reject PROD promotions for release $(release_url)" + onTimeout: 'skip' + - stage: "Prod_RollbackToLatestRelease" + displayName: 'PROD rollback to the latest Release' + dependsOn: [Prod_RollbackForApproval] + condition: succeeded() + jobs: + - job: Prod_Rollback + pool: + name: pagopa-prod-linux + steps: + - task: HelmDeploy@0 + displayName: Helm Rollback PROD + inputs: + kubernetesServiceEndpoint: $(PROD_KUBERNETES_SERVICE_CONN) + namespace: ecommerce + command: rollback + chartName: $(K8S_IMAGE_REPOSITORY_NAME) + releaseName: $(K8S_IMAGE_REPOSITORY_NAME) + install: true + waitForExecution: true + arguments: $(K8S_IMAGE_REPOSITORY_NAME) +# --- END ROLLBACK PROD --- #