forked from buildpacks/lifecycle
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrestorer.go
139 lines (120 loc) · 4.4 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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
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/internal/layer"
"github.com/buildpacks/lifecycle/layers"
"github.com/buildpacks/lifecycle/log"
"github.com/buildpacks/lifecycle/platform/files"
)
type Restorer struct {
LayersDir string
Logger log.Logger
Buildpacks []buildpack.GroupElement
LayerMetadataRestorer layer.MetadataRestorer // Platform API >= 0.7
LayersMetadata files.LayersMetadata // Platform API >= 0.7
PlatformAPI *api.Version
SBOMRestorer layer.SBOMRestorer
}
// 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 {
defer log.NewMeasurement("Restorer", r.Logger)()
cacheMeta, err := retrieveCacheMetadata(cache, r.Logger)
if err != nil {
return err
}
useShaFiles := !r.restoresLayerMetadata()
layerSHAStore := layer.NewSHAStore(useShaFiles)
if r.restoresLayerMetadata() {
r.Logger.Debug("Restoring Layer Metadata")
if err := r.LayerMetadataRestorer.Restore(r.Buildpacks, r.LayersMetadata, cacheMeta, layerSHAStore); err != nil {
return err
}
}
var g errgroup.Group
for _, bp := range r.Buildpacks {
cachedLayers := cacheMeta.MetadataForBuildpack(bp.ID).Layers
var cachedFn func(buildpack.Layer) bool
if api.MustParse(bp.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 buildpack.Layer) bool {
bpLayer, ok := cachedLayers[filepath.Base(l.Path())]
return ok && bpLayer.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 clean up <layer>.toml files for layers that are not actually in the cache.
cachedFn = buildpack.MadeCached
}
r.Logger.Debugf("Reading Buildpack Layers directory %s", r.LayersDir)
buildpackDir, err := buildpack.ReadLayersDir(r.LayersDir, bp, 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(bp.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.restoreCacheLayer(cache, cachedLayer.SHA)
})
}
}
}
if r.PlatformAPI.AtLeast("0.8") {
g.Go(func() error {
if cacheMeta.BOM.SHA != "" {
r.Logger.Infof("Restoring data for SBOM from cache")
if err := r.SBOMRestorer.RestoreFromCache(cache, cacheMeta.BOM.SHA); err != nil {
return err
}
}
return r.SBOMRestorer.RestoreToBuildpackLayers(r.Buildpacks)
})
}
if err := g.Wait(); err != nil {
return errors.Wrap(err, "restoring data")
}
return nil
}
func (r *Restorer) restoresLayerMetadata() bool {
return r.PlatformAPI.AtLeast("0.7")
}
func (r *Restorer) restoreCacheLayer(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, "")
}