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