mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	Using:
        gomvpkg -from github.com/docker/docker/errors
                -to github.com/docker/docker/api/errors
                -vcs_mv_cmd "git mv {{.Src}} {{.Dst}}"
Signed-off-by: Daniel Nephin <dnephin@docker.com>
		
	
			
		
			
				
	
	
		
			277 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			277 lines
		
	
	
	
		
			8.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package daemon
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"path/filepath"
 | 
						|
	"regexp"
 | 
						|
	"time"
 | 
						|
 | 
						|
	"github.com/docker/docker/api/errors"
 | 
						|
	containertypes "github.com/docker/docker/api/types/container"
 | 
						|
	"github.com/docker/docker/api/types/strslice"
 | 
						|
	"github.com/docker/docker/container"
 | 
						|
	"github.com/docker/docker/daemon/network"
 | 
						|
	"github.com/docker/docker/image"
 | 
						|
	"github.com/docker/docker/pkg/signal"
 | 
						|
	"github.com/docker/docker/pkg/system"
 | 
						|
	"github.com/docker/docker/pkg/truncindex"
 | 
						|
	"github.com/docker/go-connections/nat"
 | 
						|
)
 | 
						|
 | 
						|
// GetContainer looks for a container using the provided information, which could be
 | 
						|
// one of the following inputs from the caller:
 | 
						|
//  - A full container ID, which will exact match a container in daemon's list
 | 
						|
//  - A container name, which will only exact match via the GetByName() function
 | 
						|
//  - A partial container ID prefix (e.g. short ID) of any length that is
 | 
						|
//    unique enough to only return a single container object
 | 
						|
//  If none of these searches succeed, an error is returned
 | 
						|
func (daemon *Daemon) GetContainer(prefixOrName string) (*container.Container, error) {
 | 
						|
	if len(prefixOrName) == 0 {
 | 
						|
		return nil, errors.NewBadRequestError(fmt.Errorf("No container name or ID supplied"))
 | 
						|
	}
 | 
						|
 | 
						|
	if containerByID := daemon.containers.Get(prefixOrName); containerByID != nil {
 | 
						|
		// prefix is an exact match to a full container ID
 | 
						|
		return containerByID, nil
 | 
						|
	}
 | 
						|
 | 
						|
	// GetByName will match only an exact name provided; we ignore errors
 | 
						|
	if containerByName, _ := daemon.GetByName(prefixOrName); containerByName != nil {
 | 
						|
		// prefix is an exact match to a full container Name
 | 
						|
		return containerByName, nil
 | 
						|
	}
 | 
						|
 | 
						|
	containerID, indexError := daemon.idIndex.Get(prefixOrName)
 | 
						|
	if indexError != nil {
 | 
						|
		// When truncindex defines an error type, use that instead
 | 
						|
		if indexError == truncindex.ErrNotExist {
 | 
						|
			err := fmt.Errorf("No such container: %s", prefixOrName)
 | 
						|
			return nil, errors.NewRequestNotFoundError(err)
 | 
						|
		}
 | 
						|
		return nil, indexError
 | 
						|
	}
 | 
						|
	return daemon.containers.Get(containerID), nil
 | 
						|
}
 | 
						|
 | 
						|
// Exists returns a true if a container of the specified ID or name exists,
 | 
						|
// false otherwise.
 | 
						|
func (daemon *Daemon) Exists(id string) bool {
 | 
						|
	c, _ := daemon.GetContainer(id)
 | 
						|
	return c != nil
 | 
						|
}
 | 
						|
 | 
						|
// IsPaused returns a bool indicating if the specified container is paused.
 | 
						|
func (daemon *Daemon) IsPaused(id string) bool {
 | 
						|
	c, _ := daemon.GetContainer(id)
 | 
						|
	return c.State.IsPaused()
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) containerRoot(id string) string {
 | 
						|
	return filepath.Join(daemon.repository, id)
 | 
						|
}
 | 
						|
 | 
						|
// Load reads the contents of a container from disk
 | 
						|
// This is typically done at startup.
 | 
						|
