this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

cmd/cue/cmd: factor out task code

Move to directories in which the corresponding
.cue files will be put.

Updates #39

Change-Id: If3cb498b9e3fe6e10905c4d461c2942d9fbbd997
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/1923
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>

authored by

Marcel van Lohuizen and committed by
Marcel van Lohuizen
10fd4964 c47539e6

+352 -198
+14 -198
cmd/cue/cmd/custom.go
··· 26 26 "net/http" 27 27 "net/http/httptest" 28 28 "os" 29 - "os/exec" 30 - "strings" 31 29 "sync" 32 30 33 31 "cuelang.org/go/cue" 32 + itask "cuelang.org/go/internal/task" 33 + _ "cuelang.org/go/pkg/tool/cli" // Register tasks 34 + _ "cuelang.org/go/pkg/tool/exec" 35 + _ "cuelang.org/go/pkg/tool/http" 34 36 "github.com/spf13/cobra" 35 37 "golang.org/x/sync/errgroup" 36 - "golang.org/x/xerrors" 37 38 ) 38 39 39 40 const ( ··· 187 188 m.Lock() 188 189 obj := tasks.Lookup(t.name) 189 190 m.Unlock() 190 - update, err := t.Run(ctx, obj) 191 + update, err := t.Run(&itask.Context{ctx, stdout, stderr}, obj) 191 192 if err == nil && update != nil { 192 193 m.Lock() 193 194 root, err = root.Fill(update, spec.taskPath(t.name)...) ··· 242 243 } 243 244 244 245 type task struct { 245 - Runner 246 + itask.Runner 246 247 247 248 index int 248 249 name string ··· 255 256 if err != nil { 256 257 return nil, err 257 258 } 258 - rf, ok := runners[kind] 259 - if !ok { 259 + rf := itask.Lookup(kind) 260 + if rf == nil { 260 261 return nil, fmt.Errorf("runner of kind %q not found", kind) 261 262 } 262 263 runner, err := rf(v) ··· 272 273 }, nil 273 274 } 274 275 275 - // A Runner defines a command type. 276 - type Runner interface { 277 - // Init is called with the original configuration before any task is run. 278 - // As a result, the configuration may be incomplete, but allows some 279 - // validation before tasks are kicked off. 280 - // Init(v cue.Value) 281 - 282 - // Runner runs given the current value and returns a new value which is to 283 - // be unified with the original result. 284 - Run(ctx context.Context, v cue.Value) (results interface{}, err error) 285 - } 286 - 287 - // A RunnerFunc creates a Runner. 288 - type RunnerFunc func(v cue.Value) (Runner, error) 289 - 290 - var runners = map[string]RunnerFunc{ 291 - "print": newPrintCmd, 292 - "exec": newExecCmd, 293 - "http": newHTTPCmd, 294 - "testserver": newTestServerCmd, 295 - } 296 - 297 - type printCmd struct{} 298 - 299 - func newPrintCmd(v cue.Value) (Runner, error) { 300 - return &printCmd{}, nil 301 - } 302 - 303 - func (c *printCmd) Run(ctx context.Context, v cue.Value) (res interface{}, err error) { 304 - str, err := v.Lookup("text").String() 305 - if err != nil { 306 - return nil, err 307 - } 308 - fmt.Fprintln(stdout, str) 309 - return nil, nil 310 - } 311 - 312 - type execCmd struct{} 313 - 314 - func newExecCmd(v cue.Value) (Runner, error) { 315 - return &execCmd{}, nil 316 - } 317 - 318 - func (c *execCmd) Run(ctx context.Context, v cue.Value) (res interface{}, err error) { 319 - // TODO: set environment variables, if defined. 320 - var bin string 321 - var args []string 322 - doc := "" 323 - switch v := v.Lookup("cmd"); v.Kind() { 324 - case cue.StringKind: 325 - str, _ := v.String() 326 - if str == "" { 327 - return cue.Value{}, errors.New("empty command") 328 - } 329 - doc = str 330 - list := strings.Fields(str) 331 - bin = list[0] 332 - for _, s := range list[1:] { 333 - args = append(args, s) 334 - } 335 - 336 - case cue.ListKind: 337 - list, _ := v.List() 338 - if !list.Next() { 339 - return cue.Value{}, errors.New("empty command list") 340 - } 341 - bin, err = list.Value().String() 342 - if err != nil { 343 - return cue.Value{}, err 344 - } 345 - doc += bin 346 - for list.Next() { 347 - str, err := list.Value().String() 348 - if err != nil { 349 - return cue.Value{}, err 350 - } 351 - args = append(args, str) 352 - doc += " " + str 353 - } 354 - } 355 - 356 - cmd := exec.CommandContext(ctx, bin, args...) 357 - 358 - if v := v.Lookup("stdin"); v.IsValid() { 359 - if cmd.Stdin, err = v.Reader(); err != nil { 360 - return nil, fmt.Errorf("cue: %v", err) 361 - } 362 - } 363 - captureOut := v.Lookup("stdout").Exists() 364 - if !captureOut { 365 - cmd.Stdout = stdout 366 - } 367 - captureErr := v.Lookup("stderr").Exists() 368 - if !captureErr { 369 - cmd.Stderr = stderr 370 - } 371 - 372 - update := map[string]interface{}{} 373 - if captureOut { 374 - var stdout []byte 375 - stdout, err = cmd.Output() 376 - update["stdout"] = string(stdout) 377 - } else { 378 - err = cmd.Run() 379 - } 380 - update["success"] = err == nil 381 - if err != nil { 382 - if exit := (*exec.ExitError)(nil); xerrors.As(err, &exit) && captureErr { 383 - update["stderr"] = string(exit.Stderr) 384 - } else { 385 - update = nil 386 - } 387 - err = fmt.Errorf("command %q failed: %v", doc, err) 388 - } 389 - return update, err 390 - } 391 - 392 - type httpCmd struct{} 393 - 394 - func newHTTPCmd(v cue.Value) (Runner, error) { 395 - return &httpCmd{}, nil 396 - } 397 - 398 - func (c *httpCmd) Run(ctx context.Context, v cue.Value) (res interface{}, err error) { 399 - // v.Validate() 400 - var header, trailer http.Header 401 - method := lookupString(v, "method") 402 - u := lookupString(v, "url") 403 - var r io.Reader 404 - if obj := v.Lookup("request"); v.Exists() { 405 - if v := obj.Lookup("body"); v.Exists() { 406 - r, err = v.Reader() 407 - if err != nil { 408 - return nil, err 409 - } 410 - } 411 - if header, err = parseHeaders(obj, "header"); err != nil { 412 - return nil, err 413 - } 414 - if trailer, err = parseHeaders(obj, "trailer"); err != nil { 415 - return nil, err 416 - } 417 - } 418 - req, err := http.NewRequest(method, u, r) 419 - if err != nil { 420 - return nil, err 421 - } 422 - req.Header = header 423 - req.Trailer = trailer 424 - 425 - // TODO: 426 - // - retry logic 427 - // - TLS certs 428 - resp, err := http.DefaultClient.Do(req) 429 - if err != nil { 430 - return nil, err 431 - } 432 - defer resp.Body.Close() 433 - b, err := ioutil.ReadAll(resp.Body) 434 - // parse response body and headers 435 - return map[string]interface{}{ 436 - "response": map[string]interface{}{ 437 - "body": string(b), 438 - "header": resp.Header, 439 - "trailer": resp.Trailer, 440 - }, 441 - }, err 276 + func isValid(v cue.Value) bool { 277 + return v.Kind() == cue.BottomKind 442 278 } 443 279 444 - func parseHeaders(obj cue.Value, label string) (http.Header, error) { 445 - m := obj.Lookup(label) 446 - if !m.Exists() { 447 - return nil, nil 448 - } 449 - iter, err := m.Fields() 450 - if err != nil { 451 - return nil, err 452 - } 453 - var h http.Header 454 - for iter.Next() { 455 - str, err := iter.Value().String() 456 - if err != nil { 457 - return nil, err 458 - } 459 - h.Add(iter.Label(), str) 460 - } 461 - return h, nil 462 - } 463 - 464 - func isValid(v cue.Value) bool { 465 - return v.Kind() == cue.BottomKind 280 + func init() { 281 + itask.Register("testserver", newTestServerCmd) 466 282 } 467 283 468 284 var testOnce sync.Once 469 285 470 - func newTestServerCmd(v cue.Value) (Runner, error) { 286 + func newTestServerCmd(v cue.Value) (itask.Runner, error) { 471 287 server := "" 472 288 testOnce.Do(func() { 473 289 s := httptest.NewServer(http.HandlerFunc( ··· 487 303 488 304 type testServerCmd string 489 305 490 - func (s testServerCmd) Run(ctx context.Context, v cue.Value) (x interface{}, err error) { 306 + func (s testServerCmd) Run(ctx *itask.Context, v cue.Value) (x interface{}, err error) { 491 307 return map[string]interface{}{"url": string(s)}, nil 492 308 }
+5
cue/gen.go
··· 320 320 } 321 321 if n := len(types); n != 1 && (n != 2 || types[1] != "error") { 322 322 fmt.Printf("Dropped func %s.%s: must have one return value or a value and an error %v\n", g.defaultPkg, x.Name.Name, types) 323 + return 324 + } 325 + 326 + if !ast.IsExported(x.Name.Name) || x.Recv != nil { 327 + return 323 328 } 324 329 325 330 g.sep()
+62
internal/task/task.go
··· 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 task provides a registry for tasks to be used by commands. 16 + package task 17 + 18 + import ( 19 + "context" 20 + "io" 21 + "sync" 22 + 23 + "cuelang.org/go/cue" 24 + ) 25 + 26 + // A Context provides context for running a task. 27 + type Context struct { 28 + Context context.Context 29 + Stdout io.Writer 30 + Stderr io.Writer 31 + } 32 + 33 + // A RunnerFunc creates a Runner. 34 + type RunnerFunc func(v cue.Value) (Runner, error) 35 + 36 + // A Runner defines a command type. 37 + type Runner interface { 38 + // Init is called with the original configuration before any task is run. 39 + // As a result, the configuration may be incomplete, but allows some 40 + // validation before tasks are kicked off. 41 + // Init(v cue.Value) 42 + 43 + // Runner runs given the current value and returns a new value which is to 44 + // be unified with the original result. 45 + Run(ctx *Context, v cue.Value) (results interface{}, err error) 46 + } 47 + 48 + // Register registers a task for cue commands. 49 + func Register(key string, f RunnerFunc) { 50 + runners.Store(key, f) 51 + } 52 + 53 + // Lookup returns the RunnerFunc for a key. 54 + func Lookup(key string) RunnerFunc { 55 + v, ok := runners.Load(key) 56 + if !ok { 57 + return nil 58 + } 59 + return v.(RunnerFunc) 60 + } 61 + 62 + var runners sync.Map
+45
pkg/tool/cli/cli.go
··· 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 cli provides tasks dealing with a console. 16 + package cli 17 + 18 + import ( 19 + "fmt" 20 + 21 + "cuelang.org/go/cue" 22 + "cuelang.org/go/internal/task" 23 + ) 24 + 25 + func init() { 26 + task.Register("tool/cli.Print", newPrintCmd) 27 + 28 + // For backwards compatibility. 29 + task.Register("print", newPrintCmd) 30 + } 31 + 32 + type printCmd struct{} 33 + 34 + func newPrintCmd(v cue.Value) (task.Runner, error) { 35 + return &printCmd{}, nil 36 + } 37 + 38 + func (c *printCmd) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) { 39 + str, err := v.Lookup("text").String() 40 + if err != nil { 41 + return nil, err 42 + } 43 + fmt.Fprintln(ctx.Stdout, str) 44 + return nil, nil 45 + }
+114
pkg/tool/exec/exec.go
··· 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 defines tasks for running commands. 16 + package exec 17 + 18 + import ( 19 + "errors" 20 + "fmt" 21 + "os/exec" 22 + "strings" 23 + 24 + "cuelang.org/go/cue" 25 + "cuelang.org/go/internal/task" 26 + "golang.org/x/xerrors" 27 + ) 28 + 29 + func init() { 30 + task.Register("tool/exec.Run", newExecCmd) 31 + 32 + // For backwards compatibility. 33 + task.Register("exec", newExecCmd) 34 + } 35 + 36 + type execCmd struct{} 37 + 38 + func newExecCmd(v cue.Value) (task.Runner, error) { 39 + return &execCmd{}, nil 40 + } 41 + 42 + func (c *execCmd) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) { 43 + // TODO: set environment variables, if defined. 44 + var bin string 45 + var args []string 46 + doc := "" 47 + switch v := v.Lookup("cmd"); v.Kind() { 48 + case cue.StringKind: 49 + str, _ := v.String() 50 + if str == "" { 51 + return cue.Value{}, errors.New("empty command") 52 + } 53 + doc = str 54 + list := strings.Fields(str) 55 + bin = list[0] 56 + for _, s := range list[1:] { 57 + args = append(args, s) 58 + } 59 + 60 + case cue.ListKind: 61 + list, _ := v.List() 62 + if !list.Next() { 63 + return cue.Value{}, errors.New("empty command list") 64 + } 65 + bin, err = list.Value().String() 66 + if err != nil { 67 + return cue.Value{}, err 68 + } 69 + doc += bin 70 + for list.Next() { 71 + str, err := list.Value().String() 72 + if err != nil { 73 + return cue.Value{}, err 74 + } 75 + args = append(args, str) 76 + doc += " " + str 77 + } 78 + } 79 + 80 + cmd := exec.CommandContext(ctx.Context, bin, args...) 81 + 82 + if v := v.Lookup("stdin"); v.IsValid() { 83 + if cmd.Stdin, err = v.Reader(); err != nil { 84 + return nil, fmt.Errorf("cue: %v", err) 85 + } 86 + } 87 + captureOut := v.Lookup("stdout").Exists() 88 + if !captureOut { 89 + cmd.Stdout = ctx.Stdout 90 + } 91 + captureErr := v.Lookup("stderr").Exists() 92 + if !captureErr { 93 + cmd.Stderr = ctx.Stderr 94 + } 95 + 96 + update := map[string]interface{}{} 97 + if captureOut { 98 + var stdout []byte 99 + stdout, err = cmd.Output() 100 + update["stdout"] = string(stdout) 101 + } else { 102 + err = cmd.Run() 103 + } 104 + update["success"] = err == nil 105 + if err != nil { 106 + if exit := (*exec.ExitError)(nil); xerrors.As(err, &exit) && captureErr { 107 + update["stderr"] = string(exit.Stderr) 108 + } else { 109 + update = nil 110 + } 111 + err = fmt.Errorf("command %q failed: %v", doc, err) 112 + } 113 + return update, err 114 + }
+112
pkg/tool/http/http.go
··· 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 http provides tasks related to the HTTP protocol. 16 + package http 17 + 18 + import ( 19 + "io" 20 + "io/ioutil" 21 + "net/http" 22 + 23 + "cuelang.org/go/cue" 24 + "cuelang.org/go/internal/task" 25 + ) 26 + 27 + func init() { 28 + task.Register("tool/http.Do", newHTTPCmd) 29 + 30 + // For backwards compatibility. 31 + task.Register("http", newHTTPCmd) 32 + } 33 + 34 + type httpCmd struct{} 35 + 36 + func newHTTPCmd(v cue.Value) (task.Runner, error) { 37 + return &httpCmd{}, nil 38 + } 39 + 40 + func lookupString(obj cue.Value, key string) string { 41 + str, err := obj.Lookup(key).String() 42 + if err != nil { 43 + return "" 44 + } 45 + return str 46 + } 47 + 48 + func (c *httpCmd) Run(ctx *task.Context, v cue.Value) (res interface{}, err error) { 49 + // v.Validate() 50 + var header, trailer http.Header 51 + method := lookupString(v, "method") 52 + u := lookupString(v, "url") 53 + var r io.Reader 54 + if obj := v.Lookup("request"); v.Exists() { 55 + if v := obj.Lookup("body"); v.Exists() { 56 + r, err = v.Reader() 57 + if err != nil { 58 + return nil, err 59 + } 60 + } 61 + if header, err = parseHeaders(obj, "header"); err != nil { 62 + return nil, err 63 + } 64 + if trailer, err = parseHeaders(obj, "trailer"); err != nil { 65 + return nil, err 66 + } 67 + } 68 + req, err := http.NewRequest(method, u, r) 69 + if err != nil { 70 + return nil, err 71 + } 72 + req.Header = header 73 + req.Trailer = trailer 74 + 75 + // TODO: 76 + // - retry logic 77 + // - TLS certs 78 + resp, err := http.DefaultClient.Do(req) 79 + if err != nil { 80 + return nil, err 81 + } 82 + defer resp.Body.Close() 83 + b, err := ioutil.ReadAll(resp.Body) 84 + // parse response body and headers 85 + return map[string]interface{}{ 86 + "response": map[string]interface{}{ 87 + "body": string(b), 88 + "header": resp.Header, 89 + "trailer": resp.Trailer, 90 + }, 91 + }, err 92 + } 93 + 94 + func parseHeaders(obj cue.Value, label string) (http.Header, error) { 95 + m := obj.Lookup(label) 96 + if !m.Exists() { 97 + return nil, nil 98 + } 99 + iter, err := m.Fields() 100 + if err != nil { 101 + return nil, err 102 + } 103 + var h http.Header 104 + for iter.Next() { 105 + str, err := iter.Value().String() 106 + if err != nil { 107 + return nil, err 108 + } 109 + h.Add(iter.Label(), str) 110 + } 111 + return h, nil 112 + }