-
Notifications
You must be signed in to change notification settings - Fork 156
runproc sample #21
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
runproc sample #21
Changes from 11 commits
4377a7a
a9bb995
9c8903b
53ec5f4
cb0ea06
ca48604
0eb107b
a006468
b9b4e09
d8bd8ef
049ebd7
b5c5b7a
6e1fa05
ba9f674
7338ad0
af9af76
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,17 +8,23 @@ | |
| package main | ||
|
|
||
| import ( | ||
| "bufio" | ||
| "encoding/json" | ||
| "fmt" | ||
| "io" | ||
| "io/ioutil" | ||
| "os" | ||
| "time" | ||
|
|
||
| "github.com/gocircuit/circuit/client" | ||
| "github.com/gocircuit/circuit/client/docker" | ||
|
|
||
| "github.com/gocircuit/circuit/github.com/codegangsta/cli" | ||
| ) | ||
|
|
||
| //make this timeout come from the json input payload | ||
| var timeout = time.Duration(33) | ||
|
|
||
| // circuit mkproc /X1234/hola/charlie << EOF | ||
| // { … } | ||
| // EOF | ||
|
|
@@ -53,6 +59,131 @@ func mkproc(x *cli.Context) { | |
| } | ||
| } | ||
|
|
||
| func doRun(x *cli.Context, c *client.Client, cmd client.Cmd, path string) { | ||
|
|
||
| w2, _ := parseGlob(path) | ||
| a2 := c.Walk(w2) | ||
| _runproc(a2, cmd, x.Bool("tag")) | ||
|
|
||
| } | ||
|
|
||
| func runproc(x *cli.Context) { | ||
| defer func() { | ||
| if r := recover(); r != nil { | ||
| fatalf("error, likely due to missing server or misspelled anchor: %v", r) | ||
| } | ||
| }() | ||
| c := dial(x) | ||
| args := x.Args() | ||
|
|
||
| if len(args) != 1 && !x.Bool("all") { | ||
| fatalf("runproc needs an anchor argument or use the --all flag to do the entire circuit") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "to do the entire circuit" should be "to execute on every host in the circuit" |
||
| } | ||
| buf, _ := ioutil.ReadAll(os.Stdin) | ||
| var cmd client.Cmd | ||
| if err := json.Unmarshal(buf, &cmd); err != nil { | ||
| fatalf("command json not parsing: %v", err) | ||
| } | ||
| if x.Bool("scrub") { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cmd.Scrub should always be true, regardless of command-line arguments, since you are executing one-off jobs so you always want the system to remove the process element automatically when the process exits. |
||
| cmd.Scrub = true | ||
| } | ||
|
|
||
| el := string("") | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. el := ""
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ha, yea. |
||
| if len(cmd.Name) > 0 { | ||
| el = cmd.Name | ||
| } else { | ||
| el = cmd.Path | ||
| } | ||
|
|
||
| if x.Bool("all") { | ||
|
|
||
| w, _ := parseGlob("/") | ||
|
|
||
| anchor := c.Walk(w) | ||
|
|
||
| done := make(chan bool, 10) | ||
| procs := 0 | ||
|
|
||
| for _, a := range anchor.View() { | ||
|
|
||
| procs++ | ||
|
|
||
| go func(x *cli.Context, cmd client.Cmd, a string, done chan bool) { | ||
|
|
||
| doRun(x, c, cmd, a) | ||
| done <- true | ||
|
|
||
| }(x, cmd, a.Path()+"/"+el, done) | ||
|
|
||
| } | ||
|
|
||
| for ; procs > 0 ; procs-- { | ||
|
|
||
| select { | ||
| case <-done: | ||
| continue | ||
| case <-time.After(time.Second * timeout): | ||
| fmt.Fprintln(os.Stderr, "timeout waiting for the command") | ||
| } | ||
|
|
||
| } | ||
|
|
||
| } else { | ||
|
|
||
| doRun(x, c, cmd, args[0]+"/"+el) | ||
|
|
||
| } | ||
|
|
||
| } | ||
|
|
||
| func _runproc(a client.Anchor, cmd client.Cmd, tags bool) { | ||
|
|
||
| p, err := a.MakeProc(cmd) | ||
| if err != nil { | ||
| fatalf("mkproc error: %s", err) | ||
| } | ||
|
|
||
| q := p.Stdin() | ||
| if err := q.Close(); err != nil { | ||
| fatalf("error closing stdin: %v", err) | ||
| } | ||
|
|
||
| if tags { | ||
|
|
||
| done := make(chan bool) | ||
|
|
||
| go func(a string, r io.Reader, w io.Writer, d chan bool) { | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use PrefixReader or PrefixWriter from package kit/iomisc instead. |
||
| scanner := bufio.NewScanner(r) | ||
| for scanner.Scan() { | ||
| fmt.Fprintf(w, "%s %s\n", a, scanner.Text()) | ||
| } | ||
| if err := scanner.Err(); err != nil { | ||
| d <- false | ||
| } | ||
|
|
||
| d <- true | ||
|
|
||
| }(a.Path(), p.Stdout(), os.Stdout, done) | ||
|
|
||
| select { | ||
| case rv := <-done: | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are using the EOF from the process' stdout as an indicator of process exit. That's a bug. (What about stderr?) |
||
| if !rv { | ||
| fmt.Fprintln(os.Stderr, "error prefixing the data") | ||
| } | ||
| case <-time.After(time.Second * timeout): | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's bad design. If the user doesn't want to wait, they can CTRL-C or wrap the whole invocation to the circuit tool with a another tool that kills it on time out, or at the bare minimum the timeout should be a command-line flag with a default value. Picking arbitrary timeouts is not good. |
||
| fmt.Fprintln(os.Stderr, "timeout waiting for data") | ||
| } | ||
|
|
||
| } else { | ||
|
|
||
| io.Copy(os.Stdout, p.Stdout()) | ||
|
||
|
|
||
| } | ||
|
|
||
| a.Scrub() | ||
|
||
| } | ||
|
|
||
| func mkdkr(x *cli.Context) { | ||
| defer func() { | ||
| if r := recover(); r != nil { | ||
|
|
@@ -92,7 +223,9 @@ func sgnl(x *cli.Context) { | |
| fatalf("signal needs an anchor and a signal name arguments") | ||
| } | ||
| w, _ := parseGlob(args[1]) | ||
| u, ok := c.Walk(w).Get().(interface{Signal(string) error}) | ||
| u, ok := c.Walk(w).Get().(interface { | ||
| Signal(string) error | ||
| }) | ||
| if !ok { | ||
| fatalf("anchor is not a process or a docker container") | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should not be including a Name field in the command config. The names of commands are their anchor paths. (Configs and names are thus decoupled.)
If you simply want to run one-off commands and don't care what they are named, place all one off commands in
anchors like /X.../runproc/RANDOM_ID
If you want the user to specify the anchor of a one-off command that is executed, say, on all machines*, then you can take a name argument somewhere (command-line or otherwise), but you cannot add that name to the Cmd struct.
By the way, the name is really an anchor path without the first part.
I am not really sure why ever the user would want to name a one-off job that gets executed everywhere.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking of this for showing the the name in the tag, but if the command is a one-off and will be auto-scrubbed, then there's no point in doing a name. I agree, there's no need for this.