diff --git a/cnb_image.go b/cnb_image.go index 2881d446..cac042cf 100644 --- a/cnb_image.go +++ b/cnb_image.go @@ -357,6 +357,32 @@ func (i *CNBImageCore) AddLayerWithHistory(layer v1.Layer, history v1.History) e return err } +func (i *CNBImageCore) AddOrReuseLayerWithHistory(path string, diffID string, history v1.History) error { + prevLayerExists, err := i.PreviousImageHasLayer(diffID) + if err != nil { + return err + } + if !prevLayerExists { + return i.AddLayerWithDiffIDAndHistory(path, diffID, history) + } + return i.ReuseLayerWithHistory(diffID, history) +} + +func (i *CNBImageCore) PreviousImageHasLayer(diffID string) (bool, error) { + if i.previousImage == nil { + return false, nil + } + layerHash, err := v1.NewHash(diffID) + if err != nil { + return false, fmt.Errorf("failed to get layer hash: %w", err) + } + prevConfigFile, err := getConfigFile(i.previousImage) + if err != nil { + return false, fmt.Errorf("failed to get previous image config: %w", err) + } + return contains(prevConfigFile.RootFS.DiffIDs, layerHash), nil +} + func (i *CNBImageCore) Rebase(baseTopLayerDiffID string, withNewBase Image) error { newBase := withNewBase.UnderlyingImage() // FIXME: when all imgutil.Images are v1.Images, we can remove this part var err error @@ -457,6 +483,14 @@ func getHistory(forIndex int, fromImage v1.Image) (v1.History, error) { } func (i *CNBImageCore) ReuseLayerWithHistory(diffID string, history v1.History) error { + var err error + // ensure existing history + if err = i.MutateConfigFile(func(c *v1.ConfigFile) { + c.History = NormalizedHistory(c.History, len(c.RootFS.DiffIDs)) + }); err != nil { + return err + } + layerHash, err := v1.NewHash(diffID) if err != nil { return fmt.Errorf("failed to get layer hash: %w", err) diff --git a/fakes/image.go b/fakes/image.go index 1e849d9a..a27704b6 100644 --- a/fakes/image.go +++ b/fakes/image.go @@ -18,6 +18,8 @@ import ( "github.com/buildpacks/imgutil" ) +var _ imgutil.Image = &Image{} + func NewImage(name, topLayerSha string, identifier imgutil.Identifier) *Image { return &Image{ labels: nil, @@ -238,6 +240,10 @@ func shaForFile(path string) (string, error) { return hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size()))), nil } +func (i *Image) AddOrReuseLayerWithHistory(_, _ string, _ v1.History) error { + panic("implement me") +} + func (i *Image) GetLayer(sha string) (io.ReadCloser, error) { path, ok := i.layersMap[sha] if !ok { diff --git a/image.go b/image.go index f8009442..4a43381c 100644 --- a/image.go +++ b/image.go @@ -62,6 +62,7 @@ type Image interface { AddLayer(path string) error AddLayerWithDiffID(path, diffID string) error AddLayerWithDiffIDAndHistory(path, diffID string, history v1.History) error + AddOrReuseLayerWithHistory(path, diffID string, history v1.History) error Delete() error Rebase(string, Image) error RemoveLabel(string) error diff --git a/local/local.go b/local/local.go index 77c7a032..e18cf276 100644 --- a/local/local.go +++ b/local/local.go @@ -166,6 +166,17 @@ func (i *Image) addLayerToStore(fromPath, withDiffID string) (v1.Layer, error) { return layer, nil } +func (i *Image) AddOrReuseLayerWithHistory(path string, diffID string, history v1.History) error { + prevLayerExists, err := i.PreviousImageHasLayer(diffID) + if err != nil { + return err + } + if !prevLayerExists { + return i.AddLayerWithDiffIDAndHistory(path, diffID, history) + } + return i.ReuseLayerWithHistory(diffID, history) +} + func (i *Image) Rebase(baseTopLayerDiffID string, withNewBase imgutil.Image) error { if err := i.ensureLayers(); err != nil { return err