1
0
Fork 0
mirror of https://github.com/moby/moby.git synced 2022-11-09 12:21:53 -05:00

LCOW: API: Add platform to /images/create and /build

Signed-off-by: John Howard <jhoward@microsoft.com>

This PR has the API changes described in https://github.com/moby/moby/issues/34617.
Specifically, it adds an HTTP header "X-Requested-Platform" which is a JSON-encoded
OCI Image-spec `Platform` structure.

In addition, it renames (almost all) uses of a string variable platform (and associated)
methods/functions to os. This makes it much clearer to disambiguate with the swarm
"platform" which is really os/arch. This is a stepping stone to getting the daemon towards
fully multi-platform/arch-aware, and makes it clear when "operating system" is being
referred to rather than "platform" which is misleadingly used - sometimes in the swarm
meaning, but more often as just the operating system.
This commit is contained in:
John Howard 2017-08-08 12:43:48 -07:00
parent b8571fd81c
commit 0380fbff37
82 changed files with 622 additions and 510 deletions

View file

@ -1,11 +1,17 @@
package httputils package httputils
import ( import (
"encoding/json"
"fmt"
"io" "io"
"mime" "mime"
"net/http" "net/http"
"runtime"
"strings" "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/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/net/context" "golang.org/x/net/context"
@ -109,3 +115,27 @@ func matchesContentType(contentType, expectedType string) bool {
} }
return err == nil && mimetype == expectedType 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
}

View file

