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