Skip to content

Commit 9603927

Browse files
committed
client: add utilities to encode platforms
Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent 8c3945c commit 9603927

5 files changed

Lines changed: 107 additions & 10 deletions

File tree

client/image_history.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@ func (cli *Client) ImageHistory(ctx context.Context, imageID string, opts image.
1717
return nil, err
1818
}
1919

20-
p, err := json.Marshal(*opts.Platform)
20+
p, err := encodePlatform(opts.Platform)
2121
if err != nil {
2222
return nil, fmt.Errorf("invalid platform: %v", err)
2323
}
24-
query.Set("platform", string(p))
24+
query.Set("platform", p)
2525
}
2626

2727
var history []image.HistoryResponseItem

client/image_load.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client"
22

33
import (
44
"context"
5-
"encoding/json"
65
"io"
76
"net/http"
87
"net/url"
@@ -28,11 +27,11 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.Lo
2827
return image.LoadResponse{}, err
2928
}
3029

31-
p, err := json.Marshal(*opts.Platform)
30+
p, err := encodePlatform(opts.Platform)
3231
if err != nil {
3332
return image.LoadResponse{}, err
3433
}
35-
query.Set("platform", string(p))
34+
query.Set("platform", p)
3635
}
3736

3837
resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{

client/image_save.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ package client // import "github.com/docker/docker/client"
22

33
import (
44
"context"
5-
"encoding/json"
6-
"fmt"
75
"io"
86
"net/url"
97

@@ -22,11 +20,11 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image.
2220
return nil, err
2321
}
2422

25-
p, err := json.Marshal(*opts.Platform)
23+
p, err := encodePlatform(opts.Platform)
2624
if err != nil {
27-
return nil, fmt.Errorf("invalid platform: %v", err)
25+
return nil, err
2826
}
29-
query.Set("platform", string(p))
27+
query.Set("platform", p)
3028
}
3129

3230
resp, err := cli.get(ctx, "/images/get", query, nil)

client/utils.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package client // import "github.com/docker/docker/client"
22

33
import (
4+
"encoding/json"
5+
"fmt"
46
"net/url"
57
"regexp"
68

79
"github.com/docker/docker/api/types/filters"
10+
"github.com/docker/docker/errdefs"
11+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
812
)
913

1014
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`)
@@ -32,3 +36,43 @@ func getFiltersQuery(f filters.Args) (url.Values, error) {
3236
}
3337
return query, nil
3438
}
39+
40+
// encodePlatforms marshals the given platform(s) to JSON format, to
41+
// be used for query-parameters for filtering / selecting platforms.
42+
func encodePlatforms(platform ...ocispec.Platform) ([]string, error) {
43+
if len(platform) == 0 {
44+
return []string{}, nil
45+
}
46+
if len(platform) == 1 {
47+
p, err := encodePlatform(&platform[0])
48+
if err != nil {
49+
return nil, err
50+
}
51+
return []string{p}, nil
52+
}
53+
54+
seen := make(map[string]struct{}, len(platform))
55+
out := make([]string, 0, len(platform))
56+
for i := range platform {
57+
p, err := encodePlatform(&platform[i])
58+
if err != nil {
59+
return nil, err
60+
}
61+
if _, ok := seen[p]; !ok {
62+
out = append(out, p)
63+
seen[p] = struct{}{}
64+
}
65+
}
66+
return out, nil
67+
}
68+
69+
// encodePlatforms marshals the given platform to JSON format, to
70+
// be used for query-parameters for filtering / selecting platforms. It
71+
// is used as a helper for encodePlatforms,
72+
func encodePlatform(platform *ocispec.Platform) (string, error) {
73+
p, err := json.Marshal(platform)
74+
if err != nil {
75+
return "", errdefs.InvalidParameter(fmt.Errorf("invalid platform: %v", err))
76+
}
77+
return string(p), nil
78+
}

client/utils_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
package client
2+
3+
import (
4+
"testing"
5+
6+
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
7+
"gotest.tools/v3/assert"
8+
)
9+
10+
func TestEncodePlatforms(t *testing.T) {
11+
tests := []struct {
12+
doc string
13+
platforms []ocispec.Platform
14+
expected []string
15+
}{
16+
{
17+
doc: "single platform",
18+
platforms: []ocispec.Platform{
19+
{Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
20+
},
21+
expected: []string{
22+
`{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
23+
},
24+
},
25+
{
26+
doc: "multiple platforms",
27+
platforms: []ocispec.Platform{
28+
{Architecture: "arm64", OS: "linux", Variant: "v8"},
29+
{Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
30+
},
31+
expected: []string{
32+
`{"architecture":"arm64","os":"linux","variant":"v8"}`,
33+
`{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
34+
},
35+
},
36+
{
37+
doc: "multiple platforms with duplicates",
38+
platforms: []ocispec.Platform{
39+
{Architecture: "arm64", OS: "linux", Variant: "v8"},
40+
{Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
41+
{Architecture: "arm64", OS: "linux", Variant: "v8"},
42+
},
43+
expected: []string{
44+
`{"architecture":"arm64","os":"linux","variant":"v8"}`,
45+
`{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
46+
},
47+
},
48+
}
49+
for _, tc := range tests {
50+
t.Run(tc.doc, func(t *testing.T) {
51+
out, err := encodePlatforms(tc.platforms...)
52+
assert.NilError(t, err)
53+
assert.DeepEqual(t, out, tc.expected)
54+
})
55+
}
56+
}

0 commit comments

Comments
 (0)