feat(container): add image build validation steps
This commit is contained in:
@ -1,12 +1,20 @@
|
||||
String buildAndPublishImage(Map options = [:]) {
|
||||
def dockerfile = options.get("dockerfile", "./Dockerfile")
|
||||
def contextDir = options.get("contextDir", ".")
|
||||
def imageName = options.get("imageName", "")
|
||||
def gitRef = sh(returnStdout: true, script: 'git describe --always').trim()
|
||||
def imageTag = options.get("imageTag", gitRef)
|
||||
def gitCredentialsId = options.get("gitCredentialsId", "forge-jenkins")
|
||||
def dockerRepository = options.get("dockerRepository", "docker.io")
|
||||
def dockerRepositoryCredentialsId = options.get("dockerRepositoryCredentialsId", "cadoles-docker-hub")
|
||||
String dockerfile = options.get('dockerfile', './Dockerfile')
|
||||
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)
|
||||
String gitCredentialsId = options.get('gitCredentialsId', 'forge-jenkins')
|
||||
String dockerRepository = options.get('dockerRepository', 'docker.io')
|
||||
String dockerRepositoryCredentialsId = options.get('dockerRepositoryCredentialsId', 'cadoles-docker-hub')
|
||||
Boolean dryRun = options.get('dryRun', true)
|
||||
Boolean skipVerifications = options.get('skipVerification', false)
|
||||
|
||||
String projectRepository = env.JOB_NAME
|
||||
if (env.BRANCH_NAME ==~ /^PR-.*$/) {
|
||||
projectRepository = env.JOB_NAME - "/${env.JOB_BASE_NAME}"
|
||||
}
|
||||
projectRepository = options.get('projectRepository', projectRepository)
|
||||
|
||||
withCredentials([
|
||||
usernamePassword([
|
||||
@ -17,51 +25,183 @@ String buildAndPublishImage(Map options = [:]) {
|
||||
]) {
|
||||
String fullImageName = "${dockerRepository}/${env.HUB_USERNAME}/${imageName}"
|
||||
|
||||
stage('Validate Dockerfile with Hadolint') {
|
||||
utils.when(!skipVerifications) {
|
||||
runHadolintCheck(dockerfile, projectRepository)
|
||||
}
|
||||
}
|
||||
|
||||
stage("Build image '${imageName}:${imageTag}'") {
|
||||
git.withHTTPCredentials(gitCredentialsId) {
|
||||
sh """
|
||||
docker build \
|
||||
--build-arg="GIT_USERNAME=${env.GIT_USERNAME}" \
|
||||
--build-arg="GIT_PASSWORD=${env.GIT_PASSWORD}" \
|
||||
-t ${fullImageName}:${imageTag} \
|
||||
-t '${fullImageName}:${imageTag}' \
|
||||
-f '${dockerfile}' \
|
||||
'${contextDir}'
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
stage('Validate image with Trivy') {
|
||||
utils.when(!skipVerifications) {
|
||||
runTrivyCheck("${fullImageName}:${imageTag}", projectRepository)
|
||||
}
|
||||
}
|
||||
|
||||
stage("Publish image '${fullImageName}:${imageTag}'") {
|
||||
retry(2) {
|
||||
sh """
|
||||
echo ${env.HUB_PASSWORD} | docker login -u '${env.HUB_USERNAME}' --password-stdin
|
||||
docker login '${dockerRepository}'
|
||||
docker push '${fullImageName}:${imageTag}'
|
||||
"""
|
||||
utils.when(!dryRun) {
|
||||
retry(2) {
|
||||
sh """
|
||||
echo ${env.HUB_PASSWORD} | docker login -u '${env.HUB_USERNAME}' --password-stdin
|
||||
docker login '${dockerRepository}'
|
||||
docker push '${fullImageName}:${imageTag}'
|
||||
"""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def createPodmanPackage(Map options = [:]) {
|
||||
String gomplateBin = getOrInstallGomplate()
|
||||
void runHadolintCheck(String dockerfile, String projectRepository) {
|
||||
String reportFile = ".hadolint-report-${currentBuild.startTimeInMillis}.txt"
|
||||
|
||||
try {
|
||||
validateDockerfileWithHadolint(dockerfile, ['reportFile': reportFile])
|
||||
} catch (err) {
|
||||
unstable("Dockerfile '${dockerfile}' failed linting !")
|
||||
} finally {
|
||||
String lintReport = ''
|
||||
|
||||
if (fileExists(reportFile)) {
|
||||
lintReport = """${lintReport}
|
||||
|
|
||||
|```
|
||||
|${readFile(reportFile)}
|
||||
|```"""
|
||||
} else {
|
||||
lintReport = """${lintReport}
|
||||
|
|
||||
|_Vérification échouée mais aucun rapport trouvé !?_ :thinking:"""
|
||||
}
|
||||
|
||||
String defaultReport = '_Rien à signaler !_ :thumbsup:'
|
||||
String report = """## Validation du Dockerfile `${dockerfile}`
|
||||
|
|
||||
|${lintReport ?: defaultReport}
|
||||
""".stripMargin()
|
||||
|
||||
print report
|
||||
|
||||
if (env.CHANGE_ID) {
|
||||
gitea.commentPullRequest(projectRepository, env.CHANGE_ID, report)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String getOrInstallGomplate() {
|
||||
String installDir = options.get("installDir", "/usr/local/bin")
|
||||
String version = options.get("version", "3.10.0")
|
||||
String forceDownload = options.get("forceDownload", false)
|
||||
String downloadUrl = options.get("downloadUrl", "https://github.com/hairyhenderson/gomplate/releases/download/v${version}/gomplate_linux-amd64")
|
||||
String validateDockerfileWithHadolint(String dockerfile, Map options = [:]) {
|
||||
String hadolintBin = getOrInstallHadolint(options)
|
||||
String hadolintArgs = options.get('hadolintArgs', '--no-color')
|
||||
String reportFile = options.get('reportFile', ".hadolint-report-${currentBuild.startTimeInMillis}.txt")
|
||||
|
||||
String gomplateBin = sh(returnStdout: true, script: 'which gomplate').trim("")
|
||||
if (gomplateBin == "" || forceDownload) {
|
||||
sh("""
|
||||
mkdir -p '${installDir}'
|
||||
curl -o tools/gomplate/bin/gomplate -sSL '${forceDownload}'
|
||||
chmod +x '${installDir}/gomplate'
|
||||
""")
|
||||
sh("""#!/bin/bash
|
||||
set -eo pipefail
|
||||
'${hadolintBin}' '${dockerfile}' ${hadolintArgs} | tee '${reportFile}'
|
||||
""")
|
||||
|
||||
gomplateBin = "${installDir}/gomplate"
|
||||
return reportFile
|
||||
}
|
||||
|
||||
void runTrivyCheck(String imageName, String projectRepository, Map options = [:]) {
|
||||
String reportFile = ".trivy-report-${currentBuild.startTimeInMillis}.txt"
|
||||
|
||||
try {
|
||||
validateImageWithTrivy(imageName, ['reportFile': reportFile])
|
||||
} catch (err) {
|
||||
unstable("Image '${imageName}' failed validation !")
|
||||
} finally {
|
||||
String lintReport = ''
|
||||
|
||||
if (fileExists(reportFile)) {
|
||||
lintReport = """${lintReport}
|
||||
|
|
||||
|```
|
||||
|${readFile(reportFile)}
|
||||
|```"""
|
||||
} else {
|
||||
lintReport = """${lintReport}
|
||||
|
|
||||
|_Vérification échouée mais aucun rapport trouvé !?_ :thinking:"""
|
||||
}
|
||||
|
||||
String defaultReport = '_Rien à signaler !_ :thumbsup:'
|
||||
String report = """## Validation de l'image `${imageName}`
|
||||
|
|
||||
|${lintReport ?: defaultReport}
|
||||
""".stripMargin()
|
||||
|
||||
print report
|
||||
|
||||
if (env.CHANGE_ID) {
|
||||
gitea.commentPullRequest(projectRepository, env.CHANGE_ID, report)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String validateImageWithTrivy(String imageName, Map options = [:]) {
|
||||
String trivyBin = getOrInstallTrivy(options)
|
||||
String trivyArgs = options.get('trivyArgs', '--exit-code 1')
|
||||
String cacheDirectory = options.get('cacheDirectory', '.trivy/.cache')
|
||||
String cacheDefaultBranch = options.get('cacheDefaultBranch', 'develop')
|
||||
Integer cacheMaxSize = options.get('cacheMaxSize', 250)
|
||||
String reportFile = options.get('reportFile', ".trivy-report-${currentBuild.startTimeInMillis}.txt")
|
||||
|
||||
cache(maxCacheSize: cacheMaxSize, defaultBranch: cacheDefaultBranch, caches: [
|
||||
[$class: 'ArbitraryFileCache', path: cacheDirectory, compressionMethod: 'TARGZ']
|
||||
]) {
|
||||
sh("'${trivyBin}' --cache-dir '${cacheDirectory}' image -o '${reportFile}' ${trivyArgs} '${imageName}'")
|
||||
}
|
||||
|
||||
return gomplateBin
|
||||
return reportFile
|
||||
}
|
||||
|
||||
String getOrInstallHadolint(Map options = [:]) {
|
||||
String installDir = options.get('installDir', '/usr/local/bin')
|
||||
String version = options.get('version', '2.10.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")
|
||||
|
||||
String hadolintBin = sh(returnStdout: true, script: 'which hadolint || exit 0').trim()
|
||||
if (hadolintBin == '' || forceDownload) {
|
||||
sh("""
|
||||
mkdir -p '${installDir}'
|
||||
curl -o '${installDir}/hadolint' -sSL '${downloadUrl}'
|
||||
chmod +x '${installDir}/hadolint'
|
||||
""")
|
||||
|
||||
hadolintBin = "${installDir}/hadolint"
|
||||
}
|
||||
|
||||
return hadolintBin
|
||||
}
|
||||
|
||||
String getOrInstallTrivy(Map options = [:]) {
|
||||
String installDir = options.get('installDir', '/usr/local/bin')
|
||||
String version = options.get('version', '0.27.1')
|
||||
String forceDownload = options.get('forceDownload', false)
|
||||
String installScriptDownloadUrl = options.get('downloadUrl', 'https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh')
|
||||
|
||||
String trivyBin = sh(returnStdout: true, script: 'which trivy || exit 0').trim()
|
||||
if (trivyBin == '' || forceDownload) {
|
||||
sh("""
|
||||
mkdir -p '${installDir}'
|
||||
curl -sfL '${installScriptDownloadUrl}' | sh -s -- -b '${installDir}' v${version}
|
||||
chmod +x '${installDir}/trivy'
|
||||
""")
|
||||
|
||||
trivyBin = "${installDir}/trivy"
|
||||
}
|
||||
|
||||
return trivyBin
|
||||
}
|
||||
|
Reference in New Issue
Block a user