From 8066614edae7051218ff85a0f0f53fbb1ea780e8 Mon Sep 17 00:00:00 2001 From: Michal Kovarik Date: Mar 11 2020 09:22:02 +0000 Subject: Use C3I role Move promotion and trigger logic into c3i-library and use only ansible role with snippets for build of image and acceptance testing. --- diff --git a/openshift/containers/jenkins-slave/Dockerfile b/openshift/containers/jenkins-slave/Dockerfile index 0c103e2..4c45ea0 100644 --- a/openshift/containers/jenkins-slave/Dockerfile +++ b/openshift/containers/jenkins-slave/Dockerfile @@ -20,9 +20,9 @@ ARG CA_URLS="" USER root RUN ${DNF_CMD} install -y \ - java-1.8.0-openjdk-headless gettext nss_wrapper git-core \ + java-1.8.0-openjdk-headless gettext git-core \ tar gzip skopeo wget make bind-utils \ - origin-clients \ + origin-clients python3-jinja2-cli \ # Jenkins pipeline 'sh' steps seem to require ps procps-ng \ # Tools to build and test waiverdb diff --git a/openshift/containers/jenkins-slave/jenkins-agent b/openshift/containers/jenkins-slave/jenkins-agent index 550e3a4..8614036 100644 --- a/openshift/containers/jenkins-slave/jenkins-agent +++ b/openshift/containers/jenkins-slave/jenkins-agent @@ -39,13 +39,10 @@ # Dynamically create a passwd file for non-arbitrary UIDs. # Taken from: https://docs.openshift.org/latest/creating_images/guidelines.html#openshift-origin-specific-guidelines -export USER_ID=$(id -u) -export GROUP_ID=$(id -g) - -# Skip for root user -if [ x"$USER_ID" != x"0" ]; then - cp /etc/passwd $NSS_WRAPPER_PASSWD - echo "jenkins:x:${USER_ID}:${GROUP_ID}:jenkins:${HOME}:/bin/bash" >> $NSS_WRAPPER_PASSWD +if ! whoami &> /dev/null; then + if [ -w /etc/passwd ]; then + echo "${USER_NAME:-default}:x:$(id -u):0:${USER_NAME:-default} user:${HOME}:/sbin/nologin" >> /etc/passwd + fi fi if [ $# -eq 1 ]; then diff --git a/openshift/pipelines/Makefile b/openshift/pipelines/Makefile deleted file mode 100644 index beb43ad..0000000 --- a/openshift/pipelines/Makefile +++ /dev/null @@ -1,38 +0,0 @@ -OC:=oc -OCFLAGS:= -JOBS_DIR:=jobs -TEMPLATES_DIR:=templates -JOB_PARAM_FILES:=$(wildcard $(JOBS_DIR)/*.env) -JOBS:=$(patsubst $(JOBS_DIR)/%.env,%,$(JOB_PARAM_FILES)) - -OC_CMD=$(OC) $(OCFLAGS) - -help: - @echo TARGETS - @echo -e "\tinstall\t\tInstall or update pipelines to OpenShift" - @echo -e "\tuninstall\tDelete installed pipelines from OpenShift" - @echo - @echo VARIABLES - @echo -e "\tJOBS\t\tSpace seperated list of pipeline jobs to install" - @echo -e "\tJOBS_DIR\tLooking for pipeline job definitions in an alternate directory." - @echo -e "\tTEMPLATES_DIR\tLooking for pipeline job templates in an alternate directory." - @echo -e "\tOC\t\tUse this oc command" - @echo -e "\tOCFLAGS\t\tOptions to append to the oc command arguments" -install: - @for job in $(JOBS); do \ - echo "[PIPELINE] Updating pipeline job \"$${job}\"..." ; \ - template_file=$$(cat ./$(JOBS_DIR)/$${job}.tmpl); \ - jinja2 ./$(TEMPLATES_DIR)/$${template_file} | $(OC_CMD) process --local -f - \ - --param-file ./$(JOBS_DIR)/$${job}.env | $(OC_CMD) apply -f -; \ - echo "[PIPELINE] Pipeline job \"$${job}\" updated" ; \ - done -uninstall: - @for job in $(JOBS); do \ - template_file=$$(cat ./$(JOBS_DIR)/$${job}.tmpl); \ - template_name=$${template_file%.y?ml}; \ - template_name=$${template_name%-template}; \ - echo "[PIPELINE] Deleting pipeline job \"$${job}\"..." ; \ - $(OC_CMD) delete all -l template="$$template_name" -l app="$$job" ;\ - echo "[PIPELINE] Pipeline job \"$${job}\" deleted" ; \ - done -.PHONY: help install uninstall diff --git a/openshift/pipelines/c3i-role-vars.yml b/openshift/pipelines/c3i-role-vars.yml new file mode 100644 index 0000000..1843d10 --- /dev/null +++ b/openshift/pipelines/c3i-role-vars.yml @@ -0,0 +1,19 @@ +c3i_component: waiverdb +c3i_build_and_test_snippet: snippets/waiverdb-build-and-test.groovy +c3i_build_agent_snippet: snippets/build-agent.groovy +c3i_integration_test_snippet: snippets/waiverdb-full-integration-test.groovy + +c3i_mail_address: pnt-factory2-devel@redhat.com + +c3i_integration_test_custom_parameters: + - name: BACKEND_INTEGRATION_TEST_REPO + value: https://gitlab.cee.redhat.com/devops/factory2-segment-tests.git + - name: BACKEND_INTEGRATION_TEST_REPO_BRANCH + value: master + - name: BACKEND_INTEGRATION_TEST_FILE + value: greenwave-segment-test/greenwave-segment-test-c3i.sh + +c3i_jenkins_test_agent_image: docker-registry.upshift.redhat.com/factory2/factory2-integration-test-jenkins-slave:latest + +c3i_lib_url: https://pagure.io/c3i-library.git +c3i_lib_branch: master diff --git a/openshift/pipelines/deploy.yml b/openshift/pipelines/deploy.yml new file mode 100644 index 0000000..81fe7c8 --- /dev/null +++ b/openshift/pipelines/deploy.yml @@ -0,0 +1,15 @@ +- 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/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.env b/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.env deleted file mode 100644 index e81e1d8..0000000 --- a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.env +++ /dev/null @@ -1,6 +0,0 @@ -NAME=waiverdb-greenwave-promote-to-prod -SOURCE_CONTAINER_REPO=quay.io/factory2/waiverdb -TARGET_TAG=prod -DECISION_CONTEXT_REGEX=c3i_promote_stage_to_prod -MESSAGING_TOPIC=Consumer.rh-jenkins-ci-plugin.c3i-waiverdb-promote-to-prod.VirtualTopic.eng.greenwave.decision.update -PROMOTING_DESTINATIONS=quay.io/factory2/waiverdb diff --git a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.tmpl b/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.tmpl deleted file mode 100644 index aab28b4..0000000 --- a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-prod.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-greenwave-trigger.yaml diff --git a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.env b/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.env deleted file mode 100644 index 1888ce9..0000000 --- a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.env +++ /dev/null @@ -1,6 +0,0 @@ -NAME=waiverdb-greenwave-promote-to-stage -SOURCE_CONTAINER_REPO=quay.io/factory2/waiverdb -TARGET_TAG=stage -DECISION_CONTEXT_REGEX=c3i_promote_dev_to_stage -MESSAGING_TOPIC=Consumer.rh-jenkins-ci-plugin.c3i-waiverdb-promote-to-stage.VirtualTopic.eng.greenwave.decision.update -PROMOTING_DESTINATIONS=quay.io/factory2/waiverdb diff --git a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.tmpl b/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.tmpl deleted file mode 100644 index aab28b4..0000000 --- a/openshift/pipelines/jobs/waiverdb-greenwave-promote-to-stage.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-greenwave-trigger.yaml diff --git a/openshift/pipelines/jobs/waiverdb-polling-for-master.env b/openshift/pipelines/jobs/waiverdb-polling-for-master.env deleted file mode 100644 index 77db732..0000000 --- a/openshift/pipelines/jobs/waiverdb-polling-for-master.env +++ /dev/null @@ -1,3 +0,0 @@ -NAME=waiverdb-polling-for-master -PAGURE_POLLING_SCHEDULE="H/5 * * * *" -PAGURE_POLLED_BRANCH=master diff --git a/openshift/pipelines/jobs/waiverdb-polling-for-master.tmpl b/openshift/pipelines/jobs/waiverdb-polling-for-master.tmpl deleted file mode 100644 index 3d4e8e5..0000000 --- a/openshift/pipelines/jobs/waiverdb-polling-for-master.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-polling-pagure.yaml diff --git a/openshift/pipelines/jobs/waiverdb-polling-for-prs.env b/openshift/pipelines/jobs/waiverdb-polling-for-prs.env deleted file mode 100644 index 907d8d3..0000000 --- a/openshift/pipelines/jobs/waiverdb-polling-for-prs.env +++ /dev/null @@ -1,3 +0,0 @@ -NAME=waiverdb-polling-for-prs -PAGURE_POLLING_FOR_PR=true -PAGURE_POLLING_SCHEDULE="H/5 * * * *" diff --git a/openshift/pipelines/jobs/waiverdb-polling-for-prs.tmpl b/openshift/pipelines/jobs/waiverdb-polling-for-prs.tmpl deleted file mode 100644 index 3d4e8e5..0000000 --- a/openshift/pipelines/jobs/waiverdb-polling-for-prs.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-polling-pagure.yaml diff --git a/openshift/pipelines/jobs/waiverdb-postmerge.env b/openshift/pipelines/jobs/waiverdb-postmerge.env deleted file mode 100644 index 320dbfe..0000000 --- a/openshift/pipelines/jobs/waiverdb-postmerge.env +++ /dev/null @@ -1,3 +0,0 @@ -NAME=waiverdb-postmerge -PAGURE_DOC_REPO_NAME= # Temporarily disable doc push to workaround https://pagure.io/pagure/issue/3919. Remove this line when it is fixed. -MAIL_ADDRESS=pnt-factory2-devel@redhat.com diff --git a/openshift/pipelines/jobs/waiverdb-postmerge.tmpl b/openshift/pipelines/jobs/waiverdb-postmerge.tmpl deleted file mode 100644 index 316b89d..0000000 --- a/openshift/pipelines/jobs/waiverdb-postmerge.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-build-template.yaml diff --git a/openshift/pipelines/jobs/waiverdb-premerge.env b/openshift/pipelines/jobs/waiverdb-premerge.env deleted file mode 100644 index 9c43e62..0000000 --- a/openshift/pipelines/jobs/waiverdb-premerge.env +++ /dev/null @@ -1 +0,0 @@ -NAME=waiverdb-premerge diff --git a/openshift/pipelines/jobs/waiverdb-premerge.tmpl b/openshift/pipelines/jobs/waiverdb-premerge.tmpl deleted file mode 100644 index 316b89d..0000000 --- a/openshift/pipelines/jobs/waiverdb-premerge.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-build-template.yaml diff --git a/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.env b/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.env deleted file mode 100644 index 65f4624..0000000 --- a/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.env +++ /dev/null @@ -1,4 +0,0 @@ -NAME=waiverdb-trigger-on-latest-tag -MESSAGING_TOPIC=Consumer.rh-jenkins-ci-plugin.c3i-waiverdb-trigger-on-latest-tag.VirtualTopic.eng.repotracker.container.tag.> -ENVIRONMENT=stage -TRACKED_TAG=latest diff --git a/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.tmpl b/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.tmpl deleted file mode 100644 index 9637c92..0000000 --- a/openshift/pipelines/jobs/waiverdb-trigger-on-latest-tag.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-repotracker-trigger.yaml diff --git a/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.env b/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.env deleted file mode 100644 index 48b2d71..0000000 --- a/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.env +++ /dev/null @@ -1,4 +0,0 @@ -NAME=waiverdb-trigger-on-stage-tag -MESSAGING_TOPIC=Consumer.rh-jenkins-ci-plugin.c3i-waiverdb-trigger-on-stage-tag.VirtualTopic.eng.repotracker.container.tag.> -TRACKED_TAG=stage -ENVIRONMENT=prod diff --git a/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.tmpl b/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.tmpl deleted file mode 100644 index 9637c92..0000000 --- a/openshift/pipelines/jobs/waiverdb-trigger-on-stage-tag.tmpl +++ /dev/null @@ -1 +0,0 @@ -waiverdb-repotracker-trigger.yaml diff --git a/openshift/pipelines/snippets/build-agent.groovy b/openshift/pipelines/snippets/build-agent.groovy new file mode 100644 index 0000000..14e6dc5 --- /dev/null +++ b/openshift/pipelines/snippets/build-agent.groovy @@ -0,0 +1,59 @@ +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: "jenkins-${env.JOB_BASE_NAME}" + factory2-pipeline-kind: "waiverdb-build-pipeline" + factory2-pipeline-build-number: "${env.BUILD_NUMBER}" + spec: + containers: + - name: jnlp + image: "${params.JENKINS_AGENT_IMAGE}" + imagePullPolicy: Always + tty: true + env: + - name: USER_NAME + value: jenkins + volumeMounts: + - name: postgresql-socket + mountPath: /var/run/postgresql + resources: + requests: + memory: 768Mi + cpu: 300m + limits: + memory: 1Gi + cpu: 500m + - name: db + image: registry.access.redhat.com/rhscl/postgresql-95-rhel7:latest + imagePullPolicy: Always + env: + - name: POSTGRESQL_USER + value: waiverdb + - name: POSTGRESQL_PASSWORD + value: waiverdb + - name: POSTGRESQL_DATABASE + value: waiverdb + volumeMounts: + - name: postgresql-socket + mountPath: /var/run/postgresql + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 384Mi + cpu: 200m + volumes: + - name: postgresql-socket + emptyDir: {} + """ + } +} diff --git a/openshift/pipelines/snippets/waiverdb-build-and-test.groovy b/openshift/pipelines/snippets/waiverdb-build-and-test.groovy new file mode 100644 index 0000000..edde49d --- /dev/null +++ b/openshift/pipelines/snippets/waiverdb-build-and-test.groovy @@ -0,0 +1,326 @@ +stage('Install pip dependencies') { + steps { + script { + if (sh(returnStatus: true, script: 'pip3 install --user -r ./requirements.txt') != 0) { + echo 'WARNING: Failed to install dependencies from requirements.txt.' + } + } + } +} +stage('Run checks') { + failFast false + parallel { + stage('Invoke Flake8') { + steps { + sh 'flake8' + } + } + stage('Invoke Pylint') { + steps { + sh 'pylint-3 --reports=n waiverdb' + } + } + } +} +stage('Run unit tests') { + steps { + sh 'cp conf/settings.py.example conf/settings.py' + // wait for the test datebase to come up + sh 'wait-for-it -s -t 300 127.0.0.1:5432' + // create a database role + sh "psql -h 127.0.0.1 -U postgres -q -d waiverdb -c 'CREATE ROLE \"jenkins\" WITH LOGIN SUPERUSER;'" + // run unit tests + sh 'py.test-3 -v --junitxml=junit-tests.xml tests' + } + post { + always { + junit 'junit-tests.xml' + } + } +} +stage('Build Artifacts') { + failFast false + parallel { + stage('Branch Docs') { + stages { + stage('Build Docs') { + steps { + sh 'make -C docs html' + } + post { + always { + archiveArtifacts artifacts: 'docs/_build/html/**' + } + } + } + stage('Publish Docs') { + when { + expression { + return "${params.PAGURE_DOC_REPO_NAME}" && (env.GIT_REPO_REF == params.PAGURE_MAIN_BRANCH || env.FORCE_PUBLISH_DOCS == "true") + } + } + steps { + sshagent (credentials: ["${env.TRIGGER_NAMESPACE}-${params.PAGURE_DOC_SECRET}"]) { + sh ''' + mkdir -p ~/.ssh/ + touch ~/.ssh/known_hosts + ssh-keygen -R pagure.io + echo 'pagure.io ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC198DWs0SQ3DX0ptu+8Wq6wnZMrXUCufN+wdSCtlyhHUeQ3q5B4Hgto1n2FMj752vToCfNTn9mWO7l2rNTrKeBsELpubl2jECHu4LqxkRVihu5UEzejfjiWNDN2jdXbYFY27GW9zymD7Gq3u+T/Mkp4lIcQKRoJaLobBmcVxrLPEEJMKI4AJY31jgxMTnxi7KcR+U5udQrZ3dzCn2BqUdiN5dMgckr4yNPjhl3emJeVJ/uhAJrEsgjzqxAb60smMO5/1By+yF85Wih4TnFtF4LwYYuxgqiNv72Xy4D/MGxCqkO/nH5eRNfcJ+AJFE7727F7Tnbo4xmAjilvRria/+l' >>~/.ssh/known_hosts + rm -rf docs-on-pagure + git clone ssh://git@pagure.io/docs/${params.PAGURE_DOC_REPO_NAME}.git docs-on-pagure + rm -rf docs-on-pagure/* + cp -r docs/_build/html/* docs-on-pagure/ + cd docs-on-pagure + git config user.name 'Pipeline Bot' + git config user.email "pipeline-bot@localhost.localdomain" + git add -A . + if [[ "$(git diff --cached --numstat | wc -l)" -eq 0 ]] ; then + exit 0 # No changes, nothing to commit + fi + git commit -m "Automatic commit of docs built by Jenkins job ${JOB_NAME} #${BUILD_NUMBER}" + git push origin master + ''' + } + } + } + } + } + stage('Build SRPM') { + steps { + sh './rpmbuild.sh -bs' + } + post { + success { + archiveArtifacts artifacts: 'rpmbuild-output/*.src.rpm' + } + } + } + stage('Branch RPM') { + stages { + stage('Build RPM') { + steps { + sh './rpmbuild.sh -bb' + } + post { + success { + archiveArtifacts artifacts: 'rpmbuild-output/*/*.rpm' + } + } + } + stage('Invoke Rpmlint') { + steps { + sh 'rpmlint -f rpmlint-config.py rpmbuild-output/*/*.rpm' + } + } + } + } + } +} +stage('Build container') { + environment { + BUILDCONFIG_INSTANCE_ID = "waiverdb-temp-${currentBuild.id}-${UUID.randomUUID().toString().substring(0,7)}" + } + steps { + script { + // Generate a version-release number for the target Git commit + def versions = sh(returnStdout: true, script: 'source ./version.sh && echo -en "$WAIVERDB_VERSION\n$WAIVERDB_CONTAINER_VERSION"').split('\n') + def waiverdb_version = versions[0] + env.TEMP_TAG = versions[1] + '-jenkins-' + currentBuild.id + + openshift.withCluster() { + // OpenShift BuildConfig doesn't support specifying a tag name at build time. + // We have to create a new BuildConfig for each container build. + // Create a BuildConfig from a seperated Template. + echo 'Creating a BuildConfig for container build...' + def template = readYaml file: 'openshift/waiverdb-container-template.yaml' + def processed = openshift.process(template, + "-p", "NAME=${env.BUILDCONFIG_INSTANCE_ID}", + '-p', "WAIVERDB_GIT_REPO=${params.GIT_REPO}", + // A pull-request branch, like pull/123/head, cannot be built with commit ID + // because refspec cannot be customized in an OpenShift build . + '-p', "WAIVERDB_GIT_REF=${env.PR_NO ? env.GIT_REPO_REF : env.GIT_COMMIT}", + '-p', "WAIVERDB_IMAGE_TAG=${env.TEMP_TAG}", + '-p', "WAIVERDB_VERSION=${waiverdb_version}", + '-p', "WAIVERDB_IMAGESTREAM_NAME=${params.IMAGESTREAM_NAME}", + '-p', "WAIVERDB_IMAGESTREAM_NAMESPACE=${params.IMAGESTREAM_NAMESPACE}", + ) + def build = c3i.buildAndWait(script: this, objs: processed) + echo 'Container build succeeds.' + def ocpBuild = build.object() + env.RESULTING_IMAGE_REF = ocpBuild.status.outputDockerImageReference + env.RESULTING_IMAGE_DIGEST = ocpBuild.status.output.to.imageDigest + def imagestream = openshift.selector('is', ['app': env.BUILDCONFIG_INSTANCE_ID]).object() + env.RESULTING_IMAGE_REPOS = imagestream.status.dockerImageRepository + env.RESULTING_TAG = env.TEMP_TAG + } + } + } + post { + failure { + echo "Failed to build container image ${env.TEMP_TAG}." + } + cleanup { + script { + openshift.withCluster() { + echo 'Tearing down...' + openshift.selector('bc', [ + 'app': env.BUILDCONFIG_INSTANCE_ID, + 'template': 'waiverdb-container-template', + ]).delete() + } + } + } + } +} +stage("Functional tests phase") { + stages { + stage('Prepare') { + steps { + script { + env.IMAGE = "${env.RESULTING_IMAGE_REPOS}:${env.RESULTING_TAG}" + } + } + } + stage('Cleanup') { + // Cleanup all test environments that were created 1 hour ago in case of failures of previous cleanups. + steps { + script { + openshift.withCluster() { + openshift.withProject(env.PIPELINE_ID) { + c3i.cleanup(script: this, age: 60, 'waiverdb') + } + } + } + } + } + stage('Run functional tests') { + environment { + // Jenkins BUILD_TAG could be too long (> 63 characters) for OpenShift to consume + TEST_ID = "${params.TEST_ID ?: 'jenkins-' + currentBuild.id + '-' + UUID.randomUUID().toString().substring(0,7)}" + } + steps { + echo "Container image ${env.IMAGE} will be tested." + script { + openshift.withCluster() { + // Don't set ENVIRONMENT_LABEL in the environment block! Otherwise you will get 2 different UUIDs. + env.ENVIRONMENT_LABEL = "test-${env.TEST_ID}" + def template = readYaml file: 'openshift/waiverdb-test-template.yaml' + def webPodReplicas = 1 // The current quota in UpShift is agressively limited + echo "Creating testing environment with TEST_ID=${env.TEST_ID}..." + def models = openshift.process(template, + '-p', "TEST_ID=${env.TEST_ID}", + '-p', "WAIVERDB_APP_IMAGE=${env.IMAGE}", + '-p', "WAIVERDB_REPLICAS=${webPodReplicas}", + ) + c3i.deployAndWait(script: this, objs: models, timeout: 15) + def appPod = openshift.selector('pods', ['environment': env.ENVIRONMENT_LABEL, 'service': 'web']).object() + env.IMAGE_DIGEST = appPod.status.containerStatuses[0].imageID.split('@')[1] + // Run functional tests + def route_hostname = openshift.selector('routes', ['environment': env.ENVIRONMENT_LABEL]).object().spec.host + echo "Running tests against https://${route_hostname}/" + withEnv(["WAIVERDB_TEST_URL=https://${route_hostname}/"]) { + sh 'py.test-3 -v --junitxml=junit-functional-tests.xml functional-tests/' + } + } + } + } + post { + always { + script { + junit 'junit-functional-tests.xml' + archiveArtifacts artifacts: 'junit-functional-tests.xml' + openshift.withCluster() { + /* Extract logs for debugging purposes */ + openshift.selector('deploy,pods', ['environment': env.ENVIRONMENT_LABEL]).logs() + } + } + } + cleanup { + script { + openshift.withCluster() { + /* Tear down everything we just created */ + echo "Tearing down test resources..." + try { + openshift.selector('dc,deploy,rc,configmap,secret,svc,route', + ['environment': env.ENVIRONMENT_LABEL]).delete() + } catch (e) { + echo "Failed to tear down test resources: ${e.message}" + } + } + } + } + } + } + } + post { + always { + script { + if (!env.IMAGE_DIGEST) { + // Don't send a message if the job fails before getting the image digest. + return; + } + if (!env.MESSAGING_PROVIDER) { + // Don't send a message if messaging provider is not configured + return + } + // currentBuild.result == null || currentBuild.result == 'SUCCESS' indicates a successful build, + // because it's possible that the pipeline engine hasn't set the value nor seen an error when reaching to this line. + // See example code in https://jenkins.io/doc/book/pipeline/jenkinsfile/#deploy + def sendResult = sendCIMessage \ + providerName: params.MESSAGING_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": "https://pagure.io/waiverdb/blob/master/f/openshift", + "irc": "#pnt-devops-dev", + "email": "pnt-factory2-devel@redhat.com", + "environment": "stage" + }, + "run": { + "url": "${env.BUILD_URL}", + "log": "${env.BUILD_URL}/console", + "debug": "", + "rebuild": "${env.BUILD_URL}/rebuild/parametrized" + }, + "artifact": { + "type": "container-image", + "repository": "factory2/waiverdb", + "digest": "${env.IMAGE_DIGEST}", + "nvr": "${env.IMAGE}", + "issuer": "c3i-jenkins", + "scratch": ${params.GIT_REPO_REF != params.PAGURE_MAIN_BRANCH}, + "id": "waiverdb@${env.IMAGE_DIGEST}" + }, + "system": + [{ + "os": "${params.JENKINS_AGENT_IMAGE}", + "provider": "openshift", + "architecture": "x86_64" + }], + "type": "integration", + "category": "dev", + "status": "${currentBuild.result == null || currentBuild.result == 'SUCCESS' ? 'passed':'failed'}", + "xunit": "${env.BUILD_URL}/artifacts/junit-functional-tests.xml", + "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.' + } + } + } + } +} diff --git a/openshift/pipelines/snippets/waiverdb-full-integration-test.groovy b/openshift/pipelines/snippets/waiverdb-full-integration-test.groovy new file mode 100644 index 0000000..d0ae046 --- /dev/null +++ b/openshift/pipelines/snippets/waiverdb-full-integration-test.groovy @@ -0,0 +1,59 @@ +stage('Run integration tests') { + stages { + stage('Request Pipeline') { + steps { + script { + openshift.withCluster() { + openshift.withProject(params.PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE) { + c3i.buildAndWait(script: this, objs: "bc/pipeline-as-a-service", + '-e', "DEFAULT_IMAGE_TAG=${env.ENVIRONMENT}", + '-e', "WAIVERDB_IMAGE=${env.IMAGE}", + '-e', "PIPELINE_ID=${env.PIPELINE_ID}", + '-e', "PAAS_DOMAIN=${env.PAAS_DOMAIN}", + '-e', "SERVICES_TO_DEPLOY='resultsdb-updater datanommer greenwave resultsdb umb waiverdb datagrepper krb5 ldap koji-hub'", + '-e', "TRIGGERED_BY=${env.BUILD_URL}" + ) + } + } + } + } + } + stage('Run integration test') { + steps { + script { + c3i.clone(repo: params.BACKEND_INTEGRATION_TEST_REPO, + branch: params.BACKEND_INTEGRATION_TEST_REPO_BRANCH) + sh "${env.WORKSPACE}/${BACKEND_INTEGRATION_TEST_FILE} https://${env.PIPELINE_ID}.${env.PAAS_DOMAIN}" + } + } + } + } + post { + changed { + script { + if (params.MAIL_ADDRESS) { + emailext to: "${env.MAIL_ADDRESS}", + subject: "${env.JOB_NAME} ${env.BUILD_NUMBER} changed: ${currentBuild.result}", + body: "${env.JOB_NAME} ${env.BUILD_NUMBER} changed. Current status: ${currentBuild.result}. You can check it out: ${env.BUILD_URL}" + } + } + } + always { + script { + pipeline_data = controller.getVars() + c3i.sendResultToMessageBus( + imageRef: pipeline_data.WAIVERDB_IMAGE, + digest: pipeline_data.WAIVERDB_IMAGE_DIGEST, + environment: env.ENVIRONMENT, + scratch: false, + docs: 'https://gitlab.cee.redhat.com/devops/factory2-segment-tests/tree/master/integration-test' + ) + } + } + failure { + script { + c3i.archiveContainersLogs(env.PIPELINE_ID) + } + } + } +} diff --git a/openshift/pipelines/templates/snippets/c3i-library-parameters.yaml b/openshift/pipelines/templates/snippets/c3i-library-parameters.yaml deleted file mode 100644 index 5f96d60..0000000 --- a/openshift/pipelines/templates/snippets/c3i-library-parameters.yaml +++ /dev/null @@ -1,8 +0,0 @@ -- name: C3I_LIB_URL - displayName: C3I library git url - required: true - value: "https://pagure.io/c3i-library.git" -- name: C3I_LIB_BRANCH - displayName: C3I library branch - required: true - value: "master" diff --git a/openshift/pipelines/templates/snippets/c3i-library.groovy b/openshift/pipelines/templates/snippets/c3i-library.groovy deleted file mode 100644 index e752c81..0000000 --- a/openshift/pipelines/templates/snippets/c3i-library.groovy +++ /dev/null @@ -1,2 +0,0 @@ -library identifier: "c3i@${C3I_LIB_BRANCH}", changelog: false, - retriever: modernSCM([$class: 'GitSCMSource', remote: "${C3I_LIB_URL}"]) diff --git a/openshift/pipelines/templates/snippets/default-agent.groovy b/openshift/pipelines/templates/snippets/default-agent.groovy deleted file mode 100644 index 00fa9a3..0000000 --- a/openshift/pipelines/templates/snippets/default-agent.groovy +++ /dev/null @@ -1,29 +0,0 @@ -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/openshift/pipelines/templates/snippets/waiverdb-full-integration-test.groovy b/openshift/pipelines/templates/snippets/waiverdb-full-integration-test.groovy deleted file mode 100644 index f265524..0000000 --- a/openshift/pipelines/templates/snippets/waiverdb-full-integration-test.groovy +++ /dev/null @@ -1,96 +0,0 @@ -stage('Run integration tests') { - stages { - stage('Request Pipeline') { - steps { - script { - env.TESTCASE_CATEGORY = env.ENVIRONMENT - 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}" - } - env.PIPELINE_ID = 'c3i-pipeline-' + UUID.randomUUID().toString().substring(0,4) - openshift.withCluster() { - openshift.withProject(params.PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE) { - c3i.buildAndWait(script: this, objs: "bc/pipeline-as-a-service", - '-e', "DEFAULT_IMAGE_TAG=${env.ENVIRONMENT}", - '-e', "WAIVERDB_IMAGE=${env.IMAGE}", - '-e', "PIPELINE_ID=${env.PIPELINE_ID}", - '-e', "PAAS_DOMAIN=${env.PAAS_DOMAIN}", - '-e', "SERVICES_TO_DEPLOY='resultsdb-updater datanommer greenwave resultsdb umb waiverdb datagrepper krb5 ldap koji'", - '-e', "TRIGGERED_BY=${env.BUILD_URL}" - ) - } - } - } - } - } - stage('Run integration test') { - steps { - script { - c3i.clone(repo: params.BACKEND_INTEGRATION_TEST_REPO, - branch: params.BACKEND_INTEGRATION_TEST_REPO_BRANCH) - sh "${env.WORKSPACE}/${BACKEND_INTEGRATION_TEST_FILE} https://${env.PIPELINE_ID}.${env.PAAS_DOMAIN}" - } - } - } - } - post { - changed { - script { - if (params.MAIL_ADDRESS) { - emailext to: "${env.MAIL_ADDRESS}", - subject: "${env.JOB_NAME} ${env.BUILD_NUMBER} changed: ${currentBuild.result}", - body: "${env.JOB_NAME} ${env.BUILD_NUMBER} changed. Current status: ${currentBuild.result}. You can check it out: ${env.BUILD_URL}" - } - } - } - always { - script { - if (!env.MESSAGING_PROVIDER) { - // Don't send a message if messaging provider is not configured - return - } - pipeline_data = controller.getVars() - // convert 'quay.io/factory2/waiverdb@sha256:1647bbaa..' or 'quay.io/factory2/waiverdb:tag' to factory2/waiverdb - def image_repo = pipeline_data.WAIVERDB_IMAGE.tokenize(':@')[0].tokenize('/')[1..-1].join('/') - c3i.sendResultToMessageBus( - image_repo, - pipeline_data.WAIVERDB_IMAGE_DIGEST, - env.BUILD_TAG, - env.TARGET_IMAGE_IS_SCRATCH == "true", - env.MESSAGING_PROVIDER - ) - } - } - failure { - script { - c3i.archiveContainersLogs(env.PIPELINE_ID) - } - } - cleanup { - script { - if (env.NO_CLEANUP_AFTER_TEST == 'true') { - return - } - openshift.withCluster() { - openshift.withProject(env.PIPELINE_ID) { - /* Tear down everything we just created */ - echo 'Tearing down test resources...' - openshift.selector('all,pvc,configmap,secret', - ['c3i.redhat.com/pipeline': env.PIPELINE_ID]).delete('--ignore-not-found=true') - } - } - } - } - } -} diff --git a/openshift/pipelines/templates/snippets/waiverdb-integration-test.groovy b/openshift/pipelines/templates/snippets/waiverdb-integration-test.groovy deleted file mode 100644 index 46c9326..0000000 --- a/openshift/pipelines/templates/snippets/waiverdb-integration-test.groovy +++ /dev/null @@ -1,152 +0,0 @@ -stage("Functional tests phase") { - stages { - stage('Prepare') { - steps { - script { - env.IMAGE = "${env.RESULTING_IMAGE_REPO}:${env.RESULTING_TAG}" - } - } - } - stage('Cleanup') { - // Cleanup all test environments that were created 1 hour ago in case of failures of previous cleanups. - steps { - script { - openshift.withCluster() { - openshift.withProject(env.PIPELINE_ID) { - c3i.cleanup(script: this, age: 60, 'waiverdb') - } - } - } - } - } - stage('Run functional tests') { - environment { - // Jenkins BUILD_TAG could be too long (> 63 characters) for OpenShift to consume - TEST_ID = "${params.TEST_ID ?: 'jenkins-' + currentBuild.id + '-' + UUID.randomUUID().toString().substring(0,7)}" - } - steps { - echo "Container image ${env.IMAGE} will be tested." - script { - openshift.withCluster() { - // Don't set ENVIRONMENT_LABEL in the environment block! Otherwise you will get 2 different UUIDs. - env.ENVIRONMENT_LABEL = "test-${env.TEST_ID}" - def template = readYaml file: 'openshift/waiverdb-test-template.yaml' - def webPodReplicas = 1 // The current quota in UpShift is agressively limited - echo "Creating testing environment with TEST_ID=${env.TEST_ID}..." - def models = openshift.process(template, - '-p', "TEST_ID=${env.TEST_ID}", - '-p', "WAIVERDB_APP_IMAGE=${env.IMAGE}", - '-p', "WAIVERDB_REPLICAS=${webPodReplicas}", - ) - c3i.deployAndWait(script: this, objs: models, timeout: 15) - def appPod = openshift.selector('pods', ['environment': env.ENVIRONMENT_LABEL, 'service': 'web']).object() - env.IMAGE_DIGEST = appPod.status.containerStatuses[0].imageID.split('@')[1] - // Run functional tests - def route_hostname = openshift.selector('routes', ['environment': env.ENVIRONMENT_LABEL]).object().spec.host - echo "Running tests against https://${route_hostname}/" - withEnv(["WAIVERDB_TEST_URL=https://${route_hostname}/"]) { - sh 'py.test-3 -v --junitxml=junit-functional-tests.xml functional-tests/' - } - } - } - } - post { - always { - script { - junit 'junit-functional-tests.xml' - archiveArtifacts artifacts: 'junit-functional-tests.xml' - openshift.withCluster() { - /* Extract logs for debugging purposes */ - openshift.selector('deploy,pods', ['environment': env.ENVIRONMENT_LABEL]).logs() - } - } - } - cleanup { - script { - openshift.withCluster() { - /* Tear down everything we just created */ - echo "Tearing down test resources..." - try { - openshift.selector('dc,deploy,rc,configmap,secret,svc,route', - ['environment': env.ENVIRONMENT_LABEL]).delete() - } catch (e) { - echo "Failed to tear down test resources: ${e.message}" - } - } - } - } - } - } - } - post { - always { - script { - if (!env.IMAGE_DIGEST) { - // Don't send a message if the job fails before getting the image digest. - return; - } - if (!env.MESSAGING_PROVIDER) { - // Don't send a message if messaging provider is not configured - return - } - // currentBuild.result == null || currentBuild.result == 'SUCCESS' indicates a successful build, - // because it's possible that the pipeline engine hasn't set the value nor seen an error when reaching to this line. - // See example code in https://jenkins.io/doc/book/pipeline/jenkinsfile/#deploy - def sendResult = sendCIMessage \ - providerName: params.MESSAGING_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": "https://pagure.io/waiverdb/blob/master/f/openshift", - "irc": "#pnt-devops-dev", - "email": "pnt-factory2-devel@redhat.com", - "environment": "stage" - }, - "run": { - "url": "${env.BUILD_URL}", - "log": "${env.BUILD_URL}/console", - "debug": "", - "rebuild": "${env.BUILD_URL}/rebuild/parametrized" - }, - "artifact": { - "type": "container-image", - "repository": "factory2/waiverdb", - "digest": "${env.IMAGE_DIGEST}", - "nvr": "${env.IMAGE}", - "issuer": "c3i-jenkins", - "scratch": ${params.WAIVERDB_GIT_REF != params.WAIVERDB_MAIN_BRANCH}, - "id": "waiverdb@${env.IMAGE_DIGEST}" - }, - "system": - [{ - "os": "${params.JENKINS_AGENT_IMAGE}", - "provider": "openshift", - "architecture": "x86_64" - }], - "type": "integration", - "category": "${params.ENVIRONMENT}", - "status": "${currentBuild.result == null || currentBuild.result == 'SUCCESS' ? 'passed':'failed'}", - "xunit": "${env.BUILD_URL}/artifacts/junit-functional-tests.xml", - "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.' - } - } - } - } -} diff --git a/openshift/pipelines/templates/waiverdb-build-template.yaml b/openshift/pipelines/templates/waiverdb-build-template.yaml deleted file mode 100644 index ca7de7c..0000000 --- a/openshift/pipelines/templates/waiverdb-build-template.yaml +++ /dev/null @@ -1,239 +0,0 @@ -# Template to produce a new WaiverDB build job in OpenShift. -# -# WaiverDB build job is a part of the WaiverDB C3I Pipeline, covering the following steps: -# -# - Run Flake8 and Pylint checks -# - Run unit tests -# - Build Docs -# - Publish Docs -# - Build SRPM -# - Build RPM -# - Invoke Rpmlint -# - Build container -# - Run functional tests -# - Push container -# -# Required Jenkins Plugins: -# - Openshift Sync plugin -# - Openshift Client plugin -# - Kubernetes plugin -# - SSH Agent plugin -# - Timestamper plugin -# ---- -apiVersion: v1 -kind: Template -metadata: - name: waiverdb-build-pipeline -parameters: -- name: NAME - displayName: Short unique identifier for the templated instances - description: This field is used to deploy multiple pipelines to one OpenShift project from this template. - required: true - value: waiverdb-build -- name: WAIVERDB_GIT_REPO - displayName: WaiverDB Git repo URL - description: Default WaiverDB Git repo URL in which to run dev tests against - required: true - value: "https://pagure.io/waiverdb.git" -- name: WAIVERDB_GIT_REF - displayName: WaiverDB Git repo ref - description: Default WaiverDB Git repo ref in which to run dev tests against - required: true - value: master -- name: WAIVERDB_MAIN_BRANCH - displayName: Name of the main branch. - description: If WAIVERDB_MAIN_BRANCH equals WAIVERDB_GIT_REF, this is a post-merge build, otherwise it's a pre-merge build. - value: master - required: true -- name: OPENSHIFT_CLOUD_NAME - displayName: Name of OpenShift cloud in Jenkins master configuration - required: true - value: openshift -- name: JENKINS_AGENT_IMAGE - displayName: Container image for Jenkins slave pods - required: true - value: docker-registry.upshift.redhat.com/factory2/waiverdb-jenkins-slave:latest -- name: PAGURE_DOC_REPO_NAME - displayName: namespace/project of Pagure doc repo for publishing docs - description: If not emptry, docs will be published to the specified Pagure doc repo when this is a post-merge build - required: false - value: waiverdb -- name: PAGURE_DOC_SECRET - displayName: Name of the OpenShift SSH secret for publishing docs to Pagure. - required: false - value: pagure-doc-secret -- name: WAIVERDB_DEV_IMAGE_DESTINATIONS - displayName: Comma seperated list of container repositories (without tag) to which the built WaiverDB dev image will be pushed - description: OpenShift registries must be prefixed with 'atomic:' - required: false - value: "quay.io/factory2/waiverdb" -- name: CONTAINER_REGISTRY_CREDENTIALS - displayName: Secret name of container registries used for pulling and pushing images - value: factory2-pipeline-registry-credentials - required: false -- name: WAIVERDB_DEV_IMAGE_TAG - displayName: Tag name of the resulting container image for development environment - value: "latest" - required: true -- name: WAIVERDB_IMAGESTREAM_NAME - displayName: Name of ImageStream for WaiverDB container images - required: true - value: waiverdb -- name: WAIVERDB_IMAGESTREAM_NAMESPACE - displayName: Namespace of ImageStream for WaiverDB container images - required: false -- name: WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAME - displayName: Name of BuildConfig for running integration tests - required: true - value: waiverdb-dev-integration-test -- name: WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAMESPACE - displayName: Namespace of BuildConfig for running integration tests - required: false -- name: FORCE_PUBLISH_IMAGE - displayName: Whether to push the resulting image regardless of the Git branch - value: "false" - required: true -- name: FORCE_PUBLISH_DOCS - displayName: Whether to publish docs regardless of the Git branch - value: "false" - required: true -- name: TAG_INTO_IMAGESTREAM - displayName: Whether to tag the pushed image as dev - value: "true" - required: true -- name: MESSAGING_PROVIDER - displayName: Name of the JMS messaging provider - value: Red Hat UMB -- name: PAGURE_URL - displayName: Pagure URL - value: https://pagure.io -- name: PAGURE_REPO_NAME - value: waiverdb -- name: PAGURE_REPO_IS_FORK - value: 'false' -- name: PAGURE_API_KEY_SECRET_NAME - displayName: Name of Pagure API key secret for updating Pagure pull-request statuses - value: 'pagure-api-key' -- name: MAIL_ADDRESS - displayName: If set, build failure messages to this mail address. -{% include "snippets/c3i-library-parameters.yaml" %} -labels: - template: waiverdb-build -objects: -- kind: "BuildConfig" - apiVersion: "v1" - metadata: - name: "${NAME}-jenkins-slave" - labels: - app: "${NAME}" - 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: openshift/containers/jenkins-slave/Dockerfile - resources: - requests: - memory: "512Mi" - cpu: "300m" - limits: - memory: "768Mi" - cpu: "500m" - source: - git: - uri: "${WAIVERDB_GIT_REPO}" - ref: "${WAIVERDB_GIT_REF}" - output: - to: - kind: "DockerImage" - name: "${JENKINS_AGENT_IMAGE}" - pushSecret: - name: "${CONTAINER_REGISTRY_CREDENTIALS}" - -- kind: ServiceAccount - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave" - labels: - app: "${NAME}" - -- kind: RoleBinding - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave_edit" - labels: - app: "${NAME}" - subjects: - - kind: ServiceAccount - name: "${NAME}-jenkins-slave" - roleRef: - name: edit - -- kind: "BuildConfig" - apiVersion: "v1" - metadata: - name: "${NAME}" - labels: - app: "${NAME}" - spec: - runPolicy: "Serial" - completionDeadlineSeconds: 1800 - strategy: - type: JenkinsPipeline - jenkinsPipelineStrategy: - env: - - name: "WAIVERDB_GIT_REPO" - value: "${WAIVERDB_GIT_REPO}" - - name: "WAIVERDB_GIT_REF" - value: "${WAIVERDB_GIT_REF}" - - name: "OPENSHIFT_CLOUD_NAME" - value: "${OPENSHIFT_CLOUD_NAME}" - - name: "JENKINS_AGENT_IMAGE" - value: "${JENKINS_AGENT_IMAGE}" - - name: "JENKINS_AGENT_SERVICE_ACCOUNT" - value: "${NAME}-jenkins-slave" - - name: "WAIVERDB_DEV_IMAGE_DESTINATIONS" - value: "${WAIVERDB_DEV_IMAGE_DESTINATIONS}" - - name: "CONTAINER_REGISTRY_CREDENTIALS" - value: "${CONTAINER_REGISTRY_CREDENTIALS}" - - name: "FORCE_PUBLISH_IMAGE" - value: "${FORCE_PUBLISH_IMAGE}" - - name: "TAG_INTO_IMAGESTREAM" - value: "${TAG_INTO_IMAGESTREAM}" - - name: "WAIVERDB_DEV_IMAGE_TAG" - value: "${WAIVERDB_DEV_IMAGE_TAG}" - - name: "WAIVERDB_IMAGESTREAM_NAME" - value: "${WAIVERDB_IMAGESTREAM_NAME}" - - name: "WAIVERDB_IMAGESTREAM_NAMESPACE" - value: "${WAIVERDB_IMAGESTREAM_NAMESPACE}" - - name: "FORCE_PUBLISH_DOCS" - value: "${FORCE_PUBLISH_DOCS}" - - name: "PAGURE_DOC_REPO_NAME" - value: "${PAGURE_DOC_REPO_NAME}" - - name: "PAGURE_DOC_SECRET" - value: "${PAGURE_DOC_SECRET}" - - name: "WAIVERDB_MAIN_BRANCH" - value: "${WAIVERDB_MAIN_BRANCH}" - - name: "WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAME" - value: "${WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAME}" - - name: "WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAMESPACE" - value: "${WAIVERDB_INTEGRATION_TEST_BUILD_CONFIG_NAMESPACE}" - - name: "MESSAGING_PROVIDER" - value: "${MESSAGING_PROVIDER}" - - name: PAGURE_REPO_NAME - value: "${PAGURE_REPO_NAME}" - - name: PAGURE_REPO_IS_FORK - value: "${PAGURE_REPO_IS_FORK}" - - name: PAGURE_URL - value: "${PAGURE_URL}" - - name: PAGURE_API_KEY_SECRET_NAME - value: "${PAGURE_API_KEY_SECRET_NAME}" - - name: MAIL_ADDRESS - value: "${MAIL_ADDRESS}" - jenkinsfile: | - {% filter indent(width=10) %}{% include "waiverdb-build.Jenkinsfile" %}{% endfilter %} diff --git a/openshift/pipelines/templates/waiverdb-build.Jenkinsfile b/openshift/pipelines/templates/waiverdb-build.Jenkinsfile deleted file mode 100644 index 6005d5a..0000000 --- a/openshift/pipelines/templates/waiverdb-build.Jenkinsfile +++ /dev/null @@ -1,491 +0,0 @@ -{% include "snippets/c3i-library.groovy" %} -import static org.apache.commons.lang.StringEscapeUtils.escapeHtml; -pipeline { - 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: "jenkins-${env.JOB_BASE_NAME}" - factory2-pipeline-kind: "waiverdb-build-pipeline" - factory2-pipeline-build-number: "${env.BUILD_NUMBER}" - spec: - containers: - - name: jnlp - image: "${params.JENKINS_AGENT_IMAGE}" - imagePullPolicy: Always - tty: true - env: - # Required by unit tests: Set up NSS Wrapper to generate a fake user name for the random UID assigned by OpenShift - - name: LD_PRELOAD - value: '/usr/lib64/libnss_wrapper.so' - - name: NSS_WRAPPER_PASSWD - value: '/tmp/passwd' - - name: NSS_WRAPPER_GROUP - value: '/etc/group' - volumeMounts: - - name: postgresql-socket - mountPath: /var/run/postgresql - resources: - requests: - memory: 768Mi - cpu: 300m - limits: - memory: 1Gi - cpu: 500m - - name: db - image: registry.access.redhat.com/rhscl/postgresql-95-rhel7:latest - imagePullPolicy: Always - env: - - name: POSTGRESQL_USER - value: waiverdb - - name: POSTGRESQL_PASSWORD - value: waiverdb - - name: POSTGRESQL_DATABASE - value: waiverdb - volumeMounts: - - name: postgresql-socket - mountPath: /var/run/postgresql - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 384Mi - cpu: 200m - volumes: - - name: postgresql-socket - emptyDir: {} - """ - } - } - options { - timestamps() - timeout(time: 30, unit: 'MINUTES') - } - environment { - TRIGGER_NAMESPACE = readFile('/run/secrets/kubernetes.io/serviceaccount/namespace').trim() - PIPELINE_USERNAME = sh(returnStdout: true, script: 'id -un').trim() - PAGURE_API = "${params.PAGURE_URL}/api/0" - PAGURE_REPO_IS_FORK = "${params.PAGURE_REPO_IS_FORK}" - PAGURE_REPO_HOME = "${env.PAGURE_URL}${env.PAGURE_REPO_IS_FORK == 'true' ? '/fork' : ''}/${params.PAGURE_REPO_NAME}" - } - stages { - stage('Prepare') { - steps { - script { - // check out specified branch/commit - /*def scmVars =*/ checkout([$class: 'GitSCM', - branches: [[name: params.WAIVERDB_GIT_REF]], - userRemoteConfigs: [[url: params.WAIVERDB_GIT_REPO, refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*/head:refs/remotes/origin/pull/*/head']], - ]) - - // get current commit ID - // FIXME: Due to a bug discribed in https://issues.jenkins-ci.org/browse/JENKINS-45489, - // the return value of checkout() is unreliable. - // Not working: env.WAIVERDB_GIT_COMMIT = scmVars.GIT_COMMIT - env.WAIVERDB_GIT_COMMIT = sh(returnStdout: true, script: 'git rev-parse HEAD').trim() - echo "Build ${params.WAIVERDB_GIT_REF}, commit=${env.WAIVERDB_GIT_COMMIT}" - - // Set GIT_COMMIT for pagure in c3i lib - env.GIT_COMMIT = env.WAIVERDB_GIT_COMMIT - - // Is the current branch a pull-request? If no, env.PR_NO will be empty. - env.PR_NO = getPrNo(params.WAIVERDB_GIT_REF) - - // Generate a version-release number for the target Git commit - def versions = sh(returnStdout: true, script: 'source ./version.sh && echo -en "$WAIVERDB_VERSION\n$WAIVERDB_CONTAINER_VERSION"').split('\n') - env.WAIVERDB_VERSION = versions[0] - env.WAIVERDB_CONTAINER_VERSION = versions[1] - env.TEMP_TAG = env.WAIVERDB_CONTAINER_VERSION + '-jenkins-' + currentBuild.id - - if (sh(returnStatus: true, script: 'pip3 install --user -r ./requirements.txt') != 0) { - echo 'WARNING: Failed to install dependencies from requirements.txt.' - } - } - } - } - stage('Update Build Info') { - when { - expression { - return params.PAGURE_URL && params.PAGURE_REPO_NAME - } - } - steps { - script { - // Set friendly display name and description - if (env.PR_NO) { // is pull-request - env.PR_URL = "${env.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 { // is a branch - currentBuild.displayName = "${env.WAIVERDB_GIT_REF}: ${env.WAIVERDB_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.WAIVERDB_GIT_COMMIT} status to PENDING." - } catch (e) { - echo "Error updating commit ${env.WAIVERDB_GIT_COMMIT} status to PENDING: ${e}" - } - } - } - } - } - } - stage('Run checks') { - failFast false - parallel { - stage('Invoke Flake8') { - steps { - sh 'flake8' - } - } - stage('Invoke Pylint') { - steps { - sh 'pylint-3 --reports=n waiverdb' - } - } - } - } - stage('Run unit tests') { - steps { - sh 'cp conf/settings.py.example conf/settings.py' - // wait for the test datebase to come up - sh 'wait-for-it -s -t 300 127.0.0.1:5432' - // create a database role - sh 'psql -h 127.0.0.1 -U "postgres" -q -d "waiverdb" -c "CREATE ROLE \"$PIPELINE_USERNAME\" WITH LOGIN SUPERUSER;"' - // run unit tests - sh 'py.test-3 -v --junitxml=junit-tests.xml tests' - } - post { - always { - junit 'junit-tests.xml' - } - } - } - stage('Build Artifacts') { - failFast false - parallel { - stage('Branch Docs') { - stages { - stage('Build Docs') { - steps { - sh 'make -C docs html' - } - post { - always { - archiveArtifacts artifacts: 'docs/_build/html/**' - } - } - } - stage('Publish Docs') { - when { - expression { - return "${params.PAGURE_DOC_REPO_NAME}" && (params.WAIVERDB_GIT_REF == params.WAIVERDB_MAIN_BRANCH || env.FORCE_PUBLISH_DOCS == "true") - } - } - steps { - sshagent (credentials: ["${env.TRIGGER_NAMESPACE}-${params.PAGURE_DOC_SECRET}"]) { - sh ''' - mkdir -p ~/.ssh/ - touch ~/.ssh/known_hosts - ssh-keygen -R pagure.io - echo 'pagure.io ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC198DWs0SQ3DX0ptu+8Wq6wnZMrXUCufN+wdSCtlyhHUeQ3q5B4Hgto1n2FMj752vToCfNTn9mWO7l2rNTrKeBsELpubl2jECHu4LqxkRVihu5UEzejfjiWNDN2jdXbYFY27GW9zymD7Gq3u+T/Mkp4lIcQKRoJaLobBmcVxrLPEEJMKI4AJY31jgxMTnxi7KcR+U5udQrZ3dzCn2BqUdiN5dMgckr4yNPjhl3emJeVJ/uhAJrEsgjzqxAb60smMO5/1By+yF85Wih4TnFtF4LwYYuxgqiNv72Xy4D/MGxCqkO/nH5eRNfcJ+AJFE7727F7Tnbo4xmAjilvRria/+l' >>~/.ssh/known_hosts - rm -rf docs-on-pagure - git clone ssh://git@pagure.io/docs/${PAGURE_DOC_REPO_NAME}.git docs-on-pagure - rm -rf docs-on-pagure/* - cp -r docs/_build/html/* docs-on-pagure/ - cd docs-on-pagure - git config user.name 'Pipeline Bot' - git config user.email "pipeline-bot@localhost.localdomain" - git add -A . - if [[ "$(git diff --cached --numstat | wc -l)" -eq 0 ]] ; then - exit 0 # No changes, nothing to commit - fi - git commit -m "Automatic commit of docs built by Jenkins job ${JOB_NAME} #${BUILD_NUMBER}" - git push origin master - ''' - } - } - } - } - } - stage('Build SRPM') { - steps { - sh './rpmbuild.sh -bs' - } - post { - success { - archiveArtifacts artifacts: 'rpmbuild-output/*.src.rpm' - } - } - } - stage('Branch RPM') { - stages { - stage('Build RPM') { - steps { - sh './rpmbuild.sh -bb' - } - post { - success { - archiveArtifacts artifacts: 'rpmbuild-output/*/*.rpm' - } - } - } - stage('Invoke Rpmlint') { - steps { - sh 'rpmlint -f rpmlint-config.py rpmbuild-output/*/*.rpm' - } - } - } - } - } - } - stage('Build container') { - environment { - BUILDCONFIG_INSTANCE_ID = "waiverdb-temp-${currentBuild.id}-${UUID.randomUUID().toString().substring(0,7)}" - } - steps { - script { - openshift.withCluster() { - // OpenShift BuildConfig doesn't support specifying a tag name at build time. - // We have to create a new BuildConfig for each container build. - // Create a BuildConfig from a seperated Template. - echo 'Creating a BuildConfig for container build...' - def template = readYaml file: 'openshift/waiverdb-container-template.yaml' - def processed = openshift.process(template, - "-p", "NAME=${env.BUILDCONFIG_INSTANCE_ID}", - '-p', "WAIVERDB_GIT_REPO=${params.WAIVERDB_GIT_REPO}", - // A pull-request branch, like pull/123/head, cannot be built with commit ID - // because refspec cannot be customized in an OpenShift build . - '-p', "WAIVERDB_GIT_REF=${env.PR_NO ? params.WAIVERDB_GIT_REF : env.WAIVERDB_GIT_COMMIT}", - '-p', "WAIVERDB_IMAGE_TAG=${env.TEMP_TAG}", - '-p', "WAIVERDB_VERSION=${env.WAIVERDB_VERSION}", - '-p', "WAIVERDB_IMAGESTREAM_NAME=${params.WAIVERDB_IMAGESTREAM_NAME}", - '-p', "WAIVERDB_IMAGESTREAM_NAMESPACE=${params.WAIVERDB_IMAGESTREAM_NAMESPACE}", - ) - def build = c3i.buildAndWait(script: this, objs: processed) - echo 'Container build succeeds.' - def ocpBuild = build.object() - env.RESULTING_IMAGE_REF = ocpBuild.status.outputDockerImageReference - env.RESULTING_IMAGE_DIGEST = ocpBuild.status.output.to.imageDigest - def imagestream = openshift.selector('is', ['app': env.BUILDCONFIG_INSTANCE_ID]).object() - env.RESULTING_IMAGE_REPO = imagestream.status.dockerImageRepository - env.RESULTING_TAG = env.TEMP_TAG - } - } - } - post { - failure { - echo "Failed to build container image ${env.TEMP_TAG}." - } - cleanup { - script { - openshift.withCluster() { - echo 'Tearing down...' - openshift.selector('bc', [ - 'app': env.BUILDCONFIG_INSTANCE_ID, - 'template': 'waiverdb-container-template', - ]).delete() - } - } - } - } - } - {% include "snippets/waiverdb-integration-test.groovy" %} - stage('Push container') { - when { - expression { - return params.FORCE_PUBLISH_IMAGE == 'true' || - params.WAIVERDB_GIT_REF == params.WAIVERDB_MAIN_BRANCH - } - } - steps { - script { - def destinations = env.WAIVERDB_DEV_IMAGE_DESTINATIONS ? - env.WAIVERDB_DEV_IMAGE_DESTINATIONS.split(',') : [] - openshift.withCluster() { - def sourceImage = env.RESULTING_IMAGE_REPO + ":" + env.RESULTING_TAG - if (params.CONTAINER_REGISTRY_CREDENTIALS) { - dir ("${env.HOME}/.docker") { - def dockerconf = openshift.selector('secret', params.CONTAINER_REGISTRY_CREDENTIALS).object().data['.dockerconfigjson'] - writeFile file: 'config.json', text: dockerconf, encoding: "Base64" - } - } - // pull the built image from imagestream - echo "Pulling container from ${sourceImage}..." - def registryToken = readFile(file: '/var/run/secrets/kubernetes.io/serviceaccount/token') - withEnv(["SOURCE_IMAGE_REF=${sourceImage}", "TOKEN=${registryToken}"]) { - sh '''set -e +x # hide the token from Jenkins console - mkdir -p _build - skopeo copy \ - --src-cert-dir=/var/run/secrets/kubernetes.io/serviceaccount/ \ - --src-creds=serviceaccount:"$TOKEN" \ - docker://"$SOURCE_IMAGE_REF" dir:_build/waiverdb_container - ''' - } - // push to registries - def pushTasks = destinations.collectEntries { - ["Pushing ${it}" : { - def dest = "${it}:${params.WAIVERDB_DEV_IMAGE_TAG ?: 'latest'}" - // Only docker and atomic registries are allowed - if (!dest.startsWith('atomic:') && !dest.startsWith('docker://')) { - dest = 'docker://' + dest - } - echo "Pushing container to ${dest}..." - withEnv(["DEST_IMAGE_REF=${dest}"]) { - /* Pushes to the internal registry can sometimes randomly fail - * with "unknown blob" due to a known issue with the registry - * storage configuration. So we retry up to 5 times. */ - retry(5) { - sh 'skopeo copy dir:_build/waiverdb_container "$DEST_IMAGE_REF"' - } - } - }] - } - parallel pushTasks - } - } - } - } - stage('Tag into image stream') { - when { - expression { - return "${params.WAIVERDB_DEV_IMAGE_TAG}" && params.TAG_INTO_IMAGESTREAM == "true" && - (params.FORCE_PUBLISH_IMAGE == 'true' || params.WAIVERDB_GIT_REF == params.WAIVERDB_MAIN_BRANCH) - } - } - steps { - script { - openshift.withCluster() { - openshift.withProject("${params.WAIVERDB_IMAGESTREAM_NAMESPACE}") { - def sourceRef = "${params.WAIVERDB_IMAGESTREAM_NAME}:${env.RESULTING_TAG}" - def destRef = "${params.WAIVERDB_IMAGESTREAM_NAME}:${params.WAIVERDB_DEV_IMAGE_TAG}" - echo "Tagging ${sourceRef} as ${destRef}..." - openshift.tag("${sourceRef}", "${destRef}") - } - } - } - } - } - } - post { - cleanup { - script { - if (env.RESULTING_TAG) { - echo "Removing tag ${env.RESULTING_TAG} from the ImageStream..." - openshift.withCluster() { - openshift.withProject("${params.WAIVERDB_IMAGESTREAM_NAMESPACE}") { - openshift.tag("${params.WAIVERDB_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.WAIVERDB_GIT_COMMIT} status to PASS." - } catch (e) { - echo "Error updating commit ${env.WAIVERDB_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.WAIVERDB_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.WAIVERDB_GIT_COMMIT} status to FAILURE." - } catch (e) { - echo "Error updating commit ${env.WAIVERDB_GIT_COMMIT} status to FAILURE: ${e}" - } - } - // sending email - if (params.MAIL_ADDRESS){ - try { - sendBuildStatusEmail('failed') - } catch (e) { - echo "Error sending email: ${e}" - } - } - } - } - } - } -} -@NonCPS -def getPrNo(branch) { - def prMatch = branch =~ /^(?:.+\/)?pull\/(\d+)\/head$/ - return prMatch ? prMatch[0][1] : '' -} - -def sendBuildStatusEmail(String status) { - def recipient = params.MAIL_ADDRESS - def subject = "Jenkins job ${env.JOB_NAME} #${env.BUILD_NUMBER} ${status}." - 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 -} diff --git a/openshift/pipelines/templates/waiverdb-greenwave-trigger.Jenkinsfile b/openshift/pipelines/templates/waiverdb-greenwave-trigger.Jenkinsfile deleted file mode 100644 index d8a758a..0000000 --- a/openshift/pipelines/templates/waiverdb-greenwave-trigger.Jenkinsfile +++ /dev/null @@ -1,124 +0,0 @@ -// Use scripted syntax because CIBuildTrigger currently doesn't support the declarative syntax -{% include "snippets/c3i-library.groovy" %} -pipeline { - {% include "snippets/default-agent.groovy" %} - 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: false, - 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) { - error("This build is not started by a CI message. Only configurations were done.") - } - 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}" - echo "Starting promotion of image ${env.IMAGE} to :${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('Pull image') { - steps { - echo "Pulling container image ${env.IMAGE}..." - withEnv(["SOURCE_IMAGE_REF=${env.IMAGE}"]) { - sh ''' - set -e +x # hide the token from Jenkins console - mkdir -p _image - skopeo copy docker://"$SOURCE_IMAGE_REF" dir:_image - ''' - } - } - } - stage('Promote image') { - steps { - script { - def destinations = params.PROMOTING_DESTINATIONS ? params.PROMOTING_DESTINATIONS.split(',') : [] - openshift.withCluster() { - def pushTasks = destinations.collectEntries { - ["Pushing ${it}" : { - def dest = "${it}:${params.TARGET_TAG}" - // Only docker and atomic registries are allowed - if (!dest.startsWith('atomic:') && !dest.startsWith('docker://')) { - dest = "docker://${dest}" - } - echo "Pushing container image to ${dest}..." - withEnv(["DEST_IMAGE_REF=${dest}"]) { - retry(5) { - sh 'skopeo copy dir:_image "$DEST_IMAGE_REF"' - } - } - }] - } - parallel pushTasks - } - } - } - } - stage('Tag ImageStream') { - when { - expression { - return params.DEST_IMAGESTREAM_NAME && params.TAG_INTO_IMAGESTREAM == "true" - } - } - steps { - script { - def destRef = "${params.DEST_IMAGESTREAM_NAMESPACE ?: env.PIPELINE_NAMESPACE}/${params.DEST_IMAGESTREAM_NAME}:${params.TARGET_TAG}" - openshift.withCluster() { - echo "Tagging ${env.IMAGE} into ${destRef}..." - openshift.tag('--source=docker', env.IMAGE, destRef) - } - } - } - } - } -} diff --git a/openshift/pipelines/templates/waiverdb-greenwave-trigger.yaml b/openshift/pipelines/templates/waiverdb-greenwave-trigger.yaml deleted file mode 100644 index f6d0fed..0000000 --- a/openshift/pipelines/templates/waiverdb-greenwave-trigger.yaml +++ /dev/null @@ -1,127 +0,0 @@ -# Template to produce a new OpenShift pipeline job for triggering a build on repotracker messages -# ---- -apiVersion: v1 -kind: Template -metadata: - name: waiverdb-greenwave-trigger -labels: - template: waiverdb-greenwave-trigger -parameters: -- name: NAME - displayName: Short unique identifier for the templated instances - description: This field is used to deploy multiple pipelines to one OpenShift project from this template. - value: waiverdb-greenwave-trigger -- name: DECISION_CONTEXT_REGEX - displayName: Regex pattern for Greenwave decision context in CI message - required: true -- name: SUBJECT_IDENTIFIER_REGEX - displayName: Regex pattern for Greenwave subject identifier in CI message - value: ^factory2/waiverdb@ -- name: SOURCE_CONTAINER_REPO - displayName: Container repo of the image - value: quay.io/factory2/waiverdb -- name: TARGET_TAG - displayName: Tag name to promote the image to - required: true -- name: CONTAINER_REGISTRY_CREDENTIALS - displayName: Secret name of container registries used for pulling and pushing images - value: factory2-pipeline-registry-credentials - required: false -- name: TAG_INTO_IMAGESTREAM - displayName: Whether to tag the image into an ImageStream - value: "false" - required: true -- name: PROMOTING_DESTINATIONS - displayName: Comma seperated list of container repositories (without tags) to which the image will be promoted - description: OpenShift registries must be prefixed with 'atomic:' - required: false - value: "quay.io/factory2/waiverdb" -- name: DEST_IMAGESTREAM_NAME - displayName: Name of the ImageStream to be tagged - required: false - value: waiverdb -- name: DEST_IMAGESTREAM_NAMESPACE - displayName: Namespace of the ImageStream to be tagged - description: Leaving blank means using the same namespace as the pipeline build - required: false - value: waiverdb-stage -- name: MESSAGING_PROVIDER - displayName: Name of the JMS messaging provider - value: Red Hat UMB -- name: MESSAGING_TOPIC - displayName: Name of the topic that the trigger subscribes to - value: "Consumer.rh-jenkins-ci-plugin.c3i-greenwave-trigger.VirtualTopic.eng.greenwave.decision.update" -- name: JENKINS_AGENT_IMAGE - displayName: Container image for Jenkins slave pods - value: docker-registry.upshift.redhat.com/factory2/waiverdb-jenkins-slave:latest -- name: OPENSHIFT_CLOUD_NAME - displayName: Name of OpenShift cloud in Jenkins master configuration - value: openshift -{% include "snippets/c3i-library-parameters.yaml" %} -objects: -- kind: ServiceAccount - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave" - labels: - app: "${NAME}" -- kind: RoleBinding - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave_edit" - labels: - app: "${NAME}" - subjects: - - kind: ServiceAccount - name: "${NAME}-jenkins-slave" - roleRef: - name: edit -- kind: "BuildConfig" - apiVersion: "v1" - metadata: - name: "${NAME}" - labels: - app: "${NAME}" - spec: - runPolicy: "Serial" - completionDeadlineSeconds: 1800 - strategy: - type: JenkinsPipeline - jenkinsPipelineStrategy: - env: - - name: OPENSHIFT_CLOUD_NAME - value: "${OPENSHIFT_CLOUD_NAME}" - - name: PROMOTING_DESTINATIONS - value: "${PROMOTING_DESTINATIONS}" - - name: CONTAINER_REGISTRY_CREDENTIALS - value: "${CONTAINER_REGISTRY_CREDENTIALS}" - - name: TAG_INTO_IMAGESTREAM - value: "${TAG_INTO_IMAGESTREAM}" - - name: DEST_IMAGESTREAM_NAME - value: "${DEST_IMAGESTREAM_NAME}" - - name: DEST_IMAGESTREAM_NAMESPACE - value: "${DEST_IMAGESTREAM_NAMESPACE}" - - name: JENKINS_AGENT_IMAGE - value: "${JENKINS_AGENT_IMAGE}" - - name: JENKINS_AGENT_SERVICE_ACCOUNT - value: "${NAME}-jenkins-slave" - - name: SOURCE_CONTAINER_REPO - value: "${SOURCE_CONTAINER_REPO}" - - name: TARGET_TAG - value: "${TARGET_TAG}" - - name: DECISION_CONTEXT_REGEX - value: "${DECISION_CONTEXT_REGEX}" - - name: SUBJECT_IDENTIFIER_REGEX - value: "${SUBJECT_IDENTIFIER_REGEX}" - - name: MESSAGING_PROVIDER - value: "${MESSAGING_PROVIDER}" - - name: MESSAGING_TOPIC - value: "${MESSAGING_TOPIC}" - # CI_MESSAGE and MESSAGE_HEADERS are used internally by JMS messaging plugin - - name: CI_MESSAGE - value: - - name: MESSAGE_HEADERS - value: - jenkinsfile: | - {% filter indent(width=10) %}{% include "waiverdb-greenwave-trigger.Jenkinsfile" %}{% endfilter %} diff --git a/openshift/pipelines/templates/waiverdb-polling-pagure.Jenkinsfile b/openshift/pipelines/templates/waiverdb-polling-pagure.Jenkinsfile deleted file mode 100644 index dec4684..0000000 --- a/openshift/pipelines/templates/waiverdb-polling-pagure.Jenkinsfile +++ /dev/null @@ -1,136 +0,0 @@ -{% include "snippets/c3i-library.groovy" %} -pipeline { - agent { - kubernetes { - cloud "${OPENSHIFT_CLOUD_NAME}" - label "jenkins-slave-${UUID.randomUUID().toString()}" - serviceAccount "${NAME}-jenkins-slave" - defaultContainer 'jnlp' - yaml """ - apiVersion: v1 - kind: Pod - metadata: - labels: - app: "jenkins-${env.JOB_BASE_NAME}" - factory2-pipeline-kind: "waiverdb-polling-to-pagure-pipeline" - factory2-pipeline-build-number: "${env.BUILD_NUMBER}" - spec: - containers: - - name: jnlp - image: "${JENKINS_AGENT_IMAGE}" - imagePullPolicy: Always - tty: true - resources: - requests: - memory: 378Mi - cpu: 200m - limits: - memory: 768Mi - cpu: 500m - """ - } - } - options { - timestamps() - } - environment { - PIPELINE_NAMESPACE = readFile('/run/secrets/kubernetes.io/serviceaccount/namespace').trim() - PAGURE_URL = "${PAGURE_URL}" - PAGURE_REPO_IS_FORK = "${PAGURE_REPO_IS_FORK}" - PAGURE_POLLING_FOR_PR = "${PAGURE_POLLING_FOR_PR}" - PAGURE_REPO_HOME = "${env.PAGURE_URL}${env.PAGURE_REPO_IS_FORK == 'true' ? '/fork' : ''}/${PAGURE_REPO_NAME}" - GIT_URL = "${env.PAGURE_URL}/${env.PAGURE_REPO_IS_FORK == 'true' ? 'forks/' : ''}${PAGURE_REPO_NAME}.git" - PREMERGE_JOB_NAME = "${PREMERGE_JOB_NAME}" - POSTMERGE_JOB_NAME = "${POSTMERGE_JOB_NAME}" - } - triggers { pollSCM("${PAGURE_POLLING_SCHEDULE}") } - stages { - stage('Prepare') { - agent { label 'master' } - steps { - script { - // checking out the polled branch - def polledBranch = env.PAGURE_POLLING_FOR_PR == 'true' ? 'origin/pull/*/head' : "origin/${PAGURE_POLLED_BRANCH}" - def scmVars = checkout([$class: 'GitSCM', - branches: [[name: polledBranch]], - userRemoteConfigs: [ - [ - name: 'origin', - url: env.GIT_URL, - refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*/head:refs/remotes/origin/pull/*/head', - ], - ], - extensions: [[$class: 'CleanBeforeCheckout']], - ]) - env.WAIVERDB_GIT_COMMIT = scmVars.GIT_COMMIT - // setting build display name - def prefix = 'origin/' - def branch = scmVars.GIT_BRANCH.startsWith(prefix) ? scmVars.GIT_BRANCH.substring(prefix.size()) - : scmVars.GIT_BRANCH // origin/pull/1234/head -> pull/1234/head, origin/master -> master - env.WAIVERDB_GIT_BRANCH = branch - echo "Build on branch=${env.WAIVERDB_GIT_BRANCH}, commit=${env.WAIVERDB_GIT_COMMIT}" - if (env.PAGURE_POLLING_FOR_PR == 'false') { - currentBuild.displayName = "${env.WAIVERDB_GIT_BRANCH}: ${env.WAIVERDB_GIT_COMMIT.substring(0, 7)}" - currentBuild.description = """${currentBuild.displayName}""" - } - else if (env.PAGURE_POLLING_FOR_PR == 'true' && branch ==~ /^pull\/[0-9]+\/head$/) { - env.PR_NO = branch.split('/')[1] - env.PR_URL = "${env.PAGURE_REPO_HOME}/pull-request/${env.PR_NO}" - // To HTML syntax in build description, go to `Jenkins/Global Security/Markup Formatter` and select 'Safe HTML'. - def pagureLink = """PR#${env.PR_NO}""" - echo "Building PR #${env.PR_NO}: ${env.PR_URL}" - currentBuild.displayName = "PR#${env.PR_NO}" - currentBuild.description = pagureLink - } else { // This shouldn't happen. - error("Build is aborted due to unexpected polling trigger actions.") - } - } - } - } - stage('Update pipeline jobs') { - when { - expression { - return "${PIPELINE_UPDATE_JOBS_DIR}" && env.PAGURE_POLLING_FOR_PR == 'false' && env.WAIVERDB_GIT_BRANCH == "${PAGURE_POLLED_BRANCH}" - } - } - steps { - checkout([$class: 'GitSCM', - branches: [[name: env.WAIVERDB_GIT_COMMIT]], - userRemoteConfigs: [ - [ - name: 'origin', - url: env.GIT_URL, - refspec: '+refs/heads/*:refs/remotes/origin/* +refs/pull/*/head:refs/remotes/origin/pull/*/head', - ], - ], - extensions: [[$class: 'CleanBeforeCheckout']], - ]) - script { - dir('openshift/pipelines') { - sh ''' - make install JOBS_DIR="${PIPELINE_UPDATE_JOBS_DIR}" - ''' - } - } - } - } - stage('Build') { - steps { - script { - openshift.withCluster() { - def bc = env.PAGURE_POLLING_FOR_PR == 'true' ? env.PREMERGE_JOB_NAME : env.POSTMERGE_JOB_NAME - def build = c3i.build(script: this, objs: "bc/${bc}", - '-e', "WAIVERDB_GIT_REF=${env.WAIVERDB_GIT_BRANCH}", - ) - 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." - } - } - } - } - } -} - diff --git a/openshift/pipelines/templates/waiverdb-polling-pagure.yaml b/openshift/pipelines/templates/waiverdb-polling-pagure.yaml deleted file mode 100644 index 194abf3..0000000 --- a/openshift/pipelines/templates/waiverdb-polling-pagure.yaml +++ /dev/null @@ -1,93 +0,0 @@ -# Template to produce a new OpenShift pipeline job for polling for Pagure branches or PRs -# ---- -apiVersion: v1 -kind: Template -metadata: - name: waiverdb-polling-to-pagure -labels: - template: waiverdb-polling-to-pagure -parameters: -- name: NAME - displayName: Short unique identifier for the templated instances - description: This field is used to deploy multiple pipelines to one OpenShift project from this template. - required: true - value: waiverdb-polling-to-pagure -- name: PAGURE_REPO_NAME - displayName: Pagure project name - description: // - required: true - value: waiverdb -- name: PAGURE_REPO_IS_FORK - displayName: Is the Pagure repo a fork? - required: true - value: "false" -- name: PAGURE_POLLING_FOR_PR - displayName: set to 'true' to poll for PRs, or 'false' for the master branch - required: true - value: "false" -- name: PAGURE_URL - displayName: Pagure URL - value: "https://pagure.io" -- name: PAGURE_POLLING_SCHEDULE - displayName: Schedule of polling - description: using cron-style syntax - required: true - value: "H/5 * * * *" -- name: PAGURE_POLLED_BRANCH - displayName: Name of polled branch - required: true - value: "master" -- name: PREMERGE_JOB_NAME - displayName: Downstream pre-merge job name - required: true - value: waiverdb-premerge -- name: POSTMERGE_JOB_NAME - displayName: Downstream post-merge job name - required: true - value: waiverdb-postmerge -- name: PIPELINE_UPDATE_JOBS_DIR - displayName: location of pipeline job definitions for auto update - value: jobs - required: false -- name: JENKINS_AGENT_IMAGE - displayName: Container image for Jenkins slave pods - required: true - value: docker-registry.upshift.redhat.com/factory2/waiverdb-jenkins-slave:latest -- name: OPENSHIFT_CLOUD_NAME - displayName: Name of OpenShift cloud in Jenkins master configuration - required: true - value: openshift -{% include "snippets/c3i-library-parameters.yaml" %} -objects: -- kind: ServiceAccount - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave" - labels: - app: "${NAME}" -- kind: RoleBinding - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave_edit" - labels: - app: "${NAME}" - subjects: - - kind: ServiceAccount - name: "${NAME}-jenkins-slave" - roleRef: - name: edit -- kind: "BuildConfig" - apiVersion: "v1" - metadata: - name: "${NAME}" - labels: - app: "${NAME}" - spec: - runPolicy: "Serial" - completionDeadlineSeconds: 1800 - strategy: - type: JenkinsPipeline - jenkinsPipelineStrategy: - jenkinsfile: | - {% filter indent(width=10) %}{% include "waiverdb-polling-pagure.Jenkinsfile" %}{% endfilter %} diff --git a/openshift/pipelines/templates/waiverdb-repotracker-trigger.Jenkinsfile b/openshift/pipelines/templates/waiverdb-repotracker-trigger.Jenkinsfile deleted file mode 100644 index 942a57f..0000000 --- a/openshift/pipelines/templates/waiverdb-repotracker-trigger.Jenkinsfile +++ /dev/null @@ -1,39 +0,0 @@ -{% include "snippets/c3i-library.groovy" %} -pipeline { - {% include "snippets/default-agent.groovy" %} - options { - timestamps() - timeout(time: 120, unit: 'MINUTES') - buildDiscarder(logRotator(numToKeepStr: '10')) - } - triggers { - ciBuildTrigger( - noSquash: false, - 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) { - error("This build is not started by a CI message. Only configurations were done.") - } - 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}" - echo "Triggering a job to test if ${env.IMAGE} meets all criteria of desired tag :${message.tag}" - env.IMAGE_IS_SCRATCH = false - env.PIPELINE_ID = "c3i-waiverdb-tag-${message.tag}-${message.digest[-9..-1]}" - } - } - } - {% include "snippets/waiverdb-full-integration-test.groovy" %} - } -} diff --git a/openshift/pipelines/templates/waiverdb-repotracker-trigger.yaml b/openshift/pipelines/templates/waiverdb-repotracker-trigger.yaml deleted file mode 100644 index 307c62c..0000000 --- a/openshift/pipelines/templates/waiverdb-repotracker-trigger.yaml +++ /dev/null @@ -1,123 +0,0 @@ -# Template to produce a new OpenShift pipeline job for triggering a build on repotracker messages -# ---- -apiVersion: v1 -kind: Template -metadata: - name: waiverdb-repotracker-trigger -labels: - template: waiverdb-repotracker-trigger -parameters: -- name: NAME - displayName: Short unique identifier for the templated instances - description: This field is used to deploy multiple pipelines to one OpenShift project from this template. - value: waiverdb-repotracker-trigger -- name: TRACKED_CONTAINER_REPO - displayName: Container repo to be tracked - value: "quay.io/factory2/waiverdb" -- name: TRACKED_TAG - displayName: Name of tag to be tracked - required: true -- name: JENKINS_AGENT_IMAGE - displayName: Container image for Jenkins slave pods - value: docker-registry.upshift.redhat.com/factory2/factory2-integration-test-jenkins-slave:latest -- name: OPENSHIFT_CLOUD_NAME - displayName: Name of OpenShift cloud in Jenkins master configuration - value: openshift -- name: PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE - displayName: The namespace where the Pipeline-as-a-Service project request BuildConfig has been defined - required: false - value: c3i -- name: MESSAGING_PROVIDER - displayName: Name of the JMS messaging provider - value: Red Hat UMB -- name: MESSAGING_TOPIC - displayName: Name of the topic that the trigger subscribes to - value: "Consumer.rh-jenkins-ci-plugin.c3i-waiverdb-repotracker-trigger.VirtualTopic.eng.repotracker.container.tag.>" -- name: ENVIRONMENT - displayName: environment name (dev/stage/prod) - required: true - value: dev -- name: BACKEND_INTEGRATION_TEST_REPO - displayName: backend integration test repo - value: https://gitlab.cee.redhat.com/devops/factory2-segment-tests.git -- name: BACKEND_INTEGRATION_TEST_REPO_BRANCH - displayName: backend integration test repo - required: true - value: master -- name: BACKEND_INTEGRATION_TEST_FILE - displayName: backend integration test file - required: true - value: greenwave-segment-test/greenwave-segment-test-c3i.sh -- name: NO_CLEANUP_AFTER_TEST - displayName: Keep environment after test - value: "false" -- name: MAIL_ADDRESS - displayName: If set, build failure messages to this mail address. -{% include "snippets/c3i-library-parameters.yaml" %} -objects: -- kind: ServiceAccount - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave" - labels: - app: "${NAME}" -- kind: RoleBinding - apiVersion: v1 - metadata: - name: "${NAME}-jenkins-slave_edit" - labels: - app: "${NAME}" - subjects: - - kind: ServiceAccount - name: "${NAME}-jenkins-slave" - roleRef: - name: edit -- kind: "BuildConfig" - apiVersion: "v1" - metadata: - name: "${NAME}" - labels: - app: "${NAME}" - spec: - runPolicy: "Serial" - completionDeadlineSeconds: 1800 - strategy: - type: JenkinsPipeline - jenkinsPipelineStrategy: - env: - - name: "OPENSHIFT_CLOUD_NAME" - value: "${OPENSHIFT_CLOUD_NAME}" - - name: "JENKINS_AGENT_IMAGE" - value: "${JENKINS_AGENT_IMAGE}" - - name: "JENKINS_AGENT_SERVICE_ACCOUNT" - value: "${NAME}-jenkins-slave" - - name: "TRACKED_CONTAINER_REPO" - value: "${TRACKED_CONTAINER_REPO}" - - name: "TRACKED_TAG" - value: "${TRACKED_TAG}" - - name: MESSAGING_PROVIDER - value: "${MESSAGING_PROVIDER}" - - name: MESSAGING_TOPIC - value: "${MESSAGING_TOPIC}" - - name: ENVIRONMENT - value: "${ENVIRONMENT}" - - name: PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE - value: "${PIPELINE_AS_A_SERVICE_BUILD_NAMESPACE}" - - name: "NO_CLEANUP_AFTER_TEST" - value: "${NO_CLEANUP_AFTER_TEST}" - - name: MAIL_ADDRESS - value: "${MAIL_ADDRESS}" - - name: BACKEND_INTEGRATION_TEST_REPO - value: "${BACKEND_INTEGRATION_TEST_REPO}" - - name: BACKEND_INTEGRATION_TEST_REPO_BRANCH - value: "${BACKEND_INTEGRATION_TEST_REPO_BRANCH}" - - name: BACKEND_INTEGRATION_TEST_FILE - value: "${BACKEND_INTEGRATION_TEST_FILE}" - # CI_MESSAGE and MESSAGE_HEADERS are used internally by JMS messaging plugin - - name: CI_MESSAGE - value: - - name: MESSAGE_HEADERS - value: - jenkinsfile: | - {% filter indent(width=10) %}{% include "waiverdb-repotracker-trigger.Jenkinsfile" %}{% endfilter %}