lifecycle/cache/caching_image.go

158 lines
3.4 KiB
Go

package cache
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"os"
"github.com/buildpacks/imgutil"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/pkg/errors"
)
type CachingImage struct {
imgutil.Image
cache *VolumeCache
}
func NewCachingImage(image imgutil.Image, cache *VolumeCache) imgutil.Image {
return &CachingImage{
Image: image,
cache: cache,
}
}
func (c *CachingImage) AddLayer(path string) error {
f, err := os.Open(path)
if err != nil {
return errors.Wrap(err, "opening layer file")
}
defer f.Close()
hasher := sha256.New()
if _, err := io.Copy(hasher, f); err != nil {
return errors.Wrap(err, "hashing layer")
}
diffID := "sha256:" + hex.EncodeToString(hasher.Sum(make([]byte, 0, hasher.Size())))
return c.AddLayerWithDiffID(path, diffID)
}
func (c *CachingImage) AddLayerWithDiffID(path string, diffID string) error {
if err := c.cache.AddLayerFile(path, diffID); err != nil {
return err
}
return c.Image.AddLayerWithDiffID(path, diffID)
}
func (c *CachingImage) AddLayerWithDiffIDAndHistory(path string, diffID string, history v1.History) error {
if err := c.cache.AddLayerFile(path, diffID); err != nil {
return err
}
return c.Image.AddLayerWithDiffIDAndHistory(path, diffID, history)
}
func (c *CachingImage) ReuseLayer(diffID string) error {
found, err := c.cache.HasLayer(diffID)
if err != nil {
return err
}
if found {
if err := c.cache.ReuseLayer(diffID); err != nil {
return err
}
path, err := c.cache.RetrieveLayerFile(diffID)
if err != nil {
return err
}
return c.Image.AddLayerWithDiffID(path, diffID)
}
if err := c.Image.ReuseLayer(diffID); err != nil {
return err
}
rc, err := c.Image.GetLayer(diffID)
if err != nil {
return err
}
return c.cache.AddLayer(rc, diffID)
}
func (c *CachingImage) ReuseLayerWithHistory(diffID string, history v1.History) error {
found, err := c.cache.HasLayer(diffID)
if err != nil {
return err
}
if found {
if err := c.cache.ReuseLayer(diffID); err != nil {
return err
}
path, err := c.cache.RetrieveLayerFile(diffID)
if err != nil {
return err
}
return c.Image.AddLayerWithDiffIDAndHistory(path, diffID, history)
}
if err := c.Image.ReuseLayerWithHistory(diffID, history); err != nil {
return err
}
rc, err := c.Image.GetLayer(diffID)
if err != nil {
return err
}
return c.cache.AddLayer(rc, diffID)
}
func (c *CachingImage) GetLayer(diffID string) (io.ReadCloser, error) {
if found, err := c.cache.HasLayer(diffID); err != nil {
return nil, fmt.Errorf("layer with SHA '%s' not found", diffID)
} else if found {
return c.cache.RetrieveLayer(diffID)
}
return c.Image.GetLayer(diffID)
}
func (c *CachingImage) Save(additionalNames ...string) error {
err := c.Image.Save(additionalNames...)
if saveSucceededFor(c.Name(), err) {
if err := c.cache.Commit(); err != nil {
return errors.Wrap(err, "failed to commit cache")
}
}
return err
}
func (c *CachingImage) SaveAs(name string, additionalNames ...string) error {
err := c.Image.SaveAs(name, additionalNames...)
if saveSucceededFor(c.Name(), err) {
if err := c.cache.Commit(); err != nil {
return errors.Wrap(err, "failed to commit cache")
}
}
return err
}
func saveSucceededFor(imageName string, err error) bool {
if err == nil {
return true
}
if saveErr, isSaveErr := err.(imgutil.SaveError); isSaveErr {
for _, d := range saveErr.Errors {
if d.ImageName == imageName {
return false
}
}
return true
}
return false
}