github.com/vipernet-xyz/tm@v0.34.24/test/e2e/runner/main.go (about) 1 package main 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "strconv" 9 10 "github.com/spf13/cobra" 11 12 "github.com/vipernet-xyz/tm/libs/log" 13 e2e "github.com/vipernet-xyz/tm/test/e2e/pkg" 14 "github.com/vipernet-xyz/tm/test/e2e/pkg/infra" 15 "github.com/vipernet-xyz/tm/test/e2e/pkg/infra/docker" 16 ) 17 18 var ( 19 logger = log.NewTMLogger(log.NewSyncWriter(os.Stdout)) 20 ) 21 22 func main() { 23 NewCLI().Run() 24 } 25 26 // CLI is the Cobra-based command-line interface. 27 type CLI struct { 28 root *cobra.Command 29 testnet *e2e.Testnet 30 preserve bool 31 infp infra.Provider 32 } 33 34 // NewCLI sets up the CLI. 35 func NewCLI() *CLI { 36 cli := &CLI{} 37 cli.root = &cobra.Command{ 38 Use: "runner", 39 Short: "End-to-end test runner", 40 SilenceUsage: true, 41 SilenceErrors: true, // we'll output them ourselves in Run() 42 PersistentPreRunE: func(cmd *cobra.Command, args []string) error { 43 file, err := cmd.Flags().GetString("file") 44 if err != nil { 45 return err 46 } 47 m, err := e2e.LoadManifest(file) 48 if err != nil { 49 return err 50 } 51 52 inft, err := cmd.Flags().GetString("infrastructure-type") 53 if err != nil { 54 return err 55 } 56 57 var ifd e2e.InfrastructureData 58 switch inft { 59 case "docker": 60 var err error 61 ifd, err = e2e.NewDockerInfrastructureData(m) 62 if err != nil { 63 return err 64 } 65 case "digital-ocean": 66 p, err := cmd.Flags().GetString("infrastructure-data") 67 if err != nil { 68 return err 69 } 70 if p == "" { 71 return errors.New("'--infrastructure-data' must be set when using the 'digital-ocean' infrastructure-type") 72 } 73 ifd, err = e2e.InfrastructureDataFromFile(p) 74 if err != nil { 75 return fmt.Errorf("parsing infrastructure data: %s", err) 76 } 77 default: 78 return fmt.Errorf("unknown infrastructure type '%s'", inft) 79 } 80 81 testnet, err := e2e.LoadTestnet(m, file, ifd) 82 if err != nil { 83 return fmt.Errorf("loading testnet: %s", err) 84 } 85 86 cli.testnet = testnet 87 cli.infp = &infra.NoopProvider{} 88 if inft == "docker" { 89 cli.infp = &docker.Provider{Testnet: testnet} 90 } 91 return nil 92 }, 93 RunE: func(cmd *cobra.Command, args []string) error { 94 if err := Cleanup(cli.testnet); err != nil { 95 return err 96 } 97 if err := Setup(cli.testnet, cli.infp); err != nil { 98 return err 99 } 100 101 chLoadResult := make(chan error) 102 ctx, loadCancel := context.WithCancel(context.Background()) 103 defer loadCancel() 104 go func() { 105 err := Load(ctx, cli.testnet, 1) 106 if err != nil { 107 logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error())) 108 } 109 chLoadResult <- err 110 }() 111 112 if err := Start(cli.testnet); err != nil { 113 return err 114 } 115 116 if lastMisbehavior := cli.testnet.LastMisbehaviorHeight(); lastMisbehavior > 0 { 117 // wait for misbehaviors before starting perturbations. We do a separate 118 // wait for another 5 blocks, since the last misbehavior height may be 119 // in the past depending on network startup ordering. 120 if err := WaitUntil(cli.testnet, lastMisbehavior); err != nil { 121 return err 122 } 123 } 124 if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through 125 return err 126 } 127 128 if cli.testnet.HasPerturbations() { 129 if err := Perturb(cli.testnet); err != nil { 130 return err 131 } 132 if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through 133 return err 134 } 135 } 136 137 loadCancel() 138 if err := <-chLoadResult; err != nil { 139 return err 140 } 141 if err := Wait(cli.testnet, 5); err != nil { // wait for network to settle before tests 142 return err 143 } 144 if err := Test(cli.testnet); err != nil { 145 return err 146 } 147 if !cli.preserve { 148 if err := Cleanup(cli.testnet); err != nil { 149 return err 150 } 151 } 152 return nil 153 }, 154 } 155 156 cli.root.PersistentFlags().StringP("file", "f", "", "Testnet TOML manifest") 157 _ = cli.root.MarkPersistentFlagRequired("file") 158 159 cli.root.PersistentFlags().StringP("infrastructure-type", "", "docker", "Backing infrastructure used to run the testnet. Either 'digital-ocean' or 'docker'") 160 161 cli.root.PersistentFlags().StringP("infrastructure-data", "", "", "path to the json file containing the infrastructure data. Only used if the 'infrastructure-type' is set to a value other than 'docker'") 162 163 cli.root.Flags().BoolVarP(&cli.preserve, "preserve", "p", false, 164 "Preserves the running of the test net after tests are completed") 165 166 cli.root.AddCommand(&cobra.Command{ 167 Use: "setup", 168 Short: "Generates the testnet directory and configuration", 169 RunE: func(cmd *cobra.Command, args []string) error { 170 return Setup(cli.testnet, cli.infp) 171 }, 172 }) 173 174 cli.root.AddCommand(&cobra.Command{ 175 Use: "start", 176 Short: "Starts the Docker testnet, waiting for nodes to become available", 177 RunE: func(cmd *cobra.Command, args []string) error { 178 _, err := os.Stat(cli.testnet.Dir) 179 if os.IsNotExist(err) { 180 err = Setup(cli.testnet, cli.infp) 181 } 182 if err != nil { 183 return err 184 } 185 return Start(cli.testnet) 186 }, 187 }) 188 189 cli.root.AddCommand(&cobra.Command{ 190 Use: "perturb", 191 Short: "Perturbs the Docker testnet, e.g. by restarting or disconnecting nodes", 192 RunE: func(cmd *cobra.Command, args []string) error { 193 return Perturb(cli.testnet) 194 }, 195 }) 196 197 cli.root.AddCommand(&cobra.Command{ 198 Use: "wait", 199 Short: "Waits for a few blocks to be produced and all nodes to catch up", 200 RunE: func(cmd *cobra.Command, args []string) error { 201 return Wait(cli.testnet, 5) 202 }, 203 }) 204 205 cli.root.AddCommand(&cobra.Command{ 206 Use: "stop", 207 Short: "Stops the Docker testnet", 208 RunE: func(cmd *cobra.Command, args []string) error { 209 logger.Info("Stopping testnet") 210 return execCompose(cli.testnet.Dir, "down") 211 }, 212 }) 213 214 cli.root.AddCommand(&cobra.Command{ 215 Use: "load [multiplier]", 216 Args: cobra.MaximumNArgs(1), 217 Short: "Generates transaction load until the command is canceled", 218 RunE: func(cmd *cobra.Command, args []string) (err error) { 219 m := 1 220 221 if len(args) == 1 { 222 m, err = strconv.Atoi(args[0]) 223 if err != nil { 224 return err 225 } 226 } 227 228 return Load(context.Background(), cli.testnet, m) 229 }, 230 }) 231 232 cli.root.AddCommand(&cobra.Command{ 233 Use: "test", 234 Short: "Runs test cases against a running testnet", 235 RunE: func(cmd *cobra.Command, args []string) error { 236 return Test(cli.testnet) 237 }, 238 }) 239 240 cli.root.AddCommand(&cobra.Command{ 241 Use: "cleanup", 242 Short: "Removes the testnet directory", 243 RunE: func(cmd *cobra.Command, args []string) error { 244 return Cleanup(cli.testnet) 245 }, 246 }) 247 248 cli.root.AddCommand(&cobra.Command{ 249 Use: "logs", 250 Short: "Shows the testnet logs", 251 RunE: func(cmd *cobra.Command, args []string) error { 252 return execComposeVerbose(cli.testnet.Dir, "logs") 253 }, 254 }) 255 256 cli.root.AddCommand(&cobra.Command{ 257 Use: "tail", 258 Short: "Tails the testnet logs", 259 RunE: func(cmd *cobra.Command, args []string) error { 260 return execComposeVerbose(cli.testnet.Dir, "logs", "--follow") 261 }, 262 }) 263 264 cli.root.AddCommand(&cobra.Command{ 265 Use: "benchmark", 266 Short: "Benchmarks testnet", 267 Long: `Benchmarks the following metrics: 268 Mean Block Interval 269 Standard Deviation 270 Min Block Interval 271 Max Block Interval 272 over a 100 block sampling period. 273 274 Does not run any perbutations. 275 `, 276 RunE: func(cmd *cobra.Command, args []string) error { 277 if err := Cleanup(cli.testnet); err != nil { 278 return err 279 } 280 if err := Setup(cli.testnet, cli.infp); err != nil { 281 return err 282 } 283 284 chLoadResult := make(chan error) 285 ctx, loadCancel := context.WithCancel(context.Background()) 286 defer loadCancel() 287 go func() { 288 err := Load(ctx, cli.testnet, 1) 289 if err != nil { 290 logger.Error(fmt.Sprintf("Transaction load failed: %v", err.Error())) 291 } 292 chLoadResult <- err 293 }() 294 295 if err := Start(cli.testnet); err != nil { 296 return err 297 } 298 299 if err := Wait(cli.testnet, 5); err != nil { // allow some txs to go through 300 return err 301 } 302 303 // we benchmark performance over the next 100 blocks 304 if err := Benchmark(cli.testnet, 100); err != nil { 305 return err 306 } 307 308 loadCancel() 309 if err := <-chLoadResult; err != nil { 310 return err 311 } 312 313 if err := Cleanup(cli.testnet); err != nil { 314 return err 315 } 316 317 return nil 318 }, 319 }) 320 321 return cli 322 } 323 324 // Run runs the CLI. 325 func (cli *CLI) Run() { 326 if err := cli.root.Execute(); err != nil { 327 logger.Error(err.Error()) 328 os.Exit(1) 329 } 330 }