github.com/Datadog/cnab-go@v0.3.3-beta1.0.20191007143216-bba4b7e723d0/driver/command/command.go (about) 1 package command 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "os" 9 "os/exec" 10 "strings" 11 12 "github.com/deislabs/cnab-go/driver" 13 ) 14 15 // Driver relies upon a system command to provide a driver implementation 16 type Driver struct { 17 Name string 18 } 19 20 // Run executes the command 21 func (d *Driver) Run(op *driver.Operation) (driver.OperationResult, error) { 22 return d.exec(op) 23 } 24 25 // Handles executes the driver with `--handles` and parses the results 26 func (d *Driver) Handles(dt string) bool { 27 out, err := exec.Command(d.cliName(), "--handles").CombinedOutput() 28 if err != nil { 29 fmt.Printf("%s --handles: %s", d.cliName(), err) 30 return false 31 } 32 types := strings.Split(string(out), ",") 33 for _, tt := range types { 34 if dt == strings.TrimSpace(tt) { 35 return true 36 } 37 } 38 return false 39 } 40 41 func (d *Driver) cliName() string { 42 return "cnab-" + strings.ToLower(d.Name) 43 } 44 45 func (d *Driver) exec(op *driver.Operation) (driver.OperationResult, error) { 46 // We need to do two things here: We need to make it easier for the 47 // command to access data, and we need to make it easy for the command 48 // to pass that data on to the image it invokes. So we do some data 49 // duplication. 50 51 // Construct an environment for the subprocess by cloning our 52 // environment and adding in all the extra env vars. 53 pairs := os.Environ() 54 added := []string{} 55 for k, v := range op.Environment { 56 pairs = append(pairs, fmt.Sprintf("%s=%s", k, v)) 57 added = append(added, k) 58 } 59 // CNAB_VARS is a list of variables we added to the env. This is to make 60 // it easier for shell script drivers to clone the env vars. 61 pairs = append(pairs, fmt.Sprintf("CNAB_VARS=%s", strings.Join(added, ","))) 62 63 data, err := json.Marshal(op) 64 if err != nil { 65 return driver.OperationResult{}, err 66 } 67 args := []string{} 68 cmd := exec.Command(d.cliName(), args...) 69 cmd.Dir, err = os.Getwd() 70 if err != nil { 71 return driver.OperationResult{}, err 72 } 73 cmd.Env = pairs 74 cmd.Stdin = bytes.NewBuffer(data) 75 76 // Make stdout and stderr from driver available immediately 77 78 stdout, err := cmd.StdoutPipe() 79 if err != nil { 80 return driver.OperationResult{}, fmt.Errorf("Setting up output handling for driver (%s) failed: %v", d.Name, err) 81 } 82 83 go func() { 84 85 // Errors not handled here as they only prevent output from the driver being shown, errors in the command execution are handled when command is executed 86 87 io.Copy(op.Out, stdout) 88 }() 89 stderr, err := cmd.StderrPipe() 90 if err != nil { 91 return driver.OperationResult{}, fmt.Errorf("Setting up error output handling for driver (%s) failed: %v", d.Name, err) 92 } 93 go func() { 94 95 // Errors not handled here as they only prevent output from the driver being shown, errors in the command execution are handled when command is executed 96 97 io.Copy(op.Out, stderr) 98 }() 99 100 if err = cmd.Start(); err != nil { 101 return driver.OperationResult{}, fmt.Errorf("Start of driver (%s) failed: %v", d.Name, err) 102 } 103 104 return driver.OperationResult{}, cmd.Wait() 105 }