github.com/hspak/nomad@v0.7.2-0.20180309000617-bc4ae22a39a5/client/driver/docker.go (about)

     1  package driver
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"log"
     8  	"net"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strconv"
    14  	"strings"
    15  	"sync"
    16  	"syscall"
    17  	"time"
    18  
    19  	"github.com/armon/circbuf"
    20  	docker "github.com/fsouza/go-dockerclient"
    21  
    22  	"github.com/docker/docker/cli/config/configfile"
    23  	"github.com/docker/docker/reference"
    24  	"github.com/docker/docker/registry"
    25  
    26  	"github.com/hashicorp/go-multierror"
    27  	"github.com/hashicorp/go-plugin"
    28  	"github.com/hashicorp/nomad/client/allocdir"
    29  	"github.com/hashicorp/nomad/client/driver/env"
    30  	"github.com/hashicorp/nomad/client/driver/executor"
    31  	dstructs "github.com/hashicorp/nomad/client/driver/structs"
    32  	cstructs "github.com/hashicorp/nomad/client/structs"
    33  	"github.com/hashicorp/nomad/helper"
    34  	"github.com/hashicorp/nomad/helper/fields"
    35  	shelpers "github.com/hashicorp/nomad/helper/stats"
    36  	"github.com/hashicorp/nomad/nomad/structs"
    37  	"github.com/mitchellh/mapstructure"
    38  )
    39  
    40  var (
    41  	// We store the clients globally to cache the connection to the docker daemon.
    42  	createClients sync.Once
    43  
    44  	// client is a docker client with a timeout of 1 minute. This is for doing
    45  	// all operations with the docker daemon besides which are not long running
    46  	// such as creating, killing containers, etc.
    47  	client *docker.Client
    48  
    49  	// waitClient is a docker client with no timeouts. This is used for long
    50  	// running operations such as waiting on containers and collect stats
    51  	waitClient *docker.Client
    52  
    53  	// The statistics the Docker driver exposes
    54  	DockerMeasuredMemStats = []string{"RSS", "Cache", "Swap", "Max Usage"}
    55  	DockerMeasuredCpuStats = []string{"Throttled Periods", "Throttled Time", "Percent"}
    56  
    57  	// recoverableErrTimeouts returns a recoverable error if the error was due
    58  	// to timeouts
    59  	recoverableErrTimeouts = func(err error) error {
    60  		r := false
    61  		if strings.Contains(err.Error(), "Client.Timeout exceeded while awaiting headers") ||
    62  			strings.Contains(err.Error(), "EOF") {
    63  			r = true
    64  		}
    65  		return structs.NewRecoverableError(err, r)
    66  	}
    67  )
    68  
    69  const (
    70  	// NoSuchContainerError is returned by the docker daemon if the container
    71  	// does not exist.
    72  	NoSuchContainerError = "No such container"
    73  
    74  	// The key populated in Node Attributes to indicate presence of the Docker
    75  	// driver
    76  	dockerDriverAttr = "driver.docker"
    77  
    78  	// dockerSELinuxLabelConfigOption is the key for configuring the
    79  	// SELinux label for binds.
    80  	dockerSELinuxLabelConfigOption = "docker.volumes.selinuxlabel"
    81  
    82  	// dockerVolumesConfigOption is the key for enabling the use of custom
    83  	// bind volumes to arbitrary host paths.
    84  	dockerVolumesConfigOption  = "docker.volumes.enabled"
    85  	dockerVolumesConfigDefault = true
    86  
    87  	// dockerPrivilegedConfigOption is the key for running containers in
    88  	// Docker's privileged mode.
    89  	dockerPrivilegedConfigOption = "docker.privileged.enabled"
    90  
    91  	// dockerCleanupImageConfigOption is the key for whether or not to
    92  	// cleanup images after the task exits.
    93  	dockerCleanupImageConfigOption  = "docker.cleanup.image"
    94  	dockerCleanupImageConfigDefault = true
    95  
    96  	// dockerPullTimeoutConfigOption is the key for setting an images pull
    97  	// timeout
    98  	dockerImageRemoveDelayConfigOption  = "docker.cleanup.image.delay"
    99  	dockerImageRemoveDelayConfigDefault = 3 * time.Minute
   100  
   101  	// dockerCapsWhitelistConfigOption is the key for setting the list of
   102  	// allowed Linux capabilities
   103  	dockerCapsWhitelistConfigOption  = "docker.caps.whitelist"
   104  	dockerCapsWhitelistConfigDefault = dockerBasicCaps
   105  
   106  	// dockerTimeout is the length of time a request can be outstanding before
   107  	// it is timed out.
   108  	dockerTimeout = 5 * time.Minute
   109  
   110  	// dockerImageResKey is the CreatedResources key for docker images
   111  	dockerImageResKey = "image"
   112  
   113  	// dockerAuthHelperPrefix is the prefix to attach to the credential helper
   114  	// and should be found in the $PATH. Example: ${prefix-}${helper-name}
   115  	dockerAuthHelperPrefix = "docker-credential-"
   116  
   117  	// dockerBasicCaps is comma-separated list of Linux capabilities that are
   118  	// allowed by docker by default, as documented in
   119  	// https://docs.docker.com/engine/reference/run/#block-io-bandwidth-blkio-constraint
   120  	dockerBasicCaps = "CHOWN,DAC_OVERRIDE,FSETID,FOWNER,MKNOD,NET_RAW,SETGID," +
   121  		"SETUID,SETFCAP,SETPCAP,NET_BIND_SERVICE,SYS_CHROOT,KILL,AUDIT_WRITE"
   122  
   123  	// This is cpu.cfs_period_us: the length of a period.
   124  	// The default values is 100 milliseconds (ms) represented in microseconds (us).
   125  	// Below is the documnentation:
   126  	// https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt
   127  	// https://docs.docker.com/engine/api/v1.35/#
   128  	defaultCFSPeriodUS = 100000
   129  )
   130  
   131  type DockerDriver struct {
   132  	DriverContext
   133  
   134  	driverConfig *DockerDriverConfig
   135  	imageID      string
   136  
   137  	// A tri-state boolean to know if the fingerprinting has happened and
   138  	// whether it has been successful
   139  	fingerprintSuccess *bool
   140  }
   141  
   142  type DockerDriverAuth struct {
   143  	Username      string `mapstructure:"username"`       // username for the registry
   144  	Password      string `mapstructure:"password"`       // password to access the registry
   145  	Email         string `mapstructure:"email"`          // email address of the user who is allowed to access the registry
   146  	ServerAddress string `mapstructure:"server_address"` // server address of the registry
   147  }
   148  
   149  type DockerLoggingOpts struct {
   150  	Type      string              `mapstructure:"type"`
   151  	ConfigRaw []map[string]string `mapstructure:"config"`
   152  	Config    map[string]string   `mapstructure:"-"`
   153  }
   154  
   155  type DockerMount struct {
   156  	Target        string                 `mapstructure:"target"`
   157  	Source        string                 `mapstructure:"source"`
   158  	ReadOnly      bool                   `mapstructure:"readonly"`
   159  	VolumeOptions []*DockerVolumeOptions `mapstructure:"volume_options"`
   160  }
   161  
   162  type DockerDevice struct {
   163  	HostPath          string `mapstructure:"host_path"`
   164  	ContainerPath     string `mapstructure:"container_path"`
   165  	CgroupPermissions string `mapstructure:"cgroup_permissions"`
   166  }
   167  
   168  type DockerVolumeOptions struct {
   169  	NoCopy       bool                       `mapstructure:"no_copy"`
   170  	Labels       []map[string]string        `mapstructure:"labels"`
   171  	DriverConfig []DockerVolumeDriverConfig `mapstructure:"driver_config"`
   172  }
   173  
   174  // VolumeDriverConfig holds a map of volume driver specific options
   175  type DockerVolumeDriverConfig struct {
   176  	Name    string              `mapstructure:"name"`
   177  	Options []map[string]string `mapstructure:"options"`
   178  }
   179  
   180  // DockerDriverConfig defines the user specified config block in a jobspec
   181  type DockerDriverConfig struct {
   182  	ImageName            string              `mapstructure:"image"`                  // Container's Image Name
   183  	LoadImage            string              `mapstructure:"load"`                   // LoadImage is a path to an image archive file
   184  	Command              string              `mapstructure:"command"`                // The Command to run when the container starts up
   185  	Args                 []string            `mapstructure:"args"`                   // The arguments to the Command
   186  	Entrypoint           []string            `mapstructure:"entrypoint"`             // Override the containers entrypoint
   187  	IpcMode              string              `mapstructure:"ipc_mode"`               // The IPC mode of the container - host and none
   188  	NetworkMode          string              `mapstructure:"network_mode"`           // The network mode of the container - host, nat and none
   189  	NetworkAliases       []string            `mapstructure:"network_aliases"`        // The network-scoped alias for the container
   190  	IPv4Address          string              `mapstructure:"ipv4_address"`           // The container ipv4 address
   191  	IPv6Address          string              `mapstructure:"ipv6_address"`           // the container ipv6 address
   192  	PidMode              string              `mapstructure:"pid_mode"`               // The PID mode of the container - host and none
   193  	UTSMode              string              `mapstructure:"uts_mode"`               // The UTS mode of the container - host and none
   194  	UsernsMode           string              `mapstructure:"userns_mode"`            // The User namespace mode of the container - host and none
   195  	PortMapRaw           []map[string]string `mapstructure:"port_map"`               //
   196  	PortMap              map[string]int      `mapstructure:"-"`                      // A map of host port labels and the ports exposed on the container
   197  	Privileged           bool                `mapstructure:"privileged"`             // Flag to run the container in privileged mode
   198  	SysctlRaw            []map[string]string `mapstructure:"sysctl"`                 //
   199  	Sysctl               map[string]string   `mapstructure:"-"`                      // The sysctl custom configurations
   200  	UlimitRaw            []map[string]string `mapstructure:"ulimit"`                 //
   201  	Ulimit               []docker.ULimit     `mapstructure:"-"`                      // The ulimit custom configurations
   202  	DNSServers           []string            `mapstructure:"dns_servers"`            // DNS Server for containers
   203  	DNSSearchDomains     []string            `mapstructure:"dns_search_domains"`     // DNS Search domains for containers
   204  	DNSOptions           []string            `mapstructure:"dns_options"`            // DNS Options
   205  	ExtraHosts           []string            `mapstructure:"extra_hosts"`            // Add host to /etc/hosts (host:IP)
   206  	Hostname             string              `mapstructure:"hostname"`               // Hostname for containers
   207  	LabelsRaw            []map[string]string `mapstructure:"labels"`                 //
   208  	Labels               map[string]string   `mapstructure:"-"`                      // Labels to set when the container starts up
   209  	Auth                 []DockerDriverAuth  `mapstructure:"auth"`                   // Authentication credentials for a private Docker registry
   210  	AuthSoftFail         bool                `mapstructure:"auth_soft_fail"`         // Soft-fail if auth creds are provided but fail
   211  	TTY                  bool                `mapstructure:"tty"`                    // Allocate a Pseudo-TTY
   212  	Interactive          bool                `mapstructure:"interactive"`            // Keep STDIN open even if not attached
   213  	ShmSize              int64               `mapstructure:"shm_size"`               // Size of /dev/shm of the container in bytes
   214  	WorkDir              string              `mapstructure:"work_dir"`               // Working directory inside the container
   215  	Logging              []DockerLoggingOpts `mapstructure:"logging"`                // Logging options for syslog server
   216  	Volumes              []string            `mapstructure:"volumes"`                // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container
   217  	Mounts               []DockerMount       `mapstructure:"mounts"`                 // Docker volumes to mount
   218  	VolumeDriver         string              `mapstructure:"volume_driver"`          // Docker volume driver used for the container's volumes
   219  	ForcePull            bool                `mapstructure:"force_pull"`             // Always force pull before running image, useful if your tags are mutable
   220  	MacAddress           string              `mapstructure:"mac_address"`            // Pin mac address to container
   221  	SecurityOpt          []string            `mapstructure:"security_opt"`           // Flags to pass directly to security-opt
   222  	Devices              []DockerDevice      `mapstructure:"devices"`                // To allow mounting USB or other serial control devices
   223  	CapAdd               []string            `mapstructure:"cap_add"`                // Flags to pass directly to cap-add
   224  	CapDrop              []string            `mapstructure:"cap_drop"`               // Flags to pass directly to cap-drop
   225  	ReadonlyRootfs       bool                `mapstructure:"readonly_rootfs"`        // Mount the container’s root filesystem as read only
   226  	AdvertiseIPv6Address bool                `mapstructure:"advertise_ipv6_address"` // Flag to use the GlobalIPv6Address from the container as the detected IP
   227  	CPUHardLimit         bool                `mapstructure:"cpu_hard_limit"`         // Enforce CPU hard limit.
   228  }
   229  
   230  func sliceMergeUlimit(ulimitsRaw map[string]string) ([]docker.ULimit, error) {
   231  	var ulimits []docker.ULimit
   232  
   233  	for name, ulimitRaw := range ulimitsRaw {
   234  		if len(ulimitRaw) == 0 {
   235  			return []docker.ULimit{}, fmt.Errorf("Malformed ulimit specification %v: %q, cannot be empty", name, ulimitRaw)
   236  		}
   237  		// hard limit is optional
   238  		if strings.Contains(ulimitRaw, ":") == false {
   239  			ulimitRaw = ulimitRaw + ":" + ulimitRaw
   240  		}
   241  
   242  		splitted := strings.SplitN(ulimitRaw, ":", 2)
   243  		if len(splitted) < 2 {
   244  			return []docker.ULimit{}, fmt.Errorf("Malformed ulimit specification %v: %v", name, ulimitRaw)
   245  		}
   246  		soft, err := strconv.Atoi(splitted[0])
   247  		if err != nil {
   248  			return []docker.ULimit{}, fmt.Errorf("Malformed soft ulimit %v: %v", name, ulimitRaw)
   249  		}
   250  		hard, err := strconv.Atoi(splitted[1])
   251  		if err != nil {
   252  			return []docker.ULimit{}, fmt.Errorf("Malformed hard ulimit %v: %v", name, ulimitRaw)
   253  		}
   254  
   255  		ulimit := docker.ULimit{
   256  			Name: name,
   257  			Soft: int64(soft),
   258  			Hard: int64(hard),
   259  		}
   260  		ulimits = append(ulimits, ulimit)
   261  	}
   262  	return ulimits, nil
   263  }
   264  
   265  // Validate validates a docker driver config
   266  func (c *DockerDriverConfig) Validate() error {
   267  	if c.ImageName == "" {
   268  		return fmt.Errorf("Docker Driver needs an image name")
   269  	}
   270  	if len(c.Devices) > 0 {
   271  		for _, dev := range c.Devices {
   272  			if dev.HostPath == "" {
   273  				return fmt.Errorf("host path must be set in configuration for devices")
   274  			}
   275  			if dev.CgroupPermissions != "" {
   276  				for _, c := range dev.CgroupPermissions {
   277  					ch := string(c)
   278  					if ch != "r" && ch != "w" && ch != "m" {
   279  						return fmt.Errorf("invalid cgroup permission string: %q", dev.CgroupPermissions)
   280  					}
   281  				}
   282  			}
   283  		}
   284  	}
   285  	c.Sysctl = mapMergeStrStr(c.SysctlRaw...)
   286  	c.Labels = mapMergeStrStr(c.LabelsRaw...)
   287  	if len(c.Logging) > 0 {
   288  		c.Logging[0].Config = mapMergeStrStr(c.Logging[0].ConfigRaw...)
   289  	}
   290  
   291  	mergedUlimitsRaw := mapMergeStrStr(c.UlimitRaw...)
   292  	ulimit, err := sliceMergeUlimit(mergedUlimitsRaw)
   293  	if err != nil {
   294  		return err
   295  	}
   296  	c.Ulimit = ulimit
   297  	return nil
   298  }
   299  
   300  // NewDockerDriverConfig returns a docker driver config by parsing the HCL
   301  // config
   302  func NewDockerDriverConfig(task *structs.Task, env *env.TaskEnv) (*DockerDriverConfig, error) {
   303  	var dconf DockerDriverConfig
   304  
   305  	if err := mapstructure.WeakDecode(task.Config, &dconf); err != nil {
   306  		return nil, err
   307  	}
   308  
   309  	// Interpolate everything that is a string
   310  	dconf.ImageName = env.ReplaceEnv(dconf.ImageName)
   311  	dconf.Command = env.ReplaceEnv(dconf.Command)
   312  	dconf.Entrypoint = env.ParseAndReplace(dconf.Entrypoint)
   313  	dconf.IpcMode = env.ReplaceEnv(dconf.IpcMode)
   314  	dconf.NetworkMode = env.ReplaceEnv(dconf.NetworkMode)
   315  	dconf.NetworkAliases = env.ParseAndReplace(dconf.NetworkAliases)
   316  	dconf.IPv4Address = env.ReplaceEnv(dconf.IPv4Address)
   317  	dconf.IPv6Address = env.ReplaceEnv(dconf.IPv6Address)
   318  	dconf.PidMode = env.ReplaceEnv(dconf.PidMode)
   319  	dconf.UTSMode = env.ReplaceEnv(dconf.UTSMode)
   320  	dconf.Hostname = env.ReplaceEnv(dconf.Hostname)
   321  	dconf.WorkDir = env.ReplaceEnv(dconf.WorkDir)
   322  	dconf.LoadImage = env.ReplaceEnv(dconf.LoadImage)
   323  	dconf.Volumes = env.ParseAndReplace(dconf.Volumes)
   324  	dconf.VolumeDriver = env.ReplaceEnv(dconf.VolumeDriver)
   325  	dconf.DNSServers = env.ParseAndReplace(dconf.DNSServers)
   326  	dconf.DNSSearchDomains = env.ParseAndReplace(dconf.DNSSearchDomains)
   327  	dconf.DNSOptions = env.ParseAndReplace(dconf.DNSOptions)
   328  	dconf.ExtraHosts = env.ParseAndReplace(dconf.ExtraHosts)
   329  	dconf.MacAddress = env.ReplaceEnv(dconf.MacAddress)
   330  	dconf.SecurityOpt = env.ParseAndReplace(dconf.SecurityOpt)
   331  	dconf.CapAdd = env.ParseAndReplace(dconf.CapAdd)
   332  	dconf.CapDrop = env.ParseAndReplace(dconf.CapDrop)
   333  
   334  	for _, m := range dconf.SysctlRaw {
   335  		for k, v := range m {
   336  			delete(m, k)
   337  			m[env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   338  		}
   339  	}
   340  
   341  	for _, m := range dconf.UlimitRaw {
   342  		for k, v := range m {
   343  			delete(m, k)
   344  			m[env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   345  		}
   346  	}
   347  
   348  	for _, m := range dconf.LabelsRaw {
   349  		for k, v := range m {
   350  			delete(m, k)
   351  			m[env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   352  		}
   353  	}
   354  	dconf.Labels = mapMergeStrStr(dconf.LabelsRaw...)
   355  
   356  	for i, a := range dconf.Auth {
   357  		dconf.Auth[i].Username = env.ReplaceEnv(a.Username)
   358  		dconf.Auth[i].Password = env.ReplaceEnv(a.Password)
   359  		dconf.Auth[i].Email = env.ReplaceEnv(a.Email)
   360  		dconf.Auth[i].ServerAddress = env.ReplaceEnv(a.ServerAddress)
   361  	}
   362  
   363  	for i, l := range dconf.Logging {
   364  		dconf.Logging[i].Type = env.ReplaceEnv(l.Type)
   365  		for _, c := range l.ConfigRaw {
   366  			for k, v := range c {
   367  				delete(c, k)
   368  				c[env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   369  			}
   370  		}
   371  	}
   372  
   373  	for i, m := range dconf.Mounts {
   374  		dconf.Mounts[i].Target = env.ReplaceEnv(m.Target)
   375  		dconf.Mounts[i].Source = env.ReplaceEnv(m.Source)
   376  
   377  		if len(m.VolumeOptions) > 1 {
   378  			return nil, fmt.Errorf("Only one volume_options stanza allowed")
   379  		}
   380  
   381  		if len(m.VolumeOptions) == 1 {
   382  			vo := m.VolumeOptions[0]
   383  			if len(vo.Labels) > 1 {
   384  				return nil, fmt.Errorf("labels may only be specified once in volume_options stanza")
   385  			}
   386  
   387  			if len(vo.Labels) == 1 {
   388  				for k, v := range vo.Labels[0] {
   389  					if k != env.ReplaceEnv(k) {
   390  						delete(vo.Labels[0], k)
   391  					}
   392  					vo.Labels[0][env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   393  				}
   394  			}
   395  
   396  			if len(vo.DriverConfig) > 1 {
   397  				return nil, fmt.Errorf("volume driver config may only be specified once")
   398  			}
   399  			if len(vo.DriverConfig) == 1 {
   400  				vo.DriverConfig[0].Name = env.ReplaceEnv(vo.DriverConfig[0].Name)
   401  				if len(vo.DriverConfig[0].Options) > 1 {
   402  					return nil, fmt.Errorf("volume driver options may only be specified once")
   403  				}
   404  
   405  				if len(vo.DriverConfig[0].Options) == 1 {
   406  					options := vo.DriverConfig[0].Options[0]
   407  					for k, v := range options {
   408  						if k != env.ReplaceEnv(k) {
   409  							delete(options, k)
   410  						}
   411  						options[env.ReplaceEnv(k)] = env.ReplaceEnv(v)
   412  					}
   413  				}
   414  			}
   415  		}
   416  	}
   417  
   418  	if len(dconf.Logging) > 0 {
   419  		dconf.Logging[0].Config = mapMergeStrStr(dconf.Logging[0].ConfigRaw...)
   420  	}
   421  
   422  	portMap := make(map[string]int)
   423  	for _, m := range dconf.PortMapRaw {
   424  		for k, v := range m {
   425  			ki, vi := env.ReplaceEnv(k), env.ReplaceEnv(v)
   426  			p, err := strconv.Atoi(vi)
   427  			if err != nil {
   428  				return nil, fmt.Errorf("failed to parse port map value %v to %v: %v", ki, vi, err)
   429  			}
   430  			portMap[ki] = p
   431  		}
   432  	}
   433  	dconf.PortMap = portMap
   434  
   435  	// Remove any http
   436  	if strings.Contains(dconf.ImageName, "https://") {
   437  		dconf.ImageName = strings.Replace(dconf.ImageName, "https://", "", 1)
   438  	}
   439  
   440  	// If devices are configured set default cgroup permissions
   441  	if len(dconf.Devices) > 0 {
   442  		for i, dev := range dconf.Devices {
   443  			if dev.CgroupPermissions == "" {
   444  				dev.CgroupPermissions = "rwm"
   445  			}
   446  			dconf.Devices[i] = dev
   447  		}
   448  	}
   449  
   450  	if err := dconf.Validate(); err != nil {
   451  		return nil, err
   452  	}
   453  	return &dconf, nil
   454  }
   455  
   456  type dockerPID struct {
   457  	Version        string
   458  	Image          string
   459  	ImageID        string
   460  	ContainerID    string
   461  	KillTimeout    time.Duration
   462  	MaxKillTimeout time.Duration
   463  	PluginConfig   *PluginReattachConfig
   464  }
   465  
   466  type DockerHandle struct {
   467  	pluginClient      *plugin.Client
   468  	executor          executor.Executor
   469  	client            *docker.Client
   470  	waitClient        *docker.Client
   471  	logger            *log.Logger
   472  	Image             string
   473  	ImageID           string
   474  	containerID       string
   475  	version           string
   476  	killTimeout       time.Duration
   477  	maxKillTimeout    time.Duration
   478  	resourceUsageLock sync.RWMutex
   479  	resourceUsage     *cstructs.TaskResourceUsage
   480  	waitCh            chan *dstructs.WaitResult
   481  	doneCh            chan bool
   482  }
   483  
   484  func NewDockerDriver(ctx *DriverContext) Driver {
   485  	return &DockerDriver{DriverContext: *ctx}
   486  }
   487  
   488  func (d *DockerDriver) Fingerprint(req *cstructs.FingerprintRequest, resp *cstructs.FingerprintResponse) error {
   489  	client, _, err := d.dockerClients()
   490  	if err != nil {
   491  		if d.fingerprintSuccess == nil || *d.fingerprintSuccess {
   492  			d.logger.Printf("[INFO] driver.docker: failed to initialize client: %s", err)
   493  		}
   494  		d.fingerprintSuccess = helper.BoolToPtr(false)
   495  		resp.RemoveAttribute(dockerDriverAttr)
   496  		return nil
   497  	}
   498  
   499  	// This is the first operation taken on the client so we'll try to
   500  	// establish a connection to the Docker daemon. If this fails it means
   501  	// Docker isn't available so we'll simply disable the docker driver.
   502  	env, err := client.Version()
   503  	if err != nil {
   504  		if d.fingerprintSuccess == nil || *d.fingerprintSuccess {
   505  			d.logger.Printf("[DEBUG] driver.docker: could not connect to docker daemon at %s: %s", client.Endpoint(), err)
   506  		}
   507  		d.fingerprintSuccess = helper.BoolToPtr(false)
   508  		resp.RemoveAttribute(dockerDriverAttr)
   509  		return nil
   510  	}
   511  
   512  	resp.AddAttribute(dockerDriverAttr, "1")
   513  	resp.AddAttribute("driver.docker.version", env.Get("Version"))
   514  	resp.Detected = true
   515  
   516  	privileged := d.config.ReadBoolDefault(dockerPrivilegedConfigOption, false)
   517  	if privileged {
   518  		resp.AddAttribute(dockerPrivilegedConfigOption, "1")
   519  	}
   520  
   521  	// Advertise if this node supports Docker volumes
   522  	if d.config.ReadBoolDefault(dockerVolumesConfigOption, dockerVolumesConfigDefault) {
   523  		resp.AddAttribute("driver."+dockerVolumesConfigOption, "1")
   524  	}
   525  
   526  	// Detect bridge IP address - #2785
   527  	if nets, err := client.ListNetworks(); err != nil {
   528  		d.logger.Printf("[WARN] driver.docker: error discovering bridge IP: %v", err)
   529  	} else {
   530  		for _, n := range nets {
   531  			if n.Name != "bridge" {
   532  				continue
   533  			}
   534  
   535  			if len(n.IPAM.Config) == 0 {
   536  				d.logger.Printf("[WARN] driver.docker: no IPAM config for bridge network")
   537  				break
   538  			}
   539  
   540  			if n.IPAM.Config[0].Gateway != "" {
   541  				resp.AddAttribute("driver.docker.bridge_ip", n.IPAM.Config[0].Gateway)
   542  			} else if d.fingerprintSuccess == nil {
   543  				// Docker 17.09.0-ce dropped the Gateway IP from the bridge network
   544  				// See https://github.com/moby/moby/issues/32648
   545  				d.logger.Printf("[DEBUG] driver.docker: bridge_ip could not be discovered")
   546  			}
   547  			break
   548  		}
   549  	}
   550  
   551  	d.fingerprintSuccess = helper.BoolToPtr(true)
   552  	return nil
   553  }
   554  
   555  // Validate is used to validate the driver configuration
   556  func (d *DockerDriver) Validate(config map[string]interface{}) error {
   557  	fd := &fields.FieldData{
   558  		Raw: config,
   559  		Schema: map[string]*fields.FieldSchema{
   560  			"image": {
   561  				Type:     fields.TypeString,
   562  				Required: true,
   563  			},
   564  			"load": {
   565  				Type: fields.TypeString,
   566  			},
   567  			"command": {
   568  				Type: fields.TypeString,
   569  			},
   570  			"args": {
   571  				Type: fields.TypeArray,
   572  			},
   573  			"entrypoint": {
   574  				Type: fields.TypeArray,
   575  			},
   576  			"ipc_mode": {
   577  				Type: fields.TypeString,
   578  			},
   579  			"network_mode": {
   580  				Type: fields.TypeString,
   581  			},
   582  			"network_aliases": {
   583  				Type: fields.TypeArray,
   584  			},
   585  			"ipv4_address": {
   586  				Type: fields.TypeString,
   587  			},
   588  			"ipv6_address": {
   589  				Type: fields.TypeString,
   590  			},
   591  			"mac_address": {
   592  				Type: fields.TypeString,
   593  			},
   594  			"pid_mode": {
   595  				Type: fields.TypeString,
   596  			},
   597  			"uts_mode": {
   598  				Type: fields.TypeString,
   599  			},
   600  			"userns_mode": {
   601  				Type: fields.TypeString,
   602  			},
   603  			"sysctl": {
   604  				Type: fields.TypeArray,
   605  			},
   606  			"ulimit": {
   607  				Type: fields.TypeArray,
   608  			},
   609  			"port_map": {
   610  				Type: fields.TypeArray,
   611  			},
   612  			"privileged": {
   613  				Type: fields.TypeBool,
   614  			},
   615  			"dns_servers": {
   616  				Type: fields.TypeArray,
   617  			},
   618  			"dns_options": {
   619  				Type: fields.TypeArray,
   620  			},
   621  			"dns_search_domains": {
   622  				Type: fields.TypeArray,
   623  			},
   624  			"extra_hosts": {
   625  				Type: fields.TypeArray,
   626  			},
   627  			"hostname": {
   628  				Type: fields.TypeString,
   629  			},
   630  			"labels": {
   631  				Type: fields.TypeArray,
   632  			},
   633  			"auth": {
   634  				Type: fields.TypeArray,
   635  			},
   636  			"auth_soft_fail": {
   637  				Type: fields.TypeBool,
   638  			},
   639  			// COMPAT: Remove in 0.6.0. SSL is no longer needed
   640  			"ssl": {
   641  				Type: fields.TypeBool,
   642  			},
   643  			"tty": {
   644  				Type: fields.TypeBool,
   645  			},
   646  			"interactive": {
   647  				Type: fields.TypeBool,
   648  			},
   649  			"shm_size": {
   650  				Type: fields.TypeInt,
   651  			},
   652  			"work_dir": {
   653  				Type: fields.TypeString,
   654  			},
   655  			"logging": {
   656  				Type: fields.TypeArray,
   657  			},
   658  			"volumes": {
   659  				Type: fields.TypeArray,
   660  			},
   661  			"volume_driver": {
   662  				Type: fields.TypeString,
   663  			},
   664  			"mounts": {
   665  				Type: fields.TypeArray,
   666  			},
   667  			"force_pull": {
   668  				Type: fields.TypeBool,
   669  			},
   670  			"security_opt": {
   671  				Type: fields.TypeArray,
   672  			},
   673  			"devices": {
   674  				Type: fields.TypeArray,
   675  			},
   676  			"cap_add": {
   677  				Type: fields.TypeArray,
   678  			},
   679  			"cap_drop": {
   680  				Type: fields.TypeArray,
   681  			},
   682  			"readonly_rootfs": {
   683  				Type: fields.TypeBool,
   684  			},
   685  			"advertise_ipv6_address": {
   686  				Type: fields.TypeBool,
   687  			},
   688  			"cpu_hard_limit": {
   689  				Type: fields.TypeBool,
   690  			},
   691  		},
   692  	}
   693  
   694  	if err := fd.Validate(); err != nil {
   695  		return err
   696  	}
   697  
   698  	return nil
   699  }
   700  
   701  func (d *DockerDriver) Abilities() DriverAbilities {
   702  	return DriverAbilities{
   703  		SendSignals: true,
   704  		Exec:        true,
   705  	}
   706  }
   707  
   708  func (d *DockerDriver) FSIsolation() cstructs.FSIsolation {
   709  	return cstructs.FSIsolationImage
   710  }
   711  
   712  // getDockerCoordinator returns the docker coordinator and the caller ID to use when
   713  // interacting with the coordinator
   714  func (d *DockerDriver) getDockerCoordinator(client *docker.Client) (*dockerCoordinator, string) {
   715  	config := &dockerCoordinatorConfig{
   716  		client:      client,
   717  		cleanup:     d.config.ReadBoolDefault(dockerCleanupImageConfigOption, dockerCleanupImageConfigDefault),
   718  		logger:      d.logger,
   719  		removeDelay: d.config.ReadDurationDefault(dockerImageRemoveDelayConfigOption, dockerImageRemoveDelayConfigDefault),
   720  	}
   721  
   722  	return GetDockerCoordinator(config), fmt.Sprintf("%s-%s", d.DriverContext.allocID, d.DriverContext.taskName)
   723  }
   724  
   725  func (d *DockerDriver) Prestart(ctx *ExecContext, task *structs.Task) (*PrestartResponse, error) {
   726  	driverConfig, err := NewDockerDriverConfig(task, ctx.TaskEnv)
   727  	if err != nil {
   728  		return nil, err
   729  	}
   730  
   731  	// Set state needed by Start
   732  	d.driverConfig = driverConfig
   733  
   734  	// Initialize docker API clients
   735  	client, _, err := d.dockerClients()
   736  	if err != nil {
   737  		return nil, fmt.Errorf("Failed to connect to docker daemon: %s", err)
   738  	}
   739  
   740  	// Ensure the image is available
   741  	id, err := d.createImage(driverConfig, client, ctx.TaskDir)
   742  	if err != nil {
   743  		return nil, err
   744  	}
   745  	d.imageID = id
   746  
   747  	resp := NewPrestartResponse()
   748  	resp.CreatedResources.Add(dockerImageResKey, id)
   749  
   750  	// Return the PortMap if it's set
   751  	if len(driverConfig.PortMap) > 0 {
   752  		resp.Network = &cstructs.DriverNetwork{
   753  			PortMap: driverConfig.PortMap,
   754  		}
   755  	}
   756  	return resp, nil
   757  }
   758  
   759  func (d *DockerDriver) Start(ctx *ExecContext, task *structs.Task) (*StartResponse, error) {
   760  	pluginLogFile := filepath.Join(ctx.TaskDir.Dir, "executor.out")
   761  	executorConfig := &dstructs.ExecutorConfig{
   762  		LogFile:  pluginLogFile,
   763  		LogLevel: d.config.LogLevel,
   764  	}
   765  
   766  	exec, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig)
   767  	if err != nil {
   768  		return nil, err
   769  	}
   770  	executorCtx := &executor.ExecutorContext{
   771  		TaskEnv:        ctx.TaskEnv,
   772  		Task:           task,
   773  		Driver:         "docker",
   774  		LogDir:         ctx.TaskDir.LogDir,
   775  		TaskDir:        ctx.TaskDir.Dir,
   776  		PortLowerBound: d.config.ClientMinPort,
   777  		PortUpperBound: d.config.ClientMaxPort,
   778  	}
   779  	if err := exec.SetContext(executorCtx); err != nil {
   780  		pluginClient.Kill()
   781  		return nil, fmt.Errorf("failed to set executor context: %v", err)
   782  	}
   783  
   784  	// The user hasn't specified any logging options so launch our own syslog
   785  	// server if possible.
   786  	syslogAddr := ""
   787  	if len(d.driverConfig.Logging) == 0 {
   788  		if runtime.GOOS == "darwin" {
   789  			d.logger.Printf("[DEBUG] driver.docker: disabling syslog driver as Docker for Mac workaround")
   790  		} else {
   791  			ss, err := exec.LaunchSyslogServer()
   792  			if err != nil {
   793  				pluginClient.Kill()
   794  				return nil, fmt.Errorf("failed to start syslog collector: %v", err)
   795  			}
   796  			syslogAddr = ss.Addr
   797  		}
   798  	}
   799  
   800  	config, err := d.createContainerConfig(ctx, task, d.driverConfig, syslogAddr)
   801  	if err != nil {
   802  		d.logger.Printf("[ERR] driver.docker: failed to create container configuration for image %q (%q): %v", d.driverConfig.ImageName, d.imageID, err)
   803  		pluginClient.Kill()
   804  		return nil, fmt.Errorf("Failed to create container configuration for image %q (%q): %v", d.driverConfig.ImageName, d.imageID, err)
   805  	}
   806  
   807  	container, err := d.createContainer(config)
   808  	if err != nil {
   809  		wrapped := fmt.Sprintf("Failed to create container: %v", err)
   810  		d.logger.Printf("[ERR] driver.docker: %s", wrapped)
   811  		pluginClient.Kill()
   812  		return nil, structs.WrapRecoverable(wrapped, err)
   813  	}
   814  
   815  	d.logger.Printf("[INFO] driver.docker: created container %s", container.ID)
   816  
   817  	// We don't need to start the container if the container is already running
   818  	// since we don't create containers which are already present on the host
   819  	// and are running
   820  	if !container.State.Running {
   821  		// Start the container
   822  		if err := d.startContainer(container); err != nil {
   823  			d.logger.Printf("[ERR] driver.docker: failed to start container %s: %s", container.ID, err)
   824  			pluginClient.Kill()
   825  			return nil, structs.NewRecoverableError(fmt.Errorf("Failed to start container %s: %s", container.ID, err), structs.IsRecoverable(err))
   826  		}
   827  
   828  		// InspectContainer to get all of the container metadata as
   829  		// much of the metadata (eg networking) isn't populated until
   830  		// the container is started
   831  		runningContainer, err := client.InspectContainer(container.ID)
   832  		if err != nil {
   833  			err = fmt.Errorf("failed to inspect started container %s: %s", container.ID, err)
   834  			d.logger.Printf("[ERR] driver.docker: %v", err)
   835  			pluginClient.Kill()
   836  			return nil, structs.NewRecoverableError(err, true)
   837  		}
   838  		container = runningContainer
   839  		d.logger.Printf("[INFO] driver.docker: started container %s", container.ID)
   840  	} else {
   841  		d.logger.Printf("[DEBUG] driver.docker: re-attaching to container %s with status %q",
   842  			container.ID, container.State.String())
   843  	}
   844  
   845  	// Return a driver handle
   846  	maxKill := d.DriverContext.config.MaxKillTimeout
   847  	h := &DockerHandle{
   848  		client:         client,
   849  		waitClient:     waitClient,
   850  		executor:       exec,
   851  		pluginClient:   pluginClient,
   852  		logger:         d.logger,
   853  		Image:          d.driverConfig.ImageName,
   854  		ImageID:        d.imageID,
   855  		containerID:    container.ID,
   856  		version:        d.config.Version.VersionNumber(),
   857  		killTimeout:    GetKillTimeout(task.KillTimeout, maxKill),
   858  		maxKillTimeout: maxKill,
   859  		doneCh:         make(chan bool),
   860  		waitCh:         make(chan *dstructs.WaitResult, 1),
   861  	}
   862  	go h.collectStats()
   863  	go h.run()
   864  
   865  	// Detect container address
   866  	ip, autoUse := d.detectIP(container)
   867  
   868  	// Create a response with the driver handle and container network metadata
   869  	resp := &StartResponse{
   870  		Handle: h,
   871  		Network: &cstructs.DriverNetwork{
   872  			PortMap:       d.driverConfig.PortMap,
   873  			IP:            ip,
   874  			AutoAdvertise: autoUse,
   875  		},
   876  	}
   877  	return resp, nil
   878  }
   879  
   880  // detectIP of Docker container. Returns the first IP found as well as true if
   881  // the IP should be advertised (bridge network IPs return false). Returns an
   882  // empty string and false if no IP could be found.
   883  func (d *DockerDriver) detectIP(c *docker.Container) (string, bool) {
   884  	if c.NetworkSettings == nil {
   885  		// This should only happen if there's been a coding error (such
   886  		// as not calling InspetContainer after CreateContainer). Code
   887  		// defensively in case the Docker API changes subtly.
   888  		d.logger.Printf("[ERROR] driver.docker: no network settings for container %s", c.ID)
   889  		return "", false
   890  	}
   891  
   892  	ip, ipName := "", ""
   893  	auto := false
   894  	for name, net := range c.NetworkSettings.Networks {
   895  		if net.IPAddress == "" {
   896  			// Ignore networks without an IP address
   897  			continue
   898  		}
   899  
   900  		ip = net.IPAddress
   901  		if d.driverConfig.AdvertiseIPv6Address {
   902  			ip = net.GlobalIPv6Address
   903  			auto = true
   904  		}
   905  		ipName = name
   906  
   907  		// Don't auto-advertise IPs for default networks (bridge on
   908  		// Linux, nat on Windows)
   909  		if name != "bridge" && name != "nat" {
   910  			auto = true
   911  		}
   912  
   913  		break
   914  	}
   915  
   916  	if n := len(c.NetworkSettings.Networks); n > 1 {
   917  		d.logger.Printf("[WARN] driver.docker: task %s multiple (%d) Docker networks for container %q but Nomad only supports 1: choosing %q", d.taskName, n, c.ID, ipName)
   918  	}
   919  
   920  	return ip, auto
   921  }
   922  
   923  func (d *DockerDriver) Cleanup(_ *ExecContext, res *CreatedResources) error {
   924  	retry := false
   925  	var merr multierror.Error
   926  	for key, resources := range res.Resources {
   927  		switch key {
   928  		case dockerImageResKey:
   929  			for _, value := range resources {
   930  				err := d.cleanupImage(value)
   931  				if err != nil {
   932  					if structs.IsRecoverable(err) {
   933  						retry = true
   934  					}
   935  					merr.Errors = append(merr.Errors, err)
   936  					continue
   937  				}
   938  
   939  				// Remove cleaned image from resources
   940  				res.Remove(dockerImageResKey, value)
   941  			}
   942  		default:
   943  			d.logger.Printf("[ERR] driver.docker: unknown resource to cleanup: %q", key)
   944  		}
   945  	}
   946  	return structs.NewRecoverableError(merr.ErrorOrNil(), retry)
   947  }
   948  
   949  // cleanupImage removes a Docker image. No error is returned if the image
   950  // doesn't exist or is still in use. Requires the global client to already be
   951  // initialized.
   952  func (d *DockerDriver) cleanupImage(imageID string) error {
   953  	if !d.config.ReadBoolDefault(dockerCleanupImageConfigOption, dockerCleanupImageConfigDefault) {
   954  		// Config says not to cleanup
   955  		return nil
   956  	}
   957  
   958  	coordinator, callerID := d.getDockerCoordinator(client)
   959  	coordinator.RemoveImage(imageID, callerID)
   960  
   961  	return nil
   962  }
   963  
   964  // dockerClients creates two *docker.Client, one for long running operations and
   965  // the other for shorter operations. In test / dev mode we can use ENV vars to
   966  // connect to the docker daemon. In production mode we will read docker.endpoint
   967  // from the config file.
   968  func (d *DockerDriver) dockerClients() (*docker.Client, *docker.Client, error) {
   969  	if client != nil && waitClient != nil {
   970  		return client, waitClient, nil
   971  	}
   972  
   973  	var err error
   974  	var merr multierror.Error
   975  	createClients.Do(func() {
   976  		// Default to using whatever is configured in docker.endpoint. If this is
   977  		// not specified we'll fall back on NewClientFromEnv which reads config from
   978  		// the DOCKER_* environment variables DOCKER_HOST, DOCKER_TLS_VERIFY, and
   979  		// DOCKER_CERT_PATH. This allows us to lock down the config in production
   980  		// but also accept the standard ENV configs for dev and test.
   981  		dockerEndpoint := d.config.Read("docker.endpoint")
   982  		if dockerEndpoint != "" {
   983  			cert := d.config.Read("docker.tls.cert")
   984  			key := d.config.Read("docker.tls.key")
   985  			ca := d.config.Read("docker.tls.ca")
   986  
   987  			if cert+key+ca != "" {
   988  				d.logger.Printf("[DEBUG] driver.docker: using TLS client connection to %s", dockerEndpoint)
   989  				client, err = docker.NewTLSClient(dockerEndpoint, cert, key, ca)
   990  				if err != nil {
   991  					merr.Errors = append(merr.Errors, err)
   992  				}
   993  				waitClient, err = docker.NewTLSClient(dockerEndpoint, cert, key, ca)
   994  				if err != nil {
   995  					merr.Errors = append(merr.Errors, err)
   996  				}
   997  			} else {
   998  				d.logger.Printf("[DEBUG] driver.docker: using standard client connection to %s", dockerEndpoint)
   999  				client, err = docker.NewClient(dockerEndpoint)
  1000  				if err != nil {
  1001  					merr.Errors = append(merr.Errors, err)
  1002  				}
  1003  				waitClient, err = docker.NewClient(dockerEndpoint)
  1004  				if err != nil {
  1005  					merr.Errors = append(merr.Errors, err)
  1006  				}
  1007  			}
  1008  			client.SetTimeout(dockerTimeout)
  1009  			return
  1010  		}
  1011  
  1012  		d.logger.Println("[DEBUG] driver.docker: using client connection initialized from environment")
  1013  		client, err = docker.NewClientFromEnv()
  1014  		if err != nil {
  1015  			merr.Errors = append(merr.Errors, err)
  1016  		}
  1017  		client.SetTimeout(dockerTimeout)
  1018  
  1019  		waitClient, err = docker.NewClientFromEnv()
  1020  		if err != nil {
  1021  			merr.Errors = append(merr.Errors, err)
  1022  		}
  1023  	})
  1024  	return client, waitClient, merr.ErrorOrNil()
  1025  }
  1026  
  1027  func (d *DockerDriver) containerBinds(driverConfig *DockerDriverConfig, ctx *ExecContext,
  1028  	task *structs.Task) ([]string, error) {
  1029  
  1030  	allocDirBind := fmt.Sprintf("%s:%s", ctx.TaskDir.SharedAllocDir, ctx.TaskEnv.EnvMap[env.AllocDir])
  1031  	taskLocalBind := fmt.Sprintf("%s:%s", ctx.TaskDir.LocalDir, ctx.TaskEnv.EnvMap[env.TaskLocalDir])
  1032  	secretDirBind := fmt.Sprintf("%s:%s", ctx.TaskDir.SecretsDir, ctx.TaskEnv.EnvMap[env.SecretsDir])
  1033  	binds := []string{allocDirBind, taskLocalBind, secretDirBind}
  1034  
  1035  	volumesEnabled := d.config.ReadBoolDefault(dockerVolumesConfigOption, dockerVolumesConfigDefault)
  1036  
  1037  	if !volumesEnabled && driverConfig.VolumeDriver != "" {
  1038  		return nil, fmt.Errorf("%s is false; cannot use volume driver %q", dockerVolumesConfigOption, driverConfig.VolumeDriver)
  1039  	}
  1040  
  1041  	for _, userbind := range driverConfig.Volumes {
  1042  		parts := strings.Split(userbind, ":")
  1043  		if len(parts) < 2 {
  1044  			return nil, fmt.Errorf("invalid docker volume: %q", userbind)
  1045  		}
  1046  
  1047  		// Resolve dotted path segments
  1048  		parts[0] = filepath.Clean(parts[0])
  1049  
  1050  		// Absolute paths aren't always supported
  1051  		if filepath.IsAbs(parts[0]) {
  1052  			if !volumesEnabled {
  1053  				// Disallow mounting arbitrary absolute paths
  1054  				return nil, fmt.Errorf("%s is false; cannot mount host paths: %+q", dockerVolumesConfigOption, userbind)
  1055  			}
  1056  			binds = append(binds, userbind)
  1057  			continue
  1058  		}
  1059  
  1060  		// Relative paths are always allowed as they mount within a container
  1061  		// When a VolumeDriver is set, we assume we receive a binding in the format volume-name:container-dest
  1062  		// Otherwise, we assume we receive a relative path binding in the format relative/to/task:/also/in/container
  1063  		if driverConfig.VolumeDriver == "" {
  1064  			// Expand path relative to alloc dir
  1065  			parts[0] = filepath.Join(ctx.TaskDir.Dir, parts[0])
  1066  		}
  1067  
  1068  		binds = append(binds, strings.Join(parts, ":"))
  1069  	}
  1070  
  1071  	if selinuxLabel := d.config.Read(dockerSELinuxLabelConfigOption); selinuxLabel != "" {
  1072  		// Apply SELinux Label to each volume
  1073  		for i := range binds {
  1074  			binds[i] = fmt.Sprintf("%s:%s", binds[i], selinuxLabel)
  1075  		}
  1076  	}
  1077  
  1078  	return binds, nil
  1079  }
  1080  
  1081  // createContainerConfig initializes a struct needed to call docker.client.CreateContainer()
  1082  func (d *DockerDriver) createContainerConfig(ctx *ExecContext, task *structs.Task,
  1083  	driverConfig *DockerDriverConfig, syslogAddr string) (docker.CreateContainerOptions, error) {
  1084  	var c docker.CreateContainerOptions
  1085  	if task.Resources == nil {
  1086  		// Guard against missing resources. We should never have been able to
  1087  		// schedule a job without specifying this.
  1088  		d.logger.Println("[ERR] driver.docker: task.Resources is empty")
  1089  		return c, fmt.Errorf("task.Resources is empty")
  1090  	}
  1091  
  1092  	binds, err := d.containerBinds(driverConfig, ctx, task)
  1093  	if err != nil {
  1094  		return c, err
  1095  	}
  1096  
  1097  	// create the config block that will later be consumed by go-dockerclient
  1098  	config := &docker.Config{
  1099  		Image:       d.imageID,
  1100  		Entrypoint:  driverConfig.Entrypoint,
  1101  		Hostname:    driverConfig.Hostname,
  1102  		User:        task.User,
  1103  		Tty:         driverConfig.TTY,
  1104  		OpenStdin:   driverConfig.Interactive,
  1105  		StopTimeout: int(task.KillTimeout.Seconds()),
  1106  		StopSignal:  task.KillSignal,
  1107  	}
  1108  
  1109  	if driverConfig.WorkDir != "" {
  1110  		config.WorkingDir = driverConfig.WorkDir
  1111  	}
  1112  
  1113  	memLimit := int64(task.Resources.MemoryMB) * 1024 * 1024
  1114  
  1115  	if len(driverConfig.Logging) == 0 {
  1116  		if runtime.GOOS == "darwin" {
  1117  			d.logger.Printf("[DEBUG] driver.docker: deferring logging to docker on Docker for Mac")
  1118  		} else {
  1119  			d.logger.Printf("[DEBUG] driver.docker: Setting default logging options to syslog and %s", syslogAddr)
  1120  			driverConfig.Logging = []DockerLoggingOpts{
  1121  				{Type: "syslog", Config: map[string]string{"syslog-address": syslogAddr}},
  1122  			}
  1123  		}
  1124  	}
  1125  
  1126  	hostConfig := &docker.HostConfig{
  1127  		// Convert MB to bytes. This is an absolute value.
  1128  		Memory: memLimit,
  1129  		// Convert Mhz to shares. This is a relative value.
  1130  		CPUShares: int64(task.Resources.CPU),
  1131  
  1132  		// Binds are used to mount a host volume into the container. We mount a
  1133  		// local directory for storage and a shared alloc directory that can be
  1134  		// used to share data between different tasks in the same task group.
  1135  		Binds: binds,
  1136  
  1137  		VolumeDriver: driverConfig.VolumeDriver,
  1138  	}
  1139  
  1140  	// Calculate CPU Quota
  1141  	// cfs_quota_us is the time per core, so we must
  1142  	// multiply the time by the number of cores available
  1143  	// See https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/resource_management_guide/sec-cpu
  1144  	if driverConfig.CPUHardLimit {
  1145  		numCores := runtime.NumCPU()
  1146  		percentTicks := float64(task.Resources.CPU) / float64(d.node.Resources.CPU)
  1147  		hostConfig.CPUQuota = int64(percentTicks*defaultCFSPeriodUS) * int64(numCores)
  1148  	}
  1149  
  1150  	// Windows does not support MemorySwap/MemorySwappiness #2193
  1151  	if runtime.GOOS == "windows" {
  1152  		hostConfig.MemorySwap = 0
  1153  		hostConfig.MemorySwappiness = -1
  1154  	} else {
  1155  		hostConfig.MemorySwap = memLimit // MemorySwap is memory + swap.
  1156  	}
  1157  
  1158  	if len(driverConfig.Logging) != 0 {
  1159  		d.logger.Printf("[DEBUG] driver.docker: Using config for logging: %+v", driverConfig.Logging[0])
  1160  		hostConfig.LogConfig = docker.LogConfig{
  1161  			Type:   driverConfig.Logging[0].Type,
  1162  			Config: driverConfig.Logging[0].Config,
  1163  		}
  1164  	}
  1165  
  1166  	d.logger.Printf("[DEBUG] driver.docker: using %d bytes memory for %s", hostConfig.Memory, task.Name)
  1167  	d.logger.Printf("[DEBUG] driver.docker: using %d cpu shares for %s", hostConfig.CPUShares, task.Name)
  1168  	if driverConfig.CPUHardLimit {
  1169  		d.logger.Printf("[DEBUG] driver.docker: using %dms cpu quota and %dms cpu period for %s", hostConfig.CPUQuota, defaultCFSPeriodUS, task.Name)
  1170  	}
  1171  	d.logger.Printf("[DEBUG] driver.docker: binding directories %#v for %s", hostConfig.Binds, task.Name)
  1172  
  1173  	//  set privileged mode
  1174  	hostPrivileged := d.config.ReadBoolDefault(dockerPrivilegedConfigOption, false)
  1175  	if driverConfig.Privileged && !hostPrivileged {
  1176  		return c, fmt.Errorf(`Docker privileged mode is disabled on this Nomad agent`)
  1177  	}
  1178  	hostConfig.Privileged = driverConfig.Privileged
  1179  
  1180  	// set capabilities
  1181  	hostCapsWhitelistConfig := d.config.ReadDefault(
  1182  		dockerCapsWhitelistConfigOption, dockerCapsWhitelistConfigDefault)
  1183  	hostCapsWhitelist := make(map[string]struct{})
  1184  	for _, cap := range strings.Split(hostCapsWhitelistConfig, ",") {
  1185  		cap = strings.ToLower(strings.TrimSpace(cap))
  1186  		hostCapsWhitelist[cap] = struct{}{}
  1187  	}
  1188  
  1189  	if _, ok := hostCapsWhitelist["all"]; !ok {
  1190  		effectiveCaps, err := tweakCapabilities(
  1191  			strings.Split(dockerBasicCaps, ","),
  1192  			driverConfig.CapAdd,
  1193  			driverConfig.CapDrop,
  1194  		)
  1195  		if err != nil {
  1196  			return c, err
  1197  		}
  1198  		var missingCaps []string
  1199  		for _, cap := range effectiveCaps {
  1200  			cap = strings.ToLower(cap)
  1201  			if _, ok := hostCapsWhitelist[cap]; !ok {
  1202  				missingCaps = append(missingCaps, cap)
  1203  			}
  1204  		}
  1205  		if len(missingCaps) > 0 {
  1206  			return c, fmt.Errorf("Docker driver doesn't have the following caps whitelisted on this Nomad agent: %s", missingCaps)
  1207  		}
  1208  	}
  1209  
  1210  	hostConfig.CapAdd = driverConfig.CapAdd
  1211  	hostConfig.CapDrop = driverConfig.CapDrop
  1212  
  1213  	// set SHM size
  1214  	if driverConfig.ShmSize != 0 {
  1215  		hostConfig.ShmSize = driverConfig.ShmSize
  1216  	}
  1217  
  1218  	// set DNS servers
  1219  	for _, ip := range driverConfig.DNSServers {
  1220  		if net.ParseIP(ip) != nil {
  1221  			hostConfig.DNS = append(hostConfig.DNS, ip)
  1222  		} else {
  1223  			d.logger.Printf("[ERR] driver.docker: invalid ip address for container dns server: %s", ip)
  1224  		}
  1225  	}
  1226  
  1227  	if len(driverConfig.Devices) > 0 {
  1228  		var devices []docker.Device
  1229  		for _, device := range driverConfig.Devices {
  1230  			dev := docker.Device{
  1231  				PathOnHost:        device.HostPath,
  1232  				PathInContainer:   device.ContainerPath,
  1233  				CgroupPermissions: device.CgroupPermissions}
  1234  			devices = append(devices, dev)
  1235  		}
  1236  		hostConfig.Devices = devices
  1237  	}
  1238  
  1239  	// Setup mounts
  1240  	for _, m := range driverConfig.Mounts {
  1241  		hm := docker.HostMount{
  1242  			Target:   m.Target,
  1243  			Source:   m.Source,
  1244  			Type:     "volume", // Only type supported
  1245  			ReadOnly: m.ReadOnly,
  1246  		}
  1247  		if len(m.VolumeOptions) == 1 {
  1248  			vo := m.VolumeOptions[0]
  1249  			hm.VolumeOptions = &docker.VolumeOptions{
  1250  				NoCopy: vo.NoCopy,
  1251  			}
  1252  
  1253  			if len(vo.DriverConfig) == 1 {
  1254  				dc := vo.DriverConfig[0]
  1255  				hm.VolumeOptions.DriverConfig = docker.VolumeDriverConfig{
  1256  					Name: dc.Name,
  1257  				}
  1258  				if len(dc.Options) == 1 {
  1259  					hm.VolumeOptions.DriverConfig.Options = dc.Options[0]
  1260  				}
  1261  			}
  1262  			if len(vo.Labels) == 1 {
  1263  				hm.VolumeOptions.Labels = vo.Labels[0]
  1264  			}
  1265  		}
  1266  		hostConfig.Mounts = append(hostConfig.Mounts, hm)
  1267  	}
  1268  
  1269  	// set DNS search domains and extra hosts
  1270  	hostConfig.DNSSearch = driverConfig.DNSSearchDomains
  1271  	hostConfig.DNSOptions = driverConfig.DNSOptions
  1272  	hostConfig.ExtraHosts = driverConfig.ExtraHosts
  1273  
  1274  	hostConfig.IpcMode = driverConfig.IpcMode
  1275  	hostConfig.PidMode = driverConfig.PidMode
  1276  	hostConfig.UTSMode = driverConfig.UTSMode
  1277  	hostConfig.UsernsMode = driverConfig.UsernsMode
  1278  	hostConfig.SecurityOpt = driverConfig.SecurityOpt
  1279  	hostConfig.Sysctls = driverConfig.Sysctl
  1280  	hostConfig.Ulimits = driverConfig.Ulimit
  1281  	hostConfig.ReadonlyRootfs = driverConfig.ReadonlyRootfs
  1282  
  1283  	hostConfig.NetworkMode = driverConfig.NetworkMode
  1284  	if hostConfig.NetworkMode == "" {
  1285  		// docker default
  1286  		d.logger.Printf("[DEBUG] driver.docker: networking mode not specified; defaulting to %s", defaultNetworkMode)
  1287  		hostConfig.NetworkMode = defaultNetworkMode
  1288  	}
  1289  
  1290  	// Setup port mapping and exposed ports
  1291  	if len(task.Resources.Networks) == 0 {
  1292  		d.logger.Println("[DEBUG] driver.docker: No network interfaces are available")
  1293  		if len(driverConfig.PortMap) > 0 {
  1294  			return c, fmt.Errorf("Trying to map ports but no network interface is available")
  1295  		}
  1296  	} else {
  1297  		// TODO add support for more than one network
  1298  		network := task.Resources.Networks[0]
  1299  		publishedPorts := map[docker.Port][]docker.PortBinding{}
  1300  		exposedPorts := map[docker.Port]struct{}{}
  1301  
  1302  		for _, port := range network.ReservedPorts {
  1303  			// By default we will map the allocated port 1:1 to the container
  1304  			containerPortInt := port.Value
  1305  
  1306  			// If the user has mapped a port using port_map we'll change it here
  1307  			if mapped, ok := driverConfig.PortMap[port.Label]; ok {
  1308  				containerPortInt = mapped
  1309  			}
  1310  
  1311  			hostPortStr := strconv.Itoa(port.Value)
  1312  			containerPort := docker.Port(strconv.Itoa(containerPortInt))
  1313  
  1314  			publishedPorts[containerPort+"/tcp"] = getPortBinding(network.IP, hostPortStr)
  1315  			publishedPorts[containerPort+"/udp"] = getPortBinding(network.IP, hostPortStr)
  1316  			d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (static)", network.IP, port.Value, port.Value)
  1317  
  1318  			exposedPorts[containerPort+"/tcp"] = struct{}{}
  1319  			exposedPorts[containerPort+"/udp"] = struct{}{}
  1320  			d.logger.Printf("[DEBUG] driver.docker: exposed port %d", port.Value)
  1321  		}
  1322  
  1323  		for _, port := range network.DynamicPorts {
  1324  			// By default we will map the allocated port 1:1 to the container
  1325  			containerPortInt := port.Value
  1326  
  1327  			// If the user has mapped a port using port_map we'll change it here
  1328  			if mapped, ok := driverConfig.PortMap[port.Label]; ok {
  1329  				containerPortInt = mapped
  1330  			}
  1331  
  1332  			hostPortStr := strconv.Itoa(port.Value)
  1333  			containerPort := docker.Port(strconv.Itoa(containerPortInt))
  1334  
  1335  			publishedPorts[containerPort+"/tcp"] = getPortBinding(network.IP, hostPortStr)
  1336  			publishedPorts[containerPort+"/udp"] = getPortBinding(network.IP, hostPortStr)
  1337  			d.logger.Printf("[DEBUG] driver.docker: allocated port %s:%d -> %d (mapped)", network.IP, port.Value, containerPortInt)
  1338  
  1339  			exposedPorts[containerPort+"/tcp"] = struct{}{}
  1340  			exposedPorts[containerPort+"/udp"] = struct{}{}
  1341  			d.logger.Printf("[DEBUG] driver.docker: exposed port %s", containerPort)
  1342  		}
  1343  
  1344  		hostConfig.PortBindings = publishedPorts
  1345  		config.ExposedPorts = exposedPorts
  1346  	}
  1347  
  1348  	parsedArgs := ctx.TaskEnv.ParseAndReplace(driverConfig.Args)
  1349  
  1350  	// If the user specified a custom command to run, we'll inject it here.
  1351  	if driverConfig.Command != "" {
  1352  		// Validate command
  1353  		if err := validateCommand(driverConfig.Command, "args"); err != nil {
  1354  			return c, err
  1355  		}
  1356  
  1357  		cmd := []string{driverConfig.Command}
  1358  		if len(driverConfig.Args) != 0 {
  1359  			cmd = append(cmd, parsedArgs...)
  1360  		}
  1361  		d.logger.Printf("[DEBUG] driver.docker: setting container startup command to: %s", strings.Join(cmd, " "))
  1362  		config.Cmd = cmd
  1363  	} else if len(driverConfig.Args) != 0 {
  1364  		config.Cmd = parsedArgs
  1365  	}
  1366  
  1367  	if len(driverConfig.Labels) > 0 {
  1368  		config.Labels = driverConfig.Labels
  1369  		d.logger.Printf("[DEBUG] driver.docker: applied labels on the container: %+v", config.Labels)
  1370  	}
  1371  
  1372  	config.Env = ctx.TaskEnv.List()
  1373  
  1374  	containerName := fmt.Sprintf("%s-%s", task.Name, d.DriverContext.allocID)
  1375  	d.logger.Printf("[DEBUG] driver.docker: setting container name to: %s", containerName)
  1376  
  1377  	var networkingConfig *docker.NetworkingConfig
  1378  	if len(driverConfig.NetworkAliases) > 0 || driverConfig.IPv4Address != "" || driverConfig.IPv6Address != "" {
  1379  		networkingConfig = &docker.NetworkingConfig{
  1380  			EndpointsConfig: map[string]*docker.EndpointConfig{
  1381  				hostConfig.NetworkMode: {},
  1382  			},
  1383  		}
  1384  	}
  1385  
  1386  	if len(driverConfig.NetworkAliases) > 0 {
  1387  		networkingConfig.EndpointsConfig[hostConfig.NetworkMode].Aliases = driverConfig.NetworkAliases
  1388  		d.logger.Printf("[DEBUG] driver.docker: using network_mode %q with network aliases: %v",
  1389  			hostConfig.NetworkMode, strings.Join(driverConfig.NetworkAliases, ", "))
  1390  	}
  1391  
  1392  	if driverConfig.IPv4Address != "" || driverConfig.IPv6Address != "" {
  1393  		networkingConfig.EndpointsConfig[hostConfig.NetworkMode].IPAMConfig = &docker.EndpointIPAMConfig{
  1394  			IPv4Address: driverConfig.IPv4Address,
  1395  			IPv6Address: driverConfig.IPv6Address,
  1396  		}
  1397  		d.logger.Printf("[DEBUG] driver.docker: using network_mode %q with ipv4: %q and ipv6: %q",
  1398  			hostConfig.NetworkMode, driverConfig.IPv4Address, driverConfig.IPv6Address)
  1399  	}
  1400  
  1401  	if driverConfig.MacAddress != "" {
  1402  		config.MacAddress = driverConfig.MacAddress
  1403  		d.logger.Printf("[DEBUG] driver.docker: using pinned mac address: %q", config.MacAddress)
  1404  	}
  1405  
  1406  	return docker.CreateContainerOptions{
  1407  		Name:             containerName,
  1408  		Config:           config,
  1409  		HostConfig:       hostConfig,
  1410  		NetworkingConfig: networkingConfig,
  1411  	}, nil
  1412  }
  1413  
  1414  func (d *DockerDriver) Periodic() (bool, time.Duration) {
  1415  	return true, 15 * time.Second
  1416  }
  1417  
  1418  // createImage creates a docker image either by pulling it from a registry or by
  1419  // loading it from the file system
  1420  func (d *DockerDriver) createImage(driverConfig *DockerDriverConfig, client *docker.Client, taskDir *allocdir.TaskDir) (string, error) {
  1421  	image := driverConfig.ImageName
  1422  	repo, tag := docker.ParseRepositoryTag(image)
  1423  	if tag == "" {
  1424  		tag = "latest"
  1425  	}
  1426  
  1427  	coordinator, callerID := d.getDockerCoordinator(client)
  1428  
  1429  	// We're going to check whether the image is already downloaded. If the tag
  1430  	// is "latest", or ForcePull is set, we have to check for a new version every time so we don't
  1431  	// bother to check and cache the id here. We'll download first, then cache.
  1432  	if driverConfig.ForcePull {
  1433  		d.logger.Printf("[DEBUG] driver.docker: force pull image '%s:%s' instead of inspecting local", repo, tag)
  1434  	} else if tag != "latest" {
  1435  		if dockerImage, _ := client.InspectImage(image); dockerImage != nil {
  1436  			// Image exists so just increment its reference count
  1437  			coordinator.IncrementImageReference(dockerImage.ID, image, callerID)
  1438  			return dockerImage.ID, nil
  1439  		}
  1440  	}
  1441  
  1442  	// Load the image if specified
  1443  	if driverConfig.LoadImage != "" {
  1444  		return d.loadImage(driverConfig, client, taskDir)
  1445  	}
  1446  
  1447  	// Download the image
  1448  	return d.pullImage(driverConfig, client, repo, tag)
  1449  }
  1450  
  1451  // pullImage creates an image by pulling it from a docker registry
  1452  func (d *DockerDriver) pullImage(driverConfig *DockerDriverConfig, client *docker.Client, repo, tag string) (id string, err error) {
  1453  	authOptions, err := d.resolveRegistryAuthentication(driverConfig, repo)
  1454  	if err != nil {
  1455  		if d.driverConfig.AuthSoftFail {
  1456  			d.logger.Printf("[WARN] Failed to find docker auth for repo %q: %v", repo, err)
  1457  		} else {
  1458  			return "", fmt.Errorf("Failed to find docker auth for repo %q: %v", repo, err)
  1459  		}
  1460  	}
  1461  
  1462  	if authIsEmpty(authOptions) {
  1463  		d.logger.Printf("[DEBUG] driver.docker: did not find docker auth for repo %q", repo)
  1464  	}
  1465  
  1466  	d.emitEvent("Downloading image %s:%s", repo, tag)
  1467  	coordinator, callerID := d.getDockerCoordinator(client)
  1468  	return coordinator.PullImage(driverConfig.ImageName, authOptions, callerID)
  1469  }
  1470  
  1471  // authBackend encapsulates a function that resolves registry credentials.
  1472  type authBackend func(string) (*docker.AuthConfiguration, error)
  1473  
  1474  // resolveRegistryAuthentication attempts to retrieve auth credentials for the
  1475  // repo, trying all authentication-backends possible.
  1476  func (d *DockerDriver) resolveRegistryAuthentication(driverConfig *DockerDriverConfig, repo string) (*docker.AuthConfiguration, error) {
  1477  	return firstValidAuth(repo, []authBackend{
  1478  		authFromTaskConfig(driverConfig),
  1479  		authFromDockerConfig(d.config.Read("docker.auth.config")),
  1480  		authFromHelper(d.config.Read("docker.auth.helper")),
  1481  	})
  1482  }
  1483  
  1484  // loadImage creates an image by loading it from the file system
  1485  func (d *DockerDriver) loadImage(driverConfig *DockerDriverConfig, client *docker.Client,
  1486  	taskDir *allocdir.TaskDir) (id string, err error) {
  1487  
  1488  	archive := filepath.Join(taskDir.LocalDir, driverConfig.LoadImage)
  1489  	d.logger.Printf("[DEBUG] driver.docker: loading image from: %v", archive)
  1490  
  1491  	f, err := os.Open(archive)
  1492  	if err != nil {
  1493  		return "", fmt.Errorf("unable to open image archive: %v", err)
  1494  	}
  1495  
  1496  	if err := client.LoadImage(docker.LoadImageOptions{InputStream: f}); err != nil {
  1497  		return "", err
  1498  	}
  1499  	f.Close()
  1500  
  1501  	dockerImage, err := client.InspectImage(driverConfig.ImageName)
  1502  	if err != nil {
  1503  		return "", recoverableErrTimeouts(err)
  1504  	}
  1505  
  1506  	coordinator, callerID := d.getDockerCoordinator(client)
  1507  	coordinator.IncrementImageReference(dockerImage.ID, driverConfig.ImageName, callerID)
  1508  	return dockerImage.ID, nil
  1509  }
  1510  
  1511  // createContainer creates the container given the passed configuration. It
  1512  // attempts to handle any transient Docker errors.
  1513  func (d *DockerDriver) createContainer(config docker.CreateContainerOptions) (*docker.Container, error) {
  1514  	// Create a container
  1515  	attempted := 0
  1516  CREATE:
  1517  	container, createErr := client.CreateContainer(config)
  1518  	if createErr == nil {
  1519  		return container, nil
  1520  	}
  1521  
  1522  	d.logger.Printf("[DEBUG] driver.docker: failed to create container %q from image %q (ID: %q) (attempt %d): %v",
  1523  		config.Name, d.driverConfig.ImageName, d.imageID, attempted+1, createErr)
  1524  	if strings.Contains(strings.ToLower(createErr.Error()), "container already exists") {
  1525  		containers, err := client.ListContainers(docker.ListContainersOptions{
  1526  			All: true,
  1527  		})
  1528  		if err != nil {
  1529  			d.logger.Printf("[ERR] driver.docker: failed to query list of containers matching name:%s", config.Name)
  1530  			return nil, recoverableErrTimeouts(fmt.Errorf("Failed to query list of containers: %s", err))
  1531  		}
  1532  
  1533  		// Delete matching containers
  1534  		// Adding a / infront of the container name since Docker returns the
  1535  		// container names with a / pre-pended to the Nomad generated container names
  1536  		containerName := "/" + config.Name
  1537  		d.logger.Printf("[DEBUG] driver.docker: searching for container name %q to purge", containerName)
  1538  		for _, shimContainer := range containers {
  1539  			d.logger.Printf("[DEBUG] driver.docker: listed container %+v", shimContainer.Names)
  1540  			found := false
  1541  			for _, name := range shimContainer.Names {
  1542  				if name == containerName {
  1543  					d.logger.Printf("[DEBUG] driver.docker: Found container %v: %v", containerName, shimContainer.ID)
  1544  					found = true
  1545  					break
  1546  				}
  1547  			}
  1548  
  1549  			if !found {
  1550  				continue
  1551  			}
  1552  
  1553  			// Inspect the container and if the container isn't dead then return
  1554  			// the container
  1555  			container, err := client.InspectContainer(shimContainer.ID)
  1556  			if err != nil {
  1557  				err = fmt.Errorf("Failed to inspect container %s: %s", shimContainer.ID, err)
  1558  
  1559  				// This error is always recoverable as it could
  1560  				// be caused by races between listing
  1561  				// containers and this container being removed.
  1562  				// See #2802
  1563  				return nil, structs.NewRecoverableError(err, true)
  1564  			}
  1565  			if container != nil && container.State.Running {
  1566  				return container, nil
  1567  			}
  1568  
  1569  			err = client.RemoveContainer(docker.RemoveContainerOptions{
  1570  				ID:    container.ID,
  1571  				Force: true,
  1572  			})
  1573  			if err != nil {
  1574  				d.logger.Printf("[ERR] driver.docker: failed to purge container %s", container.ID)
  1575  				return nil, recoverableErrTimeouts(fmt.Errorf("Failed to purge container %s: %s", container.ID, err))
  1576  			} else if err == nil {
  1577  				d.logger.Printf("[INFO] driver.docker: purged container %s", container.ID)
  1578  			}
  1579  		}
  1580  
  1581  		if attempted < 5 {
  1582  			attempted++
  1583  			time.Sleep(1 * time.Second)
  1584  			goto CREATE
  1585  		}
  1586  	} else if strings.Contains(strings.ToLower(createErr.Error()), "no such image") {
  1587  		// There is still a very small chance this is possible even with the
  1588  		// coordinator so retry.
  1589  		return nil, structs.NewRecoverableError(createErr, true)
  1590  	}
  1591  
  1592  	return nil, recoverableErrTimeouts(createErr)
  1593  }
  1594  
  1595  // startContainer starts the passed container. It attempts to handle any
  1596  // transient Docker errors.
  1597  func (d *DockerDriver) startContainer(c *docker.Container) error {
  1598  	// Start a container
  1599  	attempted := 0
  1600  START:
  1601  	startErr := client.StartContainer(c.ID, c.HostConfig)
  1602  	if startErr == nil {
  1603  		return nil
  1604  	}
  1605  
  1606  	d.logger.Printf("[DEBUG] driver.docker: failed to start container %q (attempt %d): %v", c.ID, attempted+1, startErr)
  1607  
  1608  	// If it is a 500 error it is likely we can retry and be successful
  1609  	if strings.Contains(startErr.Error(), "API error (500)") {
  1610  		if attempted < 5 {
  1611  			attempted++
  1612  			time.Sleep(1 * time.Second)
  1613  			goto START
  1614  		}
  1615  		return structs.NewRecoverableError(startErr, true)
  1616  	}
  1617  
  1618  	return recoverableErrTimeouts(startErr)
  1619  }
  1620  
  1621  func (d *DockerDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
  1622  	// Split the handle
  1623  	pidBytes := []byte(strings.TrimPrefix(handleID, "DOCKER:"))
  1624  	pid := &dockerPID{}
  1625  	if err := json.Unmarshal(pidBytes, pid); err != nil {
  1626  		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
  1627  	}
  1628  	d.logger.Printf("[INFO] driver.docker: re-attaching to docker process: %s", pid.ContainerID)
  1629  	d.logger.Printf("[DEBUG] driver.docker: re-attached to handle: %s", handleID)
  1630  	pluginConfig := &plugin.ClientConfig{
  1631  		Reattach: pid.PluginConfig.PluginConfig(),
  1632  	}
  1633  
  1634  	client, waitClient, err := d.dockerClients()
  1635  	if err != nil {
  1636  		return nil, fmt.Errorf("Failed to connect to docker daemon: %s", err)
  1637  	}
  1638  
  1639  	// Look for a running container with this ID
  1640  	containers, err := client.ListContainers(docker.ListContainersOptions{
  1641  		Filters: map[string][]string{
  1642  			"id": {pid.ContainerID},
  1643  		},
  1644  	})
  1645  	if err != nil {
  1646  		return nil, fmt.Errorf("Failed to query for container %s: %v", pid.ContainerID, err)
  1647  	}
  1648  
  1649  	found := false
  1650  	for _, container := range containers {
  1651  		if container.ID == pid.ContainerID {
  1652  			found = true
  1653  		}
  1654  	}
  1655  	if !found {
  1656  		return nil, fmt.Errorf("Failed to find container %s", pid.ContainerID)
  1657  	}
  1658  	exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput)
  1659  	if err != nil {
  1660  		d.logger.Printf("[INFO] driver.docker: couldn't re-attach to the plugin process: %v", err)
  1661  		d.logger.Printf("[DEBUG] driver.docker: stopping container %q", pid.ContainerID)
  1662  		if e := client.StopContainer(pid.ContainerID, uint(pid.KillTimeout.Seconds())); e != nil {
  1663  			d.logger.Printf("[DEBUG] driver.docker: couldn't stop container: %v", e)
  1664  		}
  1665  		return nil, err
  1666  	}
  1667  
  1668  	ver, _ := exec.Version()
  1669  	d.logger.Printf("[DEBUG] driver.docker: version of executor: %v", ver.Version)
  1670  
  1671  	// Increment the reference count since we successfully attached to this
  1672  	// container
  1673  	coordinator, callerID := d.getDockerCoordinator(client)
  1674  	coordinator.IncrementImageReference(pid.ImageID, pid.Image, callerID)
  1675  
  1676  	// Return a driver handle
  1677  	h := &DockerHandle{
  1678  		client:         client,
  1679  		waitClient:     waitClient,
  1680  		executor:       exec,
  1681  		pluginClient:   pluginClient,
  1682  		logger:         d.logger,
  1683  		Image:          pid.Image,
  1684  		ImageID:        pid.ImageID,
  1685  		containerID:    pid.ContainerID,
  1686  		version:        pid.Version,
  1687  		killTimeout:    pid.KillTimeout,
  1688  		maxKillTimeout: pid.MaxKillTimeout,
  1689  		doneCh:         make(chan bool),
  1690  		waitCh:         make(chan *dstructs.WaitResult, 1),
  1691  	}
  1692  	go h.collectStats()
  1693  	go h.run()
  1694  	return h, nil
  1695  }
  1696  
  1697  func (h *DockerHandle) ID() string {
  1698  	// Return a handle to the PID
  1699  	pid := dockerPID{
  1700  		Version:        h.version,
  1701  		ContainerID:    h.containerID,
  1702  		Image:          h.Image,
  1703  		ImageID:        h.ImageID,
  1704  		KillTimeout:    h.killTimeout,
  1705  		MaxKillTimeout: h.maxKillTimeout,
  1706  		PluginConfig:   NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
  1707  	}
  1708  	data, err := json.Marshal(pid)
  1709  	if err != nil {
  1710  		h.logger.Printf("[ERR] driver.docker: failed to marshal docker PID to JSON: %s", err)
  1711  	}
  1712  	return fmt.Sprintf("DOCKER:%s", string(data))
  1713  }
  1714  
  1715  func (h *DockerHandle) ContainerID() string {
  1716  	return h.containerID
  1717  }
  1718  
  1719  func (h *DockerHandle) WaitCh() chan *dstructs.WaitResult {
  1720  	return h.waitCh
  1721  }
  1722  
  1723  func (h *DockerHandle) Update(task *structs.Task) error {
  1724  	// Store the updated kill timeout.
  1725  	h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout)
  1726  	if err := h.executor.UpdateTask(task); err != nil {
  1727  		h.logger.Printf("[DEBUG] driver.docker: failed to update log config: %v", err)
  1728  	}
  1729  
  1730  	// Update is not possible
  1731  	return nil
  1732  }
  1733  
  1734  func (h *DockerHandle) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) {
  1735  	fullCmd := make([]string, len(args)+1)
  1736  	fullCmd[0] = cmd
  1737  	copy(fullCmd[1:], args)
  1738  	createExecOpts := docker.CreateExecOptions{
  1739  		AttachStdin:  false,
  1740  		AttachStdout: true,
  1741  		AttachStderr: true,
  1742  		Tty:          false,
  1743  		Cmd:          fullCmd,
  1744  		Container:    h.containerID,
  1745  		Context:      ctx,
  1746  	}
  1747  	exec, err := h.client.CreateExec(createExecOpts)
  1748  	if err != nil {
  1749  		return nil, 0, err
  1750  	}
  1751  
  1752  	output, _ := circbuf.NewBuffer(int64(dstructs.CheckBufSize))
  1753  	startOpts := docker.StartExecOptions{
  1754  		Detach:       false,
  1755  		Tty:          false,
  1756  		OutputStream: output,
  1757  		ErrorStream:  output,
  1758  		Context:      ctx,
  1759  	}
  1760  	if err := client.StartExec(exec.ID, startOpts); err != nil {
  1761  		return nil, 0, err
  1762  	}
  1763  	res, err := client.InspectExec(exec.ID)
  1764  	if err != nil {
  1765  		return output.Bytes(), 0, err
  1766  	}
  1767  	return output.Bytes(), res.ExitCode, nil
  1768  }
  1769  
  1770  func (h *DockerHandle) Signal(s os.Signal) error {
  1771  	// Convert types
  1772  	sysSig, ok := s.(syscall.Signal)
  1773  	if !ok {
  1774  		return fmt.Errorf("Failed to determine signal number")
  1775  	}
  1776  
  1777  	// TODO When we expose signals we will need a mapping layer that converts
  1778  	// MacOS signals to the correct signal number for docker. Or we change the
  1779  	// interface to take a signal string and leave it up to driver to map?
  1780  
  1781  	dockerSignal := docker.Signal(sysSig)
  1782  	opts := docker.KillContainerOptions{
  1783  		ID:     h.containerID,
  1784  		Signal: dockerSignal,
  1785  	}
  1786  	return h.client.KillContainer(opts)
  1787  
  1788  }
  1789  
  1790  // Kill is used to terminate the task. This uses `docker stop -t killTimeout`
  1791  func (h *DockerHandle) Kill() error {
  1792  	// Stop the container
  1793  	err := h.client.StopContainer(h.containerID, uint(h.killTimeout.Seconds()))
  1794  	if err != nil {
  1795  		h.executor.Exit()
  1796  		h.pluginClient.Kill()
  1797  
  1798  		// Container has already been removed.
  1799  		if strings.Contains(err.Error(), NoSuchContainerError) {
  1800  			h.logger.Printf("[DEBUG] driver.docker: attempted to stop non-existent container %s", h.containerID)
  1801  			return nil
  1802  		}
  1803  		h.logger.Printf("[ERR] driver.docker: failed to stop container %s: %v", h.containerID, err)
  1804  		return fmt.Errorf("Failed to stop container %s: %s", h.containerID, err)
  1805  	}
  1806  	h.logger.Printf("[INFO] driver.docker: stopped container %s", h.containerID)
  1807  	return nil
  1808  }
  1809  
  1810  func (h *DockerHandle) Stats() (*cstructs.TaskResourceUsage, error) {
  1811  	h.resourceUsageLock.RLock()
  1812  	defer h.resourceUsageLock.RUnlock()
  1813  	var err error
  1814  	if h.resourceUsage == nil {
  1815  		err = fmt.Errorf("stats collection hasn't started yet")
  1816  	}
  1817  	return h.resourceUsage, err
  1818  }
  1819  
  1820  func (h *DockerHandle) run() {
  1821  	// Wait for it...
  1822  	exitCode, werr := h.waitClient.WaitContainer(h.containerID)
  1823  	if werr != nil {
  1824  		h.logger.Printf("[ERR] driver.docker: failed to wait for %s; container already terminated", h.containerID)
  1825  	}
  1826  
  1827  	if exitCode != 0 {
  1828  		werr = fmt.Errorf("Docker container exited with non-zero exit code: %d", exitCode)
  1829  	}
  1830  
  1831  	container, ierr := h.waitClient.InspectContainer(h.containerID)
  1832  	if ierr != nil {
  1833  		h.logger.Printf("[ERR] driver.docker: failed to inspect container %s: %v", h.containerID, ierr)
  1834  	} else if container.State.OOMKilled {
  1835  		werr = fmt.Errorf("OOM Killed")
  1836  	}
  1837  
  1838  	close(h.doneCh)
  1839  
  1840  	// Shutdown the syslog collector
  1841  	if err := h.executor.Exit(); err != nil {
  1842  		h.logger.Printf("[ERR] driver.docker: failed to kill the syslog collector: %v", err)
  1843  	}
  1844  	h.pluginClient.Kill()
  1845  
  1846  	// Stop the container just incase the docker daemon's wait returned
  1847  	// incorrectly
  1848  	if err := h.client.StopContainer(h.containerID, 0); err != nil {
  1849  		_, noSuchContainer := err.(*docker.NoSuchContainer)
  1850  		_, containerNotRunning := err.(*docker.ContainerNotRunning)
  1851  		if !containerNotRunning && !noSuchContainer {
  1852  			h.logger.Printf("[ERR] driver.docker: error stopping container: %v", err)
  1853  		}
  1854  	}
  1855  
  1856  	// Remove the container
  1857  	if err := h.client.RemoveContainer(docker.RemoveContainerOptions{ID: h.containerID, RemoveVolumes: true, Force: true}); err != nil {
  1858  		h.logger.Printf("[ERR] driver.docker: error removing container: %v", err)
  1859  	}
  1860  
  1861  	// Send the results
  1862  	h.waitCh <- dstructs.NewWaitResult(exitCode, 0, werr)
  1863  	close(h.waitCh)
  1864  }
  1865  
  1866  // collectStats starts collecting resource usage stats of a docker container
  1867  func (h *DockerHandle) collectStats() {
  1868  	statsCh := make(chan *docker.Stats)
  1869  	statsOpts := docker.StatsOptions{ID: h.containerID, Done: h.doneCh, Stats: statsCh, Stream: true}
  1870  	go func() {
  1871  		//TODO handle Stats error
  1872  		if err := h.waitClient.Stats(statsOpts); err != nil {
  1873  			h.logger.Printf("[DEBUG] driver.docker: error collecting stats from container %s: %v", h.containerID, err)
  1874  		}
  1875  	}()
  1876  	numCores := runtime.NumCPU()
  1877  	for {
  1878  		select {
  1879  		case s := <-statsCh:
  1880  			if s != nil {
  1881  				ms := &cstructs.MemoryStats{
  1882  					RSS:      s.MemoryStats.Stats.Rss,
  1883  					Cache:    s.MemoryStats.Stats.Cache,
  1884  					Swap:     s.MemoryStats.Stats.Swap,
  1885  					MaxUsage: s.MemoryStats.MaxUsage,
  1886  					Measured: DockerMeasuredMemStats,
  1887  				}
  1888  
  1889  				cs := &cstructs.CpuStats{
  1890  					ThrottledPeriods: s.CPUStats.ThrottlingData.ThrottledPeriods,
  1891  					ThrottledTime:    s.CPUStats.ThrottlingData.ThrottledTime,
  1892  					Measured:         DockerMeasuredCpuStats,
  1893  				}
  1894  
  1895  				// Calculate percentage
  1896  				cs.Percent = calculatePercent(
  1897  					s.CPUStats.CPUUsage.TotalUsage, s.PreCPUStats.CPUUsage.TotalUsage,
  1898  					s.CPUStats.SystemCPUUsage, s.PreCPUStats.SystemCPUUsage, numCores)
  1899  				cs.SystemMode = calculatePercent(
  1900  					s.CPUStats.CPUUsage.UsageInKernelmode, s.PreCPUStats.CPUUsage.UsageInKernelmode,
  1901  					s.CPUStats.CPUUsage.TotalUsage, s.PreCPUStats.CPUUsage.TotalUsage, numCores)
  1902  				cs.UserMode = calculatePercent(
  1903  					s.CPUStats.CPUUsage.UsageInUsermode, s.PreCPUStats.CPUUsage.UsageInUsermode,
  1904  					s.CPUStats.CPUUsage.TotalUsage, s.PreCPUStats.CPUUsage.TotalUsage, numCores)
  1905  				cs.TotalTicks = (cs.Percent / 100) * shelpers.TotalTicksAvailable() / float64(numCores)
  1906  
  1907  				h.resourceUsageLock.Lock()
  1908  				h.resourceUsage = &cstructs.TaskResourceUsage{
  1909  					ResourceUsage: &cstructs.ResourceUsage{
  1910  						MemoryStats: ms,
  1911  						CpuStats:    cs,
  1912  					},
  1913  					Timestamp: s.Read.UTC().UnixNano(),
  1914  				}
  1915  				h.resourceUsageLock.Unlock()
  1916  			}
  1917  		case <-h.doneCh:
  1918  			return
  1919  		}
  1920  	}
  1921  }
  1922  
  1923  func calculatePercent(newSample, oldSample, newTotal, oldTotal uint64, cores int) float64 {
  1924  	numerator := newSample - oldSample
  1925  	denom := newTotal - oldTotal
  1926  	if numerator <= 0 || denom <= 0 {
  1927  		return 0.0
  1928  	}
  1929  
  1930  	return (float64(numerator) / float64(denom)) * float64(cores) * 100.0
  1931  }
  1932  
  1933  // loadDockerConfig loads the docker config at the specified path, returning an
  1934  // error if it couldn't be read.
  1935  func loadDockerConfig(file string) (*configfile.ConfigFile, error) {
  1936  	f, err := os.Open(file)
  1937  	if err != nil {
  1938  		return nil, fmt.Errorf("Failed to open auth config file: %v, error: %v", file, err)
  1939  	}
  1940  	defer f.Close()
  1941  
  1942  	cfile := new(configfile.ConfigFile)
  1943  	if err = cfile.LoadFromReader(f); err != nil {
  1944  		return nil, fmt.Errorf("Failed to parse auth config file: %v", err)
  1945  	}
  1946  	return cfile, nil
  1947  }
  1948  
  1949  // parseRepositoryInfo takes a repo and returns the Docker RepositoryInfo. This
  1950  // is useful for interacting with a Docker config object.
  1951  func parseRepositoryInfo(repo string) (*registry.RepositoryInfo, error) {
  1952  	name, err := reference.ParseNamed(repo)
  1953  	if err != nil {
  1954  		return nil, fmt.Errorf("Failed to parse named repo %q: %v", repo, err)
  1955  	}
  1956  
  1957  	repoInfo, err := registry.ParseRepositoryInfo(name)
  1958  	if err != nil {
  1959  		return nil, fmt.Errorf("Failed to parse repository: %v", err)
  1960  	}
  1961  
  1962  	return repoInfo, nil
  1963  }
  1964  
  1965  // firstValidAuth tries a list of auth backends, returning first error or AuthConfiguration
  1966  func firstValidAuth(repo string, backends []authBackend) (*docker.AuthConfiguration, error) {
  1967  	for _, backend := range backends {
  1968  		auth, err := backend(repo)
  1969  		if auth != nil || err != nil {
  1970  			return auth, err
  1971  		}
  1972  	}
  1973  	return nil, nil
  1974  }
  1975  
  1976  // authFromTaskConfig generates an authBackend for any auth given in the task-configuration
  1977  func authFromTaskConfig(driverConfig *DockerDriverConfig) authBackend {
  1978  	return func(string) (*docker.AuthConfiguration, error) {
  1979  		if len(driverConfig.Auth) == 0 {
  1980  			return nil, nil
  1981  		}
  1982  		auth := driverConfig.Auth[0]
  1983  		return &docker.AuthConfiguration{
  1984  			Username:      auth.Username,
  1985  			Password:      auth.Password,
  1986  			Email:         auth.Email,
  1987  			ServerAddress: auth.ServerAddress,
  1988  		}, nil
  1989  	}
  1990  }
  1991  
  1992  // authFromDockerConfig generate an authBackend for a dockercfg-compatible file.
  1993  // The authBacken can either be from explicit auth definitions or via credential
  1994  // helpers
  1995  func authFromDockerConfig(file string) authBackend {
  1996  	return func(repo string) (*docker.AuthConfiguration, error) {
  1997  		if file == "" {
  1998  			return nil, nil
  1999  		}
  2000  		repoInfo, err := parseRepositoryInfo(repo)
  2001  		if err != nil {
  2002  			return nil, err
  2003  		}
  2004  
  2005  		cfile, err := loadDockerConfig(file)
  2006  		if err != nil {
  2007  			return nil, err
  2008  		}
  2009  
  2010  		return firstValidAuth(repo, []authBackend{
  2011  			func(string) (*docker.AuthConfiguration, error) {
  2012  				dockerAuthConfig := registry.ResolveAuthConfig(cfile.AuthConfigs, repoInfo.Index)
  2013  				auth := &docker.AuthConfiguration{
  2014  					Username:      dockerAuthConfig.Username,
  2015  					Password:      dockerAuthConfig.Password,
  2016  					Email:         dockerAuthConfig.Email,
  2017  					ServerAddress: dockerAuthConfig.ServerAddress,
  2018  				}
  2019  				if authIsEmpty(auth) {
  2020  					return nil, nil
  2021  				}
  2022  				return auth, nil
  2023  			},
  2024  			authFromHelper(cfile.CredentialHelpers[registry.GetAuthConfigKey(repoInfo.Index)]),
  2025  			authFromHelper(cfile.CredentialsStore),
  2026  		})
  2027  	}
  2028  }
  2029  
  2030  // authFromHelper generates an authBackend for a docker-credentials-helper;
  2031  // A script taking the requested domain on input, outputting JSON with
  2032  // "Username" and "Secret"
  2033  func authFromHelper(helperName string) authBackend {
  2034  	return func(repo string) (*docker.AuthConfiguration, error) {
  2035  		if helperName == "" {
  2036  			return nil, nil
  2037  		}
  2038  		helper := dockerAuthHelperPrefix + helperName
  2039  		cmd := exec.Command(helper, "get")
  2040  
  2041  		// Ensure that the HTTPs prefix exists
  2042  		if !strings.HasPrefix(repo, "https://") {
  2043  			repo = fmt.Sprintf("https://%s", repo)
  2044  		}
  2045  
  2046  		cmd.Stdin = strings.NewReader(repo)
  2047  
  2048  		output, err := cmd.Output()
  2049  		if err != nil {
  2050  			switch err.(type) {
  2051  			default:
  2052  				return nil, err
  2053  			case *exec.ExitError:
  2054  				return nil, fmt.Errorf("%s with input %q failed with stderr: %s", helper, repo, output)
  2055  			}
  2056  		}
  2057  
  2058  		var response map[string]string
  2059  		if err := json.Unmarshal(output, &response); err != nil {
  2060  			return nil, err
  2061  		}
  2062  
  2063  		auth := &docker.AuthConfiguration{
  2064  			Username: response["Username"],
  2065  			Password: response["Secret"],
  2066  		}
  2067  
  2068  		if authIsEmpty(auth) {
  2069  			return nil, nil
  2070  		}
  2071  		return auth, nil
  2072  	}
  2073  }
  2074  
  2075  // authIsEmpty returns if auth is nil or an empty structure
  2076  func authIsEmpty(auth *docker.AuthConfiguration) bool {
  2077  	if auth == nil {
  2078  		return false
  2079  	}
  2080  	return auth.Username == "" &&
  2081  		auth.Password == "" &&
  2082  		auth.Email == "" &&
  2083  		auth.ServerAddress == ""
  2084  }