-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
image.go
137 lines (119 loc) · 3.84 KB
/
image.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
package image
import (
"context"
"fmt"
"strings"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
multierror "github.com/hashicorp/go-multierror"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
)
type imageSourceFunc func(ctx context.Context, imageName string, ref name.Reference, option types.ImageOptions) (types.Image, func(), error)
var imageSourceFuncs = map[types.ImageSource]imageSourceFunc{
types.ContainerdImageSource: tryContainerdDaemon,
types.PodmanImageSource: tryPodmanDaemon,
types.DockerImageSource: tryDockerDaemon,
types.RemoteImageSource: tryRemote,
}
func NewContainerImage(ctx context.Context, imageName string, opt types.ImageOptions) (types.Image, func(), error) {
if len(opt.ImageSources) == 0 {
return nil, func() {}, xerrors.New("no image sources supplied")
}
var errs error
var nameOpts []name.Option
if opt.RegistryOptions.Insecure {
nameOpts = append(nameOpts, name.Insecure)
}
ref, err := name.ParseReference(imageName, nameOpts...)
if err != nil {
return nil, func() {}, xerrors.Errorf("failed to parse the image name: %w", err)
}
for _, src := range opt.ImageSources {
trySrc, ok := imageSourceFuncs[src]
if !ok {
log.Warn("Unknown image source", log.String("source", string(src)))
continue
}
img, cleanup, err := trySrc(ctx, imageName, ref, opt)
if err == nil {
// Return v1.Image if the image is found
return img, cleanup, nil
}
err = multierror.Prefix(err, fmt.Sprintf("%s error:", src))
errs = multierror.Append(errs, err)
}
return nil, func() {}, xerrors.Errorf("unable to find the specified image %q in %q: %w", imageName, opt.ImageSources, errs)
}
func ID(img v1.Image) (string, error) {
h, err := img.ConfigName()
if err != nil {
return "", xerrors.Errorf("unable to get the image ID: %w", err)
}
return h.String(), nil
}
func LayerIDs(img v1.Image) ([]string, error) {
conf, err := img.ConfigFile()
if err != nil {
return nil, xerrors.Errorf("unable to get the config file: %w", err)
}
var layerIDs []string
for _, d := range conf.RootFS.DiffIDs {
layerIDs = append(layerIDs, d.String())
}
return layerIDs, nil
}
// GuessBaseImageIndex tries to guess index of base layer
//
// e.g. In the following example, we should detect layers in debian:8.
//
// FROM debian:8
// RUN apt-get update
// COPY mysecret /
// ENTRYPOINT ["entrypoint.sh"]
// CMD ["somecmd"]
//
// debian:8 may be like
//
// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in /
// CMD ["/bin/sh"]
//
// In total, it would be like:
//
// ADD file:5d673d25da3a14ce1f6cf66e4c7fd4f4b85a3759a9d93efb3fd9ff852b5b56e4 in /
// CMD ["/bin/sh"] # empty layer (detected)
// RUN apt-get update
// COPY mysecret /
// ENTRYPOINT ["entrypoint.sh"] # empty layer (skipped)
// CMD ["somecmd"] # empty layer (skipped)
//
// This method tries to detect CMD in the second line and assume the first line is a base layer.
// 1. Iterate histories from the bottom.
// 2. Skip all the empty layers at the bottom. In the above example, "entrypoint.sh" and "somecmd" will be skipped
// 3. If it finds CMD, it assumes that it is the end of base layers.
// 4. It gets all the layers as base layers above the CMD found in #3.
func GuessBaseImageIndex(histories []v1.History) int {
baseImageIndex := -1
var foundNonEmpty bool
for i := len(histories) - 1; i >= 0; i-- {
h := histories[i]
// Skip the last CMD, ENTRYPOINT, etc.
if !foundNonEmpty {
if h.EmptyLayer {
continue
}
foundNonEmpty = true
}
if !h.EmptyLayer {
continue
}
// Detect CMD instruction in base image
if strings.HasPrefix(h.CreatedBy, "/bin/sh -c #(nop) CMD") ||
strings.HasPrefix(h.CreatedBy, "CMD") { // BuildKit
baseImageIndex = i
break
}
}
return baseImageIndex
}