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