mirror of
https://github.com/tendermint/tendermint.git
synced 2026-02-05 11:31:16 +00:00
If the e2e tests error, they leave all of the e2e state around including containers and networks etc. We should clean this up when the tests shuts down, even if it exits in error.
327 lines
7.9 KiB
Go
327 lines
7.9 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"strconv"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
e2e "github.com/tendermint/tendermint/test/e2e/pkg"
|
|
)
|
|
|
|
var (
|
|
logger = log.MustNewDefaultLogger(log.LogFormatPlain, log.LogLevelInfo, false)
|
|
)
|
|
|
|
func main() {
|
|
NewCLI().Run()
|
|
}
|
|
|
|
// CLI is the Cobra-based command-line interface.
|
|
type CLI struct {
|
|
root *cobra.Command
|
|
testnet *e2e.Testnet
|
|
preserve bool
|
|
}
|
|
|
|
// NewCLI sets up the CLI.
|
|
func NewCLI() *CLI {
|
|
cli := &CLI{}
|
|
cli.root = &cobra.Command{
|
|
Use: "runner",
|
|
Short: "End-to-end test runner",
|
|
SilenceUsage: true,
|
|
SilenceErrors: true, // we'll output them ourselves in Run()
|
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
|
file, err := cmd.Flags().GetString("file")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
testnet, err := e2e.LoadTestnet(file)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
cli.testnet = testnet
|
|
return nil
|
|
},
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := Cleanup(cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if cli.preserve {
|
|
logger.Info("Preserving testnet contents because -preserve=true")
|
|
} else if err := Cleanup(cli.testnet); err != nil {
|
|
logger.Error("Error cleaning up testnet contents", "err", err)
|
|
}
|
|
}()
|
|
if err := Setup(cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
|
|
chLoadResult := make(chan error)
|
|
ctx, cancel := context.WithCancel(cmd.Context())
|
|
defer cancel()
|
|
|
|
lctx, loadCancel := context.WithCancel(ctx)
|
|
defer loadCancel()
|
|
go func() {
|
|
chLoadResult <- Load(lctx, cli.testnet)
|
|
}()
|
|
|
|
if err := Start(ctx, cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := Wait(ctx, cli.testnet, 5); err != nil { // allow some txs to go through
|
|
return err
|
|
}
|
|
|
|
if cli.testnet.HasPerturbations() {
|
|
if err := Perturb(ctx, cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
if err := Wait(ctx, cli.testnet, 5); err != nil { // allow some txs to go through
|
|
return err
|
|
}
|
|
}
|
|
|
|
if cli.testnet.Evidence > 0 {
|
|
if err := InjectEvidence(ctx, cli.testnet, cli.testnet.Evidence); err != nil {
|
|
return err
|
|
}
|
|
if err := Wait(ctx, cli.testnet, 5); err != nil { // ensure chain progress
|
|
return err
|
|
}
|
|
}
|
|
|
|
loadCancel()
|
|
|
|
if err := <-chLoadResult; err != nil {
|
|
return fmt.Errorf("transaction load failed: %w", err)
|
|
}
|
|
if err := Wait(ctx, cli.testnet, 5); err != nil { // wait for network to settle before tests
|
|
return err
|
|
}
|
|
if err := Test(cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
cli.root.PersistentFlags().StringP("file", "f", "", "Testnet TOML manifest")
|
|
_ = cli.root.MarkPersistentFlagRequired("file")
|
|
|
|
cli.root.Flags().BoolVarP(&cli.preserve, "preserve", "p", false,
|
|
"Preserves the running of the test net after tests are completed")
|
|
|
|
cli.root.SetHelpCommand(&cobra.Command{
|
|
Use: "no-help",
|
|
Hidden: true,
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "setup",
|
|
Short: "Generates the testnet directory and configuration",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return Setup(cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "start",
|
|
Short: "Starts the Docker testnet, waiting for nodes to become available",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
_, err := os.Stat(cli.testnet.Dir)
|
|
if os.IsNotExist(err) {
|
|
err = Setup(cli.testnet)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return Start(cmd.Context(), cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "perturb",
|
|
Short: "Perturbs the Docker testnet, e.g. by restarting or disconnecting nodes",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return Perturb(cmd.Context(), cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "wait",
|
|
Short: "Waits for a few blocks to be produced and all nodes to catch up",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return Wait(cmd.Context(), cli.testnet, 5)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "stop",
|
|
Short: "Stops the Docker testnet",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
logger.Info("Stopping testnet")
|
|
return execCompose(cli.testnet.Dir, "down")
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "pause",
|
|
Short: "Pauses the Docker testnet",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
logger.Info("Pausing testnet")
|
|
return execCompose(cli.testnet.Dir, "pause")
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "resume",
|
|
Short: "Resumes the Docker testnet",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
logger.Info("Resuming testnet")
|
|
return execCompose(cli.testnet.Dir, "unpause")
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "load",
|
|
Short: "Generates transaction load until the command is canceled",
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
return Load(context.Background(), cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "evidence [amount]",
|
|
Args: cobra.MaximumNArgs(1),
|
|
Short: "Generates and broadcasts evidence to a random node",
|
|
RunE: func(cmd *cobra.Command, args []string) (err error) {
|
|
amount := 1
|
|
|
|
if len(args) == 1 {
|
|
amount, err = strconv.Atoi(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return InjectEvidence(cmd.Context(), cli.testnet, amount)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "test",
|
|
Short: "Runs test cases against a running testnet",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return Test(cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "cleanup",
|
|
Short: "Removes the testnet directory",
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return Cleanup(cli.testnet)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "logs [node]",
|
|
Short: "Shows the testnet or a specefic node's logs",
|
|
Example: "runner logs validator03",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
return execComposeVerbose(cli.testnet.Dir, append([]string{"logs", "--no-color"}, args...)...)
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "tail [node]",
|
|
Short: "Tails the testnet or a specific node's logs",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if len(args) == 1 {
|
|
return execComposeVerbose(cli.testnet.Dir, "logs", "--follow", args[0])
|
|
}
|
|
return execComposeVerbose(cli.testnet.Dir, "logs", "--follow")
|
|
},
|
|
})
|
|
|
|
cli.root.AddCommand(&cobra.Command{
|
|
Use: "benchmark",
|
|
Short: "Benchmarks testnet",
|
|
Long: `Benchmarks the following metrics:
|
|
Mean Block Interval
|
|
Standard Deviation
|
|
Min Block Interval
|
|
Max Block Interval
|
|
over a 100 block sampling period.
|
|
|
|
Does not run any perbutations.
|
|
`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
if err := Cleanup(cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
defer func() {
|
|
if err := Cleanup(cli.testnet); err != nil {
|
|
logger.Error("Error cleaning up testnet contents", "err", err)
|
|
}
|
|
}()
|
|
|
|
if err := Setup(cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
|
|
chLoadResult := make(chan error)
|
|
ctx, cancel := context.WithCancel(cmd.Context())
|
|
defer cancel()
|
|
|
|
lctx, loadCancel := context.WithCancel(ctx)
|
|
defer loadCancel()
|
|
go func() {
|
|
err := Load(lctx, cli.testnet)
|
|
chLoadResult <- err
|
|
}()
|
|
|
|
if err := Start(ctx, cli.testnet); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := Wait(ctx, cli.testnet, 5); err != nil { // allow some txs to go through
|
|
return err
|
|
}
|
|
|
|
// we benchmark performance over the next 100 blocks
|
|
if err := Benchmark(ctx, cli.testnet, 100); err != nil {
|
|
return err
|
|
}
|
|
|
|
loadCancel()
|
|
if err := <-chLoadResult; err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
},
|
|
})
|
|
|
|
return cli
|
|
}
|
|
|
|
// Run runs the CLI.
|
|
func (cli *CLI) Run() {
|
|
if err := cli.root.Execute(); err != nil {
|
|
logger.Error(err.Error())
|
|
os.Exit(1)
|
|
}
|
|
}
|