Skip to content

Commit d98ecf2

Browse files
author
John Howard
committed
LCOW: API change JSON header to string POST parameter
Signed-off-by: John Howard <jhoward@microsoft.com>
1 parent 0380fbf commit d98ecf2

27 files changed

+157
-189
lines changed

api/server/httputils/httputils.go

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,11 @@
11
package httputils
22

33
import (
4-
"encoding/json"
5-
"fmt"
64
"io"
75
"mime"
86
"net/http"
9-
"runtime"
107
"strings"
118

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"
159
"github.com/pkg/errors"
1610
"github.com/sirupsen/logrus"
1711
"golang.org/x/net/context"
@@ -115,27 +109,3 @@ func matchesContentType(contentType, expectedType string) bool {
115109
}
116110
return err == nil && mimetype == expectedType
117111
}
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-
}

api/server/router/build/build_routes.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"fmt"
88
"io"
99
"net/http"
10+
"os"
1011
"runtime"
1112
"strconv"
1213
"strings"
@@ -20,6 +21,7 @@ import (
2021
"github.com/docker/docker/pkg/ioutils"
2122
"github.com/docker/docker/pkg/progress"
2223
"github.com/docker/docker/pkg/streamformatter"
24+
"github.com/docker/docker/pkg/system"
2325
units "github.com/docker/go-units"
2426
"github.com/pkg/errors"
2527
"github.com/sirupsen/logrus"
@@ -67,6 +69,24 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
6769
options.Squash = httputils.BoolValue(r, "squash")
6870
options.Target = r.FormValue("target")
6971
options.RemoteContext = r.FormValue("remote")
72+
if versions.GreaterThanOrEqualTo(version, "1.32") {
73+
// TODO @jhowardmsft. The following environment variable is an interim
74+
// measure to allow the daemon to have a default platform if omitted by
75+
// the client. This allows LCOW and WCOW to work with a down-level CLI
76+
// for a short period of time, as the CLI changes can't be merged
77+
// until after the daemon changes have been merged. Once the CLI is
78+
// updated, this can be removed. PR for CLI is currently in
79+
// https://github.com/docker/cli/pull/474.
80+
apiPlatform := r.FormValue("platform")
81+
if system.LCOWSupported() && apiPlatform == "" {
82+
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
83+
}
84+
p := system.ParsePlatform(apiPlatform)
85+
if err := system.ValidatePlatform(p); err != nil {
86+
return nil, validationError{fmt.Errorf("invalid platform: %s", err)}
87+
}
88+
options.Platform = p.OS
89+
}
7090

7191
if r.Form.Get("shmsize") != "" {
7292
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
@@ -87,12 +107,6 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
87107
return nil, validationError{fmt.Errorf("The daemon on this platform does not support setting security options on build")}
88108
}
89109

90-
platform, err := httputils.GetRequestedPlatform(ctx, r)
91-
if err != nil {
92-
return nil, err
93-
}
94-
options.Platform = *platform
95-
96110
var buildUlimits = []*units.Ulimit{}
97111
ulimitsJSON := r.FormValue("ulimits")
98112
if ulimitsJSON != "" {

api/server/router/image/image_routes.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package image
33
import (
44
"encoding/base64"
55
"encoding/json"
6+
"fmt"
67
"io"
78
"net/http"
9+
"os"
810
"strconv"
911
"strings"
1012

@@ -16,6 +18,7 @@ import (
1618
"github.com/docker/docker/api/types/versions"
1719
"github.com/docker/docker/pkg/ioutils"
1820
"github.com/docker/docker/pkg/streamformatter"
21+
"github.com/docker/docker/pkg/system"
1922
"github.com/docker/docker/registry"
2023
specs "github.com/opencontainers/image-spec/specs-go/v1"
2124
"github.com/pkg/errors"
@@ -70,6 +73,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
7073

7174
// Creates an image from Pull or from Import
7275
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
76+
7377
if err := httputils.ParseForm(r); err != nil {
7478
return err
7579
}
@@ -87,7 +91,25 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
8791

8892
w.Header().Set("Content-Type", "application/json")
8993

90-
platform, err = httputils.GetRequestedPlatform(ctx, r)
94+
version := httputils.VersionFromContext(ctx)
95+
if versions.GreaterThanOrEqualTo(version, "1.32") {
96+
// TODO @jhowardmsft. The following environment variable is an interim
97+
// measure to allow the daemon to have a default platform if omitted by
98+
// the client. This allows LCOW and WCOW to work with a down-level CLI
99+
// for a short period of time, as the CLI changes can't be merged
100+
// until after the daemon changes have been merged. Once the CLI is
101+
// updated, this can be removed. PR for CLI is currently in
102+
// https://github.com/docker/cli/pull/474.
103+
apiPlatform := r.FormValue("platform")
104+
if system.LCOWSupported() && apiPlatform == "" {
105+
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
106+
}
107+
platform = system.ParsePlatform(apiPlatform)
108+
if err = system.ValidatePlatform(platform); err != nil {
109+
err = fmt.Errorf("invalid platform: %s", err)
110+
}
111+
}
112+
91113
if err == nil {
92114
if image != "" { //pull
93115
metaHeaders := map[string][]string{}

api/swagger.yaml

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6181,17 +6181,9 @@ paths:
61816181
61826182
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.
61836183
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]]"
61956187
type: "string"
61966188
default: ""
61976189
responses:
@@ -6275,17 +6267,9 @@ paths:
62756267
in: "header"
62766268
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
62776269
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-
```
6270+
- name: "platform"
6271+
in: "query"
6272+
description: "Platform in the format os[/arch[/variant]]"
62896273
type: "string"
62906274
default: ""
62916275
tags: ["Image"]

api/types/client.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import (
88
"github.com/docker/docker/api/types/container"
99
"github.com/docker/docker/api/types/filters"
1010
units "github.com/docker/go-units"
11-
specs "github.com/opencontainers/image-spec/specs-go/v1"
1211
)
1312

1413
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
@@ -180,7 +179,7 @@ type ImageBuildOptions struct {
180179
ExtraHosts []string // List of extra hosts
181180
Target string
182181
SessionID string
183-
Platform specs.Platform
182+
Platform string
184183
}
185184

186185
// ImageBuildResponse holds information
@@ -193,8 +192,8 @@ type ImageBuildResponse struct {
193192

194193
// ImageCreateOptions holds information to create images.
195194
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.
195+
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
196+
Platform string // Platform is the target platform of the image if it needs to be pulled from the registry.
198197
}
199198

200199
// ImageImportSource holds source information for ImageImport
@@ -205,9 +204,10 @@ type ImageImportSource struct {
205204

206205
// ImageImportOptions holds information to import images from the client host.
207206
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
207+
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
208+
Message string // Message is the message to tag the image with
209+
Changes []string // Changes are the raw changes to apply to this image
210+
Platform string // Platform is the target platform of the image
211211
}
212212

213213
// ImageListOptions holds parameters to filter the list of images with.
@@ -228,7 +228,7 @@ type ImagePullOptions struct {
228228
All bool
229229
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
230230
PrivilegeFunc RequestPrivilegeFunc
231-
Platform specs.Platform
231+
Platform string
232232
}
233233

234234
// RequestPrivilegeFunc is a function interface that

api/types/types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/docker/docker/api/types/registry"
1616
"github.com/docker/docker/api/types/swarm"
1717
"github.com/docker/go-connections/nat"
18+
specs "github.com/opencontainers/image-spec/specs-go/v1"
1819
)
1920

2021
// RootFS returns Image's RootFS description including the layer IDs.
@@ -327,7 +328,7 @@ type ContainerJSONBase struct {
327328
Name string
328329
RestartCount int
329330
Driver string
330-
OS string
331+
Platform specs.Platform
331332
MountLabel string
332333
ProcessLabel string
333334
AppArmorProfile string

builder/dockerfile/builder.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"github.com/docker/docker/pkg/idtools"
2121
"github.com/docker/docker/pkg/streamformatter"
2222
"github.com/docker/docker/pkg/stringid"
23+
"github.com/docker/docker/pkg/system"
2324
"github.com/moby/buildkit/session"
2425
"github.com/pkg/errors"
2526
"github.com/sirupsen/logrus"
@@ -102,15 +103,16 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
102103
}
103104

104105
os := runtime.GOOS
106+
optionsPlatform := system.ParsePlatform(config.Options.Platform)
105107
if dockerfile.OS != "" {
106-
if config.Options.Platform.OS != "" && config.Options.Platform.OS != dockerfile.OS {
108+
if optionsPlatform.OS != "" && optionsPlatform.OS != dockerfile.OS {
107109
return nil, fmt.Errorf("invalid platform")
108110
}
109111
os = dockerfile.OS
110-
} else if config.Options.Platform.OS != "" {
111-
os = config.Options.Platform.OS
112+
} else if optionsPlatform.OS != "" {
113+
os = optionsPlatform.OS
112114
}
113-
config.Options.Platform.OS = os
115+
config.Options.Platform = os
114116
dockerfile.OS = os
115117

116118
builderOptions := builderOptions{

builder/dockerfile/copy.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
8282
pathCache: req.builder.pathCache,
8383
download: download,
8484
imageSource: imageSource,
85-
platform: req.builder.options.Platform.OS,
85+
platform: req.builder.options.Platform,
8686
}
8787
}
8888

builder/dockerfile/dispatchers.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -194,11 +194,6 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
194194
return nil
195195
}
196196

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-
202197
func (d *dispatchRequest) getExpandedImageName(shlex *ShellLex, name string) (string, error) {
203198
substitutionArgs := []string{}
204199
for key, value := range d.state.buildArgs.GetAllMeta() {
@@ -223,16 +218,17 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
223218
imageImage := &image.Image{}
224219
imageImage.OS = runtime.GOOS
225220
if runtime.GOOS == "windows" {
226-
switch d.builder.options.Platform.OS {
227-
case "windows":
221+
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
222+
switch optionsOS {
223+
case "windows", "":
228224
return nil, errors.New("Windows does not support FROM scratch")
229225
case "linux":
230226
if !system.LCOWSupported() {
231227
return nil, errors.New("Linux containers are not supported on this system")
232228
}
233229
imageImage.OS = "linux"
234230
default:
235-
return nil, errors.Errorf("operating system %q is not supported", d.builder.options.Platform.OS)
231+
return nil, errors.Errorf("operating system %q is not supported", optionsOS)
236232
}
237233
}
238234
return builder.Image(imageImage), nil
@@ -264,7 +260,8 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
264260
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
265261
runConfig := d.state.runConfig
266262
var err error
267-
runConfig.WorkingDir, err = normalizeWorkdir(d.builder.options.Platform.OS, runConfig.WorkingDir, c.Path)
263+
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
264+
runConfig.WorkingDir, err = normalizeWorkdir(optionsOS, runConfig.WorkingDir, c.Path)
268265
if err != nil {
269266
return err
270267
}
@@ -280,7 +277,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
280277
}
281278

282279
comment := "WORKDIR " + runConfig.WorkingDir
283-
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.builder.options.Platform.OS))
280+
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, optionsOS))
284281
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
285282
if err != nil || containerID == "" {
286283
return err
@@ -313,7 +310,8 @@ func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container
313310
func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
314311

315312
stateRunConfig := d.state.runConfig
316-
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.builder.options.Platform.OS)
313+
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
314+
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, optionsOS)
317315
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env)
318316

319317
saveCmd := cmdFromArgs
@@ -390,7 +388,8 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S
390388
//
391389
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
392390
runConfig := d.state.runConfig
393-
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
391+
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
392+
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
394393
runConfig.Cmd = cmd
395394
// set config as already being escaped, this prevents double escaping on windows
396395
runConfig.ArgsEscaped = true
@@ -433,7 +432,8 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
433432
//
434433
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
435434
runConfig := d.state.runConfig
436-
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
435+
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
436+
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
437437
runConfig.Entrypoint = cmd
438438
if !d.state.cmdSet {
439439
runConfig.Cmd = nil

0 commit comments

Comments
 (0)