github.com/kubeshop/testkube@v1.17.23/cmd/kubectl-testkube/commands/testsuites/common.go (about) 1 package testsuites 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/spf13/cobra" 12 13 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common" 14 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/common/render" 15 "github.com/kubeshop/testkube/cmd/kubectl-testkube/commands/tests" 16 apiclientv1 "github.com/kubeshop/testkube/pkg/api/v1/client" 17 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 18 "github.com/kubeshop/testkube/pkg/ui" 19 ) 20 21 func printExecution(execution testkube.TestSuiteExecution, startTime time.Time) { 22 if execution.TestSuite != nil { 23 ui.Warn("Name :", execution.TestSuite.Name) 24 } 25 26 if execution.Id != "" { 27 ui.Warn("Execution ID :", execution.Id) 28 ui.Warn("Execution name:", execution.Name) 29 } 30 31 if execution.Status != nil { 32 ui.Warn("Status :", string(*execution.Status)) 33 } 34 35 if execution.Id != "" { 36 ui.Warn("Duration:", execution.CalculateDuration().String()+"\n") 37 ui.Table(execution, os.Stdout) 38 } 39 40 ui.NL() 41 ui.NL() 42 } 43 44 func uiPrintExecutionStatus(client apiclientv1.Client, execution testkube.TestSuiteExecution) error { 45 if execution.Status == nil { 46 return nil 47 } 48 49 switch true { 50 case execution.IsQueued(): 51 ui.Warn("Test Suite queued for execution") 52 53 case execution.IsRunning(): 54 ui.Warn("Test Suite execution started") 55 56 case execution.IsPassed(): 57 ui.Success("Test Suite execution completed with sucess in " + execution.Duration) 58 59 info, err := client.GetServerInfo() 60 ui.ExitOnError("getting server info", err) 61 62 render.PrintTestSuiteExecutionURIs(&execution, info.DashboardUri) 63 64 case execution.IsFailed(): 65 ui.UseStderr() 66 ui.Errf("Test Suite execution failed") 67 68 info, err := client.GetServerInfo() 69 ui.ExitOnError("getting server info", err) 70 71 render.PrintTestSuiteExecutionURIs(&execution, info.DashboardUri) 72 return errors.New("failed test suite") 73 } 74 75 ui.NL() 76 return nil 77 } 78 79 func uiShellTestSuiteGetCommandBlock(id string) { 80 ui.ShellCommand( 81 "Use following command to get test execution details", 82 "kubectl testkube get tse "+id, 83 ) 84 85 ui.NL() 86 } 87 88 func uiShellTestSuiteWatchCommandBlock(id string) { 89 ui.ShellCommand( 90 "Use following command to get test execution details", 91 "kubectl testkube watch tse "+id, 92 ) 93 94 ui.NL() 95 } 96 97 // NewTestSuiteUpsertOptionsFromFlags creates test suite upsert options from command flags 98 func NewTestSuiteUpsertOptionsFromFlags(cmd *cobra.Command) (options apiclientv1.UpsertTestSuiteOptions, err error) { 99 data, err := common.NewDataFromFlags(cmd) 100 if err != nil { 101 return options, err 102 } 103 104 if data == nil { 105 return options, fmt.Errorf("empty test suite content") 106 } 107 108 if err = json.Unmarshal([]byte(*data), &options); err != nil { 109 ui.Debug("json unmarshaling", err.Error()) 110 } 111 112 emptyBatch := true 113 for _, step := range options.Steps { 114 if len(step.Execute) != 0 { 115 emptyBatch = false 116 break 117 } 118 } 119 120 if emptyBatch { 121 var testSuite testkube.TestSuiteUpsertRequestV2 122 err = json.Unmarshal([]byte(*data), &testSuite) 123 if err != nil { 124 return options, err 125 } 126 127 options = apiclientv1.UpsertTestSuiteOptions(*testSuite.ToTestSuiteUpsertRequest()) 128 if len(options.Steps) == 0 { 129 return options, fmt.Errorf("no test suite batch steps provided") 130 } 131 } 132 133 for _, step := range options.Steps { 134 if len(step.Execute) == 0 { 135 return options, fmt.Errorf("no steps defined for batch step") 136 } 137 } 138 139 name := cmd.Flag("name").Value.String() 140 if name != "" { 141 options.Name = name 142 } 143 144 labels, err := cmd.Flags().GetStringToString("label") 145 if err != nil { 146 return options, err 147 } 148 149 options.Namespace = cmd.Flag("namespace").Value.String() 150 options.Labels = labels 151 152 crdOnly, err := cmd.Flags().GetBool("crd-only") 153 if err != nil { 154 return options, err 155 } 156 157 disableSecretCreation := false 158 if !crdOnly { 159 client, _, err := common.GetClient(cmd) 160 if err != nil { 161 return options, err 162 } 163 164 info, err := client.GetServerInfo() 165 if err != nil { 166 return options, err 167 } 168 169 disableSecretCreation = info.DisableSecretCreation 170 } 171 172 variables, err := common.CreateVariables(cmd, disableSecretCreation) 173 if err != nil { 174 return options, fmt.Errorf("invalid variables %w", err) 175 } 176 177 timeout, err := cmd.Flags().GetInt32("timeout") 178 if err != nil { 179 return options, err 180 } 181 182 schedule := cmd.Flag("schedule").Value.String() 183 if err = validateSchedule(schedule); err != nil { 184 return options, fmt.Errorf("validating schedule %w", err) 185 } 186 187 jobTemplateReference := cmd.Flag("job-template-reference").Value.String() 188 cronJobTemplateReference := cmd.Flag("cronjob-template-reference").Value.String() 189 scraperTemplateReference := cmd.Flag("scraper-template-reference").Value.String() 190 pvcTemplateReference := cmd.Flag("pvc-template-reference").Value.String() 191 192 options.Schedule = schedule 193 options.ExecutionRequest = &testkube.TestSuiteExecutionRequest{ 194 Variables: variables, 195 Name: cmd.Flag("execution-name").Value.String(), 196 HttpProxy: cmd.Flag("http-proxy").Value.String(), 197 HttpsProxy: cmd.Flag("https-proxy").Value.String(), 198 Timeout: timeout, 199 JobTemplateReference: jobTemplateReference, 200 CronJobTemplateReference: cronJobTemplateReference, 201 ScraperTemplateReference: scraperTemplateReference, 202 PvcTemplateReference: pvcTemplateReference, 203 } 204 205 var fields = []struct { 206 source string 207 destination *string 208 }{ 209 { 210 cmd.Flag("job-template").Value.String(), 211 &options.ExecutionRequest.JobTemplate, 212 }, 213 { 214 cmd.Flag("cronjob-template").Value.String(), 215 &options.ExecutionRequest.CronJobTemplate, 216 }, 217 { 218 cmd.Flag("scraper-template").Value.String(), 219 &options.ExecutionRequest.ScraperTemplate, 220 }, 221 { 222 cmd.Flag("pvc-template").Value.String(), 223 &options.ExecutionRequest.PvcTemplate, 224 }, 225 } 226 227 for _, field := range fields { 228 if field.source != "" { 229 b, err := os.ReadFile(field.source) 230 if err != nil { 231 return options, err 232 } 233 234 *field.destination = string(b) 235 } 236 } 237 238 return options, nil 239 } 240 241 // NewTestSuiteUpdateOptionsFromFlags creates test suite update options from command flags 242 func NewTestSuiteUpdateOptionsFromFlags(cmd *cobra.Command) (options apiclientv1.UpdateTestSuiteOptions, err error) { 243 data, err := common.NewDataFromFlags(cmd) 244 if err != nil { 245 return options, err 246 } 247 248 if data != nil { 249 if err = json.Unmarshal([]byte(*data), &options); err != nil { 250 ui.Debug("json unmarshaling", err.Error()) 251 } 252 253 if options.Steps != nil { 254 emptyBatch := true 255 for _, step := range *options.Steps { 256 if len(step.Execute) != 0 { 257 emptyBatch = false 258 break 259 } 260 } 261 262 if emptyBatch { 263 var testSuite testkube.TestSuiteUpdateRequestV2 264 err = json.Unmarshal([]byte(*data), &testSuite) 265 if err != nil { 266 return options, err 267 } 268 269 options = apiclientv1.UpdateTestSuiteOptions(*testSuite.ToTestSuiteUpdateRequest()) 270 } 271 272 } 273 } 274 275 var fields = []struct { 276 name string 277 destination **string 278 }{ 279 { 280 "name", 281 &options.Name, 282 }, 283 { 284 "namespace", 285 &options.Namespace, 286 }, 287 } 288 289 for _, field := range fields { 290 if cmd.Flag(field.name).Changed { 291 value := cmd.Flag(field.name).Value.String() 292 *field.destination = &value 293 } 294 } 295 296 if cmd.Flag("schedule").Changed { 297 schedule := cmd.Flag("schedule").Value.String() 298 if err = validateSchedule(schedule); err != nil { 299 return options, fmt.Errorf("validating schedule %w", err) 300 } 301 302 options.Schedule = &schedule 303 } 304 305 if cmd.Flag("label").Changed { 306 labels, err := cmd.Flags().GetStringToString("label") 307 if err != nil { 308 return options, err 309 } 310 311 options.Labels = &labels 312 } 313 314 var executionRequest testkube.TestSuiteExecutionUpdateRequest 315 var nonEmpty bool 316 if cmd.Flag("variable").Changed || cmd.Flag("secret-variable").Changed || cmd.Flag("secret-variable-reference").Changed { 317 client, _, err := common.GetClient(cmd) 318 if err != nil { 319 return options, err 320 } 321 322 info, err := client.GetServerInfo() 323 if err != nil { 324 return options, err 325 } 326 327 variables, err := common.CreateVariables(cmd, info.DisableSecretCreation) 328 if err != nil { 329 return options, fmt.Errorf("invalid variables %w", err) 330 } 331 332 executionRequest.Variables = &variables 333 nonEmpty = true 334 } 335 336 if cmd.Flag("timeout").Changed { 337 timeout, err := cmd.Flags().GetInt32("timeout") 338 if err != nil { 339 return options, err 340 } 341 342 executionRequest.Timeout = &timeout 343 nonEmpty = true 344 } 345 346 var values = []struct { 347 source string 348 destination **string 349 }{ 350 { 351 "job-template", 352 &executionRequest.JobTemplate, 353 }, 354 { 355 "cronjob-template", 356 &executionRequest.CronJobTemplate, 357 }, 358 { 359 "scraper-template", 360 &executionRequest.ScraperTemplate, 361 }, 362 { 363 "pvc-template", 364 &executionRequest.PvcTemplate, 365 }, 366 } 367 368 for _, value := range values { 369 if cmd.Flag(value.source).Changed { 370 data := "" 371 name := cmd.Flag(value.source).Value.String() 372 if name != "" { 373 b, err := os.ReadFile(name) 374 if err != nil { 375 return options, err 376 } 377 378 data = string(b) 379 } 380 381 *value.destination = &data 382 nonEmpty = true 383 } 384 } 385 386 var executionFields = []struct { 387 name string 388 destination **string 389 }{ 390 { 391 "execution-name", 392 &executionRequest.Name, 393 }, 394 { 395 "http-proxy", 396 &executionRequest.HttpProxy, 397 }, 398 { 399 "https-proxy", 400 &executionRequest.HttpsProxy, 401 }, 402 { 403 "job-template-reference", 404 &executionRequest.JobTemplateReference, 405 }, 406 { 407 "cronjob-template-reference", 408 &executionRequest.CronJobTemplateReference, 409 }, 410 { 411 "scraper-template-reference", 412 &executionRequest.ScraperTemplateReference, 413 }, 414 { 415 "pvc-template-reference", 416 &executionRequest.PvcTemplateReference, 417 }, 418 } 419 420 for _, field := range executionFields { 421 if cmd.Flag(field.name).Changed { 422 value := cmd.Flag(field.name).Value.String() 423 *field.destination = &value 424 nonEmpty = true 425 } 426 } 427 428 if nonEmpty { 429 value := (&executionRequest) 430 options.ExecutionRequest = &value 431 } 432 433 return options, nil 434 } 435 436 func DownloadArtifacts(id, dir, format string, masks []string, client apiclientv1.Client) { 437 testSuiteExecution, err := client.GetTestSuiteExecution(id) 438 ui.ExitOnError("getting test suite execution ", err) 439 440 for _, execution := range testSuiteExecution.ExecuteStepResults { 441 for _, step := range execution.Execute { 442 if step.Execution != nil && step.Step != nil && step.Step.Test != "" { 443 if step.Execution.IsPassed() || step.Execution.IsFailed() { 444 tests.DownloadTestArtifacts(step.Execution.Id, filepath.Join(dir, step.Execution.TestName+"-"+step.Execution.Id), format, masks, client) 445 } 446 } 447 } 448 } 449 }