func meshageHandler() { for { m := <-meshageCommandChan go func() { mCmd := m.Body.(meshageCommand) cmd, err := minicli.CompileCommand(mCmd.Original) if err != nil { log.Error("invalid command from mesh: `%s`", mCmd.Original) return } resps := []minicli.Responses{} for resp := range runCommand(cmd, true) { resps = append(resps, resp) } if len(resps) > 1 || len(resps[0]) > 1 { // This should never happen because the only commands that // return multiple responses are `read` and `mesh send` which // aren't supposed to be sent across meshage. log.Error("unsure how to process multiple responses!!") } resp := meshageResponse{Response: *resps[0][0], TID: mCmd.TID} recipient := []string{m.Source} _, err = meshageNode.Set(recipient, resp) if err != nil { log.Errorln(err) } }() } }
func cliRead(c *minicli.Command, respChan chan minicli.Responses) { resp := &minicli.Response{Host: hostname} file, err := os.Open(c.StringArgs["file"]) if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} return } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { var cmd *minicli.Command command := scanner.Text() log.Debug("read command: %v", command) // commands don't have their newlines removed cmd, err = minicli.CompileCommand(command) if err != nil { break } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Make sure we don't have a recursive read command if hasCommand(cmd, "read") { err = errors.New("cannot run nested `read` commands") break } for resp := range minicli.ProcessCommand(cmd, true) { respChan <- resp // Stop processing at the first error if there is one response. // TODO: What should we do if the command was mesh send and there // is a mixture of success and failure? if len(resp) == 1 && resp[0].Error != "" { break } } } if err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } if err := scanner.Err(); err != nil { resp.Error = err.Error() respChan <- minicli.Responses{resp} } }
func readLocalCommand(dec *json.Decoder) (*minicli.Command, error) { var cmd minicli.Command err := dec.Decode(&cmd) if err != nil { return nil, err } log.Debug("got command over socket: %v", cmd) // HAX: Reprocess the original command since the Call target cannot be // serialized... is there a cleaner way to do this? return minicli.CompileCommand(cmd.Original) }
func TestParseInject(t *testing.T) { for _, cmdStr := range validInjectCommands { cmd, err := minicli.CompileCommand(cmdStr) if err != nil { t.Fatalf("invalid command: %s", cmdStr) } inject := parseInject(cmd) if inject.err != nil { t.Errorf("unable to parse `%s`: %v", cmdStr, inject.err) } } }
// Wrapper for minicli.ProcessCommand for commands that use meshage. // Specifically, for `mesh send all ...`, runs the subcommand locally and // across meshage, combining the results from the two channels into a single // channel. This is useful if you want to get the output of a command from all // nodes in the cluster without having to run a command locally and over // meshage. func runCommandGlobally(cmd *minicli.Command, record bool) chan minicli.Responses { cmdStr := fmt.Sprintf("mesh send %s %s", Wildcard, cmd.Original) cmd, err := minicli.CompileCommand(cmdStr) if err != nil { log.Fatal("cannot run `%v` globally -- %v", cmd.Original, err) } cmdLock.Lock() defer cmdLock.Unlock() var wg sync.WaitGroup out := make(chan minicli.Responses) // Run the command (should be `mesh send all ...` and the subcommand which // should run locally). ins := []chan minicli.Responses{ minicli.ProcessCommand(cmd, record), minicli.ProcessCommand(cmd.Subcommand, record), } // De-mux ins into out for _, in := range ins { wg.Add(1) go func(in chan minicli.Responses) { defer wg.Done() for v := range in { out <- v } }(in) } // Wait until everything has been read before closing out go func() { wg.Wait() close(out) }() return out }
// local command line interface, wrapping readline func cliLocal() { prompt := "minimega$ " for { line, err := goreadline.Rlwrap(prompt, true) if err != nil { break // EOF } command := string(line) log.Debug("got from stdin:", command) cmd, err := minicli.CompileCommand(command) if err != nil { log.Error("%v", err) //fmt.Printf("closest match: TODO\n") continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } // HAX: Don't record the read command record := !hasCommand(cmd, "read") for resp := range runCommand(cmd, record) { // print the responses pageOutput(resp.String()) errs := resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } } }
func webHosts(w http.ResponseWriter, r *http.Request) { table := htmlTable{ Header: []string{}, Tabular: [][]interface{}{}, ID: "example", Class: "hover", } cmd, err := minicli.CompileCommand("host") if err != nil { // Should never happen log.Fatalln(err) } for resps := range runCommandGlobally(cmd, false) { for _, resp := range resps { if resp.Error != "" { log.Errorln(resp.Error) continue } if len(table.Header) == 0 && len(resp.Header) > 0 { table.Header = append(table.Header, resp.Header...) } for _, row := range resp.Tabular { res := []interface{}{} for _, v := range row { res = append(res, v) } table.Tabular = append(table.Tabular, res) } } } webRenderTemplate(w, "hosts.html", table) }
func localCommand() { a := flag.Args() log.Debugln("got args:", a) command := strings.Join(a, " ") // TODO: Need to escape? cmd, err := minicli.CompileCommand(command) if err != nil { log.Fatal(err.Error()) } if cmd == nil { log.Debugln("cmd is nil") return } log.Infoln("got command:", cmd) mm, err := DialMinimega() if err != nil { log.Fatalln(err) } for resp := range mm.runCommand(cmd) { if resp.Rendered != "" { fmt.Println(resp.Rendered) } errs := resp.Resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } }
func cliAttach() { // try to connect to the local minimega mm, err := DialMinimega() if err != nil { log.Fatalln(err) } // set up signal handling sig := make(chan os.Signal, 1024) signal.Notify(sig, os.Interrupt, syscall.SIGTERM) go func() { <-sig if *f_panic { panic("teardown") } log.Debug("caught signal, disconnecting") goreadline.Rlcleanup() os.Exit(0) }() // start our own rlwrap fmt.Println("CAUTION: calling 'quit' will cause the minimega daemon to exit") fmt.Println("use 'disconnect' or ^d to exit just the minimega command line") fmt.Println() defer goreadline.Rlcleanup() var exitNext bool for { prompt := fmt.Sprintf("minimega:%v$ ", mm.url) line, err := goreadline.Rlwrap(prompt, true) if err != nil { return } command := string(line) log.Debug("got from stdin: `%s`", line) // HAX: Shortcut some commands without using minicli if command == "disconnect" { log.Debugln("disconnecting") return } else if command == "quit" { if !exitNext { fmt.Println("CAUTION: calling 'quit' will cause the minimega daemon to exit") fmt.Println("If you really want to stop the minimega daemon, enter 'quit' again") exitNext = true continue } } exitNext = false cmd, err := minicli.CompileCommand(command) if err != nil { log.Error("%v", err) //fmt.Println("closest match: TODO") continue } // No command was returned, must have been a blank line or a comment // line. Either way, don't try to run a nil command. if cmd == nil { continue } for resp := range mm.runCommand(cmd) { pageOutput(resp.Rendered) errs := resp.Resp.Error() if errs != "" { fmt.Fprintln(os.Stderr, errs) } } if command == "quit" { return } } }