func (daemon *Daemon) load(id string) (*container.Container, error) {
 | 
						|
	container := daemon.newBaseContainer(id)
 | 
						|
 | 
						|
	if err := container.FromDisk(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	if container.ID != id {
 | 
						|
		return container, fmt.Errorf("Container %s is stored at %s", container.ID, id)
 | 
						|
	}
 | 
						|
 | 
						|
	return container, nil
 | 
						|
}
 | 
						|
 | 
						|
// Register makes a container object usable by the daemon as <container.ID>
 | 
						|
func (daemon *Daemon) Register(c *container.Container) error {
 | 
						|
	// Attach to stdout and stderr
 | 
						|
	if c.Config.OpenStdin {
 | 
						|
		c.NewInputPipes()
 | 
						|
	} else {
 | 
						|
		c.NewNopInputPipe()
 | 
						|
	}
 | 
						|
 | 
						|
	daemon.containers.Add(c.ID, c)
 | 
						|
	daemon.idIndex.Add(c.ID)
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) newContainer(name string, config *containertypes.Config, imgID image.ID, managed bool) (*container.Container, error) {
 | 
						|
	var (
 | 
						|
		id             string
 | 
						|
		err            error
 | 
						|
		noExplicitName = name == ""
 | 
						|
	)
 | 
						|
	id, name, err = daemon.generateIDAndName(name)
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
 | 
						|
	daemon.generateHostname(id, config)
 | 
						|
	entrypoint, args := daemon.getEntrypointAndArgs(config.Entrypoint, config.Cmd)
 | 
						|
 | 
						|
	base := daemon.newBaseContainer(id)
 | 
						|
	base.Created = time.Now().UTC()
 | 
						|
	base.Managed = managed
 | 
						|
	base.Path = entrypoint
 | 
						|
	base.Args = args //FIXME: de-duplicate from config
 | 
						|
	base.Config = config
 | 
						|
	base.HostConfig = &containertypes.HostConfig{}
 | 
						|
	base.ImageID = imgID
 | 
						|
	base.NetworkSettings = &network.Settings{IsAnonymousEndpoint: noExplicitName}
 | 
						|
	base.Name = name
 | 
						|
	base.Driver = daemon.GraphDriverName()
 | 
						|
 | 
						|
	return base, err
 | 
						|
}
 | 
						|
 | 
						|
// GetByName returns a container given a name.
 | 
						|
func (daemon *Daemon) GetByName(name string) (*container.Container, error) {
 | 
						|
	if len(name) == 0 {
 | 
						|
		return nil, fmt.Errorf("No container name supplied")
 | 
						|
	}
 | 
						|
	fullName := name
 | 
						|
	if name[0] != '/' {
 | 
						|
		fullName = "/" + name
 | 
						|
	}
 | 
						|
	id, err := daemon.nameIndex.Get(fullName)
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("Could not find entity for %s", name)
 | 
						|
	}
 | 
						|
	e := daemon.containers.Get(id)
 | 
						|
	if e == nil {
 | 
						|
		return nil, fmt.Errorf("Could not find container for entity id %s", id)
 | 
						|
	}
 | 
						|
	return e, nil
 | 
						|
}
 | 
						|
 | 
						|
// newBaseContainer creates a new container with its initial
 | 
						|
// configuration based on the root storage from the daemon.
 | 
						|
