github.com/kubeshop/testkube@v1.17.23/contrib/executor/artillery/pkg/runner/artillery.go (about) 1 package runner 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "path/filepath" 8 "strings" 9 10 "github.com/pkg/errors" 11 12 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 13 "github.com/kubeshop/testkube/pkg/envs" 14 "github.com/kubeshop/testkube/pkg/executor" 15 "github.com/kubeshop/testkube/pkg/executor/agent" 16 "github.com/kubeshop/testkube/pkg/executor/content" 17 "github.com/kubeshop/testkube/pkg/executor/env" 18 "github.com/kubeshop/testkube/pkg/executor/output" 19 "github.com/kubeshop/testkube/pkg/executor/runner" 20 "github.com/kubeshop/testkube/pkg/executor/scraper" 21 "github.com/kubeshop/testkube/pkg/executor/scraper/factory" 22 "github.com/kubeshop/testkube/pkg/ui" 23 ) 24 25 // NewArtilleryRunner creates a new Testkube test runner for Artillery tests 26 func NewArtilleryRunner(ctx context.Context, params envs.Params) (*ArtilleryRunner, error) { 27 output.PrintLogf("%s Preparing test runner", ui.IconTruck) 28 29 var err error 30 r := &ArtilleryRunner{ 31 Params: params, 32 } 33 34 r.Scraper, err = factory.TryGetScrapper(ctx, params) 35 if err != nil { 36 return nil, err 37 } 38 39 return r, nil 40 } 41 42 // ArtilleryRunner ... 43 type ArtilleryRunner struct { 44 Params envs.Params 45 Scraper scraper.Scraper 46 } 47 48 var _ runner.Runner = &ArtilleryRunner{} 49 50 // Run ... 51 func (r *ArtilleryRunner) Run(ctx context.Context, execution testkube.Execution) (result testkube.ExecutionResult, err error) { 52 if r.Scraper != nil { 53 defer r.Scraper.Close() 54 } 55 output.PrintLogf("%s Preparing for test run", ui.IconTruck) 56 // make some validation 57 err = r.Validate(execution) 58 if err != nil { 59 return result, err 60 } 61 62 path, workingDir, err := content.GetPathAndWorkingDir(execution.Content, r.Params.DataDir) 63 if err != nil { 64 output.PrintLogf("%s Failed to resolve absolute directory for %s, using the path directly", ui.IconWarning, r.Params.DataDir) 65 } 66 67 testDir, _ := filepath.Split(path) 68 envManager := env.NewManagerWithVars(execution.Variables) 69 envManager.GetReferenceVars(envManager.Variables) 70 71 var envFile string 72 if len(envManager.Variables) != 0 { 73 envFile, err = CreateEnvFile(envManager.Variables) 74 if err != nil { 75 return result, err 76 } 77 78 defer os.Remove(envFile) 79 output.PrintEvent("created dotenv file", envFile) 80 } 81 82 // artillery test result output file 83 testReportFile := filepath.Join(testDir, "test-report.json") 84 85 args := execution.Args 86 hasJunit := false 87 hasReport := false 88 for i := len(args) - 1; i >= 0; i-- { 89 if envFile == "" && (args[i] == "--dotenv" || args[i] == "<envFile>") { 90 args = append(args[:i], args[i+1:]...) 91 continue 92 } 93 94 if args[i] == "<envFile>" { 95 args[i] = envFile 96 } 97 98 if args[i] == "<reportFile>" { 99 args[i] = testReportFile 100 hasReport = true 101 } 102 103 if args[i] == "<runPath>" { 104 args[i] = path 105 } 106 107 if args[i] == "-o" { 108 hasJunit = true 109 } 110 111 args[i] = os.ExpandEnv(args[i]) 112 } 113 114 runPath := testDir 115 if workingDir != "" { 116 runPath = workingDir 117 } 118 119 // run executor 120 command, args := executor.MergeCommandAndArgs(execution.Command, args) 121 output.PrintLogf("%s Test run command %s %s", ui.IconRocket, command, strings.Join(envManager.ObfuscateStringSlice(args), " ")) 122 out, runerr := executor.Run(runPath, command, envManager, args...) 123 124 out = envManager.ObfuscateSecrets(out) 125 126 if hasJunit && hasReport { 127 var artilleryResult ArtilleryExecutionResult 128 artilleryResult, err = r.GetArtilleryExecutionResult(testReportFile, out) 129 if err != nil { 130 return *result.WithErrors(runerr, errors.Errorf("failed to get test execution results")), err 131 } 132 133 result = MapTestSummaryToResults(artilleryResult) 134 } else { 135 result = makeSuccessExecution(out) 136 } 137 138 output.PrintLog(fmt.Sprintf("%s Mapped test summary to Execution Results...", ui.IconCheckMark)) 139 140 var rerr error 141 if execution.PostRunScript != "" && execution.ExecutePostRunScriptBeforeScraping { 142 output.PrintLog(fmt.Sprintf("%s Running post run script...", ui.IconCheckMark)) 143 144 if rerr = agent.RunScript(execution.PostRunScript, r.Params.WorkingDir); rerr != nil { 145 output.PrintLogf("%s Failed to execute post run script %s", ui.IconWarning, rerr) 146 } 147 } 148 149 if r.Params.ScrapperEnabled { 150 directories := []string{ 151 testReportFile, 152 } 153 var masks []string 154 if execution.ArtifactRequest != nil { 155 directories = append(directories, execution.ArtifactRequest.Dirs...) 156 masks = execution.ArtifactRequest.Masks 157 } 158 159 err = r.Scraper.Scrape(ctx, directories, masks, execution) 160 if err != nil { 161 return *result.Err(err), errors.Wrap(err, "error scraping artifacts for Artillery executor") 162 } 163 } 164 165 // return ExecutionResult 166 return *result.WithErrors(err, rerr), nil 167 } 168 169 // GetType returns runner type 170 func (r *ArtilleryRunner) GetType() runner.Type { 171 return runner.TypeMain 172 } 173 174 func CreateEnvFile(vars map[string]testkube.Variable) (string, error) { 175 var envVars []byte 176 for _, v := range vars { 177 envVars = append(envVars, []byte(fmt.Sprintf("%s=%s\n", v.Name, v.Value))...) 178 } 179 envFile, err := os.CreateTemp("/tmp", "") 180 if err != nil { 181 return "", errors.Errorf("could not create dotenv file: %v", err) 182 } 183 defer envFile.Close() 184 185 if _, err := envFile.Write(envVars); err != nil { 186 return "", errors.Errorf("could not write dotenv file: %v", err) 187 } 188 189 return envFile.Name(), nil 190 }