github.com/docker/engine@v22.0.0-20211208180946-d456264580cf+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 warnings, err := daemon.verifyContainerSettings(opts.params.HostConfig, opts.params.Config, false) 65 if err != nil { 66 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 67 } 68 69 if opts.params.Platform == nil && opts.params.Config.Image != "" { 70 if img, _ := daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform); img != nil { 71 p := platforms.DefaultSpec() 72 imgPlat := v1.Platform{ 73 OS: img.OS, 74 Architecture: img.Architecture, 75 Variant: img.Variant, 76 } 77 78 if !images.OnlyPlatformWithFallback(p).Match(imgPlat) { 79 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))) 80 } 81 } 82 } 83 84 err = verifyNetworkingConfig(opts.params.NetworkingConfig) 85 if err != nil { 86 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 87 } 88 89 if opts.params.HostConfig == nil { 90 opts.params.HostConfig = &containertypes.HostConfig{} 91 } 92 err = daemon.adaptContainerSettings(opts.params.HostConfig, opts.params.AdjustCPUShares) 93 if err != nil { 94 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, errdefs.InvalidParameter(err) 95 } 96 97 ctr, err := daemon.create(opts) 98 if err != nil { 99 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 100 } 101 containerActions.WithValues("create").UpdateSince(start) 102 103 if warnings == nil { 104 warnings = make([]string, 0) // Create an empty slice to avoid https://github.com/moby/moby/issues/38222 105 } 106 107 return containertypes.ContainerCreateCreatedBody{ID: ctr.ID, Warnings: warnings}, nil 108 } 109 110 // Create creates a new container from the given configuration with a given name. 111 func (daemon *Daemon) create(opts createOpts) (retC *container.Container, retErr error) { 112 var ( 113 ctr *container.Container 114 img *image.Image 115 imgID image.ID 116 err error 117 os = runtime.GOOS 118 ) 119 120 if opts.params.Config.Image != "" { 121 img, err = daemon.imageService.GetImage(opts.params.Config.Image, opts.params.Platform) 122 if err != nil { 123 return nil, err 124 } 125 os = img.OperatingSystem() 126 imgID = img.ID() 127 if !system.IsOSSupported(os) { 128 return nil, errors.New("operating system on which parent image was created is not Windows") 129 } 130 } else if isWindows { 131 os = "linux" // 'scratch' case. 132 } 133 134 // On WCOW, if are not being invoked by the builder to create this container (where 135 // ignoreImagesArgEscaped will be true) - if the image already has its arguments escaped, 136 // ensure that this is replicated across to the created container to avoid double-escaping 137 // of the arguments/command line when the runtime attempts to run the container. 138 if os == "windows" && !opts.ignoreImagesArgsEscaped && img != nil && img.RunConfig().ArgsEscaped { 139 opts.params.Config.ArgsEscaped = true 140 } 141 142 if err := daemon.mergeAndVerifyConfig(opts.params.Config, img); err != nil { 143 return nil, errdefs.InvalidParameter(err) 144 } 145 146 if err := daemon.mergeAndVerifyLogConfig(&opts.params.HostConfig.LogConfig); err != nil { 147 return nil, errdefs.InvalidParameter(err) 148 } 149 150 if ctr, err = daemon.newContainer(opts.params.Name, os, opts.params.Config, opts.params.HostConfig, imgID, opts.managed); err != nil { 151 return nil, err 152 } 153 defer func() { 154 if retErr != nil { 155 if err := daemon.cleanupContainer(ctr, true, true); err != nil { 156 logrus.Errorf("failed to cleanup container on create error: %v", err) 157 } 158 } 159 }() 160 161 if err := daemon.setSecurityOptions(ctr, opts.params.HostConfig); err != nil { 162 return nil, err 163 } 164 165 ctr.HostConfig.StorageOpt = opts.params.HostConfig.StorageOpt 166 167 // Set RWLayer for container after mount labels have been set 168 rwLayer, err := daemon.imageService.CreateLayer(ctr, setupInitLayer(daemon.idMapping)) 169 if err != nil { 170 return nil, errdefs.System(err) 171 } 172 ctr.RWLayer = rwLayer 173 174 current := idtools.CurrentIdentity() 175 if err := idtools.MkdirAndChown(ctr.Root, 0710, idtools.Identity{UID: current.UID, GID: daemon.IdentityMapping().RootPair().GID}); err != nil { 176 return nil, err 177 } 178 if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, current); err != nil { 179 return nil, err 180 } 181 182 if err := daemon.setHostConfig(ctr, opts.params.HostConfig); err != nil { 183 return nil, err 184 } 185 186 if err := daemon.createContainerOSSpecificSettings(ctr, opts.params.Config, opts.params.HostConfig); err != nil { 187 return nil, err 188 } 189 190 var endpointsConfigs map[string]*networktypes.EndpointSettings 191 if opts.params.NetworkingConfig != nil { 192 endpointsConfigs = opts.params.NetworkingConfig.EndpointsConfig 193 } 194 // Make sure NetworkMode has an acceptable value. We do this to ensure 195 // backwards API compatibility. 196 runconfig.SetDefaultNetModeIfBlank(ctr.HostConfig) 197 198 daemon.updateContainerNetworkSettings(ctr, endpointsConfigs) 199 if err := daemon.Register(ctr); err != nil { 200 return nil, err 201 } 202 stateCtr.set(ctr.ID, "stopped") 203 daemon.LogContainerEvent(ctr, "create") 204 return ctr, nil 205 } 206 207 func toHostConfigSelinuxLabels(labels []string) []string { 208 for i, l := range labels { 209 labels[i] = "label=" + l 210 } 211 return labels 212 } 213 214 func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) ([]string, error) { 215 for _, opt := range hostConfig.SecurityOpt { 216 con := strings.Split(opt, "=") 217 if con[0] == "label" { 218 // Caller overrode SecurityOpts 219 return nil, nil 220 } 221 } 222 ipcMode := hostConfig.IpcMode 223 pidMode := hostConfig.PidMode 224 privileged := hostConfig.Privileged 225 if ipcMode.IsHost() || pidMode.IsHost() || privileged { 226 return toHostConfigSelinuxLabels(selinux.DisableSecOpt()), nil 227 } 228 229 var ipcLabel []string 230 var pidLabel []string 231 ipcContainer := ipcMode.Container() 232 pidContainer := pidMode.Container() 233 if ipcContainer != "" { 234 c, err := daemon.GetContainer(ipcContainer) 235 if err != nil { 236 return nil, err 237 } 238 ipcLabel, err = selinux.DupSecOpt(c.ProcessLabel) 239 if err != nil { 240 return nil, err 241 } 242 if pidContainer == "" { 243 return toHostConfigSelinuxLabels(ipcLabel), err 244 } 245 } 246 if pidContainer != "" { 247 c, err := daemon.GetContainer(pidContainer) 248 if err != nil { 249 return nil, err 250 } 251 252 pidLabel, err = selinux.DupSecOpt(c.ProcessLabel) 253 if err != nil { 254 return nil, err 255 } 256 if ipcContainer == "" { 257 return toHostConfigSelinuxLabels(pidLabel), err 258 } 259 } 260 261 if pidLabel != nil && ipcLabel != nil { 262 for i := 0; i < len(pidLabel); i++ { 263 if pidLabel[i] != ipcLabel[i] { 264 return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") 265 } 266 } 267 return toHostConfigSelinuxLabels(pidLabel), nil 268 } 269 return nil, nil 270 } 271 272 func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { 273 if img != nil && img.Config != nil { 274 if err := merge(config, img.Config); err != nil { 275 return err 276 } 277 } 278 // Reset the Entrypoint if it is [""] 279 if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { 280 config.Entrypoint = nil 281 } 282 if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { 283 return fmt.Errorf("No command specified") 284 } 285 return nil 286 } 287 288 // Checks if the client set configurations for more than one network while creating a container 289 // Also checks if the IPAMConfig is valid 290 func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { 291 if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { 292 return nil 293 } 294 if len(nwConfig.EndpointsConfig) > 1 { 295 l := make([]string, 0, len(nwConfig.EndpointsConfig)) 296 for k := range nwConfig.EndpointsConfig { 297 l = append(l, k) 298 } 299 return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) 300 } 301 302 for k, v := range nwConfig.EndpointsConfig { 303 if v == nil { 304 return errdefs.InvalidParameter(errors.Errorf("no EndpointSettings for %s", k)) 305 } 306 if v.IPAMConfig != nil { 307 if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { 308 return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) 309 } 310 if v.IPAMConfig.IPv6Address != "" { 311 n := net.ParseIP(v.IPAMConfig.IPv6Address) 312 // if the address is an invalid network address (ParseIP == nil) or if it is 313 // an IPv4 address (To4() != nil), then it is an invalid IPv6 address 314 if n == nil || n.To4() != nil { 315 return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) 316 } 317 } 318 } 319 } 320 return nil 321 }