github.com/kubeshop/testkube@v1.17.23/pkg/executor/containerexecutor/containerexecutor_test.go (about) 1 package containerexecutor 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "github.com/golang/mock/gomock" 9 "github.com/stretchr/testify/assert" 10 "go.uber.org/zap" 11 corev1 "k8s.io/api/core/v1" 12 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 13 "k8s.io/apimachinery/pkg/runtime" 14 "k8s.io/client-go/kubernetes/fake" 15 16 executorv1 "github.com/kubeshop/testkube-operator/api/executor/v1" 17 testsv3 "github.com/kubeshop/testkube-operator/api/tests/v3" 18 templatesclientv1 "github.com/kubeshop/testkube-operator/pkg/client/templates/v1" 19 v3 "github.com/kubeshop/testkube-operator/pkg/client/tests/v3" 20 "github.com/kubeshop/testkube/pkg/api/v1/testkube" 21 "github.com/kubeshop/testkube/pkg/executor" 22 "github.com/kubeshop/testkube/pkg/executor/client" 23 "github.com/kubeshop/testkube/pkg/featureflags" 24 "github.com/kubeshop/testkube/pkg/imageinspector" 25 "github.com/kubeshop/testkube/pkg/repository/result" 26 ) 27 28 var ctx = context.Background() 29 30 func TestExecuteAsync(t *testing.T) { 31 t.Parallel() 32 33 ce := ContainerExecutor{ 34 clientSet: getFakeClient("1"), 35 log: logger(), 36 repository: FakeResultRepository{}, 37 metrics: FakeExecutionMetric{}, 38 emitter: FakeEmitter{}, 39 configMap: FakeConfigRepository{}, 40 testsClient: FakeTestsClient{}, 41 executorsClient: FakeExecutorsClient{}, 42 serviceAccountNames: map[string]string{"": ""}, 43 } 44 45 execution := &testkube.Execution{Id: "1"} 46 options := client.ExecuteOptions{} 47 res, err := ce.Execute(ctx, execution, options) 48 assert.NoError(t, err) 49 50 // Status is either running or passed, depends if async goroutine managed to finish 51 assert.Contains(t, 52 []testkube.ExecutionStatus{testkube.RUNNING_ExecutionStatus, testkube.PASSED_ExecutionStatus}, 53 *res.Status) 54 } 55 56 func TestExecuteSync(t *testing.T) { 57 t.Parallel() 58 59 ce := ContainerExecutor{ 60 clientSet: getFakeClient("1"), 61 log: logger(), 62 repository: FakeResultRepository{}, 63 metrics: FakeExecutionMetric{}, 64 emitter: FakeEmitter{}, 65 configMap: FakeConfigRepository{}, 66 testsClient: FakeTestsClient{}, 67 executorsClient: FakeExecutorsClient{}, 68 serviceAccountNames: map[string]string{"default": ""}, 69 } 70 71 execution := &testkube.Execution{Id: "1", TestNamespace: "default"} 72 options := client.ExecuteOptions{ 73 ImagePullSecretNames: []string{"secret-name1"}, 74 Sync: true, 75 } 76 res, err := ce.Execute(ctx, execution, options) 77 assert.NoError(t, err) 78 assert.Equal(t, testkube.PASSED_ExecutionStatus, *res.Status) 79 } 80 81 func TestNewExecutorJobSpecEmptyArgs(t *testing.T) { 82 t.Parallel() 83 84 jobOptions := &JobOptions{ 85 Name: "name", 86 Namespace: "namespace", 87 InitImage: "kubeshop/testkube-init-executor:0.7.10", 88 Image: "ubuntu", 89 JobTemplate: defaultJobTemplate, 90 ScraperTemplate: "", 91 PvcTemplate: "", 92 JobTemplateExtensions: "", 93 ScraperTemplateExtensions: "", 94 PvcTemplateExtensions: "", 95 Command: []string{}, 96 Args: []string{}, 97 Features: featureflags.FeatureFlags{}, 98 } 99 spec, err := NewExecutorJobSpec(logger(), jobOptions) 100 assert.NoError(t, err) 101 assert.NotNil(t, spec) 102 } 103 104 func TestNewExecutorJobSpecWithArgs(t *testing.T) { 105 t.Parallel() 106 107 jobOptions := &JobOptions{ 108 Name: "name", 109 Namespace: "namespace", 110 InitImage: "kubeshop/testkube-init-executor:0.7.10", 111 Image: "curl", 112 JobTemplate: defaultJobTemplate, 113 ScraperTemplate: "", 114 PvcTemplate: "", 115 JobTemplateExtensions: "", 116 ScraperTemplateExtensions: "", 117 PvcTemplateExtensions: "", 118 ImagePullSecrets: []string{"secret-name"}, 119 Command: []string{"/bin/curl"}, 120 Args: []string{"-v", "https://testkube.kubeshop.io"}, 121 ActiveDeadlineSeconds: 100, 122 Envs: map[string]string{"key": "value"}, 123 Variables: map[string]testkube.Variable{"aa": {Name: "aa", Value: "bb", Type_: testkube.VariableTypeBasic}}, 124 Features: featureflags.FeatureFlags{}, 125 } 126 spec, err := NewExecutorJobSpec(logger(), jobOptions) 127 128 assert.NotEmpty(t, defaultJobTemplate) 129 assert.NoError(t, err) 130 assert.NotNil(t, spec) 131 132 wantEnvs := []corev1.EnvVar{ 133 {Name: "DEBUG", Value: "false"}, 134 {Name: "RUNNER_ENDPOINT", Value: ""}, 135 {Name: "RUNNER_ACCESSKEYID", Value: ""}, 136 {Name: "RUNNER_SECRETACCESSKEY", Value: ""}, 137 {Name: "RUNNER_REGION", Value: ""}, 138 {Name: "RUNNER_TOKEN", Value: ""}, 139 {Name: "RUNNER_BUCKET", Value: ""}, 140 {Name: "RUNNER_SSL", Value: "false"}, 141 {Name: "RUNNER_SKIP_VERIFY", Value: "false"}, 142 {Name: "RUNNER_CERT_FILE", Value: ""}, 143 {Name: "RUNNER_KEY_FILE", Value: ""}, 144 {Name: "RUNNER_CA_FILE", Value: ""}, 145 {Name: "RUNNER_SCRAPPERENABLED", Value: "false"}, 146 {Name: "RUNNER_DATADIR", Value: "/data"}, 147 {Name: "RUNNER_CDEVENTS_TARGET", Value: ""}, 148 {Name: "RUNNER_DASHBOARD_URI", Value: ""}, 149 {Name: "RUNNER_COMPRESSARTIFACTS", Value: "false"}, 150 {Name: "RUNNER_WORKINGDIR", Value: ""}, 151 {Name: "RUNNER_EXECUTIONID", Value: "name"}, 152 {Name: "RUNNER_TESTNAME", Value: ""}, 153 {Name: "RUNNER_EXECUTIONNUMBER", Value: "0"}, 154 {Name: "RUNNER_CONTEXTTYPE", Value: ""}, 155 {Name: "RUNNER_CONTEXTDATA", Value: ""}, 156 {Name: "RUNNER_APIURI", Value: ""}, 157 {Name: "RUNNER_PRO_MODE", Value: "false"}, 158 {Name: "RUNNER_PRO_API_KEY", Value: ""}, 159 {Name: "RUNNER_PRO_API_URL", Value: ""}, 160 {Name: "RUNNER_PRO_API_TLS_INSECURE", Value: "false"}, 161 {Name: "RUNNER_PRO_API_SKIP_VERIFY", Value: "false"}, 162 {Name: "RUNNER_PRO_CONNECTION_TIMEOUT", Value: "10"}, 163 {Name: "RUNNER_CLOUD_MODE", Value: "false"}, // DEPRECATED 164 {Name: "RUNNER_CLOUD_API_KEY", Value: ""}, // DEPRECATED 165 {Name: "RUNNER_CLOUD_API_URL", Value: ""}, // DEPRECATED 166 {Name: "RUNNER_CLOUD_API_TLS_INSECURE", Value: "false"}, // DEPRECATED 167 {Name: "RUNNER_CLOUD_API_SKIP_VERIFY", Value: "false"}, // DEPRECATED 168 {Name: "RUNNER_CLUSTERID", Value: ""}, 169 {Name: "RUNNER_PRO_API_CERT_FILE", Value: ""}, 170 {Name: "RUNNER_PRO_API_KEY_FILE", Value: ""}, 171 {Name: "RUNNER_PRO_API_CA_FILE", Value: ""}, 172 {Name: "CI", Value: "1"}, 173 {Name: "key", Value: "value"}, 174 {Name: "aa", Value: "bb"}, 175 } 176 177 assert.ElementsMatch(t, wantEnvs, spec.Spec.Template.Spec.Containers[0].Env) 178 } 179 180 func TestNewExecutorJobSpecWithoutInitImage(t *testing.T) { 181 t.Parallel() 182 183 jobOptions := &JobOptions{ 184 Name: "name", 185 Namespace: "namespace", 186 InitImage: "", 187 Image: "ubuntu", 188 JobTemplate: defaultJobTemplate, 189 ScraperTemplate: "", 190 PvcTemplate: "", 191 JobTemplateExtensions: "", 192 ScraperTemplateExtensions: "", 193 PvcTemplateExtensions: "", 194 Command: []string{}, 195 Args: []string{}, 196 Features: featureflags.FeatureFlags{}, 197 } 198 spec, err := NewExecutorJobSpec(logger(), jobOptions) 199 assert.NoError(t, err) 200 assert.NotNil(t, spec) 201 } 202 203 func TestNewExecutorJobSpecWithWorkingDirRelative(t *testing.T) { 204 t.Parallel() 205 206 mockCtrl := gomock.NewController(t) 207 defer mockCtrl.Finish() 208 209 mockTemplatesClient := templatesclientv1.NewMockInterface(mockCtrl) 210 mockInspector := imageinspector.NewMockInspector(mockCtrl) 211 212 jobOptions, _ := NewJobOptions( 213 logger(), 214 mockTemplatesClient, 215 executor.Images{}, 216 executor.Templates{}, 217 mockInspector, 218 map[string]string{}, 219 "", 220 "", 221 "", 222 testkube.Execution{ 223 Id: "name", 224 TestName: "name-test-1", 225 TestNamespace: "namespace", 226 }, 227 client.ExecuteOptions{ 228 TestSpec: testsv3.TestSpec{ 229 ExecutionRequest: &testsv3.ExecutionRequest{ 230 Image: "ubuntu", 231 }, 232 Content: &testsv3.TestContent{ 233 Repository: &testsv3.Repository{ 234 WorkingDir: "relative/path", 235 }, 236 }, 237 }, 238 }, 239 "", 240 false, 241 ) 242 243 spec, err := NewExecutorJobSpec(logger(), jobOptions) 244 assert.NoError(t, err) 245 assert.NotNil(t, spec) 246 247 assert.Equal(t, repoPath+"/relative/path", spec.Spec.Template.Spec.Containers[0].WorkingDir) 248 } 249 250 func TestNewExecutorJobSpecWithWorkingDirAbsolute(t *testing.T) { 251 t.Parallel() 252 253 mockCtrl := gomock.NewController(t) 254 defer mockCtrl.Finish() 255 256 mockTemplatesClient := templatesclientv1.NewMockInterface(mockCtrl) 257 mockInspector := imageinspector.NewMockInspector(mockCtrl) 258 259 jobOptions, _ := NewJobOptions( 260 logger(), 261 mockTemplatesClient, 262 executor.Images{}, 263 executor.Templates{}, 264 mockInspector, 265 map[string]string{}, 266 "", 267 "", 268 "", 269 testkube.Execution{ 270 Id: "name", 271 TestName: "name-test-1", 272 TestNamespace: "namespace", 273 }, 274 client.ExecuteOptions{ 275 TestSpec: testsv3.TestSpec{ 276 ExecutionRequest: &testsv3.ExecutionRequest{ 277 Image: "ubuntu", 278 }, 279 Content: &testsv3.TestContent{ 280 Repository: &testsv3.Repository{ 281 WorkingDir: "/absolute/path", 282 }, 283 }, 284 }, 285 }, 286 "", 287 false, 288 ) 289 spec, err := NewExecutorJobSpec(logger(), jobOptions) 290 assert.NoError(t, err) 291 assert.NotNil(t, spec) 292 293 assert.Equal(t, "/absolute/path", spec.Spec.Template.Spec.Containers[0].WorkingDir) 294 } 295 296 func TestNewExecutorJobSpecWithoutWorkingDir(t *testing.T) { 297 t.Parallel() 298 299 mockCtrl := gomock.NewController(t) 300 defer mockCtrl.Finish() 301 302 mockTemplatesClient := templatesclientv1.NewMockInterface(mockCtrl) 303 mockInspector := imageinspector.NewMockInspector(mockCtrl) 304 305 jobOptions, _ := NewJobOptions( 306 logger(), 307 mockTemplatesClient, 308 executor.Images{}, 309 executor.Templates{}, 310 mockInspector, 311 map[string]string{}, 312 "", 313 "", 314 "", 315 testkube.Execution{ 316 Id: "name", 317 TestName: "name-test-1", 318 TestNamespace: "namespace", 319 }, 320 client.ExecuteOptions{ 321 Namespace: "namespace", 322 TestSpec: testsv3.TestSpec{ 323 ExecutionRequest: &testsv3.ExecutionRequest{ 324 Image: "ubuntu", 325 }, 326 Content: &testsv3.TestContent{ 327 Repository: &testsv3.Repository{}, 328 }, 329 }, 330 }, 331 "", 332 false, 333 ) 334 spec, err := NewExecutorJobSpec(logger(), jobOptions) 335 assert.NoError(t, err) 336 assert.NotNil(t, spec) 337 338 assert.Empty(t, spec.Spec.Template.Spec.Containers[0].WorkingDir) 339 } 340 341 func logger() *zap.SugaredLogger { 342 atomicLevel := zap.NewAtomicLevel() 343 atomicLevel.SetLevel(zap.DebugLevel) 344 345 zapCfg := zap.NewDevelopmentConfig() 346 zapCfg.Level = atomicLevel 347 348 z, err := zapCfg.Build() 349 if err != nil { 350 panic(err) 351 } 352 return z.Sugar() 353 } 354 355 func getFakeClient(executionID string) *fake.Clientset { 356 initObjects := []runtime.Object{ 357 &corev1.Pod{ 358 ObjectMeta: metav1.ObjectMeta{ 359 Name: executionID, 360 Namespace: "default", 361 Labels: map[string]string{ 362 "job-name": executionID, 363 }, 364 }, 365 Status: corev1.PodStatus{ 366 Phase: corev1.PodSucceeded, 367 }, 368 }, 369 } 370 fakeClient := fake.NewSimpleClientset(initObjects...) 371 return fakeClient 372 } 373 374 type FakeExecutionMetric struct { 375 } 376 377 func (FakeExecutionMetric) IncAndObserveExecuteTest(execution testkube.Execution, dashboardURI string) { 378 } 379 380 type FakeEmitter struct { 381 } 382 383 func (FakeEmitter) Notify(event testkube.Event) { 384 } 385 386 type FakeResultRepository struct { 387 } 388 389 func (r FakeResultRepository) GetNextExecutionNumber(ctx context.Context, testName string) (number int32, err error) { 390 //TODO implement me 391 panic("implement me") 392 } 393 394 func (r FakeResultRepository) GetByNameAndTest(ctx context.Context, name, testName string) (testkube.Execution, error) { 395 //TODO implement me 396 panic("implement me") 397 } 398 399 func (r FakeResultRepository) GetLatestByTest(ctx context.Context, testName string) (*testkube.Execution, error) { 400 //TODO implement me 401 panic("implement me") 402 } 403 404 func (r FakeResultRepository) GetLatestByTests(ctx context.Context, testNames []string) (executions []testkube.Execution, err error) { 405 //TODO implement me 406 panic("implement me") 407 } 408 409 func (r FakeResultRepository) GetExecutions(ctx context.Context, filter result.Filter) ([]testkube.Execution, error) { 410 //TODO implement me 411 panic("implement me") 412 } 413 414 func (r FakeResultRepository) GetExecutionTotals(ctx context.Context, paging bool, filter ...result.Filter) (result testkube.ExecutionsTotals, err error) { 415 //TODO implement me 416 panic("implement me") 417 } 418 419 func (r FakeResultRepository) Insert(ctx context.Context, result testkube.Execution) error { 420 //TODO implement me 421 panic("implement me") 422 } 423 424 func (r FakeResultRepository) Update(ctx context.Context, result testkube.Execution) error { 425 //TODO implement me 426 panic("implement me") 427 } 428 429 func (r FakeResultRepository) GetLabels(ctx context.Context) (labels map[string][]string, err error) { 430 //TODO implement me 431 panic("implement me") 432 } 433 434 func (r FakeResultRepository) DeleteByTest(ctx context.Context, testName string) error { 435 //TODO implement me 436 panic("implement me") 437 } 438 439 func (r FakeResultRepository) DeleteByTestSuite(ctx context.Context, testSuiteName string) error { 440 //TODO implement me 441 panic("implement me") 442 } 443 444 func (r FakeResultRepository) DeleteAll(ctx context.Context) error { 445 //TODO implement me 446 panic("implement me") 447 } 448 449 func (r FakeResultRepository) DeleteByTests(ctx context.Context, testNames []string) (err error) { 450 //TODO implement me 451 panic("implement me") 452 } 453 454 func (r FakeResultRepository) DeleteByTestSuites(ctx context.Context, testSuiteNames []string) (err error) { 455 //TODO implement me 456 panic("implement me") 457 } 458 459 func (r FakeResultRepository) DeleteForAllTestSuites(ctx context.Context) (err error) { 460 //TODO implement me 461 panic("implement me") 462 } 463 464 func (r FakeResultRepository) GetTestMetrics(ctx context.Context, name string, limit, last int) (metrics testkube.ExecutionsMetrics, err error) { 465 //TODO implement me 466 panic("implement me") 467 } 468 469 func (r FakeResultRepository) Count(ctx context.Context, filter result.Filter) (count int64, err error) { 470 //TODO implement me 471 panic("implement me") 472 } 473 474 func (FakeResultRepository) GetExecution(ctx context.Context, id string) (testkube.Execution, error) { 475 return testkube.Execution{}, nil 476 } 477 478 func (FakeResultRepository) Get(ctx context.Context, id string) (testkube.Execution, error) { 479 return testkube.Execution{}, nil 480 } 481 482 func (FakeResultRepository) UpdateResult(ctx context.Context, id string, execution testkube.Execution) error { 483 return nil 484 } 485 func (FakeResultRepository) StartExecution(ctx context.Context, id string, startTime time.Time) error { 486 return nil 487 } 488 func (FakeResultRepository) EndExecution(ctx context.Context, execution testkube.Execution) error { 489 return nil 490 } 491 492 type FakeConfigRepository struct { 493 } 494 495 func (FakeConfigRepository) GetUniqueClusterId(ctx context.Context) (string, error) { 496 return "", nil 497 } 498 499 func (FakeConfigRepository) GetTelemetryEnabled(ctx context.Context) (ok bool, err error) { 500 return false, nil 501 } 502 503 func (FakeConfigRepository) Get(ctx context.Context) (testkube.Config, error) { 504 return testkube.Config{}, nil 505 } 506 507 func (FakeConfigRepository) Upsert(ctx context.Context, config testkube.Config) (testkube.Config, error) { 508 return config, nil 509 } 510 511 type FakeTestsClient struct { 512 } 513 514 func (FakeTestsClient) List(selector string) (*testsv3.TestList, error) { 515 return &testsv3.TestList{}, nil 516 } 517 518 func (FakeTestsClient) ListLabels() (map[string][]string, error) { 519 return map[string][]string{}, nil 520 } 521 522 func (FakeTestsClient) Get(name string) (*testsv3.Test, error) { 523 return &testsv3.Test{}, nil 524 } 525 526 func (FakeTestsClient) Create(test *testsv3.Test, disableSecretCreation bool, options ...v3.Option) (*testsv3.Test, error) { 527 return &testsv3.Test{}, nil 528 } 529 530 func (FakeTestsClient) Update(test *testsv3.Test, disableSecretCreation bool, options ...v3.Option) (*testsv3.Test, error) { 531 return &testsv3.Test{}, nil 532 } 533 534 func (FakeTestsClient) Delete(name string) error { 535 return nil 536 } 537 538 func (FakeTestsClient) DeleteAll() error { 539 return nil 540 } 541 542 func (FakeTestsClient) CreateTestSecrets(test *testsv3.Test, disableSecretCreation bool) error { 543 return nil 544 } 545 546 func (FakeTestsClient) UpdateTestSecrets(test *testsv3.Test, disableSecretCreation bool) error { 547 return nil 548 } 549 550 func (FakeTestsClient) LoadTestVariablesSecret(test *testsv3.Test) (*corev1.Secret, error) { 551 return &corev1.Secret{}, nil 552 } 553 554 func (FakeTestsClient) GetCurrentSecretUUID(testName string) (string, error) { 555 return "", nil 556 } 557 558 func (FakeTestsClient) GetSecretTestVars(testName, secretUUID string) (map[string]string, error) { 559 return map[string]string{}, nil 560 } 561 562 func (FakeTestsClient) ListByNames(names []string) ([]testsv3.Test, error) { 563 return []testsv3.Test{}, nil 564 } 565 566 func (FakeTestsClient) DeleteByLabels(selector string) error { 567 return nil 568 } 569 570 func (FakeTestsClient) UpdateStatus(test *testsv3.Test) error { 571 return nil 572 } 573 574 type FakeExecutorsClient struct { 575 } 576 577 func (FakeExecutorsClient) List(selector string) (*executorv1.ExecutorList, error) { 578 return &executorv1.ExecutorList{}, nil 579 } 580 581 func (FakeExecutorsClient) Get(name string) (*executorv1.Executor, error) { 582 return &executorv1.Executor{}, nil 583 } 584 585 func (FakeExecutorsClient) GetByType(executorType string) (*executorv1.Executor, error) { 586 return &executorv1.Executor{}, nil 587 } 588 589 func (FakeExecutorsClient) Create(executor *executorv1.Executor) (*executorv1.Executor, error) { 590 return &executorv1.Executor{}, nil 591 } 592 593 func (FakeExecutorsClient) Delete(name string) error { 594 return nil 595 } 596 597 func (FakeExecutorsClient) Update(executor *executorv1.Executor) (*executorv1.Executor, error) { 598 return &executorv1.Executor{}, nil 599 } 600 601 func (FakeExecutorsClient) DeleteByLabels(selector string) error { 602 return nil 603 }