fix(remote): keep '=' in a prune filter value

ImageEngine.Prune split each --filter on every '=' and kept everything after the
first as separate values, so a filter whose value itself contains '=' was torn
apart. podman-image-prune.1.md documents label=key=value and gives
'--filter label=version=1.0' as an example, which produced

  filters["label"] = ["version", "1.0"]

instead of ["version=1.0"]. libimage turns every element into its own label
filter and MatchLabelFilters cuts each one on '=' itself, so the request became
'has a label key version' AND 'has a label key 1.0'. Nothing carries a key named
1.0, so the prune matched no images and silently removed nothing.

Local podman is unaffected: the abi path passes opts.Filter through untouched.
This is remote-only, and it diverges from both siblings -- List uses
SplitN(filter, "=", 2) and Events validates then rejoins with strings.Join.
Prune now mirrors List.

The existing e2e case only used 'label=abc', a bare key, which survives the old
split by accident; added one that uses a key=value label and checks the
non-matching image is still there afterwards.

Signed-off-by: Atishyy27 <sethatishayjain@gmail.com>
This commit is contained in:
Atishyy27 2026-08-20 21:27:24 +05:30
parent a1c4f9c0e6
commit 648f91e9c6
No known key found for this signature in database
2 changed files with 31 additions and 2 deletions

View file

@ -101,8 +101,12 @@ func (ir *ImageEngine) History(_ context.Context, nameOrID string, _ entities.Im
func (ir *ImageEngine) Prune(_ context.Context, opts entities.ImagePruneOptions) ([]*reports.PruneReport, error) {
filters := make(map[string][]string, len(opts.Filter))
for _, filter := range opts.Filter {
f := strings.Split(filter, "=")
filters[f[0]] = f[1:]
f := strings.SplitN(filter, "=", 2)
if len(f) > 1 {
filters[f[0]] = append(filters[f[0]], f[1])
} else {
filters[f[0]] = append(filters[f[0]], "")
}
}
options := new(images.PruneOptions).WithAll(opts.All).WithFilters(filters).WithExternal(opts.External).WithBuildCache(opts.BuildCache)
reports, err := images.Prune(ir.ClientCtx, options)

View file

@ -494,6 +494,31 @@ RUN > file2
Expect(result.OutputToStringArray()).To(BeEmpty())
})
It("podman image prune --filter label=key=value", func() {
dockerfile := `FROM quay.io/libpod/alpine:latest
RUN > file
`
dockerfile2 := `FROM quay.io/libpod/alpine:latest
RUN > file2
`
podmanTest.BuildImageWithLabel(dockerfile, "foobar.com/workdir:latest", "false", "version=1.0")
podmanTest.BuildImageWithLabel(dockerfile2, "foobar.com/workdir:latest", "false", "version=2.0")
result := podmanTest.Podman([]string{"image", "prune", "--filter", "label=version=1.0", "--force"})
result.WaitWithDefaultTimeout()
Expect(result).Should(ExitCleanly())
Expect(result.OutputToStringArray()).To(HaveLen(1))
// only version=1.0 should be gone; version=2.0 must survive
result = podmanTest.Podman([]string{"image", "list", "--filter", "label=version=1.0"})
Expect(result.OutputToStringArray()).To(BeEmpty())
result = podmanTest.Podman([]string{"image", "list", "--filter", "label=version=2.0", "-q"})
result.WaitWithDefaultTimeout()
Expect(result).Should(ExitCleanly())
Expect(result.OutputToStringArray()).To(HaveLen(1))
})
It("podman builder prune", func() {
dockerfile := `FROM quay.io/libpod/alpine:latest
RUN > file