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.

322 lines
7.6 KiB

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