From 2849a3d8d0a592f01080ca2452e05f6cd0fad8a2 Mon Sep 17 00:00:00 2001 From: William Petit Date: Wed, 15 Nov 2023 11:57:33 +0100 Subject: [PATCH] feat(container): publish image with multiple tags --- .../com/cadoles/trivy/templates/markdown.tpl | 56 +++++++++++++++++ vars/container.groovy | 63 +++++++++++++------ 2 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 resources/com/cadoles/trivy/templates/markdown.tpl diff --git a/resources/com/cadoles/trivy/templates/markdown.tpl b/resources/com/cadoles/trivy/templates/markdown.tpl new file mode 100644 index 0000000..b20a1f5 --- /dev/null +++ b/resources/com/cadoles/trivy/templates/markdown.tpl @@ -0,0 +1,56 @@ +{{- if . }} +{{- range . }} +

Target {{ escapeXML .Target }}

+{{- if (eq (len .Vulnerabilities) 0) }} +

No Vulnerabilities found

+{{- else }} +

Vulnerabilities ({{ len .Vulnerabilities }})

+ + + + + + + + + {{- range .Vulnerabilities }} + + + + + + + + {{- end }} +
PackageIDSeverityInstalled VersionFixed Version
{{ escapeXML .PkgName }}{{ escapeXML .VulnerabilityID }}{{ escapeXML .Severity }}{{ escapeXML .InstalledVersion }}{{ escapeXML .FixedVersion }}
+{{- end }} +{{- if (eq (len .Misconfigurations ) 0) }} +

No Misconfigurations found

+{{- else }} +

Misconfigurations

+ + + + + + + + + {{- range .Misconfigurations }} + + + + + + + + {{- end }} +
TypeIDCheckSeverityMessage
{{ escapeXML .Type }}{{ escapeXML .ID }}{{ escapeXML .Title }}{{ escapeXML .Severity }} + {{ escapeXML .Message }} +
{{ escapeXML .PrimaryURL }}
+
+{{- end }} +{{- end }} +{{- else }} +

Trivy Returned Empty Report

