Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
| ... | ... |
@@ -17,11 +17,11 @@ func (cli *Client) ImageHistory(ctx context.Context, imageID string, opts image. |
| 17 | 17 |
return nil, err |
| 18 | 18 |
} |
| 19 | 19 |
|
| 20 |
- p, err := json.Marshal(*opts.Platform) |
|
| 20 |
+ p, err := encodePlatform(opts.Platform) |
|
| 21 | 21 |
if err != nil {
|
| 22 | 22 |
return nil, fmt.Errorf("invalid platform: %v", err)
|
| 23 | 23 |
} |
| 24 |
- query.Set("platform", string(p))
|
|
| 24 |
+ query.Set("platform", p)
|
|
| 25 | 25 |
} |
| 26 | 26 |
|
| 27 | 27 |
var history []image.HistoryResponseItem |
| ... | ... |
@@ -2,7 +2,6 @@ package client // import "github.com/docker/docker/client" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 |
- "encoding/json" |
|
| 6 | 5 |
"io" |
| 7 | 6 |
"net/http" |
| 8 | 7 |
"net/url" |
| ... | ... |
@@ -28,11 +27,11 @@ func (cli *Client) ImageLoad(ctx context.Context, input io.Reader, opts image.Lo |
| 28 | 28 |
return image.LoadResponse{}, err
|
| 29 | 29 |
} |
| 30 | 30 |
|
| 31 |
- p, err := json.Marshal(*opts.Platform) |
|
| 31 |
+ p, err := encodePlatform(opts.Platform) |
|
| 32 | 32 |
if err != nil {
|
| 33 | 33 |
return image.LoadResponse{}, err
|
| 34 | 34 |
} |
| 35 |
- query.Set("platform", string(p))
|
|
| 35 |
+ query.Set("platform", p)
|
|
| 36 | 36 |
} |
| 37 | 37 |
|
| 38 | 38 |
resp, err := cli.postRaw(ctx, "/images/load", query, input, http.Header{
|
| ... | ... |
@@ -2,8 +2,6 @@ package client // import "github.com/docker/docker/client" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"context" |
| 5 |
- "encoding/json" |
|
| 6 |
- "fmt" |
|
| 7 | 5 |
"io" |
| 8 | 6 |
"net/url" |
| 9 | 7 |
|
| ... | ... |
@@ -22,11 +20,11 @@ func (cli *Client) ImageSave(ctx context.Context, imageIDs []string, opts image. |
| 22 | 22 |
return nil, err |
| 23 | 23 |
} |
| 24 | 24 |
|
| 25 |
- p, err := json.Marshal(*opts.Platform) |
|
| 25 |
+ p, err := encodePlatform(opts.Platform) |
|
| 26 | 26 |
if err != nil {
|
| 27 |
- return nil, fmt.Errorf("invalid platform: %v", err)
|
|
| 27 |
+ return nil, err |
|
| 28 | 28 |
} |
| 29 |
- query.Set("platform", string(p))
|
|
| 29 |
+ query.Set("platform", p)
|
|
| 30 | 30 |
} |
| 31 | 31 |
|
| 32 | 32 |
resp, err := cli.get(ctx, "/images/get", query, nil) |
| ... | ... |
@@ -1,10 +1,14 @@ |
| 1 | 1 |
package client // import "github.com/docker/docker/client" |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 5 |
+ "fmt" |
|
| 4 | 6 |
"net/url" |
| 5 | 7 |
"regexp" |
| 6 | 8 |
|
| 7 | 9 |
"github.com/docker/docker/api/types/filters" |
| 10 |
+ "github.com/docker/docker/errdefs" |
|
| 11 |
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 8 | 12 |
) |
| 9 | 13 |
|
| 10 | 14 |
var headerRegexp = regexp.MustCompile(`\ADocker/.+\s\((.+)\)\z`) |
| ... | ... |
@@ -32,3 +36,43 @@ func getFiltersQuery(f filters.Args) (url.Values, error) {
|
| 32 | 32 |
} |
| 33 | 33 |
return query, nil |
| 34 | 34 |
} |
| 35 |
+ |
|
| 36 |
+// encodePlatforms marshals the given platform(s) to JSON format, to |
|
| 37 |
+// be used for query-parameters for filtering / selecting platforms. |
|
| 38 |
+func encodePlatforms(platform ...ocispec.Platform) ([]string, error) {
|
|
| 39 |
+ if len(platform) == 0 {
|
|
| 40 |
+ return []string{}, nil
|
|
| 41 |
+ } |
|
| 42 |
+ if len(platform) == 1 {
|
|
| 43 |
+ p, err := encodePlatform(&platform[0]) |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return nil, err |
|
| 46 |
+ } |
|
| 47 |
+ return []string{p}, nil
|
|
| 48 |
+ } |
|
| 49 |
+ |
|
| 50 |
+ seen := make(map[string]struct{}, len(platform))
|
|
| 51 |
+ out := make([]string, 0, len(platform)) |
|
| 52 |
+ for i := range platform {
|
|
| 53 |
+ p, err := encodePlatform(&platform[i]) |
|
| 54 |
+ if err != nil {
|
|
| 55 |
+ return nil, err |
|
| 56 |
+ } |
|
| 57 |
+ if _, ok := seen[p]; !ok {
|
|
| 58 |
+ out = append(out, p) |
|
| 59 |
+ seen[p] = struct{}{}
|
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ return out, nil |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// encodePlatforms marshals the given platform to JSON format, to |
|
| 66 |
+// be used for query-parameters for filtering / selecting platforms. It |
|
| 67 |
+// is used as a helper for encodePlatforms, |
|
| 68 |
+func encodePlatform(platform *ocispec.Platform) (string, error) {
|
|
| 69 |
+ p, err := json.Marshal(platform) |
|
| 70 |
+ if err != nil {
|
|
| 71 |
+ return "", errdefs.InvalidParameter(fmt.Errorf("invalid platform: %v", err))
|
|
| 72 |
+ } |
|
| 73 |
+ return string(p), nil |
|
| 74 |
+} |
| 35 | 75 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,56 @@ |
| 0 |
+package client |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "testing" |
|
| 4 |
+ |
|
| 5 |
+ ocispec "github.com/opencontainers/image-spec/specs-go/v1" |
|
| 6 |
+ "gotest.tools/v3/assert" |
|
| 7 |
+) |
|
| 8 |
+ |
|
| 9 |
+func TestEncodePlatforms(t *testing.T) {
|
|
| 10 |
+ tests := []struct {
|
|
| 11 |
+ doc string |
|
| 12 |
+ platforms []ocispec.Platform |
|
| 13 |
+ expected []string |
|
| 14 |
+ }{
|
|
| 15 |
+ {
|
|
| 16 |
+ doc: "single platform", |
|
| 17 |
+ platforms: []ocispec.Platform{
|
|
| 18 |
+ {Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
|
|
| 19 |
+ }, |
|
| 20 |
+ expected: []string{
|
|
| 21 |
+ `{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
|
|
| 22 |
+ }, |
|
| 23 |
+ }, |
|
| 24 |
+ {
|
|
| 25 |
+ doc: "multiple platforms", |
|
| 26 |
+ platforms: []ocispec.Platform{
|
|
| 27 |
+ {Architecture: "arm64", OS: "linux", Variant: "v8"},
|
|
| 28 |
+ {Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
|
|
| 29 |
+ }, |
|
| 30 |
+ expected: []string{
|
|
| 31 |
+ `{"architecture":"arm64","os":"linux","variant":"v8"}`,
|
|
| 32 |
+ `{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
|
|
| 33 |
+ }, |
|
| 34 |
+ }, |
|
| 35 |
+ {
|
|
| 36 |
+ doc: "multiple platforms with duplicates", |
|
| 37 |
+ platforms: []ocispec.Platform{
|
|
| 38 |
+ {Architecture: "arm64", OS: "linux", Variant: "v8"},
|
|
| 39 |
+ {Architecture: "arm64", OS: "windows", Variant: "v8", OSVersion: "99.99.99"},
|
|
| 40 |
+ {Architecture: "arm64", OS: "linux", Variant: "v8"},
|
|
| 41 |
+ }, |
|
| 42 |
+ expected: []string{
|
|
| 43 |
+ `{"architecture":"arm64","os":"linux","variant":"v8"}`,
|
|
| 44 |
+ `{"architecture":"arm64","os":"windows","os.version":"99.99.99","variant":"v8"}`,
|
|
| 45 |
+ }, |
|
| 46 |
+ }, |
|
| 47 |
+ } |
|
| 48 |
+ for _, tc := range tests {
|
|
| 49 |
+ t.Run(tc.doc, func(t *testing.T) {
|
|
| 50 |
+ out, err := encodePlatforms(tc.platforms...) |
|
| 51 |
+ assert.NilError(t, err) |
|
| 52 |
+ assert.DeepEqual(t, out, tc.expected) |
|
| 53 |
+ }) |
|
| 54 |
+ } |
|
| 55 |
+} |