From eb4139e0be1d894fcdaa5d5bca98f7223cc1eab0 Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Mon, 16 Mar 2020 21:04:25 -0500 Subject: [PATCH] ci lib: Add applyConfigPolicy pipeline function The Jenkins pipeline definition files are highly redundant. Each one implements almost the same stages, with only a few variations. Whenever a new pipeline is added, it's copied from the most recent file and modified. If any improvements are made to it, they do not usually get implemented in any of the existing pipelines. To address this, the `applyConfigPolicy` pipeline library function is now available. This function generates the full pipeline for a particular application, including stages for setup, each individual playbook, and cleanup. Using this function, pipeline files can be as simple as: @Library('cfgpol')_ applyConfigPolicy( 'gitea', [ 'Gitea': [ 'gitea.yml', ], ] ) This will create a pipeline that mounts the root filesystem read-write on all hosts in the "gitea" group (any Ansible host pattern is allowed), applies the `gitea.yml` playbook (in a stage named "Gitea"), and then remounts the filesystems read-only. Since this "library" is so simple, containing only a single function in a single file, and since it will not be used by any pipelines outside this repository, it makes sense to keep it in this repository, instead of a separate repository as is customary for Jenkins pipeline shared libraries. --- vars/applyConfigPolicy.groovy | 104 ++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 vars/applyConfigPolicy.groovy diff --git a/vars/applyConfigPolicy.groovy b/vars/applyConfigPolicy.groovy new file mode 100644 index 0000000..17c7278 --- /dev/null +++ b/vars/applyConfigPolicy.groovy @@ -0,0 +1,104 @@ +import groovy.transform.Field + +@Field +def DOCKER_ARGS = '''\ +-v /etc/ssh/ssh_known_hosts:/etc/ssh/ssh_known_hosts:ro +''' + +def call(rw_limit, stages) { + properties([ + pipelineTriggers([cron('H H * * *')]) + ]) + + timeout(time: 1, unit: 'HOURS') { + lock('cfgpol') { + node { + checkout scm + docker.build("configpolicy", 'ci').inside(DOCKER_ARGS) { + withEnv(["KRB5CCNAME=${WORKSPACE}/.krb5cc"]) { + try { + stageKinit() + stageRemountRW(rw_limit) + generateStages(stages) + stageRemountRO(rw_limit) + } catch (err) { + postFailure(err) + } finally { + postCleanup() + } + } + } + } + } + } +} + +def stageKinit() { + stage('kinit') { + withCredentials([file( + credentialsId: 'keytab-jenkins@pyrocufflink.blue', + variable: 'KEYTAB' + )]) { + sh 'kinit -kt "${KEYTAB}" jenkins@PYROCUFFLINK.BLUE' + } + withCredentials([file( + credentialsId: 'vault-jenkins@pyrocufflink.blue', + variable: 'SUDO_PASS_FILE' + )]) { + sh 'cp "${SUDO_PASS_FILE}" group_vars/pyrocufflink/sudo-pass' + } + } +} + + +def stageRemountRW(limit) { + stage('Remount R/W') { + ansiblePlaybook \ + playbook: 'remount.yml', + limit: limit, + become: true, + vaultCredentialsId: 'ansible-vault', + extraVars: [ + remount_state: 'rw', + ] + } +} + +def generateStages(stages) { + stages.each { name, playbooks -> + stage(name) { + playbooks.each { playbook -> + ansiblePlaybook \ + playbook: playbook, + become: true, + vaultCredentialsId: 'ansible-vault', + extras: '--diff' + } + } + } +} + +def stageRemountRO(limit) { + stage('Remount R/O') { + ansiblePlaybook \ + playbook: 'remount.yml', + limit: limit, + become: true, + vaultCredentialsId: 'ansible-vault', + extras: '--diff' + } +} + +def postCleanup() { + sh 'kdestroy' + sh 'find . -name sudo-pass -delete' +} + +def postFailure(err) { + currentBuild.result = 'FAILURE' + emailext \ + to: 'gyrfalcon@ebonfire.com', + subject: '$DEFAULT_SUBJECT', + body: '$DEFAULT_CONTENT' + error "${err}" +}