github.com/jrxfive/nomad@v0.6.1-0.20170802162750-1fef470e89bf/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[:readOnly] 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 readOnly := "false" 323 // job spec: 324 // volumes = ["/host/path:/container/path[:readOnly]"] 325 // the third parameter is optional, mount is read-write by default 326 if len(parts) == 3 { 327 if parts[2] == "readOnly" { 328 d.logger.Printf("[DEBUG] Mounting %s:%s as readOnly", parts[0], parts[1]) 329 readOnly = "true" 330 } else { 331 d.logger.Printf("[WARN] Unknown volume parameter '%s' ignored for mount %s", parts[2], parts[0]) 332 } 333 } else if len(parts) != 2 { 334 return nil, fmt.Errorf("invalid rkt volume: %q", rawvol) 335 } 336 volName := fmt.Sprintf("%s-%s-%d", d.DriverContext.allocID, sanitizedName, i) 337 cmdArgs = append(cmdArgs, fmt.Sprintf("--volume=%s,kind=host,source=%s,readOnly=%s", volName, parts[0], readOnly)) 338 cmdArgs = append(cmdArgs, fmt.Sprintf("--mount=volume=%s,target=%s", volName, parts[1])) 339 } 340 } 341 342 cmdArgs = append(cmdArgs, img) 343 344 // Inject environment variables 345 for k, v := range ctx.TaskEnv.Map() { 346 cmdArgs = append(cmdArgs, fmt.Sprintf("--set-env=%s=%s", k, v)) 347 } 348 349 // Check if the user has overridden the exec command. 350 if driverConfig.Command != "" { 351 cmdArgs = append(cmdArgs, fmt.Sprintf("--exec=%v", driverConfig.Command)) 352 } 353 354 // Add memory isolator 355 cmdArgs = append(cmdArgs, fmt.Sprintf("--memory=%vM", int64(task.Resources.MemoryMB))) 356 357 // Add CPU isolator 358 cmdArgs = append(cmdArgs, fmt.Sprintf("--cpu=%vm", int64(task.Resources.CPU))) 359 360 // Add DNS servers 361 if len(driverConfig.DNSServers) == 1 && (driverConfig.DNSServers[0] == "host" || driverConfig.DNSServers[0] == "none") { 362 // Special case single item lists with the special values "host" or "none" 363 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", driverConfig.DNSServers[0])) 364 } else { 365 for _, ip := range driverConfig.DNSServers { 366 if err := net.ParseIP(ip); err == nil { 367 msg := fmt.Errorf("invalid ip address for container dns server %q", ip) 368 d.logger.Printf("[DEBUG] driver.rkt: %v", msg) 369 return nil, msg 370 } else { 371 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns=%s", ip)) 372 } 373 } 374 } 375 376 // set DNS search domains 377 for _, domain := range driverConfig.DNSSearchDomains { 378 cmdArgs = append(cmdArgs, fmt.Sprintf("--dns-search=%s", domain)) 379 } 380 381 // set network 382 network := strings.Join(driverConfig.Net, ",") 383 if network != "" { 384 cmdArgs = append(cmdArgs, fmt.Sprintf("--net=%s", network)) 385 } 386 387 // Setup port mapping and exposed ports 388 if len(task.Resources.Networks) == 0 { 389 d.logger.Println("[DEBUG] driver.rkt: No network interfaces are available") 390 if len(driverConfig.PortMap) > 0 { 391 return nil, fmt.Errorf("Trying to map ports but no network interface is available") 392 } 393 } else { 394 // TODO add support for more than one network 395 network := task.Resources.Networks[0] 396 for _, port := range network.ReservedPorts { 397 var containerPort string 398 399 mapped, ok := driverConfig.PortMap[port.Label] 400 if !ok { 401 // If the user doesn't have a mapped port using port_map, driver stops running container. 402 return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.") 403 } 404 containerPort = mapped 405 406 hostPortStr := strconv.Itoa(port.Value) 407 408 d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort) 409 // Add port option to rkt run arguments. rkt allows multiple port args 410 cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr)) 411 } 412 413 for _, port := range network.DynamicPorts { 414 // By default we will map the allocated port 1:1 to the container 415 var containerPort string 416 417 if mapped, ok := driverConfig.PortMap[port.Label]; ok { 418 containerPort = mapped 419 } else { 420 // If the user doesn't have mapped a port using port_map, driver stops running container. 421 return nil, fmt.Errorf("port_map is not set. When you defined port in the resources, you need to configure port_map.") 422 } 423 424 hostPortStr := strconv.Itoa(port.Value) 425 426 d.logger.Printf("[DEBUG] driver.rkt: exposed port %s", containerPort) 427 // Add port option to rkt run arguments. rkt allows multiple port args 428 cmdArgs = append(cmdArgs, fmt.Sprintf("--port=%s:%s", containerPort, hostPortStr)) 429 } 430 431 } 432 433 // Add user passed arguments. 434 if len(driverConfig.Args) != 0 { 435 parsed := ctx.TaskEnv.ParseAndReplace(driverConfig.Args) 436 437 // Need to start arguments with "--" 438 if len(parsed) > 0 { 439 cmdArgs = append(cmdArgs, "--") 440 } 441 442 for _, arg := range parsed { 443 cmdArgs = append(cmdArgs, fmt.Sprintf("%v", arg)) 444 } 445 } 446 447 pluginLogFile := filepath.Join(ctx.TaskDir.Dir, fmt.Sprintf("%s-executor.out", task.Name)) 448 executorConfig := &dstructs.ExecutorConfig{ 449 LogFile: pluginLogFile, 450 LogLevel: d.config.LogLevel, 451 } 452 453 execIntf, pluginClient, err := createExecutor(d.config.LogOutput, d.config, executorConfig) 454 if err != nil { 455 return nil, err 456 } 457 458 // The task's environment is set via --set-env flags above, but the rkt 459 // command itself needs an evironment with PATH set to find iptables. 460 eb := env.NewEmptyBuilder() 461 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 462 rktEnv := eb.SetHostEnvvars(filter).Build() 463 executorCtx := &executor.ExecutorContext{ 464 TaskEnv: rktEnv, 465 Driver: "rkt", 466 AllocID: d.DriverContext.allocID, 467 Task: task, 468 TaskDir: ctx.TaskDir.Dir, 469 LogDir: ctx.TaskDir.LogDir, 470 } 471 if err := execIntf.SetContext(executorCtx); err != nil { 472 pluginClient.Kill() 473 return nil, fmt.Errorf("failed to set executor context: %v", err) 474 } 475 476 absPath, err := GetAbsolutePath(rktCmd) 477 if err != nil { 478 return nil, err 479 } 480 481 execCmd := &executor.ExecCommand{ 482 Cmd: absPath, 483 Args: cmdArgs, 484 User: task.User, 485 } 486 ps, err := execIntf.LaunchCmd(execCmd) 487 if err != nil { 488 pluginClient.Kill() 489 return nil, err 490 } 491 492 // Wait for UUID file to get written 493 uuid := "" 494 deadline := time.Now().Add(rktUuidDeadline) 495 var lastErr error 496 for time.Now().Before(deadline) { 497 if uuidBytes, err := ioutil.ReadFile(uuidPath); err != nil { 498 lastErr = err 499 } else { 500 uuid = string(uuidBytes) 501 break 502 } 503 time.Sleep(400 * time.Millisecond) 504 } 505 if uuid == "" { 506 d.logger.Printf("[WARN] driver.rkt: reading uuid from %q failed; unable to run script checks for task %q. Last error: %v", 507 uuidPath, d.taskName, lastErr) 508 } 509 510 d.logger.Printf("[DEBUG] driver.rkt: started ACI %q (UUID: %s) for task %q with: %v", img, uuid, d.taskName, cmdArgs) 511 maxKill := d.DriverContext.config.MaxKillTimeout 512 h := &rktHandle{ 513 uuid: uuid, 514 env: rktEnv, 515 taskDir: ctx.TaskDir, 516 pluginClient: pluginClient, 517 executor: execIntf, 518 executorPid: ps.Pid, 519 logger: d.logger, 520 killTimeout: GetKillTimeout(task.KillTimeout, maxKill), 521 maxKillTimeout: maxKill, 522 doneCh: make(chan struct{}), 523 waitCh: make(chan *dstructs.WaitResult, 1), 524 } 525 go h.run() 526 //TODO Set Network 527 return &StartResponse{Handle: h}, nil 528 } 529 530 func (d *RktDriver) Cleanup(*ExecContext, *CreatedResources) error { return nil } 531 532 func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 533 // Parse the handle 534 pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) 535 id := &rktPID{} 536 if err := json.Unmarshal(pidBytes, id); err != nil { 537 return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) 538 } 539 540 pluginConfig := &plugin.ClientConfig{ 541 Reattach: id.PluginConfig.PluginConfig(), 542 } 543 exec, pluginClient, err := createExecutorWithConfig(pluginConfig, d.config.LogOutput) 544 if err != nil { 545 d.logger.Println("[ERROR] driver.rkt: error connecting to plugin so destroying plugin pid and user pid") 546 if e := destroyPlugin(id.PluginConfig.Pid, id.ExecutorPid); e != nil { 547 d.logger.Printf("[ERROR] driver.rkt: error destroying plugin and executor pid: %v", e) 548 } 549 return nil, fmt.Errorf("error connecting to plugin: %v", err) 550 } 551 552 // The task's environment is set via --set-env flags in Start, but the rkt 553 // command itself needs an evironment with PATH set to find iptables. 554 eb := env.NewEmptyBuilder() 555 filter := strings.Split(d.config.ReadDefault("env.blacklist", config.DefaultEnvBlacklist), ",") 556 rktEnv := eb.SetHostEnvvars(filter).Build() 557 558 ver, _ := exec.Version() 559 d.logger.Printf("[DEBUG] driver.rkt: version of executor: %v", ver.Version) 560 // Return a driver handle 561 h := &rktHandle{ 562 uuid: id.UUID, 563 env: rktEnv, 564 taskDir: ctx.TaskDir, 565 pluginClient: pluginClient, 566 executorPid: id.ExecutorPid, 567 executor: exec, 568 logger: d.logger, 569 killTimeout: id.KillTimeout, 570 maxKillTimeout: id.MaxKillTimeout, 571 doneCh: make(chan struct{}), 572 waitCh: make(chan *dstructs.WaitResult, 1), 573 } 574 go h.run() 575 return h, nil 576 } 577 578 func (h *rktHandle) ID() string { 579 // Return a handle to the PID 580 pid := &rktPID{ 581 UUID: h.uuid, 582 PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()), 583 KillTimeout: h.killTimeout, 584 MaxKillTimeout: h.maxKillTimeout, 585 ExecutorPid: h.executorPid, 586 } 587 data, err := json.Marshal(pid) 588 if err != nil { 589 h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) 590 } 591 return fmt.Sprintf("Rkt:%s", string(data)) 592 } 593 594 func (h *rktHandle) WaitCh() chan *dstructs.WaitResult { 595 return h.waitCh 596 } 597 598 func (h *rktHandle) Update(task *structs.Task) error { 599 // Store the updated kill timeout. 600 h.killTimeout = GetKillTimeout(task.KillTimeout, h.maxKillTimeout) 601 h.executor.UpdateTask(task) 602 603 // Update is not possible 604 return nil 605 } 606 607 func (h *rktHandle) Exec(ctx context.Context, cmd string, args []string) ([]byte, int, error) { 608 if h.uuid == "" { 609 return nil, 0, fmt.Errorf("unable to find rkt pod UUID") 610 } 611 // enter + UUID + cmd + args... 612 enterArgs := make([]string, 3+len(args)) 613 enterArgs[0] = "enter" 614 enterArgs[1] = h.uuid 615 enterArgs[2] = cmd 616 copy(enterArgs[3:], args) 617 return executor.ExecScript(ctx, h.taskDir.Dir, h.env, nil, rktCmd, enterArgs) 618 } 619 620 func (h *rktHandle) Signal(s os.Signal) error { 621 return fmt.Errorf("Rkt does not support signals") 622 } 623 624 // Kill is used to terminate the task. We send an Interrupt 625 // and then provide a 5 second grace period before doing a Kill. 626 func (h *rktHandle) Kill() error { 627 h.executor.ShutDown() 628 select { 629 case <-h.doneCh: 630 return nil 631 case <-time.After(h.killTimeout): 632 return h.executor.Exit() 633 } 634 } 635 636 func (h *rktHandle) Stats() (*cstructs.TaskResourceUsage, error) { 637 return nil, DriverStatsNotImplemented 638 } 639 640 func (h *rktHandle) run() { 641 ps, werr := h.executor.Wait() 642 close(h.doneCh) 643 if ps.ExitCode == 0 && werr != nil { 644 if e := killProcess(h.executorPid); e != nil { 645 h.logger.Printf("[ERROR] driver.rkt: error killing user process: %v", e) 646 } 647 } 648 649 // Exit the executor 650 if err := h.executor.Exit(); err != nil { 651 h.logger.Printf("[ERR] driver.rkt: error killing executor: %v", err) 652 } 653 h.pluginClient.Kill() 654 655 // Send the results 656 h.waitCh <- dstructs.NewWaitResult(ps.ExitCode, 0, werr) 657 close(h.waitCh) 658 }