From 50f427c542cf67990db59a93c1aef5b3219c1b08 Mon Sep 17 00:00:00 2001 From: Michal Kovarik Date: Mar 10 2020 11:27:43 +0000 Subject: Add c3i ansible role Providing unified workflows for components in pagure. Jobs are triggered by messages from UMB or fedmsg. --- diff --git a/README.md b/README.md index 0af2e53..79f4620 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,7 @@ You can run the unit test suite with the following command: ``` gradle test # or `gradle test --info` to be more verbose ``` + +# C3I Ansible role + +Repository contains [c3i ansible role](c3i-library/roles/c3i/README.md) for deploying C3I workflows. diff --git a/roles/c3i/README.md b/roles/c3i/README.md new file mode 100644 index 0000000..351a11b --- /dev/null +++ b/roles/c3i/README.md @@ -0,0 +1,100 @@ +C3I role +========= + +C3I role is providing C3I workflows: +* premerge +* postmerge +* image promotions + +Role is configurable with role varibales starting `c3i_`. Component developers are expected to provide Jenkinsfile snippets with stages for building their component container and tests for their promotin workflows. + +Requirements +------------ + +Integration with pagure.io: +* Projects have to set 'Fedmsg notificitaions' in 'Project Options' +* Projects have to activate 'Hooks' -> 'Fedmsg' +* Openshift namespace contains secret defined by `c3i_pagure_api_key_secret` + - Token permissons: Flag a commit, Comment on a pull-request, Flag a pull-request + +Jenkins master is listening on Fedmsg and UMB. + +Secret creation: +``` +oc delete secret pagure-api-key --ignore-not-found=true +oc create secret generic pagure-api-key --from-literal=secrettext=${KEY} +oc label secret pagure-api-key credential.sync.jenkins.openshift.io=true +``` + +Workflows +--------- +### Pre-merge +Job is listening on PR changes - new, update, rebase, reopen. New changes are checkouted into current directory. Pagure PR is marked as `Pending` in pagure. Snippet `c3i_build_and_test_snippet` is executed. Test results should be sent as part of `c3i_build_and_test_snippet`. Pagure PR state is updated. Temporary images are deleted. + +### Post-merge +Job is listening on commits in master branch. New changes are checkouted into current directory. Pagure PR is marked as `Pending` in pagure. Snippet `c3i_build_and_test_snippet` is executed. Test results should be sent as +part of `c3i_build_and_test_snippet`. Pagure PR state is updated. Temporary images are promoted to final destination with `latest` tag. + +### Promotion +Repotracker job listens on image change for specified tag - latest or stage. The repotracker job deployes environment and run tests defined in `c3i_integration_test_snippet`. Test results should be sent as +part of `c3i_integration_test_snippet`. + +Greenwave promotion job listens on container tests result. Job is promoting image from latest to stage or from stage to prod. + +Role Variables +-------------- + +Check [defaults/main.yml](defaults/main.yml) + +Stage snippets +-------------- + +Snippets used defining custom parts of Jenkins files. It's expected stage part in declarative syntax. + +### c3i\_build\_and\_test\_snippet +Path to snippet for Build container and test acceptance for 'latest' tag. + +Git repository is checkout into current working directory. + +Input parameters are defined in [templates/build.yml](templates/build.yml). Input parameters can be extended by `c3i_build_custom_parameters`, eg.: +```yaml +c3i_build_custom_parameters: + - name: MBS_BACKEND_IMAGESTREAM_NAME + value: mbs-backend + - name: MBS_FRONTEND_IMAGESTREAM_NAME + value: mbs-frontend + - name: MBS_SPEC_FILE + value: https://src.fedoraproject.org/rpms/module-build-service/raw/master/f/module-build-service.spec +``` + +Expected output variables: +* RESULTING\_IMAGE\_REPOS - provides list of repos cotaining generated images separated by ','. +* RESULTING\_TAG - provides tag used on generated images + + +### c3i\_integration\_test\_snippet +Path to snippet for building testing environment and running tests for promotion workflow. + +Input parameters are defined in [templates/trigger-on-tag.yml](templates/trigger-on-tag.yml). Input parameters can be extended by `c3i_integration_test_custom_parameters`. + + +Example Playbook +---------------- + +```yaml +- name: Deplomyent playbook + hosts: localhost + vars_files: + - c3i-role-vars.yml + tasks: + - git: + repo: "{{ c3i_lib_url }}" + dest: c3i-library + version: "{{ c3i_lib_branch }}" + - file: + src: c3i-library/roles + dest: roles + state: link + - include_role: + name: c3i +``` diff --git a/roles/c3i/defaults/main.yml b/roles/c3i/defaults/main.yml new file mode 100644 index 0000000..79f940e --- /dev/null +++ b/roles/c3i/defaults/main.yml @@ -0,0 +1,81 @@ +--- +# defaults file for c3i + +# Name of component +c3i_component: + +# List of images for promotion workflow +c3i_images_for_promotion: + - "{{ c3i_component }}" + +# Git repo for build +c3i_git_repo: https://pagure.io/{{ c3i_component }}.git +# Main branch used for triggering post merge workflow. +c3i_git_main_branch: master + +# Path to dockerfile for Jenkins agent +c3i_jenkins_agent_buildconfig_contextdir: . +c3i_jenkins_agent_buildconfig_dockerfile: openshift/containers/jenkins-slave/Dockerfile + + +# Set custom parameters for build worflow, list of dicts with 'key' and 'value' +c3i_build_custom_parameters: [] +# Set custom parameters for test promotion worflow, list of dicts with 'key' and 'value' +c3i_integration_test_custom_parameters: [] +# Email address for notification +c3i_mail_address: +# Path to Jenkinsfile snippet for premerge and postmerge workflow +c3i_build_and_test_snippet: +# Path to Jenkinsfile snippet for integration tests used by promotion workflow +c3i_integration_test_snippet: + +c3i_definition_dir: openshift/pipelines +c3i_definition_update_script: ansible-playbook deploy.yml -e c3i_skip_service_accounts=true + +# Api key for pagure - has to have permissions for: Flag a commit, Comment on a pull-request, Flag a pull-request +c3i_pagure_api_key_secret: pagure-api-key + +# Temporarily disable doc push to workaround https://pagure.io/pagure/issue/3919. Remove this line when it is fixed. +c3i_pagure_doc_repo_name: +c3i_pagure_doc_secret: pagure-doc-secret + + +c3i_default_agent_snippet: default-agent.groovy +c3i_build_agent_snippet: "{{ c3i_default_agent_snippet }}" + +# Secret name in openshift namespace for pushing images +c3i_container_registry_credentials: factory2-pipeline-registry-credentials + +c3i_test_subscriber: + +# Create service accounts and role bindings - can be done only by admin +c3i_skip_service_accounts: false + +c3i_imagestream_namespace: "{{ c3i_ocp_namespace }}" +c3i_imagestream_name: "{{ c3i_component }}" + +c3i_quay_address: quay.io +c3i_quay_namespace: factory2 + +c3i_dev_image_tag: "latest" + +c3i_lib_branch: master +c3i_lib_url: https://pagure.io/c3i-library.git + +c3i_jenkins_agent_image: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}/{{ c3i_component }}-jenkins-slave:latest" +c3i_jenkins_build_agent_image: "{{ c3i_jenkins_agent_image }}" +c3i_jenkins_test_agent_image: "{{ c3i_jenkins_agent_image }}" +c3i_workflow_jenkins_image: docker-registry.upshift.redhat.com/factory2/pipeline-jenkins-agent:latest + +c3i_tracked_container_repo: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}/{{ c3i_component }}" + +c3i_pipeline_as_a_service_namespace: c3i +c3i_messaging_provider: Red Hat UMB +c3i_fedmsg_provider: fedmsg +c3i_cloud_name: openshift + +# Openshift connection +c3i_ocp_token: "{{ lookup('file', '/run/secrets/kubernetes.io/serviceaccount/token', errors='ignore') | default(lookup('pipe', 'oc whoami -t'), true) }}" +c3i_ocp_host: https://paas.psi.redhat.com +c3i_ocp_verify_ssl: true +c3i_ocp_namespace: "{{ lookup('file', '/run/secrets/kubernetes.io/serviceaccount/namespace', errors='ignore') | default(lookup('pipe', \"awk '/current-context/ {print $2}' ~/.kube/config | cut -f1 -d'/'\"), true) }}" diff --git a/roles/c3i/tasks/build.yml b/roles/c3i/tasks/build.yml new file mode 100644 index 0000000..80e5a0d --- /dev/null +++ b/roles/c3i/tasks/build.yml @@ -0,0 +1,48 @@ +--- +- name: Service account {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: ServiceAccount + apiVersion: v1 + metadata: + name: "{{ job_vars.name }}-jenkins-slave" + labels: + app: "{{ job_vars.name }}" + when: not c3i_skip_service_accounts + +- name: Role binding {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: RoleBinding + apiVersion: v1 + metadata: + name: "{{ job_vars.name }}-jenkins-slave_edit" + labels: + app: "{{ job_vars.name }}" + subjects: + - kind: ServiceAccount + name: "{{ job_vars.name }}-jenkins-slave" + roleRef: + name: edit + when: not c3i_skip_service_accounts + +- name: Build config {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: "{{ lookup('template', job_vars.template) }}" + vars: + task_var_build_and_test: "{{ lookup('template', c3i_build_and_test_snippet)}}" diff --git a/roles/c3i/tasks/greenwave-promote.yml b/roles/c3i/tasks/greenwave-promote.yml new file mode 100644 index 0000000..0841f87 --- /dev/null +++ b/roles/c3i/tasks/greenwave-promote.yml @@ -0,0 +1,49 @@ +--- +- name: Service account greenwave-promote + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: ServiceAccount + apiVersion: v1 + metadata: + name: "{{ item }}-{{ job_vars.name_post }}-jenkins-slave" + labels: + app: "{{ item }}-{{ job_vars.name_post }}" + when: not c3i_skip_service_accounts + loop: "{{ c3i_images_for_promotion }}" + +- name: Role binding greenwave-promote + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: RoleBinding + apiVersion: v1 + metadata: + name: "{{ item }}-{{ job_vars.name_post }}-jenkins-slave_edit" + labels: + app: "{{ item }}-{{ job_vars.name_post }}" + subjects: + - kind: ServiceAccount + name: "{{ item }}-{{ job_vars.name_post }}-jenkins-slave" + roleRef: + name: edit + when: not c3i_skip_service_accounts + loop: "{{ c3i_images_for_promotion }}" + +- name: Build config greenwave-promote + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: "{{ lookup('template', job_vars.template) }}" + loop: "{{ c3i_images_for_promotion }}" diff --git a/roles/c3i/tasks/main.yml b/roles/c3i/tasks/main.yml new file mode 100644 index 0000000..68e990a --- /dev/null +++ b/roles/c3i/tasks/main.yml @@ -0,0 +1,19 @@ +--- +- name: Including jobs + include_tasks: proceed.yml + with_filetree: ../templates/jobs + loop_control: + loop_var: job_item +- name: Load dockerfile for jenkins agent buildConfig + stat: + path: "{{ lookup('pipe', 'git rev-parse --show-toplevel') }}/{{ c3i_jenkins_agent_buildconfig_contextdir }}/{{ c3i_jenkins_agent_buildconfig_dockerfile }}" + register: build_config_dockerfile +- name: Create jenkins agent buildConfig + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: "{{ lookup('template', 'jenkins_agent_build_config.yml') }}" + when: build_config_dockerfile.stat.exists diff --git a/roles/c3i/tasks/proceed.yml b/roles/c3i/tasks/proceed.yml new file mode 100644 index 0000000..a0a4439 --- /dev/null +++ b/roles/c3i/tasks/proceed.yml @@ -0,0 +1,6 @@ +--- +- name: Including job vars {{ job_item.src }} + include_vars: + name: job_vars + file: "{{ job_item.src }}" +- include_tasks: "{{ job_vars.template }}" diff --git a/roles/c3i/tasks/trigger-on-tag.yml b/roles/c3i/tasks/trigger-on-tag.yml new file mode 100644 index 0000000..31f84b3 --- /dev/null +++ b/roles/c3i/tasks/trigger-on-tag.yml @@ -0,0 +1,48 @@ +--- +- name: Service account {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: ServiceAccount + apiVersion: v1 + metadata: + name: "{{ job_vars.name }}-jenkins-slave" + labels: + app: "{{ job_vars.name }}" + when: not c3i_skip_service_accounts + +- name: Role binding {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: | + kind: RoleBinding + apiVersion: v1 + metadata: + name: "{{ job_vars.name }}-jenkins-slave_edit" + labels: + app: "{{ job_vars.name }}" + subjects: + - kind: ServiceAccount + name: "{{ job_vars.name }}-jenkins-slave" + roleRef: + name: edit + when: not c3i_skip_service_accounts + +- name: Build config {{ job_vars.name }} + k8s: + api_key: "{{ c3i_ocp_token }}" + host: "{{ c3i_ocp_host }}" + verify_ssl: "{{ c3i_ocp_verify_ssl }}" + namespace: "{{ c3i_ocp_namespace }}" + state: present + definition: "{{ lookup('template', job_vars.template) }}" + vars: + task_var_integration_test: "{{ lookup('template', c3i_integration_test_snippet) }}" diff --git a/roles/c3i/templates/build.Jenkinsfile b/roles/c3i/templates/build.Jenkinsfile new file mode 100644 index 0000000..5581b97 --- /dev/null +++ b/roles/c3i/templates/build.Jenkinsfile @@ -0,0 +1,278 @@ +library identifier: "c3i@{{ c3i_lib_branch }}", changelog: false, + retriever: modernSCM([$class: 'GitSCMSource', remote: "{{ c3i_lib_url }}"]) +import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; +pipeline { + {% if "job-updater" in job_vars.name %} + {% include c3i_default_agent_snippet %} + {% else %} + {% include c3i_build_agent_snippet %} + {% endif %} + + options { + timestamps() + timeout(time: 30, unit: 'MINUTES') + } + environment { + SERVICE_ACCOUNT_TOKEN = readFile(file: '/run/secrets/kubernetes.io/serviceaccount/token').trim() + // needed by c3i pagure client + TRIGGER_NAMESPACE = readFile('/run/secrets/kubernetes.io/serviceaccount/namespace').trim() + PAGURE_REPO_NAME = env.GIT_REPO.split('/')[3..-1].join('/').replace('forks/', '').replaceAll(/.git$/, '') + PAGURE_URL = env.GIT_REPO.split('/')[0..2].join('/') + PAGURE_API = "${env.PAGURE_URL}/api/0" + PAGURE_REPO_IS_FORK = "${env.GIT_REPO.contains('/forks/') ? 'true': 'false'}" + } + {% if "postmerge" not in job_vars.name %} + triggers { + ciBuildTrigger( + noSquash: true, + providerList: [ + {% if "premerge" in job_vars.name %} + {% for topic in job_vars.messaging_fedmsg_topics %} + fedmsgSubscriber( + name: params.MESSAGING_FEDMSG_PROVIDER, + overrides: [topic: "{{ topic }}"], + checks: [ + [field: '$.pullrequest.project.url_path', expectedValue: params.GIT_REPO.split('/')[3..-1].join('/').replace('forks/', 'fork/').replaceAll(/.git$/, '')], + [field: '$.pullrequest.branch', expectedValue: params.GIT_MAIN_BRANCH], + ] + ), + {% endfor %} + {% elif "job-updater" in job_vars.name %} + {% for topic in job_vars.messaging_fedmsg_topics %} + fedmsgSubscriber( + name: params.MESSAGING_FEDMSG_PROVIDER, + overrides: [topic: "{{ topic }}"], + checks: [ + [field: '$.repo.url_path', expectedValue: params.GIT_REPO.split('/')[3..-1].join('/').replace('forks/', 'fork/').replaceAll(/.git$/, '')], + [field: '$.branch', expectedValue: params.GIT_MAIN_BRANCH], + ] + ) + {% endfor %} + {% endif %} + ] + ) + } + {% endif %} + stages { + stage('Proceeding CI_MESSAGE') { + when { + expression { env.CI_MESSAGE } + } + steps { + script { + def message = readJSON text: params.CI_MESSAGE + if (env.GIT_REPO_REF != params.GIT_MAIN_BRANCH) { + env.GIT_REPO_REF = "pull/${message.pullrequest.id}/head" + } + } + } + } + stage('Update Build Info') { + steps { + script { + if (!env.GIT_REPO_REF) { + error("This build is not started by a CI message and GIT_REPO_REF is empty. Only configurations were done.") + } + + // FIXME: Due to a bug described in https://issues.jenkins-ci.org/browse/JENKINS-45489 + checkout([$class: 'GitSCM', + branches: [[name: env.GIT_REPO_REF]], + userRemoteConfigs: [[url: params.GIT_REPO, refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*/head:refs/remotes/origin/pull/*/head']], + ]) + env.GIT_COMMIT = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() + echo "Build ${env.GIT_REPO_REF}, commit=${env.GIT_COMMIT}" + + // Set friendly display name and description + def pagure_repo_home = env.GIT_REPO.replace('/forks/', '/fork/').replaceAll(/.git$/,'') + {% if "premerge" in job_vars.name %} + // Is the current branch a pull-request? If no, env.PR_NO will be empty. + env.PR_NO = env.GIT_REPO_REF.split('/')[1] // return X from pull/X/head + env.PR_URL = "${pagure_repo_home}/pull-request/${env.PR_NO}" + echo "Building PR #${env.PR_NO}: ${env.PR_URL}" + // NOTE: Old versions of OpenShift Client Jenkins plugin are buggy to handle arguments + // with special bash characters (like whitespaces, #, etc). + // https://bugzilla.redhat.com/show_bug.cgi?id=1625518 + currentBuild.displayName = "PR#${env.PR_NO}" + // To enable HTML syntax in build description, go to `Jenkins/Global Security/Markup Formatter` and select 'Safe HTML'. + def pagureLink = """${currentBuild.displayName}""" + try { + def prInfo = pagure.getPR(env.PR_NO) + pagureLink = """PR#${env.PR_NO}: ${escapeHtml(prInfo.title)}""" + // set PR status to Pending + if (params.PAGURE_API_KEY_SECRET_NAME) + pagure.setBuildStatusOnPR(null, 'Building...') + } catch (Exception e) { + echo "Error using pagure API: ${e}" + } + currentBuild.description = pagureLink + {% else %} + currentBuild.displayName = "${env.GIT_REPO_REF}: ${env.GIT_COMMIT.substring(0, 7)}" + currentBuild.description = """${currentBuild.displayName}""" + if (params.PAGURE_API_KEY_SECRET_NAME) { + try { + pagure.flagCommit('pending', null, 'Building...') + echo "Updated commit ${env.GIT_COMMIT} status to PENDING." + } catch (e) { + echo "Error updating commit ${env.GIT_COMMIT} status to PENDING: ${e}" + } + } + {% endif %} + {% include "get_paas_domain.groovy" %} + } + } + } + {% if "job-updater" in job_vars.name %} + stage('Update pipeline jobs') { + steps { + script { + dir("{{ c3i_definition_dir }}") { + sh '{{ c3i_definition_update_script }}' + } + } + } + } + stage('Trigger postmerge') { + steps { + script { + openshift.withCluster() { + def build = c3i.build(script: this, objs: "bc/${env.POSTMERGE_JOB}") + c3i.waitForBuildStart(script: this, build: build) + def devBuildInfo = build.object() + def downstreamBuildName = devBuildInfo.metadata.name + def downstreamBuildUrl = devBuildInfo.metadata.annotations['openshift.io/jenkins-build-uri'] + echo "Downstream build ${downstreamBuildName}(${downstreamBuildUrl}) started." + } + } + } + } + {% else %} + {{ task_var_build_and_test }} + stage('Push container') { + when { + expression { + return env.GIT_REPO_REF == params.GIT_MAIN_BRANCH + } + } + steps { + script { + dir ("${env.HOME}/.docker") { + // for the OpenShift internal registry + def dockerConfig = readJSON text: '{ "auths": {} }' + dockerConfig.auths['docker-registry.default.svc:5000'] = [ + 'email': '', + 'auth': sh(returnStdout: true, script: 'set +x; echo -n "serviceaccount:$SERVICE_ACCOUNT_TOKEN" | base64 -').trim() + ] + // merging user specified credentials + if (params.CONTAINER_REGISTRY_CREDENTIALS) { + openshift.withCluster() { + def dockerconf = openshift.selector('secret', params.CONTAINER_REGISTRY_CREDENTIALS).object().data['.dockerconfigjson'] + def dockerString = new String(dockerconf.decodeBase64()) + toBeMerged = readJSON text: dockerString + dockerConfig.auths.putAll(toBeMerged.auths) + } + } + // writing to ~/.docker/config.json + writeJSON file: 'config.json', json: dockerConfig + } + env.RESULTING_IMAGE_REPOS.tokenize(',').each { + def sourceImage = it + ":" + env.RESULTING_TAG + def imageName = it.split('/').last() + def destImage = "${env.IMAGE_DESTINATION_NAMESPACE}/${imageName}:${env.IMAGE_TAG}" + // copy between registies + echo "Copying container from ${sourceImage} to ${destImage}" + sh "skopeo copy --src-cert-dir=/var/run/secrets/kubernetes.io/serviceaccount/ docker://${sourceImage} docker://${destImage}" + } + } + } + } + } + post { + cleanup { + script { + if (env.RESULTING_TAG) { + echo "Removing tag ${env.RESULTING_TAG} from the ImageStream..." + openshift.withCluster() { + openshift.withProject("${params.IMAGESTREAM_NAMESPACE}") { + openshift.tag("${params.IMAGESTREAM_NAME}:${env.RESULTING_TAG}", + "-d") + } + } + } + } + } + success { + script { + // on pre-merge workflow success + if (params.PAGURE_API_KEY_SECRET_NAME && env.PR_NO) { + try { + pagure.setBuildStatusOnPR(100, 'Build passed.') + echo "Updated PR #${env.PR_NO} status to PASS." + } catch (e) { + echo "Error updating PR #${env.PR_NO} status to PASS: ${e}" + } + } + // on post-merge workflow success + if (params.PAGURE_API_KEY_SECRET_NAME && !env.PR_NO) { + try { + pagure.flagCommit('success', 100, 'Build passed.') + echo "Updated commit ${env.GIT_COMMIT} status to PASS." + } catch (e) { + echo "Error updating commit ${env.GIT_COMMIT} status to PASS: ${e}" + } + } + } + } + failure { + script { + // on pre-merge workflow failure + if (params.PAGURE_API_KEY_SECRET_NAME && env.PR_NO) { + // updating Pagure PR flag + try { + pagure.setBuildStatusOnPR(0, 'Build failed.') + echo "Updated PR #${env.PR_NO} status to FAILURE." + } catch (e) { + echo "Error updating PR #${env.PR_NO} status to FAILURE: ${e}" + } + // making a comment + try { + pagure.commentOnPR(""" + Build ${env.GIT_COMMIT} [FAILED](${env.BUILD_URL})! + Rebase or make new commits to rebuild. + """.stripIndent(), env.PR_NO) + echo "Comment made." + } catch (e) { + echo "Error making a comment on PR #${env.PR_NO}: ${e}" + } + } + // on post-merge workflow failure + if (!env.PR_NO) { + // updating Pagure commit flag + if (params.PAGURE_API_KEY_SECRET_NAME) { + try { + pagure.flagCommit('failure', 0, 'Build failed.') + echo "Updated commit ${env.GIT_COMMIT} status to FAILURE." + } catch (e) { + echo "Error updating commit ${env.GIT_COMMIT} status to FAILURE: ${e}" + } + } + // sending email + if (params.MAIL_ADDRESS){ + try { + def recipient = params.MAIL_ADDRESS + def subject = "Jenkins job ${env.JOB_NAME} #${env.BUILD_NUMBER} failed." + def body = "Build URL: ${env.BUILD_URL}" + if (env.PR_NO) { + subject = "Jenkins job ${env.JOB_NAME}, PR #${env.PR_NO} ${status}." + body += "\nPull Request: ${env.PR_URL}" + } + emailext to: recipient, subject: subject, body: body + } catch (e) { + echo "Error sending email: ${e}" + } + } + } + } + } + {% endif %} + } +} diff --git a/roles/c3i/templates/build.yml b/roles/c3i/templates/build.yml new file mode 100644 index 0000000..a8f0c25 --- /dev/null +++ b/roles/c3i/templates/build.yml @@ -0,0 +1,58 @@ +kind: "BuildConfig" +apiVersion: "v1" +metadata: + name: {{ job_vars.name }} + labels: + app: {{ job_vars.name }} +spec: + runPolicy: "Serial" + completionDeadlineSeconds: 1800 + strategy: + type: JenkinsPipeline + jenkinsPipelineStrategy: + env: + - name: GIT_REPO + value: {{ c3i_git_repo }} + - name: GIT_REPO_REF + value: {{ job_vars.git_repo_ref }} + - name: GIT_MAIN_BRANCH + value: {{ c3i_git_main_branch }} + - name: OPENSHIFT_CLOUD_NAME + value: {{ c3i_cloud_name }} + - name: JENKINS_AGENT_IMAGE + value: {{ c3i_workflow_jenkins_image if "job-updater" in job_vars.name else c3i_jenkins_build_agent_image }} + - name: JENKINS_AGENT_SERVICE_ACCOUNT + value: {{ job_vars.name }}-jenkins-slave + - name: IMAGE_DESTINATION_NAMESPACE + value: {{ c3i_quay_address }}/{{ c3i_quay_namespace }} + - name: CONTAINER_REGISTRY_CREDENTIALS + value: {{ c3i_container_registry_credentials }} + - name: IMAGE_TAG + value: {{ c3i_dev_image_tag }} + - name: PAGURE_DOC_REPO_NAME + value: {{ c3i_pagure_doc_repo_name }} + - name: PAGURE_DOC_SECRET + value: {{ c3i_pagure_doc_secret }} + - name: MESSAGING_PROVIDER + value: {{ c3i_messaging_provider }} + - name: POSTMERGE_JOB + value: {{ c3i_component }}-postmerge + - name: MESSAGING_FEDMSG_PROVIDER + value: {{ c3i_fedmsg_provider }} + - name: PAGURE_API_KEY_SECRET_NAME + value: {{ c3i_pagure_api_key_secret }} + - name: IMAGESTREAM_NAMESPACE + value: {{ c3i_imagestream_namespace }} + - name: IMAGESTREAM_NAME + value: {{ c3i_imagestream_name }} + - name: MAIL_ADDRESS + value: {{ c3i_mail_address }} +{% for param in c3i_build_custom_parameters %} + - name: {{ param.name }} + value: {{ param.value }} +{% endfor %} + # CI_MESSAGE is used internally by JMS messaging plugin + - name: CI_MESSAGE + value: + jenkinsfile: | + {% filter indent(width=10) %}{% include "templates/build.Jenkinsfile" %}{% endfilter %} diff --git a/roles/c3i/templates/default-agent.groovy b/roles/c3i/templates/default-agent.groovy new file mode 100644 index 0000000..00fa9a3 --- /dev/null +++ b/roles/c3i/templates/default-agent.groovy @@ -0,0 +1,29 @@ +agent { + kubernetes { + cloud "${params.OPENSHIFT_CLOUD_NAME}" + label "jenkins-slave-${UUID.randomUUID().toString()}" + serviceAccount "${params.JENKINS_AGENT_SERVICE_ACCOUNT}" + defaultContainer 'jnlp' + yaml """ + apiVersion: v1 + kind: Pod + metadata: + labels: + app: "${env.JOB_BASE_NAME}" + factory2-pipeline-build-number: "${env.BUILD_NUMBER}" + spec: + containers: + - name: jnlp + image: "${params.JENKINS_AGENT_IMAGE}" + imagePullPolicy: Always + tty: true + resources: + requests: + memory: 512Mi + cpu: 200m + limits: + memory: 768Mi + cpu: 300m + """ + } +} diff --git a/roles/c3i/templates/get_paas_domain.groovy b/roles/c3i/templates/get_paas_domain.groovy new file mode 100644 index 0000000..9733732 --- /dev/null +++ b/roles/c3i/templates/get_paas_domain.groovy @@ -0,0 +1,14 @@ +if (!env.TRIGGER_NAMESPACE) { + env.TRIGGER_NAMESPACE = readFile("/run/secrets/kubernetes.io/serviceaccount/namespace").trim() +} +if(!env.PAAS_DOMAIN) { + openshift.withCluster() { + openshift.withProject(env.TRIGGER_NAMESPACE) { + def testroute = openshift.create('route', 'edge', "test-${env.BUILD_NUMBER}", '--service=test', '--port=8080') + def testhost = testroute.object().spec.host + env.PAAS_DOMAIN = testhost.minus("test-${env.BUILD_NUMBER}-${env.TRIGGER_NAMESPACE}.") + testroute.delete() + } + } + echo "Routes end with ${env.PAAS_DOMAIN}" +} diff --git a/roles/c3i/templates/greenwave-promote.Jenkinsfile b/roles/c3i/templates/greenwave-promote.Jenkinsfile new file mode 100644 index 0000000..d3e9d38 --- /dev/null +++ b/roles/c3i/templates/greenwave-promote.Jenkinsfile @@ -0,0 +1,79 @@ +library identifier: "c3i@{{ c3i_lib_branch }}", changelog: false, + retriever: modernSCM([$class: 'GitSCMSource', remote: "{{ c3i_lib_url }}"]) +pipeline { + {% include c3i_default_agent_snippet %} + + options { + timestamps() + timeout(time: 30, unit: 'MINUTES') + buildDiscarder(logRotator(numToKeepStr: '10')) + } + environment { + PIPELINE_NAMESPACE = readFile(file: '/run/secrets/kubernetes.io/serviceaccount/namespace').trim() + SERVICE_ACCOUNT_TOKEN = readFile(file: '/run/secrets/kubernetes.io/serviceaccount/token').trim() + } + triggers { + ciBuildTrigger( + noSquash: true, + providerList: [ + activeMQSubscriber( + name: params.MESSAGING_PROVIDER, + overrides: [topic: params.MESSAGING_TOPIC], + checks: [ + [field: '$.msg.subject_type', expectedValue: 'container-image'], + [field: '$.msg.subject_identifier', expectedValue: params.SUBJECT_IDENTIFIER_REGEX], + [field: '$.msg.decision_context', expectedValue: params.DECISION_CONTEXT_REGEX], + [field: '$.msg.policies_satisfied', expectedValue: 'true'], + ] + ) + ] + ) + } + stages { + stage("Message Check and setup") { + steps { + script { + if (params.CI_MESSAGE) { + def message = readJSON text: params.CI_MESSAGE + // Extract the digest of the image to be promoted. + // e.g. factory2/waiverdb@sha256:35201c572fc8a137862b7a256476add8d7465fa5043d53d117f4132402f8ef6b + // -> sha256:35201c572fc8a137862b7a256476add8d7465fa5043d53d117f4132402f8ef6b + def digest = (message.msg.subject_identifier =~ /@(sha256:\w+)$/)[0][1] + // Generate the pull spec of the image + // e.g. quay.io/factory2/waiverdb@sha256:35201c572fc8a137862b7a256476add8d7465fa5043d53d117f4132402f8ef6b + env.IMAGE = "${params.SOURCE_CONTAINER_REPO}@${digest}" + } else if (!params.IMAGE) { + error("This build is not started by a CI message. Only configurations were done.") + } + echo "Starting promotion of image ${env.IMAGE} to ${env.PROMOTING_DESTINATION}:${params.TARGET_TAG}..." + // Setting up registry credentials + dir ("${env.HOME}/.docker") { + // for the OpenShift internal registry + def dockerConfig = readJSON text: '{ "auths": {} }' + dockerConfig.auths['docker-registry.default.svc:5000'] = [ + 'email': '', + 'auth': sh(returnStdout: true, script: 'set +x; echo -n "serviceaccount:$SERVICE_ACCOUNT_TOKEN" | base64 -').trim() + ] + // merging user specified credentials + if (params.CONTAINER_REGISTRY_CREDENTIALS) { + openshift.withCluster() { + def dockerconf = openshift.selector('secret', params.CONTAINER_REGISTRY_CREDENTIALS).object().data['.dockerconfigjson'] + def dockerString = new String(dockerconf.decodeBase64()) + toBeMerged = readJSON text: dockerString + dockerConfig.auths.putAll(toBeMerged.auths) + } + } + // writing to ~/.docker/config.json + writeJSON file: 'config.json', json: dockerConfig + } + } + } + } + stage('Copy image') { + steps { + echo "Copy container image ${env.IMAGE} to ${env.PROMOTING_DESTINATION}:${env.TARGET_TAG}" + sh "skopeo copy docker://${env.IMAGE} docker://${env.PROMOTING_DESTINATION}:${env.TARGET_TAG}" + } + } + } +} diff --git a/roles/c3i/templates/greenwave-promote.yml b/roles/c3i/templates/greenwave-promote.yml new file mode 100644 index 0000000..efc5996 --- /dev/null +++ b/roles/c3i/templates/greenwave-promote.yml @@ -0,0 +1,46 @@ +kind: "BuildConfig" +apiVersion: "v1" +metadata: + name: {{ item }}-{{ job_vars.name_post }} + labels: + app: {{ item }}-{{ job_vars.name_post }} +spec: + runPolicy: "Serial" + completionDeadlineSeconds: 1800 + strategy: + type: JenkinsPipeline + jenkinsPipelineStrategy: + env: + - name: IMAGE + value: + - name: OPENSHIFT_CLOUD_NAME + value: {{ c3i_cloud_name }} + - name: JENKINS_AGENT_IMAGE + value: {{ c3i_workflow_jenkins_image }} + - name: JENKINS_AGENT_SERVICE_ACCOUNT + value: {{ item }}-{{ job_vars.name_post }}-jenkins-slave + - name: CONTAINER_REGISTRY_CREDENTIALS + value: {{ c3i_container_registry_credentials }} + - name: PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE + value: {{ c3i_pipeline_as_a_service_namespace }} + - name: MESSAGING_PROVIDER + value: {{ c3i_messaging_provider }} + - name: DECISION_CONTEXT_REGEX + value: {{ job_vars.decision_context_regex }} + - name: TARGET_TAG + value: {{ job_vars.target_tag }} + - name: SUBJECT_IDENTIFIER_REGEX + value: "^{{ c3i_quay_namespace }}/{{ item }}@sha256:" + - name: PROMOTING_DESTINATION + value: {{ job_vars.promoting_destination_prefix }}/{{ item }} + - name: SOURCE_CONTAINER_REPO + value: {{ job_vars.source_container_repo_prefix }}/{{ item }} + - name: MESSAGING_TOPIC + value: {{ job_vars.messaging_greenwave_topic_pre }}-{{ item }}{{ c3i_test_subscriber }}-{{ job_vars.messaging_greenwave_topic_post }} + - name: MAIL_ADDRESS + value: {{ c3i_mail_address }} + # CI_MESSAGE is used internally by JMS messaging plugin + - name: CI_MESSAGE + value: + jenkinsfile: | + {% filter indent(width=10) %}{% include "templates/greenwave-promote.Jenkinsfile" %}{% endfilter %} diff --git a/roles/c3i/templates/jenkins_agent_build_config.yml b/roles/c3i/templates/jenkins_agent_build_config.yml new file mode 100644 index 0000000..8f7a59c --- /dev/null +++ b/roles/c3i/templates/jenkins_agent_build_config.yml @@ -0,0 +1,34 @@ +kind: "BuildConfig" +apiVersion: "v1" +metadata: + name: "{{ c3i_component }}-jenkins-slave" + labels: + app: "{{ c3i_component }}" +spec: + runPolicy: "Serial" + completionDeadlineSeconds: 1800 + strategy: + dockerStrategy: + buildArgs: + - name: CA_URLS + value: https://password.corp.redhat.com/RH-IT-Root-CA.crt + forcePull: true + dockerfilePath: {{ c3i_jenkins_agent_buildconfig_dockerfile }} + resources: + requests: + memory: "512Mi" + cpu: "300m" + limits: + memory: "768Mi" + cpu: "500m" + source: + git: + uri: "{{ c3i_git_repo }}" + ref: "{{ c3i_git_main_branch }}" + contextDir: "{{ c3i_jenkins_agent_buildconfig_contextdir }}" + output: + to: + kind: "DockerImage" + name: "{{ c3i_jenkins_build_agent_image }}" + pushSecret: + name: "{{ c3i_container_registry_credentials }}" diff --git a/roles/c3i/templates/jobs/greenwave-promote-to-prod.yml b/roles/c3i/templates/jobs/greenwave-promote-to-prod.yml new file mode 100644 index 0000000..93f9d88 --- /dev/null +++ b/roles/c3i/templates/jobs/greenwave-promote-to-prod.yml @@ -0,0 +1,9 @@ +--- +name_post: "greenwave-promote-to-prod" +template: greenwave-promote.yml +messaging_greenwave_topic_pre: "Consumer.rh-jenkins-ci-plugin.c3i" +messaging_greenwave_topic_post: "greenwave-promote-to-prod.VirtualTopic.eng.greenwave.decision.update" +decision_context_regex: c3i_promote_stage_to_prod +target_tag: prod +promoting_destination_prefix: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}" +source_container_repo_prefix: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}" diff --git a/roles/c3i/templates/jobs/greenwave-promote-to-stage.yml b/roles/c3i/templates/jobs/greenwave-promote-to-stage.yml new file mode 100644 index 0000000..d2f5fd2 --- /dev/null +++ b/roles/c3i/templates/jobs/greenwave-promote-to-stage.yml @@ -0,0 +1,9 @@ +--- +name_post: "greenwave-promote-to-stage" +template: greenwave-promote.yml +messaging_greenwave_topic_pre: "Consumer.rh-jenkins-ci-plugin.c3i-" +messaging_greenwave_topic_post: "greenwave-promote-to-stage.VirtualTopic.eng.greenwave.decision.update" +decision_context_regex: c3i_promote_dev_to_stage +target_tag: stage +promoting_destination_prefix: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}" +source_container_repo_prefix: "{{ c3i_quay_address }}/{{ c3i_quay_namespace }}" diff --git a/roles/c3i/templates/jobs/job-updater.yml b/roles/c3i/templates/jobs/job-updater.yml new file mode 100644 index 0000000..8cc8f93 --- /dev/null +++ b/roles/c3i/templates/jobs/job-updater.yml @@ -0,0 +1,6 @@ +--- +name: "{{ c3i_component }}-job-updater" +template: build.yml +messaging_fedmsg_topics: + - io.pagure.prod.pagure.git.receive +git_repo_ref: master diff --git a/roles/c3i/templates/jobs/postmerge.yml b/roles/c3i/templates/jobs/postmerge.yml new file mode 100644 index 0000000..e1dd015 --- /dev/null +++ b/roles/c3i/templates/jobs/postmerge.yml @@ -0,0 +1,4 @@ +--- +name: "{{ c3i_component }}-postmerge" +template: build.yml +git_repo_ref: master diff --git a/roles/c3i/templates/jobs/premerge.yml b/roles/c3i/templates/jobs/premerge.yml new file mode 100644 index 0000000..c44f766 --- /dev/null +++ b/roles/c3i/templates/jobs/premerge.yml @@ -0,0 +1,9 @@ +--- +name: "{{ c3i_component }}-premerge" +template: build.yml +messaging_fedmsg_topics: + - io.pagure.prod.pagure.pull-request.new + - io.pagure.prod.pagure.pull-request.updated + - io.pagure.prod.pagure.pull-request.reopened + - io.pagure.prod.pagure.pull-request.rebased +git_repo_ref: diff --git a/roles/c3i/templates/jobs/trigger-on-latest-tag.yml b/roles/c3i/templates/jobs/trigger-on-latest-tag.yml new file mode 100644 index 0000000..6070470 --- /dev/null +++ b/roles/c3i/templates/jobs/trigger-on-latest-tag.yml @@ -0,0 +1,6 @@ +--- +name: "{{ c3i_component }}-trigger-on-latest-tag" +template: trigger-on-tag.yml +messaging_repotracker_topic: Consumer.rh-jenkins-ci-plugin.c3i-{{ c3i_test_subscriber | default(c3i_component) }}-trigger-on-latest-tag.VirtualTopic.eng.repotracker.container.tag.> +environment: stage +tracked_tag: latest diff --git a/roles/c3i/templates/jobs/trigger-on-stage-tag.yml b/roles/c3i/templates/jobs/trigger-on-stage-tag.yml new file mode 100644 index 0000000..f03ecb7 --- /dev/null +++ b/roles/c3i/templates/jobs/trigger-on-stage-tag.yml @@ -0,0 +1,6 @@ +--- +name: "{{ c3i_component }}-trigger-on-stage-tag" +template: trigger-on-tag.yml +messaging_repotracker_topic: Consumer.rh-jenkins-ci-plugin.c3i-{{ c3i_test_subscriber | default(c3i_component) }}-trigger-on-stage-tag.VirtualTopic.eng.repotracker.container.tag.> +environment: prod +tracked_tag: stage diff --git a/roles/c3i/templates/trigger-on-tag.Jenkinsfile b/roles/c3i/templates/trigger-on-tag.Jenkinsfile new file mode 100644 index 0000000..be3cc32 --- /dev/null +++ b/roles/c3i/templates/trigger-on-tag.Jenkinsfile @@ -0,0 +1,45 @@ +library identifier: "c3i@{{ c3i_lib_branch }}", changelog: false, + retriever: modernSCM([$class: 'GitSCMSource', remote: "{{ c3i_lib_url }}"]) +pipeline { + {% include c3i_default_agent_snippet %} + + options { + timestamps() + timeout(time: 60, unit: 'MINUTES') + buildDiscarder(logRotator(numToKeepStr: '10')) + } + triggers { + ciBuildTrigger( + noSquash: true, + providerList: [ + activeMQSubscriber( + name: params.MESSAGING_PROVIDER, + overrides: [topic: params.MESSAGING_TOPIC], + selector: "repo = '${params.TRACKED_CONTAINER_REPO}' AND action IN ('added', 'updated') AND tag = '${params.TRACKED_TAG}'", + ) + ] + ) + } + stages { + stage("Message Check and setup") { + steps { + script { + if (params.CI_MESSAGE) { + def message = readJSON text: params.CI_MESSAGE + echo "Tag :${message.tag} is ${message.action} in ${message.repo}. New digest: ${message.digest}" + env.IMAGE = "${message.repo}@${message.digest}" + env.PIPELINE_ID = "c3i-{{ c3i_component }}-tag-${params.TRACKED_TAG}-${message.digest[-9..-1]}" + } else if (params.IMAGE) { + env.PIPELINE_ID = "c3i-{{ c3i_component }}-tag-${params.TRACKED_TAG}-manual-${UUID.randomUUID().toString().substring(0, 4)}" + } else { + error("This build is not started by a CI message. Only configurations were done.") + } + echo "Triggering a job to test if ${env.IMAGE} meets all criteria of desired tag :${params.TRACKED_TAG}" + env.IMAGE_IS_SCRATCH = false + {% include "get_paas_domain.groovy" %} + } + } + } + {{ task_var_integration_test }} + } +} diff --git a/roles/c3i/templates/trigger-on-tag.yml b/roles/c3i/templates/trigger-on-tag.yml new file mode 100644 index 0000000..d98b5c1 --- /dev/null +++ b/roles/c3i/templates/trigger-on-tag.yml @@ -0,0 +1,50 @@ +kind: "BuildConfig" +apiVersion: "v1" +metadata: + name: {{ job_vars.name }} + labels: + app: {{ job_vars.name }} +spec: + runPolicy: "Serial" + completionDeadlineSeconds: 1800 + strategy: + type: JenkinsPipeline + jenkinsPipelineStrategy: + env: + - name: GIT_REPO + value: {{ c3i_git_repo }} + - name: GIT_REPO_REF + value: {{ c3i_git_main_branch }} + - name: MESSAGING_TOPIC + value: {{ job_vars.messaging_repotracker_topic }} + - name: IMAGE + value: + - name: OPENSHIFT_CLOUD_NAME + value: {{ c3i_cloud_name }} + - name: JENKINS_AGENT_IMAGE + value: {{ c3i_jenkins_test_agent_image }} + - name: JENKINS_AGENT_SERVICE_ACCOUNT + value: {{ job_vars.name }}-jenkins-slave + - name: CONTAINER_REGISTRY_CREDENTIALS + value: {{ c3i_container_registry_credentials }} + - name: PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE + value: {{ c3i_pipeline_as_a_service_namespace }} + - name: TRACKED_CONTAINER_REPO + value: {{ c3i_tracked_container_repo }} + - name: TRACKED_TAG + value: {{ job_vars.tracked_tag }} + - name: ENVIRONMENT + value: {{ job_vars.environment }} + - name: MESSAGING_PROVIDER + value: {{ c3i_messaging_provider }} + - name: MAIL_ADDRESS + value: {{ c3i_mail_address }} +{% for param in c3i_integration_test_custom_parameters %} + - name: {{ param.name }} + value: {{ param.value }} +{% endfor %} + # CI_MESSAGE is used internally by JMS messaging plugin + - name: CI_MESSAGE + value: + jenkinsfile: | + {% filter indent(width=10) %}{% include "templates/trigger-on-tag.Jenkinsfile" %}{% endfilter %} diff --git a/vars/c3i.groovy b/vars/c3i.groovy index 3598418..857c41d 100644 --- a/vars/c3i.groovy +++ b/vars/c3i.groovy @@ -212,14 +212,82 @@ def clone(Map args) { /** * Send message with container testing results. - * @param args.imageRepo Name of the image. - * @param args.imageDigest Digest of the image. - * @param args.imageVerRel Version of the image. E.g. 1.2.3-1 - * @param args.imageIsScratchBuild Boolean value. + * @param args.imageRef Full path to image - can be with tag or digest. + * @param args.digest Digest of the image. + * @param args.environment Environment of tests - stage, prod, dev + * @param args.scratch Boolean value. * @param args.provider UMB provider name defined in Jenkins. + * @param args.docs Documentation link + * @param args.email Contact email * @return Result from sendCIMessage. */ +def sendResultToMessageBus(Map args) { + def email = args.email ?: 'pnt-factory2-devel@redhat.com' + def environment = args.environment ?: env.ENVIRONMENT + def docs = args.docs ?: "" + def repourl = args.imageRef.tokenize('@')[0].replaceAll(':[^/]*$', '') + def (registry, reponame) = repourl.split('/', 2) + def image = reponame.split('/').last() + def provider = args.provider ?: 'Red Hat UMB' + boolean scratch = args.containsKey('scratch') ? args.scratch : true + def sendResult = sendCIMessage \ + providerName: provider, \ + overrides: [topic: 'VirtualTopic.eng.ci.container-image.test.complete'], \ + messageType: 'Custom', \ + messageProperties: '', \ + messageContent: """ + { + "ci": { + "name": "C3I Jenkins", + "team": "DevOps", + "url": "${env.JENKINS_URL}", + "docs": "${docs}", + "irc": "#pnt-devops-dev", + "email": "${email}", + "environment": "${environment}" + }, + "run": { + "url": "${env.BUILD_URL}", + "log": "${env.BUILD_URL}/console", + "debug": "", + "rebuild": "${env.BUILD_URL}/rebuild/parametrized" + }, + "artifact": { + "type": "container-image", + "repository": "${reponame}", + "digest": "${args.digest}", + "nvr": "${repourl}@${args.digest}", + "issuer": "c3i-jenkins", + "scratch": ${scratch}, + "id": "${image}@${args.digest}" + }, + "system": + [{ + "os": "${params.JENKINS_AGENT_IMAGE}", + "provider": "openshift", + "architecture": "x86_64" + }], + "type": "integration", + "category": "${environment}", + "status": "${currentBuild.result == null || currentBuild.result == 'SUCCESS' ? 'passed':'failed'}", + "xunit": "", + "generated_at": "${new Date().format("yyyy-MM-dd'T'HH:mm:ss'Z'", TimeZone.getTimeZone('UTC'))}", + "namespace": "c3i", + "version": "0.1.0" + } + """ + if (sendResult.getMessageId()) { + // echo sent message id and content + echo 'Successfully sent the test result to ResultsDB.' + echo "Message ID: ${sendResult.getMessageId()}" + echo "Message content: ${sendResult.getMessageContent()}" + } else { + echo 'Failed to sent the test result to ResultsDB.' + } +} + +//old way can be removed after update of dependent components def sendResultToMessageBus( String imageRepo, // e.g. 'waiverdb' String imageDigest, // e.g. 'sha256:79bd800433a1965d970dfa17b518c3b58e1e1c1528df77900d57c0fbc7dd5949'