@ -87,6 +87,12 @@ 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")} 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{} var buildUlimits = []*units.Ulimit{}
ulimitsJSON := r.FormValue("ulimits") ulimitsJSON := r.FormValue("ulimits")
if ulimitsJSON != "" { if ulimitsJSON != "" {

View file

@ -5,7 +5,6 @@ import (
"encoding/json" "encoding/json"
"io" "io"
"net/http" "net/http"
"runtime"
"strconv" "strconv"
"strings" "strings"
@ -17,8 +16,8 @@ import (
"github.com/docker/docker/api/types/versions" "github.com/docker/docker/api/types/versions"
"github.com/docker/docker/pkg/ioutils" "github.com/docker/docker/pkg/ioutils"
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors" "github.com/pkg/errors"
"golang.org/x/net/context" "golang.org/x/net/context"
) )
@ -76,78 +75,46 @@ func (s *imageRouter) postImagesCreate(ctx context.Context, w http.ResponseWrite
} }
var ( var (
image = r.Form.Get("fromImage") image = r.Form.Get("fromImage")
repo = r.Form.Get("repo") repo = r.Form.Get("repo")
tag = r.Form.Get("tag") tag = r.Form.Get("tag")
message = r.Form.Get("message") message = r.Form.Get("message")
err error err error
output = ioutils.NewWriteFlusher(w) output = ioutils.NewWriteFlusher(w)
platform = &specs.Platform{}
) )
defer output.Close() defer output.Close()
// TODO @jhowardmsft LCOW Support: Eventually we will need an API change
// so that platform comes from (for example) r.Form.Get("platform"). For
// the initial implementation, we assume that the platform is the
// runtime OS of the host. It will also need a validation function such
// as below which should be called after getting it from the API.
//
// Ensures the requested platform is valid and normalized
//func validatePlatform(req string) (string, error) {
// req = strings.ToLower(req)
// if req == "" {
// req = runtime.GOOS // default to host platform
// }
// valid := []string{runtime.GOOS}
//
// if system.LCOWSupported() {
// valid = append(valid, "linux")
// }
//
// for _, item := range valid {
// if req == item {
// return req, nil
// }
// }
// return "", fmt.Errorf("invalid platform requested: %s", req)
//}
//
// And in the call-site:
// if platform, err = validatePlatform(platform); err != nil {
// return err
// }
platform := runtime.GOOS
if system.LCOWSupported() {
platform = "linux"
}
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
if image != "" { //pull platform, err = httputils.GetRequestedPlatform(ctx, r)
metaHeaders := map[string][]string{} if err == nil {
for k, v := range r.Header { if image != "" { //pull
if strings.HasPrefix(k, "X-Meta-") { metaHeaders := map[string][]string{}
metaHeaders[k] = v for k, v := range r.Header {
if strings.HasPrefix(k, "X-Meta-") {
metaHeaders[k] = v
}
} }
}
authEncoded := r.Header.Get("X-Registry-Auth") authEncoded := r.Header.Get("X-Registry-Auth")
authConfig := &types.AuthConfig{} authConfig := &types.AuthConfig{}
if authEncoded != "" { if authEncoded != "" {
authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded)) authJSON := base64.NewDecoder(base64.URLEncoding, strings.NewReader(authEncoded))
if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil { if err := json.NewDecoder(authJSON).Decode(authConfig); err != nil {
// for a pull it is not an error if no auth was given // for a pull it is not an error if no auth was given
// to increase compatibility with the existing api it is defaulting to be empty // to increase compatibility with the existing api it is defaulting to be empty
authConfig = &types.AuthConfig{} authConfig = &types.AuthConfig{}
}
} }
err = s.backend.PullImage(ctx, image, tag, platform.OS, metaHeaders, authConfig, output)
} else { //import
src := r.Form.Get("fromSrc")
// 'err' MUST NOT be defined within this block, we need any error
// generated from the download to be available to the output
// stream processing below
err = s.backend.ImportImage(src, repo, platform.OS, tag, message, r.Body, output, r.Form["changes"])
} }
err = s.backend.PullImage(ctx, image, tag, platform, metaHeaders, authConfig, output)
} else { //import
src := r.Form.Get("fromSrc")
// 'err' MUST NOT be defined within this block, we need any error
// generated from the download to be available to the output
// stream processing below
err = s.backend.ImportImage(src, repo, platform, tag, message, r.Body, output, r.Form["changes"])
} }
if err != nil { if err != nil {
if !output.Flushed() { if !output.Flushed() {

View file

@ -6181,6 +6181,19 @@ 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. 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" 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"
}
```
type: "string"
default: ""
responses: responses:
200: 200:
description: "no error" description: "no error"
@ -6262,6 +6275,19 @@ paths:
in: "header" in: "header"
description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)" description: "A base64-encoded auth configuration. [See the authentication section for details.](#section/Authentication)"
type: "string" 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"
}
```
type: "string"
default: ""
tags: ["Image"] tags: ["Image"]
/images/{name}/json: /images/{name}/json:
get: get:

View file

@ -40,5 +40,5 @@ type GetImageAndLayerOptions struct {
PullOption PullOption PullOption PullOption
AuthConfig map[string]types.AuthConfig AuthConfig map[string]types.AuthConfig
Output io.Writer Output io.Writer
Platform string OS string
} }

View file

@ -8,6 +8,7 @@ import (
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
units "github.com/docker/go-units" 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 // CheckpointCreateOptions holds parameters to create a checkpoint from a container
@ -179,10 +180,7 @@ type ImageBuildOptions struct {
ExtraHosts []string // List of extra hosts ExtraHosts []string // List of extra hosts
Target string Target string
SessionID string SessionID string
Platform specs.Platform
// TODO @jhowardmsft LCOW Support: This will require extending to include
// `Platform string`, but is omitted for now as it's hard-coded temporarily
// to avoid API changes.
} }
// ImageBuildResponse holds information // ImageBuildResponse holds information
@ -195,7 +193,8 @@ type ImageBuildResponse struct {
// ImageCreateOptions holds information to create images. // ImageCreateOptions holds information to create images.
type ImageCreateOptions struct { type ImageCreateOptions struct {
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry 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.
} }
// ImageImportSource holds source information for ImageImport // ImageImportSource holds source information for ImageImport
@ -229,6 +228,7 @@ type ImagePullOptions struct {
All bool All bool
RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry RegistryAuth string // RegistryAuth is the base64 encoded credentials for the registry
PrivilegeFunc RequestPrivilegeFunc PrivilegeFunc RequestPrivilegeFunc
Platform specs.Platform
} }
// RequestPrivilegeFunc is a function interface that // RequestPrivilegeFunc is a function interface that

View file

@ -16,7 +16,6 @@ type ContainerCreateConfig struct {
HostConfig *container.HostConfig HostConfig *container.HostConfig
NetworkingConfig *network.NetworkingConfig NetworkingConfig *network.NetworkingConfig
AdjustCPUShares bool AdjustCPUShares bool
Platform string
} }
// ContainerRmConfig holds arguments for the container remove // ContainerRmConfig holds arguments for the container remove

View file

@ -327,7 +327,7 @@ type ContainerJSONBase struct {
Name string Name string
RestartCount int RestartCount int
Driver string Driver string
Platform string OS string
MountLabel string MountLabel string
ProcessLabel string ProcessLabel string
AppArmorProfile string AppArmorProfile string

View file

@ -95,6 +95,7 @@ type Image interface {
ImageID() string ImageID() string
RunConfig() *container.Config RunConfig() *container.Config
MarshalJSON() ([]byte, error) MarshalJSON() ([]byte, error)
OperatingSystem() string
} }
// ReleaseableLayer is an image layer that can be mounted and released // ReleaseableLayer is an image layer that can be mounted and released

View file

@ -20,7 +20,6 @@ import (
"github.com/docker/docker/pkg/idtools" "github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/streamformatter" "github.com/docker/docker/pkg/streamformatter"
"github.com/docker/docker/pkg/stringid" "github.com/docker/docker/pkg/stringid"
"github.com/docker/docker/pkg/system"
"github.com/moby/buildkit/session" "github.com/moby/buildkit/session"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
@ -93,15 +92,6 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
} }
}() }()
// TODO @jhowardmsft LCOW support - this will require rework to allow both linux and Windows simultaneously.
// This is an interim solution to hardcode to linux if LCOW is turned on.
if dockerfile.Platform == "" {
dockerfile.Platform = runtime.GOOS
if dockerfile.Platform == "windows" && system.LCOWSupported() {
dockerfile.Platform = "linux"
}
}
ctx, cancel := context.WithCancel(ctx) ctx, cancel := context.WithCancel(ctx)
defer cancel() defer cancel()
@ -111,16 +101,26 @@ func (bm *BuildManager) Build(ctx context.Context, config backend.BuildConfig) (
source = src source = src
} }
os := runtime.GOOS
if dockerfile.OS != "" {
if config.Options.Platform.OS != "" && config.Options.Platform.OS != dockerfile.OS {
return nil, fmt.Errorf("invalid platform")
}
os = dockerfile.OS
} else if config.Options.Platform.OS != "" {
os = config.Options.Platform.OS
}
config.Options.Platform.OS = os
dockerfile.OS = os
builderOptions := builderOptions{ builderOptions := builderOptions{
Options: config.Options, Options: config.Options,
ProgressWriter: config.ProgressWriter, ProgressWriter: config.ProgressWriter,
Backend: bm.backend, Backend: bm.backend,
PathCache: bm.pathCache, PathCache: bm.pathCache,
IDMappings: bm.idMappings, IDMappings: bm.idMappings,
Platform: dockerfile.Platform,
} }
return newBuilder(ctx, builderOptions, os).build(source, dockerfile)
return newBuilder(ctx, builderOptions).build(source, dockerfile)
} }
func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) { func (bm *BuildManager) initializeClientSession(ctx context.Context, cancel func(), options *types.ImageBuildOptions) (builder.Source, error) {
@ -163,7 +163,6 @@ type builderOptions struct {
ProgressWriter backend.ProgressWriter ProgressWriter backend.ProgressWriter
PathCache pathCache PathCache pathCache
IDMappings *idtools.IDMappings IDMappings *idtools.IDMappings
Platform string
} }
// Builder is a Dockerfile builder // Builder is a Dockerfile builder
@ -185,32 +184,15 @@ type Builder struct {
pathCache pathCache pathCache pathCache
containerManager *containerManager containerManager *containerManager
imageProber ImageProber imageProber ImageProber
// TODO @jhowardmft LCOW Support. This will be moved to options at a later
// stage, however that cannot be done now as it affects the public API
// if it were.
platform string
} }
// newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options. // newBuilder creates a new Dockerfile builder from an optional dockerfile and a Options.
// TODO @jhowardmsft LCOW support: Eventually platform can be moved into the builder func newBuilder(clientCtx context.Context, options builderOptions, os string) *Builder {
// options, however, that would be an API change as it shares types.ImageBuildOptions.
func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
config := options.Options config := options.Options
if config == nil { if config == nil {
config = new(types.ImageBuildOptions) config = new(types.ImageBuildOptions)
} }
// @jhowardmsft LCOW Support. For the time being, this is interim. Eventually
// will be moved to types.ImageBuildOptions, but it can't for now as that would
// be an API change.
if options.Platform == "" {
options.Platform = runtime.GOOS
}
if options.Platform == "windows" && system.LCOWSupported() {
options.Platform = "linux"
}
b := &Builder{ b := &Builder{
clientCtx: clientCtx, clientCtx: clientCtx,
options: config, options: config,
@ -222,9 +204,8 @@ func newBuilder(clientCtx context.Context, options builderOptions) *Builder {
idMappings: options.IDMappings, idMappings: options.IDMappings,
imageSources: newImageSources(clientCtx, options), imageSources: newImageSources(clientCtx, options),
pathCache: options.PathCache, pathCache: options.PathCache,
imageProber: newImageProber(options.Backend, config.CacheFrom, options.Platform, config.NoCache), imageProber: newImageProber(options.Backend, config.CacheFrom, os, config.NoCache),
containerManager: newContainerManager(options.Backend), containerManager: newContainerManager(options.Backend),
platform: options.Platform,
} }
return b return b
@ -382,25 +363,19 @@ func BuildFromConfig(config *container.Config, changes []string) (*container.Con
return config, nil return config, nil
} }
b := newBuilder(context.Background(), builderOptions{
Options: &types.ImageBuildOptions{NoCache: true},
})
dockerfile, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n"))) dockerfile, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
if err != nil { if err != nil {
return nil, validationError{err} return nil, validationError{err}
} }
// TODO @jhowardmsft LCOW support. For now, if LCOW enabled, switch to linux. os := runtime.GOOS
// Also explicitly set the platform. Ultimately this will be in the builder if dockerfile.OS != "" {
// options, but we can't do that yet as it would change the API. os = dockerfile.OS
if dockerfile.Platform == "" {
dockerfile.Platform = runtime.GOOS
} }
if dockerfile.Platform == "windows" && system.LCOWSupported() {
dockerfile.Platform = "linux" b := newBuilder(context.Background(), builderOptions{
} Options: &types.ImageBuildOptions{NoCache: true},
b.platform = dockerfile.Platform }, os)
// ensure that the commands are valid // ensure that the commands are valid
for _, n := range dockerfile.AST.Children { for _, n := range dockerfile.AST.Children {

View file

@ -2,6 +2,6 @@
package dockerfile package dockerfile
func defaultShellForPlatform(platform string) []string { func defaultShellForOS(os string) []string {
return []string{"/bin/sh", "-c"} return []string{"/bin/sh", "-c"}
} }

View file

@ -1,7 +1,7 @@
package dockerfile package dockerfile
func defaultShellForPlatform(platform string) []string { func defaultShellForOS(os string) []string {
if platform == "linux" { if os == "linux" {
return []string{"/bin/sh", "-c"} return []string{"/bin/sh", "-c"}
} }
return []string{"cmd", "/S", "/C"} return []string{"cmd", "/S", "/C"}

View file

@ -32,7 +32,6 @@ func (c *containerManager) Create(runConfig *container.Config, hostConfig *conta
container, err := c.backend.ContainerCreate(types.ContainerCreateConfig{ container, err := c.backend.ContainerCreate(types.ContainerCreateConfig{
Config: runConfig, Config: runConfig,
HostConfig: hostConfig, HostConfig: hostConfig,
Platform: platform,
}) })
if err != nil { if err != nil {
return container, err return container, err

View file

@ -82,7 +82,7 @@ func copierFromDispatchRequest(req dispatchRequest, download sourceDownloader, i
pathCache: req.builder.pathCache, pathCache: req.builder.pathCache,
download: download, download: download,
imageSource: imageSource, imageSource: imageSource,
platform: req.builder.platform, platform: req.builder.options.Platform.OS,
} }
} }

View file

@ -220,12 +220,22 @@ func (d *dispatchRequest) getImageOrStage(name string) (builder.Image, error) {
// Windows cannot support a container with no base image unless it is LCOW. // Windows cannot support a container with no base image unless it is LCOW.
if name == api.NoBaseImageSpecifier { if name == api.NoBaseImageSpecifier {
imageImage := &image.Image{}
imageImage.OS = runtime.GOOS
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
if d.builder.platform == "windows" || (d.builder.platform != "windows" && !system.LCOWSupported()) { switch d.builder.options.Platform.OS {
case "windows":
return nil, errors.New("Windows does not support FROM scratch") return nil, errors.New("Windows does not support FROM scratch")
case "linux":
if !system.LCOWSupported() {
return nil, errors.New("Linux containers are not supported on this system")
}
imageImage.OS = "linux"
default:
return nil, errors.Errorf("operating system %q is not supported", d.builder.options.Platform.OS)
} }
} }
return scratchImage, nil return builder.Image(imageImage), nil
} }
imageMount, err := d.builder.imageSources.Get(name, localOnly) imageMount, err := d.builder.imageSources.Get(name, localOnly)
if err != nil { if err != nil {
@ -254,7 +264,7 @@ func dispatchOnbuild(d dispatchRequest, c *instructions.OnbuildCommand) error {
func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error { func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
runConfig := d.state.runConfig runConfig := d.state.runConfig
var err error var err error
runConfig.WorkingDir, err = normalizeWorkdir(d.builder.platform, runConfig.WorkingDir, c.Path) runConfig.WorkingDir, err = normalizeWorkdir(d.builder.options.Platform.OS, runConfig.WorkingDir, c.Path)
if err != nil { if err != nil {
return err return err
} }
@ -270,7 +280,7 @@ func dispatchWorkdir(d dispatchRequest, c *instructions.WorkdirCommand) error {
} }
comment := "WORKDIR " + runConfig.WorkingDir comment := "WORKDIR " + runConfig.WorkingDir
runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.builder.platform)) runConfigWithCommentCmd := copyRunConfig(runConfig, withCmdCommentString(comment, d.builder.options.Platform.OS))
containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd) containerID, err := d.builder.probeAndCreate(d.state, runConfigWithCommentCmd)
if err != nil || containerID == "" { if err != nil || containerID == "" {
return err return err
@ -303,7 +313,7 @@ func resolveCmdLine(cmd instructions.ShellDependantCmdLine, runConfig *container
func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error { func dispatchRun(d dispatchRequest, c *instructions.RunCommand) error {
stateRunConfig := d.state.runConfig stateRunConfig := d.state.runConfig
cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.builder.platform) cmdFromArgs := resolveCmdLine(c.ShellDependantCmdLine, stateRunConfig, d.builder.options.Platform.OS)
buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env) buildArgs := d.state.buildArgs.FilterAllowed(stateRunConfig.Env)
saveCmd := cmdFromArgs saveCmd := cmdFromArgs
@ -380,7 +390,7 @@ func prependEnvOnCmd(buildArgs *buildArgs, buildArgVars []string, cmd strslice.S
// //
func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error { func dispatchCmd(d dispatchRequest, c *instructions.CmdCommand) error {
runConfig := d.state.runConfig runConfig := d.state.runConfig
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.platform) cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
runConfig.Cmd = cmd runConfig.Cmd = cmd
// set config as already being escaped, this prevents double escaping on windows // set config as already being escaped, this prevents double escaping on windows
runConfig.ArgsEscaped = true runConfig.ArgsEscaped = true
@ -423,7 +433,7 @@ func dispatchHealthcheck(d dispatchRequest, c *instructions.HealthCheckCommand)
// //
func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error { func dispatchEntrypoint(d dispatchRequest, c *instructions.EntrypointCommand) error {
runConfig := d.state.runConfig runConfig := d.state.runConfig
cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.platform) cmd := resolveCmdLine(c.ShellDependantCmdLine, runConfig, d.builder.options.Platform.OS)
runConfig.Entrypoint = cmd runConfig.Entrypoint = cmd
if !d.state.cmdSet { if !d.state.cmdSet {
runConfig.Cmd = nil runConfig.Cmd = nil

View file

@ -14,6 +14,7 @@ import (
"github.com/docker/docker/builder/dockerfile/instructions" "github.com/docker/docker/builder/dockerfile/instructions"
"github.com/docker/docker/pkg/system" "github.com/docker/docker/pkg/system"
"github.com/docker/go-connections/nat" "github.com/docker/go-connections/nat"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
@ -22,13 +23,13 @@ func newBuilderWithMockBackend() *Builder {
mockBackend := &MockBackend{} mockBackend := &MockBackend{}
ctx := context.Background() ctx := context.Background()
b := &Builder{ b := &Builder{
options: &types.ImageBuildOptions{}, options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}},
docker: mockBackend, docker: mockBackend,
Stdout: new(bytes.Buffer), Stdout: new(bytes.Buffer),
clientCtx: ctx, clientCtx: ctx,
disableCommit: true, disableCommit: true,
imageSources: newImageSources(ctx, builderOptions{ imageSources: newImageSources(ctx, builderOptions{
Options: &types.ImageBuildOptions{}, Options: &types.ImageBuildOptions{Platform: specs.Platform{OS: runtime.GOOS}},
Backend: mockBackend, Backend: mockBackend,
}), }),
imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false), imageProber: newImageProber(mockBackend, nil, runtime.GOOS, false),
@ -118,11 +119,7 @@ func TestFromScratch(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
assert.True(t, sb.state.hasFromImage()) assert.True(t, sb.state.hasFromImage())
assert.Equal(t, "", sb.state.imageID) assert.Equal(t, "", sb.state.imageID)
// Windows does not set the default path. TODO @jhowardmsft LCOW support. This will need revisiting as we get further into the implementation
expected := "PATH=" + system.DefaultPathEnv(runtime.GOOS) expected := "PATH=" + system.DefaultPathEnv(runtime.GOOS)
if runtime.GOOS == "windows" {
expected = ""
}
assert.Equal(t, []string{expected}, sb.state.runConfig.Env) assert.Equal(t, []string{expected}, sb.state.runConfig.Env)
} }

View file

@ -21,7 +21,6 @@ package dockerfile
import ( import (
"reflect" "reflect"
"runtime"
"strconv" "strconv"
"strings" "strings"
@ -35,7 +34,7 @@ import (
func dispatch(d dispatchRequest, cmd instructions.Command) error { func dispatch(d dispatchRequest, cmd instructions.Command) error {
if c, ok := cmd.(instructions.PlatformSpecific); ok { if c, ok := cmd.(instructions.PlatformSpecific); ok {
err := c.CheckPlatform(d.builder.platform) err := c.CheckPlatform(d.builder.options.Platform.OS)
if err != nil { if err != nil {
return validationError{err} return validationError{err}
} }
@ -218,19 +217,15 @@ func (s *dispatchState) beginStage(stageName string, image builder.Image) {
s.runConfig.StdinOnce = false s.runConfig.StdinOnce = false
} }
// Add the default PATH to runConfig.ENV if one exists for the platform and there // Add the default PATH to runConfig.ENV if one exists for the operating system and there
// is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS // is no PATH set. Note that Windows containers on Windows won't have one as it's set by HCS
func (s *dispatchState) setDefaultPath() { func (s *dispatchState) setDefaultPath() {
// TODO @jhowardmsft LCOW Support - This will need revisiting later defaultPath := system.DefaultPathEnv(s.baseImage.OperatingSystem())
platform := runtime.GOOS if defaultPath == "" {
if system.LCOWSupported() {
platform = "linux"
}
if system.DefaultPathEnv(platform) == "" {
return return
} }
envMap := opts.ConvertKVStringsToMap(s.runConfig.Env) envMap := opts.ConvertKVStringsToMap(s.runConfig.Env)
if _, ok := envMap["PATH"]; !ok { if _, ok := envMap["PATH"]; !ok {
s.runConfig.Env = append(s.runConfig.Env, "PATH="+system.DefaultPathEnv(platform)) s.runConfig.Env = append(s.runConfig.Env, "PATH="+defaultPath)
} }
} }

View file

@ -20,8 +20,6 @@ type imageSources struct {
getImage getAndMountFunc getImage getAndMountFunc
} }
// TODO @jhowardmsft LCOW Support: Eventually, platform can be moved to options.Options.Platform,
// and removed from builderOptions, but that can't be done yet as it would affect the API.
func newImageSources(ctx context.Context, options builderOptions) *imageSources { func newImageSources(ctx context.Context, options builderOptions) *imageSources {
getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ReleaseableLayer, error) { getAndMount := func(idOrRef string, localOnly bool) (builder.Image, builder.ReleaseableLayer, error) {
pullOption := backend.PullOptionNoPull pullOption := backend.PullOptionNoPull
@ -36,7 +34,7 @@ func newImageSources(ctx context.Context, options builderOptions) *imageSources
PullOption: pullOption, PullOption: pullOption,
AuthConfig: options.Options.AuthConfigs, AuthConfig: options.Options.AuthConfigs,
Output: options.ProgressWriter.Output, Output: options.ProgressWriter.Output,
Platform: options.Platform, OS: options.Options.Platform.OS,
}) })
} }

View file

@ -83,7 +83,7 @@ func (b *Builder) commit(dispatchState *dispatchState, comment string) error {
return errors.New("Please provide a source image with `from` prior to commit") return errors.New("Please provide a source image with `from` prior to commit")
} }
runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, b.platform)) runConfigWithCommentCmd := copyRunConfig(dispatchState.runConfig, withCmdComment(comment, b.options.Platform.OS))
hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd) hit, err := b.probeCache(dispatchState, runConfigWithCommentCmd)
if err != nil || hit { if err != nil || hit {
return err return err
@ -122,7 +122,7 @@ func (b *Builder) commitContainer(dispatchState *dispatchState, id string, conta
} }
func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error { func (b *Builder) exportImage(state *dispatchState, imageMount *imageMount, runConfig *container.Config) error {
newLayer, err := imageMount.Layer().Commit(b.platform) newLayer, err := imageMount.Layer().Commit(b.options.Platform.OS)
if err != nil { if err != nil {
return err return err
} }
@ -172,7 +172,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
// TODO: should this have been using origPaths instead of srcHash in the comment? // TODO: should this have been using origPaths instead of srcHash in the comment?
runConfigWithCommentCmd := copyRunConfig( runConfigWithCommentCmd := copyRunConfig(
state.runConfig, state.runConfig,
withCmdCommentString(commentStr, b.platform)) withCmdCommentString(commentStr, b.options.Platform.OS))
hit, err := b.probeCache(state, runConfigWithCommentCmd) hit, err := b.probeCache(state, runConfigWithCommentCmd)
if err != nil || hit { if err != nil || hit {
return err return err
@ -183,7 +183,7 @@ func (b *Builder) performCopy(state *dispatchState, inst copyInstruction) error
return errors.Wrapf(err, "failed to get destination image %q", state.imageID) return errors.Wrapf(err, "failed to get destination image %q", state.imageID)
} }
destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.platform) destInfo, err := createDestInfo(state.runConfig.WorkingDir, inst, imageMount, b.options.Platform.OS)
if err != nil { if err != nil {
return err return err
} }
@ -437,9 +437,9 @@ func withEntrypointOverride(cmd []string, entrypoint []string) runConfigModifier
// getShell is a helper function which gets the right shell for prefixing the // getShell is a helper function which gets the right shell for prefixing the
// shell-form of RUN, ENTRYPOINT and CMD instructions // shell-form of RUN, ENTRYPOINT and CMD instructions
func getShell(c *container.Config, platform string) []string { func getShell(c *container.Config, os string) []string {
if 0 == len(c.Shell) { if 0 == len(c.Shell) {
return append([]string{}, defaultShellForPlatform(platform)[:]...) return append([]string{}, defaultShellForOS(os)[:]...)
} }
return append([]string{}, c.Shell[:]...) return append([]string{}, c.Shell[:]...)
} }
@ -463,13 +463,13 @@ func (b *Builder) probeAndCreate(dispatchState *dispatchState, runConfig *contai
} }
// Set a log config to override any default value set on the daemon // Set a log config to override any default value set on the daemon
hostConfig := &container.HostConfig{LogConfig: defaultLogConfig} hostConfig := &container.HostConfig{LogConfig: defaultLogConfig}
container, err := b.containerManager.Create(runConfig, hostConfig, b.platform) container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS)
return container.ID, err return container.ID, err
} }
func (b *Builder) create(runConfig *container.Config) (string, error) { func (b *Builder) create(runConfig *container.Config) (string, error) {
hostConfig := hostConfigFromOptions(b.options) hostConfig := hostConfigFromOptions(b.options)
container, err := b.containerManager.Create(runConfig, hostConfig, b.platform) container, err := b.containerManager.Create(runConfig, hostConfig, b.options.Platform.OS)
if err != nil { if err != nil {
return "", err return "", err
} }

View file

@ -103,7 +103,7 @@ func TestCopyRunConfig(t *testing.T) {
doc: "Set the command to a comment", doc: "Set the command to a comment",
modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)}, modifiers: []runConfigModifier{withCmdComment("comment", runtime.GOOS)},
expected: &container.Config{ expected: &container.Config{
Cmd: append(defaultShellForPlatform(runtime.GOOS), "#(nop) ", "comment"), Cmd: append(defaultShellForOS(runtime.GOOS), "#(nop) ", "comment"),
Env: defaultEnv, Env: defaultEnv,
}, },
}, },

View file

@ -3,6 +3,7 @@ package dockerfile
import ( import (
"encoding/json" "encoding/json"
"io" "io"
"runtime"
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/backend" "github.com/docker/docker/api/types/backend"
@ -96,6 +97,10 @@ func (i *mockImage) RunConfig() *container.Config {
return i.config return i.config
} }
func (i *mockImage) OperatingSystem() string {
return runtime.GOOS
}
func (i *mockImage) MarshalJSON() ([]byte, error) { func (i *mockImage) MarshalJSON() ([]byte, error) {
type rawImage mockImage type rawImage mockImage
return json.Marshal(rawImage(*i)) return json.Marshal(rawImage(*i))

View file

@ -91,9 +91,6 @@ var (
// DefaultEscapeToken is the default escape token // DefaultEscapeToken is the default escape token
const DefaultEscapeToken = '\\' const DefaultEscapeToken = '\\'
// defaultPlatformToken is the platform assumed for the build if not explicitly provided
var defaultPlatformToken = runtime.GOOS
// Directive is the structure used during a build run to hold the state of // Directive is the structure used during a build run to hold the state of
// parsing directives. // parsing directives.
type Directive struct { type Directive struct {
@ -152,8 +149,7 @@ func (d *Directive) possibleParserDirective(line string) error {
} }
} }
// TODO @jhowardmsft LCOW Support: Eventually this check can be removed, // Only recognise a platform token if LCOW is supported
// but only recognise a platform token if running in LCOW mode.
if system.LCOWSupported() { if system.LCOWSupported() {
tpcMatch := tokenPlatformCommand.FindStringSubmatch(strings.ToLower(line)) tpcMatch := tokenPlatformCommand.FindStringSubmatch(strings.ToLower(line))
if len(tpcMatch) != 0 { if len(tpcMatch) != 0 {
@ -177,7 +173,6 @@ func (d *Directive) possibleParserDirective(line string) error {
func NewDefaultDirective() *Directive { func NewDefaultDirective() *Directive {
directive := Directive{} directive := Directive{}
directive.setEscapeToken(string(DefaultEscapeToken)) directive.setEscapeToken(string(DefaultEscapeToken))
directive.setPlatformToken(defaultPlatformToken)
return &directive return &directive
} }
@ -242,8 +237,10 @@ func newNodeFromLine(line string, directive *Directive) (*Node, error) {
type Result struct { type Result struct {
AST *Node AST *Node
EscapeToken rune EscapeToken rune
Platform string // TODO @jhowardmsft - see https://github.com/moby/moby/issues/34617
Warnings []string // This next field will be removed in a future update for LCOW support.
OS string
Warnings []string
} }
// PrintWarnings to the writer // PrintWarnings to the writer
@ -323,7 +320,7 @@ func Parse(rwc io.Reader) (*Result, error) {
AST: root, AST: root,
Warnings: warnings, Warnings: warnings,
EscapeToken: d.escapeToken, EscapeToken: d.escapeToken,
Platform: d.platformToken, OS: d.platformToken,
}, nil }, nil
} }

View file

@ -12,6 +12,7 @@ import (
"github.com/docker/docker/api/types" "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/container"
"github.com/docker/docker/pkg/system"
) )
// ImageBuild sends request to the daemon to build images. // ImageBuild sends request to the daemon to build images.
@ -29,6 +30,20 @@ func (cli *Client) ImageBuild(ctx context.Context, buildContext io.Reader, optio
return types.ImageBuildResponse{}, err return types.ImageBuildResponse{}, err
} }
headers.Add("X-Registry-Config", base64.URLEncoding.EncodeToString(buf)) 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 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[:]))
}
headers.Set("Content-Type", "application/x-tar") headers.Set("Content-Type", "application/x-tar")
serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers) serverResp, err := cli.postRaw(ctx, "/build", query, buildContext, headers)
@ -123,6 +138,5 @@ func (cli *Client) imageBuildOptionsToQuery(options types.ImageBuildOptions) (ur
if options.SessionID != "" { if options.SessionID != "" {
query.Set("session", options.SessionID) query.Set("session", options.SessionID)
} }
return query, nil return query, nil
} }

View file

@ -1,6 +1,7 @@
package client package client
import ( import (
"encoding/json"
"io" "io"
"net/url" "net/url"
@ -8,6 +9,8 @@ import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "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. // ImageCreate creates a new image based in the parent options.
@ -21,14 +24,25 @@ func (cli *Client) ImageCreate(ctx context.Context, parentReference string, opti
query := url.Values{} query := url.Values{}
query.Set("fromImage", reference.FamiliarName(ref)) query.Set("fromImage", reference.FamiliarName(ref))
query.Set("tag", getAPITagFromNamedRef(ref)) query.Set("tag", getAPITagFromNamedRef(ref))
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return resp.body, nil return resp.body, nil
} }
func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string) (serverResponse, error) { func (cli *Client) tryImageCreate(ctx context.Context, query url.Values, registryAuth string, platform specs.Platform) (serverResponse, error) {
headers := map[string][]string{"X-Registry-Auth": {registryAuth}} 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) return cli.post(ctx, "/images/create", query, nil, headers)
} }

View file

@ -9,6 +9,7 @@ import (
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api/types" "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. // ImagePull requests the docker host to pull an image from a remote registry.
@ -31,13 +32,29 @@ func (cli *Client) ImagePull(ctx context.Context, refStr string, options types.I
query.Set("tag", getAPITagFromNamedRef(ref)) query.Set("tag", getAPITagFromNamedRef(ref))
} }
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth) // 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
}
}
resp, err := cli.tryImageCreate(ctx, query, options.RegistryAuth, options.Platform)
if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil { if resp.statusCode == http.StatusUnauthorized && options.PrivilegeFunc != nil {
newAuthHeader, privilegeErr := options.PrivilegeFunc() newAuthHeader, privilegeErr := options.PrivilegeFunc()
if privilegeErr != nil { if privilegeErr != nil {
return nil, privilegeErr return nil, privilegeErr
} }
resp, err = cli.tryImageCreate(ctx, query, newAuthHeader) resp, err = cli.tryImageCreate(ctx, query, newAuthHeader, options.Platform)
} }
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -222,10 +222,6 @@ func (cli *DaemonCli) start(opts *daemonOptions) (err error) {
logrus.Fatalf("Error creating middlewares: %v", err) logrus.Fatalf("Error creating middlewares: %v", err)
} }
if system.LCOWSupported() {
logrus.Warnln("LCOW support is enabled - this feature is incomplete")
}
d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote, pluginStore) d, err := daemon.NewDaemon(cli.Config, registryService, containerdRemote, pluginStore)
if err != nil { if err != nil {
return fmt.Errorf("Error starting daemon: %v", err) return fmt.Errorf("Error starting daemon: %v", err)

View file

@ -80,7 +80,7 @@ type Container struct {
LogPath string LogPath string
Name string Name string
Driver string Driver string
Platform string OS string
// MountLabel contains the options for the 'mount' command // MountLabel contains the options for the 'mount' command
MountLabel string MountLabel string
ProcessLabel string ProcessLabel string
@ -147,11 +147,11 @@ func (container *Container) FromDisk() error {
return err return err
} }
// Ensure the platform is set if blank. Assume it is the platform of the // Ensure the operating system is set if blank. Assume it is the OS of the
// host OS if not, to ensure containers created before multiple-platform // host OS if not, to ensure containers created before multiple-OS
// support are migrated // support are migrated
if container.Platform == "" { if container.OS == "" {
container.Platform = runtime.GOOS container.OS = runtime.GOOS
} }
return container.readHostConfig() return container.readHostConfig()
@ -264,7 +264,7 @@ func (container *Container) WriteHostConfig() (*containertypes.HostConfig, error
func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error { func (container *Container) SetupWorkingDirectory(rootIDs idtools.IDPair) error {
// TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting. // TODO @jhowardmsft, @gupta-ak LCOW Support. This will need revisiting.
// We will need to do remote filesystem operations here. // We will need to do remote filesystem operations here.
if container.Platform != runtime.GOOS { if container.OS != runtime.GOOS {
return nil return nil
} }
@ -434,7 +434,7 @@ func (container *Container) ShouldRestart() bool {
// AddMountPointWithVolume adds a new mount point configured with a volume to the container. // AddMountPointWithVolume adds a new mount point configured with a volume to the container.
func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) { func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
operatingSystem := container.Platform operatingSystem := container.OS
if operatingSystem == "" { if operatingSystem == "" {
operatingSystem = runtime.GOOS operatingSystem = runtime.GOOS
} }
@ -1047,15 +1047,14 @@ func (container *Container) ConfigFilePath(configRef swarmtypes.ConfigReference)
// CreateDaemonEnvironment creates a new environment variable slice for this container. // CreateDaemonEnvironment creates a new environment variable slice for this container.
func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string { func (container *Container) CreateDaemonEnvironment(tty bool, linkedEnv []string) []string {
// Setup environment // Setup environment
// TODO @jhowardmsft LCOW Support. This will need revisiting later. os := container.OS
platform := container.Platform if os == "" {
if platform == "" { os = runtime.GOOS
platform = runtime.GOOS
} }
env := []string{} env := []string{}
if runtime.GOOS != "windows" || (system.LCOWSupported() && platform == "linux") { if runtime.GOOS != "windows" || (runtime.GOOS == "windows" && os == "linux") {
env = []string{ env = []string{
"PATH=" + system.DefaultPathEnv(platform), "PATH=" + system.DefaultPathEnv(os),
"HOSTNAME=" + container.Config.Hostname, "HOSTNAME=" + container.Config.Hostname,
} }
if tty { if tty {

View file

@ -55,7 +55,7 @@ func (rl *releaseableLayer) Mount() (containerfs.ContainerFS, error) {
return mountPath, nil return mountPath, nil
} }
func (rl *releaseableLayer) Commit(platform string) (builder.ReleaseableLayer, error) { func (rl *releaseableLayer) Commit(os string) (builder.ReleaseableLayer, error) {
var chainID layer.ChainID var chainID layer.ChainID
if rl.roLayer != nil { if rl.roLayer != nil {
chainID = rl.roLayer.ChainID() chainID = rl.roLayer.ChainID()
@ -67,7 +67,7 @@ func (rl *releaseableLayer) Commit(platform string) (builder.ReleaseableLayer, e
} }
defer stream.Close() defer stream.Close()
newLayer, err := rl.layerStore.Register(stream, chainID, layer.Platform(platform)) newLayer, err := rl.layerStore.Register(stream, chainID, layer.OS(os))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -176,7 +176,7 @@ func (daemon *Daemon) pullForBuilder(ctx context.Context, name string, authConfi
// leaking of layers. // leaking of layers.
func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) { func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID string, opts backend.GetImageAndLayerOptions) (builder.Image, builder.ReleaseableLayer, error) {
if refOrID == "" { if refOrID == "" {
layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.Platform].layerStore) layer, err := newReleasableLayerForImage(nil, daemon.stores[opts.OS].layerStore)
return nil, layer, err return nil, layer, err
} }
@ -187,16 +187,16 @@ func (daemon *Daemon) GetImageAndReleasableLayer(ctx context.Context, refOrID st
} }
// TODO: shouldn't we error out if error is different from "not found" ? // TODO: shouldn't we error out if error is different from "not found" ?
if image != nil { if image != nil {
layer, err := newReleasableLayerForImage(image, daemon.stores[opts.Platform].layerStore) layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
return image, layer, err return image, layer, err
} }
} }
image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.Platform) image, err := daemon.pullForBuilder(ctx, refOrID, opts.AuthConfig, opts.Output, opts.OS)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
layer, err := newReleasableLayerForImage(image, daemon.stores[opts.Platform].layerStore) layer, err := newReleasableLayerForImage(image, daemon.stores[opts.OS].layerStore)
return image, layer, err return image, layer, err
} }

View file

@ -20,7 +20,6 @@ import (
containerpkg "github.com/docker/docker/container" containerpkg "github.com/docker/docker/container"
"github.com/docker/docker/daemon/cluster/convert" "github.com/docker/docker/daemon/cluster/convert"
executorpkg "github.com/docker/docker/daemon/cluster/executor" executorpkg "github.com/docker/docker/daemon/cluster/executor"
"github.com/docker/docker/pkg/system"
"github.com/docker/libnetwork" "github.com/docker/libnetwork"
"github.com/docker/swarmkit/agent/exec" "github.com/docker/swarmkit/agent/exec"
"github.com/docker/swarmkit/api" "github.com/docker/swarmkit/api"
@ -93,9 +92,6 @@ func (c *containerAdapter) pullImage(ctx context.Context) error {
// TODO @jhowardmsft LCOW Support: This will need revisiting as // TODO @jhowardmsft LCOW Support: This will need revisiting as
// the stack is built up to include LCOW support for swarm. // the stack is built up to include LCOW support for swarm.
platform := runtime.GOOS platform := runtime.GOOS
if system.LCOWSupported() {
platform = "linux"
}
err := c.backend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw) err := c.backend.PullImage(ctx, c.container.image(), "", platform, metaHeaders, authConfig, pw)
pw.CloseWithError(err) pw.CloseWithError(err)
}() }()

View file

@ -175,17 +175,17 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
parent = new(image.Image) parent = new(image.Image)
parent.RootFS = image.NewRootFS() parent.RootFS = image.NewRootFS()
} else { } else {
parent, err = daemon.stores[container.Platform].imageStore.Get(container.ImageID) parent, err = daemon.stores[container.OS].imageStore.Get(container.ImageID)
if err != nil { if err != nil {
return "", err return "", err
} }
} }
l, err := daemon.stores[container.Platform].layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.Platform(container.Platform)) l, err := daemon.stores[container.OS].layerStore.Register(rwTar, parent.RootFS.ChainID(), layer.OS(container.OS))
if err != nil { if err != nil {
return "", err return "", err
} }
defer layer.ReleaseAndLog(daemon.stores[container.Platform].layerStore, l) defer layer.ReleaseAndLog(daemon.stores[container.OS].layerStore, l)
containerConfig := c.ContainerConfig containerConfig := c.ContainerConfig
if containerConfig == nil { if containerConfig == nil {
@ -199,18 +199,18 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
Config: newConfig, Config: newConfig,
DiffID: l.DiffID(), DiffID: l.DiffID(),
} }
config, err := json.Marshal(image.NewChildImage(parent, cc, container.Platform)) config, err := json.Marshal(image.NewChildImage(parent, cc, container.OS))
if err != nil { if err != nil {
return "", err return "", err
} }
id, err := daemon.stores[container.Platform].imageStore.Create(config) id, err := daemon.stores[container.OS].imageStore.Create(config)
if err != nil { if err != nil {
return "", err return "", err
} }
if container.ImageID != "" { if container.ImageID != "" {
if err := daemon.stores[container.Platform].imageStore.SetParent(id, container.ImageID); err != nil { if err := daemon.stores[container.OS].imageStore.SetParent(id, container.ImageID); err != nil {
return "", err return "", err
} }
} }
@ -229,7 +229,7 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
return "", err return "", err
} }
} }
if err := daemon.TagImageWithReference(id, container.Platform, newTag); err != nil { if err := daemon.TagImageWithReference(id, container.OS, newTag); err != nil {
return "", err return "", err
} }
imageRef = reference.FamiliarString(newTag) imageRef = reference.FamiliarString(newTag)
@ -246,13 +246,13 @@ func (daemon *Daemon) Commit(name string, c *backend.ContainerCommitConfig) (str
} }
func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io.ReadCloser, err error) { func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io.ReadCloser, err error) {
rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID) rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer) daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
} }
}() }()
@ -273,7 +273,7 @@ func (daemon *Daemon) exportContainerRw(container *container.Container) (arch io
return ioutils.NewReadCloserWrapper(archive, func() error { return ioutils.NewReadCloserWrapper(archive, func() error {
archive.Close() archive.Close()
err = rwlayer.Unmount() err = rwlayer.Unmount()
daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer) daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
return err return err
}), }),
nil nil

