github.com/hhrutter/nomad@v0.6.0-rc2.0.20170723054333-80c4b03f0705/client/driver/rkt.go (about) 1 package driver 2 3 import ( 4 "bytes" 5 "context" 6 "encoding/json" 7 "fmt" 8 "io/ioutil" 9 "log" 10 "net" 11 "os" 12 "os/exec" 13 "path/filepath" 14 "regexp" 15 "runtime" 16 "strconv" 17 "strings" 18 "syscall" 19 "time" 20 21 "github.com/hashicorp/go-plugin" 22 "github.com/hashicorp/go-version" 23 "github.com/hashicorp/nomad/client/allocdir" 24 "github.com/hashicorp/nomad/client/config" 25 "github.com/hashicorp/nomad/client/driver/env" 26 "github.com/hashicorp/nomad/client/driver/executor" 27 dstructs "github.com/hashicorp/nomad/client/driver/structs" 28 cstructs "github.com/hashicorp/nomad/client/structs" 29 "github.com/hashicorp/nomad/helper" 30 "github.com/hashicorp/nomad/helper/fields" 31 "github.com/hashicorp/nomad/nomad/structs" 32 "github.com/mitchellh/mapstructure" 33 ) 34 35 var ( 36 reRktVersion = regexp.MustCompile(`rkt [vV]ersion[:]? (\d[.\d]+)`) 37 reAppcVersion = regexp.MustCompile(`appc [vV]ersion[:]? (\d[.\d]+)`) 38 ) 39 40 const ( 41 // minRktVersion is the earliest supported version of rkt. rkt added support 42 // for CPU and memory isolators in 0.14.0. We cannot support an earlier 43 // version to maintain an uniform interface across all drivers 44 minRktVersion = "1.0.0" 45 46 // The key populated in the Node Attributes to indicate the presence of the 47 // Rkt driver 48 rktDriverAttr = "driver.rkt" 49 50 // rktVolumesConfigOption is the key for enabling the use of custom 51 // bind volumes. 52 rktVolumesConfigOption = "rkt.volumes.enabled" 53 rktVolumesConfigDefault = true 54 55 // rktCmd is the command rkt is installed as. 56 rktCmd = "rkt" 57 58 // rktUuidDeadline is how long to wait for the uuid file to be written 59 rktUuidDeadline = 5 * time.Second 60 ) 61 62 // RktDriver is a driver for running images via Rkt 63 // We attempt to chose sane defaults for now, with more configuration available 64 // planned in the future 65 type RktDriver struct { 66 DriverContext 67 68 // A tri-state boolean to know if the fingerprinting has happened and 69 // whether it has been successful 70 fingerprintSuccess *bool 71 } 72 73 type RktDriverConfig struct { 74 ImageName string `mapstructure:"image"` 75 Command string `mapstructure:"command"` 76 Args []string `mapstructure:"args"` 77 TrustPrefix string `mapstructure:"trust_prefix"` 78 DNSServers []string `mapstructure:"dns_servers"` // DNS Server for containers 79 DNSSearchDomains []string `mapstructure:"dns_search_domains"` // DNS Search domains for containers 80 Net []string `mapstructure:"net"` // Networks for the containers 81 PortMapRaw []map[string]string `mapstructure:"port_map"` // 82 PortMap map[string]string `mapstructure:"-"` // A map of host port and the port name defined in the image manifest file 83 Volumes []string `mapstructure:"volumes"` // Host-Volumes to mount in, syntax: /path/to/host/directory:/destination/path/in/container 84 InsecureOptions []string `mapstructure:"insecure_options"` // list of args for --insecure-options 85 86 NoOverlay bool `mapstructure:"no_overlay"` // disable overlayfs for rkt run 87 Debug bool `mapstructure:"debug"` // Enable debug option for rkt command 88 } 89 90 // rktHandle is returned from Start/Open as a handle to the PID 91 type rktHandle struct { 92 uuid string 93 env *env.TaskEnv 94 taskDir *allocdir.TaskDir 95 pluginClient *plugin.Client 96 executorPid int 97 executor executor.Executor 98 logger *log.Logger 99 killTimeout time.Duration 100 maxKillTimeout time.Duration 101 waitCh chan *dstructs.WaitResult 102 doneCh chan struct{} 103 } 104 105 // rktPID is a struct to map the pid running the process to the vm image on 106 // disk 107 type rktPID struct { 108 UUID string 109 PluginConfig *PluginReattachConfig 110 ExecutorPid int 111 KillTimeout time.Duration 112 MaxKillTimeout time.Duration 113 } 114 115 // NewRktDriver is used to create a new exec driver 116 func NewRktDriver(ctx *DriverContext) Driver { 117 return &RktDriver{DriverContext: *ctx} 118 } 119 120 func (d *RktDriver) FSIsolation() cstructs.FSIsolation { 121 return cstructs.FSIsolationImage 122 } 123 124 // Validate is used to validate the driver configuration 125 func (d *RktDriver) Validate(config map[string]interface{}) error { 126 fd := &fields.FieldData{ 127 Raw: config, 128 Schema: map[string]*fields.FieldSchema{ 129 "image": &fields.FieldSchema{ 130 Type: fields.TypeString, 131 Required: true, 132 }, 133 "command": &fields.FieldSchema{ 134 Type: fields.TypeString, 135 }, 136 "args": &fields.FieldSchema{ 137 Type: fields.TypeArray, 138 }, 139 "trust_prefix": &fields.FieldSchema{ 140 Type: fields.TypeString, 141 }, 142 "dns_servers": &fields.FieldSchema{ 143 Type: fields.TypeArray, 144 }, 145 "dns_search_domains": &fields.FieldSchema{ 146 Type: fields.TypeArray, 147 }, 148 "net": &fields.FieldSchema{ 149 Type: fields.TypeArray, 150 }, 151 "port_map": &fields.FieldSchema{ 152 Type: fields.TypeArray, 153 }, 154 "debug": &fields.FieldSchema{ 155 Type: fields.TypeBool, 156 }, 157 "volumes": &fields.FieldSchema{ 158 Type: fields.TypeArray, 159 }, 160 "no_overlay": &fields.FieldSchema{ 161 Type: fields.TypeBool, 162 }, 163 "insecure_options": &fields.FieldSchema{ 164 Type: fields.TypeArray, 165 }, 166 }, 167 } 168 169 if err := fd.Validate(); err != nil { 170 return err 171 } 172 173 return nil 174 } 175 176 func (d *RktDriver) Abilities() DriverAbilities { 177 return DriverAbilities{ 178 SendSignals: false, 179 Exec: true, 180 } 181 } 182 183 func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 184 // Only enable if we are root when running on non-windows systems. 185 if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { 186 if d.fingerprintSuccess == nil || *d.fingerprintSuccess { 187 d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") 188 } 189 delete(node.Attributes, rktDriverAttr) 190 d.fingerprintSuccess = helper.BoolToPtr(false) 191 return false, nil 192 } 193 194 outBytes, err := exec.Command(rktCmd, "version").Output() 195 if err != nil { 196 delete(node.Attributes, rktDriverAttr) 197 d.fingerprintSuccess = helper.BoolToPtr(false) 198 return false, nil 199 } 200 out := strings.TrimSpace(string(outBytes)) 201 202 rktMatches := reRktVersion.FindStringSubmatch(out) 203 appcMatches := reAppcVersion.FindStringSubmatch(out) 204 if len(rktMatches) != 2 || len(appcMatches) != 2 { 205 delete(node.Attributes, rktDriverAttr) 206 d.fingerprintSuccess = helper.BoolToPtr(false) 207 return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) 208 } 209 210 node.Attributes[rktDriverAttr] = "1" 211 node.Attributes["driver.rkt.version"] = rktMatches[1] 212 node.Attributes["driver.rkt.appc.version"] = appcMatches[1] 213 214 minVersion, _ := version.NewVersion(minRktVersion) 215 currentVersion, _ := version.NewVersion(node.Attributes["driver.rkt.version"]) 216 if currentVersion.LessThan(minVersion) { 217 // Do not allow ancient rkt versions 218 d.logger.Printf("[WARN] driver.rkt: please upgrade rkt to a version >= %s", minVersion) 219 node.Attributes[rktDriverAttr] = "0" 220 } 221 222 // Advertise if this node supports rkt volumes 223 if d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault) { 224 node.Attributes["driver."+rktVolumesConfigOption] = "1" 225 } 226 d.fingerprintSuccess = helper.BoolToPtr(true) 227 return true, nil 228 } 229 230 func (d *RktDriver) Periodic() (bool, time.Duration) { 231 return true, 15 * time.Second 232 } 233 234 func (d *RktDriver) Prestart(ctx *ExecContext, task *structs.Task) (*PrestartResponse, error) { 235 return nil, nil 236 } 237 238 // Run an existing Rkt image. 239 func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (*StartResponse, error) { 240 var driverConfig RktDriverConfig 241 if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil { 242 return nil, err 243 } 244 245 driverConfig.PortMap = mapMergeStrStr(driverConfig.PortMapRaw...) 246 247 // ACI image 248 img := driverConfig.ImageName 249 250 // Build the command. 251 cmdArgs := make([]string, 0, 50) 252 253 // Add debug option to rkt command. 254 debug := driverConfig.Debug 255 256 // Add the given trust prefix 257 trustPrefix := driverConfig.TrustPrefix 258 insecure := false 259 if trustPrefix != "" { 260 var outBuf, errBuf bytes.Buffer 261 cmd := exec.Command(rktCmd, "trust", "--skip-fingerprint-review=true", fmt.Sprintf("--prefix=%s", trustPrefix), fmt.Sprintf("--debug=%t", debug)) 262 cmd.Stdout = &outBuf 263 cmd.Stderr = &errBuf 264 if err := cmd.Run(); err != nil { 265 return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s", 266 err, outBuf.String(), errBuf.String()) 267 } 268 d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trustPrefix) 269 } else { 270 // Disble signature verification if the trust command was not run. 271 insecure = true 272 } 273 274 // if we have a selective insecure_options, prefer them 275 // insecure options are rkt's global argument, so we do this before the actual "run" 276 if len(driverConfig.InsecureOptions) > 0 { 277 cmdArgs = append(cmdArgs, fmt.Sprintf("--insecure-options=%s", strings.Join(driverConfig.InsecureOptions, ","))) 278 } else if insecure { 279 cmdArgs = append(cmdArgs, "--insecure-options=all") 280 } 281 282 // debug is rkt's global argument, so add it before the actual "run" 283 cmdArgs = append(cmdArgs, fmt.Sprintf("--debug=%t", debug)) 284 285 cmdArgs = append(cmdArgs, "run") 286 287 // disable overlayfs 288 if driverConfig.NoOverlay { 289 cmdArgs = append(cmdArgs, "--no-overlay=true") 290 } 291 292 // Write the UUID out to a file in the state dir so we can read it back 293 // in and access the pod by UUID from other commands 294 uuidPath := filepath.Join(ctx.TaskDir.Dir, "rkt.uuid") 295 cmdArgs = append(cmdArgs, fmt.Sprintf("--uuid-file-save=%s", uuidPath)) 296 297 // Convert underscores to dashes in task names for use in volume names #2358 298 sanitizedName := strings.Replace(task.Name, "_", "-", -1) 299 300 // Mount /alloc 301 allocVolName := fmt.Sprintf("%s-%s-alloc", d.DriverContext.allocID, sanitizedName) 302 cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", allocVolName, ctx.TaskDir.SharedAllocDir)) 303 cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", allocVolName, allocdir.SharedAllocContainerPath)) 304 305 // Mount /local 306 localVolName := fmt.Sprintf("%s-%s-local", d.DriverContext.allocID, sanitizedName) 307 cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", localVolName, ctx.TaskDir.LocalDir)) 308 cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", localVolName, allocdir.TaskLocalContainerPath)) 309 310 // Mount /secrets 311 secretsVolName := fmt.Sprintf("%s-%s-secrets", d.DriverContext.allocID, sanitizedName) 312 cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", secretsVolName, ctx.TaskDir.SecretsDir)) 313 cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", secretsVolName, allocdir.TaskSecretsContainerPath)) 314 315 // Mount arbitrary volumes if enabled 316 if len(driverConfig.Volumes) > 0 { 317 if enabled := d.config.ReadBoolDefault(rktVolumesConfigOption, rktVolumesConfigDefault); !enabled { 318 return nil, fmt.Errorf("%s is false; cannot use rkt volumes: %+q", rktVolumesConfigOption, driverConfig.Volumes) 319 } 320 for i, rawvol := range driverConfig.Volumes { 321 parts := strings.Split(rawvol, ":") 322 if len(parts) != 2 { 323 return nil, fmt.Errorf("invalid rkt volume: %q", rawvol) 324 } 325 volName := fmt.Sprintf("%s-%s-%d", d.DriverContext.allocID, sanitizedName, i) 326 cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s", volName, parts[0])) 327 cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, parts[1])) 328 } 329 } 330 331 cmdArgs = append(cmdArgs, img) 332 333 // Inject environment variables 334 for k, v := range ctx.TaskEnv.Map() { 335 cmdArgs = append(cmdArgs, fmt.Sprintf("--set-env=%s=%s", k, v)) 336 } 337 338 // Check if the user has overridden the exec command. 339 if driverConfig.Command != "" { 340 cmdArgs = append(cmdArgs, fmt.Sprintf("--exec=%v", driverConfig.Command)) 341 } 342 343 // Add memory isolator 344 cmdArgs = append(cmdArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB))) 345 346 // Add CPU isolator 347 cmdArgs = append(cmdArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU))) 348 349 // Add DNS servers 350 if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") { 351 // Special case single item lists with the special values "host" or "none" 352 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", driverConfig.DNSServers[0])) 353 } else { 354 for _, ip := range driverConfig.DNSServers { 355 if err := net.ParseIP(ip); err == nil { 356 msg := fmt.Errorf("invalid ip address for container dns server %q", ip) 357 d.logger.Printf("[DEBUG] driver.rkt: %v", msg) 358 return nil, msg 359 } else { 360 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", ip)) 361 } 362 } 363 } 364 365 // set DNS search domains 366 for _, domain := range driverConfig.DNSSearchDomains { 367 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns-search=%s", domain)) 368 } 369 370 // set network 371 network := strings.Join(driverConfig.Net, ",") 372 if network != "" { 373 cmdArgs = append(cmdArgs, fmt.Sprintf("--net=%s", network)) 374 } 375 376 // Setup port mapping and exposed ports 377 if len(task.Resources.Networks) == 0 { 378 d.logger.Println("[DEBUG] driver.rkt: No network interfaces are available") 379 if len(driverConfig.PortMap) > 0 { 380 return nil, fmt.Errorf("Trying to map ports but no network interface is available") 381 } 382 } else { 383 // TODO add support for more than one network 384 network := task.Resources.Networks[0] 385 for _, port := range network.ReservedPorts { 386 var containerPort string 387 388 mapped, ok := driverConfig.PortMap[port.Label] 389 if !ok { 390 // If the user doesn't have a mapped port using port_map, driver stops running container. 391 return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.") 392 } 393 containerPort = mapped 394 395 hostPortStr := strconv.Itoa(port.Value) 396 397 d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort) 398 // Add port option to rkt run arguments. rkt allows multiple port args 399 cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr)) 400 } 401 402 for _, port := range network.DynamicPorts { 403 // By default we will map the allocated port 1:1 to the container 404 var containerPort string 405 406 if mapped, ok := driverConfig.PortMap[port.Label]; ok { 407 containerPort = mapped 408 } else { 409 // If the user doesn't have mapped a port using port_map, driver stops running container. 410 return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.") 411 } 412 413 hostPortStr := strconv.Itoa(port.Value) 414 415 d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort) 416 // Add port option to rkt run arguments. rkt allows multiple port args 417 cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr)) 418 } 419 420 } 421 422 // Add user passed arguments. 423 if len(driverConfig.Args) != 0 { 424 parsed := ctx.TaskEnv.ParseAndReplace(driverConfig.Args) 425 426 // Need to start arguments with "--" 427 if len(parsed) > 0 { 428 cmdArgs = append(cmdArgs, "--") 429 } 430 431 for _, arg := range parsed { 432 cmdArgs = append(cmdArgs, fmt.Sprintf("%v", arg)) 433 } 434 } 435 436 pluginLogFile := filepath.Join(ctx.TaskDir.Dir, fmt.Sprintf("%s-executor.out", task.Name)) 437 executorConfig := &dstructs.ExecutorConfig{ 438 LogFile: pluginLogFile, 439 LogLevel: d.config.LogLevel, 440 } 441 442 execIntf, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig) 443 if err != nil { 444 return nil, err 445 } 446 447 // The task's environment is set via --set-env flags above, but the rkt 448 // command itself needs an evironment with PATH set to find iptables. 449 eb := env.NewEmptyBuilder() 450 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 451 rktEnv := eb.SetHostEnvvars(filter).Build() 452 executorCtx := &executor.ExecutorContext{ 453 TaskEnv: rktEnv, 454 Driver: "rkt", 455 AllocID: d.DriverContext.allocID, 456 Task: task, 457 TaskDir: ctx.TaskDir.Dir, 458 LogDir: ctx.TaskDir.LogDir, 459 } 460 if err := execIntf.SetContext(executorCtx); err != nil { 461 pluginClient.Kill() 462 return nil, fmt.Errorf("failed to set executor context: %v", err) 463 } 464 465 absPath, err := GetAbsolutePath(rktCmd) 466 if err != nil { 467 return nil, err 468 } 469 470 execCmd := &executor.ExecCommand{ 471 Cmd: absPath, 472 Args: cmdArgs, 473 User: task.User, 474 } 475 ps, err := execIntf.LaunchCmd(execCmd) 476 if err != nil { 477 pluginClient.Kill() 478 return nil, err 479 } 480 481 // Wait for UUID file to get written 482 uuid := "" 483 deadline := time.Now().Add(rktUuidDeadline) 484 var lastErr error 485 for time.Now().Before(deadline) { 486 if uuidBytes, err := ioutil.ReadFile(uuidPath); err != nil { 487 lastErr = err 488 } else { 489 uuid = string(uuidBytes) 490 break 491 } 492 time.Sleep(400 * time.Millisecond) 493 } 494 if uuid == "" { 495 d.logger.Printf("[WARN] driver.rkt: reading uuid from %q failed; unable to run script checks for task %q. Last error: %v", 496 uuidPath, d.taskName, lastErr) 497 } 498 499 d.logger.Printf("[DEBUG] driver.rkt: started ACI %q (UUID: %s) for task %q with: %v", img, uuid, d.taskName, cmdArgs) 500 maxKill := d.DriverContext.config.MaxKillTimeout 501 h := &rktHandle{ 502 uuid: uuid, 503 env: rktEnv, 504 taskDir: ctx.TaskDir, 505 pluginClient: pluginClient, 506 executor: execIntf, 507 executorPid: ps.Pid, 508 logger: d.logger, 509 killTimeout: GetKillTimeout(task.KillTimeout, maxKill), 510 maxKillTimeout: maxKill, 511 doneCh: make(chan struct{}), 512 waitCh: make(chan *dstructs.WaitResult, 1), 513 } 514 go h.run() 515 //TODO Set Network 516 return &StartResponse{Handle: h}, nil 517 } 518 519 func (d *RktDriver) Cleanup(*ExecContext, *CreatedResources) error { return nil } 520 521 func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 522 // Parse the handle 523 pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) 524 id := &rktPID{} 525 if err := json.Unmarshal(pidBytes, id); err != nil { 526 return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) 527 } 528 529 pluginConfig := &plugin.ClientConfig{ 530 Reattach: id.PluginConfig.PluginConfig(), 531 } 532 exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput) 533 if err != nil { 534 d.logger.Println("[ERROR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid") 535 if e := destroyPlugin(id.PluginConfig.Pid, id.ExecutorPid); e != nil { 536 d.logger.Printf("[ERROR] driver.rkt: error destroying plugin and executor pid: %v", e) 537 } 538 return nil, fmt.Errorf("error connecting to plugin: %v", err) 539 } 540 541 // The task's environment is set via --set-env flags in Start, but the rkt 542 // command itself needs an evironment with PATH set to find iptables. 543 eb := env.NewEmptyBuilder() 544 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 545 rktEnv := eb.SetHostEnvvars(filter).Build() 546 547 ver, _ := exec.Version() 548 d.logger.Printf("[DEBUG] driver.rkt: version of executor: %v", ver.Version) 549 // Return a driver handle 550 h := &rktHandle{ 551 uuid: id.UUID, 552 env: rktEnv, 553 taskDir: ctx.TaskDir, 554 pluginClient: pluginClient, 555 executorPid: id.ExecutorPid, 556 executor: exec, 557 logger: d.logger, 558 killTimeout: id.KillTimeout, 559 maxKillTimeout: id.MaxKillTimeout, 560 doneCh: make(chan struct{}), 561 waitCh: make(chan *dstructs.WaitResult, 1), 562 } 563 go h.run() 564 return h, nil 565 } 566 567 func (h *rktHandle) ID() string { 568 // Return a handle to the PID 569 pid := &rktPID{ 570 UUID: h.uuid, 571 PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()), 572 KillTimeout: h.killTimeout, 573 MaxKillTimeout: h.maxKillTimeout, 574 ExecutorPid: h.executorPid, 575 } 576 data, err := json.Marshal(pid) 577 if err != nil { 578 h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) 579 } 580 return fmt.Sprintf("Rkt:%s", string(data)) 581 } 582 583 func (h *rktHandle) WaitCh() chan *dstructs.WaitResult { 584 return h.waitCh 585 } 586 587 func (h *rktHandle) Update(task *structs.Task) error { 588 // Store the updated kill timeout. 589 h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout) 590 h.executor.UpdateTask(task) 591 592 // Update is not possible 593 return nil 594 } 595 596 func (h *rktHandle) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) { 597 if h.uuid == "" { 598 return nil, 0, fmt.Errorf("unable to find rkt pod UUID") 599 } 600 // enter + UUID + cmd + args... 601 enterArgs := make([]string, 3+len(args)) 602 enterArgs[0] = "enter" 603 enterArgs[1] = h.uuid 604 enterArgs[2] = cmd 605 copy(enterArgs[3:], args) 606 return executor.ExecScript(ctx, h.taskDir.Dir, h.env, nil, rktCmd, enterArgs) 607 } 608 609 func (h *rktHandle) Signal(s os.Signal) error { 610 return fmt.Errorf("Rkt does not support signals") 611 } 612 613 // Kill is used to terminate the task. We send an Interrupt 614 // and then provide a 5 second grace period before doing a Kill. 615 func (h *rktHandle) Kill() error { 616 h.executor.ShutDown() 617 select { 618 case <-h.doneCh: 619 return nil 620 case <-time.After(h.killTimeout): 621 return h.executor.Exit() 622 } 623 } 624 625 func (h *rktHandle) Stats() (*cstructs.TaskResourceUsage, error) { 626 return nil, DriverStatsNotImplemented 627 } 628 629 func (h *rktHandle) run() { 630 ps, werr := h.executor.Wait() 631 close(h.doneCh) 632 if ps.ExitCode == 0 && werr != nil { 633 if e := killProcess(h.executorPid); e != nil { 634 h.logger.Printf("[ERROR] driver.rkt: error killing user process: %v", e) 635 } 636 } 637 638 // Exit the executor 639 if err := h.executor.Exit(); err != nil { 640 h.logger.Printf("[ERR] driver.rkt: error killing executor: %v", err) 641 } 642 h.pluginClient.Kill() 643 644 // Send the results 645 h.waitCh <- dstructs.NewWaitResult(ps.ExitCode, 0, werr) 646 close(h.waitCh) 647 }