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