diff --git a/attest/manifest/images.go b/attest/manifest/images.go index df0e9d54..0517241a 100644 --- a/attest/manifest/images.go +++ b/attest/manifest/images.go @@ -73,12 +73,7 @@ func MakeResovedImageRefStatements(images *manifestTypes.ImageList) attestTypes. func forEachImage(images *manifestTypes.ImageList, do func(attestTypes.Subject, ImageRefenceWithLocation)) { for _, image := range images.Items() { - // TODO: drop image.Source and always use image.Sources instead - sources := image.Sources - if sources == nil && image.Source != nil { - sources = []manifestTypes.Source{*image.Source} - } - for _, source := range sources { + for _, source := range image.Sources { do( attestTypes.Subject{ Name: source.Manifest, diff --git a/manifest/imageresolver/imageresolver.go b/manifest/imageresolver/imageresolver.go index 9f0b77af..2499b02b 100644 --- a/manifest/imageresolver/imageresolver.go +++ b/manifest/imageresolver/imageresolver.go @@ -70,11 +70,10 @@ func (c *RegistryResolver) FindRelatedTags(ctx context.Context, images *types.Im return nil, fmt.Errorf("related image %s has no digest", relatedImage.URL) } name, tag, _ := kimage.Split(relatedImage.URL) - err := result.AppendWithRelationTo(image, types.Image{ - Source: &types.Source{ + Sources: []types.Source{{ OriginalRef: relatedImage.URL, - }, + }}, OriginalName: name, OriginalTag: tag, Digest: relatedImage.Digest, @@ -107,9 +106,9 @@ func (c *RegistryResolver) FindRelatedFromIndecies(ctx context.Context, images * } } err := manifests.AppendWithRelationTo(image, types.Image{ - Source: &types.Source{ + Sources: []types.Source{{ OriginalRef: image.OriginalName, - }, + }}, OriginalName: image.OriginalName, Digest: manifest.Digest.String(), }) diff --git a/manifest/imageresolver/imageresolver_test.go b/manifest/imageresolver/imageresolver_test.go index 75f3e76f..9da134aa 100644 --- a/manifest/imageresolver/imageresolver_test.go +++ b/manifest/imageresolver/imageresolver_test.go @@ -69,7 +69,6 @@ func makeImageResolverTest(tc testdata.TestCase) func(t *testing.T) { } } for _, image := range images.Items() { - g.Expect(image.Source).To(BeNil()) g.Expect(image.Sources).ToNot(BeEmpty()) } } diff --git a/manifest/imagescanner/imagescanner.go b/manifest/imagescanner/imagescanner.go index 61c715ed..2a0c7db6 100644 --- a/manifest/imagescanner/imagescanner.go +++ b/manifest/imagescanner/imagescanner.go @@ -86,7 +86,7 @@ func (s *DefaultImageScanner) GetImages() *types.ImageList { for _, vv := range v.SetValueArgs() { name, tag, digest := kimage.Split(vv.Value) images.Append(types.Image{ - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: v.Manifest, ManifestDigest: v.ManifestDigest, @@ -95,7 +95,7 @@ func (s *DefaultImageScanner) GetImages() *types.ImageList { Column: vv.Column, }, OriginalRef: vv.Value, - }, + }}, OriginalName: name, OriginalTag: tag, Digest: digest, diff --git a/manifest/imagescanner/imagescanner_test.go b/manifest/imagescanner/imagescanner_test.go index 714d94c2..2b57542c 100644 --- a/manifest/imagescanner/imagescanner_test.go +++ b/manifest/imagescanner/imagescanner_test.go @@ -38,8 +38,7 @@ func makeImageScannerTest(tc testdata.TestCase) func(t *testing.T) { images := scanner.GetImages() for _, image := range images.Items() { - g.Expect(image.Source).ToNot(BeNil()) - g.Expect(image.Sources).To(BeEmpty()) + g.Expect(image.Sources).To(HaveLen(1)) } if tc.Expected != nil { diff --git a/manifest/testdata/testdata.go b/manifest/testdata/testdata.go index 61eacb60..25ff89ff 100644 --- a/manifest/testdata/testdata.go +++ b/manifest/testdata/testdata.go @@ -40,7 +40,7 @@ func BasicJSONCases() TestCases { }, Expected: []types.Image{ { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "list.json", ManifestDigest: "577caeee80cfa690caf25bcdd4b1919b99d2860eb351c48e81b46b9e4b52aea5", @@ -49,11 +49,11 @@ func BasicJSONCases() TestCases { Column: 34, }, OriginalRef: "nginx", - }, + }}, OriginalName: "nginx", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "list.json", ManifestDigest: "577caeee80cfa690caf25bcdd4b1919b99d2860eb351c48e81b46b9e4b52aea5", @@ -62,11 +62,11 @@ func BasicJSONCases() TestCases { Column: 42, }, OriginalRef: "redis", - }, + }}, OriginalName: "redis", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "list.json", ManifestDigest: "577caeee80cfa690caf25bcdd4b1919b99d2860eb351c48e81b46b9e4b52aea5", @@ -75,11 +75,11 @@ func BasicJSONCases() TestCases { Column: 42, }, OriginalRef: "redis", - }, + }}, OriginalName: "redis", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "deployment.json", ManifestDigest: "8d85ce5a5de4085bb841cee0402022fcd03f86606a67d572e62012ce4420668c", @@ -88,7 +88,7 @@ func BasicJSONCases() TestCases { Column: 16, }, OriginalRef: "nginx:1.16.1", - }, + }}, OriginalName: "nginx", OriginalTag: "1.16.1", }, @@ -118,7 +118,7 @@ var baseYAMLCases = []TestCase{ NumRelatedTags: 0, Expected: []types.Image{ { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "02-job-certgen.yaml", ManifestDigest: "ba03dc02890e0ca080f12f03fd06a1d4f6b76ff75be0346ee27c9aa73c6d1d31", @@ -127,12 +127,12 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/projectcontour/contour:v1.24.1", - }, + }}, OriginalName: "ghcr.io/projectcontour/contour", OriginalTag: "v1.24.1", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "03-contour.yaml", ManifestDigest: "a9de49647bab938407cb76c29f6b9465690bedb0b99a10736136f982d349d928", @@ -141,12 +141,12 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/projectcontour/contour:v1.24.1", - }, + }}, OriginalName: "ghcr.io/projectcontour/contour", OriginalTag: "v1.24.1", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "03-envoy.yaml", ManifestDigest: "e83cd3f98ddbbd91374511c8ce1e437d938ffc8ea8d50bc6d4ccdbf224e53ed4", @@ -155,12 +155,12 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/projectcontour/contour:v1.24.1", - }, + }}, OriginalName: "ghcr.io/projectcontour/contour", OriginalTag: "v1.24.1", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "03-envoy.yaml", ManifestDigest: "e83cd3f98ddbbd91374511c8ce1e437d938ffc8ea8d50bc6d4ccdbf224e53ed4", @@ -169,12 +169,12 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "docker.io/envoyproxy/envoy:v1.25.1", - }, + }}, OriginalName: "docker.io/envoyproxy/envoy", OriginalTag: "v1.25.1", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "03-envoy.yaml", ManifestDigest: "e83cd3f98ddbbd91374511c8ce1e437d938ffc8ea8d50bc6d4ccdbf224e53ed4", @@ -183,7 +183,7 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/projectcontour/contour:v1.24.1", - }, + }}, OriginalName: "ghcr.io/projectcontour/contour", OriginalTag: "v1.24.1", }, @@ -199,7 +199,7 @@ var baseYAMLCases = []TestCase{ NumRelatedTags: 2, Expected: []types.Image{ { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "flux.yaml", ManifestDigest: "39ad63101dbb2ead069ca6185bd44f99f52b8513682d6002109c9b0db23f73b5", @@ -208,12 +208,12 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/fluxcd/kustomize-controller:v0.30.0", - }, + }}, OriginalName: "ghcr.io/fluxcd/kustomize-controller", OriginalTag: "v0.30.0", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "flux.yaml", ManifestDigest: "39ad63101dbb2ead069ca6185bd44f99f52b8513682d6002109c9b0db23f73b5", @@ -222,7 +222,7 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "ghcr.io/fluxcd/source-controller:v0.31.0", - }, + }}, OriginalName: "ghcr.io/fluxcd/source-controller", OriginalTag: "v0.31.0", }, @@ -242,7 +242,7 @@ var baseYAMLCases = []TestCase{ NumRelatedTags: 6, Expected: []types.Image{ { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "base/tekton-base.yaml", ManifestDigest: "c2cbc6d7a3c30f99e2e504d5758d8e0ce140a8f444c4d944d85c3f29800bf8c5", @@ -251,13 +251,13 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller:v0.40.2@sha256:dc7bc7d6607466b502d8dc22ba0598461d7477f608ab68aaff1ff4dedaa04f81", - }, + }}, OriginalName: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/controller", OriginalTag: "v0.40.2", Digest: "sha256:dc7bc7d6607466b502d8dc22ba0598461d7477f608ab68aaff1ff4dedaa04f81", }, { - Source: &types.Source{ + Sources: []types.Source{{ ImageSourceLocation: types.ImageSourceLocation{ Manifest: "base/tekton-base.yaml", ManifestDigest: "c2cbc6d7a3c30f99e2e504d5758d8e0ce140a8f444c4d944d85c3f29800bf8c5", @@ -266,7 +266,7 @@ var baseYAMLCases = []TestCase{ Column: 16, }, OriginalRef: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/webhook:v0.40.2@sha256:6b8aadbdcede63969ecb719e910b55b7681d87110fc0bf92ca4ee943042f620b", - }, + }}, OriginalName: "gcr.io/tekton-releases/github.com/tektoncd/pipeline/cmd/webhook", OriginalTag: "v0.40.2", Digest: "sha256:6b8aadbdcede63969ecb719e910b55b7681d87110fc0bf92ca4ee943042f620b", @@ -292,10 +292,10 @@ func BaseYAMLCasesWithDigests(t *testing.T) TestCases { baseCases := make([]TestCase, len(baseYAMLCases)) for i, c := range baseYAMLCases { for e := range c.Expected { - if digest, ok := baseCasesDigests[c.Expected[e].OriginalRef]; ok { + if digest, ok := baseCasesDigests[c.Expected[e].OriginalRef()]; ok { c.Expected[e].Digest = digest } else if c.Expected[e].Digest == "" { - t.Logf("digest not found for %s", c.Expected[e].OriginalRef) + t.Logf("digest not found for %s", c.Expected[e].OriginalRef()) t.FailNow() } } diff --git a/manifest/types/types.go b/manifest/types/types.go index bf4b4f3e..eef0e413 100644 --- a/manifest/types/types.go +++ b/manifest/types/types.go @@ -20,8 +20,6 @@ const ( type ( Image struct { - // TODO: are two fields really needed? it would make sense if it was just the slice - *Source Sources []Source OriginalName string @@ -67,7 +65,7 @@ func NewImageList(dir string) *ImageList { func (l *ImageList) Paths() []string { paths := make([]string, len(l.items)) for i := range l.items { - paths[i] = filepath.Join(l.dir, l.items[i].Manifest) + paths[i] = filepath.Join(l.dir, l.items[i].Manifest()) } return paths } @@ -93,17 +91,6 @@ func (l *ImageList) AppendWithRelationTo(target, image Image) error { return nil } -// func (l *ImageList) IsRelated(key, target string) bool { -// if l.relationEntries == nil { -// return false -// } -// v, ok := l.relationEntries[key] -// if !ok { -// return false -// } -// return v == target -// } - func (l *ImageList) RelatedTo(ref string) []string { results := []string{} for k, v := range l.relationEntries { @@ -154,12 +141,13 @@ func (l *ImageList) Dedup() error { l.dedupeLock.Do(func() { unique := map[key]Image{} for _, image := range l.items { - sources := []Source{*image.Source} - existing, present := unique[key{image.OriginalRef, image.Digest}] + sources := slices.Clone(image.Sources) + k := key{image.OriginalRef(), image.Digest} + existing, present := unique[k] if present { sources = append(sources, existing.Sources...) } - unique[key{image.OriginalRef, image.Digest}] = Image{ + unique[k] = Image{ Sources: sources, OriginalTag: image.OriginalTag, OriginalName: image.OriginalName, @@ -173,17 +161,21 @@ func (l *ImageList) Dedup() error { for _, v := range unique { if len(v.Sources) > 1 { slices.SortFunc(v.Sources, func(a, b Source) int { - if namewise := cmp.Compare(a.Manifest, b.Manifest); namewise != 0 { - return namewise + if cmp := cmp.Compare(a.Manifest, b.Manifest); cmp != 0 { + return cmp + } + if cmp := cmp.Compare(a.Line, b.Line); cmp != 0 { + return cmp } - if linewise := cmp.Compare(a.Line, b.Line); linewise != 0 { - return linewise + if cmp := cmp.Compare(a.Column, b.Column); cmp != 0 { + return cmp } - return cmp.Compare(a.Column, b.Column) + return 0 }) } l.items = append(l.items, v) } + }) return nil } @@ -210,20 +202,15 @@ func (l *ImageList) GroupByManifest() map[string]*ImageList { } for i := range l.items { item := l.items[i] - if item.Source != nil { - addItem(item.Source.Manifest, item) - } - if len(item.Sources) > 0 { - for _, source := range item.Sources { - addItem(source.Manifest, Image{ - Sources: []Source{source}, - OriginalName: item.OriginalName, - OriginalTag: item.OriginalTag, - Digest: item.Digest, - NewName: item.NewName, - NewTag: item.NewTag, - }) - } + for _, source := range item.Sources { + addItem(source.Manifest, Image{ + Sources: []Source{source}, + OriginalName: item.OriginalName, + OriginalTag: item.OriginalTag, + Digest: item.Digest, + NewName: item.NewName, + NewTag: item.NewTag, + }) } } return groups @@ -248,6 +235,17 @@ func (i Image) Ref(original bool) string { return ref } +func (i Image) primarySource() *Source { + if len(i.Sources) == 0 { + panic("unextected empty image sources") + } + return &i.Sources[0] +} + +func (i Image) Manifest() string { return i.primarySource().Manifest } +func (i Image) ManifestDigest() digest.SHA256 { return i.primarySource().ManifestDigest } +func (i Image) OriginalRef() string { return i.primarySource().OriginalRef } + func ImagePaths() []kustomize.FieldSpec { return []kustomize.FieldSpec{ {Path: "spec/containers[]/image"}, diff --git a/manifest/updater/updater_test.go b/manifest/updater/updater_test.go index 269f3934..f9fdaffe 100644 --- a/manifest/updater/updater_test.go +++ b/manifest/updater/updater_test.go @@ -105,7 +105,7 @@ func makeUpdaterTest(tc testdata.TestCase) func(t *testing.T) { if expectedImage.Digest == image.Digest && expectedImage.NewName == image.OriginalName && expectedImage.NewTag == image.OriginalTag { - g.Expect(image.ManifestDigest).ToNot(Equal(expectedImage.ManifestDigest)) + g.Expect(image.ManifestDigest()).ToNot(Equal(expectedImage.ManifestDigest())) matched.Append(image) } }