Skip to content

Commit

Permalink
Add support for docker run
Browse files Browse the repository at this point in the history
Signed-off-by: Derek McGowan <derek@mcgstyle.net>
  • Loading branch information
dmcgowan committed Oct 9, 2019
1 parent bc80d66 commit 6ea3769
Show file tree
Hide file tree
Showing 10 changed files with 175 additions and 89 deletions.
5 changes: 3 additions & 2 deletions daemon/container.go
Expand Up @@ -23,6 +23,7 @@ import (
"github.com/docker/docker/runconfig"
volumemounts "github.com/docker/docker/volume/mounts"
"github.com/docker/go-connections/nat"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -127,7 +128,7 @@ func (daemon *Daemon) Register(c *container.Container) error {
return c.CheckpointTo(daemon.containersReplica)
}

func (daemon *Daemon) newContainer(name string, operatingSystem 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, img ocispec.Descriptor, managed bool) (*container.Container, error) {
var (
id string
err error
Expand Down Expand Up @@ -157,7 +158,7 @@ func (daemon *Daemon) newContainer(name string, operatingSystem string, config *
base.Args = args //FIXME: de-duplicate from config
base.Config = config
base.HostConfig = &containertypes.HostConfig{}
base.ImageID = imgID
base.ImageID = image.ID(img.Digest)
base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
base.Name = name
base.Driver = daemon.imageService.GraphDriverForOS(operatingSystem)
Expand Down
137 changes: 81 additions & 56 deletions daemon/create.go
@@ -1,21 +1,24 @@
package daemon // import "github.com/docker/docker/daemon"

import (
"context"
"encoding/json"
"fmt"
"net"
"runtime"
"strings"
"time"

"github.com/containerd/containerd/content"
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/images"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/image"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/runconfig"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
Expand All @@ -29,15 +32,15 @@ type createOpts struct {

// CreateManagedContainer creates a container that is managed by a Service
func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(createOpts{
return daemon.containerCreate(context.TODO(), createOpts{
params: params,
managed: true,
ignoreImagesArgsEscaped: false})
}

// ContainerCreate creates a regular container
func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(createOpts{
return daemon.containerCreate(context.TODO(), createOpts{
params: params,
managed: false,
ignoreImagesArgsEscaped: false})
Expand All @@ -46,31 +49,33 @@ func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (conta
// ContainerCreateIgnoreImagesArgsEscaped creates a regular container. This is called from the builder RUN case
// and ensures that we do not take the images ArgsEscaped
func (daemon *Daemon) ContainerCreateIgnoreImagesArgsEscaped(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(createOpts{
return daemon.containerCreate(context.TODO(), createOpts{
params: params,
managed: false,
ignoreImagesArgsEscaped: true})
}

func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.ContainerCreateCreatedBody, error) {
func (daemon *Daemon) containerCreate(ctx context.Context, opts createOpts) (containertypes.ContainerCreateCreatedBody, error) {
start := time.Now()
if opts.params.Config == nil {
return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container"))
}

os := runtime.GOOS
if opts.params.Config.Image != "" {
img, err := daemon.imageService.GetImage(opts.params.Config.Image)
if err == nil {
os = img.OS
}
} else {
// 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"
}
}
// TODO(containerd): Resolve os for LCOW
// TODO(containerd): Why is this lookup done twice just for LCOW??
//if opts.params.Config.Image != "" {
// _, img, err := daemon.imageService.GetImage(context.TODO(), params.Config.Image)
// if err == nil {
// os = img.OS
// }
//} else {
// // 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(os, opts.params.HostConfig, opts.params.Config, false)
if err != nil {
Expand All @@ -90,7 +95,7 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.Container
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err)
}

container, err := daemon.create(opts)
container, err := daemon.create(ctx, opts)
if err != nil {
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err
}
Expand All @@ -104,56 +109,55 @@ func (daemon *Daemon) containerCreate(opts createOpts) (containertypes.Container
}

// Create creates a new container from the given configuration with a given name.
func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr error) {
func (daemon *Daemon) create(ctx context.Context, opts createOpts) (retC *container.Container, retErr error) {
var (
container *container.Container
img *image.Image
imgID image.ID
desc ocispec.Descriptor
err error
)

os := runtime.GOOS
if opts.params.Config.Image != "" {
img, err = daemon.imageService.GetImage(opts.params.Config.Image)
desc, err = daemon.imageService.GetImage(ctx, opts.params.Config.Image)
if err != nil {
return nil, err
}
if img.OS != "" {
os = img.OS
} else {
// default to the host OS except on Windows with LCOW
if runtime.GOOS == "windows" && system.LCOWSupported() {
os = "linux"
}
}
imgID = img.ID()

if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() {
return nil, errors.New("operating system on which parent image was created is not Windows")
}
} else {
if runtime.GOOS == "windows" {
os = "linux" // 'scratch' case.
}
}

// On WCOW, if are not being invoked by the builder to create this container (where
// ignoreImagesArgEscaped will be true) - if the image already has its arguments escaped,
// ensure that this is replicated across to the created container to avoid double-escaping
// of the arguments/command line when the runtime attempts to run the container.
if os == "windows" && !opts.ignoreImagesArgsEscaped && img != nil && img.RunConfig().ArgsEscaped {
opts.params.Config.ArgsEscaped = true
}

if err := daemon.mergeAndVerifyConfig(opts.params.Config, img); err != nil {
if err := daemon.mergeAndVerifyConfig(ctx, opts.params.Config, desc); err != nil {
return nil, errdefs.InvalidParameter(err)
}

if err := daemon.mergeAndVerifyLogConfig(&opts.params.HostConfig.LogConfig); err != nil {
return nil, errdefs.InvalidParameter(err)
}

if container, err = daemon.newContainer(opts.params.Name, os, opts.params.Config, opts.params.HostConfig, imgID, opts.managed); err != nil {
os := runtime.GOOS
if os == "windows" {
if desc.Digest != "" {
// TODO(containerd): resolve os for LCOW on Windows
// TODO(containerd): ensure platform in descriptor?
// TODO(containerd): Read blob
// TODO(containerd): Unmarshal OS

//if img.OS != "" {
// os = img.OS
//} else {
// // default to the host OS except on Windows with LCOW
// if runtime.GOOS == "windows" && system.LCOWSupported() {
// os = "linux"
// }
//}
//imgID = desc.Digest

//if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() {
// return nil, errors.New("operating system on which parent image was created is not Windows")
//}
} else {
os = "linux" // 'scratch' case.
}
}

if container, err = daemon.newContainer(opts.params.Name, os, opts.params.Config, opts.params.HostConfig, desc, opts.managed); err != nil {
return nil, err
}
defer func() {
Expand Down Expand Up @@ -188,7 +192,13 @@ func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr
}

// Set RWLayer for container after mount labels have been set
rwLayer, err := daemon.imageService.CreateLayer(container, setupInitLayer(daemon.idMapping))
createOpts := []images.CreateLayerOpt{
images.WithLayerImage(desc),
images.WithLayerContainer(container),
images.WithLayerInit(setupInitLayer(daemon.idMapping)),
}

rwLayer, err := daemon.imageService.CreateLayer(ctx, createOpts...)
if err != nil {
return nil, errdefs.System(err)
}
Expand Down Expand Up @@ -293,10 +303,25 @@ func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig)
return nil, nil
}

func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error {
if img != nil && img.V1Image.Config != nil {
if err := merge(config, img.V1Image.Config); err != nil {
return err
func (daemon *Daemon) mergeAndVerifyConfig(ctx context.Context, config *containertypes.Config, img ocispec.Descriptor) error {
if img.Digest != "" {
p, err := content.ReadBlob(ctx, daemon.containerdCli.ContentStore(), img)
if err != nil {
return errors.Wrap(err, "failed to read config")
}

// Only parse out the config key
var imgConfig struct {
Config *containertypes.Config `json:"config,omitempty"`
}
if err := json.Unmarshal(p, &imgConfig); err != nil {
return errors.Wrap(err, "failed to parse image config")
}

if imgConfig.Config != nil {
if err := merge(config, imgConfig.Config); err != nil {
return err
}
}
}
// Reset the Entrypoint if it is [""]
Expand Down
2 changes: 1 addition & 1 deletion daemon/images/cache.go
Expand Up @@ -240,7 +240,7 @@ func (i *ImageService) MakeImageCache(sourceRefs []string) builder.ImageCache {
cache := buildcache.New(i.imageStore)

for _, ref := range sourceRefs {
img, err := i.GetImage(ref)
img, err := i.getDockerImage(ref)
if err != nil {
logrus.Warnf("Could not look up %s for cache resolution, skipping: %+v", ref, err)
continue
Expand Down
12 changes: 11 additions & 1 deletion daemon/images/image.go
Expand Up @@ -38,8 +38,18 @@ func (e ErrImageDoesNotExist) Error() string {
// NotFound implements the NotFound interface
func (e ErrImageDoesNotExist) NotFound() {}

func (i *ImageService) GetImage(ctx context.Context, refOrID string) (ocispec.Descriptor, error) {
ci, err := i.getCachedRef(ctx, refOrID)
if err != nil {
return ocispec.Descriptor{}, err
}

return ci.config, nil
}

// GetImage returns an image corresponding to the image referred to by refOrID.
func (i *ImageService) GetImage(refOrID string) (*image.Image, error) {
// Deprecated: Use (i *ImageService).GetImage instead.
func (i *ImageService) getDockerImage(refOrID string) (*image.Image, error) {
ref, err := reference.ParseAnyReference(refOrID)
if err != nil {
return nil, errdefs.InvalidParameter(err)
Expand Down
4 changes: 2 additions & 2 deletions daemon/images/image_builder.go
Expand Up @@ -162,7 +162,7 @@ func (i *ImageService) pullForBuilder(ctx context.Context, name string, authConf
if err := i.pullImageWithReference(ctx, ref, platform, nil, pullRegistryAuth, output); err != nil {
return nil, err
}
return i.GetImage(name)
return i.getDockerImage(name)
}

// GetImageAndReleasableLayer returns an image and releaseable layer for a reference or ID.
Expand All @@ -185,7 +185,7 @@ func (i *ImageService) GetImageAndReleasableLayer(ctx context.Context, refOrID s
}

if opts.PullOption != backend.PullOptionForcePull {
image, err := i.GetImage(refOrID)
image, err := i.getDockerImage(refOrID)
if err != nil && opts.PullOption == backend.PullOptionNoPull {
return nil, nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion daemon/images/image_events.go
Expand Up @@ -12,7 +12,7 @@ func (i *ImageService) LogImageEvent(imageID, refName, action string) {
// LogImageEventWithAttributes generates an event related to an image with specific given attributes.
func (i *ImageService) LogImageEventWithAttributes(imageID, refName, action string, attributes map[string]string) {
// TODO(containerd): use i.getCachedRef(imageID)
img, err := i.GetImage(imageID)
img, err := i.getDockerImage(imageID)
if err == nil && img.V1Image.Config != nil {
// image has not been removed yet.
// it could be missing if the event is `delete`.
Expand Down
2 changes: 1 addition & 1 deletion daemon/images/image_pull.go
Expand Up @@ -78,7 +78,7 @@ func (i *ImageService) pullImageWithReference(ctx context.Context, ref reference
// - Auth config
// - Custom headers
// TODO: Platforms using `platform`
// TODO: progress tracking
// TODO(containerd): progress tracking
// TODO: unpack tracking, use download manager for now?

img, err := i.client.Pull(ctx, ref.String(), opts...)
Expand Down
4 changes: 2 additions & 2 deletions daemon/images/images.go
Expand Up @@ -70,7 +70,7 @@ func (i *ImageService) Images(ctx context.Context, imageFilters filters.Args, al
var beforeFilter, sinceFilter *image.Image
err = imageFilters.WalkValues("before", func(value string) error {
var err error
beforeFilter, err = i.GetImage(value)
beforeFilter, err = i.getDockerImage(value)
return err
})
if err != nil {
Expand All @@ -79,7 +79,7 @@ func (i *ImageService) Images(ctx context.Context, imageFilters filters.Args, al

err = imageFilters.WalkValues("since", func(value string) error {
var err error
sinceFilter, err = i.GetImage(value)
sinceFilter, err = i.getDockerImage(value)
return err
})
if err != nil {
Expand Down

0 comments on commit 6ea3769

Please sign in to comment.