View file

@ -123,7 +123,7 @@ func (daemon *Daemon) Register(c *container.Container) error {
return c.CheckpointTo(daemon.containersReplica) return c.CheckpointTo(daemon.containersReplica)
} }
func (daemon *Daemon) newContainer(name string, platform string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) { func (daemon *Daemon) newContainer(name string, operatingSystem string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) {
var ( var (
id string id string
err error err error
@ -156,8 +156,8 @@ func (daemon *Daemon) newContainer(name string, platform string, config *contain
base.ImageID = imgID base.ImageID = imgID
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
base.Name = name base.Name = name
base.Driver = daemon.GraphDriverName(platform) base.Driver = daemon.GraphDriverName(operatingSystem)
base.Platform = platform base.OS = operatingSystem
return base, err return base, err
} }

View file

@ -39,17 +39,21 @@ func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, manage
return containertypes.ContainerCreateCreatedBody{}, validationError{errors.New("Config cannot be empty in order to create a container")} return containertypes.ContainerCreateCreatedBody{}, validationError{errors.New("Config cannot be empty in order to create a container")}
} }
// TODO: @jhowardmsft LCOW support - at a later point, can remove the hard-coding os := runtime.GOOS
// to force the platform to be linux. if params.Config.Image != "" {
// Default the platform if not supplied img, err := daemon.GetImage(params.Config.Image)
if params.Platform == "" { if err == nil {
params.Platform = runtime.GOOS os = img.OS
} }
if system.LCOWSupported() { } else {
params.Platform = "linux" // This mean scratch. On Windows, we can safely assume that this is a linux
// container. On other platforms, it's the host OS (which it already is)
if runtime.GOOS == "windows" && system.LCOWSupported() {
os = "linux"
}
} }
warnings, err := daemon.verifyContainerSettings(params.Platform, params.HostConfig, params.Config, false) warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false)
if err != nil { if err != nil {
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err} return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err}
} }
@ -85,29 +89,25 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
err error err error
) )
os := runtime.GOOS
if params.Config.Image != "" { if params.Config.Image != "" {
img, err = daemon.GetImage(params.Config.Image) img, err = daemon.GetImage(params.Config.Image)
if err != nil { if err != nil {
return nil, err return nil, err
} }
os = img.OS
if runtime.GOOS == "solaris" && img.OS != "solaris " { if runtime.GOOS == "solaris" && img.OS != "solaris " {
return nil, errors.New("platform on which parent image was created is not Solaris") return nil, errors.New("operating system on which parent image was created is not Solaris")
} }
imgID = img.ID() imgID = img.ID()
if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() {
return nil, errors.New("platform on which parent image was created is not Windows") return nil, errors.New("operating system on which parent image was created is not Windows")
} }
} } else {
if runtime.GOOS == "windows" {
// Make sure the platform requested matches the image os = "linux" // 'scratch' case.
if img != nil {
if params.Platform != img.Platform() {
// Ignore this in LCOW mode. @jhowardmsft TODO - This will need revisiting later.
if !system.LCOWSupported() {
return nil, fmt.Errorf("cannot create a %s container from a %s image", params.Platform, img.Platform())
}
} }
} }
@ -119,7 +119,7 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
return nil, validationError{err} return nil, validationError{err}
} }
if container, err = daemon.newContainer(params.Name, params.Platform, params.Config, params.HostConfig, imgID, managed); err != nil { if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
@ -170,7 +170,7 @@ func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (
return nil, err return nil, err
} }
if err := daemon.createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig); err != nil { if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil {
return nil, err return nil, err
} }
@ -253,7 +253,7 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
func (daemon *Daemon) setRWLayer(container *container.Container) error { func (daemon *Daemon) setRWLayer(container *container.Container) error {
var layerID layer.ChainID var layerID layer.ChainID
if container.ImageID != "" { if container.ImageID != "" {
img, err := daemon.stores[container.Platform].imageStore.Get(container.ImageID) img, err := daemon.stores[container.OS].imageStore.Get(container.ImageID)
if err != nil { if err != nil {
return err return err
} }
@ -266,7 +266,7 @@ func (daemon *Daemon) setRWLayer(container *container.Container) error {
StorageOpt: container.HostConfig.StorageOpt, StorageOpt: container.HostConfig.StorageOpt,
} }
rwLayer, err := daemon.stores[container.Platform].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) rwLayer, err := daemon.stores[container.OS].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts)
if err != nil { if err != nil {
return err return err
} }

View file

@ -15,8 +15,8 @@ import (
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
) )
// createContainerPlatformSpecificSettings performs platform specific container create functionality // createContainerOSSpecificSettings performs host-OS specific container create functionality
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error { func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
if err := daemon.Mount(container); err != nil { if err := daemon.Mount(container); err != nil {
return err return err
} }

View file

@ -10,10 +10,10 @@ import (
"github.com/docker/docker/volume" "github.com/docker/docker/volume"
) )
// createContainerPlatformSpecificSettings performs platform specific container create functionality // createContainerOSSpecificSettings performs host-OS specific container create functionality
func (daemon *Daemon) createContainerPlatformSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error { func (daemon *Daemon) createContainerOSSpecificSettings(container *container.Container, config *containertypes.Config, hostConfig *containertypes.HostConfig) error {
if container.Platform == runtime.GOOS { if container.OS == runtime.GOOS {
// Make sure the host config has the default daemon isolation if not specified by caller. // Make sure the host config has the default daemon isolation if not specified by caller.
if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) { if containertypes.Isolation.IsDefault(containertypes.Isolation(hostConfig.Isolation)) {
hostConfig.Isolation = daemon.defaultIsolation hostConfig.Isolation = daemon.defaultIsolation
@ -26,7 +26,7 @@ func (daemon *Daemon) createContainerPlatformSpecificSettings(container *contain
} }
hostConfig.Isolation = "hyperv" hostConfig.Isolation = "hyperv"
} }
parser := volume.NewParser(container.Platform) parser := volume.NewParser(container.OS)
for spec := range config.Volumes { for spec := range config.Volumes {
mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver) mp, err := parser.ParseMountRaw(spec, hostConfig.VolumeDriver)

View file

@ -162,9 +162,9 @@ func (daemon *Daemon) restore() error {
} }
// Ignore the container if it does not support the current driver being used by the graph // Ignore the container if it does not support the current driver being used by the graph
currentDriverForContainerPlatform := daemon.stores[container.Platform].graphDriver currentDriverForContainerOS := daemon.stores[container.OS].graphDriver
if (container.Driver == "" && currentDriverForContainerPlatform == "aufs") || container.Driver == currentDriverForContainerPlatform { if (container.Driver == "" && currentDriverForContainerOS == "aufs") || container.Driver == currentDriverForContainerOS {
rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID) rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID)
if err != nil { if err != nil {
logrus.Errorf("Failed to load container mount %v: %v", id, err) logrus.Errorf("Failed to load container mount %v: %v", id, err)
continue continue
@ -664,7 +664,7 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
} }
var graphDrivers []string var graphDrivers []string
for platform, ds := range d.stores { for operatingSystem, ds := range d.stores {
ls, err := layer.NewStoreFromOptions(layer.StoreOptions{ ls, err := layer.NewStoreFromOptions(layer.StoreOptions{
StorePath: config.Root, StorePath: config.Root,
MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"), MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
@ -673,14 +673,14 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
IDMappings: idMappings, IDMappings: idMappings,
PluginGetter: d.PluginStore, PluginGetter: d.PluginStore,
ExperimentalEnabled: config.Experimental, ExperimentalEnabled: config.Experimental,
Platform: platform, OS: operatingSystem,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
} }
ds.graphDriver = ls.DriverName() // As layerstore may set the driver ds.graphDriver = ls.DriverName() // As layerstore may set the driver
ds.layerStore = ls ds.layerStore = ls
d.stores[platform] = ds d.stores[operatingSystem] = ds
graphDrivers = append(graphDrivers, ls.DriverName()) graphDrivers = append(graphDrivers, ls.DriverName())
} }
@ -691,13 +691,13 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads) logrus.Debugf("Max Concurrent Downloads: %d", *config.MaxConcurrentDownloads)
lsMap := make(map[string]layer.Store) lsMap := make(map[string]layer.Store)
for platform, ds := range d.stores { for operatingSystem, ds := range d.stores {
lsMap[platform] = ds.layerStore lsMap[operatingSystem] = ds.layerStore
} }
d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads) d.downloadManager = xfer.NewLayerDownloadManager(lsMap, *config.MaxConcurrentDownloads)
logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads) logrus.Debugf("Max Concurrent Uploads: %d", *config.MaxConcurrentUploads)
d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads) d.uploadManager = xfer.NewLayerUploadManager(*config.MaxConcurrentUploads)
for platform, ds := range d.stores { for operatingSystem, ds := range d.stores {
imageRoot := filepath.Join(config.Root, "image", ds.graphDriver) imageRoot := filepath.Join(config.Root, "image", ds.graphDriver)
ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb")) ifs, err := image.NewFSStoreBackend(filepath.Join(imageRoot, "imagedb"))
if err != nil { if err != nil {
@ -705,13 +705,13 @@ func NewDaemon(config *config.Config, registryService registry.Service, containe
} }
var is image.Store var is image.Store
is, err = image.NewImageStore(ifs, platform, ds.layerStore) is, err = image.NewImageStore(ifs, operatingSystem, ds.layerStore)
if err != nil { if err != nil {
return nil, err return nil, err
} }
ds.imageRoot = imageRoot ds.imageRoot = imageRoot
ds.imageStore = is ds.imageStore = is
d.stores[platform] = ds d.stores[operatingSystem] = ds
} }
// Configure the volumes driver // Configure the volumes driver
@ -921,7 +921,7 @@ func (daemon *Daemon) Shutdown() error {
logrus.Errorf("Stop container error: %v", err) logrus.Errorf("Stop container error: %v", err)
return return
} }
if mountid, err := daemon.stores[c.Platform].layerStore.GetMountID(c.ID); err == nil { if mountid, err := daemon.stores[c.OS].layerStore.GetMountID(c.ID); err == nil {
daemon.cleanupMountsByID(mountid) daemon.cleanupMountsByID(mountid)
} }
logrus.Debugf("container stopped %s", c.ID) logrus.Debugf("container stopped %s", c.ID)
@ -981,7 +981,7 @@ func (daemon *Daemon) Mount(container *container.Container) error {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
daemon.Unmount(container) daemon.Unmount(container)
return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')", return fmt.Errorf("Error: driver %s is returning inconsistent paths for container %s ('%s' then '%s')",
daemon.GraphDriverName(container.Platform), container.ID, container.BaseFS, dir) daemon.GraphDriverName(container.OS), container.ID, container.BaseFS, dir)
} }
} }
container.BaseFS = dir // TODO: combine these fields container.BaseFS = dir // TODO: combine these fields

