80f3272e |
package daemon
import ( |
a793564b |
"fmt" |
c7045eb9 |
"net" |
934328d8 |
"runtime" |
bfa0885c |
"strings" |
3343d234 |
"time" |
a793564b |
|
934328d8 |
"github.com/pkg/errors"
|
af7f8187 |
"github.com/Sirupsen/logrus" |
934328d8 |
apierrors "github.com/docker/docker/api/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" |
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" |
a8d01349 |
"github.com/docker/docker/runconfig" |
d3eca445 |
volumestore "github.com/docker/docker/volume/store" |
c86189d5 |
"github.com/opencontainers/runc/libcontainer/label" |
80f3272e |
)
|
534a90a9 |
// CreateManagedContainer creates a container that is managed by a Service |
1b310cd4 |
func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(params, true) |
534a90a9 |
}
// ContainerCreate creates a regular container |
1b310cd4 |
func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) {
return daemon.containerCreate(params, false) |
534a90a9 |
}
|
1b310cd4 |
func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { |
3343d234 |
start := time.Now() |
a7e686a7 |
if params.Config == nil { |
bad849fc |
return containertypes.ContainerCreateCreatedBody{}, fmt.Errorf("Config cannot be empty in order to create a container") |
4ce81779 |
}
|
1b310cd4 |
warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false) |
93cdb007 |
if err != nil { |
bad849fc |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err |
72f356be |
} |
1df87b95 |
|
cfa515fd |
err = daemon.verifyNetworkingConfig(params.NetworkingConfig)
if err != nil { |
bad849fc |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err |
cfa515fd |
}
|
1415f55c |
if params.HostConfig == nil { |
7ac4232e |
params.HostConfig = &containertypes.HostConfig{} |
1415f55c |
}
err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares)
if err != nil { |
bad849fc |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err |
1415f55c |
} |
73d8af93 |
|
534a90a9 |
container, err := daemon.create(params, managed) |
80f3272e |
if err != nil { |
bad849fc |
return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, daemon.imageNotExistToErrcode(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 |
)
|
a7e686a7 |
if params.Config.Image != "" { |
4352da78 |
img, err = daemon.GetImage(params.Config.Image) |
89367899 |
if err != nil { |
e0ef11a4 |
return nil, err |
89367899 |
} |
934328d8 |
if runtime.GOOS == "solaris" && img.OS != "solaris " {
return nil, errors.New("Platform on which parent image was created is not Solaris")
} |
4352da78 |
imgID = img.ID() |
80f3272e |
} |
89367899 |
|
a7e686a7 |
if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { |
e0ef11a4 |
return nil, err |
80f3272e |
} |
6b8129d1 |
|
1790980e |
if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil {
return nil, err
}
|
870beb70 |
if container, err = daemon.newContainer(params.Name, 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
|
d8e09066 |
// Set RWLayer for container after mount labels have been set
if err := daemon.setRWLayer(container); err != nil {
return nil, err
}
|
4352da78 |
rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps)
if err != nil { |
1716d497 |
return nil, err
} |
6bb0d181 |
if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil { |
e0ef11a4 |
return nil, err |
80f3272e |
} |
d8fef66b |
if err := idtools.MkdirAs(container.CheckpointDir(), 0700, rootUID, rootGID); err != nil {
return nil, err
} |
4352da78 |
|
a7e686a7 |
if err := daemon.setHostConfig(container, params.HostConfig); err != nil { |
e0ef11a4 |
return nil, err |
1df87b95 |
} |
81fa9feb |
|
7c70ad05 |
if err := daemon.createContainerPlatformSpecificSettings(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.
container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig) |
2bb3fc1b |
|
2d126f19 |
daemon.updateContainerNetworkSettings(container, endpointsConfigs) |
c427131c |
|
114be249 |
if err := container.ToDisk(); err != nil { |
af7f8187 |
logrus.Errorf("Error saving new container to disk: %v", err) |
e0ef11a4 |
return nil, err |
80f3272e |
} |
114be249 |
if err := daemon.Register(container); err != nil {
return nil, err
} |
ca5ede2d |
daemon.LogContainerEvent(container, "create") |
e0ef11a4 |
return container, nil |
80f3272e |
} |
9200fdd1 |
|
c3dd6074 |
func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMode containertypes.PidMode, privileged bool) ([]string, error) {
if ipcMode.IsHost() || pidMode.IsHost() || privileged { |
497fc887 |
return label.DisableSecOpt(), nil
} |
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 == "" {
return ipcLabel, err
}
}
if pidContainer != "" {
c, err := daemon.GetContainer(pidContainer)
if err != nil {
return nil, err
} |
497fc887 |
|
fb43ef64 |
pidLabel = label.DupSecOpt(c.ProcessLabel)
if ipcContainer == "" {
return pidLabel, err
}
}
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")
}
}
return pidLabel, nil |
497fc887 |
}
return nil, nil
} |
b3b7eb27 |
|
d04fa49a |
func (daemon *Daemon) setRWLayer(container *container.Container) error {
var layerID layer.ChainID
if container.ImageID != "" {
img, err := daemon.imageStore.Get(container.ImageID)
if err != nil {
return err
}
layerID = img.RootFS.ChainID()
} |
2508ca00 |
rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.getLayerInit(), container.HostConfig.StorageOpt)
|
d04fa49a |
if err != nil {
return err
}
container.RWLayer = rwLayer
return nil
}
|
b3b7eb27 |
// VolumeCreate creates a volume with the specified name, driver, and opts |
520e601d |
// 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 { |
d3eca445 |
if volumestore.IsNameConflict(err) { |
a793564b |
return nil, fmt.Errorf("A volume named %s already exists. Choose a different volume name.", name) |
d3eca445 |
} |
b3b7eb27 |
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 |
bfa0885c |
func (daemon *Daemon) 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 { |
934328d8 |
return apierrors.NewBadRequestError(fmt.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 { |
934328d8 |
return apierrors.NewBadRequestError(fmt.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)
}
err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) |
934328d8 |
return apierrors.NewBadRequestError(err) |
bfa0885c |
} |