From 5f85a5a4fe417a13ddb64264f7595ebf0e2d6f7a Mon Sep 17 00:00:00 2001 From: "Dustin C. Hatch" Date: Fri, 10 Nov 2023 15:59:07 -0600 Subject: [PATCH] ci: Build CLI RPMs for aarch64 In order to automate certificate issuance and renewal for Raspberry Pi devices, we need aarch64 builds of the `sshca` tool. Using the `matrix` feature of Jenkins pipelines lets us reuse the same stage definition for building the client on both platforms. Unfortunately, the `matrix` block has to encompass the server stage as well, as `matrix` cannot be nested below `parallel`, and we don't want to build the server and clients sequentially. This makes the code a bit less clear, as the server and client stages are now conditional based on the matrix intersection, but it is cleaner than duplicating the entire client stage. --- ci/Jenkinsfile | 199 ++++++++++++++++++++++---------------- ci/clientPodTemplate.yaml | 3 + ci/publish-client.sh | 12 ++- 3 files changed, 132 insertions(+), 82 deletions(-) diff --git a/ci/Jenkinsfile b/ci/Jenkinsfile index 7948b3e..f0b450a 100644 --- a/ci/Jenkinsfile +++ b/ci/Jenkinsfile @@ -3,106 +3,143 @@ pipeline { stages { stage('SSHCA') { - parallel { - stage('Server') { - agent { - kubernetes { - yamlFile 'ci/serverPodTemplate.yaml' - yamlMergeStrategy merge() - defaultContainer 'buildah' - } + matrix { + axes { + axis { + name 'COMPONENT' + values 'client', 'server' } - stages { - stage('Build - Server') { - steps { - sh '. ci/build-server.sh' - } - } + axis { + name 'ARCH' + values 'amd64', 'arm64' + } + } - stage('Publish - Server') { - steps { - withEnv([ - "REGISTRY_AUTH_FILE=${env.WORKSPACE_TMP}/auth.json" - ]) { - withCredentials([usernamePassword( - credentialsId: 'jenkins-packages', - usernameVariable: 'BUILDAH_USERNAME', - passwordVariable: 'BUILDAH_PASSWORD', - )]) { - sh """ - buildah login \ - --username \${BUILDAH_USERNAME} \ - --password \${BUILDAH_PASSWORD} \ - git.pyrocufflink.net - """ - } - sh '. ci/publish-server.sh' - } - } + excludes { + exclude { + axis { + name 'COMPONENT' + values 'server' + } + axis { + name 'ARCH' + values 'arm64' } } } - stage('CLI') { - agent { - kubernetes { - yamlFile 'ci/clientPodTemplate.yaml' - yamlMergeStrategy merge() - defaultContainer 'fedora' - } - } - environment { - GNUPGHOME = "${env.WORKSPACE_TMP}/gnupg" - } - stages { - stage('Prepare - CLI') { - steps { - sh '. ci/prepare-client.sh' + stages { + stage('Server') { + when { + expression { + env.COMPONENT == 'server' } } + agent { + kubernetes { + yamlFile 'ci/serverPodTemplate.yaml' + yamlMergeStrategy merge() + defaultContainer 'buildah' + } + } + stages { + stage('Build - Server') { + steps { + sh '. ci/build-server.sh' + } + } - stage('Build - CLI') { - steps { - sh '. ci/build-client.sh' - script { - if (env.BRANCH_NAME == 'master') { - withCredentials([ - file( - credentialsId: 'rpm-gpg-key', - variable: 'RPM_GPG_PRIVATE_KEY', - ), - file( - credentialsId: 'rpm-gpg-key-passphrase', - variable: 'RPM_GPG_KEY_PASSPHRASE', - ), - ]) { - sh '. ci/sign-rpms.sh' + stage('Publish - Server') { + steps { + withEnv([ + "REGISTRY_AUTH_FILE=${env.WORKSPACE_TMP}/auth.json" + ]) { + withCredentials([usernamePassword( + credentialsId: 'jenkins-packages', + usernameVariable: 'BUILDAH_USERNAME', + passwordVariable: 'BUILDAH_PASSWORD', + )]) { + sh """ + buildah login \ + --username \${BUILDAH_USERNAME} \ + --password \${BUILDAH_PASSWORD} \ + git.pyrocufflink.net + """ + } + sh '. ci/publish-server.sh' + } + } + } + } + } + + stage('CLI') { + when { + expression { + env.COMPONENT = 'client' + } + } + agent { + kubernetes { + yamlFile 'ci/clientPodTemplate.yaml' + yamlMergeStrategy merge() + defaultContainer 'fedora' + nodeSelector "kubernetes.io/arch=${ARCH}" + } + } + environment { + GNUPGHOME = "${env.WORKSPACE_TMP}/gnupg" + } + stages { + stage('Prepare - CLI') { + steps { + sh '. ci/prepare-client.sh' + } + } + + stage('Build - CLI') { + steps { + sh '. ci/build-client.sh' + script { + if (env.BRANCH_NAME == 'master') { + withCredentials([ + file( + credentialsId: 'rpm-gpg-key', + variable: 'RPM_GPG_PRIVATE_KEY', + ), + file( + credentialsId: 'rpm-gpg-key-passphrase', + variable: 'RPM_GPG_KEY_PASSPHRASE', + ), + ]) { + sh '. ci/sign-rpms.sh' + } + } + } + } + post { + success { + dir('cli') { + archiveArtifacts '*.rpm' } } } } - post { - success { - dir('cli') { - archiveArtifacts '*.rpm' + + stage('Publish - CLI') { + when { + branch 'master' + } + steps { + sshagent(['jenkins-repohost']) { + sh '. ci/publish-client.sh' } } } } - - stage('Publish - CLI') { - when { - branch 'master' - } - steps { - sshagent(['jenkins-repohost']) { - sh '. ci/publish-client.sh' - } - } - } } - } + } } } diff --git a/ci/clientPodTemplate.yaml b/ci/clientPodTemplate.yaml index 1c4502e..1fe52c1 100644 --- a/ci/clientPodTemplate.yaml +++ b/ci/clientPodTemplate.yaml @@ -11,6 +11,9 @@ spec: name: ssh-known-hosts subPath: ssh_known_hosts hostUsers: false + tolerations: + - key: du5t1n.me/machine + value: raspberrypi volumes: - name: ssh-known-hosts configMap: diff --git a/ci/publish-client.sh b/ci/publish-client.sh index efbd0f5..1d22c8a 100644 --- a/ci/publish-client.sh +++ b/ci/publish-client.sh @@ -6,9 +6,19 @@ REPO_PATH=/srv/www/repohost/repos/dch/fedora/$(rpm --eval %fedora) ssh-add -l ssh-add -L +case "$(uname -m)" in +x86_64) + # only include the SRPM once + include='*.rpm' + ;; +*) + include="*.${ARCH}.rpm" + ;; +esac + rsync -rtiO \ --chmod=ugo=rwX \ - --include '*.rpm' \ + --include "${include}" \ --exclude '*' \ cli/ \ "${REPO_HOST}:${REPO_PATH}/"