+{{- end }} \ No newline at end of file diff --git a/vars/container.groovy b/vars/container.groovy index da1ecb6..43d9500 100644 --- a/vars/container.groovy +++ b/vars/container.groovy @@ -6,7 +6,7 @@ * - dockerfile - String - Chemin vers le fichier Dockerfile à utiliser pour construire l'image, par défaut "./Dockerfile" * - contextDir - String - Répertoire servant de "contexte" pour la construction de l'image, par défault "./" * - imageName - String - Nom de l'image à construire, par défaut "" -* - imageTag - String - Tag apposé sur l'image après construction, par défaut résultat de la commande `git describe --always` +* - imageTags - String - Tag(s) apposé(s) sur l'image après construction, par défaut tags générés par la méthode utils.getProjectVersionTags() * - gitCredentialsId - String - Identifiant des "credentials" Jenkins utilisés pour cloner le dépôt Git, par défaut "forge-jenkins" * - dockerRepository - String - Nom d'hôte du registre Docker sur lequel publier l'image, par défaut "reg.cadoles.com" * - dockerRepositoryCredentialsId - String - Identifiant des "credentials" Jenkins utilisés pour déployer l'image sur le registre Docker, par défault "reg.cadoles.com-jenkins" @@ -18,7 +18,13 @@ String buildAndPublishImage(Map options = [:]) { String contextDir = options.get('contextDir', '.') String imageName = options.get('imageName', '') String gitRef = sh(returnStdout: true, script: 'git describe --always').trim() - String imageTag = options.get('imageTag', gitRef) + + List imageTags = options.get('imageTags', utils.getProjectVersionTags()) + // Handle legacy imageTag parameter + if (options.containsKey('imageTag')) { + imageTags = [ options.get("imageTag", gitRef) ] + } + String gitCredentialsId = options.get('gitCredentialsId', 'forge-jenkins') String dockerRepository = options.get('dockerRepository', 'reg.cadoles.com') String dockerRepositoryCredentialsId = options.get('dockerRepositoryCredentialsId', 'reg.cadoles.com-jenkins') @@ -44,13 +50,15 @@ String buildAndPublishImage(Map options = [:]) { } } - stage("Build image '${imageName}:${imageTag}'") { + String primaryImageTag = imageTags[0] + + stage("Build image '${imageName}:${primaryImageTag}'") { git.withHTTPCredentials(gitCredentialsId) { sh """ docker build \ --build-arg="GIT_USERNAME=${env.GIT_USERNAME}" \ --build-arg="GIT_PASSWORD=${env.GIT_PASSWORD}" \ - -t '${imageName}:${imageTag}' \ + -t '${imageName}:${primaryImageTag}' \ -f '${dockerfile}' \ '${contextDir}' """ @@ -59,20 +67,33 @@ String buildAndPublishImage(Map options = [:]) { stage('Validate image with Trivy') { utils.when(!skipVerifications) { - runTrivyCheck("${imageName}:${imageTag}", projectRepository) + runTrivyCheck("${imageName}:${primaryImageTag}", projectRepository) } } - stage("Publish image '${imageName}:${imageTag}'") { + stage("Login with image repository") { utils.when(!dryRun) { - retry(2) { + sh """ + echo ${env.HUB_PASSWORD} | docker login -u '${env.HUB_USERNAME}' --password-stdin '${dockerRepository}' + """ + } + } + + imageTags.each { imageTag -> + stage("Publish image '${imageName}:${imageTag}'") { + utils.when(!dryRun) { sh """ - echo ${env.HUB_PASSWORD} | docker login -u '${env.HUB_USERNAME}' --password-stdin '${dockerRepository}' - docker push '${imageName}:${imageTag}' + docker tag "${imageName}:${primaryImageTag}" "${imageName}:${imageTag}" """ + retry(2) { + sh """ + docker push '${imageName}:${imageTag}' + """ + } } } - } + } + } } @@ -87,10 +108,12 @@ void runHadolintCheck(String dockerfile, String projectRepository) { String lintReport = '' if (fileExists(reportFile)) { + String report = readFile(reportFile) + lintReport = """${lintReport} | |``` - |${readFile(reportFile)} + |${report.trim() ? report : "Rien à signaler."} |```""" } else { lintReport = """${lintReport} @@ -99,7 +122,7 @@ void runHadolintCheck(String dockerfile, String projectRepository) { } String defaultReport = '_Rien à signaler !_ :thumbsup:' - String report = """## Validation du Dockerfile `${dockerfile}` + String report = """## Rapport d'analyse du fichier `${dockerfile}` avec [Hadolint](https://github.com/hadolint/hadolint) | |${lintReport ?: defaultReport} """.stripMargin() @@ -138,9 +161,8 @@ void runTrivyCheck(String imageName, String projectRepository, Map options = [:] if (fileExists(reportFile)) { lintReport = """${lintReport} | - |``` |${readFile(reportFile)} - |```""" + |""" } else { lintReport = """${lintReport} | @@ -148,7 +170,7 @@ void runTrivyCheck(String imageName, String projectRepository, Map options = [:] } String defaultReport = '_Rien à signaler !_ :thumbsup:' - String report = """## Validation de l'image `${imageName}` + String report = """## Rapport d'analyse de l'image avec [Trivy](https://github.com/aquasecurity/trivy) | |${lintReport ?: defaultReport} """.stripMargin() @@ -168,11 +190,14 @@ String validateImageWithTrivy(String imageName, Map options = [:]) { String cacheDefaultBranch = options.get('cacheDefaultBranch', 'develop') Integer cacheMaxSize = options.get('cacheMaxSize', 250) String reportFile = options.get('reportFile', ".trivy-report-${currentBuild.startTimeInMillis}.txt") - + + String markdownTemplate = libraryResource 'com/cadoles/trivy/templates/markdown.tpl' + writeFile file:'.trivy-markdown.tpl', text: markdownTemplate + cache(maxCacheSize: cacheMaxSize, defaultBranch: cacheDefaultBranch, caches: [ [$class: 'ArbitraryFileCache', path: cacheDirectory, compressionMethod: 'TARGZ'] ]) { - sh("'${trivyBin}' --cache-dir '${cacheDirectory}' image -o '${reportFile}' ${trivyArgs} '${imageName}'") + sh("'${trivyBin}' --cache-dir '${cacheDirectory}' image --format template --template '@.trivy-markdown.tpl' -o '${reportFile}' ${trivyArgs} '${imageName}'") } return reportFile @@ -180,7 +205,7 @@ String validateImageWithTrivy(String imageName, Map options = [:]) { String getOrInstallHadolint(Map options = [:]) { String installDir = options.get('installDir', '/usr/local/bin') - String version = options.get('version', '2.10.0') + String version = options.get('version', '2.12.0') String forceDownload = options.get('forceDownload', false) String downloadUrl = options.get('downloadUrl', "https://github.com/hadolint/hadolint/releases/download/v${version}/hadolint-Linux-x86_64") @@ -200,7 +225,7 @@ String getOrInstallHadolint(Map options = [:]) { String getOrInstallTrivy(Map options = [:]) { String installDir = options.get('installDir', '/usr/local/bin') - String version = options.get('version', '0.27.1') + String version = options.get('version', '0.47.0') String forceDownload = options.get('forceDownload', false) String installScriptDownloadUrl = options.get('downloadUrl', 'https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh')