-
Notifications
You must be signed in to change notification settings - Fork 106
/
analyzer.go
148 lines (133 loc) · 4.5 KB
/
analyzer.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
140
141
142
143
144
145
146
147
148
package phase
import (
"github.com/buildpacks/imgutil"
"github.com/pkg/errors"
"github.com/buildpacks/lifecycle/api"
"github.com/buildpacks/lifecycle/image"
"github.com/buildpacks/lifecycle/internal/fsutil"
"github.com/buildpacks/lifecycle/internal/layer"
"github.com/buildpacks/lifecycle/log"
"github.com/buildpacks/lifecycle/platform"
"github.com/buildpacks/lifecycle/platform/files"
)
// Analyzer reads metadata from the previous image (if it exists) and the run image,
// and additionally restores the SBOM layer from the previous image for use later in the build.
type Analyzer struct {
PreviousImage imgutil.Image
RunImage imgutil.Image
Logger log.Logger
SBOMRestorer layer.SBOMRestorer
PlatformAPI *api.Version
}
// NewAnalyzer configures a new Analyzer according to the provided Platform API version.
func (f *ConnectedFactory) NewAnalyzer(inputs platform.LifecycleInputs, logger log.Logger) (*Analyzer, error) {
analyzer := &Analyzer{
Logger: logger,
SBOMRestorer: &layer.NopSBOMRestorer{},
PlatformAPI: f.platformAPI,
}
if err := f.ensureRegistryAccess(inputs); err != nil {
return nil, err
}
if f.platformAPI.AtLeast("0.8") && !inputs.SkipLayers {
analyzer.SBOMRestorer = &layer.DefaultSBOMRestorer{
LayersDir: inputs.LayersDir,
Logger: logger,
}
}
var err error
if analyzer.PreviousImage, err = f.getPreviousImage(inputs.PreviousImageRef, inputs.LaunchCacheDir); err != nil {
return nil, err
}
if analyzer.RunImage, err = f.getRunImage(inputs.RunImageRef); err != nil {
return nil, err
}
return analyzer, nil
}
// Analyze fetches the layers metadata from the previous image and writes analyzed.toml.
func (a *Analyzer) Analyze() (files.Analyzed, error) {
defer log.NewMeasurement("Analyzer", a.Logger)()
var (
err error
appMeta files.LayersMetadata
previousImageRef string
runImageRef string
)
appMeta, previousImageRef, err = a.retrieveAppMetadata()
if err != nil {
return files.Analyzed{}, err
}
if sha := bomSHA(appMeta); sha != "" {
if err = a.SBOMRestorer.RestoreFromPrevious(a.PreviousImage, sha); err != nil {
return files.Analyzed{}, errors.Wrap(err, "retrieving launch SBOM layer")
}
}
var (
atm *files.TargetMetadata
runImageName string
)
if a.RunImage != nil {
runImageRef, err = a.getImageIdentifier(a.RunImage)
if err != nil {
return files.Analyzed{}, errors.Wrap(err, "identifying run image")
}
if a.PlatformAPI.AtLeast("0.12") {
runImageName = a.RunImage.Name()
atm, err = platform.GetTargetMetadata(a.RunImage)
if err != nil {
return files.Analyzed{}, errors.Wrap(err, "unpacking metadata from image")
}
if atm.OS == "" {
platform.GetTargetOSFromFileSystem(&fsutil.Detect{}, atm, a.Logger)
}
}
}
return files.Analyzed{
PreviousImage: &files.ImageIdentifier{
Reference: previousImageRef,
},
RunImage: &files.RunImage{
Reference: runImageRef, // the image identifier, e.g. "s0m3d1g3st" (the image identifier) when exporting to a daemon, or "some.registry/some-repo@sha256:s0m3d1g3st" when exporting to a registry
TargetMetadata: atm,
Image: runImageName, // the provided tag, e.g., "some.registry/some-repo:some-tag" if supported by the platform
},
LayersMetadata: appMeta,
}, nil
}
func (a *Analyzer) getImageIdentifier(image imgutil.Image) (string, error) {
if !image.Found() {
a.Logger.Infof("Image with name %q not found", image.Name())
return "", nil
}
identifier, err := image.Identifier()
if err != nil {
return "", err
}
a.Logger.Debugf("Found image with identifier %q", identifier.String())
return identifier.String(), nil
}
func bomSHA(appMeta files.LayersMetadata) string {
if appMeta.BOM == nil {
return ""
}
return appMeta.BOM.SHA
}
func (a *Analyzer) retrieveAppMetadata() (files.LayersMetadata, string, error) {
if a.PreviousImage == nil {
return files.LayersMetadata{}, "", nil
}
previousImageRef, err := a.getImageIdentifier(a.PreviousImage)
if err != nil {
return files.LayersMetadata{}, "", errors.Wrap(err, "identifying previous image")
}
if a.PreviousImage.Found() && !a.PreviousImage.Valid() {
a.Logger.Infof("Ignoring image %q because it was corrupt", a.PreviousImage.Name())
return files.LayersMetadata{}, "", nil
}
var appMeta files.LayersMetadata
// continue even if the label cannot be decoded
if err = image.DecodeLabel(a.PreviousImage, platform.LifecycleMetadataLabel, &appMeta); err != nil {
return files.LayersMetadata{}, "", nil
}
return appMeta, previousImageRef, nil
}