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 }