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