Skip to content

Commit

Permalink
feat(multiarch): support cleanup of images built in multiarch mode
Browse files Browse the repository at this point in the history
Signed-off-by: Timofey Kirillov <timofey.kirillov@flant.com>
  • Loading branch information
distorhead committed Apr 21, 2023
1 parent 6870d4f commit 64b50e8
Show file tree
Hide file tree
Showing 15 changed files with 262 additions and 135 deletions.
4 changes: 2 additions & 2 deletions pkg/build/build_phase.go
Expand Up @@ -372,7 +372,7 @@ func (phase *BuildPhase) publishMultiplatformImageMetadata(ctx context.Context,
return fmt.Errorf("unable to post multiplatform image %s %s: %w", name, img.GetStageID(), err)
}

desc, err := stagesStorage.GetStageDescription(ctx, phase.Conveyor.ProjectName(), img.GetStageID().Digest, img.GetStageID().UniqueID)
desc, err := stagesStorage.GetStageDescription(ctx, phase.Conveyor.ProjectName(), img.GetStageID())
if err != nil {
return fmt.Errorf("unable to get image %s %s descriptor: %w", name, img.GetStageID(), err)
}
Expand Down Expand Up @@ -1093,7 +1093,7 @@ func (phase *BuildPhase) atomicBuildStageImage(ctx context.Context, img *image.I
return fmt.Errorf("unable to store stage %s digest %s image %s into repo %s: %w", stg.LogDetailedName(), stg.GetDigest(), stageImage.Image.Name(), phase.Conveyor.StorageManager.GetStagesStorage().String(), err)
}

