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