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.

326 lines
7.5 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.NewTMLogger(log.NewSyncWriter(os.Stdout))
  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, loadCancel := context.WithCancel(context.Background())
  52. defer loadCancel()
  53. go func() {
  54. err := Load(ctx, cli.testnet, 1)
  55. if err != nil {
  56. logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error()))
  57. }
  58. chLoadResult <- err
  59. }()
  60. if err := Start(cli.testnet); err != nil {
  61. return err
  62. }
  63. if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
  64. return err
  65. }
  66. if cli.testnet.HasPerturbations() {
  67. if err := Perturb(cli.testnet); err != nil {
  68. return err
  69. }
  70. if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
  71. return err
  72. }
  73. }
  74. if cli.testnet.Evidence > 0 {
  75. if err := InjectEvidence(cli.testnet, cli.testnet.Evidence); err != nil {
  76. return err
  77. }
  78. if err := Wait(cli.testnet, 1); err != nil { // ensure chain progress
  79. return err
  80. }
  81. }
  82. loadCancel()
  83. if err := <-chLoadResult; err != nil {
  84. return err
  85. }
  86. if err := Wait(cli.testnet, 8); err != nil { // wait for network to settle before tests
  87. return err
  88. }
  89. if err := Test(cli.testnet); err != nil {
  90. return err
  91. }
  92. if !cli.preserve {
  93. if err := Cleanup(cli.testnet); err != nil {
  94. return err
  95. }
  96. }
  97. return nil
  98. },
  99. }
  100. cli.root.PersistentFlags().StringP("file", "f", "", "Testnet TOML manifest")
  101. _ = cli.root.MarkPersistentFlagRequired("file")
  102. cli.root.Flags().BoolVarP(&cli.preserve, "preserve", "p", false,
  103. "Preserves the running of the test net after tests are completed")
  104. cli.root.SetHelpCommand(&cobra.Command{
  105. Use: "no-help",
  106. Hidden: true,
  107. })
  108. cli.root.AddCommand(&cobra.Command{
  109. Use: "setup",
  110. Short: "Generates the testnet directory and configuration",
  111. RunE: func(cmd *cobra.Command, args []string) error {
  112. return Setup(cli.testnet)
  113. },
  114. })
  115. cli.root.AddCommand(&cobra.Command{
  116. Use: "start",
  117. Short: "Starts the Docker testnet, waiting for nodes to become available",
  118. RunE: func(cmd *cobra.Command, args []string) error {
  119. _, err := os.Stat(cli.testnet.Dir)
  120. if os.IsNotExist(err) {
  121. err = Setup(cli.testnet)
  122. }
  123. if err != nil {
  124. return err
  125. }
  126. return Start(cli.testnet)
  127. },
  128. })
  129. cli.root.AddCommand(&cobra.Command{
  130. Use: "perturb",
  131. Short: "Perturbs the Docker testnet, e.g. by restarting or disconnecting nodes",
  132. RunE: func(cmd *cobra.Command, args []string) error {
  133. return Perturb(cli.testnet)
  134. },
  135. })
  136. cli.root.AddCommand(&cobra.Command{
  137. Use: "wait",
  138. Short: "Waits for a few blocks to be produced and all nodes to catch up",
  139. RunE: func(cmd *cobra.Command, args []string) error {
  140. return Wait(cli.testnet, 5)
  141. },
  142. })
  143. cli.root.AddCommand(&cobra.Command{
  144. Use: "stop",
  145. Short: "Stops the Docker testnet",
  146. RunE: func(cmd *cobra.Command, args []string) error {
  147. logger.Info("Stopping testnet")
  148. return execCompose(cli.testnet.Dir, "down")
  149. },
  150. })
  151. cli.root.AddCommand(&cobra.Command{
  152. Use: "resume",
  153. Short: "Resumes the Docker testnet",
  154. RunE: func(cmd *cobra.Command, args []string) error {
  155. logger.Info("Resuming testnet")
  156. return execCompose(cli.testnet.Dir, "up")
  157. },
  158. })
  159. cli.root.AddCommand(&cobra.Command{
  160. Use: "load [multiplier]",
  161. Args: cobra.MaximumNArgs(1),
  162. Short: "Generates transaction load until the command is canceled",
  163. RunE: func(cmd *cobra.Command, args []string) (err error) {
  164. m := 1
  165. if len(args) == 1 {
  166. m, err = strconv.Atoi(args[0])
  167. if err != nil {
  168. return err
  169. }
  170. }
  171. return Load(context.Background(), cli.testnet, m)
  172. },
  173. })
  174. cli.root.AddCommand(&cobra.Command{
  175. Use: "evidence [amount]",
  176. Args: cobra.MaximumNArgs(1),
  177. Short: "Generates and broadcasts evidence to a random node",
  178. RunE: func(cmd *cobra.Command, args []string) (err error) {
  179. amount := 1
  180. if len(args) == 1 {
  181. amount, err = strconv.Atoi(args[0])
  182. if err != nil {
  183. return err
  184. }
  185. }
  186. return InjectEvidence(cli.testnet, amount)
  187. },
  188. })
  189. cli.root.AddCommand(&cobra.Command{
  190. Use: "test",
  191. Short: "Runs test cases against a running testnet",
  192. RunE: func(cmd *cobra.Command, args []string) error {
  193. return Test(cli.testnet)
  194. },
  195. })
  196. cli.root.AddCommand(&cobra.Command{
  197. Use: "cleanup",
  198. Short: "Removes the testnet directory",
  199. RunE: func(cmd *cobra.Command, args []string) error {
  200. return Cleanup(cli.testnet)
  201. },
  202. })
  203. cli.root.AddCommand(&cobra.Command{
  204. Use: "logs [node]",
  205. Short: "Shows the testnet or a specefic node's logs",
  206. Example: "runner logs validator03",
  207. Args: cobra.MaximumNArgs(1),
  208. RunE: func(cmd *cobra.Command, args []string) error {
  209. if len(args) == 1 {
  210. return execComposeVerbose(cli.testnet.Dir, "logs", args[0])
  211. }
  212. return execComposeVerbose(cli.testnet.Dir, "logs")
  213. },
  214. })
  215. cli.root.AddCommand(&cobra.Command{
  216. Use: "tail [node]",
  217. Short: "Tails the testnet or a specific node's logs",
  218. Args: cobra.MaximumNArgs(1),
  219. RunE: func(cmd *cobra.Command, args []string) error {
  220. if len(args) == 1 {
  221. return execComposeVerbose(cli.testnet.Dir, "logs", "--follow", args[0])
  222. }
  223. return execComposeVerbose(cli.testnet.Dir, "logs", "--follow")
  224. },
  225. })
  226. cli.root.AddCommand(&cobra.Command{
  227. Use: "benchmark",
  228. Short: "Benchmarks testnet",
  229. Long: `Benchmarks the following metrics:
  230. Mean Block Interval
  231. Standard Deviation
  232. Min Block Interval
  233. Max Block Interval
  234. over a 100 block sampling period.
  235. Does not run any perbutations.
  236. `,
  237. RunE: func(cmd *cobra.Command, args []string) error {
  238. if err := Cleanup(cli.testnet); err != nil {
  239. return err
  240. }
  241. if err := Setup(cli.testnet); err != nil {
  242. return err
  243. }
  244. chLoadResult := make(chan error)
  245. ctx, loadCancel := context.WithCancel(context.Background())
  246. defer loadCancel()
  247. go func() {
  248. err := Load(ctx, cli.testnet, 1)
  249. if err != nil {
  250. logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error()))
  251. }
  252. chLoadResult <- err
  253. }()
  254. if err := Start(cli.testnet); err != nil {
  255. return err
  256. }
  257. if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through
  258. return err
  259. }
  260. // we benchmark performance over the next 100 blocks
  261. if err := Benchmark(cli.testnet, 100); err != nil {
  262. return err
  263. }
  264. loadCancel()
  265. if err := <-chLoadResult; err != nil {
  266. return err
  267. }
  268. if err := Cleanup(cli.testnet); err != nil {
  269. return err
  270. }
  271. return nil
  272. },
  273. })
  274. return cli
  275. }
  276. // Run runs the CLI.
  277. func (cli *CLI) Run() {
  278. if err := cli.root.Execute(); err != nil {
  279. logger.Error(err.Error())
  280. os.Exit(1)
  281. }
  282. }