github.com/skanehira/moby@v17.12.1-ce-rc2+incompatible/daemon/create.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "net" 6 "runtime" 7 "strings" 8 "time" 9 10 "github.com/pkg/errors" 11 12 "github.com/docker/docker/api/types" 13 containertypes "github.com/docker/docker/api/types/container" 14 networktypes "github.com/docker/docker/api/types/network" 15 "github.com/docker/docker/container" 16 "github.com/docker/docker/image" 17 "github.com/docker/docker/layer" 18 "github.com/docker/docker/pkg/idtools" 19 "github.com/docker/docker/pkg/stringid" 20 "github.com/docker/docker/pkg/system" 21 "github.com/docker/docker/runconfig" 22 "github.com/opencontainers/selinux/go-selinux/label" 23 "github.com/sirupsen/logrus" 24 ) 25 26 // CreateManagedContainer creates a container that is managed by a Service 27 func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 28 return daemon.containerCreate(params, true) 29 } 30 31 // ContainerCreate creates a regular container 32 func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 33 return daemon.containerCreate(params, false) 34 } 35 36 func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { 37 start := time.Now() 38 if params.Config == nil { 39 return containertypes.ContainerCreateCreatedBody{}, validationError{errors.New("Config cannot be empty in order to create a container")} 40 } 41 42 os := runtime.GOOS 43 if params.Config.Image != "" { 44 img, err := daemon.GetImage(params.Config.Image) 45 if err == nil { 46 os = img.OS 47 } 48 } else { 49 // This mean scratch. On Windows, we can safely assume that this is a linux 50 // container. On other platforms, it's the host OS (which it already is) 51 if runtime.GOOS == "windows" && system.LCOWSupported() { 52 os = "linux" 53 } 54 } 55 56 warnings, err := daemon.verifyContainerSettings(os, params.HostConfig, params.Config, false) 57 if err != nil { 58 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err} 59 } 60 61 err = verifyNetworkingConfig(params.NetworkingConfig) 62 if err != nil { 63 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err} 64 } 65 66 if params.HostConfig == nil { 67 params.HostConfig = &containertypes.HostConfig{} 68 } 69 err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) 70 if err != nil { 71 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, validationError{err} 72 } 73 74 container, err := daemon.create(params, managed) 75 if err != nil { 76 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 77 } 78 containerActions.WithValues("create").UpdateSince(start) 79 80 return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil 81 } 82 83 // Create creates a new container from the given configuration with a given name. 84 func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { 85 var ( 86 container *container.Container 87 img *image.Image 88 imgID image.ID 89 err error 90 ) 91 92 os := runtime.GOOS 93 if params.Config.Image != "" { 94 img, err = daemon.GetImage(params.Config.Image) 95 if err != nil { 96 return nil, err 97 } 98 if img.OS != "" { 99 os = img.OS 100 } else { 101 // default to the host OS except on Windows with LCOW 102 if runtime.GOOS == "windows" && system.LCOWSupported() { 103 os = "linux" 104 } 105 } 106 imgID = img.ID() 107 108 if runtime.GOOS == "windows" && img.OS == "linux" && !system.LCOWSupported() { 109 return nil, errors.New("operating system on which parent image was created is not Windows") 110 } 111 } else { 112 if runtime.GOOS == "windows" { 113 os = "linux" // 'scratch' case. 114 } 115 } 116 117 if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { 118 return nil, validationError{err} 119 } 120 121 if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { 122 return nil, validationError{err} 123 } 124 125 if container, err = daemon.newContainer(params.Name, os, params.Config, params.HostConfig, imgID, managed); err != nil { 126 return nil, err 127 } 128 defer func() { 129 if retErr != nil { 130 if err := daemon.cleanupContainer(container, true, true); err != nil { 131 logrus.Errorf("failed to cleanup container on create error: %v", err) 132 } 133 } 134 }() 135 136 if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { 137 return nil, err 138 } 139 140 container.HostConfig.StorageOpt = params.HostConfig.StorageOpt 141 142 // Fixes: https://github.com/moby/moby/issues/34074 and 143 // https://github.com/docker/for-win/issues/999. 144 // Merge the daemon's storage options if they aren't already present. We only 145 // do this on Windows as there's no effective sandbox size limit other than 146 // physical on Linux. 147 if runtime.GOOS == "windows" { 148 if container.HostConfig.StorageOpt == nil { 149 container.HostConfig.StorageOpt = make(map[string]string) 150 } 151 for _, v := range daemon.configStore.GraphOptions { 152 opt := strings.SplitN(v, "=", 2) 153 if _, ok := container.HostConfig.StorageOpt[opt[0]]; !ok { 154 container.HostConfig.StorageOpt[opt[0]] = opt[1] 155 } 156 } 157 } 158 159 // Set RWLayer for container after mount labels have been set 160 if err := daemon.setRWLayer(container); err != nil { 161 return nil, systemError{err} 162 } 163 164 rootIDs := daemon.idMappings.RootPair() 165 if err := idtools.MkdirAndChown(container.Root, 0700, rootIDs); err != nil { 166 return nil, err 167 } 168 if err := idtools.MkdirAndChown(container.CheckpointDir(), 0700, rootIDs); err != nil { 169 return nil, err 170 } 171 172 if err := daemon.setHostConfig(container, params.HostConfig); err != nil { 173 return nil, err 174 } 175 176 if err := daemon.createContainerOSSpecificSettings(container, params.Config, params.HostConfig); err != nil { 177 return nil, err 178 } 179 180 var endpointsConfigs map[string]*networktypes.EndpointSettings 181 if params.NetworkingConfig != nil { 182 endpointsConfigs = params.NetworkingConfig.EndpointsConfig 183 } 184 // Make sure NetworkMode has an acceptable value. We do this to ensure 185 // backwards API compatibility. 186 runconfig.SetDefaultNetModeIfBlank(container.HostConfig) 187 188 daemon.updateContainerNetworkSettings(container, endpointsConfigs) 189 if err := daemon.Register(container); err != nil { 190 return nil, err 191 } 192 stateCtr.set(container.ID, "stopped") 193 daemon.LogContainerEvent(container, "create") 194 return container, nil 195 } 196 197 func toHostConfigSelinuxLabels(labels []string) []string { 198 for i, l := range labels { 199 labels[i] = "label=" + l 200 } 201 return labels 202 } 203 204 func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) ([]string, error) { 205 for _, opt := range hostConfig.SecurityOpt { 206 con := strings.Split(opt, "=") 207 if con[0] == "label" { 208 // Caller overrode SecurityOpts 209 return nil, nil 210 } 211 } 212 ipcMode := hostConfig.IpcMode 213 pidMode := hostConfig.PidMode 214 privileged := hostConfig.Privileged 215 if ipcMode.IsHost() || pidMode.IsHost() || privileged { 216 return toHostConfigSelinuxLabels(label.DisableSecOpt()), nil 217 } 218 219 var ipcLabel []string 220 var pidLabel []string 221 ipcContainer := ipcMode.Container() 222 pidContainer := pidMode.Container() 223 if ipcContainer != "" { 224 c, err := daemon.GetContainer(ipcContainer) 225 if err != nil { 226 return nil, err 227 } 228 ipcLabel = label.DupSecOpt(c.ProcessLabel) 229 if pidContainer == "" { 230 return toHostConfigSelinuxLabels(ipcLabel), err 231 } 232 } 233 if pidContainer != "" { 234 c, err := daemon.GetContainer(pidContainer) 235 if err != nil { 236 return nil, err 237 } 238 239 pidLabel = label.DupSecOpt(c.ProcessLabel) 240 if ipcContainer == "" { 241 return toHostConfigSelinuxLabels(pidLabel), err 242 } 243 } 244 245 if pidLabel != nil && ipcLabel != nil { 246 for i := 0; i < len(pidLabel); i++ { 247 if pidLabel[i] != ipcLabel[i] { 248 return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") 249 } 250 } 251 return toHostConfigSelinuxLabels(pidLabel), nil 252 } 253 return nil, nil 254 } 255 256 func (daemon *Daemon) setRWLayer(container *container.Container) error { 257 var layerID layer.ChainID 258 if container.ImageID != "" { 259 img, err := daemon.stores[container.OS].imageStore.Get(container.ImageID) 260 if err != nil { 261 return err 262 } 263 layerID = img.RootFS.ChainID() 264 } 265 266 rwLayerOpts := &layer.CreateRWLayerOpts{ 267 MountLabel: container.MountLabel, 268 InitFunc: daemon.getLayerInit(), 269 StorageOpt: container.HostConfig.StorageOpt, 270 } 271 272 rwLayer, err := daemon.stores[container.OS].layerStore.CreateRWLayer(container.ID, layerID, rwLayerOpts) 273 if err != nil { 274 return err 275 } 276 container.RWLayer = rwLayer 277 278 return nil 279 } 280 281 // VolumeCreate creates a volume with the specified name, driver, and opts 282 // This is called directly from the Engine API 283 func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) { 284 if name == "" { 285 name = stringid.GenerateNonCryptoID() 286 } 287 288 v, err := daemon.volumes.Create(name, driverName, opts, labels) 289 if err != nil { 290 return nil, err 291 } 292 293 daemon.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()}) 294 apiV := volumeToAPIType(v) 295 apiV.Mountpoint = v.Path() 296 return apiV, nil 297 } 298 299 func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { 300 if img != nil && img.Config != nil { 301 if err := merge(config, img.Config); err != nil { 302 return err 303 } 304 } 305 // Reset the Entrypoint if it is [""] 306 if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { 307 config.Entrypoint = nil 308 } 309 if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { 310 return fmt.Errorf("No command specified") 311 } 312 return nil 313 } 314 315 // Checks if the client set configurations for more than one network while creating a container 316 // Also checks if the IPAMConfig is valid 317 func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { 318 if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { 319 return nil 320 } 321 if len(nwConfig.EndpointsConfig) == 1 { 322 for _, v := range nwConfig.EndpointsConfig { 323 if v != nil && v.IPAMConfig != nil { 324 if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { 325 return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) 326 } 327 if v.IPAMConfig.IPv6Address != "" { 328 n := net.ParseIP(v.IPAMConfig.IPv6Address) 329 // if the address is an invalid network address (ParseIP == nil) or if it is 330 // an IPv4 address (To4() != nil), then it is an invalid IPv6 address 331 if n == nil || n.To4() != nil { 332 return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) 333 } 334 } 335 } 336 } 337 return nil 338 } 339 l := make([]string, 0, len(nwConfig.EndpointsConfig)) 340 for k := range nwConfig.EndpointsConfig { 341 l = append(l, k) 342 } 343 return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) 344 }