if desc, err := phase.Conveyor.StorageManager.GetStagesStorage().GetStageDescription(ctx, phase.Conveyor.ProjectName(), stg.GetDigest(), uniqueID); err != nil {
if desc, err := phase.Conveyor.StorageManager.GetStagesStorage().GetStageDescription(ctx, phase.Conveyor.ProjectName(), *imagePkg.NewStageID(stg.GetDigest(), uniqueID)); err != nil {
return fmt.Errorf("unable to get stage %s digest %s image %s description from repo %s after stages has been stored into repo: %w", stg.LogDetailedName(), stg.GetDigest(), stageImage.Image.Name(), phase.Conveyor.StorageManager.GetStagesStorage().String(), err)
} else {
stageImage.Image.SetStageDescription(desc)
Expand Down
2 changes: 1 addition & 1 deletion pkg/build/image/multiplatform_image.go
Expand Up @@ -35,7 +35,7 @@ func NewMultiplatformImage(name string, images []*Image, storageManager manager.
return stageDesc.StageID.String()
})
img.calculatedDigest = util.Sha3_224Hash(metaStageDeps...)
img.stageID = common_image.StageID{Digest: img.GetDigest()}
img.stageID = *common_image.NewStageID(img.GetDigest(), 0)

return img
}
Expand Down
97 changes: 79 additions & 18 deletions pkg/cleaning/cleanup.go
Expand Up @@ -669,13 +669,15 @@ func (m *cleanupManager) cleanupUnusedStages(ctx context.Context) error {

// skip stages and their relatives covered by Kubernetes- or git history-based cleanup policies
stageDescriptionListToDelete := stageDescriptionList

{
excludedSDListByReason := make(map[string][]*image.StageDescription)

for reason, sdList := range m.stageManager.GetProtectedStageDescriptionListByReason() {
for _, sd := range sdList {
var excludedSDListBySD []*image.StageDescription
stageDescriptionListToDelete, excludedSDListBySD = m.excludeStageAndRelativesByImageID(stageDescriptionListToDelete, sd.Info.ID)

stageDescriptionListToDelete, excludedSDListBySD = m.excludeStageAndRelativesByImage(stageDescriptionListToDelete, sd.Info)

for _, exclSD := range excludedSDListBySD {
if sd.Info.Name == exclSD.Info.Name {
Expand Down Expand Up @@ -714,7 +716,7 @@ func (m *cleanupManager) cleanupUnusedStages(ctx context.Context) error {
for _, sd := range stageDescriptionListToDelete {
if (time.Since(sd.Info.GetCreatedAt()).Hours()) <= float64(keepImagesBuiltWithinLastNHours) {
var excludedRelativesSDList []*image.StageDescription
stageDescriptionListToDelete, excludedRelativesSDList = m.excludeStageAndRelativesByImageID(stageDescriptionListToDelete, sd.Info.ID)
stageDescriptionListToDelete, excludedRelativesSDList = m.excludeStageAndRelativesByImage(stageDescriptionListToDelete, sd.Info)
excludedSDList = append(excludedSDList, excludedRelativesSDList...)
}
}
Expand Down Expand Up @@ -854,36 +856,95 @@ func deleteImportsMetadata(ctx context.Context, projectName string, storageManag
})
}

func (m *cleanupManager) excludeStageAndRelativesByImageID(stages []*image.StageDescription, imageID string) ([]*image.StageDescription, []*image.StageDescription) {
stage := findStageByImageID(stages, imageID)
if stage == nil {
func (m *cleanupManager) excludeStageAndRelativesByImage(stages []*image.StageDescription, excludeImage *image.Info) ([]*image.StageDescription, []*image.StageDescription) {
excludeStages := findStagesByImageID(stages, excludeImage)
if len(excludeStages) == 0 {
return stages, []*image.StageDescription{}
}
return m.excludeStageAndRelativesByStages(stages, excludeStages)
}

// findStagesByImage could return multiple stages when target image.Info is an image index
func findStagesByImage(stages []*image.StageDescription, target *image.Info, imageMatcher, indexMatcher func(stg *image.StageDescription, target *image.Info) bool) (res []*image.StageDescription) {
if target.IsIndex {
if indexMatcher != nil {
for _, stg := range stages {
if indexMatcher(stg, target) {
res = append(res, stg)
}
}
}
for _, subimg := range target.Index {
res = append(res, findStagesByImage(stages, subimg, imageMatcher, indexMatcher)...)
}
} else if imageMatcher != nil {
for _, stg := range stages {
if imageMatcher(stg, target) {
res = append(res, stg)
}
}
}
return
}

return m.excludeStageAndRelativesByStage(stages, stage)
func findStagesByImageID(stages []*image.StageDescription, target *image.Info) []*image.StageDescription {
return findStagesByImage(
stages, target,
func(stg *image.StageDescription, target *image.Info) bool {
return stg.Info.ID == target.ID
},
func(stg *image.StageDescription, target *image.Info) bool {
return stg.Info.Name == target.Name
},
)
}

func findStagesByImageParentID(stages []*image.StageDescription, target *image.Info) []*image.StageDescription {
return findStagesByImage(
stages, target,
func(stg *image.StageDescription, target *image.Info) bool {
return stg.Info.ID == target.ParentID
}, nil,
)
}

func findStageByImageID(stages []*image.StageDescription, imageID string) *image.StageDescription {
for _, stage := range stages {
if stage.Info.ID == imageID {
return stage
for _, stg := range stages {
if stg.Info.ID == imageID {
return stg
}
}

return nil
}

func (m *cleanupManager) excludeStageAndRelativesByStage(stages []*image.StageDescription, stage *image.StageDescription) ([]*image.StageDescription, []*image.StageDescription) {
func appendUniqueStageDescriptions(stages []*image.StageDescription, newStages ...*image.StageDescription) []*image.StageDescription {
appendUnique:
for _, newStg := range newStages {
for _, stg := range stages {
if stg.StageID.IsEqual(*newStg.StageID) {
continue appendUnique
}
}
stages = append(stages, newStg)
}
return stages
}

func (m *cleanupManager) excludeStageAndRelativesByStages(stages, stagesToExclude []*image.StageDescription) ([]*image.StageDescription, []*image.StageDescription) {
var excludedStages []*image.StageDescription
currentStage := stage
for {
stages = excludeStages(stages, currentStage)
excludedStages = append(excludedStages, currentStage)
currentStagesToExclude := stagesToExclude

for len(currentStagesToExclude) > 0 {
stages = excludeStages(stages, currentStagesToExclude...)
excludedStages = appendUniqueStageDescriptions(excludedStages, currentStagesToExclude...)

currentStage = findStageByImageID(stages, currentStage.Info.ParentID)
if currentStage == nil {
break
var nextStagesToExclude []*image.StageDescription
for _, excludeStg := range currentStagesToExclude {
excludeByParents := findStagesByImageParentID(stages, excludeStg.Info)
nextStagesToExclude = appendUniqueStageDescriptions(nextStagesToExclude, excludeByParents...)
}

currentStagesToExclude = nextStagesToExclude
}

return stages, excludedStages
Expand Down
1 change: 1 addition & 0 deletions pkg/cleaning/stage_manager/manager.go
Expand Up @@ -27,6 +27,7 @@ func NewManager() Manager {

type stage struct {
stageID string
isMultiplatform bool
description *image.StageDescription
isProtected bool
protectionReason string
Expand Down

0 comments on commit 64b50e8

Please sign in to comment.