github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/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 "path/filepath" 11 "regexp" 12 "runtime" 13 "strings" 14 "syscall" 15 "time" 16 17 "github.com/hashicorp/nomad/client/allocdir" 18 "github.com/hashicorp/nomad/client/config" 19 "github.com/hashicorp/nomad/client/driver/args" 20 "github.com/hashicorp/nomad/nomad/structs" 21 ) 22 23 var ( 24 reRktVersion = regexp.MustCompile(`rkt version (\d[.\d]+)`) 25 reAppcVersion = regexp.MustCompile(`appc version (\d[.\d]+)`) 26 ) 27 28 // RktDriver is a driver for running images via Rkt 29 // We attempt to chose sane defaults for now, with more configuration available 30 // planned in the future 31 type RktDriver struct { 32 DriverContext 33 } 34 35 // rktHandle is returned from Start/Open as a handle to the PID 36 type rktHandle struct { 37 proc *os.Process 38 image string 39 logger *log.Logger 40 waitCh chan error 41 doneCh chan struct{} 42 } 43 44 // rktPID is a struct to map the pid running the process to the vm image on 45 // disk 46 type rktPID struct { 47 Pid int 48 Image string 49 } 50 51 // NewRktDriver is used to create a new exec driver 52 func NewRktDriver(ctx *DriverContext) Driver { 53 return &RktDriver{*ctx} 54 } 55 56 func (d *RktDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) { 57 // Only enable if we are root when running on non-windows systems. 58 if runtime.GOOS != "windows" && syscall.Geteuid() != 0 { 59 d.logger.Printf("[DEBUG] driver.rkt: must run as root user, disabling") 60 return false, nil 61 } 62 63 outBytes, err := exec.Command("rkt", "version").Output() 64 if err != nil { 65 return false, nil 66 } 67 out := strings.TrimSpace(string(outBytes)) 68 69 rktMatches := reRktVersion.FindStringSubmatch(out) 70 appcMatches := reAppcVersion.FindStringSubmatch(out) 71 if len(rktMatches) != 2 || len(appcMatches) != 2 { 72 return false, fmt.Errorf("Unable to parse Rkt version string: %#v", rktMatches) 73 } 74 75 node.Attributes["driver.rkt"] = "1" 76 node.Attributes["driver.rkt.version"] = rktMatches[1] 77 node.Attributes["driver.rkt.appc.version"] = appcMatches[1] 78 79 return true, nil 80 } 81 82 // Run an existing Rkt image. 83 func (d *RktDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) { 84 // Validate that the config is valid. 85 img, ok := task.Config["image"] 86 if !ok || img == "" { 87 return nil, fmt.Errorf("Missing ACI image for rkt") 88 } 89 90 // Get the tasks local directory. 91 taskName := d.DriverContext.taskName 92 taskDir, ok := ctx.AllocDir.TaskDirs[taskName] 93 if !ok { 94 return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName) 95 } 96 taskLocal := filepath.Join(taskDir, allocdir.TaskLocal) 97 98 // Add the given trust prefix 99 trust_prefix, trust_cmd := task.Config["trust_prefix"] 100 if trust_cmd { 101 var outBuf, errBuf bytes.Buffer 102 cmd := exec.Command("rkt", "trust", fmt.Sprintf("--prefix=%s", trust_prefix)) 103 cmd.Stdout = &outBuf 104 cmd.Stderr = &errBuf 105 if err := cmd.Run(); err != nil { 106 return nil, fmt.Errorf("Error running rkt trust: %s\n\nOutput: %s\n\nError: %s", 107 err, outBuf.String(), errBuf.String()) 108 } 109 d.logger.Printf("[DEBUG] driver.rkt: added trust prefix: %q", trust_prefix) 110 } 111 112 // Build the command. 113 var cmd_args []string 114 115 // Inject the environment variables. 116 envVars := TaskEnvironmentVariables(ctx, task) 117 118 // Clear the task directories as they are not currently supported. 119 envVars.ClearTaskLocalDir() 120 envVars.ClearAllocDir() 121 122 for k, v := range envVars.Map() { 123 cmd_args = append(cmd_args, fmt.Sprintf("--set-env=%v=%v", k, v)) 124 } 125 126 // Disble signature verification if the trust command was not run. 127 if !trust_cmd { 128 cmd_args = append(cmd_args, "--insecure-skip-verify") 129 } 130 131 // Append the run command. 132 cmd_args = append(cmd_args, "run", "--mds-register=false", img) 133 134 // Check if the user has overriden the exec command. 135 if exec_cmd, ok := task.Config["command"]; ok { 136 cmd_args = append(cmd_args, fmt.Sprintf("--exec=%v", exec_cmd)) 137 } 138 139 // Add user passed arguments. 140 if userArgs, ok := task.Config["args"]; ok { 141 parsed, err := args.ParseAndReplace(userArgs, envVars.Map()) 142 if err != nil { 143 return nil, err 144 } 145 146 // Need to start arguments with "--" 147 if len(parsed) > 0 { 148 cmd_args = append(cmd_args, "--") 149 } 150 151 for _, arg := range parsed { 152 cmd_args = append(cmd_args, fmt.Sprintf("%v", arg)) 153 } 154 } 155 156 // Create files to capture stdin and out. 157 stdoutFilename := filepath.Join(taskLocal, fmt.Sprintf("%s.stdout", taskName)) 158 stderrFilename := filepath.Join(taskLocal, fmt.Sprintf("%s.stderr", taskName)) 159 160 stdo, err := os.OpenFile(stdoutFilename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666) 161 if err != nil { 162 return nil, fmt.Errorf("Error opening file to redirect stdout: %v", err) 163 } 164 165 stde, err := os.OpenFile(stderrFilename, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666) 166 if err != nil { 167 return nil, fmt.Errorf("Error opening file to redirect stderr: %v", err) 168 } 169 170 cmd := exec.Command("rkt", cmd_args...) 171 cmd.Stdout = stdo 172 cmd.Stderr = stde 173 174 if err := cmd.Start(); err != nil { 175 return nil, fmt.Errorf("Error running rkt: %v", err) 176 } 177 178 d.logger.Printf("[DEBUG] driver.rkt: started ACI %q with: %v", img, cmd.Args) 179 h := &rktHandle{ 180 proc: cmd.Process, 181 image: img, 182 logger: d.logger, 183 doneCh: make(chan struct{}), 184 waitCh: make(chan error, 1), 185 } 186 go h.run() 187 return h, nil 188 } 189 190 func (d *RktDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) { 191 // Parse the handle 192 pidBytes := []byte(strings.TrimPrefix(handleID, "Rkt:")) 193 qpid := &rktPID{} 194 if err := json.Unmarshal(pidBytes, qpid); err != nil { 195 return nil, fmt.Errorf("failed to parse Rkt handle '%s': %v", handleID, err) 196 } 197 198 // Find the process 199 proc, err := os.FindProcess(qpid.Pid) 200 if proc == nil || err != nil { 201 return nil, fmt.Errorf("failed to find Rkt PID %d: %v", qpid.Pid, err) 202 } 203 204 // Return a driver handle 205 h := &rktHandle{ 206 proc: proc, 207 image: qpid.Image, 208 logger: d.logger, 209 doneCh: make(chan struct{}), 210 waitCh: make(chan error, 1), 211 } 212 213 go h.run() 214 return h, nil 215 } 216 217 func (h *rktHandle) ID() string { 218 // Return a handle to the PID 219 pid := &rktPID{ 220 Pid: h.proc.Pid, 221 Image: h.image, 222 } 223 data, err := json.Marshal(pid) 224 if err != nil { 225 h.logger.Printf("[ERR] driver.rkt: failed to marshal rkt PID to JSON: %s", err) 226 } 227 return fmt.Sprintf("Rkt:%s", string(data)) 228 } 229 230 func (h *rktHandle) WaitCh() chan error { 231 return h.waitCh 232 } 233 234 func (h *rktHandle) Update(task *structs.Task) error { 235 // Update is not possible 236 return nil 237 } 238 239 // Kill is used to terminate the task. We send an Interrupt 240 // and then provide a 5 second grace period before doing a Kill. 241 func (h *rktHandle) Kill() error { 242 h.proc.Signal(os.Interrupt) 243 select { 244 case <-h.doneCh: 245 return nil 246 case <-time.After(5 * time.Second): 247 return h.proc.Kill() 248 } 249 } 250 251 func (h *rktHandle) run() { 252 ps, err := h.proc.Wait() 253 close(h.doneCh) 254 if err != nil { 255 h.waitCh <- err 256 } else if !ps.Success() { 257 h.waitCh <- fmt.Errorf("task exited with error") 258 } 259 close(h.waitCh) 260 }