github.com/jmitchell/nomad@v0.1.3-0.20151007230021-7ab84c2862d8/client/driver/rkt.go (about) 1 package driver 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "log" 8 "os" 9 "os/exec" 10 "regexp" 11 "runtime" 12 "strings" 13 "syscall" 14 "time" 15 16 "github.com/hashicorp/nomad/client/config" 17 "github.com/hashicorp/nomad/nomad/structs" 18 ) 19 20 var ( 21 reRktVersion = regexp.MustCompile("rkt version ([\\d\\.]+).+") 22 reAppcVersion = regexp.MustCompile("appc version ([\\d\\.]+).+") 23 ) 24 25 // RktDriver is a driver for running images via Rkt 26 // We attempt to chose sane defaults for now, with more configuration available 27 // planned in the future 28 type RktDriver struct { 29 DriverContext 30 } 31 32 // rktHandle is returned from Start/Open as a handle to the PID 33 type rktHandle struct { 34 proc *os.Process 35 name string 36 logger *log.Logger 37 waitCh chan error 38 doneCh chan struct{} 39 } 40 41 // rktPID is a struct to map the pid running the process to the vm image on 42 // disk 43 type rktPID struct { 44 Pid int 45 Name string 46 } 47 48 // NewRktDriver is used to create a new exec driver 49 func NewRktDriver(ctx *DriverContext) Driver { 50 return &RktDriver{*ctx} 51 } 52 53 func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 54 // Only enable if we are root when running on non-windows systems. 55 if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { 56 d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") 57 return false, nil 58 } 59 60 outBytes, err := exec.Command("rkt", "version").Output() 61 if err != nil { 62 return false, nil 63 } 64 out := strings.TrimSpace(string(outBytes)) 65 66 rktMatches := reRktVersion.FindStringSubmatch(out) 67 appcMatches := reRktVersion.FindStringSubmatch(out) 68 if len(rktMatches) != 2 || len(appcMatches) != 2 { 69 return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) 70 } 71 72 node.Attributes["driver.rkt"] = "true" 73 node.Attributes["driver.rkt.version"] = rktMatches[0] 74 node.Attributes["driver.rkt.appc.version"] = appcMatches[1] 75 76 return true, nil 77 } 78 79 // Run an existing Rkt image. 80 func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 81 trust_prefix, ok := task.Config["trust_prefix"] 82 if !ok || trust_prefix == "" { 83 return nil, fmt.Errorf("Missing trust prefix for rkt") 84 } 85 86 // Add the given trust prefix 87 var outBuf, errBuf bytes.Buffer 88 cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix)) 89 cmd.Stdout = &outBuf 90 cmd.Stderr = &errBuf 91 d.logger.Printf("[DEBUG] driver.rkt: starting rkt command: %q", cmd.Args) 92 if err := cmd.Run(); err != nil { 93 return nil, fmt.Errorf( 94 "Error running rkt: %s\n\nOutput: %s\n\nError: %s", 95 err, outBuf.String(), errBuf.String()) 96 } 97 d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix) 98 99 name, ok := task.Config["name"] 100 if !ok || name == "" { 101 return nil, fmt.Errorf("Missing ACI name for rkt") 102 } 103 104 exec_cmd, ok := task.Config["exec"] 105 if !ok || exec_cmd == "" { 106 d.logger.Printf("[WARN] driver.rkt: could not find a command to execute in the ACI, the default command will be executed") 107 } 108 109 // Run the ACI 110 var aoutBuf, aerrBuf bytes.Buffer 111 run_cmd := []string{ 112 "rkt", 113 "run", 114 "--mds-register=false", 115 name, 116 } 117 if exec_cmd != "" { 118 splitted := strings.Fields(exec_cmd) 119 run_cmd = append(run_cmd, "--exec=", splitted[0], "--") 120 run_cmd = append(run_cmd, splitted[1:]...) 121 run_cmd = append(run_cmd, "---") 122 } 123 acmd := exec.Command(run_cmd[0], run_cmd[1:]...) 124 acmd.Stdout = &aoutBuf 125 acmd.Stderr = &aerrBuf 126 d.logger.Printf("[DEBUG] driver:rkt: starting rkt command: %q", acmd.Args) 127 if err := acmd.Start(); err != nil { 128 return nil, fmt.Errorf( 129 "Error running rkt: %s\n\nOutput: %s\n\nError: %s", 130 err, aoutBuf.String(), aerrBuf.String()) 131 } 132 d.logger.Printf("[DEBUG] driver.rkt: started ACI: %q", name) 133 h := &rktHandle{ 134 proc: acmd.Process, 135 name: name, 136 logger: d.logger, 137 doneCh: make(chan struct{}), 138 waitCh: make(chan error, 1), 139 } 140 go h.run() 141 return h, nil 142 } 143 144 func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 145 // Parse the handle 146 pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) 147 qpid := &rktPID{} 148 if err := json.Unmarshal(pidBytes, qpid); err != nil { 149 return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) 150 } 151 152 // Find the process 153 proc, err := os.FindProcess(qpid.Pid) 154 if proc == nil || err != nil { 155 return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err) 156 } 157 158 // Return a driver handle 159 h := &rktHandle{ 160 proc: proc, 161 name: qpid.Name, 162 logger: d.logger, 163 doneCh: make(chan struct{}), 164 waitCh: make(chan error, 1), 165 } 166 167 go h.run() 168 return h, nil 169 } 170 171 func (h *rktHandle) ID() string { 172 // Return a handle to the PID 173 pid := &rktPID{ 174 Pid: h.proc.Pid, 175 Name: h.name, 176 } 177 data, err := json.Marshal(pid) 178 if err != nil { 179 h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) 180 } 181 return fmt.Sprintf("Rkt:%s", string(data)) 182 } 183 184 func (h *rktHandle) WaitCh() chan error { 185 return h.waitCh 186 } 187 188 func (h *rktHandle) Update(task *structs.Task) error { 189 // Update is not possible 190 return nil 191 } 192 193 // Kill is used to terminate the task. We send an Interrupt 194 // and then provide a 5 second grace period before doing a Kill. 195 func (h *rktHandle) Kill() error { 196 h.proc.Signal(os.Interrupt) 197 select { 198 case <-h.doneCh: 199 return nil 200 case <-time.After(5 * time.Second): 201 return h.proc.Kill() 202 } 203 } 204 205 func (h *rktHandle) run() { 206 ps, err := h.proc.Wait() 207 close(h.doneCh) 208 if err != nil { 209 h.waitCh <- err 210 } else if !ps.Success() { 211 h.waitCh <- fmt.Errorf("task exited with error") 212 } 213 close(h.waitCh) 214 }