github.com/gravitational/moby@v1.13.1/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/Sirupsen/logrus" 13 apierrors "github.com/docker/docker/api/errors" 14 "github.com/docker/docker/api/types" 15 containertypes "github.com/docker/docker/api/types/container" 16 networktypes "github.com/docker/docker/api/types/network" 17 "github.com/docker/docker/container" 18 "github.com/docker/docker/image" 19 "github.com/docker/docker/layer" 20 "github.com/docker/docker/pkg/idtools" 21 "github.com/docker/docker/pkg/stringid" 22 "github.com/docker/docker/runconfig" 23 volumestore "github.com/docker/docker/volume/store" 24 "github.com/opencontainers/runc/libcontainer/label" 25 ) 26 27 // CreateManagedContainer creates a container that is managed by a Service 28 func (daemon *Daemon) CreateManagedContainer(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 29 return daemon.containerCreate(params, true) 30 } 31 32 // ContainerCreate creates a regular container 33 func (daemon *Daemon) ContainerCreate(params types.ContainerCreateConfig) (containertypes.ContainerCreateCreatedBody, error) { 34 return daemon.containerCreate(params, false) 35 } 36 37 func (daemon *Daemon) containerCreate(params types.ContainerCreateConfig, managed bool) (containertypes.ContainerCreateCreatedBody, error) { 38 start := time.Now() 39 if params.Config == nil { 40 return containertypes.ContainerCreateCreatedBody{}, fmt.Errorf("Config cannot be empty in order to create a container") 41 } 42 43 warnings, err := daemon.verifyContainerSettings(params.HostConfig, params.Config, false) 44 if err != nil { 45 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 46 } 47 48 err = daemon.verifyNetworkingConfig(params.NetworkingConfig) 49 if err != nil { 50 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 51 } 52 53 if params.HostConfig == nil { 54 params.HostConfig = &containertypes.HostConfig{} 55 } 56 err = daemon.adaptContainerSettings(params.HostConfig, params.AdjustCPUShares) 57 if err != nil { 58 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, err 59 } 60 61 container, err := daemon.create(params, managed) 62 if err != nil { 63 return containertypes.ContainerCreateCreatedBody{Warnings: warnings}, daemon.imageNotExistToErrcode(err) 64 } 65 containerActions.WithValues("create").UpdateSince(start) 66 67 return containertypes.ContainerCreateCreatedBody{ID: container.ID, Warnings: warnings}, nil 68 } 69 70 // Create creates a new container from the given configuration with a given name. 71 func (daemon *Daemon) create(params types.ContainerCreateConfig, managed bool) (retC *container.Container, retErr error) { 72 var ( 73 container *container.Container 74 img *image.Image 75 imgID image.ID 76 err error 77 ) 78 79 if params.Config.Image != "" { 80 img, err = daemon.GetImage(params.Config.Image) 81 if err != nil { 82 return nil, err 83 } 84 85 if runtime.GOOS == "solaris" && img.OS != "solaris " { 86 return nil, errors.New("Platform on which parent image was created is not Solaris") 87 } 88 imgID = img.ID() 89 } 90 91 if err := daemon.mergeAndVerifyConfig(params.Config, img); err != nil { 92 return nil, err 93 } 94 95 if err := daemon.mergeAndVerifyLogConfig(¶ms.HostConfig.LogConfig); err != nil { 96 return nil, err 97 } 98 99 if container, err = daemon.newContainer(params.Name, params.Config, params.HostConfig, imgID, managed); err != nil { 100 return nil, err 101 } 102 defer func() { 103 if retErr != nil { 104 if err := daemon.cleanupContainer(container, true, true); err != nil { 105 logrus.Errorf("failed to cleanup container on create error: %v", err) 106 } 107 } 108 }() 109 110 if err := daemon.setSecurityOptions(container, params.HostConfig); err != nil { 111 return nil, err 112 } 113 114 container.HostConfig.StorageOpt = params.HostConfig.StorageOpt 115 116 // Set RWLayer for container after mount labels have been set 117 if err := daemon.setRWLayer(container); err != nil { 118 return nil, err 119 } 120 121 rootUID, rootGID, err := idtools.GetRootUIDGID(daemon.uidMaps, daemon.gidMaps) 122 if err != nil { 123 return nil, err 124 } 125 if err := idtools.MkdirAs(container.Root, 0700, rootUID, rootGID); err != nil { 126 return nil, err 127 } 128 if err := idtools.MkdirAs(container.CheckpointDir(), 0700, rootUID, rootGID); err != nil { 129 return nil, err 130 } 131 132 if err := daemon.setHostConfig(container, params.HostConfig); err != nil { 133 return nil, err 134 } 135 136 if err := daemon.createContainerPlatformSpecificSettings(container, params.Config, params.HostConfig); err != nil { 137 return nil, err 138 } 139 140 var endpointsConfigs map[string]*networktypes.EndpointSettings 141 if params.NetworkingConfig != nil { 142 endpointsConfigs = params.NetworkingConfig.EndpointsConfig 143 } 144 // Make sure NetworkMode has an acceptable value. We do this to ensure 145 // backwards API compatibility. 146 container.HostConfig = runconfig.SetDefaultNetModeIfBlank(container.HostConfig) 147 148 daemon.updateContainerNetworkSettings(container, endpointsConfigs) 149 150 if err := container.ToDisk(); err != nil { 151 logrus.Errorf("Error saving new container to disk: %v", err) 152 return nil, err 153 } 154 if err := daemon.Register(container); err != nil { 155 return nil, err 156 } 157 daemon.LogContainerEvent(container, "create") 158 return container, nil 159 } 160 161 func (daemon *Daemon) generateSecurityOpt(ipcMode containertypes.IpcMode, pidMode containertypes.PidMode, privileged bool) ([]string, error) { 162 if ipcMode.IsHost() || pidMode.IsHost() || privileged { 163 return label.DisableSecOpt(), nil 164 } 165 166 var ipcLabel []string 167 var pidLabel []string 168 ipcContainer := ipcMode.Container() 169 pidContainer := pidMode.Container() 170 if ipcContainer != "" { 171 c, err := daemon.GetContainer(ipcContainer) 172 if err != nil { 173 return nil, err 174 } 175 ipcLabel = label.DupSecOpt(c.ProcessLabel) 176 if pidContainer == "" { 177 return ipcLabel, err 178 } 179 } 180 if pidContainer != "" { 181 c, err := daemon.GetContainer(pidContainer) 182 if err != nil { 183 return nil, err 184 } 185 186 pidLabel = label.DupSecOpt(c.ProcessLabel) 187 if ipcContainer == "" { 188 return pidLabel, err 189 } 190 } 191 192 if pidLabel != nil && ipcLabel != nil { 193 for i := 0; i < len(pidLabel); i++ { 194 if pidLabel[i] != ipcLabel[i] { 195 return nil, fmt.Errorf("--ipc and --pid containers SELinux labels aren't the same") 196 } 197 } 198 return pidLabel, nil 199 } 200 return nil, nil 201 } 202 203 func (daemon *Daemon) setRWLayer(container *container.Container) error { 204 var layerID layer.ChainID 205 if container.ImageID != "" { 206 img, err := daemon.imageStore.Get(container.ImageID) 207 if err != nil { 208 return err 209 } 210 layerID = img.RootFS.ChainID() 211 } 212 213 rwLayer, err := daemon.layerStore.CreateRWLayer(container.ID, layerID, container.MountLabel, daemon.getLayerInit(), container.HostConfig.StorageOpt) 214 215 if err != nil { 216 return err 217 } 218 container.RWLayer = rwLayer 219 220 return nil 221 } 222 223 // VolumeCreate creates a volume with the specified name, driver, and opts 224 // This is called directly from the Engine API 225 func (daemon *Daemon) VolumeCreate(name, driverName string, opts, labels map[string]string) (*types.Volume, error) { 226 if name == "" { 227 name = stringid.GenerateNonCryptoID() 228 } 229 230 v, err := daemon.volumes.Create(name, driverName, opts, labels) 231 if err != nil { 232 if volumestore.IsNameConflict(err) { 233 return nil, fmt.Errorf("A volume named %s already exists. Choose a different volume name.", name) 234 } 235 return nil, err 236 } 237 238 daemon.LogVolumeEvent(v.Name(), "create", map[string]string{"driver": v.DriverName()}) 239 apiV := volumeToAPIType(v) 240 apiV.Mountpoint = v.Path() 241 return apiV, nil 242 } 243 244 func (daemon *Daemon) mergeAndVerifyConfig(config *containertypes.Config, img *image.Image) error { 245 if img != nil && img.Config != nil { 246 if err := merge(config, img.Config); err != nil { 247 return err 248 } 249 } 250 // Reset the Entrypoint if it is [""] 251 if len(config.Entrypoint) == 1 && config.Entrypoint[0] == "" { 252 config.Entrypoint = nil 253 } 254 if len(config.Entrypoint) == 0 && len(config.Cmd) == 0 { 255 return fmt.Errorf("No command specified") 256 } 257 return nil 258 } 259 260 // Checks if the client set configurations for more than one network while creating a container 261 // Also checks if the IPAMConfig is valid 262 func (daemon *Daemon) verifyNetworkingConfig(nwConfig *networktypes.NetworkingConfig) error { 263 if nwConfig == nil || len(nwConfig.EndpointsConfig) == 0 { 264 return nil 265 } 266 if len(nwConfig.EndpointsConfig) == 1 { 267 for _, v := range nwConfig.EndpointsConfig { 268 if v != nil && v.IPAMConfig != nil { 269 if v.IPAMConfig.IPv4Address != "" && net.ParseIP(v.IPAMConfig.IPv4Address).To4() == nil { 270 return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv4 address: %s", v.IPAMConfig.IPv4Address)) 271 } 272 if v.IPAMConfig.IPv6Address != "" { 273 n := net.ParseIP(v.IPAMConfig.IPv6Address) 274 // if the address is an invalid network address (ParseIP == nil) or if it is 275 // an IPv4 address (To4() != nil), then it is an invalid IPv6 address 276 if n == nil || n.To4() != nil { 277 return apierrors.NewBadRequestError(fmt.Errorf("invalid IPv6 address: %s", v.IPAMConfig.IPv6Address)) 278 } 279 } 280 } 281 } 282 return nil 283 } 284 l := make([]string, 0, len(nwConfig.EndpointsConfig)) 285 for k := range nwConfig.EndpointsConfig { 286 l = append(l, k) 287 } 288 err := fmt.Errorf("Container cannot be connected to network endpoints: %s", strings.Join(l, ", ")) 289 return apierrors.NewBadRequestError(err) 290 }