You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

230 lines
5.6 KiB

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "github.com/spf13/cobra"
  7. "github.com/tendermint/tendermint/libs/log"
  8. e2e "github.com/tendermint/tendermint/test/e2e/pkg"
  9. )
  10. var (
  11. logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout))
  12. )
  13. func main() {
  14. NewCLI().Run()
  15. }
  16. // CLI is the Cobra-based command-line interface.
  17. type CLI struct {
  18. root *cobra.Command
  19. testnet *e2e.Testnet
  20. preserve bool
  21. }
  22. // NewCLI sets up the CLI.
  23. func NewCLI() *CLI {
  24. cli := &CLI{}
  25. cli.root = &cobra.Command{
  26. Use: "runner",
  27. Short: "End-to-end test runner",
  28. SilenceUsage: true,
  29. SilenceErrors: true, // we'll output them ourselves in Run()
  30. PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
  31. file, err := cmd.Flags().GetString("file")
  32. if err != nil {
  33. return err
  34. }
  35. testnet, err := e2e.LoadTestnet(file)
  36. if err != nil {
  37. return err
  38. }
  39. cli.testnet = testnet
  40. return nil
  41. },
  42. RunE: func(cmd *cobra.Command, args []string) error {
  43. if err := Cleanup(cli.testnet); err != nil {
  44. return err
  45. }
  46. if err := Setup(cli.testnet); err != nil {
  47. return err
  48. }
  49. chLoadResult := make(chan error)
  50. ctx, loadCancel := context.WithCancel(context.Background())
  51. defer loadCancel()
  52. go func() {
  53. err := Load(ctx, cli.testnet)
  54. if err != nil {
  55. logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error()))
  56. }
  57. chLoadResult <- err
  58. }()
  59. if err := Start(cli.testnet); err != nil {
  60. return err
  61. }
  62. if lastMisbehavior := cli.testnet.LastMisbehaviorHeight(); lastMisbehavior > 0 {
  63. // wait for misbehaviors before starting perturbations. We do a separate
  64. // wait for another 5 blocks, since the last misbehavior height may be
  65. // in the past depending on network startup ordering.
  66. if err := WaitUntil(cli.testnet, lastMisbehavior); err != nil {
  67. return err
  68. }
  69. }
  70. if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
  71. return err
  72. }
  73. if cli.testnet.HasPerturbations() {
  74. if err := Perturb(cli.testnet); err != nil {
  75. return err
  76. }
  77. if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
  78. return err
  79. }
  80. }
  81. loadCancel()
  82. if err := <-chLoadResult; err != nil {
  83. return err
  84. }
  85. if err := Wait(cli.testnet, 5); err != nil { // wait for network to settle before tests
  86. return err
  87. }
  88. if err := Test(cli.testnet); err != nil {
  89. return err
  90. }
  91. if !cli.preserve {
  92. if err := Cleanup(cli.testnet); err != nil {
  93. return err
  94. }
  95. }
  96. return nil
  97. },
  98. }
  99. cli.root.PersistentFlags().StringP("file", "f", "", "Testnet TOML manifest")
  100. _ = cli.root.MarkPersistentFlagRequired("file")
  101. cli.root.Flags().BoolVarP(&cli.preserve, "preserve", "p", false,
  102. "Preserves the running of the test net after tests are completed")
  103. cli.root.SetHelpCommand(&cobra.Command{
  104. Use: "no-help",
  105. Hidden: true,
  106. })
  107. cli.root.AddCommand(&cobra.Command{
  108. Use: "setup",
  109. Short: "Generates the testnet directory and configuration",
  110. RunE: func(cmd *cobra.Command, args []string) error {
  111. return Setup(cli.testnet)
  112. },
  113. })
  114. cli.root.AddCommand(&cobra.Command{
  115. Use: "start",
  116. Short: "Starts the Docker testnet, waiting for nodes to become available",
  117. RunE: func(cmd *cobra.Command, args []string) error {
  118. _, err := os.Stat(cli.testnet.Dir)
  119. if os.IsNotExist(err) {
  120. err = Setup(cli.testnet)
  121. }
  122. if err != nil {
  123. return err
  124. }
  125. return Start(cli.testnet)
  126. },
  127. })
  128. cli.root.AddCommand(&cobra.Command{
  129. Use: "perturb",
  130. Short: "Perturbs the Docker testnet, e.g. by restarting or disconnecting nodes",
  131. RunE: func(cmd *cobra.Command, args []string) error {
  132. return Perturb(cli.testnet)
  133. },
  134. })
  135. cli.root.AddCommand(&cobra.Command{
  136. Use: "wait",
  137. Short: "Waits for a few blocks to be produced and all nodes to catch up",
  138. RunE: func(cmd *cobra.Command, args []string) error {
  139. return Wait(cli.testnet, 5)
  140. },
  141. })
  142. cli.root.AddCommand(&cobra.Command{
  143. Use: "stop",
  144. Short: "Stops the Docker testnet",
  145. RunE: func(cmd *cobra.Command, args []string) error {
  146. logger.Info("Stopping testnet")
  147. return execCompose(cli.testnet.Dir, "down")
  148. },
  149. })
  150. cli.root.AddCommand(&cobra.Command{
  151. Use: "load",
  152. Short: "Generates transaction load until the command is cancelled",
  153. RunE: func(cmd *cobra.Command, args []string) error {
  154. return Load(context.Background(), cli.testnet)
  155. },
  156. })
  157. cli.root.AddCommand(&cobra.Command{
  158. Use: "test",
  159. Short: "Runs test cases against a running testnet",
  160. RunE: func(cmd *cobra.Command, args []string) error {
  161. return Test(cli.testnet)
  162. },
  163. })
  164. cli.root.AddCommand(&cobra.Command{
  165. Use: "cleanup",
  166. Short: "Removes the testnet directory",
  167. RunE: func(cmd *cobra.Command, args []string) error {
  168. return Cleanup(cli.testnet)
  169. },
  170. })
  171. cli.root.AddCommand(&cobra.Command{
  172. Use: "logs [node]",
  173. Short: "Shows the testnet or a specefic node's logs",
  174. Example: "runner logs valiator03",
  175. Args: cobra.MaximumNArgs(1),
  176. RunE: func(cmd *cobra.Command, args []string) error {
  177. if len(args) == 1 {
  178. return execComposeVerbose(cli.testnet.Dir, "logs", args[0])
  179. }
  180. return execComposeVerbose(cli.testnet.Dir, "logs")
  181. },
  182. })
  183. cli.root.AddCommand(&cobra.Command{
  184. Use: "tail [node]",
  185. Short: "Tails the testnet or a specific node's logs",
  186. Args: cobra.MaximumNArgs(1),
  187. RunE: func(cmd *cobra.Command, args []string) error {
  188. if len(args) == 1 {
  189. return execComposeVerbose(cli.testnet.Dir, "logs", "--follow", args[0])
  190. }
  191. return execComposeVerbose(cli.testnet.Dir, "logs", "--follow")
  192. },
  193. })
  194. return cli
  195. }
  196. // Run runs the CLI.
  197. func (cli *CLI) Run() {
  198. if err := cli.root.Execute(); err != nil {
  199. logger.Error(err.Error())
  200. os.Exit(1)
  201. }
  202. }