158 lines
3.4 KiB
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
|
|
}
|