Signed-off-by: John Howard <jhoward@microsoft.com>
John Howard authored on 2017/09/14 04:49:04... | ... |
@@ -1,17 +1,11 @@ |
1 | 1 |
package httputils |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "encoding/json" |
|
5 |
- "fmt" |
|
6 | 4 |
"io" |
7 | 5 |
"mime" |
8 | 6 |
"net/http" |
9 |
- "runtime" |
|
10 | 7 |
"strings" |
11 | 8 |
|
12 |
- "github.com/docker/docker/api/types/versions" |
|
13 |
- "github.com/docker/docker/pkg/system" |
|
14 |
- specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
15 | 9 |
"github.com/pkg/errors" |
16 | 10 |
"github.com/sirupsen/logrus" |
17 | 11 |
"golang.org/x/net/context" |
... | ... |
@@ -115,27 +109,3 @@ func matchesContentType(contentType, expectedType string) bool { |
115 | 115 |
} |
116 | 116 |
return err == nil && mimetype == expectedType |
117 | 117 |
} |
118 |
- |
|
119 |
-// GetRequestedPlatform extracts an optional platform structure from an HTTP request header |
|
120 |
-func GetRequestedPlatform(ctx context.Context, r *http.Request) (*specs.Platform, error) { |
|
121 |
- platform := &specs.Platform{} |
|
122 |
- version := VersionFromContext(ctx) |
|
123 |
- if versions.GreaterThanOrEqualTo(version, "1.32") { |
|
124 |
- requestedPlatform := r.Header.Get("X-Requested-Platform") |
|
125 |
- if requestedPlatform != "" { |
|
126 |
- if err := json.Unmarshal([]byte(requestedPlatform), platform); err != nil { |
|
127 |
- return nil, fmt.Errorf("invalid X-Requested-Platform header: %s", err) |
|
128 |
- } |
|
129 |
- } |
|
130 |
- if err := system.ValidatePlatform(platform); err != nil { |
|
131 |
- return nil, err |
|
132 |
- } |
|
133 |
- } |
|
134 |
- if platform.OS == "" { |
|
135 |
- platform.OS = runtime.GOOS |
|
136 |
- } |
|
137 |
- if platform.Architecture == "" { |
|
138 |
- platform.Architecture = runtime.GOARCH |
|
139 |
- } |
|
140 |
- return platform, nil |
|
141 |
-} |
... | ... |
@@ -7,6 +7,7 @@ import ( |
7 | 7 |
"fmt" |
8 | 8 |
"io" |
9 | 9 |
"net/http" |
10 |
+ "os" |
|
10 | 11 |
"runtime" |
11 | 12 |
"strconv" |
12 | 13 |
"strings" |
... | ... |
@@ -20,6 +21,7 @@ import ( |
20 | 20 |
"github.com/docker/docker/pkg/ioutils" |
21 | 21 |
"github.com/docker/docker/pkg/progress" |
22 | 22 |
"github.com/docker/docker/pkg/streamformatter" |
23 |
+ "github.com/docker/docker/pkg/system" |
|
23 | 24 |
units "github.com/docker/go-units" |
24 | 25 |
"github.com/pkg/errors" |
25 | 26 |
"github.com/sirupsen/logrus" |
... | ... |
@@ -67,6 +69,24 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui |
67 | 67 |
options.Squash = httputils.BoolValue(r, "squash") |
68 | 68 |
options.Target = r.FormValue("target") |
69 | 69 |
options.RemoteContext = r.FormValue("remote") |
70 |
+ if versions.GreaterThanOrEqualTo(version, "1.32") { |
|
71 |
+ // TODO @jhowardmsft. The following environment variable is an interim |
|
72 |
+ // measure to allow the daemon to have a default platform if omitted by |
|
73 |
+ // the client. This allows LCOW and WCOW to work with a down-level CLI |
|
74 |
+ // for a short period of time, as the CLI changes can't be merged |
|
75 |
+ // until after the daemon changes have been merged. Once the CLI is |
|
76 |
+ // updated, this can be removed. PR for CLI is currently in |
|
77 |
+ // https://github.com/docker/cli/pull/474. |
|
78 |
+ apiPlatform := r.FormValue("platform") |
|
79 |
+ if system.LCOWSupported() && apiPlatform == "" { |
|
80 |
+ apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED") |
|
81 |
+ } |
|
82 |
+ p := system.ParsePlatform(apiPlatform) |
|
83 |
+ if err := system.ValidatePlatform(p); err != nil { |
|
84 |
+ return nil, validationError{fmt.Errorf("invalid platform: %s", err)} |
|
85 |
+ } |
|
86 |
+ options.Platform = p.OS |
|
87 |
+ } |
|
70 | 88 |
|
71 | 89 |
if r.Form.Get("shmsize") != "" { |
72 | 90 |
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64) |
... | ... |
@@ -87,12 +107,6 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui |
87 | 87 |
return nil, validationError{fmt.Errorf("The daemon on this platform does not support setting security options on build")} |
88 | 88 |
} |
89 | 89 |
|
90 |
- platform, err := httputils.GetRequestedPlatform(ctx, r) |
|
91 |
- if err != nil { |
|
92 |
- return nil, err |
|
93 |
- } |
|
94 |
- options.Platform = *platform |
|
95 |
- |
|
96 | 90 |
var buildUlimits = []*units.Ulimit{} |
97 | 91 |
ulimitsJSON := r.FormValue("ulimits") |
98 | 92 |
if ulimitsJSON != "" { |
... | ... |
@@ -3,8 +3,10 @@ package image |
3 | 3 |
import ( |
4 | 4 |
"encoding/base64" |
5 | 5 |
"encoding/json" |
6 |
+ "fmt" |
|
6 | 7 |
"io" |
7 | 8 |
"net/http" |
9 |
+ "os" |
|
8 | 10 |
"strconv" |
9 | 11 |
"strings" |
10 | 12 |
|
... | ... |
@@ -16,6 +18,7 @@ import ( |
16 | 16 |
"github.com/docker/docker/api/types/versions" |
17 | 17 |
"github.com/docker/docker/pkg/ioutils" |
18 | 18 |
"github.com/docker/docker/pkg/streamformatter" |
19 |
+ "github.com/docker/docker/pkg/system" |
|
19 | 20 |
"github.com/docker/docker/registry" |
20 | 21 |
specs "github.com/opencontainers/image-spec/specs-go/v1" |
21 | 22 |
"github.com/pkg/errors" |
... | ... |
@@ -70,6 +73,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r * |
70 | 70 |
|
71 | 71 |
// Creates an image from Pull or from Import |
72 | 72 |
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error { |
73 |
+ |
|
73 | 74 |
if err := httputils.ParseForm(r); err != nil { |
74 | 75 |
return err |
75 | 76 |
} |
... | ... |
@@ -87,7 +91,25 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite |
87 | 87 |
|
88 | 88 |
w.Header().Set("Content-Type", "application/json") |
89 | 89 |
|
90 |
- platform, err = httputils.GetRequestedPlatform(ctx, r) |
|
90 |
+ version := httputils.VersionFromContext(ctx) |
|
91 |
+ if versions.GreaterThanOrEqualTo(version, "1.32") { |
|
92 |
+ // TODO @jhowardmsft. The following environment variable is an interim |
|
93 |
+ // measure to allow the daemon to have a default platform if omitted by |
|
94 |
+ // the client. This allows LCOW and WCOW to work with a down-level CLI |
|
95 |
+ // for a short period of time, as the CLI changes can't be merged |
|
96 |
+ // until after the daemon changes have been merged. Once the CLI is |
|
97 |
+ // updated, this can be removed. PR for CLI is currently in |
|
98 |
+ // https://github.com/docker/cli/pull/474. |
|
99 |
+ apiPlatform := r.FormValue("platform") |
|
100 |
+ if system.LCOWSupported() && apiPlatform == "" { |
|
101 |
+ apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED") |
|
102 |
+ } |
|
103 |
+ platform = system.ParsePlatform(apiPlatform) |
|
104 |
+ if err = system.ValidatePlatform(platform); err != nil { |
|
105 |
+ err = fmt.Errorf("invalid platform: %s", err) |
|
106 |
+ } |
|
107 |
+ } |
|
108 |
+ |
|
91 | 109 |
if err == nil { |
92 | 110 |
if image != "" { //pull |
93 | 111 |
metaHeaders := map[string][]string{} |
... | ... |
@@ -6181,17 +6181,9 @@ paths: |
6181 | 6181 |
|
6182 | 6182 |
Only the registry domain name (and port if not the default 443) are required. However, for legacy reasons, the Docker Hub registry must be specified with both a `https://` prefix and a `/v1/` suffix even though Docker will prefer to use the v2 registry API. |
6183 | 6183 |
type: "string" |
6184 |
- - name: "X-Requested-Platform" |
|
6185 |
- in: "header" |
|
6186 |
- description: | |
|
6187 |
- This is a JSON object representing an OCI image-spec `Platform` object. It is used to request a platform in the case that the engine supports multiple platforms. For example: |
|
6188 |
- |
|
6189 |
- ``` |
|
6190 |
- { |
|
6191 |
- "architecture": "amd64", |
|
6192 |
- "os": "linux" |
|
6193 |
- } |
|
6194 |
- ``` |
|
6184 |
+ - name: "platform" |
|
6185 |
+ in: "query" |
|
6186 |
+ description: "Platform in the format os[/arch[/variant]]" |
|
6195 | 6187 |
type: "string" |
6196 | 6188 |
default: "" |
6197 | 6189 |
responses: |
... | ... |
@@ -6275,17 +6267,9 @@ paths: |
6275 | 6275 |
in: "header" |
6276 | 6276 |
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" |
6277 | 6277 |
type: "string" |
6278 |
- - name: "X-Requested-Platform" |
|
6279 |
- in: "header" |
|
6280 |
- description: | |
|
6281 |
- This is a JSON object representing an OCI image-spec `Platform` object. It is used to request a platform in the case that the engine supports multiple platforms. For example: |
|
6282 |
- |
|
6283 |
- ``` |
|
6284 |
- { |
|
6285 |
- "architecture": "amd64", |
|
6286 |
- "os": "linux" |
|
6287 |
- } |
|
6288 |
- ``` |
|
6278 |
+ - name: "platform" |
|
6279 |
+ in: "query" |
|
6280 |
+ description: "Platform in the format os[/arch[/variant]]" |
|
6289 | 6281 |
type: "string" |
6290 | 6282 |
default: "" |
6291 | 6283 |
tags: ["Image"] |
... | ... |
@@ -8,7 +8,6 @@ import ( |
8 | 8 |
"github.com/docker/docker/api/types/container" |
9 | 9 |
"github.com/docker/docker/api/types/filters" |
10 | 10 |
units "github.com/docker/go-units" |
11 |
- specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
12 | 11 |
) |
13 | 12 |
|
14 | 13 |
// CheckpointCreateOptions holds parameters to create a checkpoint from a container |
... | ... |
@@ -180,7 +179,7 @@ type ImageBuildOptions struct { |
180 | 180 |
ExtraHosts []string // List of extra hosts |
181 | 181 |
Target string |
182 | 182 |
SessionID string |
183 |
- Platform specs.Platform |
|
183 |
+ Platform string |
|
184 | 184 |
} |
185 | 185 |
|
186 | 186 |
// ImageBuildResponse holds information |
... | ... |
@@ -193,8 +192,8 @@ type ImageBuildResponse struct { |
193 | 193 |
|
194 | 194 |
// ImageCreateOptions holds information to create images. |
195 | 195 |
type ImageCreateOptions struct { |
196 |
- RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry. |
|
197 |
- Platform specs.Platform // Platform is the target platform of the image if it needs to be pulled from the registry. |
|
196 |
+ RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry. |
|
197 |
+ Platform string // Platform is the target platform of the image if it needs to be pulled from the registry. |
|
198 | 198 |
} |
199 | 199 |
|
200 | 200 |
// ImageImportSource holds source information for ImageImport |
... | ... |
@@ -205,9 +204,10 @@ type ImageImportSource struct { |
205 | 205 |
|
206 | 206 |
// ImageImportOptions holds information to import images from the client host. |
207 | 207 |
type ImageImportOptions struct { |
208 |
- Tag string // Tag is the name to tag this image with. This attribute is deprecated. |
|
209 |
- Message string // Message is the message to tag the image with |
|
210 |
- Changes []string // Changes are the raw changes to apply to this image |
|
208 |
+ Tag string // Tag is the name to tag this image with. This attribute is deprecated. |
|
209 |
+ Message string // Message is the message to tag the image with |
|
210 |
+ Changes []string // Changes are the raw changes to apply to this image |
|
211 |
+ Platform string // Platform is the target platform of the image |
|
211 | 212 |
} |
212 | 213 |
|
213 | 214 |
// ImageListOptions holds parameters to filter the list of images with. |
... | ... |
@@ -228,7 +228,7 @@ type ImagePullOptions struct { |
228 | 228 |
All bool |
229 | 229 |
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry |
230 | 230 |
PrivilegeFunc RequestPrivilegeFunc |
231 |
- Platform specs.Platform |
|
231 |
+ Platform string |
|
232 | 232 |
} |
233 | 233 |
|
234 | 234 |
// RequestPrivilegeFunc is a function interface that |
... | ... |
@@ -15,6 +15,7 @@ import ( |
15 | 15 |
"github.com/docker/docker/api/types/registry" |
16 | 16 |
"github.com/docker/docker/api/types/swarm" |
17 | 17 |
"github.com/docker/go-connections/nat" |
18 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
18 | 19 |
) |
19 | 20 |
|
20 | 21 |
// RootFS returns Image's RootFS description including the layer IDs. |
... | ... |
@@ -327,7 +328,7 @@ type ContainerJSONBase struct { |
327 | 327 |
Name string |
328 | 328 |
RestartCount int |
329 | 329 |
Driver string |
330 |
- OS string |
|
330 |
+ Platform specs.Platform |
|
331 | 331 |
MountLabel string |
332 | 332 |
ProcessLabel string |
333 | 333 |
AppArmorProfile string |
... | ... |
@@ -20,6 +20,7 @@ import ( |
20 | 20 |
"github.com/docker/docker/pkg/idtools" |
21 | 21 |
"github.com/docker/docker/pkg/streamformatter" |
22 | 22 |
"github.com/docker/docker/pkg/stringid" |
23 |
+ "github.com/docker/docker/pkg/system" |
|
23 | 24 |
"github.com/moby/buildkit/session" |
24 | 25 |
"github.com/pkg/errors" |
25 | 26 |
"github.com/sirupsen/logrus" |
... | ... |
@@ -102,15 +103,16 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) ( |
102 | 102 |
} |
103 | 103 |
|
104 | 104 |
os := runtime.GOOS |
105 |
+ optionsPlatform := system.ParsePlatform(config.Options.Platform) |
|
105 | 106 |
if dockerfile.OS != "" { |
106 |
- if config.Options.Platform.OS != "" && config.Options.Platform.OS != dockerfile.OS { |
|
107 |
+ if optionsPlatform.OS != "" && optionsPlatform.OS != dockerfile.OS { |
|
107 | 108 |
return nil, fmt.Errorf("invalid platform") |
108 | 109 |
} |
109 | 110 |
os = dockerfile.OS |
110 |
- } else if config.Options.Platform.OS != "" { |
|
111 |
- os = config.Options.Platform.OS |
|
111 |
+ } else if optionsPlatform.OS != "" { |
|
112 |
+ os = optionsPlatform.OS |
|
112 | 113 |
} |
113 |
- config.Options.Platform.OS = os |
|
114 |
+ config.Options.Platform = os |
|
114 | 115 |
dockerfile.OS = os |
115 | 116 |
|
116 | 117 |
builderOptions := builderOptions{ |
... | ... |
@@ -82,7 +82,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i |
82 | 82 |
pathCache: req.builder.pathCache, |
83 | 83 |
download: download, |
84 | 84 |
imageSource: imageSource, |
85 |
- platform: req.builder.options.Platform.OS, |
|
85 |
+ platform: req.builder.options.Platform, |
|
86 | 86 |
} |
87 | 87 |
} |
88 | 88 |
|
... | ... |
@@ -194,11 +194,6 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error { |
194 | 194 |
return nil |
195 | 195 |
} |
196 | 196 |
|
197 |
-// scratchImage is used as a token for the empty base image. It uses buildStage |
|
198 |
-// as a convenient implementation of builder.Image, but is not actually a |
|
199 |
-// buildStage. |
|
200 |
-var scratchImage builder.Image = &image.Image{} |
|
201 |
- |
|
202 | 197 |
func (d *dispatchRequest) getExpandedImageName(shlex *ShellLex, name string) (string, error) { |
203 | 198 |
substitutionArgs := []string{} |
204 | 199 |
for key, value := range d.state.buildArgs.GetAllMeta() { |
... | ... |
@@ -223,8 +218,9 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) { |
223 | 223 |
imageImage := &image.Image{} |
224 | 224 |
imageImage.OS = runtime.GOOS |
225 | 225 |
if runtime.GOOS == "windows" { |
226 |
- switch d.builder.options.Platform.OS { |
|
227 |
- case "windows": |
|
226 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
227 |
+ switch optionsOS { |
|
228 |
+ case "windows", "": |
|
228 | 229 |
return nil, errors.New("Windows does not support FROM scratch") |
229 | 230 |
case "linux": |
230 | 231 |
if !system.LCOWSupported() { |
... | ... |
@@ -232,7 +228,7 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) { |
232 | 232 |
} |
233 | 233 |
imageImage.OS = "linux" |
234 | 234 |
default: |
235 |
- return nil, errors.Errorf("operating system %q is not supported", d.builder.options.Platform.OS) |
|
235 |
+ return nil, errors.Errorf("operating system %q is not supported", optionsOS) |
|
236 | 236 |
} |
237 | 237 |
} |
238 | 238 |
return builder.Image(imageImage), nil |
... | ... |
@@ -264,7 +260,8 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error { |
264 | 264 |
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { |
265 | 265 |
runConfig := d.state.runConfig |
266 | 266 |
var err error |
267 |
- runConfig.WorkingDir, err = normalizeWorkdir(d.builder.options.Platform.OS, runConfig.WorkingDir, c.Path) |
|
267 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
268 |
+ runConfig.WorkingDir, err = normalizeWorkdir(optionsOS, runConfig.WorkingDir, c.Path) |
|
268 | 269 |
if err != nil { |
269 | 270 |
return err |
270 | 271 |
} |
... | ... |
@@ -280,7 +277,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { |
280 | 280 |
} |
281 | 281 |
|
282 | 282 |
comment := "WORKDIR " + runConfig.WorkingDir |
283 |
- runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.builder.options.Platform.OS)) |
|
283 |
+ runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, optionsOS)) |
|
284 | 284 |
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd) |
285 | 285 |
if err != nil || containerID == "" { |
286 | 286 |
return err |
... | ... |
@@ -313,7 +310,8 @@ func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container |
313 | 313 |
func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { |
314 | 314 |
|
315 | 315 |
stateRunConfig := d.state.runConfig |
316 |
- cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.builder.options.Platform.OS) |
|
316 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
317 |
+ cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, optionsOS) |
|
317 | 318 |
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env) |
318 | 319 |
|
319 | 320 |
saveCmd := cmdFromArgs |
... | ... |
@@ -390,7 +388,8 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S |
390 | 390 |
// |
391 | 391 |
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error { |
392 | 392 |
runConfig := d.state.runConfig |
393 |
- cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS) |
|
393 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
394 |
+ cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS) |
|
394 | 395 |
runConfig.Cmd = cmd |
395 | 396 |
// set config as already being escaped, this prevents double escaping on windows |
396 | 397 |
runConfig.ArgsEscaped = true |
... | ... |
@@ -433,7 +432,8 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand) |
433 | 433 |
// |
434 | 434 |
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error { |
435 | 435 |
runConfig := d.state.runConfig |
436 |
- cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS) |
|
436 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
437 |
+ cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS) |
|
437 | 438 |
runConfig.Entrypoint = cmd |
438 | 439 |
if !d.state.cmdSet { |
439 | 440 |
runConfig.Cmd = nil |
... | ... |
@@ -14,7 +14,6 @@ import ( |
14 | 14 |
"github.com/docker/docker/builder/dockerfile/instructions" |
15 | 15 |
"github.com/docker/docker/pkg/system" |
16 | 16 |
"github.com/docker/go-connections/nat" |
17 |
- specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
18 | 17 |
"github.com/stretchr/testify/assert" |
19 | 18 |
"github.com/stretchr/testify/require" |
20 | 19 |
) |
... | ... |
@@ -23,13 +22,13 @@ func newBuilderWithMockBackend() *Builder { |
23 | 23 |
mockBackend := &MockBackend{} |
24 | 24 |
ctx := context.Background() |
25 | 25 |
b := &Builder{ |
26 |
- options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}}, |
|
26 |
+ options: &types.ImageBuildOptions{Platform: runtime.GOOS}, |
|
27 | 27 |
docker: mockBackend, |
28 | 28 |
Stdout: new(bytes.Buffer), |
29 | 29 |
clientCtx: ctx, |
30 | 30 |
disableCommit: true, |
31 | 31 |
imageSources: newImageSources(ctx, builderOptions{ |
32 |
- Options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}}, |
|
32 |
+ Options: &types.ImageBuildOptions{Platform: runtime.GOOS}, |
|
33 | 33 |
Backend: mockBackend, |
34 | 34 |
}), |
35 | 35 |
imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false), |
... | ... |
@@ -34,7 +34,8 @@ import ( |
34 | 34 |
|
35 | 35 |
func dispatch(d dispatchRequest, cmd instructions.Command) error { |
36 | 36 |
if c, ok := cmd.(instructions.PlatformSpecific); ok { |
37 |
- err := c.CheckPlatform(d.builder.options.Platform.OS) |
|
37 |
+ optionsOS := system.ParsePlatform(d.builder.options.Platform).OS |
|
38 |
+ err := c.CheckPlatform(optionsOS) |
|
38 | 39 |
if err != nil { |
39 | 40 |
return validationError{err} |
40 | 41 |
} |
... | ... |
@@ -5,6 +5,7 @@ import ( |
5 | 5 |
"github.com/docker/docker/builder" |
6 | 6 |
"github.com/docker/docker/builder/remotecontext" |
7 | 7 |
dockerimage "github.com/docker/docker/image" |
8 |
+ "github.com/docker/docker/pkg/system" |
|
8 | 9 |
"github.com/pkg/errors" |
9 | 10 |
"github.com/sirupsen/logrus" |
10 | 11 |
"golang.org/x/net/context" |
... | ... |
@@ -30,11 +31,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources |
30 | 30 |
pullOption = backend.PullOptionPreferLocal |
31 | 31 |
} |
32 | 32 |
} |
33 |
+ optionsPlatform := system.ParsePlatform(options.Options.Platform) |
|
33 | 34 |
return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{ |
34 | 35 |
PullOption: pullOption, |
35 | 36 |
AuthConfig: options.Options.AuthConfigs, |
36 | 37 |
Output: options.ProgressWriter.Output, |
37 |
- OS: options.Options.Platform.OS, |
|
38 |
+ OS: optionsPlatform.OS, |
|
38 | 39 |
}) |
39 | 40 |
} |
40 | 41 |
|
... | ... |
@@ -83,7 +83,8 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error { |
83 | 83 |
return errors.New("Please provide a source image with `from` prior to commit") |
84 | 84 |
} |
85 | 85 |
|
86 |
- runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, b.options.Platform.OS)) |
|
86 |
+ optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
87 |
+ runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, optionsPlatform.OS)) |
|
87 | 88 |
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd) |
88 | 89 |
if err != nil || hit { |
89 | 90 |
return err |
... | ... |
@@ -122,7 +123,8 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta |
122 | 122 |
} |
123 | 123 |
|
124 | 124 |
func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error { |
125 |
- newLayer, err := imageMount.Layer().Commit(b.options.Platform.OS) |
|
125 |
+ optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
126 |
+ newLayer, err := imageMount.Layer().Commit(optionsPlatform.OS) |
|
126 | 127 |
if err != nil { |
127 | 128 |
return err |
128 | 129 |
} |
... | ... |
@@ -170,9 +172,10 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error |
170 | 170 |
commentStr := fmt.Sprintf("%s %s%s in %s ", inst.cmdName, chownComment, srcHash, inst.dest) |
171 | 171 |
|
172 | 172 |
// TODO: should this have been using origPaths instead of srcHash in the comment? |
173 |
+ optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
173 | 174 |
runConfigWithCommentCmd := copyRunConfig( |
174 | 175 |
state.runConfig, |
175 |
- withCmdCommentString(commentStr, b.options.Platform.OS)) |
|
176 |
+ withCmdCommentString(commentStr, optionsPlatform.OS)) |
|
176 | 177 |
hit, err := b.probeCache(state, runConfigWithCommentCmd) |
177 | 178 |
if err != nil || hit { |
178 | 179 |
return err |
... | ... |
@@ -183,7 +186,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error |
183 | 183 |
return errors.Wrapf(err, "failed to get destination image %q", state.imageID) |
184 | 184 |
} |
185 | 185 |
|
186 |
- destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform.OS) |
|
186 |
+ destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform) |
|
187 | 187 |
if err != nil { |
188 | 188 |
return err |
189 | 189 |
} |
... | ... |
@@ -463,13 +466,15 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai |
463 | 463 |
} |
464 | 464 |
// Set a log config to override any default value set on the daemon |
465 | 465 |
hostConfig := &container.HostConfig{LogConfig: defaultLogConfig} |
466 |
- container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS) |
|
466 |
+ optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
467 |
+ container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS) |
|
467 | 468 |
return container.ID, err |
468 | 469 |
} |
469 | 470 |
|
470 | 471 |
func (b *Builder) create(runConfig *container.Config) (string, error) { |
471 | 472 |
hostConfig := hostConfigFromOptions(b.options) |
472 |
- container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS) |
|
473 |
+ optionsPlatform := system.ParsePlatform(b.options.Platform) |
|
474 |
+ container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS) |
|
473 | 475 |
if err != nil { |
474 | 476 |
return "", err |
475 | 477 |
} |
... | ... |
@@ -7,12 +7,12 @@ import ( |
7 | 7 |
"net/http" |
8 | 8 |
"net/url" |
9 | 9 |
"strconv" |
10 |
+ "strings" |
|
10 | 11 |
|
11 | 12 |
"golang.org/x/net/context" |
12 | 13 |
|
13 | 14 |
"github.com/docker/docker/api/types" |
14 | 15 |
"github.com/docker/docker/api/types/container" |
15 |
- "github.com/docker/docker/pkg/system" |
|
16 | 16 |
) |
17 | 17 |
|
18 | 18 |
// ImageBuild sends request to the daemon to build images. |
... | ... |
@@ -31,18 +31,11 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio |
31 | 31 |
} |
32 | 32 |
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) |
33 | 33 |
|
34 |
- // TODO @jhowardmsft: system.IsPlatformEmpty is a temporary function. We need to move |
|
35 |
- // (in the reasonably short future) to a package which supports all the platform |
|
36 |
- // validation such as is proposed in https://github.com/containerd/containerd/pull/1403 |
|
37 |
- if !system.IsPlatformEmpty(options.Platform) { |
|
34 |
+ if options.Platform != "" { |
|
38 | 35 |
if err := cli.NewVersionError("1.32", "platform"); err != nil { |
39 | 36 |
return types.ImageBuildResponse{}, err |
40 | 37 |
} |
41 |
- platformJSON, err := json.Marshal(options.Platform) |
|
42 |
- if err != nil { |
|
43 |
- return types.ImageBuildResponse{}, err |
|
44 |
- } |
|
45 |
- headers.Add("X-Requested-Platform", string(platformJSON[:])) |
|
38 |
+ query.Set("platform", options.Platform) |
|
46 | 39 |
} |
47 | 40 |
headers.Set("Content-Type", "application/x-tar") |
48 | 41 |
|
... | ... |
@@ -138,5 +131,8 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur |
138 | 138 |
if options.SessionID != "" { |
139 | 139 |
query.Set("session", options.SessionID) |
140 | 140 |
} |
141 |
+ if options.Platform != "" { |
|
142 |
+ query.Set("platform", strings.ToLower(options.Platform)) |
|
143 |
+ } |
|
141 | 144 |
return query, nil |
142 | 145 |
} |
... | ... |
@@ -1,16 +1,14 @@ |
1 | 1 |
package client |
2 | 2 |
|
3 | 3 |
import ( |
4 |
- "encoding/json" |
|
5 | 4 |
"io" |
6 | 5 |
"net/url" |
6 |
+ "strings" |
|
7 | 7 |
|
8 | 8 |
"golang.org/x/net/context" |
9 | 9 |
|
10 | 10 |
"github.com/docker/distribution/reference" |
11 | 11 |
"github.com/docker/docker/api/types" |
12 |
- "github.com/docker/docker/pkg/system" |
|
13 |
- specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
14 | 12 |
) |
15 | 13 |
|
16 | 14 |
// ImageCreate creates a new image based in the parent options. |
... | ... |
@@ -24,25 +22,17 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti |
24 | 24 |
query := url.Values{} |
25 | 25 |
query.Set("fromImage", reference.FamiliarName(ref)) |
26 | 26 |
query.Set("tag", getAPITagFromNamedRef(ref)) |
27 |
- resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform) |
|
27 |
+ if options.Platform != "" { |
|
28 |
+ query.Set("platform", strings.ToLower(options.Platform)) |
|
29 |
+ } |
|
30 |
+ resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) |
|
28 | 31 |
if err != nil { |
29 | 32 |
return nil, err |
30 | 33 |
} |
31 | 34 |
return resp.body, nil |
32 | 35 |
} |
33 | 36 |
|
34 |
-func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string, platform specs.Platform) (serverResponse, error) { |
|
37 |
+func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { |
|
35 | 38 |
headers := map[string][]string{"X-Registry-Auth": {registryAuth}} |
36 |
- |
|
37 |
- // TODO @jhowardmsft: system.IsPlatformEmpty is a temporary function. We need to move |
|
38 |
- // (in the reasonably short future) to a package which supports all the platform |
|
39 |
- // validation such as is proposed in https://github.com/containerd/containerd/pull/1403 |
|
40 |
- if !system.IsPlatformEmpty(platform) { |
|
41 |
- platformJSON, err := json.Marshal(platform) |
|
42 |
- if err != nil { |
|
43 |
- return serverResponse{}, err |
|
44 |
- } |
|
45 |
- headers["X-Requested-Platform"] = []string{string(platformJSON[:])} |
|
46 |
- } |
|
47 | 39 |
return cli.post(ctx, "/images/create", query, nil, headers) |
48 | 40 |
} |
... | ... |
@@ -3,6 +3,7 @@ package client |
3 | 3 |
import ( |
4 | 4 |
"io" |
5 | 5 |
"net/url" |
6 |
+ "strings" |
|
6 | 7 |
|
7 | 8 |
"golang.org/x/net/context" |
8 | 9 |
|
... | ... |
@@ -25,6 +26,9 @@ func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSour |
25 | 25 |
query.Set("repo", ref) |
26 | 26 |
query.Set("tag", options.Tag) |
27 | 27 |
query.Set("message", options.Message) |
28 |
+ if options.Platform != "" { |
|
29 |
+ query.Set("platform", strings.ToLower(options.Platform)) |
|
30 |
+ } |
|
28 | 31 |
for _, change := range options.Changes { |
29 | 32 |
query.Add("changes", change) |
30 | 33 |
} |
... | ... |
@@ -4,12 +4,12 @@ import ( |
4 | 4 |
"io" |
5 | 5 |
"net/http" |
6 | 6 |
"net/url" |
7 |
+ "strings" |
|
7 | 8 |
|
8 | 9 |
"golang.org/x/net/context" |
9 | 10 |
|
10 | 11 |
"github.com/docker/distribution/reference" |
11 | 12 |
"github.com/docker/docker/api/types" |
12 |
- "github.com/docker/docker/pkg/system" |
|
13 | 13 |
) |
14 | 14 |
|
15 | 15 |
// ImagePull requests the docker host to pull an image from a remote registry. |
... | ... |
@@ -31,30 +31,17 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.I |
31 | 31 |
if !options.All { |
32 | 32 |
query.Set("tag", getAPITagFromNamedRef(ref)) |
33 | 33 |
} |
34 |
- |
|
35 |
- // TODO 1: Extend to include "and the platform is supported by the daemon". |
|
36 |
- // This is dependent on https://github.com/moby/moby/pull/34628 though, |
|
37 |
- // and the daemon returning the set of platforms it supports via the _ping |
|
38 |
- // API endpoint. |
|
39 |
- // |
|
40 |
- // TODO 2: system.IsPlatformEmpty is a temporary function. We need to move |
|
41 |
- // (in the reasonably short future) to a package which supports all the platform |
|
42 |
- // validation such as is proposed in https://github.com/containerd/containerd/pull/1403 |
|
43 |
- // |
|
44 |
- // @jhowardmsft. |
|
45 |
- if !system.IsPlatformEmpty(options.Platform) { |
|
46 |
- if err := cli.NewVersionError("1.32", "platform"); err != nil { |
|
47 |
- return nil, err |
|
48 |
- } |
|
34 |
+ if options.Platform != "" { |
|
35 |
+ query.Set("platform", strings.ToLower(options.Platform)) |
|
49 | 36 |
} |
50 | 37 |
|
51 |
- resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform) |
|
38 |
+ resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) |
|
52 | 39 |
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { |
53 | 40 |
newAuthHeader, privilegeErr := options.PrivilegeFunc() |
54 | 41 |
if privilegeErr != nil { |
55 | 42 |
return nil, privilegeErr |
56 | 43 |
} |
57 |
- resp, err = cli.tryImageCreate(ctx, query, newAuthHeader, options.Platform) |
|
44 |
+ resp, err = cli.tryImageCreate(ctx, query, newAuthHeader) |
|
58 | 45 |
} |
59 | 46 |
if err != nil { |
60 | 47 |
return nil, err |
... | ... |
@@ -66,7 +66,7 @@ func (container *Container) BuildHostnameFile() error { |
66 | 66 |
func (container *Container) NetworkMounts() []Mount { |
67 | 67 |
var mounts []Mount |
68 | 68 |
shared := container.HostConfig.NetworkMode.IsContainer() |
69 |
- parser := volume.NewParser(container.Platform) |
|
69 |
+ parser := volume.NewParser(container.OS) |
|
70 | 70 |
if container.ResolvConfPath != "" { |
71 | 71 |
if _, err := os.Stat(container.ResolvConfPath); err != nil { |
72 | 72 |
logrus.Warnf("ResolvConfPath set to %q, but can't stat this filename (err = %v); skipping", container.ResolvConfPath, err) |
... | ... |
@@ -195,7 +195,7 @@ func (container *Container) UnmountIpcMount(unmount func(pth string) error) erro |
195 | 195 |
// IpcMounts returns the list of IPC mounts |
196 | 196 |
func (container *Container) IpcMounts() []Mount { |
197 | 197 |
var mounts []Mount |
198 |
- parser := volume.NewParser(container.Platform) |
|
198 |
+ parser := volume.NewParser(container.OS) |
|
199 | 199 |
|
200 | 200 |
if container.HasMountFor("/dev/shm") { |
201 | 201 |
return mounts |
... | ... |
@@ -429,7 +429,7 @@ func copyOwnership(source, destination string) error { |
429 | 429 |
|
430 | 430 |
// TmpfsMounts returns the list of tmpfs mounts |
431 | 431 |
func (container *Container) TmpfsMounts() ([]Mount, error) { |
432 |
- parser := volume.NewParser(container.Platform) |
|
432 |
+ parser := volume.NewParser(container.OS) |
|
433 | 433 |
var mounts []Mount |
434 | 434 |
for dest, data := range container.HostConfig.Tmpfs { |
435 | 435 |
mounts = append(mounts, Mount{ |
... | ... |
@@ -12,7 +12,7 @@ import ( |
12 | 12 |
// cannot be configured with a read-only rootfs. |
13 | 13 |
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) { |
14 | 14 |
var toVolume bool |
15 |
- parser := volume.NewParser(container.Platform) |
|
15 |
+ parser := volume.NewParser(container.OS) |
|
16 | 16 |
for _, mnt := range container.MountPoints { |
17 | 17 |
if toVolume = parser.HasResource(mnt, absPath); toVolume { |
18 | 18 |
if mnt.RW { |
... | ... |
@@ -13,6 +13,7 @@ import ( |
13 | 13 |
"github.com/docker/docker/daemon/network" |
14 | 14 |
volumestore "github.com/docker/docker/volume/store" |
15 | 15 |
"github.com/docker/go-connections/nat" |
16 |
+ specs "github.com/opencontainers/image-spec/specs-go/v1" |
|
16 | 17 |
) |
17 | 18 |
|
18 | 19 |
// ContainerInspect returns low-level information about a |
... | ... |
@@ -171,7 +172,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con |
171 | 171 |
Name: container.Name, |
172 | 172 |
RestartCount: container.RestartCount, |
173 | 173 |
Driver: container.Driver, |
174 |
- OS: container.OS, |
|
174 |
+ Platform: specs.Platform{OS: container.OS}, |
|
175 | 175 |
MountLabel: container.MountLabel, |
176 | 176 |
ProcessLabel: container.ProcessLabel, |
177 | 177 |
ExecIDs: container.GetExecIDs(), |
... | ... |
@@ -12,6 +12,7 @@ import ( |
12 | 12 |
"time" |
13 | 13 |
|
14 | 14 |
"github.com/docker/distribution/reference" |
15 |
+ "github.com/docker/distribution/registry/client/auth" |
|
15 | 16 |
"github.com/docker/distribution/registry/client/transport" |
16 | 17 |
"github.com/docker/docker/distribution/metadata" |
17 | 18 |
"github.com/docker/docker/distribution/xfer" |
... | ... |
@@ -68,7 +69,9 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, platform strin |
68 | 68 |
return nil |
69 | 69 |
} |
70 | 70 |
|
71 |
-func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error { |
|
71 |
+// Note use auth.Scope rather than reference.Named due to this warning causing Jenkins CI to fail: |
|
72 |
+// warning: ref can be github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.Scope (interfacer) |
|
73 |
+func (p *v1Puller) pullRepository(ctx context.Context, ref auth.Scope) error { |
|
72 | 74 |
progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name()) |
73 | 75 |
|
74 | 76 |
tagged, isTagged := ref.(reference.NamedTagged) |
... | ... |
@@ -510,7 +510,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv |
510 | 510 |
|
511 | 511 |
// Early bath if the requested OS doesn't match that of the configuration. |
512 | 512 |
// This avoids doing the download, only to potentially fail later. |
513 |
- if !strings.EqualFold(string(configOS), requestedOS) { |
|
513 |
+ if !strings.EqualFold(configOS, requestedOS) { |
|
514 | 514 |
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS) |
515 | 515 |
} |
516 | 516 |
|
... | ... |
@@ -651,7 +651,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s |
651 | 651 |
} |
652 | 652 |
|
653 | 653 |
if configJSON == nil { |
654 |
- configJSON, configRootFS, configOS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) |
|
654 |
+ configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) |
|
655 | 655 |
if err == nil && configRootFS == nil { |
656 | 656 |
err = errRootFSInvalid |
657 | 657 |
} |
... | ... |
@@ -723,7 +723,7 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf |
723 | 723 |
|
724 | 724 |
logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), os, runtime.GOARCH) |
725 | 725 |
|
726 |
- manifestMatches := filterManifests(mfstList.Manifests) |
|
726 |
+ manifestMatches := filterManifests(mfstList.Manifests, os) |
|
727 | 727 |
|
728 | 728 |
if len(manifestMatches) == 0 { |
729 | 729 |
errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", os, runtime.GOARCH) |
... | ... |
@@ -16,13 +16,13 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo |
16 | 16 |
return blobs.Open(ctx, ld.digest) |
17 | 17 |
} |
18 | 18 |
|
19 |
-func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor { |
|
19 |
+func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor { |
|
20 | 20 |
var matches []manifestlist.ManifestDescriptor |
21 | 21 |
for _, manifestDescriptor := range manifests { |
22 |
- if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS { |
|
22 |
+ if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os { |
|
23 | 23 |
matches = append(matches, manifestDescriptor) |
24 | 24 |
|
25 |
- logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) |
|
25 |
+ logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) |
|
26 | 26 |
} |
27 | 27 |
} |
28 | 28 |
return matches |
... | ... |
@@ -62,29 +62,28 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo |
62 | 62 |
return rsc, err |
63 | 63 |
} |
64 | 64 |
|
65 |
-func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor { |
|
66 |
- version := system.GetOSVersion() |
|
67 |
- |
|
68 |
- // TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode. |
|
69 |
- lookingForOS := runtime.GOOS |
|
70 |
- osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) |
|
71 |
- if system.LCOWSupported() { |
|
72 |
- lookingForOS = "linux" |
|
73 |
- osVersion = "" |
|
65 |
+func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor { |
|
66 |
+ osVersion := "" |
|
67 |
+ if os == "windows" { |
|
68 |
+ version := system.GetOSVersion() |
|
69 |
+ osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build) |
|
70 |
+ logrus.Debugf("will only match entries with version %s", osVersion) |
|
74 | 71 |
} |
75 | 72 |
|
76 | 73 |
var matches []manifestlist.ManifestDescriptor |
77 | 74 |
for _, manifestDescriptor := range manifests { |
78 |
- if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS { |
|
79 |
- if lookingForOS == "windows" && !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) { |
|
75 |
+ if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os { |
|
76 |
+ if os == "windows" && !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) { |
|
77 |
+ logrus.Debugf("skipping %s", manifestDescriptor.Platform.OSVersion) |
|
80 | 78 |
continue |
81 | 79 |
} |
82 | 80 |
matches = append(matches, manifestDescriptor) |
83 |
- |
|
84 |
- logrus.Debugf("found match for %s/%s with media type %s, digest %s", lookingForOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) |
|
81 |
+ logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String()) |
|
85 | 82 |
} |
86 | 83 |
} |
87 |
- sort.Stable(manifestsByVersion(matches)) |
|
84 |
+ if os == "windows" { |
|
85 |
+ sort.Stable(manifestsByVersion(matches)) |
|
86 |
+ } |
|
88 | 87 |
return matches |
89 | 88 |
} |
90 | 89 |
|
... | ... |
@@ -62,7 +62,7 @@ func TestImage(t *testing.T) { |
62 | 62 |
Domainname: "domain", |
63 | 63 |
User: "root", |
64 | 64 |
} |
65 |
- platform := runtime.GOOS |
|
65 |
+ os := runtime.GOOS |
|
66 | 66 |
|
67 | 67 |
img := &Image{ |
68 | 68 |
V1Image: V1Image{ |
... | ... |
@@ -73,19 +73,19 @@ func TestImage(t *testing.T) { |
73 | 73 |
|
74 | 74 |
assert.Equal(t, cid, img.ImageID()) |
75 | 75 |
assert.Equal(t, cid, img.ID().String()) |
76 |
- assert.Equal(t, platform, img.Platform()) |
|
76 |
+ assert.Equal(t, os, img.OperatingSystem()) |
|
77 | 77 |
assert.Equal(t, config, img.RunConfig()) |
78 | 78 |
} |
79 | 79 |
|
80 |
-func TestImagePlatformNotEmpty(t *testing.T) { |
|
81 |
- platform := "platform" |
|
80 |
+func TestImageOSNotEmpty(t *testing.T) { |
|
81 |
+ os := "os" |
|
82 | 82 |
img := &Image{ |
83 | 83 |
V1Image: V1Image{ |
84 |
- OS: platform, |
|
84 |
+ OS: os, |
|
85 | 85 |
}, |
86 | 86 |
OSVersion: "osversion", |
87 | 87 |
} |
88 |
- assert.Equal(t, platform, img.Platform()) |
|
88 |
+ assert.Equal(t, os, img.OperatingSystem()) |
|
89 | 89 |
} |
90 | 90 |
|
91 | 91 |
func TestNewChildImageFromImageWithRootFS(t *testing.T) { |
... | ... |
@@ -8,21 +8,6 @@ import ( |
8 | 8 |
specs "github.com/opencontainers/image-spec/specs-go/v1" |
9 | 9 |
) |
10 | 10 |
|
11 |
-// IsPlatformEmpty determines if an OCI image-spec platform structure is not populated. |
|
12 |
-// TODO This is a temporary function - can be replaced by parsing from |
|
13 |
-// https://github.com/containerd/containerd/pull/1403/files at a later date. |
|
14 |
-// @jhowardmsft |
|
15 |
-func IsPlatformEmpty(platform specs.Platform) bool { |
|
16 |
- if platform.Architecture == "" && |
|
17 |
- platform.OS == "" && |
|
18 |
- len(platform.OSFeatures) == 0 && |
|
19 |
- platform.OSVersion == "" && |
|
20 |
- platform.Variant == "" { |
|
21 |
- return true |
|
22 |
- } |
|
23 |
- return false |
|
24 |
-} |
|
25 |
- |
|
26 | 11 |
// ValidatePlatform determines if a platform structure is valid. |
27 | 12 |
// TODO This is a temporary function - can be replaced by parsing from |
28 | 13 |
// https://github.com/containerd/containerd/pull/1403/files at a later date. |
... | ... |
@@ -30,7 +15,9 @@ func IsPlatformEmpty(platform specs.Platform) bool { |
30 | 30 |
func ValidatePlatform(platform *specs.Platform) error { |
31 | 31 |
platform.Architecture = strings.ToLower(platform.Architecture) |
32 | 32 |
platform.OS = strings.ToLower(platform.OS) |
33 |
- if platform.Architecture != "" && platform.Architecture != runtime.GOARCH { |
|
33 |
+ // Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do |
|
34 |
+ // not support anything except operating system. |
|
35 |
+ if platform.Architecture != "" { |
|
34 | 36 |
return fmt.Errorf("invalid platform architecture %q", platform.Architecture) |
35 | 37 |
} |
36 | 38 |
if platform.OS != "" { |