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