mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
LCOW: API change JSON header to string POST parameter
Signed-off-by: John Howard <jhoward@microsoft.com>
This commit is contained in:
parent
0380fbff37
commit
d98ecf2d6c
27 changed files with 157 additions and 189 deletions
|
@ -1,17 +1,11 @@
|
|||
package httputils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"mime"
|
||||
"net/http"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -115,27 +109,3 @@ func matchesContentType(contentType, expectedType string) bool {
|
|||
}
|
||||
return err == nil && mimetype == expectedType
|
||||
}
|
||||
|
||||
// GetRequestedPlatform extracts an optional platform structure from an HTTP request header
|
||||
func GetRequestedPlatform(ctx context.Context, r *http.Request) (*specs.Platform, error) {
|
||||
platform := &specs.Platform{}
|
||||
version := VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
requestedPlatform := r.Header.Get("X-Requested-Platform")
|
||||
if requestedPlatform != "" {
|
||||
if err := json.Unmarshal([]byte(requestedPlatform), platform); err != nil {
|
||||
return nil, fmt.Errorf("invalid X-Requested-Platform header: %s", err)
|
||||
}
|
||||
}
|
||||
if err := system.ValidatePlatform(platform); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if platform.OS == "" {
|
||||
platform.OS = runtime.GOOS
|
||||
}
|
||||
if platform.Architecture == "" {
|
||||
platform.Architecture = runtime.GOARCH
|
||||
}
|
||||
return platform, nil
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
@ -20,6 +21,7 @@ import (
|
|||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/progress"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
units "github.com/docker/go-units"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -67,6 +69,24 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
|||
options.Squash = httputils.BoolValue(r, "squash")
|
||||
options.Target = r.FormValue("target")
|
||||
options.RemoteContext = r.FormValue("remote")
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
// TODO @jhowardmsft. The following environment variable is an interim
|
||||
// measure to allow the daemon to have a default platform if omitted by
|
||||
// the client. This allows LCOW and WCOW to work with a down-level CLI
|
||||
// for a short period of time, as the CLI changes can't be merged
|
||||
// until after the daemon changes have been merged. Once the CLI is
|
||||
// updated, this can be removed. PR for CLI is currently in
|
||||
// https://github.com/docker/cli/pull/474.
|
||||
apiPlatform := r.FormValue("platform")
|
||||
if system.LCOWSupported() && apiPlatform == "" {
|
||||
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
|
||||
}
|
||||
p := system.ParsePlatform(apiPlatform)
|
||||
if err := system.ValidatePlatform(p); err != nil {
|
||||
return nil, validationError{fmt.Errorf("invalid platform: %s", err)}
|
||||
}
|
||||
options.Platform = p.OS
|
||||
}
|
||||
|
||||
if r.Form.Get("shmsize") != "" {
|
||||
shmSize, err := strconv.ParseInt(r.Form.Get("shmsize"), 10, 64)
|
||||
|
@ -87,12 +107,6 @@ func newImageBuildOptions(ctx context.Context, r *http.Request) (*types.ImageBui
|
|||
return nil, validationError{fmt.Errorf("The daemon on this platform does not support setting security options on build")}
|
||||
}
|
||||
|
||||
platform, err := httputils.GetRequestedPlatform(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
options.Platform = *platform
|
||||
|
||||
var buildUlimits = []*units.Ulimit{}
|
||||
ulimitsJSON := r.FormValue("ulimits")
|
||||
if ulimitsJSON != "" {
|
||||
|
|
|
@ -3,8 +3,10 @@ package image
|
|||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
@ -16,6 +18,7 @@ import (
|
|||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/docker/docker/pkg/ioutils"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/docker/registry"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/pkg/errors"
|
||||
|
@ -70,6 +73,7 @@ func (s *imageRouter) postCommit(ctx context.Context, w http.ResponseWriter, r *
|
|||
|
||||
// Creates an image from Pull or from Import
|
||||
func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWriter, r *http.Request, vars map[string]string) error {
|
||||
|
||||
if err := httputils.ParseForm(r); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -87,7 +91,25 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
|
|||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
||||
platform, err = httputils.GetRequestedPlatform(ctx, r)
|
||||
version := httputils.VersionFromContext(ctx)
|
||||
if versions.GreaterThanOrEqualTo(version, "1.32") {
|
||||
// TODO @jhowardmsft. The following environment variable is an interim
|
||||
// measure to allow the daemon to have a default platform if omitted by
|
||||
// the client. This allows LCOW and WCOW to work with a down-level CLI
|
||||
// for a short period of time, as the CLI changes can't be merged
|
||||
// until after the daemon changes have been merged. Once the CLI is
|
||||
// updated, this can be removed. PR for CLI is currently in
|
||||
// https://github.com/docker/cli/pull/474.
|
||||
apiPlatform := r.FormValue("platform")
|
||||
if system.LCOWSupported() && apiPlatform == "" {
|
||||
apiPlatform = os.Getenv("LCOW_API_PLATFORM_IF_OMITTED")
|
||||
}
|
||||
platform = system.ParsePlatform(apiPlatform)
|
||||
if err = system.ValidatePlatform(platform); err != nil {
|
||||
err = fmt.Errorf("invalid platform: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if image != "" { //pull
|
||||
metaHeaders := map[string][]string{}
|
||||
|
|
|
@ -6181,17 +6181,9 @@ paths:
|
|||
|
||||
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.
|
||||
type: "string"
|
||||
- name: "X-Requested-Platform"
|
||||
in: "header"
|
||||
description: |
|
||||
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:
|
||||
|
||||
```
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
```
|
||||
- name: "platform"
|
||||
in: "query"
|
||||
description: "Platform in the format os[/arch[/variant]]"
|
||||
type: "string"
|
||||
default: ""
|
||||
responses:
|
||||
|
@ -6275,17 +6267,9 @@ paths:
|
|||
in: "header"
|
||||
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
|
||||
type: "string"
|
||||
- name: "X-Requested-Platform"
|
||||
in: "header"
|
||||
description: |
|
||||
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:
|
||||
|
||||
```
|
||||
{
|
||||
"architecture": "amd64",
|
||||
"os": "linux"
|
||||
}
|
||||
```
|
||||
- name: "platform"
|
||||
in: "query"
|
||||
description: "Platform in the format os[/arch[/variant]]"
|
||||
type: "string"
|
||||
default: ""
|
||||
tags: ["Image"]
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
units "github.com/docker/go-units"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// CheckpointCreateOptions holds parameters to create a checkpoint from a container
|
||||
|
@ -180,7 +179,7 @@ type ImageBuildOptions struct {
|
|||
ExtraHosts []string // List of extra hosts
|
||||
Target string
|
||||
SessionID string
|
||||
Platform specs.Platform
|
||||
Platform string
|
||||
}
|
||||
|
||||
// ImageBuildResponse holds information
|
||||
|
@ -193,8 +192,8 @@ type ImageBuildResponse struct {
|
|||
|
||||
// ImageCreateOptions holds information to create images.
|
||||
type ImageCreateOptions struct {
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
|
||||
Platform specs.Platform // Platform is the target platform of the image if it needs to be pulled from the registry.
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry.
|
||||
Platform string // Platform is the target platform of the image if it needs to be pulled from the registry.
|
||||
}
|
||||
|
||||
// ImageImportSource holds source information for ImageImport
|
||||
|
@ -205,9 +204,10 @@ type ImageImportSource struct {
|
|||
|
||||
// ImageImportOptions holds information to import images from the client host.
|
||||
type ImageImportOptions struct {
|
||||
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||
Message string // Message is the message to tag the image with
|
||||
Changes []string // Changes are the raw changes to apply to this image
|
||||
Tag string // Tag is the name to tag this image with. This attribute is deprecated.
|
||||
Message string // Message is the message to tag the image with
|
||||
Changes []string // Changes are the raw changes to apply to this image
|
||||
Platform string // Platform is the target platform of the image
|
||||
}
|
||||
|
||||
// ImageListOptions holds parameters to filter the list of images with.
|
||||
|
@ -228,7 +228,7 @@ type ImagePullOptions struct {
|
|||
All bool
|
||||
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
|
||||
PrivilegeFunc RequestPrivilegeFunc
|
||||
Platform specs.Platform
|
||||
Platform string
|
||||
}
|
||||
|
||||
// RequestPrivilegeFunc is a function interface that
|
||||
|
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/docker/docker/api/types/registry"
|
||||
"github.com/docker/docker/api/types/swarm"
|
||||
"github.com/docker/go-connections/nat"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// RootFS returns Image's RootFS description including the layer IDs.
|
||||
|
@ -327,7 +328,7 @@ type ContainerJSONBase struct {
|
|||
Name string
|
||||
RestartCount int
|
||||
Driver string
|
||||
OS string
|
||||
Platform specs.Platform
|
||||
MountLabel string
|
||||
ProcessLabel string
|
||||
AppArmorProfile string
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/docker/docker/pkg/idtools"
|
||||
"github.com/docker/docker/pkg/streamformatter"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/moby/buildkit/session"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
@ -102,15 +103,16 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
|
|||
}
|
||||
|
||||
os := runtime.GOOS
|
||||
optionsPlatform := system.ParsePlatform(config.Options.Platform)
|
||||
if dockerfile.OS != "" {
|
||||
if config.Options.Platform.OS != "" && config.Options.Platform.OS != dockerfile.OS {
|
||||
if optionsPlatform.OS != "" && optionsPlatform.OS != dockerfile.OS {
|
||||
return nil, fmt.Errorf("invalid platform")
|
||||
}
|
||||
os = dockerfile.OS
|
||||
} else if config.Options.Platform.OS != "" {
|
||||
os = config.Options.Platform.OS
|
||||
} else if optionsPlatform.OS != "" {
|
||||
os = optionsPlatform.OS
|
||||
}
|
||||
config.Options.Platform.OS = os
|
||||
config.Options.Platform = os
|
||||
dockerfile.OS = os
|
||||
|
||||
builderOptions := builderOptions{
|
||||
|
|
|
@ -82,7 +82,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
|
|||
pathCache: req.builder.pathCache,
|
||||
download: download,
|
||||
imageSource: imageSource,
|
||||
platform: req.builder.options.Platform.OS,
|
||||
platform: req.builder.options.Platform,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -194,11 +194,6 @@ func dispatchTriggeredOnBuild(d dispatchRequest, triggers []string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// scratchImage is used as a token for the empty base image. It uses buildStage
|
||||
// as a convenient implementation of builder.Image, but is not actually a
|
||||
// buildStage.
|
||||
var scratchImage builder.Image = &image.Image{}
|
||||
|
||||
func (d *dispatchRequest) getExpandedImageName(shlex *ShellLex, name string) (string, error) {
|
||||
substitutionArgs := []string{}
|
||||
for key, value := range d.state.buildArgs.GetAllMeta() {
|
||||
|
@ -223,8 +218,9 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
|||
imageImage := &image.Image{}
|
||||
imageImage.OS = runtime.GOOS
|
||||
if runtime.GOOS == "windows" {
|
||||
switch d.builder.options.Platform.OS {
|
||||
case "windows":
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
switch optionsOS {
|
||||
case "windows", "":
|
||||
return nil, errors.New("Windows does not support FROM scratch")
|
||||
case "linux":
|
||||
if !system.LCOWSupported() {
|
||||
|
@ -232,7 +228,7 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
|
|||
}
|
||||
imageImage.OS = "linux"
|
||||
default:
|
||||
return nil, errors.Errorf("operating system %q is not supported", d.builder.options.Platform.OS)
|
||||
return nil, errors.Errorf("operating system %q is not supported", optionsOS)
|
||||
}
|
||||
}
|
||||
return builder.Image(imageImage), nil
|
||||
|
@ -264,7 +260,8 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
|
|||
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
var err error
|
||||
runConfig.WorkingDir, err = normalizeWorkdir(d.builder.options.Platform.OS, runConfig.WorkingDir, c.Path)
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
runConfig.WorkingDir, err = normalizeWorkdir(optionsOS, runConfig.WorkingDir, c.Path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -280,7 +277,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
|
|||
}
|
||||
|
||||
comment := "WORKDIR " + runConfig.WorkingDir
|
||||
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.builder.options.Platform.OS))
|
||||
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, optionsOS))
|
||||
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
|
||||
if err != nil || containerID == "" {
|
||||
return err
|
||||
|
@ -313,7 +310,8 @@ func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container
|
|||
func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
|
||||
|
||||
stateRunConfig := d.state.runConfig
|
||||
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.builder.options.Platform.OS)
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, optionsOS)
|
||||
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env)
|
||||
|
||||
saveCmd := cmdFromArgs
|
||||
|
@ -390,7 +388,8 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S
|
|||
//
|
||||
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
|
||||
runConfig.Cmd = cmd
|
||||
// set config as already being escaped, this prevents double escaping on windows
|
||||
runConfig.ArgsEscaped = true
|
||||
|
@ -433,7 +432,8 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
|
|||
//
|
||||
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
|
||||
runConfig := d.state.runConfig
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, optionsOS)
|
||||
runConfig.Entrypoint = cmd
|
||||
if !d.state.cmdSet {
|
||||
runConfig.Cmd = nil
|
||||
|
|
|
@ -14,7 +14,6 @@ import (
|
|||
"github.com/docker/docker/builder/dockerfile/instructions"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/docker/go-connections/nat"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
@ -23,13 +22,13 @@ func newBuilderWithMockBackend() *Builder {
|
|||
mockBackend := &MockBackend{}
|
||||
ctx := context.Background()
|
||||
b := &Builder{
|
||||
options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}},
|
||||
options: &types.ImageBuildOptions{Platform: runtime.GOOS},
|
||||
docker: mockBackend,
|
||||
Stdout: new(bytes.Buffer),
|
||||
clientCtx: ctx,
|
||||
disableCommit: true,
|
||||
imageSources: newImageSources(ctx, builderOptions{
|
||||
Options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}},
|
||||
Options: &types.ImageBuildOptions{Platform: runtime.GOOS},
|
||||
Backend: mockBackend,
|
||||
}),
|
||||
imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false),
|
||||
|
|
|
@ -34,7 +34,8 @@ import (
|
|||
|
||||
func dispatch(d dispatchRequest, cmd instructions.Command) error {
|
||||
if c, ok := cmd.(instructions.PlatformSpecific); ok {
|
||||
err := c.CheckPlatform(d.builder.options.Platform.OS)
|
||||
optionsOS := system.ParsePlatform(d.builder.options.Platform).OS
|
||||
err := c.CheckPlatform(optionsOS)
|
||||
if err != nil {
|
||||
return validationError{err}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/docker/docker/builder"
|
||||
"github.com/docker/docker/builder/remotecontext"
|
||||
dockerimage "github.com/docker/docker/image"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/net/context"
|
||||
|
@ -30,11 +31,12 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
|
|||
pullOption = backend.PullOptionPreferLocal
|
||||
}
|
||||
}
|
||||
optionsPlatform := system.ParsePlatform(options.Options.Platform)
|
||||
return options.Backend.GetImageAndReleasableLayer(ctx, idOrRef, backend.GetImageAndLayerOptions{
|
||||
PullOption: pullOption,
|
||||
AuthConfig: options.Options.AuthConfigs,
|
||||
Output: options.ProgressWriter.Output,
|
||||
OS: options.Options.Platform.OS,
|
||||
OS: optionsPlatform.OS,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -83,7 +83,8 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
|
|||
return errors.New("Please provide a source image with `from` prior to commit")
|
||||
}
|
||||
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, b.options.Platform.OS))
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, optionsPlatform.OS))
|
||||
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
|
@ -122,7 +123,8 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
|
|||
}
|
||||
|
||||
func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error {
|
||||
newLayer, err := imageMount.Layer().Commit(b.options.Platform.OS)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
newLayer, err := imageMount.Layer().Commit(optionsPlatform.OS)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -170,9 +172,10 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
|||
commentStr := fmt.Sprintf("%s %s%s in %s ", inst.cmdName, chownComment, srcHash, inst.dest)
|
||||
|
||||
// TODO: should this have been using origPaths instead of srcHash in the comment?
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
runConfigWithCommentCmd := copyRunConfig(
|
||||
state.runConfig,
|
||||
withCmdCommentString(commentStr, b.options.Platform.OS))
|
||||
withCmdCommentString(commentStr, optionsPlatform.OS))
|
||||
hit, err := b.probeCache(state, runConfigWithCommentCmd)
|
||||
if err != nil || hit {
|
||||
return err
|
||||
|
@ -183,7 +186,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
|
|||
return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
|
||||
}
|
||||
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform.OS)
|
||||
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -463,13 +466,15 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai
|
|||
}
|
||||
// Set a log config to override any default value set on the daemon
|
||||
hostConfig := &container.HostConfig{LogConfig: defaultLogConfig}
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS)
|
||||
return container.ID, err
|
||||
}
|
||||
|
||||
func (b *Builder) create(runConfig *container.Config) (string, error) {
|
||||
hostConfig := hostConfigFromOptions(b.options)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS)
|
||||
optionsPlatform := system.ParsePlatform(b.options.Platform)
|
||||
container, err := b.containerManager.Create(runConfig, hostConfig, optionsPlatform.OS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
|
|
@ -7,12 +7,12 @@ import (
|
|||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
// ImageBuild sends request to the daemon to build images.
|
||||
|
@ -31,18 +31,11 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
|
|||
}
|
||||
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf))
|
||||
|
||||
// TODO @jhowardmsft: system.IsPlatformEmpty is a temporary function. We need to move
|
||||
// (in the reasonably short future) to a package which supports all the platform
|
||||
// validation such as is proposed in https://github.com/containerd/containerd/pull/1403
|
||||
if !system.IsPlatformEmpty(options.Platform) {
|
||||
if options.Platform != "" {
|
||||
if err := cli.NewVersionError("1.32", "platform"); err != nil {
|
||||
return types.ImageBuildResponse{}, err
|
||||
}
|
||||
platformJSON, err := json.Marshal(options.Platform)
|
||||
if err != nil {
|
||||
return types.ImageBuildResponse{}, err
|
||||
}
|
||||
headers.Add("X-Requested-Platform", string(platformJSON[:]))
|
||||
query.Set("platform", options.Platform)
|
||||
}
|
||||
headers.Set("Content-Type", "application/x-tar")
|
||||
|
||||
|
@ -138,5 +131,8 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
|
|||
if options.SessionID != "" {
|
||||
query.Set("session", options.SessionID)
|
||||
}
|
||||
if options.Platform != "" {
|
||||
query.Set("platform", strings.ToLower(options.Platform))
|
||||
}
|
||||
return query, nil
|
||||
}
|
||||
|
|
|
@ -1,16 +1,14 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ImageCreate creates a new image based in the parent options.
|
||||
|
@ -24,25 +22,17 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
|
|||
query := url.Values{}
|
||||
query.Set("fromImage", reference.FamiliarName(ref))
|
||||
query.Set("tag", getAPITagFromNamedRef(ref))
|
||||
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform)
|
||||
if options.Platform != "" {
|
||||
query.Set("platform", strings.ToLower(options.Platform))
|
||||
}
|
||||
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.body, nil
|
||||
}
|
||||
|
||||
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string, platform specs.Platform) (serverResponse, error) {
|
||||
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) {
|
||||
headers := map[string][]string{"X-Registry-Auth": {registryAuth}}
|
||||
|
||||
// TODO @jhowardmsft: system.IsPlatformEmpty is a temporary function. We need to move
|
||||
// (in the reasonably short future) to a package which supports all the platform
|
||||
// validation such as is proposed in https://github.com/containerd/containerd/pull/1403
|
||||
if !system.IsPlatformEmpty(platform) {
|
||||
platformJSON, err := json.Marshal(platform)
|
||||
if err != nil {
|
||||
return serverResponse{}, err
|
||||
}
|
||||
headers["X-Requested-Platform"] = []string{string(platformJSON[:])}
|
||||
}
|
||||
return cli.post(ctx, "/images/create", query, nil, headers)
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package client
|
|||
import (
|
||||
"io"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
|
@ -25,6 +26,9 @@ func (cli *Client) ImageImport(ctx context.Context, source types.ImageImportSour
|
|||
query.Set("repo", ref)
|
||||
query.Set("tag", options.Tag)
|
||||
query.Set("message", options.Message)
|
||||
if options.Platform != "" {
|
||||
query.Set("platform", strings.ToLower(options.Platform))
|
||||
}
|
||||
for _, change := range options.Changes {
|
||||
query.Add("changes", change)
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/pkg/system"
|
||||
)
|
||||
|
||||
// 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
|
|||
if !options.All {
|
||||
query.Set("tag", getAPITagFromNamedRef(ref))
|
||||
}
|
||||
|
||||
// TODO 1: Extend to include "and the platform is supported by the daemon".
|
||||
// This is dependent on https://github.com/moby/moby/pull/34628 though,
|
||||
// and the daemon returning the set of platforms it supports via the _ping
|
||||
// API endpoint.
|
||||
//
|
||||
// TODO 2: system.IsPlatformEmpty is a temporary function. We need to move
|
||||
// (in the reasonably short future) to a package which supports all the platform
|
||||
// validation such as is proposed in https://github.com/containerd/containerd/pull/1403
|
||||
//
|
||||
// @jhowardmsft.
|
||||
if !system.IsPlatformEmpty(options.Platform) {
|
||||
if err := cli.NewVersionError("1.32", "platform"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if options.Platform != "" {
|
||||
query.Set("platform", strings.ToLower(options.Platform))
|
||||
}
|
||||
|
||||
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform)
|
||||
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth)
|
||||
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
|
||||
newAuthHeader, privilegeErr := options.PrivilegeFunc()
|
||||
if privilegeErr != nil {
|
||||
return nil, privilegeErr
|
||||
}
|
||||
resp, err = cli.tryImageCreate(ctx, query, newAuthHeader, options.Platform)
|
||||
resp, err = cli.tryImageCreate(ctx, query, newAuthHeader)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -66,7 +66,7 @@ func (container *Container) BuildHostnameFile() error {
|
|||
func (container *Container) NetworkMounts() []Mount {
|
||||
var mounts []Mount
|
||||
shared := container.HostConfig.NetworkMode.IsContainer()
|
||||
parser := volume.NewParser(container.Platform)
|
||||
parser := volume.NewParser(container.OS)
|
||||
if container.ResolvConfPath != "" {
|
||||
if _, err := os.Stat(container.ResolvConfPath); err != nil {
|
||||
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
|
|||
// IpcMounts returns the list of IPC mounts
|
||||
func (container *Container) IpcMounts() []Mount {
|
||||
var mounts []Mount
|
||||
parser := volume.NewParser(container.Platform)
|
||||
parser := volume.NewParser(container.OS)
|
||||
|
||||
if container.HasMountFor("/dev/shm") {
|
||||
return mounts
|
||||
|
@ -429,7 +429,7 @@ func copyOwnership(source, destination string) error {
|
|||
|
||||
// TmpfsMounts returns the list of tmpfs mounts
|
||||
func (container *Container) TmpfsMounts() ([]Mount, error) {
|
||||
parser := volume.NewParser(container.Platform)
|
||||
parser := volume.NewParser(container.OS)
|
||||
var mounts []Mount
|
||||
for dest, data := range container.HostConfig.Tmpfs {
|
||||
mounts = append(mounts, Mount{
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
// cannot be configured with a read-only rootfs.
|
||||
func checkIfPathIsInAVolume(container *container.Container, absPath string) (bool, error) {
|
||||
var toVolume bool
|
||||
parser := volume.NewParser(container.Platform)
|
||||
parser := volume.NewParser(container.OS)
|
||||
for _, mnt := range container.MountPoints {
|
||||
if toVolume = parser.HasResource(mnt, absPath); toVolume {
|
||||
if mnt.RW {
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
"github.com/docker/docker/daemon/network"
|
||||
volumestore "github.com/docker/docker/volume/store"
|
||||
"github.com/docker/go-connections/nat"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// ContainerInspect returns low-level information about a
|
||||
|
@ -171,7 +172,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
|
|||
Name: container.Name,
|
||||
RestartCount: container.RestartCount,
|
||||
Driver: container.Driver,
|
||||
OS: container.OS,
|
||||
Platform: specs.Platform{OS: container.OS},
|
||||
MountLabel: container.MountLabel,
|
||||
ProcessLabel: container.ProcessLabel,
|
||||
ExecIDs: container.GetExecIDs(),
|
||||
|
|
|
@ -12,6 +12,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/docker/distribution/registry/client/auth"
|
||||
"github.com/docker/distribution/registry/client/transport"
|
||||
"github.com/docker/docker/distribution/metadata"
|
||||
"github.com/docker/docker/distribution/xfer"
|
||||
|
@ -68,7 +69,9 @@ func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, platform strin
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p *v1Puller) pullRepository(ctx context.Context, ref reference.Named) error {
|
||||
// Note use auth.Scope rather than reference.Named due to this warning causing Jenkins CI to fail:
|
||||
// warning: ref can be github.com/docker/docker/vendor/github.com/docker/distribution/registry/client/auth.Scope (interfacer)
|
||||
func (p *v1Puller) pullRepository(ctx context.Context, ref auth.Scope) error {
|
||||
progress.Message(p.config.ProgressOutput, "", "Pulling repository "+p.repoInfo.Name.Name())
|
||||
|
||||
tagged, isTagged := ref.(reference.NamedTagged)
|
||||
|
|
|
@ -510,7 +510,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
|
|||
|
||||
// Early bath if the requested OS doesn't match that of the configuration.
|
||||
// This avoids doing the download, only to potentially fail later.
|
||||
if !strings.EqualFold(string(configOS), requestedOS) {
|
||||
if !strings.EqualFold(configOS, requestedOS) {
|
||||
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
|
||||
}
|
||||
|
||||
|
@ -651,7 +651,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
|
|||
}
|
||||
|
||||
if configJSON == nil {
|
||||
configJSON, configRootFS, configOS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
||||
configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
|
||||
if err == nil && configRootFS == nil {
|
||||
err = errRootFSInvalid
|
||||
}
|
||||
|
@ -723,7 +723,7 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
|
|||
|
||||
logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a %s/%s match", ref, len(mfstList.Manifests), os, runtime.GOARCH)
|
||||
|
||||
manifestMatches := filterManifests(mfstList.Manifests)
|
||||
manifestMatches := filterManifests(mfstList.Manifests, os)
|
||||
|
||||
if len(manifestMatches) == 0 {
|
||||
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
|
|||
return blobs.Open(ctx, ld.digest)
|
||||
}
|
||||
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
|
||||
var matches []manifestlist.ManifestDescriptor
|
||||
for _, manifestDescriptor := range manifests {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == runtime.GOOS {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
|
||||
matches = append(matches, manifestDescriptor)
|
||||
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", runtime.GOOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
}
|
||||
}
|
||||
return matches
|
||||
|
|
|
@ -62,29 +62,28 @@ func (ld *v2LayerDescriptor) open(ctx context.Context) (distribution.ReadSeekClo
|
|||
return rsc, err
|
||||
}
|
||||
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist.ManifestDescriptor {
|
||||
version := system.GetOSVersion()
|
||||
|
||||
// TODO @jhowardmsft LCOW Support: Need to remove the hard coding in LCOW mode.
|
||||
lookingForOS := runtime.GOOS
|
||||
osVersion := fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
|
||||
if system.LCOWSupported() {
|
||||
lookingForOS = "linux"
|
||||
osVersion = ""
|
||||
func filterManifests(manifests []manifestlist.ManifestDescriptor, os string) []manifestlist.ManifestDescriptor {
|
||||
osVersion := ""
|
||||
if os == "windows" {
|
||||
version := system.GetOSVersion()
|
||||
osVersion = fmt.Sprintf("%d.%d.%d", version.MajorVersion, version.MinorVersion, version.Build)
|
||||
logrus.Debugf("will only match entries with version %s", osVersion)
|
||||
}
|
||||
|
||||
var matches []manifestlist.ManifestDescriptor
|
||||
for _, manifestDescriptor := range manifests {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
|
||||
if lookingForOS == "windows" && !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) {
|
||||
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == os {
|
||||
if os == "windows" && !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) {
|
||||
logrus.Debugf("skipping %s", manifestDescriptor.Platform.OSVersion)
|
||||
continue
|
||||
}
|
||||
matches = append(matches, manifestDescriptor)
|
||||
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", lookingForOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
logrus.Debugf("found match for %s/%s with media type %s, digest %s", os, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
|
||||
}
|
||||
}
|
||||
sort.Stable(manifestsByVersion(matches))
|
||||
if os == "windows" {
|
||||
sort.Stable(manifestsByVersion(matches))
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ func TestImage(t *testing.T) {
|
|||
Domainname: "domain",
|
||||
User: "root",
|
||||
}
|
||||
platform := runtime.GOOS
|
||||
os := runtime.GOOS
|
||||
|
||||
img := &Image{
|
||||
V1Image: V1Image{
|
||||
|
@ -73,19 +73,19 @@ func TestImage(t *testing.T) {
|
|||
|
||||
assert.Equal(t, cid, img.ImageID())
|
||||
assert.Equal(t, cid, img.ID().String())
|
||||
assert.Equal(t, platform, img.Platform())
|
||||
assert.Equal(t, os, img.OperatingSystem())
|
||||
assert.Equal(t, config, img.RunConfig())
|
||||
}
|
||||
|
||||
func TestImagePlatformNotEmpty(t *testing.T) {
|
||||
platform := "platform"
|
||||
func TestImageOSNotEmpty(t *testing.T) {
|
||||
os := "os"
|
||||
img := &Image{
|
||||
V1Image: V1Image{
|
||||
OS: platform,
|
||||
OS: os,
|
||||
},
|
||||
OSVersion: "osversion",
|
||||
}
|
||||
assert.Equal(t, platform, img.Platform())
|
||||
assert.Equal(t, os, img.OperatingSystem())
|
||||
}
|
||||
|
||||
func TestNewChildImageFromImageWithRootFS(t *testing.T) {
|
||||
|
|
|
@ -8,21 +8,6 @@ import (
|
|||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
)
|
||||
|
||||
// IsPlatformEmpty determines if an OCI image-spec platform structure is not populated.
|
||||
// TODO This is a temporary function - can be replaced by parsing from
|
||||
// https://github.com/containerd/containerd/pull/1403/files at a later date.
|
||||
// @jhowardmsft
|
||||
func IsPlatformEmpty(platform specs.Platform) bool {
|
||||
if platform.Architecture == "" &&
|
||||
platform.OS == "" &&
|
||||
len(platform.OSFeatures) == 0 &&
|
||||
platform.OSVersion == "" &&
|
||||
platform.Variant == "" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ValidatePlatform determines if a platform structure is valid.
|
||||
// TODO This is a temporary function - can be replaced by parsing from
|
||||
// https://github.com/containerd/containerd/pull/1403/files at a later date.
|
||||
|
@ -30,7 +15,9 @@ func IsPlatformEmpty(platform specs.Platform) bool {
|
|||
func ValidatePlatform(platform *specs.Platform) error {
|
||||
platform.Architecture = strings.ToLower(platform.Architecture)
|
||||
platform.OS = strings.ToLower(platform.OS)
|
||||
if platform.Architecture != "" && platform.Architecture != runtime.GOARCH {
|
||||
// Based on https://github.com/moby/moby/pull/34642#issuecomment-330375350, do
|
||||
// not support anything except operating system.
|
||||
if platform.Architecture != "" {
|
||||
return fmt.Errorf("invalid platform architecture %q", platform.Architecture)
|
||||
}
|
||||
if platform.OS != "" {
|
||||
|
|
Loading…
Reference in a new issue