From 1be646943eea5f23ef68055069b6518ab45d169c Mon Sep 17 00:00:00 2001 From: Rustam Tagaev Date: Thu, 27 Feb 2025 12:33:53 +0300 Subject: [PATCH] [DO-1617] add_terraform_checker (!85) Co-authored-by: Rustam Tagaev Reviewed-on: https://git.avroid.tech/DevOps/jenkins-pipelines/pulls/85 Reviewed-by: Vasiliy Chipizhin Reviewed-by: Aleksandr Vodyanov Reviewed-by: Denis Patrakeev --- .../DevOps/terraform_checker.groovy | 28 +++ .../DevOps/terraform_checker.groovy | 189 ++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 jobs-dsl/jobs/Automation/DevOps/terraform_checker.groovy create mode 100644 pipelines/Automation/DevOps/terraform_checker.groovy diff --git a/jobs-dsl/jobs/Automation/DevOps/terraform_checker.groovy b/jobs-dsl/jobs/Automation/DevOps/terraform_checker.groovy new file mode 100644 index 0000000..4e47215 --- /dev/null +++ b/jobs-dsl/jobs/Automation/DevOps/terraform_checker.groovy @@ -0,0 +1,28 @@ +pipelineJob('Automation/DevOps/terraform-checker') { + description("Terraform checker") + definition { + cpsScm { + scm { + git { + remote { + url("${JENKINS_GIT_REPOSITORY_URL}/DevOps/jenkins-pipelines.git") + credentials("${JENKINS_GIT_CREDENTIALS_HTTP}") + } + branch('master') + } + } + scriptPath('pipelines/Automation/DevOps/terraform_checker.groovy') + } + } + + properties { + disableConcurrentBuilds() + pipelineTriggers { + triggers { + cron { + spec('H 2 * * *') + } + } + } + } +} diff --git a/pipelines/Automation/DevOps/terraform_checker.groovy b/pipelines/Automation/DevOps/terraform_checker.groovy new file mode 100644 index 0000000..93b63ef --- /dev/null +++ b/pipelines/Automation/DevOps/terraform_checker.groovy @@ -0,0 +1,189 @@ +@Library('shared-lib') _ + +import tech.avroid.scm.Git +import tech.avroid.terraform.Terraform +import tech.avroid.kube.PodTemplates +import groovy.text.SimpleTemplateEngine +import tech.avroid.jenkins.Notifications + +Map statuses = [ + 0: 'Ok', + 1: 'Terraform plan error', + 2: 'Infrastructure has been changed', + 3: 'Terraform init problem', + 4: 'Terraform init script problem', + 5: 'Unknow error!' +] + +String repository = 'devops/terraform' +String branch = 'master' +String terraformInitScript = 'terraform_init.sh' +String maintainer = 'devops@avroid.team' + +List excludeDirs = [ + '__tf_template__.*', + '__example__.*', + '^dc-adlinux/', + '^dc-cluster/', + '^cloud-ext/', + '^kvm-server/', + '^pve-cluster/', + '.*__tf_template__/$' +] + +Map tfStatuses = [:] +Map stagesMap = [:] + +String mirrorsProvider = ''' +provider_installation { + network_mirror { + url = "https://terraform-mirror.yandexcloud.net/" + include = ["registry.terraform.io/*/*"] +} + direct { + exclude = ["registry.terraform.io/*/*"] + } +} +''' + +Boolean skipStep(Integer statusCode){ + Boolean skip = statusCode != 0 ? true : false + return skip +} + +List splitQueue(Object filesList, Integer step){ + List result = [] + Integer start = 0 + + for (i=0; i< filesList.size() / step ; i++){ + if (step + start > filesList.size()){ + result.add(filesList[start..filesList.size() -1]) + return result + } + + result.add(filesList[start..step+start -1]) + start = start + step + } + + return result +} + +String generateReport(Map states){ + SimpleTemplateEngine engine = new SimpleTemplateEngine() + + String report = ''' + + + + + + <% states.each { key, value -> %>\ + ${value} + + <% } %>\ +
Terraform report
PathStatus
${key}
+ ''' + + def result = engine.createTemplate(report).make([states: states]).toString() + return result +} + +timeout(time: 8, unit: 'HOURS') { + properties([ + disableConcurrentBuilds(), + buildDiscarder(logRotator(numToKeepStr: '10')), + pipelineTriggers([cron('H 2 * * *')]) + ]) + + PodTemplates slaveTemplates = new PodTemplates(this, env.JENKINS_DOCKER_REGISTRY, + [env.JENKINS_K8S_HARBOR_SECRET]) + Terraform terraform = new Terraform(script: this) + + try { + ansiColor('xterm'){ + slaveTemplates.jnlp { + slaveTemplates.terraform { + slaveTemplates.vault { + node(POD_LABEL){ + stage('Get repository') { + Git git = new Git(this, env.JENKINS_GIT_CREDENTIALS_SSH) + git.clone([urlRepo: "${env.JENKINS_GIT_REPOSITORY_SSH_URL}/${repository}.git", + branch: branch]) + } + + List tfFiles = splitQueue(findFiles(glob: "**/${terraformInitScript}"),50) + tfFiles.each { filesList -> + filesList.each { file -> + + String terraformWorkDir = file.path.replace(terraformInitScript, '') + Integer returnСode = 0 + + if (excludeDirs.any { terraformWorkDir ==~ it }) {return} + + stagesMap[terraformWorkDir] = { + dir(terraformWorkDir){ + withCredentials([[$class: 'VaultTokenCredentialBinding', + credentialsId: 'vault-role', + vaultAddr: env.JENKINS_VAULT_URL]]) { + container('vault') { + returnСode = sh( + script: "set +x && sh ./${terraformInitScript}", + returnStatus: true + ) + + tfStatuses[terraformWorkDir] = returnСode != 0 ? statuses[4] : statuses[0] + + if (skipStep(returnСode)) { return } + } + + withEnv(['TF_CLI_CONFIG_FILE=.terraformrc']){ + container(name: 'terraform') { + writeFile(file: '.terraformrc', text: mirrorsProvider) + + returnСode = terraform.init() + + tfStatuses[terraformWorkDir] = returnСode != 0 ? statuses[3] : statuses[0] + if (skipStep(returnСode)) { return } + + tfStatuses[terraformWorkDir] = statuses[terraform.checkStatusPlan()] == null ? statuses[5] : statuses[terraform.checkStatusPlan()] + } + } + } + } + } + } + parallel(stagesMap) + stagesMap.clear() + } + + stage('Send report'){ + Notifications.email( + script: this, + subject: 'Terraform report', + to: maintainer, + body: generateReport(tfStatuses) + ) + } + } + } + } + } + } + } catch(err) { + errorMessage = err.getMessage() + + println 'ERROR: ' + errorMessage + + currentBuild.result = 'FAILURE' + + String emailSubject = "${currentBuild.currentResult}. Pipeline task: ${currentBuild.fullDisplayName}" + + Notifications.email( + script: this, + subject: emailSubject, + errorString: errorMessage, + recipientProviders: [], + to: maintainer + ) + } +}