github.com/kinvolk/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  }