internal/testers/multi/cmd.go (145 lines of code) (raw):

package multi import ( "errors" "fmt" "os" "path/filepath" "strings" "github.com/aws/aws-k8s-tester/internal" "github.com/octago/sflags/gen/gpflag" "k8s.io/klog" "sigs.k8s.io/kubetest2/pkg/app/shim" "sigs.k8s.io/kubetest2/pkg/artifacts" "sigs.k8s.io/kubetest2/pkg/process" "sigs.k8s.io/kubetest2/pkg/testers" ) const TesterName = "multi" const usage = `kubetest2 --test=multi -- [MultiTesterDriverArgs] -- [TesterName] [TesterArgs] -- ... MultiTesterDriverArgs: arguments passed to the multi-tester driver TesterName: the name of the tester to run TesterArgs: arguments passed to tester Each tester clause is separated by "--". ` func Main() { if err := execute(); err != nil { klog.Fatalf("failed to run multi tester: %v", err) } } type multiTesterDriver struct { argv []string } type tester struct { name string path string args []string } func execute() error { driverArgs, testerClauses := splitArguments(os.Args) driver := multiTesterDriver{ argv: driverArgs, } fs, err := gpflag.Parse(&driver) if err != nil { return fmt.Errorf("failed to initialize tester: %v", err) } fs.Usage = func() { fmt.Print(usage) } if len(testerClauses) == 0 { fs.Usage() return nil } // gracefully handle -h or --help if it is the only argument help := fs.BoolP("help", "h", false, "") failFast := fs.Bool("fail-fast", false, "Exit immediately if any tester fails") // we don't care about errors, only if -h / --help was set err = fs.Parse(driver.argv) if err != nil { fs.Usage() return err } if *help { fs.Usage() return nil } if err := testers.WriteVersionToMetadata(internal.Version); err != nil { return err } if testers, err := prepareTesters(testerClauses); err != nil { return err } else { return test(testers, *failFast) } } func test(testers []tester, failFast bool) error { metadataPath := filepath.Join(artifacts.BaseDir(), "metadata.json") backupMetdataPath := metadataPath + ".bak" if err := os.Rename(metadataPath, backupMetdataPath); err != nil { klog.Errorf("failed to backup driver metadata: %v", err) } var testerErrs []error for _, tester := range testers { if err := tester.run(); err != nil { klog.Errorf("tester failed: %+v: %v", tester, err) testerErrs = append(testerErrs, fmt.Errorf("%+v: %v", tester, err)) if failFast { break } } // reset the metadata.json file // testers will try to set the tester-version key and cause conflicts if err := os.Remove(metadataPath); err != nil { return fmt.Errorf("failed to delete tester metadata: %v", err) } } if err := os.Rename(backupMetdataPath, metadataPath); err != nil { return fmt.Errorf("failed to restore driver metadata: %v", err) } if len(testerErrs) > 0 { return errors.Join(testerErrs...) } return nil } // splitArguments splits arguments into driver arguments and tester clauses, separated by "--". func splitArguments(argv []string) ([]string, [][]string) { var clauses [][]string var last int for i, arg := range argv { if arg == "--" { clauses = append(clauses, argv[last:i]) last = i + 1 } } clauses = append(clauses, argv[last:]) return clauses[0], clauses[1:] } func prepareTesters(testerClauses [][]string) ([]tester, error) { var testers []tester for _, clause := range testerClauses { testerName := clause[0] if testerName == TesterName { return nil, fmt.Errorf("nesting isn't possible with the %s tester", TesterName) } path, err := shim.FindTester(testerName) if err != nil { return nil, err } tester := tester{ name: testerName, path: path, args: expandEnv(clause[1:]), } testers = append(testers, tester) } return testers, nil } func expandEnv(args []string) []string { expandedArgs := make([]string, len(args)) for i, arg := range args { // best effort handle literal dollar for backward compatibility // this is not an all-purpose shell special character handler if strings.Contains(arg, `\$`) { expandedArgs[i] = strings.ReplaceAll(arg, `\$`, `$`) } else { expandedArgs[i] = os.ExpandEnv(arg) } } return expandedArgs } func (t *tester) run() error { klog.Infof("running tester: %+v", t) return process.ExecJUnit(t.path, t.args, os.Environ()) }