Documentation ¶
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var AttrCmd = cli.Command{ Category: "ATTRIBUTES", Name: "attr", Usage: "get, set, list attributes", Subcommands: []cli.Command{ AttrSetCmd, AttrGetCmd, AttrListCmd, }, }
View Source
var AttrGetCmd = cli.Command{ Name: "get", Usage: "get an attribute for a node", ArgsUsage: "<node> <attr>", Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") if c.NArg() != 2 { return NewUsageError("get takes exactly 2 argument") } argNode := c.Args()[0] argAttr := c.Args()[1] i, err := strconv.Atoi(argNode) if err != nil { return fmt.Errorf("parse err: %s", err) } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) node, err := tb.Node(i) if err != nil { return err } attrNode, ok := node.(testbedi.Attribute) if !ok { return fmt.Errorf("node does not implement attributes") } value, err := attrNode.Attr(argAttr) if err != nil { return err } _, err = fmt.Fprintf(c.App.Writer, "%s\n", value) return err }, }
View Source
var AttrListCmd = cli.Command{ Name: "list", Usage: "list attributes available for a node", ArgsUsage: "<node>", Flags: []cli.Flag{ cli.StringFlag{ Name: "type", Usage: "look up attributes for node type", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagType := c.String("type") if !c.Args().Present() && len(flagType) == 0 { return NewUsageError("specify a node, or a type") } if c.Args().Present() { i, err := strconv.Atoi(c.Args().First()) if err != nil { return fmt.Errorf("parse err: %s", err) } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) spec, err := tb.Spec(i) if err != nil { return err } flagType = spec.Type } plg, ok := testbed.GetPlugin(flagType) if !ok { return fmt.Errorf("Unknown plugin %s", flagType) } attrList := plg.GetAttrList() for _, a := range attrList { desc, err := plg.GetAttrDesc(a) if err != nil { return fmt.Errorf("error getting attribute description: %s", err) } fmt.Fprintf(c.App.Writer, "\t%s: %s\n", a, desc) } return nil }, }
View Source
var AttrSetCmd = cli.Command{ Name: "set", Usage: "set an attribute for a node", ArgsUsage: "<node> <attr> <value>", Flags: []cli.Flag{ cli.BoolFlag{ Name: "save", Usage: "saves attribute value to nodespec", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagSave := c.Bool("save") if c.NArg() != 3 { return NewUsageError("set takes exactly 3 argument") } argNode := c.Args()[0] argAttr := c.Args()[1] argValue := c.Args()[2] i, err := strconv.Atoi(argNode) if err != nil { return fmt.Errorf("parse err: %s", err) } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) node, err := tb.Node(i) if err != nil { return err } attrNode, ok := node.(testbedi.Attribute) if !ok { return fmt.Errorf("node does not implement attributes") } if err := attrNode.SetAttr(argAttr, argValue); err != nil { return err } if flagSave { specs, err := tb.Specs() if err != nil { return err } specs[i].SetAttr(argAttr, argValue) if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil { return err } } return nil }, }
View Source
var AutoCmd = cli.Command{ Name: "auto", Usage: "create default testbed and initialize", Description: ` The auto command is a quick way to use iptb for simple configurations. The auto command is similar to 'testbed create' except in a few ways - No attr options can be passed in - All nodes are initialize by default and ready to be started - An optional --start flag can be passed to start all nodes The following two examples are equivalent $ iptb testbed create -count 5 -type <type> -init $ iptb auto -count 5 -type <type> `, ArgsUsage: "--type <type>", Flags: []cli.Flag{ cli.IntFlag{ Name: "count", Usage: "number of nodes to initialize", Value: 1, }, cli.BoolFlag{ Name: "force", Usage: "force overwrite of existing nodespecs", }, cli.StringFlag{ Name: "type", Usage: "kind of nodes to initialize", }, cli.BoolFlag{ Name: "start", Usage: "starts nodes immediately", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") flagType := c.String("type") flagStart := c.Bool("start") flagCount := c.Int("count") flagForce := c.Bool("force") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) if err := testbed.AlreadyInitCheck(tb.Dir(), flagForce); err != nil { return err } specs, err := testbed.BuildSpecs(tb.Dir(), flagCount, flagType, nil) if err != nil { return err } if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil { return err } nodes, err := tb.Nodes() if err != nil { return err } var list []int for i, _ := range nodes { list = append(list, i) } runCmd := func(node testbedi.Core) (testbedi.Output, error) { return node.Init(context.Background()) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } if err := buildReport(results, flagQuiet); err != nil { return err } if flagStart { runCmd := func(node testbedi.Core) (testbedi.Output, error) { return node.Start(context.Background(), true) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } if err := buildReport(results, flagQuiet); err != nil { return err } } return nil }, }
View Source
var ConnectCmd = cli.Command{ Category: "CORE", Name: "connect", Usage: "connect sets of nodes together (or all)", ArgsUsage: "[nodes] [nodes]", Description: ` The connect command allows for connecting sets of nodes together. Every node listed in the first set, will try to connect to every node listed in the second set. There are three variants of the command. It can accept no arugments, a single argument, or two arguments. The no argument and single argument expands out to the two argument usage. $ iptb connect => iptb connect [0-C] [0-C] $ iptb connect [n-m] => iptb connect [n-m] [n-m] $ iptb connect [n-m] [i-k] Sets of nodes can be expressed in the following ways INPUT EXPANDED 0 0 [0] 0 [0-4] 0,1,2,3,4 [0,2-4] 0,2,3,4 [2-4,0] 2,3,4,0 [0,2,4] 0,2,4 `, Flags: []cli.Flag{ cli.StringFlag{ Name: "timeout", Usage: "timeout on the command", Value: "30s", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") flagTimeout := c.String("timeout") timeout, err := time.ParseDuration(flagTimeout) if err != nil { return err } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) args := c.Args() var results []Result switch c.NArg() { case 0: nodes, err := tb.Nodes() if err != nil { return err } fromto, err := parseRange(fmt.Sprintf("[0-%d]", len(nodes)-1)) if err != nil { return err } results, err = connectNodes(tb, fromto, fromto, timeout) if err != nil { return err } case 1: fromto, err := parseRange(args[0]) if err != nil { return err } results, err = connectNodes(tb, fromto, fromto, timeout) if err != nil { return err } case 2: from, err := parseRange(args[0]) if err != nil { return err } to, err := parseRange(args[1]) if err != nil { return err } results, err = connectNodes(tb, from, to, timeout) if err != nil { return err } default: return NewUsageError("connet accepts between 0 and 2 arguments") } return buildReport(results, flagQuiet) }, }
View Source
var EventsCmd = cli.Command{ Category: "METRICS", Name: "events", Usage: "stream events from specified nodes (or all)", ArgsUsage: "[node]", Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") if !c.Args().Present() { return NewUsageError("events takes exactly 1 argument") } i, err := strconv.Atoi(c.Args().First()) if err != nil { return fmt.Errorf("parse err: %s", err) } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) node, err := tb.Node(i) if err != nil { return err } mn, ok := node.(testbedi.Metric) if !ok { return fmt.Errorf("node does not implement metrics") } el, err := mn.Events() if err != nil { return err } _, err = io.Copy(c.App.Writer, el) return err }, }
View Source
var InitCmd = cli.Command{ Category: "CORE", Name: "init", Usage: "initialize specified nodes (or all)", ArgsUsage: "[nodes] -- [arguments...]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "terminator", Hidden: true, }, }, Before: func(c *cli.Context) error { if present := isTerminatorPresent(c); present { return c.Set("terminator", "true") } return nil }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } nodeRange, args := parseCommand(c.Args(), c.IsSet("terminator")) if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return fmt.Errorf("could not parse node range %s", nodeRange) } runCmd := func(node testbedi.Core) (testbedi.Output, error) { return node.Init(context.Background(), args...) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } return buildReport(results, flagQuiet) }, }
View Source
var LogsCmd = cli.Command{ Category: "METRICS", Name: "logs", Usage: "show logs from specified nodes (or all)", ArgsUsage: "[nodes]", Flags: []cli.Flag{ cli.BoolTFlag{ Name: "err, e", Usage: "show stderr stream", }, cli.BoolTFlag{ Name: "out, o", Usage: "show stdout stream", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") flagErr := c.BoolT("err") flagOut := c.BoolT("out") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } nodeRange := c.Args().First() if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return err } runCmd := func(node testbedi.Core) (testbedi.Output, error) { metricNode, ok := node.(testbedi.Metric) if !ok { return nil, fmt.Errorf("node does not implement metrics") } stdout := ioutil.NopCloser(strings.NewReader("")) stderr := ioutil.NopCloser(strings.NewReader("")) if flagOut { var err error stdout, err = metricNode.StdoutReader() if err != nil { return nil, err } } if flagErr { var err error stderr, err = metricNode.StderrReader() if err != nil { return nil, err } } return NewOutput(stdout, stderr), nil } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } return buildReport(results, flagQuiet) }, }
View Source
var MetricCmd = cli.Command{ Category: "METRICS", Name: "metric", Usage: "get metric from node", ArgsUsage: "<node> [metric]", Action: func(c *cli.Context) error { if c.NArg() == 1 { return metricList(c) } if c.NArg() == 2 { return metricGet(c) } return NewUsageError("metric takes 1 or 2 arguments only") }, }
View Source
var RestartCmd = cli.Command{ Category: "CORE", Name: "restart", Usage: "restart specified nodes (or all)", ArgsUsage: "[nodes] -- [arguments...]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "wait", Usage: "wait for nodes to start before returning", }, cli.BoolFlag{ Name: "terminator", Hidden: true, }, }, Before: func(c *cli.Context) error { if present := isTerminatorPresent(c); present { return c.Set("terminator", "true") } return nil }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") flagWait := c.Bool("wait") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } nodeRange, args := parseCommand(c.Args(), c.IsSet("terminator")) if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return fmt.Errorf("could not parse node range %s", nodeRange) } runCmd := func(node testbedi.Core) (testbedi.Output, error) { if err := node.Stop(context.Background()); err != nil { return nil, err } return node.Start(context.Background(), flagWait, args...) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } return buildReport(results, flagQuiet) }, }
View Source
var RunCmd = cli.Command{ Category: "CORE", Name: "run", Usage: "concurrently run command(s) on specified nodes (or all)", ArgsUsage: "[nodes] -- <command...>", Description: ` Commands may also be passed in via stdin or a pipe, e.g. the command $ iptb run 0 -- echo "Running on node 0" can be equivalently written as $ iptb run <<CMD 0 -- echo "Running on node 0" CMD or $ echo '0 -- echo "Running on node 0"' | iptb run All lines starting with '#' will be ignored, which allows for comments: $ iptb run <<CMD # print ipfs peers 0 -- ipfs swarm peers CMD Multiple commands may also be passed via stdin/pipe: $ iptb run <<CMDS 0 -- echo "Running on node 0" [0,1] -- echo "Running on nodes 0 and 1" -- echo "Running on all nodes" CMDS Note that any single call to ` + "`iptb run`" + ` runs *all* commands concurrently. So, in the above example, there is no guarantee as to the order in which the lines are printed. `, Flags: []cli.Flag{ cli.BoolFlag{ Name: "terminator", Hidden: true, }, cli.BoolFlag{ Name: "stdin", Hidden: true, }, }, Before: func(c *cli.Context) error { if c.NArg() == 0 { finfo, err := os.Stdin.Stat() if err != nil { return err } if finfo.Size() == 0 && finfo.Mode()&os.ModeNamedPipe == 0 { return fmt.Errorf("error: no command input and stdin is empty") } return c.Set("stdin", "true") } if present := isTerminatorPresent(c); present { return c.Set("terminator", "true") } return nil }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } var reader io.Reader if c.IsSet("stdin") { reader = bufio.NewReader(os.Stdin) } else { var builder strings.Builder if c.IsSet("terminator") { builder.WriteString("-- ") } for i, arg := range c.Args() { builder.WriteString(strconv.Quote(arg)) if i != c.NArg()-1 { builder.WriteString(" ") } } reader = strings.NewReader(builder.String()) } var args [][]string scanner := bufio.NewScanner(reader) line := 1 for scanner.Scan() { tokens, err := shellwords.Parse(scanner.Text()) if err != nil { return fmt.Errorf("parse error on line %d: %s", line, err) } if strings.HasPrefix(tokens[0], "#") { continue } args = append(args, tokens) line++ } ranges := make([][]int, len(args)) runCmds := make([]outputFunc, len(args)) for i, cmd := range args { nodeRange, tokens := parseCommand(cmd, false) if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return fmt.Errorf("could not parse node range %s", nodeRange) } ranges[i] = list runCmd := func(node testbedi.Core) (testbedi.Output, error) { return node.RunCmd(context.Background(), nil, tokens...) } runCmds[i] = runCmd } results, err := mapListWithOutput(ranges, nodes, runCmds) return buildReport(results, flagQuiet) }, }
View Source
var ShellCmd = cli.Command{ Category: "CORE", Name: "shell", Usage: "starts a shell within the context of node", ArgsUsage: "<node>", Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") if !c.Args().Present() { return NewUsageError("shell takes exactly 1 argument") } i, err := strconv.Atoi(c.Args().First()) if err != nil { return fmt.Errorf("parse err: %s", err) } tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } return nodes[i].Shell(context.Background(), nodes) }, }
View Source
var StartCmd = cli.Command{ Category: "CORE", Name: "start", Usage: "start specified nodes (or all)", ArgsUsage: "[nodes] -- [arguments...]", Flags: []cli.Flag{ cli.BoolFlag{ Name: "wait", Usage: "wait for nodes to start before returning", }, cli.BoolFlag{ Name: "terminator", Hidden: true, }, }, Before: func(c *cli.Context) error { if present := isTerminatorPresent(c); present { return c.Set("terminator", "true") } return nil }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") flagWait := c.Bool("wait") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } nodeRange, args := parseCommand(c.Args(), c.IsSet("terminator")) if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return fmt.Errorf("could not parse node range %s", nodeRange) } runCmd := func(node testbedi.Core) (testbedi.Output, error) { return node.Start(context.Background(), flagWait, args...) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } return buildReport(results, flagQuiet) }, }
View Source
var StopCmd = cli.Command{ Category: "CORE", Name: "stop", Usage: "stop specified nodes (or all)", ArgsUsage: "[nodes]", Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagQuiet := c.GlobalBool("quiet") tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) nodes, err := tb.Nodes() if err != nil { return err } nodeRange := c.Args().First() if nodeRange == "" { nodeRange = fmt.Sprintf("[0-%d]", len(nodes)-1) } list, err := parseRange(nodeRange) if err != nil { return fmt.Errorf("could not parse node range %s", nodeRange) } runCmd := func(node testbedi.Core) (testbedi.Output, error) { return nil, node.Stop(context.Background()) } results, err := mapWithOutput(list, nodes, runCmd) if err != nil { return err } return buildReport(results, flagQuiet) }, }
View Source
var TestbedCmd = cli.Command{ Name: "testbed", Usage: "manage testbeds", Subcommands: []cli.Command{ TestbedCreateCmd, }, }
View Source
var TestbedCreateCmd = cli.Command{ Name: "create", Usage: "create testbed", ArgsUsage: "--type <type>", Flags: []cli.Flag{ cli.IntFlag{ Name: "count", Usage: "number of nodes to initialize", Value: 1, }, cli.BoolFlag{ Name: "force", Usage: "force overwrite of existing testbed", }, cli.StringFlag{ Name: "type", Usage: "kind of nodes to initialize", }, cli.StringSliceFlag{ Name: "attr", Usage: "specify addition attributes for nodes", }, cli.BoolFlag{ Name: "init", Usage: "initialize after creation (like calling `init` after create)", }, }, Action: func(c *cli.Context) error { flagRoot := c.GlobalString("IPTB_ROOT") flagTestbed := c.GlobalString("testbed") flagType := c.String("type") flagInit := c.Bool("init") flagCount := c.Int("count") flagForce := c.Bool("force") flagAttrs := c.StringSlice("attr") attrs := parseAttrSlice(flagAttrs) tb := testbed.NewTestbed(path.Join(flagRoot, "testbeds", flagTestbed)) if err := testbed.AlreadyInitCheck(tb.Dir(), flagForce); err != nil { return err } specs, err := testbed.BuildSpecs(tb.Dir(), flagCount, flagType, attrs) if err != nil { return err } if err := testbed.WriteNodeSpecs(tb.Dir(), specs); err != nil { return err } if flagInit { nodes, err := tb.Nodes() if err != nil { return err } for _, n := range nodes { if _, err := n.Init(context.Background()); err != nil { return err } } } return nil }, }
Functions ¶
func NewOutput ¶
func NewOutput(stdout, stderr io.ReadCloser) testbedi.Output
func NewUsageError ¶
Types ¶
type Output ¶
type Output struct {
// contains filtered or unexported fields
}
func (*Output) Stderr ¶
func (o *Output) Stderr() io.ReadCloser
func (*Output) Stdout ¶
func (o *Output) Stdout() io.ReadCloser
type UsageError ¶
type UsageError struct {
// contains filtered or unexported fields
}
func (*UsageError) Error ¶
func (e *UsageError) Error() string
Click to show internal directories.
Click to hide internal directories.