gopkg.in/docker/docker.v23@v23.0.11/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/runconfig" 20 v1 "github.com/opencontainers/image-spec/specs-go/v1" 21 "github.com/opencontainers/selinux/go-selinux" 22 "github.com/pkg/errors" 23 "github.com/sirupsen/logrus" 24 archvariant "github.com/tonistiigi/go-archvariant" 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.CreateResponse, 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.CreateResponse, 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.CreateResponse, 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.CreateResponse, error) { 59 start := time.Now() 60 if opts.params.Config == nil { 61 return containertypes.CreateResponse{}, 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.CreateResponse{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 := maximumSpec() 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.CreateResponse{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.CreateResponse{Warnings: warnings}, errdefs.InvalidParameter(err) 95 } 96 97 ctr, err := daemon.create(opts) 98 if err != nil { 99 return containertypes.CreateResponse{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.CreateResponse{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 } else if isWindows { 128 os = "linux" // 'scratch' case. 129 } 130 131 // On WCOW, if are not being invoked by the builder to create this container (where 132 // ignoreImagesArgEscaped will be true) - if the image already has its arguments escaped, 133 // ensure that this is replicated across to the created container to avoid double-escaping 134 // of the arguments/command line when the runtime attempts to run the container. 135 if os == "windows" && !opts.ignoreImagesArgsEscaped && img != nil && img.RunConfig().ArgsEscaped { 136 opts.params.Config.ArgsEscaped = true 137 } 138 139 if err := daemon.mergeAndVerifyConfig(opts.params.Config, img); err != nil { 140 return nil, errdefs.InvalidParameter(err) 141 } 142 143 if err := daemon.mergeAndVerifyLogConfig(&opts.params.HostConfig.LogConfig); err != nil { 144 return nil, errdefs.InvalidParameter(err) 145 } 146 147 if ctr, err = daemon.newContainer(opts.params.Name, os, opts.params.Config, opts.params.HostConfig, imgID, opts.managed); err != nil { 148 return nil, err 149 } 150 defer func() { 151 if retErr != nil { 152 err = daemon.cleanupContainer(ctr, types.ContainerRmConfig{ 153 ForceRemove: true, 154 RemoveVolume: true, 155 }) 156 if err != nil { 157 logrus.WithError(err).Error("failed to cleanup container on create error") 158 } 159 } 160 }() 161 162 if err := daemon.setSecurityOptions(ctr, opts.params.HostConfig); err != nil { 163 return nil, err 164 } 165 166 ctr.HostConfig.StorageOpt = opts.params.HostConfig.StorageOpt 167 168 // Set RWLayer for container after mount labels have been set 169 rwLayer, err := daemon.imageService.CreateLayer(ctr, setupInitLayer(daemon.idMapping)) 170 if err != nil { 171 return nil, errdefs.System(err) 172 } 173 ctr.RWLayer = rwLayer 174 175 current := idtools.CurrentIdentity() 176 if err := idtools.MkdirAndChown(ctr.Root, 0710, idtools.Identity{UID: current.UID, GID: daemon.IdentityMapping().RootPair().GID}); err != nil { 177 return nil, err 178 } 179 if err := idtools.MkdirAndChown(ctr.CheckpointDir(), 0700, current); err != nil { 180 return nil, err 181 } 182 183 if err := daemon.setHostConfig(ctr, opts.params.HostConfig); err != nil { 184 return nil, err 185 } 186 187 if err := daemon.createContainerOSSpecificSettings(ctr, opts.params.Config, opts.params.HostConfig); err != nil { 188 return nil, err 189 } 190 191 var endpointsConfigs map[string]*networktypes.EndpointSettings 192 if opts.params.NetworkingConfig != nil { 193 endpointsConfigs = opts.params.NetworkingConfig.EndpointsConfig 194 } 195 // Make sure NetworkMode has an acceptable value. We do this to ensure 196 // backwards API compatibility. 197 runconfig.SetDefaultNetModeIfBlank(ctr.HostConfig) 198 199 daemon.updateContainerNetworkSettings(ctr, endpointsConfigs) 200 if err := daemon.Register(ctr); err != nil { 201 return nil, err 202 } 203 stateCtr.set(ctr.ID, "stopped") 204 daemon.LogContainerEvent(ctr, "create") 205 return ctr, nil 206 } 207 208 func toHostConfigSelinuxLabels(labels []string) []string { 209 for i, l := range labels { 210 labels[i] = "label=" + l 211 } 212 return labels 213 } 214 215 func (daemon *Daemon) generateSecurityOpt(hostConfig *containertypes.HostConfig) ([]string, error) { 216 for _, opt := range hostConfig.SecurityOpt { 217 con := strings.Split(opt, "=") 218 if con[0] == "label" { 219 // Caller overrode SecurityOpts 220 return nil, nil 221 } 222 } 223 ipcMode := hostConfig.IpcMode 224 pidMode := hostConfig.PidMode 225 privileged := hostConfig.Privileged 226 if ipcMode.IsHost() || pidMode.IsHost() || privileged { 227 return toHostConfigSelinuxLabels(selinux.DisableSecOpt()), nil 228 } 229 230 var ipcLabel []string 231 var pidLabel []string 232 ipcContainer := ipcMode.Container() 233 pidContainer := pidMode.Container() 234 if ipcContainer != "" { 235 c, err := daemon.GetContainer(ipcContainer) 236 if err != nil { 237 return nil, err 238 } 239 ipcLabel, err = selinux.DupSecOpt(c.ProcessLabel) 240 if err != nil { 241 return nil, err 242 } 243 if pidContainer == "" { 244 return toHostConfigSelinuxLabels(ipcLabel), err 245 } 246 } 247 if pidContainer != "" { 248 c, err := daemon.GetContainer(pidContainer) 249 if err != nil { 250 return nil, err 251 } 252 253 pidLabel, err = selinux.DupSecOpt(c.ProcessLabel) 254 if err != nil { 255 return nil, err 256 } 257 if ipcContainer == "" { 258 return toHostConfigSelinuxLabels(pidLabel), err 259 } 260 } 261 262 if pidLabel != nil && ipcLabel != nil { 263 for i := 0; i < len(pidLabel); i++ { 264 if pidLabel[i] != ipcLabel[i] { 265 return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") 266 } 267 } 268 return toHostConfigSelinuxLabels(pidLabel), nil 269 } 270 return nil, nil 271 } 272 273 func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { 274 if img != nil && img.Config != nil { 275 if err := merge(config, img.Config); err != nil { 276 return err 277 } 278 } 279 // Reset the Entrypoint if it is [""] 280 if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { 281 config.Entrypoint = nil 282 } 283 if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { 284 return fmt.Errorf("No command specified") 285 } 286 return nil 287 } 288 289 // Checks if the client set configurations for more than one network while creating a container 290 // Also checks if the IPAMConfig is valid 291 func verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { 292 if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { 293 return nil 294 } 295 if len(nwConfig.EndpointsConfig) > 1 { 296 l := make([]string, 0, len(nwConfig.EndpointsConfig)) 297 for k := range nwConfig.EndpointsConfig { 298 l = append(l, k) 299 } 300 return errors.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) 301 } 302 303 for k, v := range nwConfig.EndpointsConfig { 304 if v == nil { 305 return errdefs.InvalidParameter(errors.Errorf("no EndpointSettings for %s", k)) 306 } 307 if v.IPAMConfig != nil { 308 if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { 309 return errors.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address) 310 } 311 if v.IPAMConfig.IPv6Address != "" { 312 n := net.ParseIP(v.IPAMConfig.IPv6Address) 313 // if the address is an invalid network address (ParseIP == nil) or if it is 314 // an IPv4 address (To4() != nil), then it is an invalid IPv6 address 315 if n == nil || n.To4() != nil { 316 return errors.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address) 317 } 318 } 319 } 320 } 321 return nil 322 } 323 324 // maximumSpec returns the distribution platform with maximum compatibility for the current node. 325 func maximumSpec() v1.Platform { 326 p := platforms.DefaultSpec() 327 if p.Architecture == "amd64" { 328 p.Variant = archvariant.AMD64Variant() 329 } 330 return p 331 }