github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/testsuites/run.go (about)

     1  package testsuites
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"github.com/pkg/errors"
    11  	"github.com/spf13/cobra"
    12  
    13  	"github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common"
    14  	apiv1 "github.com/kubeshop/testkube/pkg/api/v1/client"
    15  	"github.com/kubeshop/testkube/pkg/api/v1/testkube"
    16  	"github.com/kubeshop/testkube/pkg/ui"
    17  )
    18  
    19  const (
    20  	maxErrorMessageLength = 100000
    21  )
    22  
    23  func NewRunTestSuiteCmd() *cobra.Command {
    24  	var (
    25  		name                     string
    26  		watchEnabled             bool
    27  		variables                []string
    28  		secretVariables          []string
    29  		executionLabels          map[string]string
    30  		selectors                []string
    31  		concurrencyLevel         int
    32  		httpProxy, httpsProxy    string
    33  		secretVariableReferences map[string]string
    34  		gitBranch                string
    35  		gitCommit                string
    36  		gitPath                  string
    37  		gitWorkingDir            string
    38  		runningContext           string
    39  		jobTemplate              string
    40  		scraperTemplate          string
    41  		pvcTemplate              string
    42  		jobTemplateReference     string
    43  		scraperTemplateReference string
    44  		pvcTemplateReference     string
    45  		downloadArtifactsEnabled bool
    46  		downloadDir              string
    47  		format                   string
    48  		masks                    []string
    49  		silentMode               bool
    50  	)
    51  
    52  	cmd := &cobra.Command{
    53  		Use:     "testsuite <testSuiteName>",
    54  		Aliases: []string{"ts"},
    55  		Short:   "Starts new test suite",
    56  		Long:    `Starts new test suite based on TestSuite Custom Resource name, returns results to console`,
    57  		Run: func(cmd *cobra.Command, args []string) {
    58  			startTime := time.Now()
    59  			client, namespace, err := common.GetClient(cmd)
    60  			ui.ExitOnError("getting client", err)
    61  
    62  			var executions []testkube.TestSuiteExecution
    63  
    64  			options := apiv1.ExecuteTestSuiteOptions{
    65  				HTTPProxy:       httpProxy,
    66  				HTTPSProxy:      httpsProxy,
    67  				ExecutionLabels: executionLabels,
    68  				RunningContext: &testkube.RunningContext{
    69  					Type_:   string(testkube.RunningContextTypeUserCLI),
    70  					Context: runningContext,
    71  				},
    72  				ConcurrencyLevel:         int32(concurrencyLevel),
    73  				JobTemplateReference:     jobTemplateReference,
    74  				ScraperTemplateReference: scraperTemplateReference,
    75  				PvcTemplateReference:     pvcTemplateReference,
    76  			}
    77  
    78  			var fields = []struct {
    79  				source      string
    80  				title       string
    81  				destination *string
    82  			}{
    83  				{
    84  					jobTemplate,
    85  					"job template",
    86  					&options.JobTemplate,
    87  				},
    88  				{
    89  					scraperTemplate,
    90  					"scraper template",
    91  					&options.ScraperTemplate,
    92  				},
    93  				{
    94  					pvcTemplate,
    95  					"pvc template",
    96  					&options.PvcTemplate,
    97  				},
    98  			}
    99  
   100  			for _, field := range fields {
   101  				if field.source != "" {
   102  					b, err := os.ReadFile(field.source)
   103  					ui.ExitOnError("reading "+field.title, err)
   104  					*field.destination = string(b)
   105  				}
   106  			}
   107  
   108  			info, err := client.GetServerInfo()
   109  			ui.ExitOnError("getting server info", err)
   110  
   111  			options.ExecutionVariables, err = common.CreateVariables(cmd, info.DisableSecretCreation)
   112  			ui.WarnOnError("getting variables", err)
   113  
   114  			if gitBranch != "" || gitCommit != "" || gitPath != "" || gitWorkingDir != "" {
   115  				options.ContentRequest = &testkube.TestContentRequest{
   116  					Repository: &testkube.RepositoryParameters{
   117  						Branch:     gitBranch,
   118  						Commit:     gitCommit,
   119  						Path:       gitPath,
   120  						WorkingDir: gitWorkingDir,
   121  					},
   122  				}
   123  			}
   124  
   125  			switch {
   126  			case len(args) > 0:
   127  				testSuiteName := args[0]
   128  				namespacedName := fmt.Sprintf("%s/%s", namespace, testSuiteName)
   129  
   130  				execution, err := client.ExecuteTestSuite(testSuiteName, name, options)
   131  				ui.ExitOnError("starting test suite execution "+namespacedName, err)
   132  				executions = append(executions, execution)
   133  			case len(selectors) != 0:
   134  				selector := strings.Join(selectors, ",")
   135  				executions, err = client.ExecuteTestSuites(selector, concurrencyLevel, options)
   136  				ui.ExitOnError("starting test suite executions "+selector, err)
   137  			default:
   138  				ui.Failf("Pass Test suite name or labels to run by labels ")
   139  			}
   140  
   141  			go func() {
   142  				<-cmd.Context().Done()
   143  				if errors.Is(cmd.Context().Err(), context.Canceled) {
   144  					os.Exit(0)
   145  				}
   146  			}()
   147  
   148  			var execErrors []error
   149  			for _, execution := range executions {
   150  				if execution.IsFailed() {
   151  					execErrors = append(execErrors, errors.New("failed execution"))
   152  				}
   153  
   154  				if execution.Id != "" {
   155  					if watchEnabled && len(args) > 0 {
   156  						watchResp := client.WatchTestSuiteExecution(execution.Id)
   157  						for resp := range watchResp {
   158  							ui.ExitOnError("watching test suite execution", resp.Error)
   159  							if !silentMode {
   160  								execution.TruncateErrorMessages(maxErrorMessageLength)
   161  								printExecution(execution, startTime)
   162  							}
   163  						}
   164  					}
   165  
   166  					execution, err = client.GetTestSuiteExecution(execution.Id)
   167  				}
   168  
   169  				execution.TruncateErrorMessages(maxErrorMessageLength)
   170  				printExecution(execution, startTime)
   171  				ui.ExitOnError("getting recent execution data id:"+execution.Id, err)
   172  
   173  				if err = uiPrintExecutionStatus(client, execution); err != nil {
   174  					execErrors = append(execErrors, err)
   175  				}
   176  
   177  				uiShellTestSuiteGetCommandBlock(execution.Id)
   178  				if execution.Id != "" {
   179  					if watchEnabled && len(args) > 0 {
   180  						if downloadArtifactsEnabled {
   181  							DownloadArtifacts(execution.Id, downloadDir, format, masks, client)
   182  						}
   183  					}
   184  
   185  					if !watchEnabled || len(args) == 0 {
   186  						uiShellTestSuiteWatchCommandBlock(execution.Id)
   187  					}
   188  				}
   189  			}
   190  
   191  			ui.ExitOnError("executions contain failed on errors", execErrors...)
   192  		},
   193  	}
   194  
   195  	cmd.Flags().StringVarP(&name, "name", "n", "", "execution name, if empty will be autogenerated")
   196  	cmd.Flags().StringArrayVarP(&variables, "variable", "v", []string{}, "execution variables passed to executor")
   197  	cmd.Flags().StringArrayVarP(&secretVariables, "secret-variable", "s", []string{}, "execution variables passed to executor")
   198  	cmd.Flags().BoolVarP(&watchEnabled, "watch", "f", false, "watch for changes after start")
   199  	cmd.Flags().StringSliceVarP(&selectors, "label", "l", nil, "label key value pair: --label key1=value1")
   200  	cmd.Flags().IntVar(&concurrencyLevel, "concurrency", 10, "concurrency level for multiple test suite execution")
   201  	cmd.Flags().StringVar(&httpProxy, "http-proxy", "", "http proxy for executor containers")
   202  	cmd.Flags().StringVar(&httpsProxy, "https-proxy", "", "https proxy for executor containers")
   203  	cmd.Flags().StringToStringVarP(&executionLabels, "execution-label", "", nil, "execution-label adds a label to execution in form of key value pair: --execution-label key1=value1")
   204  	cmd.Flags().StringToStringVarP(&secretVariableReferences, "secret-variable-reference", "", nil, "secret variable references in a form name1=secret_name1=secret_key1")
   205  	cmd.Flags().StringVarP(&gitBranch, "git-branch", "", "", "if uri is git repository we can set additional branch parameter")
   206  	cmd.Flags().StringVarP(&gitCommit, "git-commit", "", "", "if uri is git repository we can use commit id (sha) parameter")
   207  	cmd.Flags().StringVarP(&gitPath, "git-path", "", "", "if repository is big we need to define additional path to directory/file to checkout partially")
   208  	cmd.Flags().StringVarP(&gitWorkingDir, "git-working-dir", "", "", "if repository contains multiple directories with tests (like monorepo) and one starting directory we can set working directory parameter")
   209  	cmd.Flags().StringVar(&runningContext, "context", "", "running context description for test suite execution")
   210  	cmd.Flags().StringVar(&jobTemplate, "job-template", "", "job template file path for extensions to job template")
   211  	cmd.Flags().StringVar(&scraperTemplate, "scraper-template", "", "scraper template file path for extensions to scraper template")
   212  	cmd.Flags().StringVar(&pvcTemplate, "pvc-template", "", "pvc template file path for extensions to pvc template")
   213  	cmd.Flags().StringVar(&jobTemplateReference, "job-template-reference", "", "reference to job template to use for the test")
   214  	cmd.Flags().StringVar(&scraperTemplateReference, "scraper-template-reference", "", "reference to scraper template to use for the test")
   215  	cmd.Flags().StringVar(&pvcTemplateReference, "pvc-template-reference", "", "reference to pvc template to use for the test")
   216  	cmd.Flags().StringVar(&downloadDir, "download-dir", "artifacts", "download dir")
   217  	cmd.Flags().BoolVarP(&downloadArtifactsEnabled, "download-artifacts", "d", false, "download artifacts automatically")
   218  	cmd.Flags().StringVar(&format, "format", "folder", "data format for storing files, one of folder|archive")
   219  	cmd.Flags().StringArrayVarP(&masks, "mask", "", []string{}, "regexp to filter downloaded files, single or comma separated, like report/.* or .*\\.json,.*\\.js$")
   220  	cmd.Flags().BoolVarP(&silentMode, "silent", "", false, "don't print intermediate test suite execution")
   221  
   222  	return cmd
   223  }