diff --git a/test/groovy/TerraformStepTests.groovy b/test/groovy/TerraformStepTests.groovy index fad97218..23645c51 100644 --- a/test/groovy/TerraformStepTests.groovy +++ b/test/groovy/TerraformStepTests.groovy @@ -86,9 +86,9 @@ class TerraformStepTests extends BaseTest { // And a daily cron trigger for the job assertTrue(assertMethodCallContainsPattern('cron', '@daily')) - // And 2 nodes with default label are spawned - assertTrue(assertMethodCallContainsPattern('node', 'jnlp-linux-arm64')) - assertTrue(assertMethodCallOccurrences('node', 2)) + // And the correct pod templates defined + assertTrue(assertMethodCallContainsPattern('containerTemplate', 'jenkinsciinfra/hashicorp-tools:')) // Not tag as it's managed by updatecli + assertTrue(assertMethodCallOccurrences('containerTemplate', 2)) // Only 1 container per pod, but 2 pod spawn (staging and production) // xterm color enabled (easier to read Terraform plans) assertTrue(assertMethodCallContainsPattern('ansiColor', 'xterm')) @@ -221,14 +221,14 @@ class TerraformStepTests extends BaseTest { @Test void itRunSuccessfullyWithCustomParameters() throws Exception { def script = loadScript(scriptName) - final String customLabel = 'jnlp-windows-amd64' + final String customImage = 'hashicorp/terraform-full:0.13.0' // When calling the shared library global function with custom parameters script.call( cronTriggerExpression: '@weekly', stagingCredentials: stagingCustomCreds, productionCredentials: productionCustomCreds, - agentLabel: customLabel, + agentContainerImage: customImage, ) printCallStack() @@ -244,8 +244,9 @@ class TerraformStepTests extends BaseTest { // And the custom cron trigger assertTrue(assertMethodCallContainsPattern('cron', '@weekly')) - // And 2 nodes with custom label are spawned - assertTrue(assertMethodCallContainsPattern('node', customLabel)) - assertTrue(assertMethodCallOccurrences('node', 2)) + // And the custom agent container template defined + assertFalse(assertMethodCallContainsPattern('containerTemplate', 'jenkinsciinfra/terraform:')) + assertTrue(assertMethodCallContainsPattern('containerTemplate', customImage)) + assertTrue(assertMethodCallOccurrences('containerTemplate', 2)) } } diff --git a/updatecli/updatecli.d/terraform-hashicorp.yml b/updatecli/updatecli.d/terraform-hashicorp.yml new file mode 100644 index 00000000..34ec3a6e --- /dev/null +++ b/updatecli/updatecli.d/terraform-hashicorp.yml @@ -0,0 +1,57 @@ +name: Bump `hashicorp-tools` docker image + +scms: + default: + kind: github + spec: + user: "{{ .github.user }}" + email: "{{ .github.email }}" + owner: "{{ .github.owner }}" + repository: "{{ .github.repository }}" + token: "{{ requiredEnv .github.token }}" + username: "{{ .github.username }}" + branch: "{{ .github.branch }}" + +sources: + dockerHashicorpToolsImageVersion: + kind: githubrelease + spec: + owner: "jenkins-infra" + repository: "docker-hashicorp-tools" + token: "{{ requiredEnv .github.token }}" + username: "{{ .github.username }}" + versionfilter: + kind: semver + transformers: + - trimprefix: v + +conditions: + checkIfDockerImageIsPublished: + name: "Check if the Docker Image is published" + kind: dockerimage + spec: + image: "jenkinsciinfra/hashicorp-tools" + architecture: amd64 + +targets: + updateTerraformFile: + name: Update Terraform file in groovy code + kind: file + spec: + file: ./vars/terraform.groovy + # Please note that the patterns are specified as "block scalars" (>) - https://yaml-multiline.info/ - with the last endline trimmed (-) to avoid tedious escaping of simple quotes + matchpattern: >- + 'jenkinsciinfra/hashicorp-tools:(.*)' + replacepattern: >- + 'jenkinsciinfra/hashicorp-tools:{{ source `dockerHashicorpToolsImageVersion` }}' + scmid: default + +actions: + default: + kind: github/pullrequest + scmid: default + title: Bump `hashicorp-tools` docker image to {{ source "dockerHashicorpToolsImageVersion" }} + spec: + labels: + - dependencies + - jenkinsciinfra/hashicorp-tools diff --git a/vars/terraform.groovy b/vars/terraform.groovy index d518bdc5..56f03468 100644 --- a/vars/terraform.groovy +++ b/vars/terraform.groovy @@ -9,7 +9,7 @@ def call(userConfig = [:]) { stagingCredentials: [], // No custom secrets for staging by default productionCredentials: [], // No custom secrets for production by default productionBranch: 'main', // Defaults to the principal branch - agentLabel: 'jnlp-linux-arm64', // replace agentContainerImage + agentContainerImage: 'jenkinsciinfra/hashicorp-tools:1.0.62', // Version managed by updatecli runTests: false, // Executes the tests provided by the "calling" project, which should provide a tests/Makefile runCommonTests: true, // Executes the default test suite from the shared tools repository (terratest) ] @@ -50,7 +50,7 @@ def call(userConfig = [:]) { if (!isBuildCauseUser) { parallelStages['staging'] = { stage('Staging') { - agentTemplate(finalConfig.agentLabel, { + agentTemplate(finalConfig.agentContainerImage, { withCredentials(finalConfig.stagingCredentials) { stage('🔎 Validate Terraform for Staging Environment') { getInfraSharedTools(sharedToolsSubDir) @@ -75,7 +75,7 @@ def call(userConfig = [:]) { parallelStages['production'] = { stage('Production') { - agentTemplate(finalConfig.agentLabel, { + agentTemplate(finalConfig.agentContainerImage, { withCredentials(defaultConfig.productionCredentials) { final String planFileName = 'terraform-plan-for-humans.txt' def scmOutput @@ -139,14 +139,44 @@ def call(userConfig = [:]) { } } -def agentTemplate(agentLabel, body) { - node (agentLabel) { - timeout(time: 1, unit: 'HOURS') { - ansiColor('xterm') { - body.call() +def agentTemplate(containerImage, body) { + podTemplate( + // Custom YAML definition to enforce no service account token (if Terraform uses kubernetes, it would grant it a wrong access) + yaml: ''' + apiVersion: v1 + kind: Pod + spec: + automountServiceAccountToken: false + nodeSelector: + kubernetes.azure.com/agentpool: infracipool + kubernetes.io/os: linux + tolerations: + - key: "jenkins" + operator: "Equal" + value: "infra.ci.jenkins.io" + effect: "NoSchedule" + - key: "kubernetes.azure.com/scalesetpriority" + operator: "Equal" + value: "spot" + effect: "NoSchedule" + resources: + limits: + cpu: 2 + memory: 2Gi + requests: + cpu: 2 + memory: 2Gi + ''', + // The Docker image here is aimed at "1 container per pod" and is embedding Jenkins agent tooling + containers: [containerTemplate(name: 'jnlp', image: containerImage)]) { + node(POD_LABEL) { + timeout(time: 1, unit: 'HOURS') { + ansiColor('xterm') { + body.call() + } + } + } } - } - } }