forked from buildpacks/lifecycle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrestorer.go
119 lines (103 loc) · 3.78 KB
/
restorer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package lifecycle
import (
"path/filepath"
"github.com/pkg/errors"
"golang.org/x/sync/errgroup"
"github.com/buildpacks/lifecycle/api"
"github.com/buildpacks/lifecycle/buildpack"
"github.com/buildpacks/lifecycle/layers"
"github.com/buildpacks/lifecycle/platform"
)
type Restorer struct {
LayersDir string
Logger Logger
Buildpacks []buildpack.GroupBuildpack
LayerMetadataRestorer LayerMetadataRestorer // Platform API >= 0.7
LayersMetadata platform.LayersMetadata // Platform API >= 0.7
Platform Platform
}
// Restore restores metadata for launch and cache layers into the layers directory and attempts to restore layer data for cache=true layers, removing the layer when unsuccessful.
// If a usable cache is not provided, Restore will not restore any cache=true layer metadata.
func (r *Restorer) Restore(cache Cache) error {
cacheMeta, err := retrieveCacheMetadata(cache, r.Logger)
if err != nil {
return err
}
useShaFiles := !r.restoresLayerMetadata()
layerSHAStore := NewLayerSHAStore(useShaFiles)
if r.restoresLayerMetadata() {
if err := r.LayerMetadataRestorer.Restore(r.Buildpacks, r.LayersMetadata, cacheMeta, layerSHAStore); err != nil {
return err
}
}
var g errgroup.Group
for _, buildpack := range r.Buildpacks {
cachedLayers := cacheMeta.MetadataForBuildpack(buildpack.ID).Layers
var cachedFn func(bpLayer) bool
if api.MustParse(buildpack.API).AtLeast("0.6") {
// On Buildpack API 0.6+, the <layer>.toml file never contains layer types information.
// The cache metadata is the only way to identify cache=true layers.
cachedFn = func(l bpLayer) bool {
layer, ok := cachedLayers[filepath.Base(l.path)]
return ok && layer.Cache
}
} else {
// On Buildpack API < 0.6, the <layer>.toml file contains layer types information.
// Prefer <layer>.toml file to cache metadata in case the cache was cleared between builds and
// the analyzer that wrote the files is on a previous version of the lifecycle, that doesn't cross-reference the cache metadata when writing the files.
// This allows the restorer to cleanup <layer>.toml files for layers that are not actually in the cache.
cachedFn = forCached
}
buildpackDir, err := readBuildpackLayersDir(r.LayersDir, buildpack, r.Logger)
if err != nil {
return errors.Wrapf(err, "reading buildpack layer directory")
}
foundLayers := buildpackDir.findLayers(cachedFn)
for _, bpLayer := range foundLayers {
cachedLayer, exists := cachedLayers[bpLayer.name()]
if !exists {
r.Logger.Infof("Removing %q, not in cache", bpLayer.Identifier())
if err := bpLayer.remove(); err != nil {
return errors.Wrapf(err, "removing layer")
}
continue
}
layerSha, err := layerSHAStore.get(buildpack.ID, bpLayer)
if err != nil {
return err
}
if layerSha != cachedLayer.SHA {
r.Logger.Infof("Removing %q, wrong sha", bpLayer.Identifier())
r.Logger.Debugf("Layer sha: %q, cache sha: %q", layerSha, cachedLayer.SHA)
if err := bpLayer.remove(); err != nil {
return errors.Wrapf(err, "removing layer")
}
} else {
r.Logger.Infof("Restoring data for %q from cache", bpLayer.Identifier())
g.Go(func() error {
return r.restoreLayer(cache, cachedLayer.SHA)
})
}
}
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "restoring data")
}
return nil
}
func (r *Restorer) restoresLayerMetadata() bool {
return api.MustParse(r.Platform.API()).AtLeast("0.7")
}
func (r *Restorer) restoreLayer(cache Cache, sha string) error {
// Sanity check to prevent panic.
if cache == nil {
return errors.New("restoring layer: cache not provided")
}
r.Logger.Debugf("Retrieving data for %q", sha)
rc, err := cache.RetrieveLayer(sha)
if err != nil {
return err
}
defer rc.Close()
return layers.Extract(rc, "")
}