bitbucket.org/number571/tendermint@v0.8.14/test/e2e/runner/main.go (about)

     1  package main
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strconv"
     8  
     9  	"github.com/spf13/cobra"
    10  
    11  	"bitbucket.org/number571/tendermint/libs/log"
    12  	e2e "bitbucket.org/number571/tendermint/test/e2e/pkg"
    13  )
    14  
    15  var (
    16  	logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false)
    17  )
    18  
    19  func main() {
    20  	NewCLI().Run()
    21  }
    22  
    23  // CLI is the Cobra-based command-line interface.
    24  type CLI struct {
    25  	root     *cobra.Command
    26  	testnet  *e2e.Testnet
    27  	preserve bool
    28  }
    29  
    30  // NewCLI sets up the CLI.
    31  func NewCLI() *CLI {
    32  	cli := &CLI{}
    33  	cli.root = &cobra.Command{
    34  		Use:           "runner",
    35  		Short:         "End-to-end test runner",
    36  		SilenceUsage:  true,
    37  		SilenceErrors: true, // we'll output them ourselves in Run()
    38  		PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
    39  			file, err := cmd.Flags().GetString("file")
    40  			if err != nil {
    41  				return err
    42  			}
    43  			testnet, err := e2e.LoadTestnet(file)
    44  			if err != nil {
    45  				return err
    46  			}
    47  
    48  			cli.testnet = testnet
    49  			return nil
    50  		},
    51  		RunE: func(cmd *cobra.Command, args []string) error {
    52  			if err := Cleanup(cli.testnet); err != nil {
    53  				return err
    54  			}
    55  			if err := Setup(cli.testnet); err != nil {
    56  				return err
    57  			}
    58  
    59  			chLoadResult := make(chan error)
    60  			ctx, loadCancel := context.WithCancel(context.Background())
    61  			defer loadCancel()
    62  			go func() {
    63  				err := Load(ctx, cli.testnet, 1)
    64  				chLoadResult <- err
    65  			}()
    66  
    67  			if err := Start(cli.testnet); err != nil {
    68  				return err
    69  			}
    70  
    71  			if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
    72  				return err
    73  			}
    74  
    75  			if cli.testnet.HasPerturbations() {
    76  				if err := Perturb(cli.testnet); err != nil {
    77  					return err
    78  				}
    79  				if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
    80  					return err
    81  				}
    82  			}
    83  
    84  			if cli.testnet.Evidence > 0 {
    85  				if err := InjectEvidence(cli.testnet, cli.testnet.Evidence); err != nil {
    86  					return err
    87  				}
    88  				if err := Wait(cli.testnet, 5); err != nil { // ensure chain progress
    89  					return err
    90  				}
    91  			}
    92  
    93  			loadCancel()
    94  			if err := <-chLoadResult; err != nil {
    95  				return fmt.Errorf("transaction load failed: %w", err)
    96  			}
    97  			if err := Wait(cli.testnet, 5); err != nil { // wait for network to settle before tests
    98  				return err
    99  			}
   100  			if err := Test(cli.testnet); err != nil {
   101  				return err
   102  			}
   103  			if !cli.preserve {
   104  				if err := Cleanup(cli.testnet); err != nil {
   105  					return err
   106  				}
   107  			}
   108  			return nil
   109  		},
   110  	}
   111  
   112  	cli.root.PersistentFlags().StringP("file", "f", "", "Testnet TOML manifest")
   113  	_ = cli.root.MarkPersistentFlagRequired("file")
   114  
   115  	cli.root.Flags().BoolVarP(&cli.preserve, "preserve", "p", false,
   116  		"Preserves the running of the test net after tests are completed")
   117  
   118  	cli.root.SetHelpCommand(&cobra.Command{
   119  		Use:    "no-help",
   120  		Hidden: true,
   121  	})
   122  
   123  	cli.root.AddCommand(&cobra.Command{
   124  		Use:   "setup",
   125  		Short: "Generates the testnet directory and configuration",
   126  		RunE: func(cmd *cobra.Command, args []string) error {
   127  			return Setup(cli.testnet)
   128  		},
   129  	})
   130  
   131  	cli.root.AddCommand(&cobra.Command{
   132  		Use:   "start",
   133  		Short: "Starts the Docker testnet, waiting for nodes to become available",
   134  		RunE: func(cmd *cobra.Command, args []string) error {
   135  			_, err := os.Stat(cli.testnet.Dir)
   136  			if os.IsNotExist(err) {
   137  				err = Setup(cli.testnet)
   138  			}
   139  			if err != nil {
   140  				return err
   141  			}
   142  			return Start(cli.testnet)
   143  		},
   144  	})
   145  
   146  	cli.root.AddCommand(&cobra.Command{
   147  		Use:   "perturb",
   148  		Short: "Perturbs the Docker testnet, e.g. by restarting or disconnecting nodes",
   149  		RunE: func(cmd *cobra.Command, args []string) error {
   150  			return Perturb(cli.testnet)
   151  		},
   152  	})
   153  
   154  	cli.root.AddCommand(&cobra.Command{
   155  		Use:   "wait",
   156  		Short: "Waits for a few blocks to be produced and all nodes to catch up",
   157  		RunE: func(cmd *cobra.Command, args []string) error {
   158  			return Wait(cli.testnet, 5)
   159  		},
   160  	})
   161  
   162  	cli.root.AddCommand(&cobra.Command{
   163  		Use:   "stop",
   164  		Short: "Stops the Docker testnet",
   165  		RunE: func(cmd *cobra.Command, args []string) error {
   166  			logger.Info("Stopping testnet")
   167  			return execCompose(cli.testnet.Dir, "down")
   168  		},
   169  	})
   170  
   171  	cli.root.AddCommand(&cobra.Command{
   172  		Use:   "resume",
   173  		Short: "Resumes the Docker testnet",
   174  		RunE: func(cmd *cobra.Command, args []string) error {
   175  			logger.Info("Resuming testnet")
   176  			return execCompose(cli.testnet.Dir, "up")
   177  		},
   178  	})
   179  
   180  	cli.root.AddCommand(&cobra.Command{
   181  		Use:   "load [multiplier]",
   182  		Args:  cobra.MaximumNArgs(1),
   183  		Short: "Generates transaction load until the command is canceled",
   184  		RunE: func(cmd *cobra.Command, args []string) (err error) {
   185  			m := 1
   186  
   187  			if len(args) == 1 {
   188  				m, err = strconv.Atoi(args[0])
   189  				if err != nil {
   190  					return err
   191  				}
   192  			}
   193  
   194  			return Load(context.Background(), cli.testnet, m)
   195  		},
   196  	})
   197  
   198  	cli.root.AddCommand(&cobra.Command{
   199  		Use:   "evidence [amount]",
   200  		Args:  cobra.MaximumNArgs(1),
   201  		Short: "Generates and broadcasts evidence to a random node",
   202  		RunE: func(cmd *cobra.Command, args []string) (err error) {
   203  			amount := 1
   204  
   205  			if len(args) == 1 {
   206  				amount, err = strconv.Atoi(args[0])
   207  				if err != nil {
   208  					return err
   209  				}
   210  			}
   211  
   212  			return InjectEvidence(cli.testnet, amount)
   213  		},
   214  	})
   215  
   216  	cli.root.AddCommand(&cobra.Command{
   217  		Use:   "test",
   218  		Short: "Runs test cases against a running testnet",
   219  		RunE: func(cmd *cobra.Command, args []string) error {
   220  			return Test(cli.testnet)
   221  		},
   222  	})
   223  
   224  	cli.root.AddCommand(&cobra.Command{
   225  		Use:   "cleanup",
   226  		Short: "Removes the testnet directory",
   227  		RunE: func(cmd *cobra.Command, args []string) error {
   228  			return Cleanup(cli.testnet)
   229  		},
   230  	})
   231  
   232  	cli.root.AddCommand(&cobra.Command{
   233  		Use:     "logs [node]",
   234  		Short:   "Shows the testnet or a specefic node's logs",
   235  		Example: "runner logs validator03",
   236  		Args:    cobra.MaximumNArgs(1),
   237  		RunE: func(cmd *cobra.Command, args []string) error {
   238  			return execComposeVerbose(cli.testnet.Dir, append([]string{"logs", "--no-color"}, args...)...)
   239  		},
   240  	})
   241  
   242  	cli.root.AddCommand(&cobra.Command{
   243  		Use:   "tail [node]",
   244  		Short: "Tails the testnet or a specific node's logs",
   245  		Args:  cobra.MaximumNArgs(1),
   246  		RunE: func(cmd *cobra.Command, args []string) error {
   247  			if len(args) == 1 {
   248  				return execComposeVerbose(cli.testnet.Dir, "logs", "--follow", args[0])
   249  			}
   250  			return execComposeVerbose(cli.testnet.Dir, "logs", "--follow")
   251  		},
   252  	})
   253  
   254  	cli.root.AddCommand(&cobra.Command{
   255  		Use:   "benchmark",
   256  		Short: "Benchmarks testnet",
   257  		Long: `Benchmarks the following metrics:
   258  	Mean Block Interval
   259  	Standard Deviation
   260  	Min Block Interval
   261  	Max Block Interval
   262  over a 100 block sampling period.
   263  		
   264  Does not run any perbutations.
   265  		`,
   266  		RunE: func(cmd *cobra.Command, args []string) error {
   267  			if err := Cleanup(cli.testnet); err != nil {
   268  				return err
   269  			}
   270  			if err := Setup(cli.testnet); err != nil {
   271  				return err
   272  			}
   273  
   274  			chLoadResult := make(chan error)
   275  			ctx, loadCancel := context.WithCancel(context.Background())
   276  			defer loadCancel()
   277  			go func() {
   278  				err := Load(ctx, cli.testnet, 1)
   279  				chLoadResult <- err
   280  			}()
   281  
   282  			if err := Start(cli.testnet); err != nil {
   283  				return err
   284  			}
   285  
   286  			if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
   287  				return err
   288  			}
   289  
   290  			// we benchmark performance over the next 100 blocks
   291  			if err := Benchmark(cli.testnet, 100); err != nil {
   292  				return err
   293  			}
   294  
   295  			loadCancel()
   296  			if err := <-chLoadResult; err != nil {
   297  				return err
   298  			}
   299  
   300  			if err := Cleanup(cli.testnet); err != nil {
   301  				return err
   302  			}
   303  
   304  			return nil
   305  		},
   306  	})
   307  
   308  	return cli
   309  }
   310  
   311  // Run runs the CLI.
   312  func (cli *CLI) Run() {
   313  	if err := cli.root.Execute(); err != nil {
   314  		logger.Error(err.Error())
   315  		os.Exit(1)
   316  	}
   317  }