Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support for multi tag build and build options #76

Open
wants to merge 17 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions ci-runner/executor/stage/ciStages.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ func (impl *CiStage) runBuildArtifact(ciCdRequest *helper.CiCdTriggerEvent, metr
// build
start := time.Now()
metrics.BuildStartTime = start
dest, err := impl.dockerHelper.BuildArtifact(ciCdRequest.CommonWorkflowRequest) // TODO make it skipable
dest, err := impl.dockerHelper.BuildArtifact(ciCdRequest.CommonWorkflowRequest, scriptEnvs, preCiStageOutVariable) // TODO make it skipable
metrics.BuildDuration = time.Since(start).Seconds()
if err != nil {
log.Println("Error in building artifact", "err", err)
Expand All @@ -410,7 +410,7 @@ func (impl *CiStage) runBuildArtifact(ciCdRequest *helper.CiCdTriggerEvent, metr
return dest, err
}

func (impl *CiStage) extractDigest(ciCdRequest *helper.CiCdTriggerEvent, dest string, metrics *helper.CIMetrics, artifactUploaded bool) (string, error) {
func (impl *CiStage) extractDigest(ciCdRequest *helper.CiCdTriggerEvent, dest string, metrics *helper.CIMetrics, artifactUploaded bool, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*helper.VariableObject) (string, error) {

var digest string
var err error
Expand All @@ -423,7 +423,7 @@ func (impl *CiStage) extractDigest(ciCdRequest *helper.CiCdTriggerEvent, dest st
} else {
// push to dest
log.Println(util.DEVTRON, "Docker push Artifact", "dest", dest)
err = impl.pushArtifact(ciCdRequest, dest, digest, metrics, artifactUploaded)
err = impl.pushArtifact(ciCdRequest, dest, digest, metrics, artifactUploaded, scriptEnvs, preCiStageOutVariable)
if err != nil {
return err
}
Expand Down Expand Up @@ -496,7 +496,7 @@ func (impl *CiStage) getImageDestAndDigest(ciCdRequest *helper.CiCdTriggerEvent,
if err != nil {
return "", "", err
}
digest, err := impl.extractDigest(ciCdRequest, dest, metrics, artifactUploaded)
digest, err := impl.extractDigest(ciCdRequest, dest, metrics, artifactUploaded, scriptEnvs, preCiStageOutVariable)
if err != nil {
log.Println("Error in extracting digest", "err", err)
return "", "", err
Expand Down Expand Up @@ -564,9 +564,10 @@ func sendCDFailureEvent(ciRequest *helper.CommonWorkflowRequest, err *helper.CdS
}
}

func (impl *CiStage) pushArtifact(ciCdRequest *helper.CiCdTriggerEvent, dest string, digest string, metrics *helper.CIMetrics, artifactUploaded bool) error {
func (impl *CiStage) pushArtifact(ciCdRequest *helper.CiCdTriggerEvent, dest string, digest string, metrics *helper.CIMetrics, artifactUploaded bool, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*helper.VariableObject) error {
imageRetryCountValue := ciCdRequest.CommonWorkflowRequest.ImageRetryCount
imageRetryIntervalValue := ciCdRequest.CommonWorkflowRequest.ImageRetryInterval

var err error
for i := 0; i < imageRetryCountValue+1; i++ {
if i != 0 {
Expand All @@ -585,6 +586,27 @@ func (impl *CiStage) pushArtifact(ciCdRequest *helper.CiCdTriggerEvent, dest str
WithFailureMessage(workFlow.PushFailed.String()).
WithArtifactUploaded(artifactUploaded)
}
multiDockerTagsValue := helper.GetMultiDockerTagsValue(scriptEnvs, preCiStageOutVariable)
if len(multiDockerTagsValue) > 0 {
tags := strings.Split(multiDockerTagsValue, `,`)
for _, tmpDockerTag := range tags {
if !strings.Contains(tmpDockerTag, ":") {
fullImageUrl, err := helper.BuildDockerImagePathForCustomTag(ciCdRequest.CommonWorkflowRequest, tmpDockerTag)
if err != nil {
log.Println("Error in building docker image", "fullImageUrl", fullImageUrl, "err", err)
return err
}
tmpDockerTag = fullImageUrl
}
log.Println(" -----> custom-tag push " + tmpDockerTag)
ciContext := cicxt.BuildCiContext(context.Background(), ciCdRequest.CommonWorkflowRequest.EnableSecretMasking)
err = impl.dockerHelper.PushArtifact(ciContext, tmpDockerTag)
if err != nil {
log.Println("Error in pushing artifact", "artifact", tmpDockerTag, "err", err)
}
}

}
return err
}

Expand Down
167 changes: 147 additions & 20 deletions ci-runner/helper/DockerHelper.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,13 @@ const (
BUILDX_K8S_DRIVER_NAME = "devtron-buildx-builder"
BUILDX_NODE_NAME = "devtron-buildx-node-"
DOCKERD_OUTPUT_FILE_PATH = "/usr/local/bin/nohup.out"
MULTI_TAG_ENV = "docker_env_multi_tags"
)

type DockerHelper interface {
StartDockerDaemon(commonWorkflowRequest *CommonWorkflowRequest)
DockerLogin(ciContext cicxt.CiContext, dockerCredentials *DockerCredentials) error
BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error)
BuildArtifact(ciRequest *CommonWorkflowRequest, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) (string, error)
StopDocker(ciContext cicxt.CiContext) error
PushArtifact(ciContext cicxt.CiContext, dest string) error
ExtractDigestForBuildx(dest string, ciRequest *CommonWorkflowRequest) (string, error)
Expand Down Expand Up @@ -275,7 +276,8 @@ func (impl *DockerHelperImpl) DockerLogin(ciContext cicxt.CiContext, dockerCrede
return util.ExecuteWithStageInfoLog(util.DOCKER_LOGIN_STAGE, performDockerLogin)
}

func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest) (string, error) {
func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) (string, error) {

ciContext := cicxt.BuildCiContext(context.Background(), ciRequest.EnableSecretMasking)
err := impl.DockerLogin(ciContext, &DockerCredentials{
DockerUsername: ciRequest.DockerUsername,
Expand Down Expand Up @@ -323,7 +325,8 @@ func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest) (s
}

}
dockerBuildFlags := getDockerBuildFlagsMap(dockerBuildConfig)

dockerBuildFlags := getDockerBuildFlagsMap(dockerBuildConfig, scriptEnvs, preCiStageOutVariable)
for key, value := range dockerBuildFlags {
dockerBuild = dockerBuild + " " + key + value
}
Expand Down Expand Up @@ -385,7 +388,25 @@ func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest) (s

multiNodeK8sDriver := useBuildxK8sDriver && len(eligibleK8sDriverNodes) > 1
exportBuildxCacheAfterBuild := ciRequest.AsyncBuildxCacheExport && multiNodeK8sDriver
dockerBuild, buildxExportCacheFunc = impl.getBuildxBuildCommand(ciContext, exportBuildxCacheAfterBuild, cacheEnabled, ciRequest.BuildxCacheModeMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath)

multiDockerTagsValue := GetMultiDockerTagsValue(scriptEnvs, preCiStageOutVariable)
fullUrlDockerTags := []string{}
if len(multiDockerTagsValue) > 0 {
dockerTags := strings.Split(multiDockerTagsValue, `,`)
for _, tmpDockerTag := range dockerTags {
tmpDockerTag = strings.TrimSpace(tmpDockerTag)
if !strings.Contains(tmpDockerTag, ":") {
fullImageUrl, err := BuildDockerImagePathForCustomTag(ciRequest, tmpDockerTag)
if err != nil {
log.Println("Error in building docker image multiple tags", "fullImageUrl", fullImageUrl, "err", err)
return "", err
}
tmpDockerTag = fullImageUrl
}
fullUrlDockerTags = append(fullUrlDockerTags, tmpDockerTag)
}
}
dockerBuild, buildxExportCacheFunc = impl.getBuildxBuildCommand(ciContext, exportBuildxCacheAfterBuild, cacheEnabled, ciRequest.BuildxCacheModeMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath, fullUrlDockerTags)
} else {
dockerBuild = fmt.Sprintf("%s -f %s --network host -t %s %s", dockerBuild, dockerfilePath, ciRequest.DockerRepository, dockerBuildConfig.BuildContext)
}
Expand Down Expand Up @@ -426,7 +447,7 @@ func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest) (s
}

if !useBuildx {
err = impl.tagDockerBuild(ciContext, ciRequest.DockerRepository, dest)
err = impl.tagDockerBuild(ciContext, ciRequest, dest, scriptEnvs, preCiStageOutVariable)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -476,25 +497,60 @@ func (impl *DockerHelperImpl) BuildArtifact(ciRequest *CommonWorkflowRequest) (s
return dest, nil
}

func getDockerBuildFlagsMap(dockerBuildConfig *DockerBuildConfig) map[string]string {
func getDockerBuildFlagsMap(dockerBuildConfig *DockerBuildConfig, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) map[string]string {
dockerBuildFlags := make(map[string]string)
dockerBuildArgsMap := dockerBuildConfig.Args
for k, v := range dockerBuildArgsMap {
flagKey := fmt.Sprintf("%s %s", BUILD_ARG_FLAG, k)
dockerBuildFlags[flagKey] = parseDockerFlagParam(v)
parsedDockerFlagValue := parseDockerFlagParam(v, scriptEnvs, preCiStageOutVariable)
if len(parsedDockerFlagValue) > 0 {
dockerBuildFlags[flagKey] = parsedDockerFlagValue
}
}
dockerBuildOptionsMap := dockerBuildConfig.DockerBuildOptions
for k, v := range dockerBuildOptionsMap {
flagKey := "--" + k
dockerBuildFlags[flagKey] = parseDockerFlagParam(v)
parsedDockerFlagValue := parseDockerFlagParam(v, scriptEnvs, preCiStageOutVariable)
if len(parsedDockerFlagValue) > 0 {
dockerBuildFlags[flagKey] = parsedDockerFlagValue
}
}
return dockerBuildFlags
}

func parseDockerFlagParam(param string) string {
func parseDockerFlagParam(param string, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) string {
value := param
if strings.HasPrefix(param, DEVTRON_ENV_VAR_PREFIX) {
value = os.Getenv(strings.TrimPrefix(param, DEVTRON_ENV_VAR_PREFIX))
key := strings.TrimPrefix(param, DEVTRON_ENV_VAR_PREFIX)

if preCiStageOutVariable != nil {
for k, task := range preCiStageOutVariable {
if _, ok := preCiStageOutVariable[k]; ok {
for variable, details := range task {
if variable == key {
outputVariableEnv := details.Value
if len(outputVariableEnv) > 0 {
value = outputVariableEnv
}
}

}
}
}
}
if len(value) == 0 && scriptEnvs != nil {
scriptEnvVal, ok := scriptEnvs[key]
if ok {
value = scriptEnvVal
}
}

if len(value) == 0 {
value = os.Getenv(key)
}
if strings.HasPrefix(value, DEVTRON_ENV_VAR_PREFIX) {
return ""
}
}

return wrapSingleOrDoubleQuotedValue(value)
Expand Down Expand Up @@ -578,7 +634,7 @@ func getSourceCaches(targetPlatforms, oldCachePathLocation string) string {
return strings.Join(allCachePaths, " ")
}

func (impl *DockerHelperImpl) getBuildxBuildCommandV2(ciContext cicxt.CiContext, cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string) (string, func() error) {
func (impl *DockerHelperImpl) getBuildxBuildCommandV2(ciContext cicxt.CiContext, cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string, additionalImageTags []string) (string, func() error) {
dockerBuild = fmt.Sprintf("%s %s -f %s --network host --allow network.host --allow security.insecure", dockerBuild, dockerBuildConfig.BuildContext, dockerfilePath)
exportCacheCmds := make(map[string]string)

Expand All @@ -600,18 +656,26 @@ func (impl *DockerHelperImpl) getBuildxBuildCommandV2(ciContext cicxt.CiContext,
}

manifestLocation := util.LOCAL_BUILDX_LOCATION + "/manifest.json"
dockerBuild = fmt.Sprintf("%s -t %s --push --metadata-file %s", dockerBuild, dest, manifestLocation)
dockerBuild = fmt.Sprintf("%s %s --push --metadata-file %s", dockerBuild, GetTagArgumentFlagForBuildx(dest, additionalImageTags), manifestLocation)

return dockerBuild, impl.getBuildxExportCacheFunc(ciContext, exportCacheCmds)
}

func (impl *DockerHelperImpl) getBuildxBuildCommandV1(cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string) (string, func() error) {
func GetTagArgumentFlagForBuildx(dest string, additionalTags []string) string {
flagCmd := fmt.Sprintf("-t %s", dest)
for _, tag := range additionalTags {
flagCmd = fmt.Sprintf("%s -t %s", flagCmd, tag)
}
return flagCmd
}

func (impl *DockerHelperImpl) getBuildxBuildCommandV1(cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string, additionalImageTags []string) (string, func() error) {

cacheMode := CacheModeMax
if useCacheMin {
cacheMode = CacheModeMin
}
dockerBuild = fmt.Sprintf("%s -f %s -t %s --push %s --network host --allow network.host --allow security.insecure", dockerBuild, dockerfilePath, dest, dockerBuildConfig.BuildContext)
dockerBuild = fmt.Sprintf("%s -f %s %s --push %s --network host --allow network.host --allow security.insecure", dockerBuild, dockerfilePath, GetTagArgumentFlagForBuildx(dest, additionalImageTags), dockerBuildConfig.BuildContext)
if cacheEnabled {
dockerBuild = fmt.Sprintf("%s --cache-to=type=local,dest=%s,mode=%s --cache-from=type=local,src=%s", dockerBuild, localCachePath, cacheMode, oldCacheBuildxPath)
}
Expand All @@ -629,11 +693,11 @@ func (impl *DockerHelperImpl) getBuildxBuildCommandV1(cacheEnabled bool, useCach
return dockerBuild, nil
}

func (impl *DockerHelperImpl) getBuildxBuildCommand(ciContext cicxt.CiContext, exportBuildxCacheAfterBuild bool, cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string) (string, func() error) {
func (impl *DockerHelperImpl) getBuildxBuildCommand(ciContext cicxt.CiContext, exportBuildxCacheAfterBuild bool, cacheEnabled bool, useCacheMin bool, dockerBuild, oldCacheBuildxPath, localCachePath, dest string, dockerBuildConfig *DockerBuildConfig, dockerfilePath string, additionalImageTags []string) (string, func() error) {
if exportBuildxCacheAfterBuild {
return impl.getBuildxBuildCommandV2(ciContext, cacheEnabled, useCacheMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath)
return impl.getBuildxBuildCommandV2(ciContext, cacheEnabled, useCacheMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath, additionalImageTags)
}
return impl.getBuildxBuildCommandV1(cacheEnabled, useCacheMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath)
return impl.getBuildxBuildCommandV1(cacheEnabled, useCacheMin, dockerBuild, oldCacheBuildxPath, localCachePath, dest, dockerBuildConfig, dockerfilePath, additionalImageTags)
}

func (impl *DockerHelperImpl) handleLanguageVersion(ciContext cicxt.CiContext, projectPath string, buildpackConfig *BuildPackConfig) {
Expand Down Expand Up @@ -722,18 +786,70 @@ func (impl *DockerHelperImpl) executeCmd(ciContext cicxt.CiContext, dockerBuild
return err
}

func (impl *DockerHelperImpl) tagDockerBuild(ciContext cicxt.CiContext, dockerRepository string, dest string) error {
dockerTag := "docker tag " + dockerRepository + ":latest" + " " + dest
log.Println(" -----> " + dockerTag)
func (impl *DockerHelperImpl) tagDockerBuild(ciContext cicxt.CiContext, ciRequest *CommonWorkflowRequest, dest string, scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) error {
dockerTag := "docker tag " + ciRequest.DockerRepository + ":latest" + " " + dest
dockerTagCMD := impl.GetCommandToExecute(dockerTag)
err := impl.cmdExecutor.RunCommand(ciContext, dockerTagCMD)
if err != nil {
log.Println(err)
return err
}

log.Println(" -----> " + dockerTag)
multiDockerTagsValue := GetMultiDockerTagsValue(scriptEnvs, preCiStageOutVariable)
if len(multiDockerTagsValue) > 0 {
tags := strings.Split(multiDockerTagsValue, `,`)
for _, tmpDockerTag := range tags {
tmpDockerTag = strings.TrimSpace(tmpDockerTag)
if !strings.Contains(tmpDockerTag, ":") {
fullImageUrl, err := BuildDockerImagePathForCustomTag(ciRequest, tmpDockerTag)
if err != nil {
log.Println("Error in building docker image", "err", err)
return err
}
tmpDockerTag = fullImageUrl
}
log.Println(" -----> custom-tag " + tmpDockerTag)
tmpDockerTagCommand := "docker tag " + ciRequest.DockerRepository + ":latest" + " " + tmpDockerTag
tmpDockerTagCMD := impl.GetCommandToExecute(tmpDockerTagCommand)
err := impl.cmdExecutor.RunCommand(ciContext, tmpDockerTagCMD)
if err != nil {
log.Println(err)
return err
}
}

}

return nil
}

func GetMultiDockerTagsValue(scriptEnvs map[string]string, preCiStageOutVariable map[int]map[string]*VariableObject) string {
multiDockerTagsValue := ""
if preCiStageOutVariable != nil {
for k, task := range preCiStageOutVariable {
if _, ok := preCiStageOutVariable[k]; ok {
for variable, details := range task {
if variable == MULTI_TAG_ENV {
outputVariableEnv := details.Value
if len(outputVariableEnv) > 0 {
multiDockerTagsValue = outputVariableEnv
}
}

}
}
}
}
if len(multiDockerTagsValue) == 0 && scriptEnvs != nil {
scriptEnvVal, ok := scriptEnvs[MULTI_TAG_ENV]
if ok {
multiDockerTagsValue = scriptEnvVal
}
}
return multiDockerTagsValue
}

func (impl *DockerHelperImpl) setupCacheForBuildx(ciContext cicxt.CiContext, localCachePath string, oldCacheBuildxPath string) error {
err := impl.checkAndCreateDirectory(ciContext, localCachePath)
if err != nil {
Expand Down Expand Up @@ -818,6 +934,17 @@ func BuildDockerImagePath(ciRequest *CommonWorkflowRequest) (string, error) {

}

func BuildDockerImagePathForCustomTag(ciRequest *CommonWorkflowRequest, dockerTag string) (string, error) {
return utils.BuildDockerImagePath(bean.DockerRegistryInfo{
DockerImageTag: dockerTag,
DockerRegistryId: ciRequest.DockerRegistryId,
DockerRegistryType: ciRequest.DockerRegistryType,
DockerRegistryURL: ciRequest.IntermediateDockerRegistryUrl,
DockerRepository: ciRequest.DockerRepository,
})

}

func (impl *DockerHelperImpl) PushArtifact(ciContext cicxt.CiContext, dest string) error {
//awsLogin := "$(aws ecr get-login --no-include-email --region " + ciRequest.AwsRegion + ")"
dockerPush := "docker push " + dest
Expand Down
Loading