diff --git a/pkg/snapshot/layered_map.go b/pkg/snapshot/layered_map.go index 7e973793a0..3ffd63d860 100644 --- a/pkg/snapshot/layered_map.go +++ b/pkg/snapshot/layered_map.go @@ -38,6 +38,7 @@ type LayeredMap struct { cacheHasher func(string) (string, error) } +// NewLayeredMap creates a new layered map which keeps track of adds and deletes. func NewLayeredMap(h func(string) (string, error), c func(string) (string, error)) *LayeredMap { l := LayeredMap{ hasher: h, @@ -49,69 +50,85 @@ func NewLayeredMap(h func(string) (string, error), c func(string) (string, error return &l } +// Snapshot creates a new layer. func (l *LayeredMap) Snapshot() { // Save current state of image - l.UpdateCurrentImage() + l.updateCurrentImage() l.whiteouts = append(l.whiteouts, map[string]struct{}{}) l.layers = append(l.layers, map[string]string{}) l.layerHashCache = map[string]string{} // Erase the hash cache for this new layer. } -// Key returns a hash for added files +// Key returns a hash for added and delted files. func (l *LayeredMap) Key() (string, error) { c := bytes.NewBuffer([]byte{}) enc := json.NewEncoder(c) - err := enc.Encode(l.layers) + err := enc.Encode(l.layers[len(l.layers)-1]) + if err != nil { + return "", err + } + err = enc.Encode(l.whiteouts[len(l.whiteouts)-1]) if err != nil { return "", err } return util.SHA256(c) } -// UpdateCurrentImage computes the current image by -// flattening all layers and stores the result in currentImage. -func (l *LayeredMap) UpdateCurrentImage() { +// getCurrentImage returns the current image by merging the latest +// adds and deletes on to the current image (if its not yet valid.) +func (l *LayeredMap) getCurrentImage() map[string]string { if l.isCurrentImageValid { - return + return l.currentImage } - l.currentImage = map[string]string{} + current := map[string]string{} - // Adding and deleting files over all layers. - for i := 0; i < len(l.layers); i++ { - addedFiles := l.layers[i] - deletedFiles := l.whiteouts[i] + // Copy current image paths/hashes. + for p, h := range l.currentImage { + current[p] = h + } - for add, hash := range addedFiles { - l.currentImage[add] = hash - } + // Add the last layer on top. + addedFiles := l.layers[len(l.layers)-1] + deletedFiles := l.whiteouts[len(l.whiteouts)-1] - for del := range deletedFiles { - delete(l.currentImage, del) - } + for add, hash := range addedFiles { + current[add] = hash } - l.isCurrentImageValid = true + for del := range deletedFiles { + delete(current, del) + } + + return current } -func (l *LayeredMap) Get(s string) (string, bool) { - for i := len(l.layers) - 1; i >= 0; i-- { - if v, ok := l.layers[i][s]; ok { - return v, ok - } +// updateCurrentImage update the internal current image by merging the +// top adds and deletes onto the current image. +func (l *LayeredMap) updateCurrentImage() { + if l.isCurrentImageValid { + return } - return "", false + + l.currentImage = l.getCurrentImage() + l.isCurrentImageValid = true +} + +// get returns the current hash in the current image `l.currentImage`. +func (l *LayeredMap) get(s string) (string, bool) { + h, ok := l.currentImage[s] + return h, ok } // GetCurrentPaths returns all existing paths in the actual current image // cached by FlattenLayers. func (l *LayeredMap) GetCurrentPaths() map[string]struct{} { - l.UpdateCurrentImage() + current := l.getCurrentImage() paths := map[string]struct{}{} - for f := range l.currentImage { + for f := range current { paths[f] = struct{}{} } return paths @@ -162,7 +179,7 @@ func (l *LayeredMap) CheckFileChange(s string) (bool, error) { // adding the file. l.layerHashCache[s] = newV - oldV, ok := l.currentImage[s] + oldV, ok := l.get(s) if ok && newV == oldV { // File hash did not change => Unchanged. return false, nil