k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/hack/cluster-migration/main_test.go (about) 1 /* 2 Copyright 2023 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "testing" 21 22 v1 "k8s.io/api/core/v1" 23 cfg "sigs.k8s.io/prow/pkg/config" 24 ) 25 26 func TestConfigValidation_EmptyConfigPath(t *testing.T) { 27 config := Config{ 28 configPath: "", 29 jobConfigPath: "somepath", 30 repoReport: false, 31 repo: "someRepo", 32 output: "someOutput", 33 } 34 35 err := config.validate() 36 if err == nil { 37 t.Error("Expected an error due to empty configPath, but got none") 38 } 39 40 if err.Error() != "--config must set" { 41 t.Errorf("Expected error message '--config must set', but got: %s", err.Error()) 42 } 43 } 44 45 func TestConfigValidation_ValidConfigPath(t *testing.T) { 46 config := Config{ 47 configPath: "validPath", 48 jobConfigPath: "somepath", 49 repoReport: false, 50 repo: "someRepo", 51 output: "someOutput", 52 } 53 54 err := config.validate() 55 if err != nil { 56 t.Errorf("Did not expect an error, but got: %s", err.Error()) 57 } 58 } 59 60 func TestGetJobStatus_NonDefaultCluster(t *testing.T) { 61 job := cfg.JobBase{ 62 Cluster: "test-infra-trusted", 63 } 64 65 cluster, eligible, _ := getJobStatus(job) 66 if cluster != "test-infra-trusted" || !eligible { 67 t.Errorf("Expected cluster 'test-infra-trusted' and eligible true, got cluster '%s' and eligible %v", cluster, eligible) 68 } 69 } 70 71 func TestGetJobStatus_DefaultClusterEligible(t *testing.T) { 72 type testCase struct { 73 name string 74 job cfg.JobBase 75 expectedCluster string 76 expectedEligible bool 77 } 78 jobs := []testCase{ 79 { 80 name: "Test Case: Job is in the default cluster", 81 job: cfg.JobBase{ 82 Cluster: "default", 83 Spec: &v1.PodSpec{ 84 Containers: []v1.Container{ 85 { 86 Name: "someContainer", 87 }, 88 }, 89 }, 90 }, 91 expectedCluster: "", 92 expectedEligible: true, 93 }, 94 // 95 { 96 name: "Test Case: Job is in the eks-prow-build-cluster cluster", 97 job: cfg.JobBase{ 98 Cluster: "eks-prow-build-cluster", 99 Spec: &v1.PodSpec{ 100 Containers: []v1.Container{ 101 { 102 Name: "someContainer", 103 }, 104 }, 105 }, 106 }, 107 expectedCluster: "eks-prow-build-cluster", 108 expectedEligible: true, 109 }, 110 // 111 { 112 name: "Test Case: Job is in the default cluster and has a cred label", 113 job: cfg.JobBase{ 114 Cluster: "default", 115 Labels: map[string]string{ 116 "cred": "someValue", 117 }, 118 Spec: &v1.PodSpec{ 119 Containers: []v1.Container{ 120 { 121 Name: "someContainer", 122 }, 123 }, 124 }, 125 }, 126 expectedCluster: "", 127 expectedEligible: false, 128 }, 129 // 130 { 131 name: "Test Case: Job has an allowed cred label", 132 job: cfg.JobBase{ 133 Cluster: "default", 134 Labels: map[string]string{ 135 "preset-aws-credential": "someValue", 136 "preset-aws-ssh": "someValue", 137 }, 138 Spec: &v1.PodSpec{ 139 Containers: []v1.Container{ 140 { 141 Name: "someContainer", 142 }, 143 }, 144 }, 145 }, 146 expectedCluster: "", 147 expectedEligible: true, 148 }, 149 // 150 { 151 name: "Test Case: Job container environment is derived from a secret", 152 job: cfg.JobBase{ 153 Cluster: "default", 154 Spec: &v1.PodSpec{ 155 Containers: []v1.Container{ 156 { 157 Name: "someContainer", 158 Env: []v1.EnvVar{ 159 { 160 Name: "someVariable", 161 ValueFrom: &v1.EnvVarSource{ 162 SecretKeyRef: &v1.SecretKeySelector{ 163 Key: "someSecret", 164 }, 165 }, 166 }, 167 }, 168 }, 169 }, 170 }, 171 }, 172 expectedCluster: "", 173 expectedEligible: false, 174 }, 175 // 176 { 177 name: "Test Case: Job container environment is derived from an allowed secret", 178 job: cfg.JobBase{ 179 Cluster: "default", 180 Spec: &v1.PodSpec{ 181 Containers: []v1.Container{ 182 { 183 Name: "someContainer", 184 Env: []v1.EnvVar{ 185 { 186 Name: "someVariable", 187 ValueFrom: &v1.EnvVarSource{ 188 SecretKeyRef: &v1.SecretKeySelector{ 189 Key: "aws-ssh-key-secret", 190 }, 191 }, 192 }, 193 }, 194 }, 195 }, 196 }, 197 }, 198 expectedCluster: "", 199 expectedEligible: true, 200 }, 201 // 202 { 203 name: "Test Case: Jobs container environment has \"cred\" in the name", 204 job: cfg.JobBase{ 205 Cluster: "default", 206 Spec: &v1.PodSpec{ 207 Containers: []v1.Container{ 208 { 209 Name: "someContainer", 210 Env: []v1.EnvVar{ 211 { 212 Name: "somecred", 213 }, 214 }, 215 }, 216 }, 217 }, 218 }, 219 expectedCluster: "", 220 expectedEligible: false, 221 }, 222 // 223 { 224 name: "Test Case: Jobs container environment is an allowed variable", 225 job: cfg.JobBase{ 226 Cluster: "default", 227 Spec: &v1.PodSpec{ 228 Containers: []v1.Container{ 229 { 230 Name: "someContainer", 231 Env: []v1.EnvVar{ 232 { 233 Name: "AWS_SHARED_CREDENTIALS_FILE", 234 }, 235 }, 236 }, 237 }, 238 }, 239 }, 240 expectedCluster: "", 241 expectedEligible: true, 242 }, 243 // 244 { 245 name: "Test Case: Jobs container environment has \"Cred\" in the name", 246 job: cfg.JobBase{ 247 Cluster: "default", 248 Spec: &v1.PodSpec{ 249 Containers: []v1.Container{ 250 { 251 Name: "someContainer", 252 Env: []v1.EnvVar{ 253 { 254 Name: "someCred", 255 }, 256 }, 257 }, 258 }, 259 }, 260 }, 261 expectedCluster: "", 262 expectedEligible: false, 263 }, 264 // 265 { 266 name: "Test Case: Jobs container arguments don't contain any disallowed words", 267 job: cfg.JobBase{ 268 Cluster: "default", 269 Spec: &v1.PodSpec{ 270 Containers: []v1.Container{ 271 { 272 Name: "someContainer", 273 Args: []string{ 274 "test", 275 }, 276 }, 277 }, 278 }, 279 }, 280 expectedCluster: "", 281 expectedEligible: true, 282 }, 283 // 284 { 285 name: "Test Case: Jobs container arguments contain \"gcp\"", 286 job: cfg.JobBase{ 287 Cluster: "default", 288 Spec: &v1.PodSpec{ 289 Containers: []v1.Container{ 290 { 291 Name: "someContainer", 292 Args: []string{ 293 "--gcp-zone=us-central1-f", 294 }, 295 }, 296 }, 297 }, 298 }, 299 expectedCluster: "", 300 expectedEligible: true, 301 }, 302 // 303 { 304 name: "Test Case: Job container command doesn't contain disallowed words", 305 job: cfg.JobBase{ 306 Cluster: "default", 307 Spec: &v1.PodSpec{ 308 Containers: []v1.Container{ 309 { 310 Name: "someContainer", 311 Command: []string{ 312 "someCommand", 313 }, 314 }, 315 }, 316 }, 317 }, 318 expectedCluster: "", 319 expectedEligible: true, 320 }, 321 // 322 { 323 name: "Test Case: Job container command contains \"gcp\"", 324 job: cfg.JobBase{ 325 Cluster: "default", 326 Spec: &v1.PodSpec{ 327 Containers: []v1.Container{ 328 { 329 Name: "someContainer", 330 Command: []string{ 331 "someCommand --gcp-zone=us-central1-f", 332 }, 333 }, 334 }, 335 }, 336 }, 337 expectedCluster: "", 338 expectedEligible: true, 339 }, 340 // 341 { 342 name: "Test Case: Job container volume mount contains \"Secret\"", 343 job: cfg.JobBase{ 344 Cluster: "default", 345 Spec: &v1.PodSpec{ 346 Containers: []v1.Container{ 347 { 348 Name: "someContainer", 349 VolumeMounts: []v1.VolumeMount{ 350 { 351 Name: "someSecret", 352 }, 353 }, 354 }, 355 }, 356 }, 357 }, 358 expectedCluster: "", 359 expectedEligible: false, 360 }, 361 // 362 { 363 name: "Test Case: Job container volume mount contains \"secret\"", 364 job: cfg.JobBase{ 365 Cluster: "default", 366 Spec: &v1.PodSpec{ 367 Containers: []v1.Container{ 368 { 369 Name: "someContainer", 370 VolumeMounts: []v1.VolumeMount{ 371 { 372 Name: "somesecret", 373 }, 374 }, 375 }, 376 }, 377 }, 378 }, 379 expectedCluster: "", 380 expectedEligible: false, 381 }, 382 // 383 { 384 name: "Test Case: Job container volume mount contains \"cred\"", 385 job: cfg.JobBase{ 386 Cluster: "default", 387 Spec: &v1.PodSpec{ 388 Containers: []v1.Container{ 389 { 390 Name: "someContainer", 391 VolumeMounts: []v1.VolumeMount{ 392 { 393 Name: "somecred", 394 }, 395 }, 396 }, 397 }, 398 }, 399 }, 400 expectedCluster: "", 401 expectedEligible: false, 402 }, 403 // 404 { 405 name: "Test Case: Job volume contains \"cred\"", 406 job: cfg.JobBase{ 407 Cluster: "default", 408 Spec: &v1.PodSpec{ 409 Containers: []v1.Container{ 410 { 411 Name: "someContainer", 412 }, 413 }, 414 Volumes: []v1.Volume{ 415 { 416 Name: "someCred", 417 }, 418 }, 419 }, 420 }, 421 expectedCluster: "", 422 expectedEligible: false, 423 }, 424 // 425 { 426 name: "Test Case: Job volume contains allowed volume name", 427 job: cfg.JobBase{ 428 Cluster: "default", 429 Spec: &v1.PodSpec{ 430 Containers: []v1.Container{ 431 { 432 Name: "someContainer", 433 }, 434 }, 435 Volumes: []v1.Volume{ 436 { 437 Name: "aws-cred", 438 }, 439 }, 440 }, 441 }, 442 expectedCluster: "", 443 expectedEligible: true, 444 }, 445 // 446 { 447 name: "Test Case: Job volume is derived from unapproved secret", 448 job: cfg.JobBase{ 449 Cluster: "default", 450 Spec: &v1.PodSpec{ 451 Containers: []v1.Container{ 452 { 453 Name: "someContainer", 454 }, 455 }, 456 Volumes: []v1.Volume{ 457 { 458 Name: "someVolume", 459 VolumeSource: v1.VolumeSource{ 460 Secret: &v1.SecretVolumeSource{ 461 SecretName: "someSecret", 462 }, 463 }, 464 }, 465 }, 466 }, 467 }, 468 expectedCluster: "", 469 expectedEligible: false, 470 }, 471 // 472 { 473 name: "Test Case: Job volume is derived from approved secret", 474 job: cfg.JobBase{ 475 Cluster: "default", 476 Spec: &v1.PodSpec{ 477 Containers: []v1.Container{ 478 { 479 Name: "someContainer", 480 }, 481 }, 482 Volumes: []v1.Volume{ 483 { 484 Name: "someVolume", 485 VolumeSource: v1.VolumeSource{ 486 Secret: &v1.SecretVolumeSource{ 487 SecretName: "ssh-key-secret", 488 }, 489 }, 490 }, 491 }, 492 }, 493 }, 494 expectedCluster: "", 495 expectedEligible: true, 496 }, 497 // 498 { 499 name: "Test Case: Job volume is derived from approved secret", 500 job: cfg.JobBase{ 501 Cluster: "default", 502 Spec: &v1.PodSpec{ 503 Containers: []v1.Container{ 504 { 505 Name: "someContainer", 506 }, 507 }, 508 Volumes: []v1.Volume{ 509 { 510 Name: "someVolume", 511 VolumeSource: v1.VolumeSource{ 512 Secret: &v1.SecretVolumeSource{ 513 SecretName: "service-account", 514 }, 515 }, 516 }, 517 }, 518 }, 519 }, 520 expectedCluster: "", 521 expectedEligible: true, 522 }, 523 } 524 525 for _, job := range jobs { 526 cluster, eligible, _ := getJobStatus(job.job) 527 if cluster != job.expectedCluster || eligible != job.expectedEligible { 528 t.Errorf("%v: Expected cluster '%s' and eligible %v, got cluster '%s' and eligible %v", job.name, job.expectedCluster, job.expectedEligible, cluster, eligible) 529 } 530 } 531 } 532 533 func TestGetPercentage(t *testing.T) { 534 tests := []struct { 535 int1, int2 int 536 expected float64 537 }{ 538 {10, 100, 10}, 539 {5, 10, 50}, 540 {0, 10, 0}, 541 {10, 0, 100}, // Ensure handling division by zero 542 } 543 544 for _, test := range tests { 545 result := getPercentage(test.int1, test.int2) 546 if result != test.expected { 547 t.Errorf("For inputs %d and %d, expected %.2f but got %.2f", test.int1, test.int2, test.expected, result) 548 } 549 } 550 } 551 552 func TestPrintPercentage(t *testing.T) { 553 tests := []struct { 554 input float64 555 expected string 556 }{ 557 {10.5678, "10.57%"}, 558 {50, "50.00%"}, 559 {0, "0.00%"}, 560 } 561 562 for _, test := range tests { 563 result := printPercentage(test.input) 564 if result != test.expected { 565 t.Errorf("For input %.4f, expected '%s' but got '%s'", test.input, test.expected, result) 566 } 567 } 568 } 569 570 func TestContainsAny(t *testing.T) { 571 tests := []struct { 572 s string 573 words []string 574 expected bool 575 }{ 576 {"hello world", []string{"hello", "test"}, true}, 577 {"hello world", []string{"test", "sample"}, false}, 578 {"HELLO WORLD", []string{"hello"}, true}, // Ensure case-insensitivity 579 } 580 581 for _, test := range tests { 582 result := containsAny(test.s, test.words) 583 if result != test.expected { 584 t.Errorf("For input '%s' with words %v, expected %v but got %v", test.s, test.words, test.expected, result) 585 } 586 } 587 }