github.com/solo-io/cue@v0.4.7/pkg/tool/exec/exec.go (about) 1 // Copyright 2019 CUE Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package exec 16 17 //go:generate go run gen.go 18 //go:generate gofmt -s -w . 19 20 import ( 21 "fmt" 22 "os/exec" 23 "strings" 24 25 "github.com/solo-io/cue/cue" 26 "github.com/solo-io/cue/cue/errors" 27 "github.com/solo-io/cue/internal/task" 28 ) 29 30 func init() { 31 task.Register("tool/exec.Run", newExecCmd) 32 33 // For backwards compatibility. 34 task.Register("exec", newExecCmd) 35 } 36 37 type execCmd struct{} 38 39 func newExecCmd(v cue.Value) (task.Runner, error) { 40 return &execCmd{}, nil 41 } 42 43 func (c *execCmd) Run(ctx *task.Context) (res interface{}, err error) { 44 cmd, doc, err := mkCommand(ctx) 45 if err != nil { 46 return cue.Value{}, err 47 } 48 49 // TODO: set environment variables, if defined. 50 stream := func(name string) (stream cue.Value, ok bool) { 51 c := ctx.Obj.Lookup(name) 52 // Although the schema defines a default versions, older implementations 53 // may not use it yet. 54 if !c.Exists() { 55 return 56 } 57 if err := c.Null(); ctx.Err != nil || err == nil { 58 return 59 } 60 return c, true 61 } 62 63 if v, ok := stream("stdin"); !ok { 64 cmd.Stdin = ctx.Stdin 65 } else if cmd.Stdin, err = v.Reader(); err != nil { 66 return nil, errors.Wrapf(err, v.Pos(), "invalid input") 67 } 68 _, captureOut := stream("stdout") 69 if !captureOut { 70 cmd.Stdout = ctx.Stdout 71 } 72 _, captureErr := stream("stderr") 73 if !captureErr { 74 cmd.Stderr = ctx.Stderr 75 } 76 77 update := map[string]interface{}{} 78 if captureOut { 79 var stdout []byte 80 stdout, err = cmd.Output() 81 update["stdout"] = string(stdout) 82 } else { 83 err = cmd.Run() 84 } 85 update["success"] = err == nil 86 if err != nil { 87 if exit := (*exec.ExitError)(nil); errors.As(err, &exit) && captureErr { 88 update["stderr"] = string(exit.Stderr) 89 } else { 90 update = nil 91 } 92 err = fmt.Errorf("command %q failed: %v", doc, err) 93 } 94 return update, err 95 } 96 97 func mkCommand(ctx *task.Context) (c *exec.Cmd, doc string, err error) { 98 var bin string 99 var args []string 100 101 v := ctx.Lookup("cmd") 102 if ctx.Err != nil { 103 return nil, "", ctx.Err 104 } 105 106 switch v.Kind() { 107 case cue.StringKind: 108 str := ctx.String("cmd") 109 doc = str 110 list := strings.Fields(str) 111 bin = list[0] 112 args = append(args, list[1:]...) 113 114 case cue.ListKind: 115 list, _ := v.List() 116 if !list.Next() { 117 return nil, "", errors.New("empty command list") 118 } 119 bin, err = list.Value().String() 120 if err != nil { 121 return nil, "", err 122 } 123 doc += bin 124 for list.Next() { 125 str, err := list.Value().String() 126 if err != nil { 127 return nil, "", err 128 } 129 args = append(args, str) 130 doc += " " + str 131 } 132 } 133 134 if bin == "" { 135 return nil, "", errors.New("empty command") 136 } 137 138 cmd := exec.CommandContext(ctx.Context, bin, args...) 139 140 cmd.Dir, _ = ctx.Obj.Lookup("dir").String() 141 142 env := ctx.Obj.Lookup("env") 143 144 // List case. 145 for iter, _ := env.List(); iter.Next(); { 146 str, err := iter.Value().String() 147 if err != nil { 148 return nil, "", errors.Wrapf(err, v.Pos(), 149 "invalid environment variable value %q", v) 150 } 151 cmd.Env = append(cmd.Env, str) 152 } 153 154 // Struct case. 155 for iter, _ := ctx.Obj.Lookup("env").Fields(); iter.Next(); { 156 label := iter.Label() 157 v := iter.Value() 158 var str string 159 switch v.Kind() { 160 case cue.StringKind: 161 str, _ = v.String() 162 case cue.IntKind, cue.FloatKind, cue.NumberKind: 163 str = fmt.Sprint(v) 164 default: 165 return nil, "", errors.Newf(v.Pos(), 166 "invalid environment variable value %q", v) 167 } 168 cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", label, str)) 169 } 170 171 return cmd, doc, nil 172 }