diff --git a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_canary.Jenkinsfile b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_canary.Jenkinsfile deleted file mode 100644 index ff4044ce3..000000000 --- a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_canary.Jenkinsfile +++ /dev/null @@ -1,101 +0,0 @@ -@Library('ace@v1.1') -def event = new com.dynatrace.ace.Event() - -pipeline { - agent { - label 'kubegit' - } - parameters { - string( - name: 'CANARY_WEIGHT', - defaultValue: '0', - description: 'Weight of traffic that will be routed to service.', - trim: true - ) - string( - name: 'REMEDIATION_URL', - defaultValue: '', - description: 'Remediation script to call if canary release fails', - trim: true - ) - string( - name: 'REMEDIATION_TYPE', - defaultValue: '', - description: 'Remediation type to target specific remediation handler', - trim: true - ) - string( - name: 'RELEASE_STAGE', - defaultValue: 'canary-jenkins', - description: 'Namespace service will be deployed in.', - trim: true - ) - string( - name: 'RELEASE_PRODUCT', - defaultValue: 'simplenodeservice', - description: 'The name of the service to deploy.', - trim: true - ) - } - environment { - DT_API_TOKEN = credentials('DT_API_TOKEN') - DT_TENANT_URL = credentials('DT_TENANT_URL') - // RELEASE_STAGE = 'canary-jenkins' - // RELEASE_PRODUCT = 'simplenodeservice' - } - stages { - stage('Retrieve canary metadata') { - steps { - container('kubectl') { - script { - env.CANARY_NAME = sh(returnStdout: true, script: "kubectl -n ${params.RELEASE_STAGE} get ingress -o=jsonpath='{.items[?(@.metadata.annotations.nginx\\.ingress\\.kubernetes\\.io/canary==\"true\")].metadata.name}'") - // env.NON_CANARY_NAME = sh(returnStdout: true, script: "kubectl -n ${env.RELEASE_STAGE} get ingress -o=jsonpath='{.items[?(@.metadata.annotations.nginx\\.ingress\\.kubernetes\\.io/canary==\"false\")].metadata.name}'") - } - } - } - } - stage('Shift traffic') { - steps { - container('kubectl') { - sh "kubectl -n ${params.RELEASE_STAGE} annotate ingress ${env.CANARY_NAME} nginx.ingress.kubernetes.io/canary-weight='${params.CANARY_WEIGHT}' --overwrite" - } - } - } - stage('Dynatrace configuration change event') { - steps { - script { - // env.RELEASE_BUILD_VERSION = sh(returnStdout: true, script: "kubectl -n ${env.RELEASE_STAGE} get deployment ${env.CANARY_NAME} -o=jsonpath='{.metadata.labels.app\\.kubernetes\\.io/version}'") - // sh "echo ${env.RELEASE_BUILD_VERSION}" - - event.pushDynatraceConfigurationEvent( - tagRule : getTagRulesForServiceEvent(), - description : "${params.RELEASE_PRODUCT} canary weight set to ${params.CANARY_WEIGHT}%", - source : 'Jenkins', - configuration : 'Load Balancer', - customProperties : [ - 'remediationAction': "${params.REMEDIATION_URL}", - 'remediationType': "${params.REMEDIATION_TYPE}" - ] - ) - } - } - } - } -} - -// -// Legacy tag rules function can be removed with availabilty of dta feature -// -def getTagRulesForServiceEvent() { - def tagMatchRules = [ - [ - 'meTypes': ['SERVICE'], - tags: [ - ['context': 'ENVIRONMENT', 'key': 'DT_RELEASE_PRODUCT', 'value': "${params.RELEASE_PRODUCT}"], - ['context': 'ENVIRONMENT', 'key': 'DT_RELEASE_STAGE', 'value': "${params.RELEASE_STAGE}"] - ] - ] - ] - - return tagMatchRules -} diff --git a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_test.Jenkinsfile b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_test.Jenkinsfile new file mode 100644 index 000000000..a3369f074 --- /dev/null +++ b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/files/jenkins/04_test.Jenkinsfile @@ -0,0 +1,236 @@ +@Library('ace@v1.1') +@Library('jenkinstest@v1.3.0') + +def event = new com.dynatrace.ace.Event() +def jmeter = new com.dynatrace.ace.Jmeter() + +pipeline { + parameters { + string( + name: 'RELEASE_PRODUCT', + defaultValue: 'simplenodeservice', + description: 'The name of the service to test.', + trim: true + ) + string( + name: 'IMAGE_NAME', + defaultValue: '', + description: 'The image name of the service to test.', + trim: true + ) + string(name: 'IMAGE_TAG', defaultValue: '', description: 'The image tag of the service to test.', trim: true) + string(name: 'RELEASE_VERSION', defaultValue: '', description: 'SemVer release version.', trim: true) + string( + name: 'RELEASE_BUILD_VERSION', + defaultValue: '', + description: 'Release version, including build id.', + trim: true + ) + string( + name: 'RELEASE_STAGE', + defaultValue: 'staging-jenkins', + description: 'Namespace service will be tested in.', + trim: true + ) + choice(name: 'QG_MODE', choices: ['yaml', 'dashboard'], description: 'Use yaml or dashboard for QG') + } + environment { + // Testing + VU = 1 + TESTDURATION = 180 + + // DT params + DT_API_TOKEN = credentials('DT_API_TOKEN') + DT_TENANT_URL = credentials('DT_TENANT_URL') + + // Keptn params + CLOUD_AUTOMATION_API_TOKEN = credentials('KEPTN_API_TOKEN') + CLOUD_AUTOMATION_ENDPOINT = "${env.KEPTN_ENDPOINT}" + CLOUD_AUTOMATION_PROJECT = "${env.RELEASE_PRODUCT}" + CLOUD_AUTOMATION_SERVICE = "${env.RELEASE_PRODUCT}" + CLOUD_AUTOMATION_STAGE = 'staging' // For the sake of this demo, "staging" is preferred over "${env.RELEASE_STAGE}". + CLOUD_AUTOMATION_SOURCE = 'jenkins' + CLOUD_AUTOMATION_MONITORING = 'dynatrace' + SHIPYARD_FILE = 'cloudautomation/shipyard.yaml' + SLO_FILE = 'cloudautomation/slo.yaml' + SLI_FILE = 'cloudautomation/sli.yaml' + DT_CONFIG_FILE = 'cloudautomation/dynatrace.conf.yaml' + } + agent { + label 'kubegit' + } + stages { + stage('Quality Gate Init') { + agent { + label 'cloud-automation-runner' + } + steps { + checkout scm + container('cloud-automation-runner') { + sh '/cloud_automation/cloud_automation_init.sh' + } + stash includes: 'cloud_automation.init.json', name: 'cloud_automation-init' + } + } + stage('DT Test Start') { + steps { + script { + def rootDir = pwd() + def sharedLib = load "${rootDir}/jenkins/shared/shared.groovy" + event.pushDynatraceInfoEvent( + tagRule: sharedLib.getTagRulesForPGIEvent(), + title: "Jmeter Start ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + description: "Performance test started for ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + source : 'jmeter', + customProperties : [ + 'Jenkins Build Number': env.BUILD_ID, + 'Virtual Users' : env.VU, + 'Test Duration' : env.TESTDURATION + ] + ) + } + } + } + stage('Run performance test') { + steps { + container('jmeter') { + sh 'echo $(date --utc +%FT%T.000Z) > cloud_automation.test.starttime' + } + stash includes: 'cloud_automation.test.starttime', name: 'cloud_automation.test.starttime' + checkout scm + container('jmeter') { + script { + def status = jmeter.executeJmeterTest( + scriptName: 'jmeter/simplenodeservice_test_by_duration.jmx', + resultsDir: "perfCheck_${env.RELEASE_PRODUCT}_staging_${BUILD_NUMBER}", + serverUrl: "${env.RELEASE_PRODUCT}.${env.RELEASE_STAGE}", + serverPort: 80, + checkPath: '/health', + vuCount: env.VU.toInteger(), + testDuration: env.TESTDURATION.toInteger(), + LTN: "perfCheck_${env.RELEASE_PRODUCT}_${BUILD_NUMBER}", + funcValidation: false, + avgRtValidation: 4000 + ) + if (status != 0) { + currentBuild.result = 'FAILED' + error 'Performance test in staging failed.' + } + } + } + + container('jmeter') { + sh 'echo $(date --utc +%FT%T.000Z) > cloud_automation.test.endtime' + } + stash includes: 'cloud_automation.test.endtime', name: 'cloud_automation.test.endtime' + } + } + stage('DT Test Stop') { + steps { + script { + def rootDir = pwd() + def sharedLib = load "${rootDir}/jenkins/shared/shared.groovy" + event.pushDynatraceInfoEvent( + tagRule: sharedLib.getTagRulesForPGIEvent(), + title: "Jmeter Stop ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + description: "Performance test stopped for ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + source : 'jmeter', + customProperties : [ + 'Jenkins Build Number': env.BUILD_ID, + 'Virtual Users' : env.VU, + 'Test Duration' : env.TESTDURATION + ] + ) + } + } + } + + stage('Quality Gate') { + agent { + label 'cloud-automation-runner' + } + steps { + unstash 'cloud_automation-init' + unstash 'cloud_automation.test.starttime' + unstash 'cloud_automation.test.endtime' + + container('cloud-automation-runner') { + sh """ + export CLOUD_AUTOMATION_LABELS='[{"DT_RELEASE_VERSION":"'${env.RELEASE_VERSION}'"},{"DT_RELEASE_BUILD_VERSION":"'${env.RELEASE_BUILD_VERSION}'"},{"DT_RELEASE_STAGE":"'${env.RELEASE_STAGE}'"},{"DT_RELEASE_PRODUCT":"'${env.RELEASE_PRODUCT}'"}]' + + export CI_PIPELINE_IID="${BUILD_ID}" + export CI_JOB_NAME="${JOB_NAME}" + export CI_JOB_URL="${JOB_URL}" + export CI_PROJECT_NAME="${env.RELEASE_PRODUCT}" + + /cloud_automation/cloud_automation_eval.sh + """ + } + } + } + + stage('Release approval') { + // no agent, so executors are not used up when waiting for approvals + agent none + steps { + script { + switch (currentBuild.result) { + case 'SUCCESS': + env.DPROD = true + break + case 'UNSTABLE': + try { + timeout(time:3, unit:'MINUTES') { + env.APPROVE_PROD = input message: 'Promote to Production', ok: 'Continue', parameters: [choice(name: 'APPROVE_PROD', choices: 'YES\nNO', description: 'Deploy from STAGING to PRODUCTION?')] + if (env.APPROVE_PROD == 'YES') { + env.DPROD = true + } else { + env.DPROD = false + } + } + } catch (error) { + env.DPROD = false + echo 'Timeout has been reached! Deploy to PRODUCTION automatically stopped' + } + break + case 'FAILURE': + env.DPROD = false + + event.pushDynatraceErrorEvent( + tagRule: getTagRules(), + title: "Quality Gate failed for ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + description: "Quality Gate evaluation failed for ${env.RELEASE_PRODUCT} ${env.RELEASE_BUILD_VERSION}", + source : 'jenkins', + customProperties : [ + 'Jenkins Build Number': env.BUILD_ID + ] + ) + break + } + } + } + } + + stage('Promote to production') { + // no agent, so executors are not used up when waiting for other job to complete + agent none + when { + expression { + return env.DPROD == 'true' + } + } + steps { + build job: '4. Deploy production', + wait: false, + parameters: [ + string(name: 'RELEASE_PRODUCT', value: "${env.RELEASE_PRODUCT}"), + string(name: 'RELEASE_VERSION', value: "${env.RELEASE_VERSION}"), + string(name: 'RELEASE_BUILD_VERSION', value: "${env.RELEASE_BUILD_VERSION}"), + string(name: 'RELEASE_STAGE', value: 'prod-jenkins'), + string(name: 'IMAGE_TAG', value: "${env.IMAGE_TAG}"), + string(name: 'IMAGE_NAME', value: "${env.IMAGE_NAME}"), + ] + } + } + } +} diff --git a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-default-jobs.yml.j2 b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-default-jobs.yml.j2 index 063fdcc79..0923765fc 100644 --- a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-default-jobs.yml.j2 +++ b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-default-jobs.yml.j2 @@ -27,7 +27,7 @@ controller: } definition { cpsScm { - scriptPath('jenkins/build.Jenkinsfile') + scriptPath('jenkins/01_build_images.Jenkinsfile') scm { git { remote { @@ -42,7 +42,26 @@ controller: } } - script: > - pipelineJob('{{ demo_jenkins_folder }}/2. Deploy') { + pipelineJob('{{ demo_jenkins_folder }}/2. Monaco') { + definition { + cpsScm { + scriptPath('jenkins/02_monaco.Jenkinsfile') + scm { + git { + remote { + url('{{ ingress_protocol }}://{{ git_domain }}/{{ demo_org }}/{{ demo_repo }}') + credentials('git-creds-ace') + } + branch('*/main') + } + } + lightweight() + } + } + } + + - script: > + pipelineJob('{{ demo_jenkins_folder }}/3. Deploy') { parameters { stringParam('RELEASE_PRODUCT') stringParam('IMAGE_NAME') @@ -53,7 +72,7 @@ controller: } definition { cpsScm { - scriptPath('jenkins/deployStaging.Jenkinsfile') + scriptPath('jenkins/03_deploy.Jenkinsfile') scm { git { remote { @@ -68,7 +87,7 @@ controller: } } - script: > - pipelineJob('{{ demo_jenkins_folder }}/3. Test') { + pipelineJob('{{ demo_jenkins_folder }}/4. Test') { parameters { stringParam('RELEASE_PRODUCT') stringParam('IMAGE_NAME') @@ -80,7 +99,7 @@ controller: } definition { cpsScm { - scriptPath('jenkins/test.Jenkinsfile') + scriptPath('jenkins/04_test.Jenkinsfile') scm { git { remote { diff --git a/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-quality-gates-jenkins-dashboard.yml.j2 b/user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-release-validation-jenkins-dashboard.yml.j2 similarity index 100% rename from user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-quality-gates-jenkins-dashboard.yml.j2 rename to user-skel/ansible_collections/ace_box/ace_box/roles/demo-release-validation-srg-jenkins/templates/demo-release-validation-jenkins-dashboard.yml.j2