github.com/akerouanton/docker@v1.11.0-rc3/container/container.go (about)

     1  package container
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"os"
     9  	"path/filepath"
    10  	"strconv"
    11  	"strings"
    12  	"sync"
    13  	"syscall"
    14  	"time"
    15  
    16  	"golang.org/x/net/context"
    17  
    18  	"github.com/Sirupsen/logrus"
    19  	"github.com/docker/docker/daemon/exec"
    20  	"github.com/docker/docker/daemon/logger"
    21  	"github.com/docker/docker/daemon/logger/jsonfilelog"
    22  	"github.com/docker/docker/daemon/network"
    23  	"github.com/docker/docker/image"
    24  	"github.com/docker/docker/layer"
    25  	"github.com/docker/docker/pkg/idtools"
    26  	"github.com/docker/docker/pkg/promise"
    27  	"github.com/docker/docker/pkg/signal"
    28  	"github.com/docker/docker/pkg/symlink"
    29  	"github.com/docker/docker/restartmanager"
    30  	"github.com/docker/docker/runconfig"
    31  	runconfigopts "github.com/docker/docker/runconfig/opts"
    32  	"github.com/docker/docker/volume"
    33  	containertypes "github.com/docker/engine-api/types/container"
    34  	networktypes "github.com/docker/engine-api/types/network"
    35  	"github.com/docker/go-connections/nat"
    36  	"github.com/docker/libnetwork"
    37  	"github.com/docker/libnetwork/netlabel"
    38  	"github.com/docker/libnetwork/options"
    39  	"github.com/docker/libnetwork/types"
    40  	"github.com/opencontainers/runc/libcontainer/label"
    41  )
    42  
    43  const configFileName = "config.v2.json"
    44  
    45  var (
    46  	errInvalidEndpoint = fmt.Errorf("invalid endpoint while building port map info")
    47  	errInvalidNetwork  = fmt.Errorf("invalid network settings while building port map info")
    48  )
    49  
    50  // CommonContainer holds the fields for a container which are
    51  // applicable across all platforms supported by the daemon.
    52  type CommonContainer struct {
    53  	*runconfig.StreamConfig
    54  	// embed for Container to support states directly.
    55  	*State          `json:"State"` // Needed for remote api version <= 1.11
    56  	Root            string         `json:"-"` // Path to the "home" of the container, including metadata.
    57  	BaseFS          string         `json:"-"` // Path to the graphdriver mountpoint
    58  	RWLayer         layer.RWLayer  `json:"-"`
    59  	ID              string
    60  	Created         time.Time
    61  	Path            string
    62  	Args            []string
    63  	Config          *containertypes.Config
    64  	ImageID         image.ID `json:"Image"`
    65  	NetworkSettings *network.Settings
    66  	LogPath         string
    67  	Name            string
    68  	Driver          string
    69  	// MountLabel contains the options for the 'mount' command
    70  	MountLabel             string
    71  	ProcessLabel           string
    72  	RestartCount           int
    73  	HasBeenStartedBefore   bool
    74  	HasBeenManuallyStopped bool // used for unless-stopped restart policy
    75  	MountPoints            map[string]*volume.MountPoint
    76  	HostConfig             *containertypes.HostConfig `json:"-"` // do not serialize the host config in the json, otherwise we'll make the container unportable
    77  	ExecCommands           *exec.Store                `json:"-"`
    78  	// logDriver for closing
    79  	LogDriver      logger.Logger  `json:"-"`
    80  	LogCopier      *logger.Copier `json:"-"`
    81  	restartManager restartmanager.RestartManager
    82  	attachContext  *attachContext
    83  }
    84  
    85  // NewBaseContainer creates a new container with its
    86  // basic configuration.
    87  func NewBaseContainer(id, root string) *Container {
    88  	return &Container{
    89  		CommonContainer: CommonContainer{
    90  			ID:            id,
    91  			State:         NewState(),
    92  			ExecCommands:  exec.NewStore(),
    93  			Root:          root,
    94  			MountPoints:   make(map[string]*volume.MountPoint),
    95  			StreamConfig:  runconfig.NewStreamConfig(),
    96  			attachContext: &attachContext{},
    97  		},
    98  	}
    99  }
   100  
   101  // FromDisk loads the container configuration stored in the host.
   102  func (container *Container) FromDisk() error {
   103  	pth, err := container.ConfigPath()
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	jsonSource, err := os.Open(pth)
   109  	if err != nil {
   110  		return err
   111  	}
   112  	defer jsonSource.Close()
   113  
   114  	dec := json.NewDecoder(jsonSource)
   115  
   116  	// Load container settings
   117  	if err := dec.Decode(container); err != nil {
   118  		return err
   119  	}
   120  
   121  	if err := label.ReserveLabel(container.ProcessLabel); err != nil {
   122  		return err
   123  	}
   124  	return container.readHostConfig()
   125  }
   126  
   127  // ToDisk saves the container configuration on disk.
   128  func (container *Container) ToDisk() error {
   129  	pth, err := container.ConfigPath()
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	jsonSource, err := os.Create(pth)
   135  	if err != nil {
   136  		return err
   137  	}
   138  	defer jsonSource.Close()
   139  
   140  	enc := json.NewEncoder(jsonSource)
   141  
   142  	// Save container settings
   143  	if err := enc.Encode(container); err != nil {
   144  		return err
   145  	}
   146  
   147  	return container.WriteHostConfig()
   148  }
   149  
   150  // ToDiskLocking saves the container configuration on disk in a thread safe way.
   151  func (container *Container) ToDiskLocking() error {
   152  	container.Lock()
   153  	err := container.ToDisk()
   154  	container.Unlock()
   155  	return err
   156  }
   157  
   158  // readHostConfig reads the host configuration from disk for the container.
   159  func (container *Container) readHostConfig() error {
   160  	container.HostConfig = &containertypes.HostConfig{}
   161  	// If the hostconfig file does not exist, do not read it.
   162  	// (We still have to initialize container.HostConfig,
   163  	// but that's OK, since we just did that above.)
   164  	pth, err := container.HostConfigPath()
   165  	if err != nil {
   166  		return err
   167  	}
   168  
   169  	f, err := os.Open(pth)
   170  	if err != nil {
   171  		if os.IsNotExist(err) {
   172  			return nil
   173  		}
   174  		return err
   175  	}
   176  	defer f.Close()
   177  
   178  	if err := json.NewDecoder(f).Decode(&container.HostConfig); err != nil {
   179  		return err
   180  	}
   181  
   182  	container.InitDNSHostConfig()
   183  
   184  	return nil
   185  }
   186  
   187  // WriteHostConfig saves the host configuration on disk for the container.
   188  func (container *Container) WriteHostConfig() error {
   189  	pth, err := container.HostConfigPath()
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	f, err := os.Create(pth)
   195  	if err != nil {
   196  		return err
   197  	}
   198  	defer f.Close()
   199  
   200  	return json.NewEncoder(f).Encode(&container.HostConfig)
   201  }
   202  
   203  // SetupWorkingDirectory sets up the container's working directory as set in container.Config.WorkingDir
   204  func (container *Container) SetupWorkingDirectory(rootUID, rootGID int) error {
   205  	if container.Config.WorkingDir == "" {
   206  		return nil
   207  	}
   208  
   209  	// If can't mount container FS at this point (eg Hyper-V Containers on
   210  	// Windows) bail out now with no action.
   211  	if !container.canMountFS() {
   212  		return nil
   213  	}
   214  
   215  	container.Config.WorkingDir = filepath.Clean(container.Config.WorkingDir)
   216  
   217  	pth, err := container.GetResourcePath(container.Config.WorkingDir)
   218  	if err != nil {
   219  		return err
   220  	}
   221  
   222  	if err := idtools.MkdirAllNewAs(pth, 0755, rootUID, rootGID); err != nil {
   223  		pthInfo, err2 := os.Stat(pth)
   224  		if err2 == nil && pthInfo != nil && !pthInfo.IsDir() {
   225  			return fmt.Errorf("Cannot mkdir: %s is not a directory", container.Config.WorkingDir)
   226  		}
   227  
   228  		return err
   229  	}
   230  
   231  	return nil
   232  }
   233  
   234  // GetResourcePath evaluates `path` in the scope of the container's BaseFS, with proper path
   235  // sanitisation. Symlinks are all scoped to the BaseFS of the container, as
   236  // though the container's BaseFS was `/`.
   237  //
   238  // The BaseFS of a container is the host-facing path which is bind-mounted as
   239  // `/` inside the container. This method is essentially used to access a
   240  // particular path inside the container as though you were a process in that
   241  // container.
   242  //
   243  // NOTE: The returned path is *only* safely scoped inside the container's BaseFS
   244  //       if no component of the returned path changes (such as a component
   245  //       symlinking to a different path) between using this method and using the
   246  //       path. See symlink.FollowSymlinkInScope for more details.
   247  func (container *Container) GetResourcePath(path string) (string, error) {
   248  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   249  	// any filepath operations must be done in an OS agnostic way.
   250  
   251  	cleanPath := cleanResourcePath(path)
   252  	r, e := symlink.FollowSymlinkInScope(filepath.Join(container.BaseFS, cleanPath), container.BaseFS)
   253  	return r, e
   254  }
   255  
   256  // GetRootResourcePath evaluates `path` in the scope of the container's root, with proper path
   257  // sanitisation. Symlinks are all scoped to the root of the container, as
   258  // though the container's root was `/`.
   259  //
   260  // The root of a container is the host-facing configuration metadata directory.
   261  // Only use this method to safely access the container's `container.json` or
   262  // other metadata files. If in doubt, use container.GetResourcePath.
   263  //
   264  // NOTE: The returned path is *only* safely scoped inside the container's root
   265  //       if no component of the returned path changes (such as a component
   266  //       symlinking to a different path) between using this method and using the
   267  //       path. See symlink.FollowSymlinkInScope for more details.
   268  func (container *Container) GetRootResourcePath(path string) (string, error) {
   269  	// IMPORTANT - These are paths on the OS where the daemon is running, hence
   270  	// any filepath operations must be done in an OS agnostic way.
   271  	cleanPath := filepath.Join(string(os.PathSeparator), path)
   272  	return symlink.FollowSymlinkInScope(filepath.Join(container.Root, cleanPath), container.Root)
   273  }
   274  
   275  // ExitOnNext signals to the monitor that it should not restart the container
   276  // after we send the kill signal.
   277  func (container *Container) ExitOnNext() {
   278  	if container.restartManager != nil {
   279  		container.restartManager.Cancel()
   280  	}
   281  }
   282  
   283  // HostConfigPath returns the path to the container's JSON hostconfig
   284  func (container *Container) HostConfigPath() (string, error) {
   285  	return container.GetRootResourcePath("hostconfig.json")
   286  }
   287  
   288  // ConfigPath returns the path to the container's JSON config
   289  func (container *Container) ConfigPath() (string, error) {
   290  	return container.GetRootResourcePath(configFileName)
   291  }
   292  
   293  // StartLogger starts a new logger driver for the container.
   294  func (container *Container) StartLogger(cfg containertypes.LogConfig) (logger.Logger, error) {
   295  	c, err := logger.GetLogDriver(cfg.Type)
   296  	if err != nil {
   297  		return nil, fmt.Errorf("Failed to get logging factory: %v", err)
   298  	}
   299  	ctx := logger.Context{
   300  		Config:              cfg.Config,
   301  		ContainerID:         container.ID,
   302  		ContainerName:       container.Name,
   303  		ContainerEntrypoint: container.Path,
   304  		ContainerArgs:       container.Args,
   305  		ContainerImageID:    container.ImageID.String(),
   306  		ContainerImageName:  container.Config.Image,
   307  		ContainerCreated:    container.Created,
   308  		ContainerEnv:        container.Config.Env,
   309  		ContainerLabels:     container.Config.Labels,
   310  	}
   311  
   312  	// Set logging file for "json-logger"
   313  	if cfg.Type == jsonfilelog.Name {
   314  		ctx.LogPath, err = container.GetRootResourcePath(fmt.Sprintf("%s-json.log", container.ID))
   315  		if err != nil {
   316  			return nil, err
   317  		}
   318  	}
   319  	return c(ctx)
   320  }
   321  
   322  // GetProcessLabel returns the process label for the container.
   323  func (container *Container) GetProcessLabel() string {
   324  	// even if we have a process label return "" if we are running
   325  	// in privileged mode
   326  	if container.HostConfig.Privileged {
   327  		return ""
   328  	}
   329  	return container.ProcessLabel
   330  }
   331  
   332  // GetMountLabel returns the mounting label for the container.
   333  // This label is empty if the container is privileged.
   334  func (container *Container) GetMountLabel() string {
   335  	if container.HostConfig.Privileged {
   336  		return ""
   337  	}
   338  	return container.MountLabel
   339  }
   340  
   341  // GetExecIDs returns the list of exec commands running on the container.
   342  func (container *Container) GetExecIDs() []string {
   343  	return container.ExecCommands.List()
   344  }
   345  
   346  // Attach connects to the container's TTY, delegating to standard
   347  // streams or websockets depending on the configuration.
   348  func (container *Container) Attach(stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
   349  	ctx := container.InitAttachContext()
   350  	return AttachStreams(ctx, container.StreamConfig, container.Config.OpenStdin, container.Config.StdinOnce, container.Config.Tty, stdin, stdout, stderr, keys)
   351  }
   352  
   353  // AttachStreams connects streams to a TTY.
   354  // Used by exec too. Should this move somewhere else?
   355  func AttachStreams(ctx context.Context, streamConfig *runconfig.StreamConfig, openStdin, stdinOnce, tty bool, stdin io.ReadCloser, stdout io.Writer, stderr io.Writer, keys []byte) chan error {
   356  	var (
   357  		cStdout, cStderr io.ReadCloser
   358  		cStdin           io.WriteCloser
   359  		wg               sync.WaitGroup
   360  		errors           = make(chan error, 3)
   361  	)
   362  
   363  	if stdin != nil && openStdin {
   364  		cStdin = streamConfig.StdinPipe()
   365  		wg.Add(1)
   366  	}
   367  
   368  	if stdout != nil {
   369  		cStdout = streamConfig.StdoutPipe()
   370  		wg.Add(1)
   371  	}
   372  
   373  	if stderr != nil {
   374  		cStderr = streamConfig.StderrPipe()
   375  		wg.Add(1)
   376  	}
   377  
   378  	// Connect stdin of container to the http conn.
   379  	go func() {
   380  		if stdin == nil || !openStdin {
   381  			return
   382  		}
   383  		logrus.Debugf("attach: stdin: begin")
   384  
   385  		var err error
   386  		if tty {
   387  			_, err = copyEscapable(cStdin, stdin, keys)
   388  		} else {
   389  			_, err = io.Copy(cStdin, stdin)
   390  
   391  		}
   392  		if err == io.ErrClosedPipe {
   393  			err = nil
   394  		}
   395  		if err != nil {
   396  			logrus.Errorf("attach: stdin: %s", err)
   397  			errors <- err
   398  		}
   399  		if stdinOnce && !tty {
   400  			cStdin.Close()
   401  		} else {
   402  			// No matter what, when stdin is closed (io.Copy unblock), close stdout and stderr
   403  			if cStdout != nil {
   404  				cStdout.Close()
   405  			}
   406  			if cStderr != nil {
   407  				cStderr.Close()
   408  			}
   409  		}
   410  		logrus.Debugf("attach: stdin: end")
   411  		wg.Done()
   412  	}()
   413  
   414  	attachStream := func(name string, stream io.Writer, streamPipe io.ReadCloser) {
   415  		if stream == nil {
   416  			return
   417  		}
   418  
   419  		logrus.Debugf("attach: %s: begin", name)
   420  		_, err := io.Copy(stream, streamPipe)
   421  		if err == io.ErrClosedPipe {
   422  			err = nil
   423  		}
   424  		if err != nil {
   425  			logrus.Errorf("attach: %s: %v", name, err)
   426  			errors <- err
   427  		}
   428  		// Make sure stdin gets closed
   429  		if stdin != nil {
   430  			stdin.Close()
   431  		}
   432  		streamPipe.Close()
   433  		logrus.Debugf("attach: %s: end", name)
   434  		wg.Done()
   435  	}
   436  
   437  	go attachStream("stdout", stdout, cStdout)
   438  	go attachStream("stderr", stderr, cStderr)
   439  
   440  	return promise.Go(func() error {
   441  		done := make(chan struct{})
   442  		go func() {
   443  			wg.Wait()
   444  			close(done)
   445  		}()
   446  		select {
   447  		case <-done:
   448  		case <-ctx.Done():
   449  			// close all pipes
   450  			if cStdin != nil {
   451  				cStdin.Close()
   452  			}
   453  			if cStdout != nil {
   454  				cStdout.Close()
   455  			}
   456  			if cStderr != nil {
   457  				cStderr.Close()
   458  			}
   459  			<-done
   460  		}
   461  		close(errors)
   462  		for err := range errors {
   463  			if err != nil {
   464  				return err
   465  			}
   466  		}
   467  		return nil
   468  	})
   469  }
   470  
   471  // Code c/c from io.Copy() modified to handle escape sequence
   472  func copyEscapable(dst io.Writer, src io.ReadCloser, keys []byte) (written int64, err error) {
   473  	if len(keys) == 0 {
   474  		// Default keys : ctrl-p ctrl-q
   475  		keys = []byte{16, 17}
   476  	}
   477  	buf := make([]byte, 32*1024)
   478  	for {
   479  		nr, er := src.Read(buf)
   480  		if nr > 0 {
   481  			// ---- Docker addition
   482  			for i, key := range keys {
   483  				if nr != 1 || buf[0] != key {
   484  					break
   485  				}
   486  				if i == len(keys)-1 {
   487  					if err := src.Close(); err != nil {
   488  						return 0, err
   489  					}
   490  					return 0, nil
   491  				}
   492  				nr, er = src.Read(buf)
   493  			}
   494  			// ---- End of docker
   495  			nw, ew := dst.Write(buf[0:nr])
   496  			if nw > 0 {
   497  				written += int64(nw)
   498  			}
   499  			if ew != nil {
   500  				err = ew
   501  				break
   502  			}
   503  			if nr != nw {
   504  				err = io.ErrShortWrite
   505  				break
   506  			}
   507  		}
   508  		if er == io.EOF {
   509  			break
   510  		}
   511  		if er != nil {
   512  			err = er
   513  			break
   514  		}
   515  	}
   516  	return written, err
   517  }
   518  
   519  // ShouldRestart decides whether the daemon should restart the container or not.
   520  // This is based on the container's restart policy.
   521  func (container *Container) ShouldRestart() bool {
   522  	return container.HostConfig.RestartPolicy.Name == "always" ||
   523  		(container.HostConfig.RestartPolicy.Name == "unless-stopped" && !container.HasBeenManuallyStopped) ||
   524  		(container.HostConfig.RestartPolicy.Name == "on-failure" && container.ExitCode != 0)
   525  }
   526  
   527  // AddBindMountPoint adds a new bind mount point configuration to the container.
   528  func (container *Container) AddBindMountPoint(name, source, destination string, rw bool) {
   529  	container.MountPoints[destination] = &volume.MountPoint{
   530  		Name:        name,
   531  		Source:      source,
   532  		Destination: destination,
   533  		RW:          rw,
   534  	}
   535  }
   536  
   537  // AddLocalMountPoint adds a new local mount point configuration to the container.
   538  func (container *Container) AddLocalMountPoint(name, destination string, rw bool) {
   539  	container.MountPoints[destination] = &volume.MountPoint{
   540  		Name:        name,
   541  		Driver:      volume.DefaultDriverName,
   542  		Destination: destination,
   543  		RW:          rw,
   544  	}
   545  }
   546  
   547  // AddMountPointWithVolume adds a new mount point configured with a volume to the container.
   548  func (container *Container) AddMountPointWithVolume(destination string, vol volume.Volume, rw bool) {
   549  	container.MountPoints[destination] = &volume.MountPoint{
   550  		Name:        vol.Name(),
   551  		Driver:      vol.DriverName(),
   552  		Destination: destination,
   553  		RW:          rw,
   554  		Volume:      vol,
   555  		CopyData:    volume.DefaultCopyMode,
   556  	}
   557  }
   558  
   559  // IsDestinationMounted checks whether a path is mounted on the container or not.
   560  func (container *Container) IsDestinationMounted(destination string) bool {
   561  	return container.MountPoints[destination] != nil
   562  }
   563  
   564  // StopSignal returns the signal used to stop the container.
   565  func (container *Container) StopSignal() int {
   566  	var stopSignal syscall.Signal
   567  	if container.Config.StopSignal != "" {
   568  		stopSignal, _ = signal.ParseSignal(container.Config.StopSignal)
   569  	}
   570  
   571  	if int(stopSignal) == 0 {
   572  		stopSignal, _ = signal.ParseSignal(signal.DefaultStopSignal)
   573  	}
   574  	return int(stopSignal)
   575  }
   576  
   577  // InitDNSHostConfig ensures that the dns fields are never nil.
   578  // New containers don't ever have those fields nil,
   579  // but pre created containers can still have those nil values.
   580  // The non-recommended host configuration in the start api can
   581  // make these fields nil again, this corrects that issue until
   582  // we remove that behavior for good.
   583  // See https://github.com/docker/docker/pull/17779
   584  // for a more detailed explanation on why we don't want that.
   585  func (container *Container) InitDNSHostConfig() {
   586  	container.Lock()
   587  	defer container.Unlock()
   588  	if container.HostConfig.DNS == nil {
   589  		container.HostConfig.DNS = make([]string, 0)
   590  	}
   591  
   592  	if container.HostConfig.DNSSearch == nil {
   593  		container.HostConfig.DNSSearch = make([]string, 0)
   594  	}
   595  
   596  	if container.HostConfig.DNSOptions == nil {
   597  		container.HostConfig.DNSOptions = make([]string, 0)
   598  	}
   599  }
   600  
   601  // GetEndpointInNetwork returns the container's endpoint to the provided network.
   602  func (container *Container) GetEndpointInNetwork(n libnetwork.Network) (libnetwork.Endpoint, error) {
   603  	endpointName := strings.TrimPrefix(container.Name, "/")
   604  	return n.EndpointByName(endpointName)
   605  }
   606  
   607  func (container *Container) buildPortMapInfo(ep libnetwork.Endpoint) error {
   608  	if ep == nil {
   609  		return errInvalidEndpoint
   610  	}
   611  
   612  	networkSettings := container.NetworkSettings
   613  	if networkSettings == nil {
   614  		return errInvalidNetwork
   615  	}
   616  
   617  	if len(networkSettings.Ports) == 0 {
   618  		pm, err := getEndpointPortMapInfo(ep)
   619  		if err != nil {
   620  			return err
   621  		}
   622  		networkSettings.Ports = pm
   623  	}
   624  	return nil
   625  }
   626  
   627  func getEndpointPortMapInfo(ep libnetwork.Endpoint) (nat.PortMap, error) {
   628  	pm := nat.PortMap{}
   629  	driverInfo, err := ep.DriverInfo()
   630  	if err != nil {
   631  		return pm, err
   632  	}
   633  
   634  	if driverInfo == nil {
   635  		// It is not an error for epInfo to be nil
   636  		return pm, nil
   637  	}
   638  
   639  	if expData, ok := driverInfo[netlabel.ExposedPorts]; ok {
   640  		if exposedPorts, ok := expData.([]types.TransportPort); ok {
   641  			for _, tp := range exposedPorts {
   642  				natPort, err := nat.NewPort(tp.Proto.String(), strconv.Itoa(int(tp.Port)))
   643  				if err != nil {
   644  					return pm, fmt.Errorf("Error parsing Port value(%v):%v", tp.Port, err)
   645  				}
   646  				pm[natPort] = nil
   647  			}
   648  		}
   649  	}
   650  
   651  	mapData, ok := driverInfo[netlabel.PortMap]
   652  	if !ok {
   653  		return pm, nil
   654  	}
   655  
   656  	if portMapping, ok := mapData.([]types.PortBinding); ok {
   657  		for _, pp := range portMapping {
   658  			natPort, err := nat.NewPort(pp.Proto.String(), strconv.Itoa(int(pp.Port)))
   659  			if err != nil {
   660  				return pm, err
   661  			}
   662  			natBndg := nat.PortBinding{HostIP: pp.HostIP.String(), HostPort: strconv.Itoa(int(pp.HostPort))}
   663  			pm[natPort] = append(pm[natPort], natBndg)
   664  		}
   665  	}
   666  
   667  	return pm, nil
   668  }
   669  
   670  func getSandboxPortMapInfo(sb libnetwork.Sandbox) nat.PortMap {
   671  	pm := nat.PortMap{}
   672  	if sb == nil {
   673  		return pm
   674  	}
   675  
   676  	for _, ep := range sb.Endpoints() {
   677  		pm, _ = getEndpointPortMapInfo(ep)
   678  		if len(pm) > 0 {
   679  			break
   680  		}
   681  	}
   682  	return pm
   683  }
   684  
   685  // BuildEndpointInfo sets endpoint-related fields on container.NetworkSettings based on the provided network and endpoint.
   686  func (container *Container) BuildEndpointInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
   687  	if ep == nil {
   688  		return errInvalidEndpoint
   689  	}
   690  
   691  	networkSettings := container.NetworkSettings
   692  	if networkSettings == nil {
   693  		return errInvalidNetwork
   694  	}
   695  
   696  	epInfo := ep.Info()
   697  	if epInfo == nil {
   698  		// It is not an error to get an empty endpoint info
   699  		return nil
   700  	}
   701  
   702  	if _, ok := networkSettings.Networks[n.Name()]; !ok {
   703  		networkSettings.Networks[n.Name()] = new(networktypes.EndpointSettings)
   704  	}
   705  	networkSettings.Networks[n.Name()].NetworkID = n.ID()
   706  	networkSettings.Networks[n.Name()].EndpointID = ep.ID()
   707  
   708  	iface := epInfo.Iface()
   709  	if iface == nil {
   710  		return nil
   711  	}
   712  
   713  	if iface.MacAddress() != nil {
   714  		networkSettings.Networks[n.Name()].MacAddress = iface.MacAddress().String()
   715  	}
   716  
   717  	if iface.Address() != nil {
   718  		ones, _ := iface.Address().Mask.Size()
   719  		networkSettings.Networks[n.Name()].IPAddress = iface.Address().IP.String()
   720  		networkSettings.Networks[n.Name()].IPPrefixLen = ones
   721  	}
   722  
   723  	if iface.AddressIPv6() != nil && iface.AddressIPv6().IP.To16() != nil {
   724  		onesv6, _ := iface.AddressIPv6().Mask.Size()
   725  		networkSettings.Networks[n.Name()].GlobalIPv6Address = iface.AddressIPv6().IP.String()
   726  		networkSettings.Networks[n.Name()].GlobalIPv6PrefixLen = onesv6
   727  	}
   728  
   729  	return nil
   730  }
   731  
   732  // UpdateJoinInfo updates network settings when container joins network n with endpoint ep.
   733  func (container *Container) UpdateJoinInfo(n libnetwork.Network, ep libnetwork.Endpoint) error {
   734  	if err := container.buildPortMapInfo(ep); err != nil {
   735  		return err
   736  	}
   737  
   738  	epInfo := ep.Info()
   739  	if epInfo == nil {
   740  		// It is not an error to get an empty endpoint info
   741  		return nil
   742  	}
   743  	if epInfo.Gateway() != nil {
   744  		container.NetworkSettings.Networks[n.Name()].Gateway = epInfo.Gateway().String()
   745  	}
   746  	if epInfo.GatewayIPv6().To16() != nil {
   747  		container.NetworkSettings.Networks[n.Name()].IPv6Gateway = epInfo.GatewayIPv6().String()
   748  	}
   749  
   750  	return nil
   751  }
   752  
   753  // UpdateSandboxNetworkSettings updates the sandbox ID and Key.
   754  func (container *Container) UpdateSandboxNetworkSettings(sb libnetwork.Sandbox) error {
   755  	container.NetworkSettings.SandboxID = sb.ID()
   756  	container.NetworkSettings.SandboxKey = sb.Key()
   757  	return nil
   758  }
   759  
   760  // BuildJoinOptions builds endpoint Join options from a given network.
   761  func (container *Container) BuildJoinOptions(n libnetwork.Network) ([]libnetwork.EndpointOption, error) {
   762  	var joinOptions []libnetwork.EndpointOption
   763  	if epConfig, ok := container.NetworkSettings.Networks[n.Name()]; ok {
   764  		for _, str := range epConfig.Links {
   765  			name, alias, err := runconfigopts.ParseLink(str)
   766  			if err != nil {
   767  				return nil, err
   768  			}
   769  			joinOptions = append(joinOptions, libnetwork.CreateOptionAlias(name, alias))
   770  		}
   771  	}
   772  	return joinOptions, nil
   773  }
   774  
   775  // BuildCreateEndpointOptions builds endpoint options from a given network.
   776  func (container *Container) BuildCreateEndpointOptions(n libnetwork.Network, epConfig *networktypes.EndpointSettings, sb libnetwork.Sandbox) ([]libnetwork.EndpointOption, error) {
   777  	var (
   778  		bindings      = make(nat.PortMap)
   779  		pbList        []types.PortBinding
   780  		exposeList    []types.TransportPort
   781  		createOptions []libnetwork.EndpointOption
   782  	)
   783  
   784  	defaultNetName := runconfig.DefaultDaemonNetworkMode().NetworkName()
   785  
   786  	if n.Name() == defaultNetName || container.NetworkSettings.IsAnonymousEndpoint {
   787  		createOptions = append(createOptions, libnetwork.CreateOptionAnonymous())
   788  	}
   789  
   790  	if epConfig != nil {
   791  		ipam := epConfig.IPAMConfig
   792  		if ipam != nil && (ipam.IPv4Address != "" || ipam.IPv6Address != "") {
   793  			createOptions = append(createOptions,
   794  				libnetwork.CreateOptionIpam(net.ParseIP(ipam.IPv4Address), net.ParseIP(ipam.IPv6Address), nil))
   795  		}
   796  
   797  		for _, alias := range epConfig.Aliases {
   798  			createOptions = append(createOptions, libnetwork.CreateOptionMyAlias(alias))
   799  		}
   800  	}
   801  
   802  	if !containertypes.NetworkMode(n.Name()).IsUserDefined() {
   803  		createOptions = append(createOptions, libnetwork.CreateOptionDisableResolution())
   804  	}
   805  
   806  	// configs that are applicable only for the endpoint in the network
   807  	// to which container was connected to on docker run.
   808  	// Ideally all these network-specific endpoint configurations must be moved under
   809  	// container.NetworkSettings.Networks[n.Name()]
   810  	if n.Name() == container.HostConfig.NetworkMode.NetworkName() ||
   811  		(n.Name() == defaultNetName && container.HostConfig.NetworkMode.IsDefault()) {
   812  		if container.Config.MacAddress != "" {
   813  			mac, err := net.ParseMAC(container.Config.MacAddress)
   814  			if err != nil {
   815  				return nil, err
   816  			}
   817  
   818  			genericOption := options.Generic{
   819  				netlabel.MacAddress: mac,
   820  			}
   821  
   822  			createOptions = append(createOptions, libnetwork.EndpointOptionGeneric(genericOption))
   823  		}
   824  	}
   825  
   826  	// Port-mapping rules belong to the container & applicable only to non-internal networks
   827  	portmaps := getSandboxPortMapInfo(sb)
   828  	if n.Info().Internal() || len(portmaps) > 0 {
   829  		return createOptions, nil
   830  	}
   831  
   832  	if container.HostConfig.PortBindings != nil {
   833  		for p, b := range container.HostConfig.PortBindings {
   834  			bindings[p] = []nat.PortBinding{}
   835  			for _, bb := range b {
   836  				bindings[p] = append(bindings[p], nat.PortBinding{
   837  					HostIP:   bb.HostIP,
   838  					HostPort: bb.HostPort,
   839  				})
   840  			}
   841  		}
   842  	}
   843  
   844  	portSpecs := container.Config.ExposedPorts
   845  	ports := make([]nat.Port, len(portSpecs))
   846  	var i int
   847  	for p := range portSpecs {
   848  		ports[i] = p
   849  		i++
   850  	}
   851  	nat.SortPortMap(ports, bindings)
   852  	for _, port := range ports {
   853  		expose := types.TransportPort{}
   854  		expose.Proto = types.ParseProtocol(port.Proto())
   855  		expose.Port = uint16(port.Int())
   856  		exposeList = append(exposeList, expose)
   857  
   858  		pb := types.PortBinding{Port: expose.Port, Proto: expose.Proto}
   859  		binding := bindings[port]
   860  		for i := 0; i < len(binding); i++ {
   861  			pbCopy := pb.GetCopy()
   862  			newP, err := nat.NewPort(nat.SplitProtoPort(binding[i].HostPort))
   863  			var portStart, portEnd int
   864  			if err == nil {
   865  				portStart, portEnd, err = newP.Range()
   866  			}
   867  			if err != nil {
   868  				return nil, fmt.Errorf("Error parsing HostPort value(%s):%v", binding[i].HostPort, err)
   869  			}
   870  			pbCopy.HostPort = uint16(portStart)
   871  			pbCopy.HostPortEnd = uint16(portEnd)
   872  			pbCopy.HostIP = net.ParseIP(binding[i].HostIP)
   873  			pbList = append(pbList, pbCopy)
   874  		}
   875  
   876  		if container.HostConfig.PublishAllPorts && len(binding) == 0 {
   877  			pbList = append(pbList, pb)
   878  		}
   879  	}
   880  
   881  	createOptions = append(createOptions,
   882  		libnetwork.CreateOptionPortMapping(pbList),
   883  		libnetwork.CreateOptionExposedPorts(exposeList))
   884  
   885  	return createOptions, nil
   886  }
   887  
   888  // UpdateMonitor updates monitor configure for running container
   889  func (container *Container) UpdateMonitor(restartPolicy containertypes.RestartPolicy) {
   890  	type policySetter interface {
   891  		SetPolicy(containertypes.RestartPolicy)
   892  	}
   893  
   894  	if rm, ok := container.RestartManager(false).(policySetter); ok {
   895  		rm.SetPolicy(restartPolicy)
   896  	}
   897  }
   898  
   899  // FullHostname returns hostname and optional domain appended to it.
   900  func (container *Container) FullHostname() string {
   901  	fullHostname := container.Config.Hostname
   902  	if container.Config.Domainname != "" {
   903  		fullHostname = fmt.Sprintf("%s.%s", fullHostname, container.Config.Domainname)
   904  	}
   905  	return fullHostname
   906  }
   907  
   908  // RestartManager returns the current restartmanager instace connected to container.
   909  func (container *Container) RestartManager(reset bool) restartmanager.RestartManager {
   910  	if reset {
   911  		container.RestartCount = 0
   912  	}
   913  	if container.restartManager == nil {
   914  		container.restartManager = restartmanager.New(container.HostConfig.RestartPolicy)
   915  	}
   916  	return container.restartManager
   917  }
   918  
   919  type attachContext struct {
   920  	ctx    context.Context
   921  	cancel context.CancelFunc
   922  	mu     sync.Mutex
   923  }
   924  
   925  // InitAttachContext initialize or returns existing context for attach calls to
   926  // track container liveness.
   927  func (container *Container) InitAttachContext() context.Context {
   928  	container.attachContext.mu.Lock()
   929  	defer container.attachContext.mu.Unlock()
   930  	if container.attachContext.ctx == nil {
   931  		container.attachContext.ctx, container.attachContext.cancel = context.WithCancel(context.Background())
   932  	}
   933  	return container.attachContext.ctx
   934  }
   935  
   936  // CancelAttachContext cancel attach context. All attach calls should detach
   937  // after this call.
   938  func (container *Container) CancelAttachContext() {
   939  	container.attachContext.mu.Lock()
   940  	if container.attachContext.ctx != nil {
   941  		container.attachContext.cancel()
   942  		container.attachContext.ctx = nil
   943  	}
   944  	container.attachContext.mu.Unlock()
   945  }