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  }