Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -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 != "" {
|