80f3272e |
package daemon
import ( |
a793564b |
"fmt" |
c7045eb9 |
"net" |
934328d8 |
"runtime" |
bfa0885c |
"strings" |
3343d234 |
"time" |
a793564b |
|
934328d8 |
"github.com/pkg/errors"
|
91e197d6 |
"github.com/docker/docker/api/types"
containertypes "github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network" |
6bb0d181 |
"github.com/docker/docker/container" |
d453fe35 |
"github.com/docker/docker/errdefs" |
9001ea26 |
"github.com/docker/docker/image" |
d04fa49a |
"github.com/docker/docker/layer" |
4352da78 |
"github.com/docker/docker/pkg/idtools" |
b3b7eb27 |
"github.com/docker/docker/pkg/stringid" |
08252bc9 |
"github.com/docker/docker/pkg/system" |
a8d01349 |
"github.com/docker/docker/runconfig" |
abbbf914 |
"github.com/opencontainers/selinux/go-selinux/label" |
1009e6a4 |
"github.com/sirupsen/logrus" |
80f3272e |
)
|
534a90a9 |
// CreateManagedContainer creates a container that is managed by a Service |
ef39256d |
func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(params, true) |
534a90a9 |
}
// ContainerCreate creates a regular container |
ef39256d |
func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(params, false) |
534a90a9 |
}
|
ef39256d |
func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { |
3343d234 |
start := time.Now() |
a7e686a7 |
if params.Config == nil { |
87a12421 |
return containertypes.ContainerCreateCreatedBody{}, errdefs.InvalidParameter(errors.New("Config cannot be empty in order to create a container")) |
4ce81779 |
}
|
0380fbff |
os := runtime.GOOS
if params.Config.Image != "" {
img, err := daemon.GetImage(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"
} |
9fa44906 |
}
|
0380fbff |
warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false) |
93cdb007 |
if err != nil { |
87a12421 |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) |
72f356be |
} |
1df87b95 |
|
ebcb7d6b |
err = verifyNetworkingConfig(params.NetworkingConfig) |
cfa515fd |
if err != nil { |
87a12421 |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) |
cfa515fd |
}
|
1415f55c |
if params.HostConfig == nil { |
7ac4232e |
params.HostConfig = &containertypes.HostConfig{} |
1415f55c |
}
err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares)
if err != nil { |
87a12421 |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) |
1415f55c |
} |
73d8af93 |
|
534a90a9 |
container, err := daemon.create(params, managed) |
80f3272e |
if err != nil { |
ebcb7d6b |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err |
80f3272e |
} |
3343d234 |
containerActions.WithValues("create").UpdateSince(start) |
bad849fc |
return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil |
80f3272e |
}
// Create creates a new container from the given configuration with a given name. |
534a90a9 |
func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { |
80f3272e |
var ( |
6bb0d181 |
container *container.Container |
9001ea26 |
img *image.Image |
4352da78 |
imgID image.ID |
89367899 |
err error |
80f3272e |
)
|
0380fbff |
os := runtime.GOOS |
a7e686a7 |
if params.Config.Image != "" { |
4352da78 |
img, err = daemon.GetImage(params.Config.Image) |
89367899 |
if err != nil { |
e0ef11a4 |
return nil, err |
89367899 |
} |
a97817b6 |
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"
}
} |
4352da78 |
imgID = img.ID() |
08252bc9 |
if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { |
0380fbff |
return nil, errors.New("operating system on which parent image was created is not Windows") |
08252bc9 |
} |
0380fbff |
} else {
if runtime.GOOS == "windows" {
os = "linux" // 'scratch' case. |
08252bc9 |
} |
80f3272e |
} |
89367899 |
|
a7e686a7 |
if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { |
87a12421 |
return nil, errdefs.InvalidParameter(err) |
80f3272e |
} |
6b8129d1 |
|
1790980e |
if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { |
87a12421 |
return nil, errdefs.InvalidParameter(err) |
1790980e |
}
|
0380fbff |
if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil { |
e0ef11a4 |
return nil, err |
80f3272e |
} |
1ed15550 |
defer func() {
if retErr != nil { |
4df77c11 |
if err := daemon.cleanupContainer(container, true, true); err != nil { |
114be249 |
logrus.Errorf("failed to cleanup container on create error: %v", err) |
1ed15550 |
}
}
}()
|
d8e09066 |
if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil {
return nil, err
}
|
b16decfc |
container.HostConfig.StorageOpt = params.HostConfig.StorageOpt
|
932ae425 |
// Fixes: https://github.com/moby/moby/issues/34074 and
// https://github.com/docker/for-win/issues/999.
// Merge the daemon's storage options if they aren't already present. We only
// do this on Windows as there's no effective sandbox size limit other than
// physical on Linux.
if runtime.GOOS == "windows" {
if container.HostConfig.StorageOpt == nil {
container.HostConfig.StorageOpt = make(map[string]string)
}
for _, v := range daemon.configStore.GraphOptions {
opt := strings.SplitN(v, "=", 2)
if _, ok := container.HostConfig.StorageOpt[opt[0]]; !ok {
container.HostConfig.StorageOpt[opt[0]] = opt[1]
}
}
}
|
d8e09066 |
// Set RWLayer for container after mount labels have been set
if err := daemon.setRWLayer(container); err != nil { |
87a12421 |
return nil, errdefs.System(err) |
d8e09066 |
}
|
93fbdb69 |
rootIDs := daemon.idMappings.RootPair() |
09cd96c5 |
if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { |
e0ef11a4 |
return nil, err |
80f3272e |
} |
09cd96c5 |
if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { |
d8fef66b |
return nil, err
} |
4352da78 |
|
a7e686a7 |
if err := daemon.setHostConfig(container, params.HostConfig); err != nil { |
e0ef11a4 |
return nil, err |
1df87b95 |
} |
81fa9feb |
|
0380fbff |
if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil { |
e0ef11a4 |
return nil, err |
7107898d |
} |
47c56e43 |
|
2bb3fc1b |
var endpointsConfigs map[string]*networktypes.EndpointSettings
if params.NetworkingConfig != nil {
endpointsConfigs = params.NetworkingConfig.EndpointsConfig
} |
a8d01349 |
// Make sure NetworkMode has an acceptable value. We do this to ensure
// backwards API compatibility. |
7917a36c |
runconfig.SetDefaultNetModeIfBlank(container.HostConfig) |
2bb3fc1b |
|
2d126f19 |
daemon.updateContainerNetworkSettings(container, endpointsConfigs) |
eed4c7b7 |
if err := daemon.Register(container); err != nil {
return nil, err
} |
e4c03623 |
stateCtr.set(container.ID, "stopped") |
ca5ede2d |
daemon.LogContainerEvent(container, "create") |
e0ef11a4 |
return container, nil |
80f3272e |
} |
9200fdd1 |
|
abbbf914 |
func toHostConfigSelinuxLabels(labels []string) []string {
for i, l := range labels {
labels[i] = "label=" + l
}
return labels
}
|
881e20ee |
func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) ([]string, error) {
for _, opt := range hostConfig.SecurityOpt {
con := strings.Split(opt, "=")
if con[0] == "label" {
// Caller overrode SecurityOpts
return nil, nil
}
}
ipcMode := hostConfig.IpcMode
pidMode := hostConfig.PidMode
privileged := hostConfig.Privileged |
c3dd6074 |
if ipcMode.IsHost() || pidMode.IsHost() || privileged { |
abbbf914 |
return toHostConfigSelinuxLabels(label.DisableSecOpt()), nil |
497fc887 |
} |
fb43ef64 |
var ipcLabel []string
var pidLabel []string
ipcContainer := ipcMode.Container()
pidContainer := pidMode.Container()
if ipcContainer != "" { |
d7d512bb |
c, err := daemon.GetContainer(ipcContainer) |
d25a6537 |
if err != nil {
return nil, err |
497fc887 |
} |
fb43ef64 |
ipcLabel = label.DupSecOpt(c.ProcessLabel)
if pidContainer == "" { |
abbbf914 |
return toHostConfigSelinuxLabels(ipcLabel), err |
fb43ef64 |
}
}
if pidContainer != "" {
c, err := daemon.GetContainer(pidContainer)
if err != nil {
return nil, err
} |
497fc887 |
|
fb43ef64 |
pidLabel = label.DupSecOpt(c.ProcessLabel)
if ipcContainer == "" { |
abbbf914 |
return toHostConfigSelinuxLabels(pidLabel), err |
fb43ef64 |
}
}
if pidLabel != nil && ipcLabel != nil {
for i := 0; i < len(pidLabel); i++ {
if pidLabel[i] != ipcLabel[i] {
return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same")
}
} |
abbbf914 |
return toHostConfigSelinuxLabels(pidLabel), nil |
497fc887 |
}
return nil, nil
} |
b3b7eb27 |
|
d04fa49a |
func (daemon *Daemon) setRWLayer(container *container.Container) error {
var layerID layer.ChainID
if container.ImageID != "" { |
ce8e529e |
img, err := daemon.imageStore.Get(container.ImageID) |
d04fa49a |
if err != nil {
return err
}
layerID = img.RootFS.ChainID()
} |
2508ca00 |
|
f7f3d342 |
rwLayerOpts := &layer.CreateRWLayerOpts{
MountLabel: container.MountLabel,
InitFunc: daemon.getLayerInit(),
StorageOpt: container.HostConfig.StorageOpt,
} |
2508ca00 |
|
0cba7740 |
// Indexing by OS is safe here as validation of OS has already been performed in create() (the only
// caller), and guaranteed non-nil |
afd305c4 |
rwLayer, err := daemon.layerStores[container.OS].CreateRWLayer(container.ID, layerID, rwLayerOpts) |
d04fa49a |
if err != nil {
return err
}
container.RWLayer = rwLayer
return nil
}
|
b3b7eb27 |
// VolumeCreate creates a volume with the specified name, driver, and opts |
f0d55cd0 |
// This is called directly from the Engine API |
fc214b44 |
func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) { |
b3b7eb27 |
if name == "" {
name = stringid.GenerateNonCryptoID()
}
|
fc214b44 |
v, err := daemon.volumes.Create(name, driverName, opts, labels) |
b3b7eb27 |
if err != nil {
return nil, err
} |
0ff3123e |
|
d3eca445 |
daemon.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()}) |
9e6b1852 |
apiV := volumeToAPIType(v)
apiV.Mountpoint = v.Path()
return apiV, nil |
b3b7eb27 |
} |
fb48bf51 |
func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error {
if img != nil && img.Config != nil {
if err := merge(config, img.Config); err != nil {
return err
}
} |
c8d3ee80 |
// Reset the Entrypoint if it is [""]
if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" {
config.Entrypoint = nil
} |
fb48bf51 |
if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 {
return fmt.Errorf("No command specified")
}
return nil
} |
bfa0885c |
// Checks if the client set configurations for more than one network while creating a container |
c7045eb9 |
// Also checks if the IPAMConfig is valid |
ebcb7d6b |
func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { |
c7045eb9 |
if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 {
return nil
}
if len(nwConfig.EndpointsConfig) == 1 {
for _, v := range nwConfig.EndpointsConfig { |
99a98ccc |
if v != nil && v.IPAMConfig != nil { |
c7045eb9 |
if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { |
ebcb7d6b |
return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) |
c7045eb9 |
} |
1e6eccae |
if v.IPAMConfig.IPv6Address != "" {
n := net.ParseIP(v.IPAMConfig.IPv6Address)
// if the address is an invalid network address (ParseIP == nil) or if it is
// an IPv4 address (To4() != nil), then it is an invalid IPv6 address
if n == nil || n.To4() != nil { |
ebcb7d6b |
return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) |
1e6eccae |
} |
c7045eb9 |
}
}
} |
bfa0885c |
return nil
}
l := make([]string, 0, len(nwConfig.EndpointsConfig))
for k := range nwConfig.EndpointsConfig {
l = append(l, k)
} |
ebcb7d6b |
return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) |
bfa0885c |
} |