2025-02-07 07:18:40 +00:00
package com . freeleaps . devops
import com.freeleaps.devops.enums.ImageBuilderTypes
class ImageBuilder {
def steps
def workspace
def contextRoot
def dockerfile
def builderType
2025-02-09 18:56:27 +00:00
// customized parameters
def name
def registry
def repository
def architectures
def version
def registryCredentialsId
def buildxBuilderName
2025-02-09 19:03:18 +00:00
ImageBuilder ( steps , workspace , contextRoot , dockerfile , builderType ) {
2025-02-07 07:18:40 +00:00
this . steps = steps
this . workspace = workspace
this . contextRoot = contextRoot
this . dockerfile = dockerfile
this . builderType = builderType
}
2025-02-09 18:56:27 +00:00
def setManifestsOfImage ( registry , repository , name , version ) {
if ( registry = = null | | registry . isEmpty ( ) ) {
steps . error ( "registry is empty" )
}
this . registry = registry
if ( repository = = null | | repository . isEmpty ( ) ) {
steps . error ( "repository is empty" )
}
this . repository = repository
if ( name = = null | | name . isEmpty ( ) ) {
steps . error ( "name is empty" )
}
this . name = name
if ( version = = null | | version . isEmpty ( ) ) {
steps . error ( "version is empty" )
}
this . version = version
}
def setArchitectures ( architectures ) {
if ( architectures = = null | | architectures . isEmpty ( ) ) {
steps . error ( "architectures is empty" )
}
this . architectures = architectures
if ( builderType = = ImageBuilderTypes . DOCKER_IN_DOCKER & & architectures . size ( ) > 1 ) {
steps . log . warn ( "ImageBuilder" , "If you want to build multi-arch images and using Docker in Docker (DIND) as builder, system will using buildx to replace build command." )
steps . log . info ( "ImageBuilder" , "Creating buildx builder with name: multiarch-builder-${name}" )
2025-02-09 21:04:59 +00:00
steps . sh "docker buildx create --use --name multiarch-builder-${name} --platform ${architectures.join(" , ")} --driver-opt network=host"
2025-02-09 18:56:27 +00:00
steps . log . info ( "ImageBuilder" , "Inspecting buildx builder with name: multiarch-builder-${name}" )
steps . sh "docker buildx inspect --bootstrap"
this . buildxBuilderName = "multiarch-builder-${name}"
}
}
def useCredentials ( registryCredentialsId ) {
if ( registryCredentialsId = = null | | registryCredentialsId . isEmpty ( ) ) {
steps . error ( "registryCredentialsId is empty" )
}
this . registryCredentialsId = registryCredentialsId
}
def build ( ) {
2025-02-09 20:00:49 +00:00
try {
steps . log . info ( "ImageBuilder" , "Building image with ${builderType.builder}" )
steps . log . info ( "ImageBuilder" , "Workspace sets to: ${workspace}" )
steps . log . info ( "ImageBuilder" , "Using dockerfile at: ${dockerfile}, context root sets to: ${contextRoot}" )
if ( architectures = = null | | architectures . isEmpty ( ) ) {
steps . log . warn ( "ImageBuilder" , "No architectures specified, using default amd64" )
architectures = [ 'linux/amd64' ]
}
2025-02-07 07:18:40 +00:00
2025-02-09 20:00:49 +00:00
steps . withCredentials ( [ steps . usernamePassword ( credentialsId: registryCredentialsId , passwordVariable: 'DOCKER_PASSWORD' , usernameVariable: 'DOCKER_USERNAME' ) ] ) {
steps . log . info ( "ImageBuilder" , "Authentication to ${registry}" )
switch ( builderType ) {
case ImageBuilderTypes . DOCKER_IN_DOCKER :
steps . sh "docker login -u ${steps.env.DOCKER_USERNAME} -p ${steps.env.DOCKER_PASSWORD} ${registry}"
break
case ImageBuilderTypes . KANIKO :
def auth = "${steps.env.DOCKER_USERNAME}:${steps.env.DOCKER_PASSWORD}" . bytes . encodeBase64 ( ) . toString ( )
steps . writeFile file: '/kaniko/.docker/config.json' , text: "" " {
"auths" : {
"${registry}" : {
"auth" : "${auth}"
}
}
} "" "
break
default :
steps . error ( "Unsupported builder type: ${builderType.builder}" )
}
}
2025-02-07 07:18:40 +00:00
2025-02-08 04:07:41 +00:00
switch ( builderType ) {
case ImageBuilderTypes . DOCKER_IN_DOCKER :
2025-02-09 20:00:49 +00:00
steps . dir ( workspace ) {
if ( buildxBuilderName ! = null & & ! buildxBuilderName . isEmpty ( ) & & architectures . size ( ) > 1 ) {
steps . log . info ( "ImageBuilder" , "Building image ${registry}/${repository}/${name} with architectures: ${architectures} using buildx builder: ${buildxBuilderName}, tag sets to ${version}" )
2025-04-14 07:13:55 +00:00
def currentPath = steps . sh ( script: "pwd" , returnStdout: true ) . trim ( )
steps . log . info ( "ImageBuilder" , "Current working dir: ${currentPath}" )
2025-04-14 07:42:10 +00:00
def filesInCurrentDir = steps . sh ( script: "ls -la" , returnStdout: true ) . trim ( )
steps . log . info ( "ImageBuilder" , "Files in current dir: ${filesInCurrentDir}" )
2025-04-14 08:12:23 +00:00
def filesInContext = steps . sh ( script: "ls -la ${contextRoot}" , returnStdout: true ) . trim ( )
steps . log . info ( "ImageBuilder" , "Files in build context: ${filesInContext}" )
2025-02-09 20:18:58 +00:00
steps . log . info ( "ImageBuilder" , "Set builder log level to plain..." )
steps . env . BUILDKIT_PROGRESS = "plain"
2025-02-09 21:04:59 +00:00
steps . log . info ( "ImageBuilder" , "Set builder timeout to 10min..." )
steps . env . BUILDKIT_TIMEOUT = "1800s"
2025-04-14 08:21:15 +00:00
steps . sh "docker buildx build --builder ${buildxBuilderName} --no-cache --platform ${architectures.join(" , ")} -t ${registry}/${repository}/${name}:${version} -f ${dockerfile} --push ${contextRoot}"
2025-02-17 10:14:40 +00:00
steps . env . BUILD_IMAGE_REGISTRY = "${registry}"
steps . env . BUILD_IMAGE_REPO = "${repository}"
steps . env . BUILD_IMAGE_NAME = "${name}"
steps . env . BUILD_IMAGE_VERSION = "${version}"
2025-02-09 20:00:49 +00:00
} else {
architectures . each { architecture - >
def archTag = architecture . split ( "/" ) [ 1 ]
steps . log . info ( "ImageBuilder" , "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}" )
steps . sh "docker build -t ${registry}/${repository}/${name}:${version}-${archTag} --platform ${architecture} -f ${dockerfile} ${contextRoot}"
steps . sh "docker push ${registry}/${repository}/${name}:${version}-${archTag}"
2025-02-08 04:07:41 +00:00
}
2025-02-17 10:14:40 +00:00
steps . env . BUILD_IMAGE_REGISTRY = "${registry}"
steps . env . BUILD_IMAGE_REPO = "${repository}"
steps . env . BUILD_IMAGE_NAME = "${name}"
steps . env . BUILD_IMAGE_VERSION = "${version}-linux-amd64"
2025-02-08 04:07:41 +00:00
}
2025-02-09 20:00:49 +00:00
}
2025-02-08 04:07:41 +00:00
break
2025-02-09 20:00:49 +00:00
case ImageBuilderTypes . KANIKO :
steps . dir ( workspace ) {
2025-02-09 18:56:27 +00:00
architectures . each { architecture - >
def archTag = architecture . split ( "/" ) [ 1 ]
2025-02-09 20:00:49 +00:00
steps . log . info ( "ImageBuilder" , "Building image ${registry}/${repository}/${name} with architectures: ${architectures}, tag sets to ${version}-${archTag}" )
steps . sh "/kaniko/executor --log-format text --context ${contextRoot} --dockerfile ${dockerfile} --destination ${registry}/${repository}/${name}:${version}-${archTag} --custom-platform ${architecture}"
2025-02-09 18:56:27 +00:00
}
2025-02-17 10:14:40 +00:00
steps . env . BUILD_IMAGE_REGISTRY = "${registry}"
steps . env . BUILD_IMAGE_REPO = "${repository}"
steps . env . BUILD_IMAGE_NAME = "${name}"
steps . env . BUILD_IMAGE_VERSION = "${version}-linux-amd64"
2025-02-07 07:18:40 +00:00
}
2025-02-09 20:00:49 +00:00
break
default :
steps . error ( "Unsupported builder type: ${builderType.builder}" )
}
} catch ( Exception e ) {
steps . log . error ( "ImageBuilder" , "Failed to build image: ${e.message}" )
throw e
} finally {
if ( buildxBuilderName ! = null & & ! buildxBuilderName . isEmpty ( ) & & architectures . size ( ) > 1 ) {
try {
steps . log . info ( "ImageBuilder" , "Cleaning up buildx builder: ${buildxBuilderName}" )
steps . sh "docker buildx rm ${buildxBuilderName} || true"
} catch ( Exception e ) {
steps . log . warn ( "ImageBuilder" , "Failed to cleanup buildx builder: ${e.message}" )
2025-02-07 07:18:40 +00:00
}
2025-02-09 20:00:49 +00:00
}
2025-02-07 07:18:40 +00:00
}
}
}
2025-02-09 20:00:49 +00:00