cmd/core/command.go (107 lines of code) (raw):
/*
Copyright (c) Facebook, Inc. and its affiliates.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.
*/
package core
import (
"fmt"
"strings"
"github.com/facebookincubator/fbender/cmd/core/errors"
"github.com/facebookincubator/fbender/cmd/core/options"
"github.com/facebookincubator/fbender/cmd/core/runner"
"github.com/spf13/cobra"
)
// CommandParams is used to generate params for the runner.
type CommandParams func(*cobra.Command, *options.Options) (*runner.Params, error)
// CommandTemplate groups parameters used to generate the test command.
type CommandTemplate struct {
// Command name it will be invoked with
Name string
// Short and long help messages
Short, Long string
// Usage examples can containt {test} string which will be replaced with the
// actual test name (throughput|concurrency)
Fixed, Constraints string
}
// NewTestCommand creates a protocol command with all the test subcommands.
// ${protocol} throughput fixed
// ${protocol} throughput constraints
// ${protocol} concurrency fixed
// ${protocol} concurrency constraints
//nolint:funlen
func NewTestCommand(c *CommandTemplate, p CommandParams) *cobra.Command {
// Help messages
var (
tShort = fmt.Sprintf("%s throughput (QPS)", c.Short)
tfShort = fmt.Sprintf("%s with fixed amount of QPS", tShort)
tcShort = fmt.Sprintf("%s with constraints", tShort)
cShort = fmt.Sprintf("%s concurrent connections", c.Short)
cfShort = fmt.Sprintf("%s with fixed number of connections", cShort)
ccShort = fmt.Sprintf("%s with constraints", cShort)
)
// Examples
var (
tfExamples = strings.ReplaceAll(c.Fixed, "{test}", "throughput")
tcExamples = strings.ReplaceAll(c.Constraints, "{test}", "throughput")
tExamples = fmt.Sprintf("%s\n%s", tfExamples, tcExamples)
cfExamples = strings.ReplaceAll(c.Fixed, "{test}", "concurrency")
ccExamples = strings.ReplaceAll(c.Constraints, "{test}", "concurrency")
cExamples = fmt.Sprintf("%s\n%s", cfExamples, ccExamples)
examples = fmt.Sprintf("%s\n%s", tExamples, cExamples)
)
// Top level command
command := &cobra.Command{
Use: c.Name,
Short: c.Short,
Long: fmt.Sprintf("%s.\n%s", c.Short, c.Long),
Example: examples,
}
// Subcommands
tCommand := &cobra.Command{
Use: "throughput",
Short: tShort,
Long: fmt.Sprintf("%s.\n%s", tShort, c.Long),
Example: tExamples,
}
cCommand := &cobra.Command{
Use: "concurrency",
Short: cShort,
Long: fmt.Sprintf("%s.\n%s", cShort, c.Long),
Example: cExamples,
}
command.AddCommand(tCommand)
command.AddCommand(cCommand)
// Throughput subcommands
tfCommand := &cobra.Command{
Use: "fixed",
Short: tfShort,
Long: fmt.Sprintf("%s.\n%s", tfShort, c.Long),
Example: tfExamples,
Args: fixedArgs,
RunE: RunLoadTestThroughputFixed(p),
}
tcCommand := &cobra.Command{
Use: "constraints",
Short: tcShort,
Long: fmt.Sprintf("%s.\n%s\n%s", tcShort, c.Long, ConstraintsHelp),
Example: tcExamples,
Args: constraintsArgs,
RunE: RunLoadTestThroughputConstraints(p),
}
tcCommand.PersistentFlags().AddFlagSet(ConstraintsFlags)
tCommand.AddCommand(tfCommand)
tCommand.AddCommand(tcCommand)
// Concurrency subcommands
cfCommand := &cobra.Command{
Use: "fixed",
Short: cfShort,
Long: fmt.Sprintf("%s.\n%s", cfShort, c.Long),
Example: cfExamples,
Args: fixedArgs,
RunE: RunLoadTestConcurrencyFixed(p),
}
ccCommand := &cobra.Command{
Use: "constraints",
Short: ccShort,
Long: fmt.Sprintf("%s.\n%s\n%s", ccShort, c.Long, ConstraintsHelp),
Example: ccExamples,
Args: constraintsArgs,
RunE: RunLoadTestConcurrencyConstraints(p),
}
ccCommand.PersistentFlags().AddFlagSet(ConstraintsFlags)
cCommand.AddCommand(cfCommand)
cCommand.AddCommand(ccCommand)
return command
}
// fixedArgs validates arguments for a fixed QPS test.
func fixedArgs(cmd *cobra.Command, args []string) error {
if len(args) < 1 {
return fmt.Errorf("%w: requires at least one test value", errors.ErrInvalidArgument)
}
_, err := ExtractTests(args)
return err
}
// constraintsArgs validates arguments for a constraints tests.
func constraintsArgs(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("%w: requires starting test value", errors.ErrInvalidArgument)
}
_, err := ExtractTests(args)
return err
}