Skip to content

Commit a9649e7

Browse files
authored
Merge pull request #49982 from vvoland/c8d-image-remove-platform
c8d/delete: Add support for deleting specific platforms
2 parents d0ad135 + 072483f commit a9649e7

File tree

18 files changed

+365
-139
lines changed

18 files changed

+365
-139
lines changed

api/server/httputils/form.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,3 +162,22 @@ func DecodePlatform(platformJSON string) (*ocispec.Platform, error) {
162162

163163
return &p, nil
164164
}
165+
166+
// DecodePlatforms decodes the OCI platform JSON string into a Platform struct.
167+
//
168+
// Typically, the argument is a value of: r.Form["platform"]
169+
func DecodePlatforms(platformJSONs []string) ([]ocispec.Platform, error) {
170+
if len(platformJSONs) == 0 {
171+
return nil, nil
172+
}
173+
174+
var output []ocispec.Platform
175+
for _, platform := range platformJSONs {
176+
p, err := DecodePlatform(platform)
177+
if err != nil {
178+
return nil, err
179+
}
180+
output = append(output, *p)
181+
}
182+
return output, nil
183+
}

api/server/router/image/backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ type Backend interface {
2222
}
2323

2424
type imageBackend interface {
25-
ImageDelete(ctx context.Context, imageRef string, force, prune bool) ([]image.DeleteResponse, error)
25+
ImageDelete(ctx context.Context, imageRef string, options image.RemoveOptions) ([]image.DeleteResponse, error)
2626
ImageHistory(ctx context.Context, imageName string, platform *ocispec.Platform) ([]*image.HistoryResponseItem, error)
2727
Images(ctx context.Context, opts image.ListOptions) ([]*image.Summary, error)
2828
GetImage(ctx context.Context, refOrID string, options backend.GetImageOpts) (*dockerimage.Image, error)

api/server/router/image/image_routes.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,20 @@ func (ir *imageRouter) deleteImages(ctx context.Context, w http.ResponseWriter,
326326
force := httputils.BoolValue(r, "force")
327327
prune := !httputils.BoolValue(r, "noprune")
328328

329-
list, err := ir.backend.ImageDelete(ctx, name, force, prune)
329+
var platforms []ocispec.Platform
330+
if versions.GreaterThanOrEqualTo(httputils.VersionFromContext(ctx), "1.50") {
331+
p, err := httputils.DecodePlatforms(r.Form["platforms"])
332+
if err != nil {
333+
return err
334+
}
335+
platforms = p
336+
}
337+
338+
list, err := ir.backend.ImageDelete(ctx, name, imagetypes.RemoveOptions{
339+
Force: force,
340+
PruneChildren: prune,
341+
Platforms: platforms,
342+
})
330343
if err != nil {
331344
return err
332345
}

api/swagger.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9818,6 +9818,18 @@ paths:
98189818
description: "Do not delete untagged parent images"
98199819
type: "boolean"
98209820
default: false
9821+
- name: "platforms"
9822+
in: "query"
9823+
description: |
9824+
Select platform-specific content to delete.
9825+
Multiple values are accepted.
9826+
Each platform is a OCI platform encoded as a JSON string.
9827+
type: "array"
9828+
items:
9829+
# This should be OCIPlatform
9830+
# but $ref is not supported for array in query in Swagger 2.0
9831+
# $ref: "#/definitions/OCIPlatform"
9832+
type: "string"
98219833
tags: ["Image"]
98229834
/images/search:
98239835
get:

api/types/image/opts.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ type ListOptions struct {
8383

8484
// RemoveOptions holds parameters to remove images.
8585
type RemoveOptions struct {
86+
Platforms []ocispec.Platform
8687
Force bool
8788
PruneChildren bool
8889
}

client/image_remove.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@ func (cli *Client) ImageRemove(ctx context.Context, imageID string, options imag
1919
query.Set("noprune", "1")
2020
}
2121

22+
if len(options.Platforms) > 0 {
23+
p, err := encodePlatforms(options.Platforms...)
24+
if err != nil {
25+
return nil, err
26+
}
27+
query["platforms"] = p
28+
}
29+
2230
resp, err := cli.delete(ctx, "/images/"+imageID, query, nil)
2331
defer ensureReaderClosed(resp)
2432
if err != nil {

client/image_remove_test.go

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212

1313
cerrdefs "github.com/containerd/errdefs"
1414
"github.com/docker/docker/api/types/image"
15+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
1516
"gotest.tools/v3/assert"
1617
is "gotest.tools/v3/assert/cmp"
1718
)
@@ -40,6 +41,7 @@ func TestImageRemove(t *testing.T) {
4041
removeCases := []struct {
4142
force bool
4243
pruneChildren bool
44+
platform *ocispec.Platform
4345
expectedQueryParams map[string]string
4446
}{
4547
{
@@ -49,14 +51,24 @@ func TestImageRemove(t *testing.T) {
4951
"force": "",
5052
"noprune": "1",
5153
},
52-
}, {
54+
},
55+
{
5356
force: true,
5457
pruneChildren: true,
5558
expectedQueryParams: map[string]string{
5659
"force": "1",
5760
"noprune": "",
5861
},
5962
},
63+
{
64+
platform: &ocispec.Platform{
65+
Architecture: "amd64",
66+
OS: "linux",
67+
},
68+
expectedQueryParams: map[string]string{
69+
"platforms": `{"architecture":"amd64","os":"linux"}`,
70+
},
71+
},
6072
}
6173
for _, removeCase := range removeCases {
6274
client := &Client{
@@ -92,10 +104,16 @@ func TestImageRemove(t *testing.T) {
92104
}, nil
93105
}),
94106
}
95-
imageDeletes, err := client.ImageRemove(context.Background(), "image_id", image.RemoveOptions{
107+
108+
opts := image.RemoveOptions{
96109
Force: removeCase.force,
97110
PruneChildren: removeCase.pruneChildren,
98-
})
111+
}
112+
if removeCase.platform != nil {
113+
opts.Platforms = []ocispec.Platform{*removeCase.platform}
114+
}
115+
116+
imageDeletes, err := client.ImageRemove(context.Background(), "image_id", opts)
99117
assert.NilError(t, err)
100118
assert.Check(t, is.Len(imageDeletes, 2))
101119
}

daemon/containerd/fake_service_test.go

Lines changed: 0 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"fmt"
77
"os"
88
"path/filepath"
9-
"sync"
109
"testing"
1110
"time"
1211

@@ -244,48 +243,3 @@ func (s *delayedStore) Update(ctx context.Context, info content.Info, fieldpaths
244243
s.delay()
245244
return s.store.Update(ctx, info, fieldpaths...)
246245
}
247-
248-
type memoryLabelStore struct {
249-
mu sync.Mutex
250-
labels map[digest.Digest]map[string]string
251-
}
252-
253-
// Get returns all the labels for the given digest
254-
func (s *memoryLabelStore) Get(dgst digest.Digest) (map[string]string, error) {
255-
s.mu.Lock()
256-
labels := s.labels[dgst]
257-
s.mu.Unlock()
258-
return labels, nil
259-
}
260-
261-
// Set sets all the labels for a given digest
262-
func (s *memoryLabelStore) Set(dgst digest.Digest, labels map[string]string) error {
263-
s.mu.Lock()
264-
if s.labels == nil {
265-
s.labels = make(map[digest.Digest]map[string]string)
266-
}
267-
s.labels[dgst] = labels
268-
s.mu.Unlock()
269-
return nil
270-
}
271-
272-
// Update replaces the given labels for a digest,
273-
// a key with an empty value removes a label.
274-
func (s *memoryLabelStore) Update(dgst digest.Digest, update map[string]string) (map[string]string, error) {
275-
s.mu.Lock()
276-
defer s.mu.Unlock()
277-
278-
labels, ok := s.labels[dgst]
279-
if !ok {
280-
labels = map[string]string{}
281-
}
282-
for k, v := range update {
283-
labels[k] = v
284-
}
285-
if s.labels == nil {
286-
s.labels = map[digest.Digest]map[string]string{}
287-
}
288-
s.labels[dgst] = labels
289-
290-
return labels, nil
291-
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy