github.com/jiasir/docker@v1.3.3-0.20170609024000-252e610103e7/daemon/container.go (about) 1 package daemon 2 3 import ( 4 "fmt" 5 "os" 6 "path/filepath" 7 "time" 8 9 "github.com/docker/docker/api/errors" 10 containertypes "github.com/docker/docker/api/types/container" 11 "github.com/docker/docker/api/types/strslice" 12 "github.com/docker/docker/container" 13 "github.com/docker/docker/daemon/network" 14 "github.com/docker/docker/image" 15 "github.com/docker/docker/opts" 16 "github.com/docker/docker/pkg/signal" 17 "github.com/docker/docker/pkg/system" 18 "github.com/docker/docker/pkg/truncindex" 19 "github.com/docker/docker/runconfig" 20 "github.com/docker/go-connections/nat" 21 ) 22 23 // GetContainer looks for a container using the provided information, which could be 24 // one of the following inputs from the caller: 25 // - A full container ID, which will exact match a container in daemon's list 26 // - A container name, which will only exact match via the GetByName() function 27 // - A partial container ID prefix (e.g. short ID) of any length that is 28 // unique enough to only return a single container object 29 // If none of these searches succeed, an error is returned 30 func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, error) { 31 if len(prefixOrName) == 0 { 32 return nil, errors.NewBadRequestError(fmt.Errorf("No container name or ID supplied")) 33 } 34 35 if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil { 36 // prefix is an exact match to a full container ID 37 return containerByID, nil 38 } 39 40 // GetByName will match only an exact name provided; we ignore errors 41 if containerByName, _ := daemon.GetByName(prefixOrName); containerByName != nil { 42 // prefix is an exact match to a full container Name 43 return containerByName, nil 44 } 45 46 containerID, indexError := daemon.idIndex.Get(prefixOrName) 47 if indexError != nil { 48 // When truncindex defines an error type, use that instead 49 if indexError == truncindex.ErrNotExist { 50 err := fmt.Errorf("No such container: %s", prefixOrName) 51 return nil, errors.NewRequestNotFoundError(err) 52 } 53 return nil, indexError 54 } 55 return daemon.containers.Get(containerID), nil 56 } 57 58 // checkContainer make sure the specified container validates the specified conditions 59 func (daemon *Daemon) checkContainer(container *container.Container, conditions ...func(*container.Container) error) error { 60 for _, condition := range conditions { 61 if err := condition(container); err != nil { 62 return err 63 } 64 } 65 return nil 66 } 67 68 // Exists returns a true if a container of the specified ID or name exists, 69 // false otherwise. 70 func (daemon *Daemon) Exists(id string) bool { 71 c, _ := daemon.GetContainer(id) 72 return c != nil 73 } 74 75 // IsPaused returns a bool indicating if the specified container is paused. 76 func (daemon *Daemon) IsPaused(id string) bool { 77 c, _ := daemon.GetContainer(id) 78 return c.State.IsPaused() 79 } 80 81 func (daemon *Daemon) containerRoot(id string) string { 82 return filepath.Join(daemon.repository, id) 83 } 84 85 // Load reads the contents of a container from disk 86 // This is typically done at startup. 87 func (daemon *Daemon) load(id string) (*container.Container, error) { 88 container := daemon.newBaseContainer(id) 89 90 if err := container.FromDisk(); err != nil { 91 return nil, err 92 } 93 94 if container.ID != id { 95 return container, fmt.Errorf("Container %s is stored at %s", container.ID, id) 96 } 97 98 return container, nil 99 } 100 101 // Register makes a container object usable by the daemon as <container.ID> 102 func (daemon *Daemon) Register(c *container.Container) { 103 // Attach to stdout and stderr 104 if c.Config.OpenStdin { 105 c.StreamConfig.NewInputPipes() 106 } else { 107 c.StreamConfig.NewNopInputPipe() 108 } 109 110 daemon.containers.Add(c.ID, c) 111 daemon.idIndex.Add(c.ID) 112 } 113 114 func (daemon *Daemon) newContainer(name string, config *containertypes.Config, hostConfig *containertypes.HostConfig, imgID image.ID, managed bool) (*container.Container, error) { 115 var ( 116 id string 117 err error 118 noExplicitName = name == "" 119 ) 120 id, name, err = daemon.generateIDAndName(name) 121 if err != nil { 122 return nil, err 123 } 124 125 if hostConfig.NetworkMode.IsHost() { 126 if config.Hostname == "" { 127 config.Hostname, err = os.Hostname() 128 if err != nil { 129 return nil, err 130 } 131 } 132 } else { 133 daemon.generateHostname(id, config) 134 } 135 entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd) 136 137 base := daemon.newBaseContainer(id) 138 base.Created = time.Now().UTC() 139 base.Managed = managed 140 base.Path = entrypoint 141 base.Args = args //FIXME: de-duplicate from config 142 base.Config = config 143 base.HostConfig = &containertypes.HostConfig{} 144 base.ImageID = imgID 145 base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName} 146 base.Name = name 147 base.Driver = daemon.GraphDriverName() 148 149 return base, err 150 } 151 152 // GetByName returns a container given a name. 153 func (daemon *Daemon) GetByName(name string) (*container.Container, error) { 154 if len(name) == 0 { 155 return nil, fmt.Errorf("No container name supplied") 156 } 157 fullName := name 158 if name[0] != '/' { 159 fullName = "/" + name 160 } 161 id, err := daemon.nameIndex.Get(fullName) 162 if err != nil { 163 return nil, fmt.Errorf("Could not find entity for %s", name) 164 } 165 e := daemon.containers.Get(id) 166 if e == nil { 167 return nil, fmt.Errorf("Could not find container for entity id %s", id) 168 } 169 return e, nil 170 } 171 172 // newBaseContainer creates a new container with its initial 173 // configuration based on the root storage from the daemon. 174 func (daemon *Daemon) newBaseContainer(id string) *container.Container { 175 return container.NewBaseContainer(id, daemon.containerRoot(id)) 176 } 177 178 func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint strslice.StrSlice, configCmd strslice.StrSlice) (string, []string) { 179 if len(configEntrypoint) != 0 { 180 return configEntrypoint[0], append(configEntrypoint[1:], configCmd...) 181 } 182 return configCmd[0], configCmd[1:] 183 } 184 185 func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) { 186 // Generate default hostname 187 if config.Hostname == "" { 188 config.Hostname = id[:12] 189 } 190 } 191 192 func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error { 193 container.Lock() 194 defer container.Unlock() 195 return daemon.parseSecurityOpt(container, hostConfig) 196 } 197 198 func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error { 199 // Do not lock while creating volumes since this could be calling out to external plugins 200 // Don't want to block other actions, like `docker ps` because we're waiting on an external plugin 201 if err := daemon.registerMountPoints(container, hostConfig); err != nil { 202 return err 203 } 204 205 container.Lock() 206 defer container.Unlock() 207 208 // Register any links from the host config before starting the container 209 if err := daemon.registerLinks(container, hostConfig); err != nil { 210 return err 211 } 212 213 runconfig.SetDefaultNetModeIfBlank(hostConfig) 214 container.HostConfig = hostConfig 215 return container.ToDisk() 216 } 217 218 // verifyContainerSettings performs validation of the hostconfig and config 219 // structures. 220 func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool) ([]string, error) { 221 222 // First perform verification of settings common across all platforms. 223 if config != nil { 224 if config.WorkingDir != "" { 225 config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics 226 if !system.IsAbs(config.WorkingDir) { 227 return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir) 228 } 229 } 230 231 if len(config.StopSignal) > 0 { 232 _, err := signal.ParseSignal(config.StopSignal) 233 if err != nil { 234 return nil, err 235 } 236 } 237 238 // Validate if Env contains empty variable or not (e.g., ``, `=foo`) 239 for _, env := range config.Env { 240 if _, err := opts.ValidateEnv(env); err != nil { 241 return nil, err 242 } 243 } 244 245 // Validate the healthcheck params of Config 246 if config.Healthcheck != nil { 247 if config.Healthcheck.Interval != 0 && config.Healthcheck.Interval < containertypes.MinimumDuration { 248 return nil, fmt.Errorf("Interval in Healthcheck cannot be less than %s", containertypes.MinimumDuration) 249 } 250 251 if config.Healthcheck.Timeout != 0 && config.Healthcheck.Timeout < containertypes.MinimumDuration { 252 return nil, fmt.Errorf("Timeout in Healthcheck cannot be less than %s", containertypes.MinimumDuration) 253 } 254 255 if config.Healthcheck.Retries < 0 { 256 return nil, fmt.Errorf("Retries in Healthcheck cannot be negative") 257 } 258 259 if config.Healthcheck.StartPeriod != 0 && config.Healthcheck.StartPeriod < containertypes.MinimumDuration { 260 return nil, fmt.Errorf("StartPeriod in Healthcheck cannot be less than %s", containertypes.MinimumDuration) 261 } 262 } 263 } 264 265 if hostConfig == nil { 266 return nil, nil 267 } 268 269 if hostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() { 270 return nil, fmt.Errorf("can't create 'AutoRemove' container with restart policy") 271 } 272 273 for _, extraHost := range hostConfig.ExtraHosts { 274 if _, err := opts.ValidateExtraHost(extraHost); err != nil { 275 return nil, err 276 } 277 } 278 279 for port := range hostConfig.PortBindings { 280 _, portStr := nat.SplitProtoPort(string(port)) 281 if _, err := nat.ParsePort(portStr); err != nil { 282 return nil, fmt.Errorf("invalid port specification: %q", portStr) 283 } 284 for _, pb := range hostConfig.PortBindings[port] { 285 _, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort)) 286 if err != nil { 287 return nil, fmt.Errorf("invalid port specification: %q", pb.HostPort) 288 } 289 } 290 } 291 292 p := hostConfig.RestartPolicy 293 294 switch p.Name { 295 case "always", "unless-stopped", "no": 296 if p.MaximumRetryCount != 0 { 297 return nil, fmt.Errorf("maximum retry count cannot be used with restart policy '%s'", p.Name) 298 } 299 case "on-failure": 300 if p.MaximumRetryCount < 0 { 301 return nil, fmt.Errorf("maximum retry count cannot be negative") 302 } 303 case "": 304 // do nothing 305 default: 306 return nil, fmt.Errorf("invalid restart policy '%s'", p.Name) 307 } 308 309 // Now do platform-specific verification 310 return verifyPlatformContainerSettings(daemon, hostConfig, config, update) 311 }