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