github.com/vincentwoo/docker@v0.7.3-0.20160116130405-82401a4b13c0/container/container.go (about)

     1  package container
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"sync"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/Sirupsen/logrus"
    14  	"github.com/docker/docker/daemon/exec"
    15  	"github.com/docker/docker/daemon/execdriver"
    16  	"github.com/docker/docker/daemon/logger"
    17  	"github.com/docker/docker/daemon/logger/jsonfilelog"
    18  	"github.com/docker/docker/daemon/network"
    19  	derr "github.com/docker/docker/errors"
    20  	"github.com/docker/docker/image"
    21  	"github.com/docker/docker/layer"
    22  	"github.com/docker/docker/pkg/promise"
    23  	"github.com/docker/docker/pkg/signal"
    24  	"github.com/docker/docker/pkg/symlink"
    25  	"github.com/docker/docker/runconfig"
    26  	"github.com/docker/docker/volume"
    27  	containertypes "github.com/docker/engine-api/types/container"
    28  	"github.com/docker/go-connections/nat"
    29  	"github.com/opencontainers/runc/libcontainer/label"
    30  )
    31  
    32  const configFileName = "config.v2.json"
    33  
    34  // CommonContainer holds the fields for a container which are
    35  // applicable across all platforms supported by the daemon.
    36  type CommonContainer struct {
    37  	*runconfig.StreamConfig
    38  	// embed for Container to support states directly.
    39  	*State          `json:"State"` // Needed for remote api version <= 1.11
    40  	Root            string         `json:"-"` // Path to the "home" of the container, including metadata.
    41  	BaseFS          string         `json:"-"` // Path to the graphdriver mountpoint
    42  	RWLayer         layer.RWLayer  `json:"-"`
    43  	ID              string
    44  	Created         time.Time
    45  	Path            string
    46  	Args            []string
    47  	Config          *containertypes.Config
    48  	ImageID         image.ID `json:"Image"`
    49  	NetworkSettings *network.Settings
    50  	LogPath         string
    51  	Name            string
    52  	Driver          string
    53  	// MountLabel contains the options for the 'mount' command
    54  	MountLabel             string
    55  	ProcessLabel           string
    56  	RestartCount           int
    57  	HasBeenStartedBefore   bool
    58  	HasBeenManuallyStopped bool // used for unless-stopped restart policy
    59  	MountPoints            map[string]*volume.MountPoint
    60  	HostConfig             *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
    61  	Command                *execdriver.Command        `json:"-"`
    62  	monitor                *containerMonitor
    63  	ExecCommands           *exec.Store `json:"-"`
    64  	// logDriver for closing
    65  	LogDriver logger.Logger  `json:"-"`
    66  	LogCopier *logger.Copier `json:"-"`
    67  }
    68  
    69  // NewBaseContainer creates a new container with its
    70  // basic configuration.
    71  func NewBaseContainer(id, root string) *Container {
    72  	return &Container{
    73  		CommonContainer: CommonContainer{
    74  			ID:           id,
    75  			State:        NewState(),
    76  			ExecCommands: exec.NewStore(),
    77  			Root:         root,
    78  			MountPoints:  make(map[string]*volume.MountPoint),
    79  			StreamConfig: runconfig.NewStreamConfig(),
    80  		},
    81  	}
    82  }
    83  
    84  // FromDisk loads the container configuration stored in the host.
    85  func (container *Container) FromDisk() error {
    86  	pth, err := container.ConfigPath()
    87  	if err != nil {
    88  		return err
    89  	}
    90  
    91  	jsonSource, err := os.Open(pth)
    92  	if err != nil {
    93  		return err
    94  	}
    95  	defer jsonSource.Close()
    96  
    97  	dec := json.NewDecoder(jsonSource)
    98  
    99  	// Load container settings
   100  	if err := dec.Decode(container); err != nil {
   101  		return err
   102  	}
   103  
   104  	if err := label.ReserveLabel(container.ProcessLabel); err != nil {
   105  		return err
   106  	}
   107  	return container.readHostConfig()
   108  }
   109  
   110  // ToDisk saves the container configuration on disk.
   111  func (container *Container) ToDisk() error {
   112  	pth, err := container.ConfigPath()
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	jsonSource, err := os.Create(pth)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	defer jsonSource.Close()
   122  
   123  	enc := json.NewEncoder(jsonSource)
   124  
   125  	// Save container settings
   126  	if err := enc.Encode(container); err != nil {
   127  		return err
   128  	}
   129  
   130  	return container.WriteHostConfig()
   131  }
   132  
   133  // ToDiskLocking saves the container configuration on disk in a thread safe way.
   134  func (container *Container) ToDiskLocking() error {
   135  	container.Lock()
   136  	err := container.ToDisk()
   137  	container.Unlock()
   138  	return err
   139  }
   140  
   141  // readHostConfig reads the host configuration from disk for the container.
   142  func (container *Container) readHostConfig() error {
   143  	container.HostConfig = &containertypes.HostConfig{}
   144  	// If the hostconfig file does not exist, do not read it.
   145  	// (We still have to initialize container.HostConfig,
   146  	// but that's OK, since we just did that above.)
   147  	pth, err := container.HostConfigPath()
   148  	if err != nil {
   149  		return err
   150  	}
   151  
   152  	f, err := os.Open(pth)
   153  	if err != nil {
   154  		if os.IsNotExist(err) {
   155  			return nil
   156  		}
   157  		return err
   158  	}
   159  	defer f.Close()
   160  
   161  	if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil {
   162  		return err
   163  	}
   164  
   165  	container.InitDNSHostConfig()
   166  
   167  	return nil
   168  }
   169  
   170  // WriteHostConfig saves the host configuration on disk for the container.
   171  func (container *Container) WriteHostConfig() error {
   172  	pth, err := container.HostConfigPath()
   173  	if err != nil {
   174  		return err
   175  	}
   176  
   177  	f, err := os.Create(pth)
   178  	if err != nil {
   179  		return err
   180  	}
   181  	defer f.Close()
   182  
   183  	return json.NewEncoder(f).Encode(&container.HostConfig)
   184  }
   185  
   186  // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path
   187  // sanitisation. Symlinks are all scoped to the BaseFS of the container, as
   188  // though the container's BaseFS was `/`.
   189  //
   190  // The BaseFS of a container is the host-facing path which is bind-mounted as
   191  // `/` inside the container. This method is essentially used to access a
   192  // particular path inside the container as though you were a process in that
   193  // container.
   194  //
   195  // NOTE: The returned path is *only* safely scoped inside the container's BaseFS
   196  //       if no component of the returned path changes (such as a component
   197  //       symlinking to a different path) between using this method and using the
   198  //       path. See symlink.FollowSymlinkInScope for more details.
   199  func (container *Container) GetResourcePath(path string) (string, error) {
   200  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   201  	// any filepath operations must be done in an OS agnostic way.
   202  	cleanPath := filepath.Join(string(os.PathSeparator), path)
   203  	r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS)
   204  	return r, e
   205  }
   206  
   207  // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path
   208  // sanitisation. Symlinks are all scoped to the root of the container, as
   209  // though the container's root was `/`.
   210  //
   211  // The root of a container is the host-facing configuration metadata directory.
   212  // Only use this method to safely access the container's `container.json` or
   213  // other metadata files. If in doubt, use container.GetResourcePath.
   214  //
   215  // NOTE: The returned path is *only* safely scoped inside the container's root
   216  //       if no component of the returned path changes (such as a component
   217  //       symlinking to a different path) between using this method and using the
   218  //       path. See symlink.FollowSymlinkInScope for more details.
   219  func (container *Container) GetRootResourcePath(path string) (string, error) {
   220  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   221  	// any filepath operations must be done in an OS agnostic way.
   222  	cleanPath := filepath.Join(string(os.PathSeparator), path)
   223  	return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root)
   224  }
   225  
   226  // ExitOnNext signals to the monitor that it should not restart the container
   227  // after we send the kill signal.
   228  func (container *Container) ExitOnNext() {
   229  	container.monitor.ExitOnNext()
   230  }
   231  
   232  // Resize changes the TTY of the process running inside the container
   233  // to the given height and width. The container must be running.
   234  func (container *Container) Resize(h, w int) error {
   235  	if container.Command.ProcessConfig.Terminal == nil {
   236  		return fmt.Errorf("Container %s does not have a terminal ready", container.ID)
   237  	}
   238  	if err := container.Command.ProcessConfig.Terminal.Resize(h, w); err != nil {
   239  		return err
   240  	}
   241  	return nil
   242  }
   243  
   244  // HostConfigPath returns the path to the container's JSON hostconfig
   245  func (container *Container) HostConfigPath() (string, error) {
   246  	return container.GetRootResourcePath("hostconfig.json")
   247  }
   248  
   249  // ConfigPath returns the path to the container's JSON config
   250  func (container *Container) ConfigPath() (string, error) {
   251  	return container.GetRootResourcePath(configFileName)
   252  }
   253  
   254  func validateID(id string) error {
   255  	if id == "" {
   256  		return derr.ErrorCodeEmptyID
   257  	}
   258  	return nil
   259  }
   260  
   261  // Returns true if the container exposes a certain port
   262  func (container *Container) exposes(p nat.Port) bool {
   263  	_, exists := container.Config.ExposedPorts[p]
   264  	return exists
   265  }
   266  
   267  // GetLogConfig returns the log configuration for the container.
   268  func (container *Container) GetLogConfig(defaultConfig containertypes.LogConfig) containertypes.LogConfig {
   269  	cfg := container.HostConfig.LogConfig
   270  	if cfg.Type != "" || len(cfg.Config) > 0 { // container has log driver configured
   271  		if cfg.Type == "" {
   272  			cfg.Type = jsonfilelog.Name
   273  		}
   274  		return cfg
   275  	}
   276  	// Use daemon's default log config for containers
   277  	return defaultConfig
   278  }
   279  
   280  // StartLogger starts a new logger driver for the container.
   281  func (container *Container) StartLogger(cfg containertypes.LogConfig) (logger.Logger, error) {
   282  	c, err := logger.GetLogDriver(cfg.Type)
   283  	if err != nil {
   284  		return nil, derr.ErrorCodeLoggingFactory.WithArgs(err)
   285  	}
   286  	ctx := logger.Context{
   287  		Config:              cfg.Config,
   288  		ContainerID:         container.ID,
   289  		ContainerName:       container.Name,
   290  		ContainerEntrypoint: container.Path,
   291  		ContainerArgs:       container.Args,
   292  		ContainerImageID:    container.ImageID.String(),
   293  		ContainerImageName:  container.Config.Image,
   294  		ContainerCreated:    container.Created,
   295  		ContainerEnv:        container.Config.Env,
   296  		ContainerLabels:     container.Config.Labels,
   297  	}
   298  
   299  	// Set logging file for "json-logger"
   300  	if cfg.Type == jsonfilelog.Name {
   301  		ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
   302  		if err != nil {
   303  			return nil, err
   304  		}
   305  	}
   306  	return c(ctx)
   307  }
   308  
   309  // GetProcessLabel returns the process label for the container.
   310  func (container *Container) GetProcessLabel() string {
   311  	// even if we have a process label return "" if we are running
   312  	// in privileged mode
   313  	if container.HostConfig.Privileged {
   314  		return ""
   315  	}
   316  	return container.ProcessLabel
   317  }
   318  
   319  // GetMountLabel returns the mounting label for the container.
   320  // This label is empty if the container is privileged.
   321  func (container *Container) GetMountLabel() string {
   322  	if container.HostConfig.Privileged {
   323  		return ""
   324  	}
   325  	return container.MountLabel
   326  }
   327  
   328  // GetExecIDs returns the list of exec commands running on the container.
   329  func (container *Container) GetExecIDs() []string {
   330  	return container.ExecCommands.List()
   331  }
   332  
   333  // Attach connects to the container's TTY, delegating to standard
   334  // streams or websockets depending on the configuration.
   335  func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
   336  	return AttachStreams(container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
   337  }
   338  
   339  // AttachStreams connects streams to a TTY.
   340  // Used by exec too. Should this move somewhere else?
   341  func AttachStreams(streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
   342  	var (
   343  		cStdout, cStderr io.ReadCloser
   344  		cStdin           io.WriteCloser
   345  		wg               sync.WaitGroup
   346  		errors           = make(chan error, 3)
   347  	)
   348  
   349  	if stdin != nil && openStdin {
   350  		cStdin = streamConfig.StdinPipe()
   351  		wg.Add(1)
   352  	}
   353  
   354  	if stdout != nil {
   355  		cStdout = streamConfig.StdoutPipe()
   356  		wg.Add(1)
   357  	}
   358  
   359  	if stderr != nil {
   360  		cStderr = streamConfig.StderrPipe()
   361  		wg.Add(1)
   362  	}
   363  
   364  	// Connect stdin of container to the http conn.
   365  	go func() {
   366  		if stdin == nil || !openStdin {
   367  			return
   368  		}
   369  		logrus.Debugf("attach: stdin: begin")
   370  		defer func() {
   371  			if stdinOnce && !tty {
   372  				cStdin.Close()
   373  			} else {
   374  				// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
   375  				if cStdout != nil {
   376  					cStdout.Close()
   377  				}
   378  				if cStderr != nil {
   379  					cStderr.Close()
   380  				}
   381  			}
   382  			wg.Done()
   383  			logrus.Debugf("attach: stdin: end")
   384  		}()
   385  
   386  		var err error
   387  		if tty {
   388  			_, err = copyEscapable(cStdin, stdin, keys)
   389  		} else {
   390  			_, err = io.Copy(cStdin, stdin)
   391  
   392  		}
   393  		if err == io.ErrClosedPipe {
   394  			err = nil
   395  		}
   396  		if err != nil {
   397  			logrus.Errorf("attach: stdin: %s", err)
   398  			errors <- err
   399  			return
   400  		}
   401  	}()
   402  
   403  	attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
   404  		if stream == nil {
   405  			return
   406  		}
   407  		defer func() {
   408  			// Make sure stdin gets closed
   409  			if stdin != nil {
   410  				stdin.Close()
   411  			}
   412  			streamPipe.Close()
   413  			wg.Done()
   414  			logrus.Debugf("attach: %s: end", name)
   415  		}()
   416  
   417  		logrus.Debugf("attach: %s: begin", name)
   418  		_, err := io.Copy(stream, streamPipe)
   419  		if err == io.ErrClosedPipe {
   420  			err = nil
   421  		}
   422  		if err != nil {
   423  			logrus.Errorf("attach: %s: %v", name, err)
   424  			errors <- err
   425  		}
   426  	}
   427  
   428  	go attachStream("stdout", stdout, cStdout)
   429  	go attachStream("stderr", stderr, cStderr)
   430  
   431  	return promise.Go(func() error {
   432  		wg.Wait()
   433  		close(errors)
   434  		for err := range errors {
   435  			if err != nil {
   436  				return err
   437  			}
   438  		}
   439  		return nil
   440  	})
   441  }
   442  
   443  // Code c/c from io.Copy() modified to handle escape sequence
   444  func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
   445  	if len(keys) == 0 {
   446  		// Default keys : ctrl-p ctrl-q
   447  		keys = []byte{16, 17}
   448  	}
   449  	buf := make([]byte, 32*1024)
   450  	for {
   451  		nr, er := src.Read(buf)
   452  		if nr > 0 {
   453  			// ---- Docker addition
   454  			for i, key := range keys {
   455  				if nr != 1 || buf[0] != key {
   456  					break
   457  				}
   458  				if i == len(keys)-1 {
   459  					if err := src.Close(); err != nil {
   460  						return 0, err
   461  					}
   462  					return 0, nil
   463  				}
   464  				nr, er = src.Read(buf)
   465  			}
   466  			// ---- End of docker
   467  			nw, ew := dst.Write(buf[0:nr])
   468  			if nw > 0 {
   469  				written += int64(nw)
   470  			}
   471  			if ew != nil {
   472  				err = ew
   473  				break
   474  			}
   475  			if nr != nw {
   476  				err = io.ErrShortWrite
   477  				break
   478  			}
   479  		}
   480  		if er == io.EOF {
   481  			break
   482  		}
   483  		if er != nil {
   484  			err = er
   485  			break
   486  		}
   487  	}
   488  	return written, err
   489  }
   490  
   491  // ShouldRestart decides whether the daemon should restart the container or not.
   492  // This is based on the container's restart policy.
   493  func (container *Container) ShouldRestart() bool {
   494  	return container.HostConfig.RestartPolicy.Name == "always" ||
   495  		(container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
   496  		(container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
   497  }
   498  
   499  // AddBindMountPoint adds a new bind mount point configuration to the container.
   500  func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) {
   501  	container.MountPoints[destination] = &volume.MountPoint{
   502  		Name:        name,
   503  		Source:      source,
   504  		Destination: destination,
   505  		RW:          rw,
   506  	}
   507  }
   508  
   509  // AddLocalMountPoint adds a new local mount point configuration to the container.
   510  func (container *Container) AddLocalMountPoint(name, destination string, rw bool) {
   511  	container.MountPoints[destination] = &volume.MountPoint{
   512  		Name:        name,
   513  		Driver:      volume.DefaultDriverName,
   514  		Destination: destination,
   515  		RW:          rw,
   516  	}
   517  }
   518  
   519  // AddMountPointWithVolume adds a new mount point configured with a volume to the container.
   520  func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
   521  	container.MountPoints[destination] = &volume.MountPoint{
   522  		Name:        vol.Name(),
   523  		Driver:      vol.DriverName(),
   524  		Destination: destination,
   525  		RW:          rw,
   526  		Volume:      vol,
   527  	}
   528  }
   529  
   530  // IsDestinationMounted checks whether a path is mounted on the container or not.
   531  func (container *Container) IsDestinationMounted(destination string) bool {
   532  	return container.MountPoints[destination] != nil
   533  }
   534  
   535  // StopSignal returns the signal used to stop the container.
   536  func (container *Container) StopSignal() int {
   537  	var stopSignal syscall.Signal
   538  	if container.Config.StopSignal != "" {
   539  		stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
   540  	}
   541  
   542  	if int(stopSignal) == 0 {
   543  		stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal)
   544  	}
   545  	return int(stopSignal)
   546  }
   547  
   548  // InitDNSHostConfig ensures that the dns fields are never nil.
   549  // New containers don't ever have those fields nil,
   550  // but pre created containers can still have those nil values.
   551  // The non-recommended host configuration in the start api can
   552  // make these fields nil again, this corrects that issue until
   553  // we remove that behavior for good.
   554  // See https://github.com/docker/docker/pull/17779
   555  // for a more detailed explanation on why we don't want that.
   556  func (container *Container) InitDNSHostConfig() {
   557  	container.Lock()
   558  	defer container.Unlock()
   559  	if container.HostConfig.DNS == nil {
   560  		container.HostConfig.DNS = make([]string, 0)
   561  	}
   562  
   563  	if container.HostConfig.DNSSearch == nil {
   564  		container.HostConfig.DNSSearch = make([]string, 0)
   565  	}
   566  
   567  	if container.HostConfig.DNSOptions == nil {
   568  		container.HostConfig.DNSOptions = make([]string, 0)
   569  	}
   570  }