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 }