func (daemon *Daemon) newBaseContainer(id string) *container.Container {
 | 
						|
	return container.NewBaseContainer(id, daemon.containerRoot(id))
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) getEntrypointAndArgs(configEntrypoint strslice.StrSlice, configCmd strslice.StrSlice) (string, []string) {
 | 
						|
	if len(configEntrypoint) != 0 {
 | 
						|
		return configEntrypoint[0], append(configEntrypoint[1:], configCmd...)
 | 
						|
	}
 | 
						|
	return configCmd[0], configCmd[1:]
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) generateHostname(id string, config *containertypes.Config) {
 | 
						|
	// Generate default hostname
 | 
						|
	if config.Hostname == "" {
 | 
						|
		config.Hostname = id[:12]
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) setSecurityOptions(container *container.Container, hostConfig *containertypes.HostConfig) error {
 | 
						|
	container.Lock()
 | 
						|
	defer container.Unlock()
 | 
						|
	return parseSecurityOpt(container, hostConfig)
 | 
						|
}
 | 
						|
 | 
						|
func (daemon *Daemon) setHostConfig(container *container.Container, hostConfig *containertypes.HostConfig) error {
 | 
						|
	// Do not lock while creating volumes since this could be calling out to external plugins
 | 
						|
	// Don't want to block other actions, like `docker ps` because we're waiting on an external plugin
 | 
						|
	if err := daemon.registerMountPoints(container, hostConfig); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	container.Lock()
 | 
						|
	defer container.Unlock()
 | 
						|
 | 
						|
	// Register any links from the host config before starting the container
 | 
						|
	if err := daemon.registerLinks(container, hostConfig); err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	// make sure links is not nil
 | 
						|
	// this ensures that on the next daemon restart we don't try to migrate from legacy sqlite links
 | 
						|
	if hostConfig.Links == nil {
 | 
						|
		hostConfig.Links = []string{}
 | 
						|
	}
 | 
						|
 | 
						|
	container.HostConfig = hostConfig
 | 
						|
	return container.ToDisk()
 | 
						|
}
 | 
						|
 | 
						|
// verifyContainerSettings performs validation of the hostconfig and config
 | 
						|
// structures.
 | 
						|
func (daemon *Daemon) verifyContainerSettings(hostConfig *containertypes.HostConfig, config *containertypes.Config, update bool, validateHostname bool) ([]string, error) {
 | 
						|
 | 
						|
	// First perform verification of settings common across all platforms.
 | 
						|
	if config != nil {
 | 
						|
		if config.WorkingDir != "" {
 | 
						|
			config.WorkingDir = filepath.FromSlash(config.WorkingDir) // Ensure in platform semantics
 | 
						|
			if !system.IsAbs(config.WorkingDir) {
 | 
						|
				return nil, fmt.Errorf("the working directory '%s' is invalid, it needs to be an absolute path", config.WorkingDir)
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		if len(config.StopSignal) > 0 {
 | 
						|
			_, err := signal.ParseSignal(config.StopSignal)
 | 
						|
			if err != nil {
 | 
						|
				return nil, err
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		// Validate if the given hostname is RFC 1123 (https://tools.ietf.org/html/rfc1123) compliant.
 | 
						|
		if validateHostname && len(config.Hostname) > 0 {
 | 
						|
			// RFC1123 specifies that 63 bytes is the maximium length
 | 
						|
			// Windows has the limitation of 63 bytes in length
 | 
						|
			// Linux hostname is limited to HOST_NAME_MAX=64, not including the terminating null byte.
 | 
						|
			// We limit the length to 63 bytes here to match RFC1035 and RFC1123.
 | 
						|
			matched, _ := regexp.MatchString("^(([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])\\.)*([[:alnum:]]|[[:alnum:]][[:alnum:]\\-]*[[:alnum:]])$", config.Hostname)
 | 
						|
			if len(config.Hostname) > 63 || !matched {
 | 
						|
				return nil, fmt.Errorf("invalid hostname format: %s", config.Hostname)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if hostConfig == nil {
 | 
						|
		return nil, nil
 | 
						|
	}
 | 
						|
 | 
						|
	if hostConfig.AutoRemove && !hostConfig.RestartPolicy.IsNone() {
 | 
						|
		return nil, fmt.Errorf("can't create 'AutoRemove' container with restart policy")
 | 
						|
	}
 | 
						|
 | 
						|
	for port := range hostConfig.PortBindings {
 | 
						|
		_, portStr := nat.SplitProtoPort(string(port))
 | 
						|
		if _, err := nat.ParsePort(portStr); err != nil {
 | 
						|
			return nil, fmt.Errorf("invalid port specification: %q", portStr)
 | 
						|
		}
 | 
						|
		for _, pb := range hostConfig.PortBindings[port] {
 | 
						|
			_, err := nat.NewPort(nat.SplitProtoPort(pb.HostPort))
 | 
						|
			if err != nil {
 | 
						|
				return nil, fmt.Errorf("invalid port specification: %q", pb.HostPort)
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	p := hostConfig.RestartPolicy
 | 
						|
 | 
						|
	switch p.Name {
 | 
						|
	case "always", "unless-stopped", "no":
 | 
						|
		if p.MaximumRetryCount != 0 {
 | 
						|
			return nil, fmt.Errorf("maximum restart count not valid with restart policy of '%s'", p.Name)
 | 
						|
		}
 | 
						|
	case "on-failure":
 | 
						|
		if p.MaximumRetryCount < 1 {
 | 
						|
			return nil, fmt.Errorf("maximum restart count must be a positive integer")
 | 
						|
		}
 | 
						|
	case "":
 | 
						|
	// do nothing
 | 
						|
	default:
 | 
						|
		return nil, fmt.Errorf("invalid restart policy '%s'", p.Name)
 | 
						|
	}
 | 
						|
 | 
						|
	// Now do platform-specific verification
 | 
						|
	return verifyPlatformContainerSettings(daemon, hostConfig, config, update)
 | 
						|
}
 |