View file

@ -493,12 +493,14 @@ func (daemon *Daemon) runAsHyperVContainer(hostConfig *containertypes.HostConfig
// conditionalMountOnStart is a platform specific helper function during the // conditionalMountOnStart is a platform specific helper function during the
// container start to call mount. // container start to call mount.
func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error { func (daemon *Daemon) conditionalMountOnStart(container *container.Container) error {
// Bail out now for Linux containers // Bail out now for Linux containers. We cannot mount the containers filesystem on the
if system.LCOWSupported() && container.Platform != "windows" { // host as it is a non-Windows filesystem.
if system.LCOWSupported() && container.OS != "windows" {
return nil return nil
} }
// We do not mount if a Hyper-V container // We do not mount if a Hyper-V container as it needs to be mounted inside the
// utility VM, not the host.
if !daemon.runAsHyperVContainer(container.HostConfig) { if !daemon.runAsHyperVContainer(container.HostConfig) {
return daemon.Mount(container) return daemon.Mount(container)
} }
@ -509,7 +511,7 @@ func (daemon *Daemon) conditionalMountOnStart(container *container.Container) er
// during the cleanup of a container to unmount. // during the cleanup of a container to unmount.
func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error { func (daemon *Daemon) conditionalUnmountOnCleanup(container *container.Container) error {
// Bail out now for Linux containers // Bail out now for Linux containers
if system.LCOWSupported() && container.Platform != "windows" { if system.LCOWSupported() && container.OS != "windows" {
return nil return nil
} }

View file

@ -116,10 +116,10 @@ func (daemon *Daemon) cleanupContainer(container *container.Container, forceRemo
// When container creation fails and `RWLayer` has not been created yet, we // When container creation fails and `RWLayer` has not been created yet, we
// do not call `ReleaseRWLayer` // do not call `ReleaseRWLayer`
if container.RWLayer != nil { if container.RWLayer != nil {
metadata, err := daemon.stores[container.Platform].layerStore.ReleaseRWLayer(container.RWLayer) metadata, err := daemon.stores[container.OS].layerStore.ReleaseRWLayer(container.RWLayer)
layer.LogReleaseMetadata(metadata) layer.LogReleaseMetadata(metadata)
if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) { if err != nil && err != layer.ErrMountDoesNotExist && !os.IsNotExist(errors.Cause(err)) {
return errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.Platform), container.ID) return errors.Wrapf(err, "driver %q failed to remove root filesystem for %s", daemon.GraphDriverName(container.OS), container.ID)
} }
} }

View file

@ -8,7 +8,7 @@ import (
func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error { func execSetPlatformOpt(c *container.Container, ec *exec.Config, p *libcontainerd.Process) error {
// Process arguments need to be escaped before sending to OCI. // Process arguments need to be escaped before sending to OCI.
if c.Platform == "windows" { if c.OS == "windows" {
p.Args = escapeArgs(p.Args) p.Args = escapeArgs(p.Args)
p.User.Username = ec.User p.User.Username = ec.User
} }

View file

@ -18,8 +18,8 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
return err return err
} }
if runtime.GOOS == "windows" && container.Platform == "windows" { if runtime.GOOS == "windows" && container.OS == "windows" {
return fmt.Errorf("the daemon on this platform does not support exporting Windows containers") return fmt.Errorf("the daemon on this operating system does not support exporting Windows containers")
} }
if container.IsDead() { if container.IsDead() {
@ -46,13 +46,13 @@ func (daemon *Daemon) ContainerExport(name string, out io.Writer) error {
} }
func (daemon *Daemon) containerExport(container *container.Container) (arch io.ReadCloser, err error) { func (daemon *Daemon) containerExport(container *container.Container) (arch io.ReadCloser, err error) {
rwlayer, err := daemon.stores[container.Platform].layerStore.GetRWLayer(container.ID) rwlayer, err := daemon.stores[container.OS].layerStore.GetRWLayer(container.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer func() { defer func() {
if err != nil { if err != nil {
daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer) daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
} }
}() }()
@ -73,7 +73,7 @@ func (daemon *Daemon) containerExport(container *container.Container) (arch io.R
arch = ioutils.NewReadCloserWrapper(archive, func() error { arch = ioutils.NewReadCloserWrapper(archive, func() error {
err := archive.Close() err := archive.Close()
rwlayer.Unmount() rwlayer.Unmount()
daemon.stores[container.Platform].layerStore.ReleaseRWLayer(rwlayer) daemon.stores[container.OS].layerStore.ReleaseRWLayer(rwlayer)
return err return err
}) })
daemon.LogContainerEvent(container, "export") daemon.LogContainerEvent(container, "export")

View file

@ -154,19 +154,8 @@ func (d *Driver) Status() [][2]string {
} }
} }
// panicIfUsedByLcow does exactly what it says.
// TODO @jhowardmsft - this is a temporary measure for the bring-up of
// Linux containers on Windows. It is a failsafe to ensure that the right
// graphdriver is used.
func panicIfUsedByLcow() {
if system.LCOWSupported() {
panic("inconsistency - windowsfilter graphdriver should not be used when in LCOW mode")
}
}
// Exists returns true if the given id is registered with this driver. // Exists returns true if the given id is registered with this driver.
func (d *Driver) Exists(id string) bool { func (d *Driver) Exists(id string) bool {
panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
return false return false
@ -181,7 +170,6 @@ func (d *Driver) Exists(id string) bool {
// CreateReadWrite creates a layer that is writable for use as a container // CreateReadWrite creates a layer that is writable for use as a container
// file system. // file system.
func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error { func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts) error {
panicIfUsedByLcow()
if opts != nil { if opts != nil {
return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt) return d.create(id, parent, opts.MountLabel, false, opts.StorageOpt)
} }
@ -190,7 +178,6 @@ func (d *Driver) CreateReadWrite(id, parent string, opts *graphdriver.CreateOpts
// Create creates a new read-only layer with the given id. // Create creates a new read-only layer with the given id.
func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error { func (d *Driver) Create(id, parent string, opts *graphdriver.CreateOpts) error {
panicIfUsedByLcow()
if opts != nil { if opts != nil {
return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt) return d.create(id, parent, opts.MountLabel, true, opts.StorageOpt)
} }
@ -274,7 +261,6 @@ func (d *Driver) dir(id string) string {
// Remove unmounts and removes the dir information. // Remove unmounts and removes the dir information.
func (d *Driver) Remove(id string) error { func (d *Driver) Remove(id string) error {
panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
return err return err
@ -356,7 +342,6 @@ func (d *Driver) Remove(id string) error {
// Get returns the rootfs path for the id. This will mount the dir at its given path. // Get returns the rootfs path for the id. This will mount the dir at its given path.
func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) { func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
panicIfUsedByLcow()
logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel) logrus.Debugf("WindowsGraphDriver Get() id %s mountLabel %s", id, mountLabel)
var dir string var dir string
@ -415,7 +400,6 @@ func (d *Driver) Get(id, mountLabel string) (containerfs.ContainerFS, error) {
// Put adds a new layer to the driver. // Put adds a new layer to the driver.
func (d *Driver) Put(id string) error { func (d *Driver) Put(id string) error {
panicIfUsedByLcow()
logrus.Debugf("WindowsGraphDriver Put() id %s", id) logrus.Debugf("WindowsGraphDriver Put() id %s", id)
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
@ -474,7 +458,6 @@ func (d *Driver) Cleanup() error {
// layer and its parent layer which may be "". // layer and its parent layer which may be "".
// The layer should be mounted when calling this function // The layer should be mounted when calling this function
func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) { func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
return return
@ -511,7 +494,6 @@ func (d *Driver) Diff(id, parent string) (_ io.ReadCloser, err error) {
// and its parent layer. If parent is "", then all changes will be ADD changes. // and its parent layer. If parent is "", then all changes will be ADD changes.
// The layer should not be mounted when calling this function. // The layer should not be mounted when calling this function.
func (d *Driver) Changes(id, parent string) ([]archive.Change, error) { func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
panicIfUsedByLcow()
rID, err := d.resolveID(id) rID, err := d.resolveID(id)
if err != nil { if err != nil {
return nil, err return nil, err
@ -567,7 +549,6 @@ func (d *Driver) Changes(id, parent string) ([]archive.Change, error) {
// new layer in bytes. // new layer in bytes.
// The layer should not be mounted when calling this function // The layer should not be mounted when calling this function
func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) { func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
panicIfUsedByLcow()
var layerChain []string var layerChain []string
if parent != "" { if parent != "" {
rPId, err := d.resolveID(parent) rPId, err := d.resolveID(parent)
@ -602,7 +583,6 @@ func (d *Driver) ApplyDiff(id, parent string, diff io.Reader) (int64, error) {
// and its parent and returns the size in bytes of the changes // and its parent and returns the size in bytes of the changes
// relative to its base filesystem directory. // relative to its base filesystem directory.
func (d *Driver) DiffSize(id, parent string) (size int64, err error) { func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
panicIfUsedByLcow()
rPId, err := d.resolveID(parent) rPId, err := d.resolveID(parent)
if err != nil { if err != nil {
return return
@ -624,7 +604,6 @@ func (d *Driver) DiffSize(id, parent string) (size int64, err error) {
// GetMetadata returns custom driver information. // GetMetadata returns custom driver information.
func (d *Driver) GetMetadata(id string) (map[string]string, error) { func (d *Driver) GetMetadata(id string) (map[string]string, error) {
panicIfUsedByLcow()
m := make(map[string]string) m := make(map[string]string)
m["dir"] = d.dir(id) m["dir"] = d.dir(id)
return m, nil return m, nil
@ -927,7 +906,6 @@ func (fg *fileGetCloserWithBackupPrivileges) Close() error {
// DiffGetter returns a FileGetCloser that can read files from the directory that // DiffGetter returns a FileGetCloser that can read files from the directory that
// contains files for the layer differences. Used for direct access for tar-split. // contains files for the layer differences. Used for direct access for tar-split.
func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) { func (d *Driver) DiffGetter(id string) (graphdriver.FileGetCloser, error) {
panicIfUsedByLcow()
id, err := d.resolveID(id) id, err := d.resolveID(id)
if err != nil { if err != nil {
return nil, err return nil, err

View file

@ -24,9 +24,9 @@ func (e errImageDoesNotExist) Error() string {
func (e errImageDoesNotExist) NotFound() {} func (e errImageDoesNotExist) NotFound() {}
// GetImageIDAndPlatform returns an image ID and platform corresponding to the image referred to by // GetImageIDAndOS returns an image ID and operating system corresponding to the image referred to by
// refOrID. // refOrID.
func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, error) { func (daemon *Daemon) GetImageIDAndOS(refOrID string) (image.ID, string, error) {
ref, err := reference.ParseAnyReference(refOrID) ref, err := reference.ParseAnyReference(refOrID)
if err != nil { if err != nil {
return "", "", validationError{err} return "", "", validationError{err}
@ -47,16 +47,16 @@ func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, e
} }
if digest, err := daemon.referenceStore.Get(namedRef); err == nil { if digest, err := daemon.referenceStore.Get(namedRef); err == nil {
// Search the image stores to get the platform, defaulting to host OS. // Search the image stores to get the operating system, defaulting to host OS.
imagePlatform := runtime.GOOS imageOS := runtime.GOOS
id := image.IDFromDigest(digest) id := image.IDFromDigest(digest)
for platform := range daemon.stores { for os := range daemon.stores {
if img, err := daemon.stores[platform].imageStore.Get(id); err == nil { if img, err := daemon.stores[os].imageStore.Get(id); err == nil {
imagePlatform = img.Platform() imageOS = img.OperatingSystem()
break break
} }
} }
return id, imagePlatform, nil return id, imageOS, nil
} }
// deprecated: repo:shortid https://github.com/docker/docker/pull/799 // deprecated: repo:shortid https://github.com/docker/docker/pull/799
@ -75,9 +75,9 @@ func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, e
} }
// Search based on ID // Search based on ID
for platform := range daemon.stores { for os := range daemon.stores {
if id, err := daemon.stores[platform].imageStore.Search(refOrID); err == nil { if id, err := daemon.stores[os].imageStore.Search(refOrID); err == nil {
return id, platform, nil return id, os, nil
} }
} }
@ -86,9 +86,9 @@ func (daemon *Daemon) GetImageIDAndPlatform(refOrID string) (image.ID, string, e
// GetImage returns an image corresponding to the image referred to by refOrID. // GetImage returns an image corresponding to the image referred to by refOrID.
func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) { func (daemon *Daemon) GetImage(refOrID string) (*image.Image, error) {
imgID, platform, err := daemon.GetImageIDAndPlatform(refOrID) imgID, os, err := daemon.GetImageIDAndOS(refOrID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return daemon.stores[platform].imageStore.Get(imgID) return daemon.stores[os].imageStore.Get(imgID)
} }

View file

@ -65,7 +65,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
start := time.Now() start := time.Now()
records := []types.ImageDeleteResponseItem{} records := []types.ImageDeleteResponseItem{}
imgID, platform, err := daemon.GetImageIDAndPlatform(imageRef) imgID, os, err := daemon.GetImageIDAndOS(imageRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -94,7 +94,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
return nil, err return nil, err
} }
parsedRef, err = daemon.removeImageRef(platform, parsedRef) parsedRef, err = daemon.removeImageRef(os, parsedRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -122,7 +122,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
remainingRefs := []reference.Named{} remainingRefs := []reference.Named{}
for _, repoRef := range repoRefs { for _, repoRef := range repoRefs {
if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() { if _, repoRefIsCanonical := repoRef.(reference.Canonical); repoRefIsCanonical && parsedRef.Name() == repoRef.Name() {
if _, err := daemon.removeImageRef(platform, repoRef); err != nil { if _, err := daemon.removeImageRef(os, repoRef); err != nil {
return records, err return records, err
} }
@ -152,12 +152,12 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
if !force { if !force {
c |= conflictSoft &^ conflictActiveReference c |= conflictSoft &^ conflictActiveReference
} }
if conflict := daemon.checkImageDeleteConflict(imgID, platform, c); conflict != nil { if conflict := daemon.checkImageDeleteConflict(imgID, os, c); conflict != nil {
return nil, conflict return nil, conflict
} }
for _, repoRef := range repoRefs { for _, repoRef := range repoRefs {
parsedRef, err := daemon.removeImageRef(platform, repoRef) parsedRef, err := daemon.removeImageRef(os, repoRef)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -170,7 +170,7 @@ func (daemon *Daemon) ImageDelete(imageRef string, force, prune bool) ([]types.I
} }
} }
if err := daemon.imageDeleteHelper(imgID, platform, &records, force, prune, removedRepositoryRef); err != nil { if err := daemon.imageDeleteHelper(imgID, os, &records, force, prune, removedRepositoryRef); err != nil {
return nil, err return nil, err
} }

View file

@ -8,7 +8,7 @@ import (
// TagImage creates the tag specified by newTag, pointing to the image named // TagImage creates the tag specified by newTag, pointing to the image named
// imageName (alternatively, imageName can also be an image ID). // imageName (alternatively, imageName can also be an image ID).
func (daemon *Daemon) TagImage(imageName, repository, tag string) error { func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
imageID, platform, err := daemon.GetImageIDAndPlatform(imageName) imageID, os, err := daemon.GetImageIDAndOS(imageName)
if err != nil { if err != nil {
return err return err
} }
@ -23,16 +23,16 @@ func (daemon *Daemon) TagImage(imageName, repository, tag string) error {
} }
} }
return daemon.TagImageWithReference(imageID, platform, newTag) return daemon.TagImageWithReference(imageID, os, newTag)
} }
// TagImageWithReference adds the given reference to the image ID provided. // TagImageWithReference adds the given reference to the image ID provided.
func (daemon *Daemon) TagImageWithReference(imageID image.ID, platform string, newTag reference.Named) error { func (daemon *Daemon) TagImageWithReference(imageID image.ID, os string, newTag reference.Named) error {
if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil { if err := daemon.referenceStore.AddTag(newTag, imageID.Digest(), true); err != nil {
return err return err
} }
if err := daemon.stores[platform].imageStore.SetLastUpdated(imageID); err != nil { if err := daemon.stores[os].imageStore.SetLastUpdated(imageID); err != nil {
return err return err
} }
daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag") daemon.LogImageEvent(imageID.String(), reference.FamiliarString(newTag), "tag")

View file

@ -36,7 +36,7 @@ func (r byCreated) Less(i, j int) bool { return r[i].Created < r[j].Created }
// Map returns a map of all images in the ImageStore // Map returns a map of all images in the ImageStore
func (daemon *Daemon) Map() map[image.ID]*image.Image { func (daemon *Daemon) Map() map[image.ID]*image.Image {
// TODO @jhowardmsft LCOW. This will need work to enumerate the stores for all platforms. // TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced
platform := runtime.GOOS platform := runtime.GOOS
if system.LCOWSupported() { if system.LCOWSupported() {
platform = "linux" platform = "linux"
@ -51,7 +51,7 @@ func (daemon *Daemon) Map() map[image.ID]*image.Image {
// the heads. // the heads.
func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) { func (daemon *Daemon) Images(imageFilters filters.Args, all bool, withExtraAttrs bool) ([]*types.ImageSummary, error) {
// TODO @jhowardmsft LCOW. This will need work to enumerate the stores for all platforms. // TODO @jhowardmsft LCOW. This can be removed when imagestores are coalesced
platform := runtime.GOOS platform := runtime.GOOS
if system.LCOWSupported() { if system.LCOWSupported() {
platform = "linux" platform = "linux"
@ -273,7 +273,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
var parentImg *image.Image var parentImg *image.Image
var parentChainID layer.ChainID var parentChainID layer.ChainID
if len(parent) != 0 { if len(parent) != 0 {
parentImg, err = daemon.stores[img.Platform()].imageStore.Get(image.ID(parent)) parentImg, err = daemon.stores[img.OperatingSystem()].imageStore.Get(image.ID(parent))
if err != nil { if err != nil {
return "", errors.Wrap(err, "error getting specified parent layer") return "", errors.Wrap(err, "error getting specified parent layer")
} }
@ -283,11 +283,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
parentImg = &image.Image{RootFS: rootFS} parentImg = &image.Image{RootFS: rootFS}
} }
l, err := daemon.stores[img.Platform()].layerStore.Get(img.RootFS.ChainID()) l, err := daemon.stores[img.OperatingSystem()].layerStore.Get(img.RootFS.ChainID())
if err != nil { if err != nil {
return "", errors.Wrap(err, "error getting image layer") return "", errors.Wrap(err, "error getting image layer")
} }
defer daemon.stores[img.Platform()].layerStore.Release(l) defer daemon.stores[img.OperatingSystem()].layerStore.Release(l)
ts, err := l.TarStreamFrom(parentChainID) ts, err := l.TarStreamFrom(parentChainID)
if err != nil { if err != nil {
@ -295,11 +295,11 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
} }
defer ts.Close() defer ts.Close()
newL, err := daemon.stores[img.Platform()].layerStore.Register(ts, parentChainID, layer.Platform(img.Platform())) newL, err := daemon.stores[img.OperatingSystem()].layerStore.Register(ts, parentChainID, layer.OS(img.OperatingSystem()))
if err != nil { if err != nil {
return "", errors.Wrap(err, "error registering layer") return "", errors.Wrap(err, "error registering layer")
} }
defer daemon.stores[img.Platform()].layerStore.Release(newL) defer daemon.stores[img.OperatingSystem()].layerStore.Release(newL)
newImage := *img newImage := *img
newImage.RootFS = nil newImage.RootFS = nil
@ -334,7 +334,7 @@ func (daemon *Daemon) SquashImage(id, parent string) (string, error) {
return "", errors.Wrap(err, "error marshalling image config") return "", errors.Wrap(err, "error marshalling image config")
} }
newImgID, err := daemon.stores[img.Platform()].imageStore.Create(b) newImgID, err := daemon.stores[img.OperatingSystem()].imageStore.Create(b)
if err != nil { if err != nil {
return "", errors.Wrap(err, "error creating new image after squash") return "", errors.Wrap(err, "error creating new image after squash")
} }

View file

@ -26,16 +26,16 @@ import (
// inConfig (if src is "-"), or from a URI specified in src. Progress output is // inConfig (if src is "-"), or from a URI specified in src. Progress output is
// written to outStream. Repository and tag names can optionally be given in // written to outStream. Repository and tag names can optionally be given in
// the repo and tag arguments, respectively. // the repo and tag arguments, respectively.
func (daemon *Daemon) ImportImage(src string, repository, platform string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error { func (daemon *Daemon) ImportImage(src string, repository, os string, tag string, msg string, inConfig io.ReadCloser, outStream io.Writer, changes []string) error {
var ( var (
rc io.ReadCloser rc io.ReadCloser
resp *http.Response resp *http.Response
newRef reference.Named newRef reference.Named
) )
// Default the platform if not supplied. // Default the operating system if not supplied.
if platform == "" { if os == "" {
platform = runtime.GOOS os = runtime.GOOS
} }
if repository != "" { if repository != "" {
@ -90,11 +90,11 @@ func (daemon *Daemon) ImportImage(src string, repository, platform string, tag s
if err != nil { if err != nil {
return err return err
} }
l, err := daemon.stores[platform].layerStore.Register(inflatedLayerData, "", layer.Platform(platform)) l, err := daemon.stores[os].layerStore.Register(inflatedLayerData, "", layer.OS(os))
if err != nil { if err != nil {
return err return err
} }
defer layer.ReleaseAndLog(daemon.stores[platform].layerStore, l) defer layer.ReleaseAndLog(daemon.stores[os].layerStore, l)
created := time.Now().UTC() created := time.Now().UTC()
imgConfig, err := json.Marshal(&image.Image{ imgConfig, err := json.Marshal(&image.Image{
@ -102,7 +102,7 @@ func (daemon *Daemon) ImportImage(src string, repository, platform string, tag s
DockerVersion: dockerversion.Version, DockerVersion: dockerversion.Version,
Config: config, Config: config,
Architecture: runtime.GOARCH, Architecture: runtime.GOARCH,
OS: platform, OS: os,
Created: created, Created: created,
Comment: msg, Comment: msg,
}, },
@ -119,14 +119,14 @@ func (daemon *Daemon) ImportImage(src string, repository, platform string, tag s
return err return err
} }
id, err := daemon.stores[platform].imageStore.Create(imgConfig) id, err := daemon.stores[os].imageStore.Create(imgConfig)
if err != nil { if err != nil {
return err return err
} }
// FIXME: connect with commit code and call refstore directly // FIXME: connect with commit code and call refstore directly
if newRef != nil { if newRef != nil {
if err := daemon.TagImageWithReference(id, platform, newRef); err != nil { if err := daemon.TagImageWithReference(id, os, newRef); err != nil {
return err return err
} }
} }

View file

@ -171,7 +171,7 @@ func (daemon *Daemon) getInspectData(container *container.Container) (*types.Con
Name: container.Name, Name: container.Name,
RestartCount: container.RestartCount, RestartCount: container.RestartCount,
Driver: container.Driver, Driver: container.Driver,
Platform: container.Platform, OS: container.OS,
MountLabel: container.MountLabel, MountLabel: container.MountLabel,
ProcessLabel: container.ProcessLabel, ProcessLabel: container.ProcessLabel,
ExecIDs: container.GetExecIDs(), ExecIDs: container.GetExecIDs(),

View file

@ -322,7 +322,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
if psFilters.Contains("ancestor") { if psFilters.Contains("ancestor") {
ancestorFilter = true ancestorFilter = true
psFilters.WalkValues("ancestor", func(ancestor string) error { psFilters.WalkValues("ancestor", func(ancestor string) error {
id, platform, err := daemon.GetImageIDAndPlatform(ancestor) id, os, err := daemon.GetImageIDAndOS(ancestor)
if err != nil { if err != nil {
logrus.Warnf("Error while looking up for image %v", ancestor) logrus.Warnf("Error while looking up for image %v", ancestor)
return nil return nil
@ -332,7 +332,7 @@ func (daemon *Daemon) foldFilter(view container.View, config *types.ContainerLis
return nil return nil
} }
// Then walk down the graph and put the imageIds in imagesFilter // Then walk down the graph and put the imageIds in imagesFilter
populateImageFilterByParents(imagesFilter, id, daemon.stores[platform].imageStore.Children) populateImageFilterByParents(imagesFilter, id, daemon.stores[os].imageStore.Children)
return nil return nil
}) })
} }
@ -566,7 +566,7 @@ func (daemon *Daemon) refreshImage(s *container.Snapshot, ctx *listContext) (*ty
c := s.Container c := s.Container
image := s.Image // keep the original ref if still valid (hasn't changed) image := s.Image // keep the original ref if still valid (hasn't changed)
if image != s.ImageID { if image != s.ImageID {
id, _, err := daemon.GetImageIDAndPlatform(image) id, _, err := daemon.GetImageIDAndOS(image)
if _, isDNE := err.(errImageDoesNotExist); err != nil && !isDNE { if _, isDNE := err.(errImageDoesNotExist); err != nil && !isDNE {
return nil, err return nil, err
} }

View file

@ -138,9 +138,9 @@ func (daemon *Daemon) createSpec(c *container.Container) (*specs.Spec, error) {
max := len(img.RootFS.DiffIDs) max := len(img.RootFS.DiffIDs)
for i := 1; i <= max; i++ { for i := 1; i <= max; i++ {
img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i] img.RootFS.DiffIDs = img.RootFS.DiffIDs[:i]
layerPath, err := layer.GetLayerPath(daemon.stores[c.Platform].layerStore, img.RootFS.ChainID()) layerPath, err := layer.GetLayerPath(daemon.stores[c.OS].layerStore, img.RootFS.ChainID())
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.stores[c.Platform].layerStore, img.RootFS.ChainID(), err) return nil, fmt.Errorf("failed to get layer path from graphdriver %s for ImageID %s - %s", daemon.stores[c.OS].layerStore, img.RootFS.ChainID(), err)
} }
// Reverse order, expecting parent most first // Reverse order, expecting parent most first
s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...) s.Windows.LayerFolders = append([]string{layerPath}, s.Windows.LayerFolders...)

View file

@ -79,7 +79,7 @@ func (daemon *Daemon) ContainerStart(name string, hostConfig *containertypes.Hos
// check if hostConfig is in line with the current system settings. // check if hostConfig is in line with the current system settings.
// It may happen cgroups are umounted or the like. // It may happen cgroups are umounted or the like.
if _, err = daemon.verifyContainerSettings(container.Platform, container.HostConfig, nil, false); err != nil { if _, err = daemon.verifyContainerSettings(container.OS, container.HostConfig, nil, false); err != nil {
return validationError{err} return validationError{err}
} }
// Adapt for old containers in case we have updates in this function and // Adapt for old containers in case we have updates in this function and
@ -191,7 +191,7 @@ func (daemon *Daemon) Cleanup(container *container.Container) {
if err := daemon.conditionalUnmountOnCleanup(container); err != nil { if err := daemon.conditionalUnmountOnCleanup(container); err != nil {
// FIXME: remove once reference counting for graphdrivers has been refactored // FIXME: remove once reference counting for graphdrivers has been refactored
// Ensure that all the mounts are gone // Ensure that all the mounts are gone
if mountid, err := daemon.stores[container.Platform].layerStore.GetMountID(container.ID); err == nil { if mountid, err := daemon.stores[container.OS].layerStore.GetMountID(container.ID); err == nil {
daemon.cleanupMountsByID(mountid) daemon.cleanupMountsByID(mountid)
} }
} }

View file

@ -10,7 +10,7 @@ func (daemon *Daemon) getLibcontainerdCreateOptions(container *container.Contain
createOptions := []libcontainerd.CreateOption{} createOptions := []libcontainerd.CreateOption{}
// LCOW options. // LCOW options.
if container.Platform == "linux" { if container.OS == "linux" {
config := &client.Config{} config := &client.Config{}
if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil { if err := config.GenerateDefault(daemon.configStore.GraphOptions); err != nil {
return nil, err return nil, err

View file

@ -16,7 +16,7 @@ func (daemon *Daemon) ContainerUpdate(name string, hostConfig *container.HostCon
return container.ContainerUpdateOKBody{Warnings: warnings}, err return container.ContainerUpdateOKBody{Warnings: warnings}, err
} }
warnings, err = daemon.verifyContainerSettings(c.Platform, hostConfig, nil, true) warnings, err = daemon.verifyContainerSettings(c.OS, hostConfig, nil, true)
if err != nil { if err != nil {
return container.ContainerUpdateOKBody{Warnings: warnings}, validationError{err} return container.ContainerUpdateOKBody{Warnings: warnings}, validationError{err}
} }

View file

@ -75,7 +75,7 @@ func (m mounts) parts(i int) int {
func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) { func (daemon *Daemon) registerMountPoints(container *container.Container, hostConfig *containertypes.HostConfig) (retErr error) {
binds := map[string]bool{} binds := map[string]bool{}
mountPoints := map[string]*volume.MountPoint{} mountPoints := map[string]*volume.MountPoint{}
parser := volume.NewParser(container.Platform) parser := volume.NewParser(container.OS)
defer func() { defer func() {
// clean up the container mountpoints once return with error // clean up the container mountpoints once return with error
if retErr != nil { if retErr != nil {
@ -256,7 +256,7 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {
container.Lock() container.Lock()
defer container.Unlock() defer container.Unlock()
parser := volume.NewParser(container.Platform) parser := volume.NewParser(container.OS)
maybeUpdate := make(map[string]bool) maybeUpdate := make(map[string]bool)
for _, mp := range container.MountPoints { for _, mp := range container.MountPoints {

View file

@ -86,7 +86,7 @@ type ImagePushConfig struct {
type ImageConfigStore interface { type ImageConfigStore interface {
Put([]byte) (digest.Digest, error) Put([]byte) (digest.Digest, error)
Get(digest.Digest) ([]byte, error) Get(digest.Digest) ([]byte, error)
RootFSAndPlatformFromConfig([]byte) (*image.RootFS, layer.Platform, error) RootFSAndOSFromConfig([]byte) (*image.RootFS, layer.OS, error)
} }
// PushLayerProvider provides layers to be pushed by ChainID. // PushLayerProvider provides layers to be pushed by ChainID.
@ -112,7 +112,7 @@ type RootFSDownloadManager interface {
// returns the final rootfs. // returns the final rootfs.
// Given progress output to track download progress // Given progress output to track download progress
// Returns function to release download resources // Returns function to release download resources
Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error)
} }
type imageConfigStore struct { type imageConfigStore struct {
@ -140,7 +140,7 @@ func (s *imageConfigStore) Get(d digest.Digest) ([]byte, error) {
return img.RawJSON(), nil return img.RawJSON(), nil
} }
func (s *imageConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) { func (s *imageConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
var unmarshalledConfig image.Image var unmarshalledConfig image.Image
if err := json.Unmarshal(c, &unmarshalledConfig); err != nil { if err := json.Unmarshal(c, &unmarshalledConfig); err != nil {
return nil, "", err return nil, "", err
@ -154,11 +154,11 @@ func (s *imageConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS,
return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS) return nil, "", fmt.Errorf("image operating system %q cannot be used on this platform", unmarshalledConfig.OS)
} }
platform := "" os := ""
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
platform = unmarshalledConfig.OS os = unmarshalledConfig.OS
} }
return unmarshalledConfig.RootFS, layer.Platform(platform), nil return unmarshalledConfig.RootFS, layer.OS(os), nil
} }
type storeLayerProvider struct { type storeLayerProvider struct {

View file

@ -2,6 +2,7 @@ package distribution
import ( import (
"fmt" "fmt"
"runtime"
"github.com/docker/distribution/reference" "github.com/docker/distribution/reference"
"github.com/docker/docker/api" "github.com/docker/docker/api"
@ -20,7 +21,7 @@ type Puller interface {
// Pull tries to pull the image referenced by `tag` // Pull tries to pull the image referenced by `tag`
// Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint. // Pull returns an error if any, as well as a boolean that determines whether to retry Pull on the next configured endpoint.
// //
Pull(ctx context.Context, ref reference.Named) error Pull(ctx context.Context, ref reference.Named, platform string) error
} }
// newPuller returns a Puller interface that will pull from either a v1 or v2 // newPuller returns a Puller interface that will pull from either a v1 or v2
@ -113,7 +114,13 @@ func Pull(ctx context.Context, ref reference.Named, imagePullConfig *ImagePullCo
lastErr = err lastErr = err
continue continue
} }
if err := puller.Pull(ctx, ref); err != nil {
// Make sure we default the platform if it hasn't been supplied
if imagePullConfig.Platform == "" {
imagePullConfig.Platform = runtime.GOOS
}
if err := puller.Pull(ctx, ref, imagePullConfig.Platform); err != nil {
// Was this pull cancelled? If so, don't try to fall // Was this pull cancelled? If so, don't try to fall
// back. // back.
fallback := false fallback := false

View file

@ -35,7 +35,7 @@ type v1Puller struct {
session *registry.Session session *registry.Session
} }
func (p *v1Puller) Pull(ctx context.Context, ref reference.Named) error { func (p *v1Puller) Pull(ctx context.Context, ref reference.Named, platform string) error {
if _, isCanonical := ref.(reference.Canonical); isCanonical { if _, isCanonical := ref.(reference.Canonical); isCanonical {
// Allowing fallback, because HTTPS v1 is before HTTP v2 // Allowing fallback, because HTTPS v1 is before HTTP v2
return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}} return fallbackError{err: ErrNoSupport{Err: errors.New("Cannot pull by digest with v1 registry")}}

View file

@ -8,6 +8,7 @@ import (
"net/url" "net/url"
"os" "os"
"runtime" "runtime"
"strings"
"github.com/docker/distribution" "github.com/docker/distribution"
"github.com/docker/distribution/manifest/manifestlist" "github.com/docker/distribution/manifest/manifestlist"
@ -61,7 +62,7 @@ type v2Puller struct {
confirmedV2 bool confirmedV2 bool
} }
func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) { func (p *v2Puller) Pull(ctx context.Context, ref reference.Named, platform string) (err error) {
// TODO(tiborvass): was ReceiveTimeout // TODO(tiborvass): was ReceiveTimeout
p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull") p.repo, p.confirmedV2, err = NewV2Repository(ctx, p.repoInfo, p.endpoint, p.config.MetaHeaders, p.config.AuthConfig, "pull")
if err != nil { if err != nil {
@ -69,7 +70,7 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
return err return err
} }
if err = p.pullV2Repository(ctx, ref); err != nil { if err = p.pullV2Repository(ctx, ref, platform); err != nil {
if _, ok := err.(fallbackError); ok { if _, ok := err.(fallbackError); ok {
return err return err
} }
@ -84,10 +85,10 @@ func (p *v2Puller) Pull(ctx context.Context, ref reference.Named) (err error) {
return err return err
} }
func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (err error) { func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named, platform string) (err error) {
var layersDownloaded bool var layersDownloaded bool
if !reference.IsNameOnly(ref) { if !reference.IsNameOnly(ref) {
layersDownloaded, err = p.pullV2Tag(ctx, ref) layersDownloaded, err = p.pullV2Tag(ctx, ref, platform)
if err != nil { if err != nil {
return err return err
} }
@ -109,7 +110,7 @@ func (p *v2Puller) pullV2Repository(ctx context.Context, ref reference.Named) (e
if err != nil { if err != nil {
return err return err
} }
pulledNew, err := p.pullV2Tag(ctx, tagRef) pulledNew, err := p.pullV2Tag(ctx, tagRef, platform)
if err != nil { if err != nil {
// Since this is the pull-all-tags case, don't // Since this is the pull-all-tags case, don't
// allow an error pulling a particular tag to // allow an error pulling a particular tag to
@ -325,7 +326,7 @@ func (ld *v2LayerDescriptor) Registered(diffID layer.DiffID) {
ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()}) ld.V2MetadataService.Add(diffID, metadata.V2Metadata{Digest: ld.digest, SourceRepository: ld.repoInfo.Name.Name()})
} }
func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdated bool, err error) { func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named, os string) (tagUpdated bool, err error) {
manSvc, err := p.repo.Manifests(ctx) manSvc, err := p.repo.Manifests(ctx)
if err != nil { if err != nil {
return false, err return false, err
@ -389,17 +390,17 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
if p.config.RequireSchema2 { if p.config.RequireSchema2 {
return false, fmt.Errorf("invalid manifest: not schema2") return false, fmt.Errorf("invalid manifest: not schema2")
} }
id, manifestDigest, err = p.pullSchema1(ctx, ref, v) id, manifestDigest, err = p.pullSchema1(ctx, ref, v, os)
if err != nil { if err != nil {
return false, err return false, err
} }
case *schema2.DeserializedManifest: case *schema2.DeserializedManifest:
id, manifestDigest, err = p.pullSchema2(ctx, ref, v) id, manifestDigest, err = p.pullSchema2(ctx, ref, v, os)
if err != nil { if err != nil {
return false, err return false, err
} }
case *manifestlist.DeserializedManifestList: case *manifestlist.DeserializedManifestList:
id, manifestDigest, err = p.pullManifestList(ctx, ref, v) id, manifestDigest, err = p.pullManifestList(ctx, ref, v, os)
if err != nil { if err != nil {
return false, err return false, err
} }
@ -435,7 +436,7 @@ func (p *v2Puller) pullV2Tag(ctx context.Context, ref reference.Named) (tagUpdat
return true, nil return true, nil
} }
func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) { func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unverifiedManifest *schema1.SignedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
var verifiedManifest *schema1.Manifest var verifiedManifest *schema1.Manifest
verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref) verifiedManifest, err = verifySchema1Manifest(unverifiedManifest, ref)
if err != nil { if err != nil {
@ -490,7 +491,8 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
// The v1 manifest itself doesn't directly contain a platform. However, // The v1 manifest itself doesn't directly contain a platform. However,
// the history does, but unfortunately that's a string, so search through // the history does, but unfortunately that's a string, so search through
// all the history until hopefully we find one which indicates the os. // all the history until hopefully we find one which indicates the os.
platform := runtime.GOOS // supertest2014/nyan is an example of a registry image with schemav1.
configOS := runtime.GOOS
if system.LCOWSupported() { if system.LCOWSupported() {
type config struct { type config struct {
Os string `json:"os,omitempty"` Os string `json:"os,omitempty"`
@ -499,14 +501,20 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
var c config var c config
if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil { if err := json.Unmarshal([]byte(v.V1Compatibility), &c); err == nil {
if c.Os != "" { if c.Os != "" {
platform = c.Os configOS = c.Os
break break
} }
} }
} }
} }
resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, layer.Platform(platform), descriptors, p.config.ProgressOutput) // 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) {
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
}
resultRootFS, release, err := p.config.DownloadManager.Download(ctx, *rootFS, layer.OS(configOS), descriptors, p.config.ProgressOutput)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -527,7 +535,7 @@ func (p *v2Puller) pullSchema1(ctx context.Context, ref reference.Reference, unv
return imageID, manifestDigest, nil return imageID, manifestDigest, nil
} }
func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest) (id digest.Digest, manifestDigest digest.Digest, err error) { func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *schema2.DeserializedManifest, requestedOS string) (id digest.Digest, manifestDigest digest.Digest, err error) {
manifestDigest, err = schema2ManifestDigest(ref, mfst) manifestDigest, err = schema2ManifestDigest(ref, mfst)
if err != nil { if err != nil {
return "", "", err return "", "", err
@ -576,11 +584,11 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
}() }()
var ( var (
configJSON []byte // raw serialized image config configJSON []byte // raw serialized image config
downloadedRootFS *image.RootFS // rootFS from registered layers downloadedRootFS *image.RootFS // rootFS from registered layers
configRootFS *image.RootFS // rootFS from configuration configRootFS *image.RootFS // rootFS from configuration
release func() // release resources from rootFS download release func() // release resources from rootFS download
platform layer.Platform // for LCOW when registering downloaded layers configOS layer.OS // for LCOW when registering downloaded layers
) )
// https://github.com/docker/docker/issues/24766 - Err on the side of caution, // https://github.com/docker/docker/issues/24766 - Err on the side of caution,
@ -592,7 +600,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
// check to block Windows images being pulled on Linux is implemented, it // check to block Windows images being pulled on Linux is implemented, it
// may be necessary to perform the same type of serialisation. // may be necessary to perform the same type of serialisation.
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
configJSON, configRootFS, platform, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) configJSON, configRootFS, configOS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
@ -605,6 +613,12 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
return "", "", errRootFSMismatch return "", "", errRootFSMismatch
} }
// 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) {
return "", "", fmt.Errorf("cannot download image with operating system %q when requesting %q", configOS, requestedOS)
}
// Populate diff ids in descriptors to avoid downloading foreign layers // Populate diff ids in descriptors to avoid downloading foreign layers
// which have been side loaded // which have been side loaded
for i := range descriptors { for i := range descriptors {
@ -619,7 +633,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
rootFS image.RootFS rootFS image.RootFS
) )
downloadRootFS := *image.NewRootFS() downloadRootFS := *image.NewRootFS()
rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, platform, descriptors, p.config.ProgressOutput) rootFS, release, err = p.config.DownloadManager.Download(ctx, downloadRootFS, layer.OS(requestedOS), descriptors, p.config.ProgressOutput)
if err != nil { if err != nil {
// Intentionally do not cancel the config download here // Intentionally do not cancel the config download here
// as the error from config download (if there is one) // as the error from config download (if there is one)
@ -637,7 +651,7 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
} }
if configJSON == nil { if configJSON == nil {
configJSON, configRootFS, _, err = receiveConfig(p.config.ImageStore, configChan, configErrChan) configJSON, configRootFS, configOS, err = receiveConfig(p.config.ImageStore, configChan, configErrChan)
if err == nil && configRootFS == nil { if err == nil && configRootFS == nil {
err = errRootFSInvalid err = errRootFSInvalid
} }
@ -684,14 +698,14 @@ func (p *v2Puller) pullSchema2(ctx context.Context, ref reference.Named, mfst *s
return imageID, manifestDigest, nil return imageID, manifestDigest, nil
} }
func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, layer.Platform, error) { func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan error) ([]byte, *image.RootFS, layer.OS, error) {
select { select {
case configJSON := <-configChan: case configJSON := <-configChan:
rootfs, platform, err := s.RootFSAndPlatformFromConfig(configJSON) rootfs, os, err := s.RootFSAndOSFromConfig(configJSON)
if err != nil { if err != nil {
return nil, nil, "", err return nil, nil, "", err
} }
return configJSON, rootfs, platform, nil return configJSON, rootfs, os, nil
case err := <-errChan: case err := <-errChan:
return nil, nil, "", err return nil, nil, "", err
// Don't need a case for ctx.Done in the select because cancellation // Don't need a case for ctx.Done in the select because cancellation
@ -701,18 +715,18 @@ func receiveConfig(s ImageConfigStore, configChan <-chan []byte, errChan <-chan
// pullManifestList handles "manifest lists" which point to various // pullManifestList handles "manifest lists" which point to various
// platform-specific manifests. // platform-specific manifests.
func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList) (id digest.Digest, manifestListDigest digest.Digest, err error) { func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mfstList *manifestlist.DeserializedManifestList, os string) (id digest.Digest, manifestListDigest digest.Digest, err error) {
manifestListDigest, err = schema2ManifestDigest(ref, mfstList) manifestListDigest, err = schema2ManifestDigest(ref, mfstList)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
logrus.Debugf("%s resolved to a manifestList object with %d entries; looking for a os/arch match", ref, len(mfstList.Manifests)) 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)
if len(manifestMatches) == 0 { if len(manifestMatches) == 0 {
errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", runtime.GOOS, runtime.GOARCH) errMsg := fmt.Sprintf("no matching manifest for %s/%s in the manifest list entries", os, runtime.GOARCH)
logrus.Debugf(errMsg) logrus.Debugf(errMsg)
return "", "", errors.New(errMsg) return "", "", errors.New(errMsg)
} }
@ -739,12 +753,12 @@ func (p *v2Puller) pullManifestList(ctx context.Context, ref reference.Named, mf
switch v := manifest.(type) { switch v := manifest.(type) {
case *schema1.SignedManifest: case *schema1.SignedManifest:
id, _, err = p.pullSchema1(ctx, manifestRef, v) id, _, err = p.pullSchema1(ctx, manifestRef, v, os)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
case *schema2.DeserializedManifest: case *schema2.DeserializedManifest:
id, _, err = p.pullSchema2(ctx, manifestRef, v) id, _, err = p.pullSchema2(ctx, manifestRef, v, os)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }

View file

@ -76,12 +76,12 @@ func filterManifests(manifests []manifestlist.ManifestDescriptor) []manifestlist
var matches []manifestlist.ManifestDescriptor var matches []manifestlist.ManifestDescriptor
for _, manifestDescriptor := range manifests { for _, manifestDescriptor := range manifests {
if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS { if manifestDescriptor.Platform.Architecture == runtime.GOARCH && manifestDescriptor.Platform.OS == lookingForOS {
if !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) { if lookingForOS == "windows" && !versionMatch(manifestDescriptor.Platform.OSVersion, osVersion) {
continue continue
} }
matches = append(matches, manifestDescriptor) 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", lookingForOS, runtime.GOARCH, manifestDescriptor.MediaType, manifestDescriptor.Digest.String())
} }
} }
sort.Stable(manifestsByVersion(matches)) sort.Stable(manifestsByVersion(matches))

View file

@ -118,7 +118,7 @@ func (p *v2Pusher) pushV2Tag(ctx context.Context, ref reference.NamedTagged, id
return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err) return fmt.Errorf("could not find image from tag %s: %v", reference.FamiliarString(ref), err)
} }
rootfs, _, err := p.config.ImageStore.RootFSAndPlatformFromConfig(imgConfig) rootfs, _, err := p.config.ImageStore.RootFSAndOSFromConfig(imgConfig)
if err != nil { if err != nil {
return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err) return fmt.Errorf("unable to get rootfs for image %s: %s", reference.FamiliarString(ref), err)
} }

View file

@ -83,7 +83,7 @@ func testTokenPassThru(t *testing.T, ts *httptest.Server) {
logrus.Debug("About to pull") logrus.Debug("About to pull")
// We expect it to fail, since we haven't mock'd the full registry exchange in our handler above // We expect it to fail, since we haven't mock'd the full registry exchange in our handler above
tag, _ := reference.WithTag(n, "tag_goes_here") tag, _ := reference.WithTag(n, "tag_goes_here")
_ = p.pullV2Repository(ctx, tag) _ = p.pullV2Repository(ctx, tag, runtime.GOOS)
} }
func TestTokenPassThru(t *testing.T) { func TestTokenPassThru(t *testing.T) {

View file

@ -95,7 +95,7 @@ type DownloadDescriptorWithRegistered interface {
// Download method is called to get the layer tar data. Layers are then // Download method is called to get the layer tar data. Layers are then
// registered in the appropriate order. The caller must call the returned // registered in the appropriate order. The caller must call the returned
// release function once it is done with the returned RootFS object. // release function once it is done with the returned RootFS object.
func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
var ( var (
topLayer layer.Layer topLayer layer.Layer
topDownload *downloadTransfer topDownload *downloadTransfer
@ -105,9 +105,9 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
downloadsByKey = make(map[string]*downloadTransfer) downloadsByKey = make(map[string]*downloadTransfer)
) )
// Assume that the platform is the host OS if blank // Assume that the operating system is the host OS if blank
if platform == "" { if os == "" {
platform = layer.Platform(runtime.GOOS) os = layer.OS(runtime.GOOS)
} }
rootFS := initialRootFS rootFS := initialRootFS
@ -121,13 +121,13 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
if err == nil { if err == nil {
getRootFS := rootFS getRootFS := rootFS
getRootFS.Append(diffID) getRootFS.Append(diffID)
l, err := ldm.layerStores[string(platform)].Get(getRootFS.ChainID()) l, err := ldm.layerStores[string(os)].Get(getRootFS.ChainID())
if err == nil { if err == nil {
// Layer already exists. // Layer already exists.
logrus.Debugf("Layer already exists: %s", descriptor.ID()) logrus.Debugf("Layer already exists: %s", descriptor.ID())
progress.Update(progressOutput, descriptor.ID(), "Already exists") progress.Update(progressOutput, descriptor.ID(), "Already exists")
if topLayer != nil { if topLayer != nil {
layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
} }
topLayer = l topLayer = l
missingLayer = false missingLayer = false
@ -146,7 +146,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
// the stack? If so, avoid downloading it more than once. // the stack? If so, avoid downloading it more than once.
var topDownloadUncasted Transfer var topDownloadUncasted Transfer
if existingDownload, ok := downloadsByKey[key]; ok { if existingDownload, ok := downloadsByKey[key]; ok {
xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload, platform) xferFunc := ldm.makeDownloadFuncFromDownload(descriptor, existingDownload, topDownload, os)
defer topDownload.Transfer.Release(watcher) defer topDownload.Transfer.Release(watcher)
topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
topDownload = topDownloadUncasted.(*downloadTransfer) topDownload = topDownloadUncasted.(*downloadTransfer)
@ -158,10 +158,10 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
var xferFunc DoFunc var xferFunc DoFunc
if topDownload != nil { if topDownload != nil {
xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload, platform) xferFunc = ldm.makeDownloadFunc(descriptor, "", topDownload, os)
defer topDownload.Transfer.Release(watcher) defer topDownload.Transfer.Release(watcher)
} else { } else {
xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil, platform) xferFunc = ldm.makeDownloadFunc(descriptor, rootFS.ChainID(), nil, os)
} }
topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput) topDownloadUncasted, watcher = ldm.tm.Transfer(transferKey, xferFunc, progressOutput)
topDownload = topDownloadUncasted.(*downloadTransfer) topDownload = topDownloadUncasted.(*downloadTransfer)
@ -171,7 +171,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
if topDownload == nil { if topDownload == nil {
return rootFS, func() { return rootFS, func() {
if topLayer != nil { if topLayer != nil {
layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
} }
}, nil }, nil
} }
@ -182,7 +182,7 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
defer func() { defer func() {
if topLayer != nil { if topLayer != nil {
layer.ReleaseAndLog(ldm.layerStores[string(platform)], topLayer) layer.ReleaseAndLog(ldm.layerStores[string(os)], topLayer)
} }
}() }()
@ -218,11 +218,11 @@ func (ldm *LayerDownloadManager) Download(ctx context.Context, initialRootFS ima
// complete before the registration step, and registers the downloaded data // complete before the registration step, and registers the downloaded data
// on top of parentDownload's resulting layer. Otherwise, it registers the // on top of parentDownload's resulting layer. Otherwise, it registers the
// layer on top of the ChainID given by parentLayer. // layer on top of the ChainID given by parentLayer.
func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, platform layer.Platform) DoFunc { func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor, parentLayer layer.ChainID, parentDownload *downloadTransfer, os layer.OS) DoFunc {
return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
d := &downloadTransfer{ d := &downloadTransfer{
Transfer: NewTransfer(), Transfer: NewTransfer(),
layerStore: ldm.layerStores[string(platform)], layerStore: ldm.layerStores[string(os)],
} }
go func() { go func() {
@ -341,9 +341,9 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
src = fs.Descriptor() src = fs.Descriptor()
} }
if ds, ok := d.layerStore.(layer.DescribableStore); ok { if ds, ok := d.layerStore.(layer.DescribableStore); ok {
d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, platform, src) d.layer, err = ds.RegisterWithDescriptor(inflatedLayerData, parentLayer, os, src)
} else { } else {
d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer, platform) d.layer, err = d.layerStore.Register(inflatedLayerData, parentLayer, os)
} }
if err != nil { if err != nil {
select { select {
@ -382,11 +382,11 @@ func (ldm *LayerDownloadManager) makeDownloadFunc(descriptor DownloadDescriptor,
// parentDownload. This function does not log progress output because it would // parentDownload. This function does not log progress output because it would
// interfere with the progress reporting for sourceDownload, which has the same // interfere with the progress reporting for sourceDownload, which has the same
// Key. // Key.
func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, platform layer.Platform) DoFunc { func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor DownloadDescriptor, sourceDownload *downloadTransfer, parentDownload *downloadTransfer, os layer.OS) DoFunc {
return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer { return func(progressChan chan<- progress.Progress, start <-chan struct{}, inactive chan<- struct{}) Transfer {
d := &downloadTransfer{ d := &downloadTransfer{
Transfer: NewTransfer(), Transfer: NewTransfer(),
layerStore: ldm.layerStores[string(platform)], layerStore: ldm.layerStores[string(os)],
} }
go func() { go func() {
@ -440,9 +440,9 @@ func (ldm *LayerDownloadManager) makeDownloadFuncFromDownload(descriptor Downloa
src = fs.Descriptor() src = fs.Descriptor()
} }
if ds, ok := d.layerStore.(layer.DescribableStore); ok { if ds, ok := d.layerStore.(layer.DescribableStore); ok {
d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, platform, src) d.layer, err = ds.RegisterWithDescriptor(layerReader, parentLayer, os, src)
} else { } else {
d.layer, err = d.layerStore.Register(layerReader, parentLayer, platform) d.layer, err = d.layerStore.Register(layerReader, parentLayer, os)
} }
if err != nil { if err != nil {
d.err = fmt.Errorf("failed to register layer: %v", err) d.err = fmt.Errorf("failed to register layer: %v", err)

View file

@ -26,7 +26,7 @@ type mockLayer struct {
diffID layer.DiffID diffID layer.DiffID
chainID layer.ChainID chainID layer.ChainID
parent layer.Layer parent layer.Layer
platform layer.Platform os layer.OS
} }
func (ml *mockLayer) TarStream() (io.ReadCloser, error) { func (ml *mockLayer) TarStream() (io.ReadCloser, error) {
@ -57,8 +57,8 @@ func (ml *mockLayer) DiffSize() (size int64, err error) {
return 0, nil return 0, nil
} }
func (ml *mockLayer) Platform() layer.Platform { func (ml *mockLayer) OS() layer.OS {
return ml.platform return ml.os
} }
func (ml *mockLayer) Metadata() (map[string]string, error) { func (ml *mockLayer) Metadata() (map[string]string, error) {
@ -91,7 +91,7 @@ func (ls *mockLayerStore) Map() map[layer.ChainID]layer.Layer {
return layers return layers
} }
func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, platform layer.Platform) (layer.Layer, error) { func (ls *mockLayerStore) Register(reader io.Reader, parentID layer.ChainID, os layer.OS) (layer.Layer, error) {
return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{}) return ls.RegisterWithDescriptor(reader, parentID, distribution.Descriptor{})
} }
@ -293,13 +293,13 @@ func TestSuccessfulDownload(t *testing.T) {
firstDescriptor := descriptors[0].(*mockDownloadDescriptor) firstDescriptor := descriptors[0].(*mockDownloadDescriptor)
// Pre-register the first layer to simulate an already-existing layer // Pre-register the first layer to simulate an already-existing layer
l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.Platform(runtime.GOOS)) l, err := layerStore.Register(firstDescriptor.mockTarStream(), "", layer.OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
firstDescriptor.diffID = l.DiffID() firstDescriptor.diffID = l.DiffID()
rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.Platform(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) rootFS, releaseFunc, err := ldm.Download(context.Background(), *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
if err != nil { if err != nil {
t.Fatalf("download error: %v", err) t.Fatalf("download error: %v", err)
} }
@ -357,7 +357,7 @@ func TestCancelledDownload(t *testing.T) {
}() }()
descriptors := downloadDescriptors(nil) descriptors := downloadDescriptors(nil)
_, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.Platform(runtime.GOOS), descriptors, progress.ChanOutput(progressChan)) _, _, err := ldm.Download(ctx, *image.NewRootFS(), layer.OS(runtime.GOOS), descriptors, progress.ChanOutput(progressChan))
if err != context.Canceled { if err != context.Canceled {
t.Fatal("expected download to be cancelled") t.Fatal("expected download to be cancelled")
} }

View file

@ -96,8 +96,8 @@ func (img *Image) RunConfig() *container.Config {
return img.Config return img.Config
} }
// Platform returns the image's operating system. If not populated, defaults to the host runtime OS. // OperatingSystem returns the image's operating system. If not populated, defaults to the host runtime OS.
func (img *Image) Platform() string { func (img *Image) OperatingSystem() string {
os := img.OS os := img.OS
if os == "" { if os == "" {
os = runtime.GOOS os = runtime.GOOS

View file

@ -47,17 +47,17 @@ type store struct {
images map[ID]*imageMeta images map[ID]*imageMeta
fs StoreBackend fs StoreBackend
digestSet *digestset.Set digestSet *digestset.Set
platform string os string
} }
// NewImageStore returns new store object for given layer store // NewImageStore returns new store object for given layer store
func NewImageStore(fs StoreBackend, platform string, ls LayerGetReleaser) (Store, error) { func NewImageStore(fs StoreBackend, os string, ls LayerGetReleaser) (Store, error) {
is := &store{ is := &store{
ls: ls, ls: ls,
images: make(map[ID]*imageMeta), images: make(map[ID]*imageMeta),
fs: fs, fs: fs,
digestSet: digestset.NewSet(), digestSet: digestset.NewSet(),
platform: platform, os: os,
} }
// load all current images and retain layers // load all current images and retain layers
@ -118,11 +118,11 @@ func (is *store) Create(config []byte) (ID, error) {
return "", err return "", err
} }
// TODO @jhowardmsft - LCOW Support. This will need revisiting. // TODO @jhowardmsft - LCOW Support. This will need revisiting when coalescing the image stores.
// Integrity check - ensure we are creating something for the correct platform // Integrity check - ensure we are creating something for the correct platform
if system.LCOWSupported() { if system.LCOWSupported() {
if strings.ToLower(img.Platform()) != strings.ToLower(is.platform) { if strings.ToLower(img.OperatingSystem()) != strings.ToLower(is.os) {
return "", fmt.Errorf("cannot create entry for platform %q in image store for platform %q", img.Platform(), is.platform) return "", fmt.Errorf("cannot create entry for operating system %q in image store for operating system %q", img.OperatingSystem(), is.os)
} }
} }

View file

@ -90,13 +90,13 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
} }
// On Windows, validate the platform, defaulting to windows if not present. // On Windows, validate the platform, defaulting to windows if not present.
platform := layer.Platform(img.OS) os := layer.OS(img.OS)
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
if platform == "" { if os == "" {
platform = "windows" os = "windows"
} }
if (platform != "windows") && (platform != "linux") { if (os != "windows") && (os != "linux") {
return fmt.Errorf("configuration for this image has an unsupported platform: %s", platform) return fmt.Errorf("configuration for this image has an unsupported operating system: %s", os)
} }
} }
@ -109,7 +109,7 @@ func (l *tarexporter) Load(inTar io.ReadCloser, outStream io.Writer, quiet bool)
r.Append(diffID) r.Append(diffID)
newLayer, err := l.ls.Get(r.ChainID()) newLayer, err := l.ls.Get(r.ChainID())
if err != nil { if err != nil {
newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), platform, m.LayerSources[diffID], progressOutput) newLayer, err = l.loadLayer(layerPath, rootFS, diffID.String(), os, m.LayerSources[diffID], progressOutput)
if err != nil { if err != nil {
return err return err
} }
@ -176,7 +176,7 @@ func (l *tarexporter) setParentID(id, parentID image.ID) error {
return l.is.SetParent(id, parentID) return l.is.SetParent(id, parentID)
} }
func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, platform layer.Platform, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) { func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string, os layer.OS, foreignSrc distribution.Descriptor, progressOutput progress.Output) (layer.Layer, error) {
// We use system.OpenSequential to use sequential file access on Windows, avoiding // We use system.OpenSequential to use sequential file access on Windows, avoiding
// depleting the standby list. On Linux, this equates to a regular os.Open. // depleting the standby list. On Linux, this equates to a regular os.Open.
rawTar, err := system.OpenSequential(filename) rawTar, err := system.OpenSequential(filename)
@ -206,9 +206,9 @@ func (l *tarexporter) loadLayer(filename string, rootFS image.RootFS, id string,
defer inflatedLayerData.Close() defer inflatedLayerData.Close()
if ds, ok := l.ls.(layer.DescribableStore); ok { if ds, ok := l.ls.(layer.DescribableStore); ok {
return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), platform, foreignSrc) return ds.RegisterWithDescriptor(inflatedLayerData, rootFS.ChainID(), os, foreignSrc)
} }
return l.ls.Register(inflatedLayerData, rootFS.ChainID(), platform) return l.ls.Register(inflatedLayerData, rootFS.ChainID(), os)
} }
func (l *tarexporter) setLoadedTag(ref reference.Named, imgID digest.Digest, outStream io.Writer) error { func (l *tarexporter) setLoadedTag(ref reference.Named, imgID digest.Digest, outStream io.Writer) error {

View file

@ -55,7 +55,7 @@ func (el *emptyLayer) Metadata() (map[string]string, error) {
return make(map[string]string), nil return make(map[string]string), nil
} }
func (el *emptyLayer) Platform() Platform { func (el *emptyLayer) OS() OS {
return "" return ""
} }

View file

@ -2,12 +2,12 @@
package layer package layer
// SetPlatform writes the "platform" file to the layer filestore // SetOS writes the "os" file to the layer filestore
func (fm *fileMetadataTransaction) SetPlatform(platform Platform) error { func (fm *fileMetadataTransaction) SetOS(os OS) error {
return nil return nil
} }
// GetPlatform reads the "platform" file from the layer filestore // GetOS reads the "os" file from the layer filestore
func (fms *fileMetadataStore) GetPlatform(layer ChainID) (Platform, error) { func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) {
return "", nil return "", nil
} }

View file

@ -7,19 +7,19 @@ import (
"strings" "strings"
) )
// SetPlatform writes the "platform" file to the layer filestore // SetOS writes the "os" file to the layer filestore
func (fm *fileMetadataTransaction) SetPlatform(platform Platform) error { func (fm *fileMetadataTransaction) SetOS(os OS) error {
if platform == "" { if os == "" {
return nil return nil
} }
return fm.ws.WriteFile("platform", []byte(platform), 0644) return fm.ws.WriteFile("os", []byte(os), 0644)
} }
// GetPlatform reads the "platform" file from the layer filestore // GetOS reads the "os" file from the layer filestore
func (fms *fileMetadataStore) GetPlatform(layer ChainID) (Platform, error) { func (fms *fileMetadataStore) GetOS(layer ChainID) (OS, error) {
contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "platform")) contentBytes, err := ioutil.ReadFile(fms.getLayerFilename(layer, "os"))
if err != nil { if err != nil {
// For backwards compatibility, the platform file may not exist. Default to "windows" if missing. // For backwards compatibility, the os file may not exist. Default to "windows" if missing.
if os.IsNotExist(err) { if os.IsNotExist(err) {
return "windows", nil return "windows", nil
} }
@ -28,8 +28,8 @@ func (fms *fileMetadataStore) GetPlatform(layer ChainID) (Platform, error) {
content := strings.TrimSpace(string(contentBytes)) content := strings.TrimSpace(string(contentBytes))
if content != "windows" && content != "linux" { if content != "windows" && content != "linux" {
return "", fmt.Errorf("invalid platform value: %s", content) return "", fmt.Errorf("invalid operating system value: %s", content)
} }
return Platform(content), nil return OS(content), nil
} }

View file

@ -53,8 +53,8 @@ var (
ErrMaxDepthExceeded = errors.New("max depth exceeded") ErrMaxDepthExceeded = errors.New("max depth exceeded")
// ErrNotSupported is used when the action is not supported // ErrNotSupported is used when the action is not supported
// on the current platform // on the current host operating system.
ErrNotSupported = errors.New("not support on this platform") ErrNotSupported = errors.New("not support on this host operating system")
) )
// ChainID is the content-addressable ID of a layer. // ChainID is the content-addressable ID of a layer.
@ -65,11 +65,11 @@ func (id ChainID) String() string {
return string(id) return string(id)
} }
// Platform is the platform of a layer // OS is the operating system of a layer
type Platform string type OS string
// String returns a string rendition of layers target platform // String returns a string rendition of layers target operating system
func (id Platform) String() string { func (id OS) String() string {
return string(id) return string(id)
} }
@ -108,8 +108,8 @@ type Layer interface {
// Parent returns the next layer in the layer chain. // Parent returns the next layer in the layer chain.
Parent() Layer Parent() Layer
// Platform returns the platform of the layer // OS returns the operating system of the layer
Platform() Platform OS() OS
// Size returns the size of the entire layer chain. The size // Size returns the size of the entire layer chain. The size
// is calculated from the total size of all files in the layers. // is calculated from the total size of all files in the layers.
@ -191,7 +191,7 @@ type CreateRWLayerOpts struct {
// Store represents a backend for managing both // Store represents a backend for managing both
// read-only and read-write layers. // read-only and read-write layers.
type Store interface { type Store interface {
Register(io.Reader, ChainID, Platform) (Layer, error) Register(io.Reader, ChainID, OS) (Layer, error)
Get(ChainID) (Layer, error) Get(ChainID) (Layer, error)
Map() map[ChainID]Layer Map() map[ChainID]Layer
Release(Layer) ([]Metadata, error) Release(Layer) ([]Metadata, error)
@ -209,7 +209,7 @@ type Store interface {
// DescribableStore represents a layer store capable of storing // DescribableStore represents a layer store capable of storing
// descriptors for layers. // descriptors for layers.
type DescribableStore interface { type DescribableStore interface {
RegisterWithDescriptor(io.Reader, ChainID, Platform, distribution.Descriptor) (Layer, error) RegisterWithDescriptor(io.Reader, ChainID, OS, distribution.Descriptor) (Layer, error)
} }
// MetadataTransaction represents functions for setting layer metadata // MetadataTransaction represents functions for setting layer metadata
@ -220,7 +220,7 @@ type MetadataTransaction interface {
SetDiffID(DiffID) error SetDiffID(DiffID) error
SetCacheID(string) error SetCacheID(string) error
SetDescriptor(distribution.Descriptor) error SetDescriptor(distribution.Descriptor) error
SetPlatform(Platform) error SetOS(OS) error
TarSplitWriter(compressInput bool) (io.WriteCloser, error) TarSplitWriter(compressInput bool) (io.WriteCloser, error)
Commit(ChainID) error Commit(ChainID) error
@ -241,7 +241,7 @@ type MetadataStore interface {
GetDiffID(ChainID) (DiffID, error) GetDiffID(ChainID) (DiffID, error)
GetCacheID(ChainID) (string, error) GetCacheID(ChainID) (string, error)
GetDescriptor(ChainID) (distribution.Descriptor, error) GetDescriptor(ChainID) (distribution.Descriptor, error)
GetPlatform(ChainID) (Platform, error) GetOS(ChainID) (OS, error)
TarSplitReader(ChainID) (io.ReadCloser, error) TarSplitReader(ChainID) (io.ReadCloser, error)
SetMountID(string, string) error SetMountID(string, string) error

View file

@ -39,7 +39,7 @@ type layerStore struct {
useTarSplit bool useTarSplit bool
platform string os string
} }
// StoreOptions are the options used to create a new Store instance // StoreOptions are the options used to create a new Store instance
@ -51,7 +51,7 @@ type StoreOptions struct {
IDMappings *idtools.IDMappings IDMappings *idtools.IDMappings
PluginGetter plugingetter.PluginGetter PluginGetter plugingetter.PluginGetter
ExperimentalEnabled bool ExperimentalEnabled bool
Platform string OS string
} }
// NewStoreFromOptions creates a new Store instance // NewStoreFromOptions creates a new Store instance
@ -73,13 +73,13 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
return nil, err return nil, err
} }
return NewStoreFromGraphDriver(fms, driver, options.Platform) return NewStoreFromGraphDriver(fms, driver, options.OS)
} }
// NewStoreFromGraphDriver creates a new Store instance using the provided // NewStoreFromGraphDriver creates a new Store instance using the provided
// metadata store and graph driver. The metadata store will be used to restore // metadata store and graph driver. The metadata store will be used to restore
// the Store. // the Store.
func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, platform string) (Store, error) { func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, os string) (Store, error) {
caps := graphdriver.Capabilities{} caps := graphdriver.Capabilities{}
if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok { if capDriver, ok := driver.(graphdriver.CapabilityDriver); ok {
caps = capDriver.Capabilities() caps = capDriver.Capabilities()
@ -91,7 +91,7 @@ func NewStoreFromGraphDriver(store MetadataStore, driver graphdriver.Driver, pla
layerMap: map[ChainID]*roLayer{}, layerMap: map[ChainID]*roLayer{},
mounts: map[string]*mountedLayer{}, mounts: map[string]*mountedLayer{},
useTarSplit: !caps.ReproducesExactDiffs, useTarSplit: !caps.ReproducesExactDiffs,
platform: platform, os: os,
} }
ids, mounts, err := store.List() ids, mounts, err := store.List()
@ -150,9 +150,9 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err) return nil, fmt.Errorf("failed to get descriptor for %s: %s", layer, err)
} }
platform, err := ls.store.GetPlatform(layer) os, err := ls.store.GetOS(layer)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get platform for %s: %s", layer, err) return nil, fmt.Errorf("failed to get operating system for %s: %s", layer, err)
} }
cl = &roLayer{ cl = &roLayer{
@ -163,7 +163,7 @@ func (ls *layerStore) loadLayer(layer ChainID) (*roLayer, error) {
layerStore: ls, layerStore: ls,
references: map[Layer]struct{}{}, references: map[Layer]struct{}{},
descriptor: descriptor, descriptor: descriptor,
platform: platform, os: os,
} }
if parent != "" { if parent != "" {
@ -259,11 +259,11 @@ func (ls *layerStore) applyTar(tx MetadataTransaction, ts io.Reader, parent stri
return nil return nil
} }
func (ls *layerStore) Register(ts io.Reader, parent ChainID, platform Platform) (Layer, error) { func (ls *layerStore) Register(ts io.Reader, parent ChainID, os OS) (Layer, error) {
return ls.registerWithDescriptor(ts, parent, platform, distribution.Descriptor{}) return ls.registerWithDescriptor(ts, parent, os, distribution.Descriptor{})
} }
func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, platform Platform, descriptor distribution.Descriptor) (Layer, error) { func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) {
// err is used to hold the error which will always trigger // err is used to hold the error which will always trigger
// cleanup of creates sources but may not be an error returned // cleanup of creates sources but may not be an error returned
// to the caller (already exists). // to the caller (already exists).
@ -271,10 +271,10 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, platf
var pid string var pid string
var p *roLayer var p *roLayer
// Integrity check - ensure we are creating something for the correct platform // Integrity check - ensure we are creating something for the correct operating system
if system.LCOWSupported() { if system.LCOWSupported() {
if strings.ToLower(ls.platform) != strings.ToLower(string(platform)) { if strings.ToLower(ls.os) != strings.ToLower(string(os)) {
return nil, fmt.Errorf("cannot create entry for platform %q in layer store for platform %q", platform, ls.platform) return nil, fmt.Errorf("cannot create entry for operating system %q in layer store for operating system %q", os, ls.os)
} }
} }
@ -306,7 +306,7 @@ func (ls *layerStore) registerWithDescriptor(ts io.Reader, parent ChainID, platf
layerStore: ls, layerStore: ls,
references: map[Layer]struct{}{}, references: map[Layer]struct{}{},
descriptor: descriptor, descriptor: descriptor,
platform: platform, os: os,
} }
if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil { if err = ls.driver.Create(layer.cacheID, pid, nil); err != nil {

View file

@ -6,6 +6,6 @@ import (
"github.com/docker/distribution" "github.com/docker/distribution"
) )
func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, platform Platform, descriptor distribution.Descriptor) (Layer, error) { func (ls *layerStore) RegisterWithDescriptor(ts io.Reader, parent ChainID, os OS, descriptor distribution.Descriptor) (Layer, error) {
return ls.registerWithDescriptor(ts, parent, platform, descriptor) return ls.registerWithDescriptor(ts, parent, os, descriptor)
} }

View file

@ -108,7 +108,7 @@ func createLayer(ls Store, parent ChainID, layerFunc layerInit) (Layer, error) {
} }
defer ts.Close() defer ts.Close()
layer, err := ls.Register(ts, parent, Platform(runtime.GOOS)) layer, err := ls.Register(ts, parent, OS(runtime.GOOS))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -501,7 +501,7 @@ func TestTarStreamStability(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer1, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS)) layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -520,7 +520,7 @@ func TestTarStreamStability(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), Platform(runtime.GOOS)) layer2, err := ls.Register(bytes.NewReader(tar2), layer1.ChainID(), OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -688,12 +688,12 @@ func TestRegisterExistingLayer(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), Platform(runtime.GOOS)) layer2a, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), Platform(runtime.GOOS)) layer2b, err := ls.Register(bytes.NewReader(tar1), layer1.ChainID(), OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -728,12 +728,12 @@ func TestTarStreamVerification(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer1, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS)) layer1, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
layer2, err := ls.Register(bytes.NewReader(tar2), "", Platform(runtime.GOOS)) layer2, err := ls.Register(bytes.NewReader(tar2), "", OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -110,14 +110,14 @@ func TestLayerMigration(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer1b, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS)) layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
assertReferences(t, layer1a, layer1b) assertReferences(t, layer1a, layer1b)
// Attempt register, should be same // Attempt register, should be same
layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), Platform(runtime.GOOS)) layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -238,7 +238,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
layer1b, err := ls.Register(bytes.NewReader(tar1), "", Platform(runtime.GOOS)) layer1b, err := ls.Register(bytes.NewReader(tar1), "", OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -246,7 +246,7 @@ func TestLayerMigrationNoTarsplit(t *testing.T) {
assertReferences(t, layer1a, layer1b) assertReferences(t, layer1a, layer1b)
// Attempt register, should be same // Attempt register, should be same
layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), Platform(runtime.GOOS)) layer2a, err := ls.Register(bytes.NewReader(tar2), layer1a.ChainID(), OS(runtime.GOOS))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

View file

@ -16,7 +16,7 @@ type roLayer struct {
size int64 size int64
layerStore *layerStore layerStore *layerStore
descriptor distribution.Descriptor descriptor distribution.Descriptor
platform Platform os OS
referenceCount int referenceCount int
references map[Layer]struct{} references map[Layer]struct{}
@ -143,7 +143,7 @@ func storeLayer(tx MetadataTransaction, layer *roLayer) error {
return err return err
} }
} }
if err := tx.SetPlatform(layer.platform); err != nil { if err := tx.SetOS(layer.os); err != nil {
return err return err
} }

View file

@ -2,6 +2,6 @@
package layer package layer
func (rl *roLayer) Platform() Platform { func (rl *roLayer) OS() OS {
return "" return ""
} }

View file

@ -8,9 +8,9 @@ func (rl *roLayer) Descriptor() distribution.Descriptor {
return rl.descriptor return rl.descriptor
} }
func (rl *roLayer) Platform() Platform { func (rl *roLayer) OS() OS {
if rl.platform == "" { if rl.os == "" {
return "windows" return "windows"
} }
return rl.platform return rl.os
} }

View file

@ -433,7 +433,7 @@ func (l *mockLayer) DiffSize() (int64, error) {
return 0, nil return 0, nil
} }
func (l *mockLayer) Platform() layer.Platform { func (l *mockLayer) OS() layer.OS {
return "" return ""
} }

View file

@ -2,17 +2,16 @@ package system
import "os" import "os"
// LCOWSupported determines if Linux Containers on Windows are supported. // lcowSupported determines if Linux Containers on Windows are supported.
// Note: This feature is in development (06/17) and enabled through an
// environment variable. At a future time, it will be enabled based
// on build number. @jhowardmsft
var lcowSupported = false var lcowSupported = false
// InitLCOW sets whether LCOW is supported or not // InitLCOW sets whether LCOW is supported or not
// TODO @jhowardmsft.
// 1. Replace with RS3 RTM build number.
// 2. Remove the getenv check when image-store is coalesced as shouldn't be needed anymore.
func InitLCOW(experimental bool) { func InitLCOW(experimental bool) {
// LCOW initialization v := GetOSVersion()
if experimental && os.Getenv("LCOW_SUPPORTED") != "" { if experimental && v.Build > 16270 && os.Getenv("LCOW_SUPPORTED") != "" {
lcowSupported = true lcowSupported = true
} }
} }

71
pkg/system/lcow.go Normal file
View file

@ -0,0 +1,71 @@
package system
import (
"fmt"
"runtime"
"strings"
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.
// @jhowardmsft
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 {
return fmt.Errorf("invalid platform architecture %q", platform.Architecture)
}
if platform.OS != "" {
if !(platform.OS == runtime.GOOS || (LCOWSupported() && platform.OS == "linux")) {
return fmt.Errorf("invalid platform os %q", platform.OS)
}
}
if len(platform.OSFeatures) != 0 {
return fmt.Errorf("invalid platform osfeatures %q", platform.OSFeatures)
}
if platform.OSVersion != "" {
return fmt.Errorf("invalid platform osversion %q", platform.OSVersion)
}
if platform.Variant != "" {
return fmt.Errorf("invalid platform variant %q", platform.Variant)
}
return nil
}
// ParsePlatform parses a platform string in the format os[/arch[/variant]
// into an OCI image-spec platform structure.
// 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 ParsePlatform(in string) *specs.Platform {
p := &specs.Platform{}
elements := strings.SplitN(strings.ToLower(in), "/", 3)
if len(elements) == 3 {
p.Variant = elements[2]
}
if len(elements) >= 2 {
p.Architecture = elements[1]
}
if len(elements) >= 1 {
p.OS = elements[0]
}
return p
}

View file

@ -14,9 +14,9 @@ const defaultUnixPathEnv = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/s
// DefaultPathEnv is unix style list of directories to search for // DefaultPathEnv is unix style list of directories to search for
// executables. Each directory is separated from the next by a colon // executables. Each directory is separated from the next by a colon
// ':' character . // ':' character .
func DefaultPathEnv(platform string) string { func DefaultPathEnv(os string) string {
if runtime.GOOS == "windows" { if runtime.GOOS == "windows" {
if platform != runtime.GOOS && LCOWSupported() { if os != runtime.GOOS {
return defaultUnixPathEnv return defaultUnixPathEnv
} }
// Deliberately empty on Windows containers on Windows as the default path will be set by // Deliberately empty on Windows containers on Windows as the default path will be set by

View file

@ -146,7 +146,7 @@ func (s *tempConfigStore) Get(d digest.Digest) ([]byte, error) {
return s.config, nil return s.config, nil
} }
func (s *tempConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) { func (s *tempConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
return configToRootFS(c) return configToRootFS(c)
} }
@ -533,7 +533,7 @@ func (s *pluginConfigStore) Get(d digest.Digest) ([]byte, error) {
return ioutil.ReadAll(rwc) return ioutil.ReadAll(rwc)
} }
func (s *pluginConfigStore) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) { func (s *pluginConfigStore) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
return configToRootFS(c) return configToRootFS(c)
} }

View file

@ -126,7 +126,7 @@ type downloadManager struct {
configDigest digest.Digest configDigest digest.Digest
} }
func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, platform layer.Platform, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) { func (dm *downloadManager) Download(ctx context.Context, initialRootFS image.RootFS, os layer.OS, layers []xfer.DownloadDescriptor, progressOutput progress.Output) (image.RootFS, func(), error) {
// TODO @jhowardmsft LCOW: May need revisiting. // TODO @jhowardmsft LCOW: May need revisiting.
for _, l := range layers { for _, l := range layers {
b, err := dm.blobStore.New() b, err := dm.blobStore.New()
@ -179,6 +179,6 @@ func (dm *downloadManager) Put(dt []byte) (digest.Digest, error) {
func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) { func (dm *downloadManager) Get(d digest.Digest) ([]byte, error) {
return nil, fmt.Errorf("digest not found") return nil, fmt.Errorf("digest not found")
} }
func (dm *downloadManager) RootFSAndPlatformFromConfig(c []byte) (*image.RootFS, layer.Platform, error) { func (dm *downloadManager) RootFSAndOSFromConfig(c []byte) (*image.RootFS, layer.OS, error) {
return configToRootFS(c) return configToRootFS(c)
} }

View file

@ -379,11 +379,11 @@ func isEqualPrivilege(a, b types.PluginPrivilege) bool {
return reflect.DeepEqual(a.Value, b.Value) return reflect.DeepEqual(a.Value, b.Value)
} }
func configToRootFS(c []byte) (*image.RootFS, layer.Platform, error) { func configToRootFS(c []byte) (*image.RootFS, layer.OS, error) {
// TODO @jhowardmsft LCOW - Will need to revisit this. For now, calculate the platform. // TODO @jhowardmsft LCOW - Will need to revisit this. For now, calculate the operating system.
platform := layer.Platform(runtime.GOOS) os := layer.OS(runtime.GOOS)
if system.LCOWSupported() { if system.LCOWSupported() {
platform = "linux" os = "linux"
} }
var pluginConfig types.PluginConfig var pluginConfig types.PluginConfig
if err := json.Unmarshal(c, &pluginConfig); err != nil { if err := json.Unmarshal(c, &pluginConfig); err != nil {
@ -391,10 +391,10 @@ func configToRootFS(c []byte) (*image.RootFS, layer.Platform, error) {
} }
// validation for empty rootfs is in distribution code // validation for empty rootfs is in distribution code
if pluginConfig.Rootfs == nil { if pluginConfig.Rootfs == nil {
return nil, platform, nil return nil, os, nil
} }
return rootFSFromPlugin(pluginConfig.Rootfs), platform, nil return rootFSFromPlugin(pluginConfig.Rootfs), os, nil
} }
func rootFSFromPlugin(pluginfs *types.PluginConfigRootfs) *image.RootFS { func rootFSFromPlugin(pluginfs *types.PluginConfigRootfs) *image.RootFS {