Skip to content

Commit 28fd0a8

Browse files
committed
Fix unexpected results when filtering images
The filtered image contains all Names of image. This causes the podman to list images that are not expected. Fixes: podman-container-tools/podman#25725 Fixes: https://issues.redhat.com/browse/RUN-2726 Signed-off-by: Jan Rodák <hony.com@seznam.cz>
1 parent 7754dd5 commit 28fd0a8

4 files changed

Lines changed: 52 additions & 11 deletions

File tree

libimage/filters.go

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ type compiledFilters map[string][]filterFunc
2525

2626
// Apply the specified filters. All filters of each key must apply.
2727
// tree must be provided if compileImageFilters indicated it is necessary.
28+
// WARNING: Application of referenceFilter sets the image names to matched names, but this only affects the values in memory, they are not written to storage.
2829
func (i *Image) applyFilters(ctx context.Context, filters compiledFilters, tree *layerTree) (bool, error) {
2930
for key := range filters {
3031
for _, filter := range filters[key] {
@@ -51,6 +52,7 @@ func (i *Image) applyFilters(ctx context.Context, filters compiledFilters, tree
5152
// filterImages returns a slice of images which are passing all specified
5253
// filters.
5354
// tree must be provided if compileImageFilters indicated it is necessary.
55+
// WARNING: Application of referenceFilter sets the image names to matched names, but this only affects the values in memory, they are not written to storage.
5456
func (r *Runtime) filterImages(ctx context.Context, images []*Image, filters compiledFilters, tree *layerTree) ([]*Image, error) {
5557
result := []*Image{}
5658
for i := range images {
@@ -290,15 +292,31 @@ func filterReferences(r *Runtime, wantedReferenceMatches, unwantedReferenceMatch
290292
return !unwantedMatched, nil
291293
}
292294

293-
// Go through the wanted matches
294-
// If an image matches the wanted filter but it also matches the unwanted
295-
// filter, don't add it to the output
296295
for _, value := range wantedReferenceMatches {
297296
matches, err := imageMatchesReferenceFilter(r, img, value)
298297
if err != nil {
299298
return false, err
300299
}
301300
if matches && !unwantedMatched {
301+
ref, err := reference.ParseNamed(value)
302+
if err == nil {
303+
if !reference.IsNameOnly(ref) {
304+
namesThatMatch := []string{}
305+
_, containsDigest := ref.(reference.Digested)
306+
for _, name := range img.Names() {
307+
if containsDigest {
308+
if nameRef, err := reference.ParseNamed(name); err == nil {
309+
if nameRef.Name() == ref.Name() {
310+
namesThatMatch = append(namesThatMatch, name)
311+
}
312+
}
313+
} else if name == value {
314+
namesThatMatch = append(namesThatMatch, name)
315+
}
316+
}
317+
img.setEphemeralNames(namesThatMatch)
318+
}
319+
}
302320
return true, nil
303321
}
304322
}
@@ -352,7 +370,6 @@ func imageMatchesReferenceFilter(r *Runtime, img *Image, value string) (bool, er
352370
}
353371
}
354372
}
355-
356373
return false, nil
357374
}
358375

libimage/filters_test.go

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ func TestFilterReference(t *testing.T) {
4242
require.NoError(t, err)
4343
err = alpine.Tag("docker.io/library/image:latest")
4444
require.NoError(t, err)
45+
err = busybox.Tag("localhost/aa:tag")
46+
require.NoError(t, err)
47+
err = busybox.Tag("localhost/a:tag")
48+
require.NoError(t, err)
4549

4650
allAlpineNames := []string{
4751
"docker.io/library/image:another-tag",
@@ -52,6 +56,8 @@ func TestFilterReference(t *testing.T) {
5256
allBusyBoxNames := []string{
5357
"quay.io/libpod/busybox:latest",
5458
"localhost/image:tag",
59+
"localhost/a:tag",
60+
"localhost/aa:tag",
5561
}
5662
allNames := []string{}
5763
allNames = append(allNames, allBusyBoxNames...)
@@ -68,11 +74,11 @@ func TestFilterReference(t *testing.T) {
6874
{[]string{"image:tag"}, 1, allBusyBoxNames},
6975
{[]string{"image:another-tag"}, 1, allAlpineNames},
7076
{[]string{"localhost/image"}, 1, allBusyBoxNames},
71-
{[]string{"localhost/image:tag"}, 1, allBusyBoxNames},
77+
{[]string{"localhost/image:tag"}, 1, []string{"localhost/image:tag"}},
7278
{[]string{"library/image"}, 1, allAlpineNames},
7379
{[]string{"docker.io/library/image*"}, 1, allAlpineNames},
7480
{[]string{"docker.io/library/image:*"}, 1, allAlpineNames},
75-
{[]string{"docker.io/library/image:another-tag"}, 1, allAlpineNames},
81+
{[]string{"docker.io/library/image:another-tag"}, 1, []string{"docker.io/library/image:another-tag"}},
7682
{[]string{"localhost/*"}, 2, allNames},
7783
{[]string{"localhost/image:*tag"}, 1, allBusyBoxNames},
7884
{[]string{"localhost/*mage:*ag"}, 2, allNames},
@@ -90,18 +96,20 @@ func TestFilterReference(t *testing.T) {
9096
{[]string{"alpine", "busybox"}, 2, allNames},
9197
{[]string{"*test", "!*box"}, 1, allAlpineNames},
9298

93-
{[]string{"quay.io/libpod/alpine@" + alpine.Digest().String()}, 1, allAlpineNames},
99+
{[]string{"quay.io/libpod/alpine@" + alpine.Digest().String()}, 1, []string{"quay.io/libpod/alpine:latest"}},
94100

95101
{[]string{"alpine@" + alpine.Digest().String()}, 1, allAlpineNames},
96102
{[]string{"alpine:latest@" + alpine.Digest().String()}, 1, allAlpineNames},
97-
{[]string{"quay.io/libpod/alpine:latest@" + alpine.Digest().String()}, 1, allAlpineNames},
98-
{[]string{"docker.io/library/image@" + alpine.Digest().String()}, 1, allAlpineNames},
103+
{[]string{"quay.io/libpod/alpine:latest@" + alpine.Digest().String()}, 1, []string{"quay.io/libpod/alpine:latest"}},
104+
{[]string{"docker.io/library/image@" + alpine.Digest().String()}, 1, []string{"docker.io/library/image:latest", "docker.io/library/image:another-tag"}},
105+
{[]string{"localhost/aa@" + busybox.Digest().String()}, 1, []string{"localhost/aa:tag"}},
106+
{[]string{"localhost/a@" + busybox.Digest().String()}, 1, []string{"localhost/a:tag"}},
99107

100108
// Make sure that tags are ignored
101109
{[]string{"alpine:ignoreme@" + alpine.Digest().String()}, 1, allAlpineNames},
102110
{[]string{"alpine:123@" + alpine.Digest().String()}, 1, allAlpineNames},
103-
{[]string{"quay.io/libpod/alpine:hurz@" + alpine.Digest().String()}, 1, allAlpineNames},
104-
{[]string{"quay.io/libpod/alpine:456@" + alpine.Digest().String()}, 1, allAlpineNames},
111+
{[]string{"quay.io/libpod/alpine:hurz@" + alpine.Digest().String()}, 1, []string{"quay.io/libpod/alpine:latest"}},
112+
{[]string{"quay.io/libpod/alpine:456@" + alpine.Digest().String()}, 1, []string{"quay.io/libpod/alpine:latest"}},
105113

106114
// Make sure that repo and digest must match
107115
{[]string{"alpine:busyboxdigest@" + busybox.Digest().String()}, 0, []string{}},

libimage/image.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ func (i *Image) Names() []string {
112112
return i.storageImage.Names
113113
}
114114

115+
// setEphemeralNames sets the names of the image.
116+
//
117+
// WARNING: this only affects the in-memory values, they are not written into the backing storage.
118+
func (i *Image) setEphemeralNames(names []string) {
119+
i.storageImage.Names = names
120+
}
121+
115122
// NamesReferences returns Names() as references.
116123
func (i *Image) NamesReferences() ([]reference.Reference, error) {
117124
if i.cached.namesReferences != nil {

libimage/runtime.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,15 @@ func (r *Runtime) ListImagesByNames(names []string) ([]*Image, error) {
599599
}
600600

601601
// ListImages lists the images in the local container storage and filter the images by ListImagesOptions
602+
//
603+
// podman images consumes the output of ListImages and produces one line for each tag in each Image.Names value,
604+
// rather than one line for each Image with all Names, so if options.Filters contains a reference filter, it makes
605+
// more sense for the user to see only the corresponding names in the output, not all the names of the deduplicated
606+
// image; therefore, we make the corresponding names available to the caller by overwriting the actual image names
607+
// with the corresponding names when the reference filter matches and the reference is a fully qualified image name
608+
// (i.e., contains a tag or digest, not just a bare repository name).
609+
//
610+
// This overwriting is done only in memory and is not written to storage in any way.
602611
func (r *Runtime) ListImages(ctx context.Context, options *ListImagesOptions) ([]*Image, error) {
603612
if options == nil {
604613
options = &ListImagesOptions{}

0 commit comments

Comments
 (0)