github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/drivers/docker/config.go (about)

     1  package docker
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime"
     7  	"strconv"
     8  	"strings"
     9  	"time"
    10  
    11  	docker "github.com/fsouza/go-dockerclient"
    12  	"github.com/hashicorp/go-hclog"
    13  	"github.com/hashicorp/nomad/drivers/shared/capabilities"
    14  	"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
    15  	"github.com/hashicorp/nomad/helper/pluginutils/loader"
    16  	"github.com/hashicorp/nomad/plugins/base"
    17  	"github.com/hashicorp/nomad/plugins/drivers"
    18  	"github.com/hashicorp/nomad/plugins/shared/hclspec"
    19  )
    20  
    21  const (
    22  	// NoSuchContainerError is returned by the docker daemon if the container
    23  	// does not exist.
    24  	NoSuchContainerError = "No such container"
    25  
    26  	// ContainerNotRunningError is returned by the docker daemon if the container
    27  	// is not running, yet we requested it to stop
    28  	ContainerNotRunningError = "Container not running"
    29  
    30  	// pluginName is the name of the plugin
    31  	pluginName = "docker"
    32  
    33  	// fingerprintPeriod is the interval at which the driver will send fingerprint responses
    34  	fingerprintPeriod = 30 * time.Second
    35  
    36  	// dockerTimeout is the length of time a request can be outstanding before
    37  	// it is timed out.
    38  	dockerTimeout = 5 * time.Minute
    39  
    40  	// dockerAuthHelperPrefix is the prefix to attach to the credential helper
    41  	// and should be found in the $PATH. Example: ${prefix-}${helper-name}
    42  	dockerAuthHelperPrefix = "docker-credential-"
    43  )
    44  
    45  func PluginLoader(opts map[string]string) (map[string]interface{}, error) {
    46  	conf := map[string]interface{}{}
    47  	if v, ok := opts["docker.endpoint"]; ok {
    48  		conf["endpoint"] = v
    49  	}
    50  
    51  	// dockerd auth
    52  	authConf := map[string]interface{}{}
    53  	if v, ok := opts["docker.auth.config"]; ok {
    54  		authConf["config"] = v
    55  	}
    56  	if v, ok := opts["docker.auth.helper"]; ok {
    57  		authConf["helper"] = v
    58  	}
    59  	conf["auth"] = authConf
    60  
    61  	// dockerd tls
    62  	if _, ok := opts["docker.tls.cert"]; ok {
    63  		conf["tls"] = map[string]interface{}{
    64  			"cert": opts["docker.tls.cert"],
    65  			"key":  opts["docker.tls.key"],
    66  			"ca":   opts["docker.tls.ca"],
    67  		}
    68  	}
    69  
    70  	// garbage collection
    71  	gcConf := map[string]interface{}{}
    72  	if v, err := strconv.ParseBool(opts["docker.cleanup.image"]); err == nil {
    73  		gcConf["image"] = v
    74  	}
    75  	if v, ok := opts["docker.cleanup.image.delay"]; ok {
    76  		gcConf["image_delay"] = v
    77  	}
    78  	if v, err := strconv.ParseBool(opts["docker.cleanup.container"]); err == nil {
    79  		gcConf["container"] = v
    80  	}
    81  	conf["gc"] = gcConf
    82  
    83  	// volume options
    84  	volConf := map[string]interface{}{}
    85  	if v, err := strconv.ParseBool(opts["docker.volumes.enabled"]); err == nil {
    86  		volConf["enabled"] = v
    87  	}
    88  	if v, ok := opts["docker.volumes.selinuxlabel"]; ok {
    89  		volConf["selinuxlabel"] = v
    90  	}
    91  	conf["volumes"] = volConf
    92  
    93  	// capabilities
    94  	// COMPAT(1.0) uses inclusive language. whitelist is used for backward compatibility.
    95  	if v, ok := opts["docker.caps.allowlist"]; ok {
    96  		conf["allow_caps"] = strings.Split(v, ",")
    97  	} else if v, ok := opts["docker.caps.whitelist"]; ok {
    98  		conf["allow_caps"] = strings.Split(v, ",")
    99  	}
   100  
   101  	// privileged containers
   102  	if v, err := strconv.ParseBool(opts["docker.privileged.enabled"]); err == nil {
   103  		conf["allow_privileged"] = v
   104  	}
   105  
   106  	// nvidia_runtime
   107  	if v, ok := opts["docker.nvidia_runtime"]; ok {
   108  		conf["nvidia_runtime"] = v
   109  	}
   110  
   111  	return conf, nil
   112  }
   113  
   114  var (
   115  	// PluginID is the docker plugin metadata registered in the plugin catalog.
   116  	PluginID = loader.PluginID{
   117  		Name:       pluginName,
   118  		PluginType: base.PluginTypeDriver,
   119  	}
   120  
   121  	// PluginConfig is the docker config factory function registered in the plugin catalog.
   122  	PluginConfig = &loader.InternalPluginConfig{
   123  		Config:  map[string]interface{}{},
   124  		Factory: func(ctx context.Context, l hclog.Logger) interface{} { return NewDockerDriver(ctx, l) },
   125  	}
   126  
   127  	// pluginInfo is the response returned for the PluginInfo RPC.
   128  	pluginInfo = &base.PluginInfoResponse{
   129  		Type:              base.PluginTypeDriver,
   130  		PluginApiVersions: []string{drivers.ApiVersion010},
   131  		PluginVersion:     "0.1.0",
   132  		Name:              pluginName,
   133  	}
   134  
   135  	danglingContainersBlock = hclspec.NewObject(map[string]*hclspec.Spec{
   136  		"enabled": hclspec.NewDefault(
   137  			hclspec.NewAttr("enabled", "bool", false),
   138  			hclspec.NewLiteral(`true`),
   139  		),
   140  		"period": hclspec.NewDefault(
   141  			hclspec.NewAttr("period", "string", false),
   142  			hclspec.NewLiteral(`"5m"`),
   143  		),
   144  		"creation_grace": hclspec.NewDefault(
   145  			hclspec.NewAttr("creation_grace", "string", false),
   146  			hclspec.NewLiteral(`"5m"`),
   147  		),
   148  		"dry_run": hclspec.NewDefault(
   149  			hclspec.NewAttr("dry_run", "bool", false),
   150  			hclspec.NewLiteral(`false`),
   151  		),
   152  	})
   153  
   154  	// configSpec is the hcl specification returned by the ConfigSchema RPC
   155  	// and is used to parse the contents of the 'plugin "docker" {...}' block.
   156  	// Example:
   157  	//	plugin "docker" {
   158  	//		config {
   159  	//		endpoint = "unix:///var/run/docker.sock"
   160  	//		auth {
   161  	//			config = "/etc/docker-auth.json"
   162  	//			helper = "docker-credential-aws"
   163  	//		}
   164  	//		tls {
   165  	//			cert = "/etc/nomad/nomad.pub"
   166  	//			key = "/etc/nomad/nomad.pem"
   167  	//			ca = "/etc/nomad/nomad.cert"
   168  	//		}
   169  	//		gc {
   170  	//			image = true
   171  	//			image_delay = "5m"
   172  	//			container = false
   173  	//		}
   174  	//		volumes {
   175  	//			enabled = true
   176  	//			selinuxlabel = "z"
   177  	//		}
   178  	//		allow_privileged = false
   179  	//		allow_caps = ["CHOWN", "NET_RAW" ... ]
   180  	//		nvidia_runtime = "nvidia"
   181  	//		}
   182  	//	}
   183  	configSpec = hclspec.NewObject(map[string]*hclspec.Spec{
   184  		"endpoint": hclspec.NewAttr("endpoint", "string", false),
   185  
   186  		// docker daemon auth option for image registry
   187  		"auth": hclspec.NewBlock("auth", false, hclspec.NewObject(map[string]*hclspec.Spec{
   188  			"config": hclspec.NewAttr("config", "string", false),
   189  			"helper": hclspec.NewAttr("helper", "string", false),
   190  		})),
   191  
   192  		// client tls options
   193  		"tls": hclspec.NewBlock("tls", false, hclspec.NewObject(map[string]*hclspec.Spec{
   194  			"cert": hclspec.NewAttr("cert", "string", false),
   195  			"key":  hclspec.NewAttr("key", "string", false),
   196  			"ca":   hclspec.NewAttr("ca", "string", false),
   197  		})),
   198  
   199  		// extra docker labels, globs supported
   200  		"extra_labels": hclspec.NewAttr("extra_labels", "list(string)", false),
   201  
   202  		// logging options
   203  		"logging": hclspec.NewDefault(hclspec.NewBlock("logging", false, hclspec.NewObject(map[string]*hclspec.Spec{
   204  			"type":   hclspec.NewAttr("type", "string", false),
   205  			"config": hclspec.NewBlockAttrs("config", "string", false),
   206  		})), hclspec.NewLiteral(`{
   207  			type = "json-file" 
   208  			config = {
   209  				max-file = "2"
   210  				max-size = "2m"
   211  			}
   212  		}`)),
   213  
   214  		// garbage collection options
   215  		// default needed for both if the gc {...} block is not set and
   216  		// if the default fields are missing
   217  		"gc": hclspec.NewDefault(hclspec.NewBlock("gc", false, hclspec.NewObject(map[string]*hclspec.Spec{
   218  			"image": hclspec.NewDefault(
   219  				hclspec.NewAttr("image", "bool", false),
   220  				hclspec.NewLiteral("true"),
   221  			),
   222  			"image_delay": hclspec.NewDefault(
   223  				hclspec.NewAttr("image_delay", "string", false),
   224  				hclspec.NewLiteral("\"3m\""),
   225  			),
   226  			"container": hclspec.NewDefault(
   227  				hclspec.NewAttr("container", "bool", false),
   228  				hclspec.NewLiteral("true"),
   229  			),
   230  			"dangling_containers": hclspec.NewDefault(
   231  				hclspec.NewBlock("dangling_containers", false, danglingContainersBlock),
   232  				hclspec.NewLiteral(`{
   233  					enabled = true
   234  					period = "5m"
   235  					creation_grace = "5m"
   236  				}`),
   237  			),
   238  		})), hclspec.NewLiteral(`{
   239  			image = true
   240  			image_delay = "3m"
   241  			container = true
   242  			dangling_containers = {
   243  				enabled = true
   244  				period = "5m"
   245  				creation_grace = "5m"
   246  			}
   247  		}`)),
   248  
   249  		// docker volume options
   250  		// defaulted needed for both if the volumes {...} block is not set and
   251  		// if the default fields are missing
   252  		"volumes": hclspec.NewDefault(hclspec.NewBlock("volumes", false, hclspec.NewObject(map[string]*hclspec.Spec{
   253  			"enabled":      hclspec.NewAttr("enabled", "bool", false),
   254  			"selinuxlabel": hclspec.NewAttr("selinuxlabel", "string", false),
   255  		})), hclspec.NewLiteral("{ enabled = false }")),
   256  		"allow_privileged": hclspec.NewAttr("allow_privileged", "bool", false),
   257  		"allow_caps": hclspec.NewDefault(
   258  			hclspec.NewAttr("allow_caps", "list(string)", false),
   259  			hclspec.NewLiteral(capabilities.HCLSpecLiteral),
   260  		),
   261  		"nvidia_runtime": hclspec.NewDefault(
   262  			hclspec.NewAttr("nvidia_runtime", "string", false),
   263  			hclspec.NewLiteral(`"nvidia"`),
   264  		),
   265  		// list of docker runtimes allowed to be used
   266  		"allow_runtimes": hclspec.NewDefault(
   267  			hclspec.NewAttr("allow_runtimes", "list(string)", false),
   268  			hclspec.NewLiteral(`["runc", "nvidia"]`),
   269  		),
   270  		// image to use when creating a network namespace parent container
   271  		"infra_image": hclspec.NewDefault(
   272  			hclspec.NewAttr("infra_image", "string", false),
   273  			hclspec.NewLiteral(fmt.Sprintf(
   274  				`"gcr.io/google_containers/pause-%s:3.1"`,
   275  				runtime.GOARCH,
   276  			)),
   277  		),
   278  		// timeout to use when pulling the infra image.
   279  		"infra_image_pull_timeout": hclspec.NewDefault(
   280  			hclspec.NewAttr("infra_image_pull_timeout", "string", false),
   281  			hclspec.NewLiteral(`"5m"`),
   282  		),
   283  
   284  		// the duration that the driver will wait for activity from the Docker engine during an image pull
   285  		// before canceling the request
   286  		"pull_activity_timeout": hclspec.NewDefault(
   287  			hclspec.NewAttr("pull_activity_timeout", "string", false),
   288  			hclspec.NewLiteral(`"2m"`),
   289  		),
   290  		"pids_limit": hclspec.NewAttr("pids_limit", "number", false),
   291  		// disable_log_collection indicates whether docker driver should collect logs of docker
   292  		// task containers.  If true, nomad doesn't start docker_logger/logmon processes
   293  		"disable_log_collection": hclspec.NewAttr("disable_log_collection", "bool", false),
   294  	})
   295  
   296  	// mountBodySpec is the hcl specification for the `mount` block
   297  	mountBodySpec = hclspec.NewObject(map[string]*hclspec.Spec{
   298  		"type": hclspec.NewDefault(
   299  			hclspec.NewAttr("type", "string", false),
   300  			hclspec.NewLiteral("\"volume\""),
   301  		),
   302  		"target":   hclspec.NewAttr("target", "string", false),
   303  		"source":   hclspec.NewAttr("source", "string", false),
   304  		"readonly": hclspec.NewAttr("readonly", "bool", false),
   305  		"bind_options": hclspec.NewBlock("bind_options", false, hclspec.NewObject(map[string]*hclspec.Spec{
   306  			"propagation": hclspec.NewAttr("propagation", "string", false),
   307  		})),
   308  		"tmpfs_options": hclspec.NewBlock("tmpfs_options", false, hclspec.NewObject(map[string]*hclspec.Spec{
   309  			"size": hclspec.NewAttr("size", "number", false),
   310  			"mode": hclspec.NewAttr("mode", "number", false),
   311  		})),
   312  		"volume_options": hclspec.NewBlock("volume_options", false, hclspec.NewObject(map[string]*hclspec.Spec{
   313  			"no_copy": hclspec.NewAttr("no_copy", "bool", false),
   314  			"labels":  hclspec.NewAttr("labels", "list(map(string))", false),
   315  			"driver_config": hclspec.NewBlock("driver_config", false, hclspec.NewObject(map[string]*hclspec.Spec{
   316  				"name":    hclspec.NewAttr("name", "string", false),
   317  				"options": hclspec.NewAttr("options", "list(map(string))", false),
   318  			})),
   319  		})),
   320  	})
   321  
   322  	// healthchecksBodySpec is the hcl specification for the `healthchecks` block
   323  	healthchecksBodySpec = hclspec.NewObject(map[string]*hclspec.Spec{
   324  		"disable": hclspec.NewAttr("disable", "bool", false),
   325  	})
   326  
   327  	// taskConfigSpec is the hcl specification for the driver config section of
   328  	// a task within a job. It is returned in the TaskConfigSchema RPC
   329  	taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{
   330  		"image":                  hclspec.NewAttr("image", "string", true),
   331  		"advertise_ipv6_address": hclspec.NewAttr("advertise_ipv6_address", "bool", false),
   332  		"args":                   hclspec.NewAttr("args", "list(string)", false),
   333  		"auth": hclspec.NewBlock("auth", false, hclspec.NewObject(map[string]*hclspec.Spec{
   334  			"username":       hclspec.NewAttr("username", "string", false),
   335  			"password":       hclspec.NewAttr("password", "string", false),
   336  			"email":          hclspec.NewAttr("email", "string", false),
   337  			"server_address": hclspec.NewAttr("server_address", "string", false),
   338  		})),
   339  		"auth_soft_fail": hclspec.NewAttr("auth_soft_fail", "bool", false),
   340  		"cap_add":        hclspec.NewAttr("cap_add", "list(string)", false),
   341  		"cap_drop":       hclspec.NewAttr("cap_drop", "list(string)", false),
   342  		"command":        hclspec.NewAttr("command", "string", false),
   343  		"cpuset_cpus":    hclspec.NewAttr("cpuset_cpus", "string", false),
   344  		"cpu_hard_limit": hclspec.NewAttr("cpu_hard_limit", "bool", false),
   345  		"cpu_cfs_period": hclspec.NewDefault(
   346  			hclspec.NewAttr("cpu_cfs_period", "number", false),
   347  			hclspec.NewLiteral(`100000`),
   348  		),
   349  		"devices": hclspec.NewBlockList("devices", hclspec.NewObject(map[string]*hclspec.Spec{
   350  			"host_path":          hclspec.NewAttr("host_path", "string", false),
   351  			"container_path":     hclspec.NewAttr("container_path", "string", false),
   352  			"cgroup_permissions": hclspec.NewAttr("cgroup_permissions", "string", false),
   353  		})),
   354  		"dns_search_domains": hclspec.NewAttr("dns_search_domains", "list(string)", false),
   355  		"dns_options":        hclspec.NewAttr("dns_options", "list(string)", false),
   356  		"dns_servers":        hclspec.NewAttr("dns_servers", "list(string)", false),
   357  		"entrypoint":         hclspec.NewAttr("entrypoint", "list(string)", false),
   358  		"extra_hosts":        hclspec.NewAttr("extra_hosts", "list(string)", false),
   359  		"force_pull":         hclspec.NewAttr("force_pull", "bool", false),
   360  		"healthchecks":       hclspec.NewBlock("healthchecks", false, healthchecksBodySpec),
   361  		"hostname":           hclspec.NewAttr("hostname", "string", false),
   362  		"init":               hclspec.NewAttr("init", "bool", false),
   363  		"interactive":        hclspec.NewAttr("interactive", "bool", false),
   364  		"ipc_mode":           hclspec.NewAttr("ipc_mode", "string", false),
   365  		"ipv4_address":       hclspec.NewAttr("ipv4_address", "string", false),
   366  		"ipv6_address":       hclspec.NewAttr("ipv6_address", "string", false),
   367  		"labels":             hclspec.NewAttr("labels", "list(map(string))", false),
   368  		"load":               hclspec.NewAttr("load", "string", false),
   369  		"logging": hclspec.NewBlock("logging", false, hclspec.NewObject(map[string]*hclspec.Spec{
   370  			"type":   hclspec.NewAttr("type", "string", false),
   371  			"driver": hclspec.NewAttr("driver", "string", false),
   372  			"config": hclspec.NewAttr("config", "list(map(string))", false),
   373  		})),
   374  		"mac_address":       hclspec.NewAttr("mac_address", "string", false),
   375  		"memory_hard_limit": hclspec.NewAttr("memory_hard_limit", "number", false),
   376  		// mount and mounts are effectively aliases, but `mounts` is meant for pre-1.0
   377  		// assignment syntax `mounts = [{type="..." ..."}]` while
   378  		// `mount` is 1.0 repeated block syntax `mount { type = "..." }`
   379  		"mount":           hclspec.NewBlockList("mount", mountBodySpec),
   380  		"mounts":          hclspec.NewBlockList("mounts", mountBodySpec),
   381  		"network_aliases": hclspec.NewAttr("network_aliases", "list(string)", false),
   382  		"network_mode":    hclspec.NewAttr("network_mode", "string", false),
   383  		"runtime":         hclspec.NewAttr("runtime", "string", false),
   384  		"pids_limit":      hclspec.NewAttr("pids_limit", "number", false),
   385  		"pid_mode":        hclspec.NewAttr("pid_mode", "string", false),
   386  		"ports":           hclspec.NewAttr("ports", "list(string)", false),
   387  		"port_map":        hclspec.NewAttr("port_map", "list(map(number))", false),
   388  		"privileged":      hclspec.NewAttr("privileged", "bool", false),
   389  		"image_pull_timeout": hclspec.NewDefault(
   390  			hclspec.NewAttr("image_pull_timeout", "string", false),
   391  			hclspec.NewLiteral(`"5m"`),
   392  		),
   393  		"readonly_rootfs": hclspec.NewAttr("readonly_rootfs", "bool", false),
   394  		"security_opt":    hclspec.NewAttr("security_opt", "list(string)", false),
   395  		"shm_size":        hclspec.NewAttr("shm_size", "number", false),
   396  		"storage_opt":     hclspec.NewBlockAttrs("storage_opt", "string", false),
   397  		"sysctl":          hclspec.NewAttr("sysctl", "list(map(string))", false),
   398  		"tty":             hclspec.NewAttr("tty", "bool", false),
   399  		"ulimit":          hclspec.NewAttr("ulimit", "list(map(string))", false),
   400  		"uts_mode":        hclspec.NewAttr("uts_mode", "string", false),
   401  		"userns_mode":     hclspec.NewAttr("userns_mode", "string", false),
   402  		"volumes":         hclspec.NewAttr("volumes", "list(string)", false),
   403  		"volume_driver":   hclspec.NewAttr("volume_driver", "string", false),
   404  		"work_dir":        hclspec.NewAttr("work_dir", "string", false),
   405  	})
   406  
   407  	// driverCapabilities represents the RPC response for what features are
   408  	// implemented by the docker task driver
   409  	driverCapabilities = &drivers.Capabilities{
   410  		SendSignals: true,
   411  		Exec:        true,
   412  		FSIsolation: drivers.FSIsolationImage,
   413  		NetIsolationModes: []drivers.NetIsolationMode{
   414  			drivers.NetIsolationModeHost,
   415  			drivers.NetIsolationModeGroup,
   416  			drivers.NetIsolationModeTask,
   417  		},
   418  		MustInitiateNetwork: true,
   419  		MountConfigs:        drivers.MountConfigSupportAll,
   420  	}
   421  )
   422  
   423  type TaskConfig struct {
   424  	Image             string             `codec:"image"`
   425  	AdvertiseIPv6Addr bool               `codec:"advertise_ipv6_address"`
   426  	Args              []string           `codec:"args"`
   427  	Auth              DockerAuth         `codec:"auth"`
   428  	AuthSoftFail      bool               `codec:"auth_soft_fail"`
   429  	CapAdd            []string           `codec:"cap_add"`
   430  	CapDrop           []string           `codec:"cap_drop"`
   431  	Command           string             `codec:"command"`
   432  	CPUCFSPeriod      int64              `codec:"cpu_cfs_period"`
   433  	CPUHardLimit      bool               `codec:"cpu_hard_limit"`
   434  	CPUSetCPUs        string             `codec:"cpuset_cpus"`
   435  	Devices           []DockerDevice     `codec:"devices"`
   436  	DNSSearchDomains  []string           `codec:"dns_search_domains"`
   437  	DNSOptions        []string           `codec:"dns_options"`
   438  	DNSServers        []string           `codec:"dns_servers"`
   439  	Entrypoint        []string           `codec:"entrypoint"`
   440  	ExtraHosts        []string           `codec:"extra_hosts"`
   441  	ForcePull         bool               `codec:"force_pull"`
   442  	Healthchecks      DockerHealthchecks `codec:"healthchecks"`
   443  	Hostname          string             `codec:"hostname"`
   444  	Init              bool               `codec:"init"`
   445  	Interactive       bool               `codec:"interactive"`
   446  	IPCMode           string             `codec:"ipc_mode"`
   447  	IPv4Address       string             `codec:"ipv4_address"`
   448  	IPv6Address       string             `codec:"ipv6_address"`
   449  	Labels            hclutils.MapStrStr `codec:"labels"`
   450  	LoadImage         string             `codec:"load"`
   451  	Logging           DockerLogging      `codec:"logging"`
   452  	MacAddress        string             `codec:"mac_address"`
   453  	MemoryHardLimit   int64              `codec:"memory_hard_limit"`
   454  	Mounts            []DockerMount      `codec:"mount"`
   455  	NetworkAliases    []string           `codec:"network_aliases"`
   456  	NetworkMode       string             `codec:"network_mode"`
   457  	Runtime           string             `codec:"runtime"`
   458  	PidsLimit         int64              `codec:"pids_limit"`
   459  	PidMode           string             `codec:"pid_mode"`
   460  	Ports             []string           `codec:"ports"`
   461  	PortMap           hclutils.MapStrInt `codec:"port_map"`
   462  	Privileged        bool               `codec:"privileged"`
   463  	ImagePullTimeout  string             `codec:"image_pull_timeout"`
   464  	ReadonlyRootfs    bool               `codec:"readonly_rootfs"`
   465  	SecurityOpt       []string           `codec:"security_opt"`
   466  	ShmSize           int64              `codec:"shm_size"`
   467  	StorageOpt        map[string]string  `codec:"storage_opt"`
   468  	Sysctl            hclutils.MapStrStr `codec:"sysctl"`
   469  	TTY               bool               `codec:"tty"`
   470  	Ulimit            hclutils.MapStrStr `codec:"ulimit"`
   471  	UTSMode           string             `codec:"uts_mode"`
   472  	UsernsMode        string             `codec:"userns_mode"`
   473  	Volumes           []string           `codec:"volumes"`
   474  	VolumeDriver      string             `codec:"volume_driver"`
   475  	WorkDir           string             `codec:"work_dir"`
   476  
   477  	// MountsList supports the pre-1.0 mounts array syntax
   478  	MountsList []DockerMount `codec:"mounts"`
   479  }
   480  
   481  type DockerAuth struct {
   482  	Username   string `codec:"username"`
   483  	Password   string `codec:"password"`
   484  	Email      string `codec:"email"`
   485  	ServerAddr string `codec:"server_address"`
   486  }
   487  
   488  type DockerDevice struct {
   489  	HostPath          string `codec:"host_path"`
   490  	ContainerPath     string `codec:"container_path"`
   491  	CgroupPermissions string `codec:"cgroup_permissions"`
   492  }
   493  
   494  func (d DockerDevice) toDockerDevice() (docker.Device, error) {
   495  	dd := docker.Device{
   496  		PathOnHost:        d.HostPath,
   497  		PathInContainer:   d.ContainerPath,
   498  		CgroupPermissions: d.CgroupPermissions,
   499  	}
   500  
   501  	if d.HostPath == "" {
   502  		return dd, fmt.Errorf("host path must be set in configuration for devices")
   503  	}
   504  
   505  	if dd.CgroupPermissions == "" {
   506  		dd.CgroupPermissions = "rwm"
   507  	}
   508  
   509  	if !validateCgroupPermission(dd.CgroupPermissions) {
   510  		return dd, fmt.Errorf("invalid cgroup permission string: %q", dd.CgroupPermissions)
   511  	}
   512  
   513  	return dd, nil
   514  }
   515  
   516  type DockerLogging struct {
   517  	Type   string             `codec:"type"`
   518  	Driver string             `codec:"driver"`
   519  	Config hclutils.MapStrStr `codec:"config"`
   520  }
   521  
   522  type DockerHealthchecks struct {
   523  	Disable bool `codec:"disable"`
   524  }
   525  
   526  func (dh *DockerHealthchecks) Disabled() bool {
   527  	return dh == nil || dh.Disable
   528  }
   529  
   530  type DockerMount struct {
   531  	Type          string              `codec:"type"`
   532  	Target        string              `codec:"target"`
   533  	Source        string              `codec:"source"`
   534  	ReadOnly      bool                `codec:"readonly"`
   535  	BindOptions   DockerBindOptions   `codec:"bind_options"`
   536  	VolumeOptions DockerVolumeOptions `codec:"volume_options"`
   537  	TmpfsOptions  DockerTmpfsOptions  `codec:"tmpfs_options"`
   538  }
   539  
   540  func (m DockerMount) toDockerHostMount() (docker.HostMount, error) {
   541  	if m.Type == "" {
   542  		// for backward compatibility, as type is optional
   543  		m.Type = "volume"
   544  	}
   545  
   546  	hm := docker.HostMount{
   547  		Target:   m.Target,
   548  		Source:   m.Source,
   549  		Type:     m.Type,
   550  		ReadOnly: m.ReadOnly,
   551  	}
   552  
   553  	switch m.Type {
   554  	case "volume":
   555  		vo := m.VolumeOptions
   556  		hm.VolumeOptions = &docker.VolumeOptions{
   557  			NoCopy: vo.NoCopy,
   558  			Labels: vo.Labels,
   559  			DriverConfig: docker.VolumeDriverConfig{
   560  				Name:    vo.DriverConfig.Name,
   561  				Options: vo.DriverConfig.Options,
   562  			},
   563  		}
   564  	case "bind":
   565  		hm.BindOptions = &docker.BindOptions{
   566  			Propagation: m.BindOptions.Propagation,
   567  		}
   568  	case "tmpfs":
   569  		if m.Source != "" {
   570  			return hm, fmt.Errorf(`invalid source, must be "" for tmpfs`)
   571  		}
   572  		hm.TempfsOptions = &docker.TempfsOptions{
   573  			SizeBytes: m.TmpfsOptions.SizeBytes,
   574  			Mode:      m.TmpfsOptions.Mode,
   575  		}
   576  	default:
   577  		return hm, fmt.Errorf(`invalid mount type, must be "bind", "volume", "tmpfs": %q`, m.Type)
   578  	}
   579  
   580  	return hm, nil
   581  }
   582  
   583  type DockerVolumeOptions struct {
   584  	NoCopy       bool                     `codec:"no_copy"`
   585  	Labels       hclutils.MapStrStr       `codec:"labels"`
   586  	DriverConfig DockerVolumeDriverConfig `codec:"driver_config"`
   587  }
   588  
   589  type DockerBindOptions struct {
   590  	Propagation string `codec:"propagation"`
   591  }
   592  
   593  type DockerTmpfsOptions struct {
   594  	SizeBytes int64 `codec:"size"`
   595  	Mode      int   `codec:"mode"`
   596  }
   597  
   598  // DockerVolumeDriverConfig holds a map of volume driver specific options
   599  type DockerVolumeDriverConfig struct {
   600  	Name    string             `codec:"name"`
   601  	Options hclutils.MapStrStr `codec:"options"`
   602  }
   603  
   604  // ContainerGCConfig controls the behavior of the GC reconciler to detects
   605  // dangling nomad containers that aren't tracked due to docker/nomad bugs
   606  type ContainerGCConfig struct {
   607  	// Enabled controls whether container reconciler is enabled
   608  	Enabled bool `codec:"enabled"`
   609  
   610  	// DryRun indicates that reconciler should log unexpectedly running containers
   611  	// if found without actually killing them
   612  	DryRun bool `codec:"dry_run"`
   613  
   614  	// PeriodStr controls the frequency of scanning containers
   615  	PeriodStr string        `codec:"period"`
   616  	period    time.Duration `codec:"-"`
   617  
   618  	// CreationGraceStr is the duration allowed for a newly created container
   619  	// to live without being registered as a running task in nomad.
   620  	// A container is treated as leaked if it lived more than grace duration
   621  	// and haven't been registered in tasks.
   622  	CreationGraceStr string        `codec:"creation_grace"`
   623  	CreationGrace    time.Duration `codec:"-"`
   624  }
   625  
   626  type DriverConfig struct {
   627  	Endpoint                      string        `codec:"endpoint"`
   628  	Auth                          AuthConfig    `codec:"auth"`
   629  	TLS                           TLSConfig     `codec:"tls"`
   630  	GC                            GCConfig      `codec:"gc"`
   631  	Volumes                       VolumeConfig  `codec:"volumes"`
   632  	AllowPrivileged               bool          `codec:"allow_privileged"`
   633  	AllowCaps                     []string      `codec:"allow_caps"`
   634  	GPURuntimeName                string        `codec:"nvidia_runtime"`
   635  	InfraImage                    string        `codec:"infra_image"`
   636  	InfraImagePullTimeout         string        `codec:"infra_image_pull_timeout"`
   637  	infraImagePullTimeoutDuration time.Duration `codec:"-"`
   638  	DisableLogCollection          bool          `codec:"disable_log_collection"`
   639  	PullActivityTimeout           string        `codec:"pull_activity_timeout"`
   640  	PidsLimit                     int64         `codec:"pids_limit"`
   641  	pullActivityTimeoutDuration   time.Duration `codec:"-"`
   642  	ExtraLabels                   []string      `codec:"extra_labels"`
   643  	Logging                       LoggingConfig `codec:"logging"`
   644  
   645  	AllowRuntimesList []string            `codec:"allow_runtimes"`
   646  	allowRuntimes     map[string]struct{} `codec:"-"`
   647  }
   648  
   649  type AuthConfig struct {
   650  	Config string `codec:"config"`
   651  	Helper string `codec:"helper"`
   652  }
   653  
   654  type TLSConfig struct {
   655  	Cert string `codec:"cert"`
   656  	Key  string `codec:"key"`
   657  	CA   string `codec:"ca"`
   658  }
   659  
   660  type GCConfig struct {
   661  	Image              bool          `codec:"image"`
   662  	ImageDelay         string        `codec:"image_delay"`
   663  	imageDelayDuration time.Duration `codec:"-"`
   664  	Container          bool          `codec:"container"`
   665  
   666  	DanglingContainers ContainerGCConfig `codec:"dangling_containers"`
   667  }
   668  
   669  type VolumeConfig struct {
   670  	Enabled      bool   `codec:"enabled"`
   671  	SelinuxLabel string `codec:"selinuxlabel"`
   672  }
   673  
   674  type LoggingConfig struct {
   675  	Type   string            `codec:"type"`
   676  	Config map[string]string `codec:"config"`
   677  }
   678  
   679  func (d *Driver) PluginInfo() (*base.PluginInfoResponse, error) {
   680  	return pluginInfo, nil
   681  }
   682  
   683  func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
   684  	return configSpec, nil
   685  }
   686  
   687  const danglingContainersCreationGraceMinimum = 1 * time.Minute
   688  const pullActivityTimeoutMinimum = 1 * time.Minute
   689  
   690  func (d *Driver) SetConfig(c *base.Config) error {
   691  	var config DriverConfig
   692  	if len(c.PluginConfig) != 0 {
   693  		if err := base.MsgPackDecode(c.PluginConfig, &config); err != nil {
   694  			return err
   695  		}
   696  	}
   697  
   698  	d.config = &config
   699  	d.config.InfraImage = strings.TrimPrefix(d.config.InfraImage, "https://")
   700  
   701  	if len(d.config.GC.ImageDelay) > 0 {
   702  		dur, err := time.ParseDuration(d.config.GC.ImageDelay)
   703  		if err != nil {
   704  			return fmt.Errorf("failed to parse 'image_delay' duration: %v", err)
   705  		}
   706  		d.config.GC.imageDelayDuration = dur
   707  	}
   708  
   709  	if len(d.config.GC.DanglingContainers.PeriodStr) > 0 {
   710  		dur, err := time.ParseDuration(d.config.GC.DanglingContainers.PeriodStr)
   711  		if err != nil {
   712  			return fmt.Errorf("failed to parse 'period' duration: %v", err)
   713  		}
   714  		d.config.GC.DanglingContainers.period = dur
   715  	}
   716  
   717  	if len(d.config.GC.DanglingContainers.CreationGraceStr) > 0 {
   718  		dur, err := time.ParseDuration(d.config.GC.DanglingContainers.CreationGraceStr)
   719  		if err != nil {
   720  			return fmt.Errorf("failed to parse 'creation_grace' duration: %v", err)
   721  		}
   722  		if dur < danglingContainersCreationGraceMinimum {
   723  			return fmt.Errorf("creation_grace is less than minimum, %v", danglingContainersCreationGraceMinimum)
   724  		}
   725  		d.config.GC.DanglingContainers.CreationGrace = dur
   726  	}
   727  
   728  	if len(d.config.PullActivityTimeout) > 0 {
   729  		dur, err := time.ParseDuration(d.config.PullActivityTimeout)
   730  		if err != nil {
   731  			return fmt.Errorf("failed to parse 'pull_activity_timeout' duaration: %v", err)
   732  		}
   733  		if dur < pullActivityTimeoutMinimum {
   734  			return fmt.Errorf("pull_activity_timeout is less than minimum, %v", pullActivityTimeoutMinimum)
   735  		}
   736  		d.config.pullActivityTimeoutDuration = dur
   737  	}
   738  
   739  	if d.config.InfraImagePullTimeout != "" {
   740  		dur, err := time.ParseDuration(d.config.InfraImagePullTimeout)
   741  		if err != nil {
   742  			return fmt.Errorf("failed to parse 'infra_image_pull_timeout' duaration: %v", err)
   743  		}
   744  		d.config.infraImagePullTimeoutDuration = dur
   745  	}
   746  
   747  	d.config.allowRuntimes = make(map[string]struct{}, len(d.config.AllowRuntimesList))
   748  	for _, r := range d.config.AllowRuntimesList {
   749  		d.config.allowRuntimes[r] = struct{}{}
   750  	}
   751  
   752  	if c.AgentConfig != nil {
   753  		d.clientConfig = c.AgentConfig.Driver
   754  	}
   755  
   756  	dockerClient, _, err := d.dockerClients()
   757  	if err != nil {
   758  		return fmt.Errorf("failed to get docker client: %v", err)
   759  	}
   760  	coordinatorConfig := &dockerCoordinatorConfig{
   761  		ctx:         d.ctx,
   762  		client:      dockerClient,
   763  		cleanup:     d.config.GC.Image,
   764  		logger:      d.logger,
   765  		removeDelay: d.config.GC.imageDelayDuration,
   766  	}
   767  
   768  	d.coordinator = newDockerCoordinator(coordinatorConfig)
   769  
   770  	d.danglingReconciler = newReconciler(d)
   771  
   772  	d.cpusetFixer = newCpusetFixer(d)
   773  
   774  	return nil
   775  }
   776  
   777  func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) {
   778  	return taskConfigSpec, nil
   779  }
   780  
   781  // Capabilities is returned by the Capabilities RPC and indicates what optional
   782  // features this driver supports.
   783  func (d *Driver) Capabilities() (*drivers.Capabilities, error) {
   784  	return driverCapabilities, nil
   785  }
   786  
   787  var _ drivers.InternalCapabilitiesDriver = (*Driver)(nil)
   788  
   789  func (d *Driver) InternalCapabilities() drivers.InternalCapabilities {
   790  	return drivers.InternalCapabilities{
   791  		DisableLogCollection: d.config != nil && d.config.DisableLogCollection,
   792  	}
   793  }