k8s.io/kubernetes@v1.29.3/pkg/apis/batch/v1/defaults_test.go (about) 1 /* 2 Copyright 2016 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 v1_test 18 19 import ( 20 "math" 21 "reflect" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 batchv1 "k8s.io/api/batch/v1" 26 v1 "k8s.io/api/core/v1" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 utilfeature "k8s.io/apiserver/pkg/util/feature" 30 featuregatetesting "k8s.io/component-base/featuregate/testing" 31 "k8s.io/kubernetes/pkg/api/legacyscheme" 32 _ "k8s.io/kubernetes/pkg/apis/batch/install" 33 _ "k8s.io/kubernetes/pkg/apis/core/install" 34 "k8s.io/kubernetes/pkg/features" 35 "k8s.io/utils/pointer" 36 37 . "k8s.io/kubernetes/pkg/apis/batch/v1" 38 ) 39 40 func TestSetDefaultJob(t *testing.T) { 41 defaultLabels := map[string]string{"default": "default"} 42 validPodTemplateSpec := v1.PodTemplateSpec{ 43 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 44 } 45 tests := map[string]struct { 46 original *batchv1.Job 47 expected *batchv1.Job 48 expectLabels bool 49 enablePodReplacementPolicy bool 50 }{ 51 "Pod failure policy with some field values unspecified -> set default values": { 52 original: &batchv1.Job{ 53 Spec: batchv1.JobSpec{ 54 Template: v1.PodTemplateSpec{ 55 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 56 }, 57 PodFailurePolicy: &batchv1.PodFailurePolicy{ 58 Rules: []batchv1.PodFailurePolicyRule{ 59 { 60 Action: batchv1.PodFailurePolicyActionFailJob, 61 OnPodConditions: []batchv1.PodFailurePolicyOnPodConditionsPattern{ 62 { 63 Type: v1.DisruptionTarget, 64 Status: v1.ConditionTrue, 65 }, 66 { 67 Type: v1.PodConditionType("MemoryLimitExceeded"), 68 Status: v1.ConditionFalse, 69 }, 70 { 71 Type: v1.PodConditionType("DiskLimitExceeded"), 72 }, 73 }, 74 }, 75 { 76 Action: batchv1.PodFailurePolicyActionFailJob, 77 OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{ 78 Operator: batchv1.PodFailurePolicyOnExitCodesOpIn, 79 Values: []int32{1}, 80 }, 81 }, 82 { 83 Action: batchv1.PodFailurePolicyActionFailJob, 84 OnPodConditions: []batchv1.PodFailurePolicyOnPodConditionsPattern{ 85 { 86 Type: v1.DisruptionTarget, 87 }, 88 }, 89 }, 90 }, 91 }, 92 }, 93 }, 94 expected: &batchv1.Job{ 95 Spec: batchv1.JobSpec{ 96 Completions: pointer.Int32(1), 97 Parallelism: pointer.Int32(1), 98 BackoffLimit: pointer.Int32(6), 99 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 100 Suspend: pointer.Bool(false), 101 ManualSelector: pointer.Bool(false), 102 PodFailurePolicy: &batchv1.PodFailurePolicy{ 103 Rules: []batchv1.PodFailurePolicyRule{ 104 { 105 Action: batchv1.PodFailurePolicyActionFailJob, 106 OnPodConditions: []batchv1.PodFailurePolicyOnPodConditionsPattern{ 107 { 108 Type: v1.DisruptionTarget, 109 Status: v1.ConditionTrue, 110 }, 111 { 112 Type: v1.PodConditionType("MemoryLimitExceeded"), 113 Status: v1.ConditionFalse, 114 }, 115 { 116 Type: v1.PodConditionType("DiskLimitExceeded"), 117 Status: v1.ConditionTrue, 118 }, 119 }, 120 }, 121 { 122 Action: batchv1.PodFailurePolicyActionFailJob, 123 OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{ 124 Operator: batchv1.PodFailurePolicyOnExitCodesOpIn, 125 Values: []int32{1}, 126 }, 127 }, 128 { 129 Action: batchv1.PodFailurePolicyActionFailJob, 130 OnPodConditions: []batchv1.PodFailurePolicyOnPodConditionsPattern{ 131 { 132 Type: v1.DisruptionTarget, 133 Status: v1.ConditionTrue, 134 }, 135 }, 136 }, 137 }, 138 }, 139 }, 140 }, 141 expectLabels: true, 142 }, 143 "Pod failure policy and defaulting for pod replacement policy": { 144 original: &batchv1.Job{ 145 Spec: batchv1.JobSpec{ 146 Template: v1.PodTemplateSpec{ 147 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 148 }, 149 PodFailurePolicy: &batchv1.PodFailurePolicy{ 150 Rules: []batchv1.PodFailurePolicyRule{ 151 { 152 Action: batchv1.PodFailurePolicyActionFailJob, 153 OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{ 154 Operator: batchv1.PodFailurePolicyOnExitCodesOpIn, 155 Values: []int32{1}, 156 }, 157 }, 158 }, 159 }, 160 }, 161 }, 162 expected: &batchv1.Job{ 163 Spec: batchv1.JobSpec{ 164 Completions: pointer.Int32(1), 165 Parallelism: pointer.Int32(1), 166 BackoffLimit: pointer.Int32(6), 167 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 168 Suspend: pointer.Bool(false), 169 PodReplacementPolicy: podReplacementPtr(batchv1.Failed), 170 ManualSelector: pointer.Bool(false), 171 PodFailurePolicy: &batchv1.PodFailurePolicy{ 172 Rules: []batchv1.PodFailurePolicyRule{ 173 { 174 Action: batchv1.PodFailurePolicyActionFailJob, 175 OnExitCodes: &batchv1.PodFailurePolicyOnExitCodesRequirement{ 176 Operator: batchv1.PodFailurePolicyOnExitCodesOpIn, 177 Values: []int32{1}, 178 }, 179 }, 180 }, 181 }, 182 }, 183 }, 184 expectLabels: true, 185 enablePodReplacementPolicy: true, 186 }, 187 "All unspecified and podReplacementPolicyEnabled -> sets all to default values": { 188 original: &batchv1.Job{ 189 Spec: batchv1.JobSpec{ 190 Template: v1.PodTemplateSpec{ 191 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 192 }, 193 }, 194 }, 195 expected: &batchv1.Job{ 196 Spec: batchv1.JobSpec{ 197 Completions: pointer.Int32(1), 198 Parallelism: pointer.Int32(1), 199 BackoffLimit: pointer.Int32(6), 200 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 201 Suspend: pointer.Bool(false), 202 PodReplacementPolicy: podReplacementPtr(batchv1.TerminatingOrFailed), 203 ManualSelector: pointer.Bool(false), 204 }, 205 }, 206 expectLabels: true, 207 enablePodReplacementPolicy: true, 208 }, 209 "All unspecified -> sets all to default values": { 210 original: &batchv1.Job{ 211 Spec: batchv1.JobSpec{ 212 Template: v1.PodTemplateSpec{ 213 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 214 }, 215 }, 216 }, 217 expected: &batchv1.Job{ 218 Spec: batchv1.JobSpec{ 219 Completions: pointer.Int32(1), 220 Parallelism: pointer.Int32(1), 221 BackoffLimit: pointer.Int32(6), 222 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 223 Suspend: pointer.Bool(false), 224 ManualSelector: pointer.Bool(false), 225 }, 226 }, 227 expectLabels: true, 228 }, 229 "All unspecified, suspend job enabled -> sets all to default values": { 230 original: &batchv1.Job{ 231 Spec: batchv1.JobSpec{ 232 Template: v1.PodTemplateSpec{ 233 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 234 }, 235 }, 236 }, 237 expected: &batchv1.Job{ 238 Spec: batchv1.JobSpec{ 239 Completions: pointer.Int32(1), 240 Parallelism: pointer.Int32(1), 241 BackoffLimit: pointer.Int32(6), 242 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 243 Suspend: pointer.Bool(false), 244 ManualSelector: pointer.Bool(false), 245 }, 246 }, 247 expectLabels: true, 248 }, 249 "suspend set, everything else is defaulted": { 250 original: &batchv1.Job{ 251 Spec: batchv1.JobSpec{ 252 Suspend: pointer.Bool(true), 253 Template: v1.PodTemplateSpec{ 254 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 255 }, 256 }, 257 }, 258 expected: &batchv1.Job{ 259 Spec: batchv1.JobSpec{ 260 Completions: pointer.Int32(1), 261 Parallelism: pointer.Int32(1), 262 BackoffLimit: pointer.Int32(6), 263 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 264 Suspend: pointer.Bool(true), 265 ManualSelector: pointer.Bool(false), 266 }, 267 }, 268 expectLabels: true, 269 }, 270 "All unspecified -> all pointers are defaulted and no default labels": { 271 original: &batchv1.Job{ 272 ObjectMeta: metav1.ObjectMeta{ 273 Labels: map[string]string{"mylabel": "myvalue"}, 274 }, 275 Spec: batchv1.JobSpec{ 276 Template: v1.PodTemplateSpec{ 277 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 278 }, 279 }, 280 }, 281 expected: &batchv1.Job{ 282 Spec: batchv1.JobSpec{ 283 Completions: pointer.Int32(1), 284 Parallelism: pointer.Int32(1), 285 BackoffLimit: pointer.Int32(6), 286 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 287 Suspend: pointer.Bool(false), 288 ManualSelector: pointer.Bool(false), 289 }, 290 }, 291 }, 292 "WQ: Parallelism explicitly 0 and completions unset -> BackoffLimit is defaulted": { 293 original: &batchv1.Job{ 294 Spec: batchv1.JobSpec{ 295 Parallelism: pointer.Int32(0), 296 Template: v1.PodTemplateSpec{ 297 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 298 }, 299 }, 300 }, 301 expected: &batchv1.Job{ 302 Spec: batchv1.JobSpec{ 303 Parallelism: pointer.Int32(0), 304 BackoffLimit: pointer.Int32(6), 305 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 306 Suspend: pointer.Bool(false), 307 ManualSelector: pointer.Bool(false), 308 }, 309 }, 310 expectLabels: true, 311 }, 312 "WQ: Parallelism explicitly 2 and completions unset -> BackoffLimit is defaulted": { 313 original: &batchv1.Job{ 314 Spec: batchv1.JobSpec{ 315 Parallelism: pointer.Int32(2), 316 Template: v1.PodTemplateSpec{ 317 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 318 }, 319 }, 320 }, 321 expected: &batchv1.Job{ 322 Spec: batchv1.JobSpec{ 323 Parallelism: pointer.Int32(2), 324 BackoffLimit: pointer.Int32(6), 325 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 326 Suspend: pointer.Bool(false), 327 ManualSelector: pointer.Bool(false), 328 }, 329 }, 330 expectLabels: true, 331 }, 332 "Completions explicitly 2 and others unset -> parallelism and BackoffLimit are defaulted": { 333 original: &batchv1.Job{ 334 Spec: batchv1.JobSpec{ 335 Completions: pointer.Int32(2), 336 Template: v1.PodTemplateSpec{ 337 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 338 }, 339 }, 340 }, 341 expected: &batchv1.Job{ 342 Spec: batchv1.JobSpec{ 343 Completions: pointer.Int32(2), 344 Parallelism: pointer.Int32(1), 345 BackoffLimit: pointer.Int32(6), 346 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 347 Suspend: pointer.Bool(false), 348 ManualSelector: pointer.Bool(false), 349 }, 350 }, 351 expectLabels: true, 352 }, 353 "BackoffLimit explicitly 5 and others unset -> parallelism and completions are defaulted": { 354 original: &batchv1.Job{ 355 Spec: batchv1.JobSpec{ 356 BackoffLimit: pointer.Int32(5), 357 Template: v1.PodTemplateSpec{ 358 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 359 }, 360 }, 361 }, 362 expected: &batchv1.Job{ 363 Spec: batchv1.JobSpec{ 364 Completions: pointer.Int32(1), 365 Parallelism: pointer.Int32(1), 366 BackoffLimit: pointer.Int32(5), 367 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 368 Suspend: pointer.Bool(false), 369 ManualSelector: pointer.Bool(false), 370 }, 371 }, 372 expectLabels: true, 373 }, 374 "All set -> no change": { 375 original: &batchv1.Job{ 376 Spec: batchv1.JobSpec{ 377 Completions: pointer.Int32(8), 378 Parallelism: pointer.Int32(9), 379 BackoffLimit: pointer.Int32(10), 380 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 381 Suspend: pointer.Bool(false), 382 PodReplacementPolicy: podReplacementPtr(batchv1.TerminatingOrFailed), 383 ManualSelector: pointer.Bool(false), 384 Template: v1.PodTemplateSpec{ 385 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 386 }, 387 }, 388 }, 389 expected: &batchv1.Job{ 390 Spec: batchv1.JobSpec{ 391 Completions: pointer.Int32(8), 392 Parallelism: pointer.Int32(9), 393 BackoffLimit: pointer.Int32(10), 394 CompletionMode: completionModePtr(batchv1.NonIndexedCompletion), 395 Suspend: pointer.Bool(false), 396 PodReplacementPolicy: podReplacementPtr(batchv1.TerminatingOrFailed), 397 ManualSelector: pointer.Bool(false), 398 Template: v1.PodTemplateSpec{ 399 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 400 }, 401 }, 402 }, 403 expectLabels: true, 404 }, 405 "All set, flipped -> no change": { 406 original: &batchv1.Job{ 407 Spec: batchv1.JobSpec{ 408 Completions: pointer.Int32(11), 409 Parallelism: pointer.Int32(10), 410 BackoffLimit: pointer.Int32(9), 411 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 412 Suspend: pointer.Bool(true), 413 PodReplacementPolicy: podReplacementPtr(batchv1.Failed), 414 ManualSelector: pointer.Bool(true), 415 Template: v1.PodTemplateSpec{ 416 ObjectMeta: metav1.ObjectMeta{Labels: defaultLabels}, 417 }, 418 }, 419 }, 420 expected: &batchv1.Job{ 421 Spec: batchv1.JobSpec{ 422 Completions: pointer.Int32(11), 423 Parallelism: pointer.Int32(10), 424 BackoffLimit: pointer.Int32(9), 425 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 426 Suspend: pointer.Bool(true), 427 PodReplacementPolicy: podReplacementPtr(batchv1.Failed), 428 ManualSelector: pointer.Bool(true), 429 }, 430 }, 431 expectLabels: true, 432 }, 433 "BackoffLimitPerIndex specified, but no BackoffLimit -> default BackoffLimit to max int32": { 434 original: &batchv1.Job{ 435 Spec: batchv1.JobSpec{ 436 Completions: pointer.Int32(11), 437 Parallelism: pointer.Int32(10), 438 BackoffLimitPerIndex: pointer.Int32(1), 439 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 440 Template: validPodTemplateSpec, 441 Suspend: pointer.Bool(true), 442 ManualSelector: pointer.Bool(false), 443 }, 444 }, 445 expected: &batchv1.Job{ 446 Spec: batchv1.JobSpec{ 447 Completions: pointer.Int32(11), 448 Parallelism: pointer.Int32(10), 449 BackoffLimit: pointer.Int32(math.MaxInt32), 450 BackoffLimitPerIndex: pointer.Int32(1), 451 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 452 Template: validPodTemplateSpec, 453 Suspend: pointer.Bool(true), 454 ManualSelector: pointer.Bool(false), 455 }, 456 }, 457 expectLabels: true, 458 }, 459 "BackoffLimitPerIndex and BackoffLimit specified -> no change": { 460 original: &batchv1.Job{ 461 Spec: batchv1.JobSpec{ 462 Completions: pointer.Int32(11), 463 Parallelism: pointer.Int32(10), 464 BackoffLimit: pointer.Int32(3), 465 BackoffLimitPerIndex: pointer.Int32(1), 466 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 467 Template: validPodTemplateSpec, 468 Suspend: pointer.Bool(true), 469 ManualSelector: pointer.Bool(true), 470 }, 471 }, 472 expected: &batchv1.Job{ 473 Spec: batchv1.JobSpec{ 474 Completions: pointer.Int32(11), 475 Parallelism: pointer.Int32(10), 476 BackoffLimit: pointer.Int32(3), 477 BackoffLimitPerIndex: pointer.Int32(1), 478 CompletionMode: completionModePtr(batchv1.IndexedCompletion), 479 Template: validPodTemplateSpec, 480 Suspend: pointer.Bool(true), 481 ManualSelector: pointer.Bool(true), 482 }, 483 }, 484 expectLabels: true, 485 }, 486 } 487 488 for name, test := range tests { 489 t.Run(name, func(t *testing.T) { 490 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.JobPodReplacementPolicy, test.enablePodReplacementPolicy)() 491 original := test.original 492 expected := test.expected 493 obj2 := roundTrip(t, runtime.Object(original)) 494 actual, ok := obj2.(*batchv1.Job) 495 if !ok { 496 t.Fatalf("Unexpected object: %v", actual) 497 } 498 499 if diff := cmp.Diff(expected.Spec.Suspend, actual.Spec.Suspend); diff != "" { 500 t.Errorf(".spec.suspend does not match; -want,+got:\n%s", diff) 501 } 502 validateDefaultInt32(t, "Completions", actual.Spec.Completions, expected.Spec.Completions) 503 validateDefaultInt32(t, "Parallelism", actual.Spec.Parallelism, expected.Spec.Parallelism) 504 validateDefaultInt32(t, "BackoffLimit", actual.Spec.BackoffLimit, expected.Spec.BackoffLimit) 505 506 if diff := cmp.Diff(expected.Spec.PodFailurePolicy, actual.Spec.PodFailurePolicy); diff != "" { 507 t.Errorf("unexpected diff in errors (-want, +got):\n%s", diff) 508 } 509 if test.expectLabels != reflect.DeepEqual(actual.Labels, actual.Spec.Template.Labels) { 510 if test.expectLabels { 511 t.Errorf("Expected labels: %v, got: %v", actual.Spec.Template.Labels, actual.Labels) 512 } else { 513 t.Errorf("Unexpected equality: %v", actual.Labels) 514 } 515 } 516 if diff := cmp.Diff(expected.Spec.CompletionMode, actual.Spec.CompletionMode); diff != "" { 517 t.Errorf("Unexpected CompletionMode (-want,+got):\n%s", diff) 518 } 519 if diff := cmp.Diff(expected.Spec.PodReplacementPolicy, actual.Spec.PodReplacementPolicy); diff != "" { 520 t.Errorf("Unexpected PodReplacementPolicy (-want,+got):\n%s", diff) 521 } 522 if diff := cmp.Diff(expected.Spec.ManualSelector, actual.Spec.ManualSelector); diff != "" { 523 t.Errorf("Unexpected ManualSelector (-want,+got):\n%s", diff) 524 } 525 }) 526 } 527 } 528 529 func validateDefaultInt32(t *testing.T, field string, actual *int32, expected *int32) { 530 if (actual == nil) != (expected == nil) { 531 t.Errorf("Got different *%s than expected: %v %v", field, actual, expected) 532 } 533 if actual != nil && expected != nil { 534 if *actual != *expected { 535 t.Errorf("Got different %s than expected: %d %d", field, *actual, *expected) 536 } 537 } 538 } 539 540 func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { 541 data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj) 542 if err != nil { 543 t.Errorf("%v\n %#v", err, obj) 544 return nil 545 } 546 obj2, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data) 547 if err != nil { 548 t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) 549 return nil 550 } 551 obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) 552 err = legacyscheme.Scheme.Convert(obj2, obj3, nil) 553 if err != nil { 554 t.Errorf("%v\nSource: %#v", err, obj2) 555 return nil 556 } 557 return obj3 558 } 559 560 func TestSetDefaultCronJob(t *testing.T) { 561 tests := map[string]struct { 562 original *batchv1.CronJob 563 expected *batchv1.CronJob 564 }{ 565 "empty batchv1.CronJob should default batchv1.ConcurrencyPolicy and Suspend": { 566 original: &batchv1.CronJob{}, 567 expected: &batchv1.CronJob{ 568 Spec: batchv1.CronJobSpec{ 569 ConcurrencyPolicy: batchv1.AllowConcurrent, 570 Suspend: pointer.Bool(false), 571 SuccessfulJobsHistoryLimit: pointer.Int32(3), 572 FailedJobsHistoryLimit: pointer.Int32(1), 573 }, 574 }, 575 }, 576 "set fields should not be defaulted": { 577 original: &batchv1.CronJob{ 578 Spec: batchv1.CronJobSpec{ 579 ConcurrencyPolicy: batchv1.ForbidConcurrent, 580 Suspend: pointer.Bool(true), 581 SuccessfulJobsHistoryLimit: pointer.Int32(5), 582 FailedJobsHistoryLimit: pointer.Int32(5), 583 }, 584 }, 585 expected: &batchv1.CronJob{ 586 Spec: batchv1.CronJobSpec{ 587 ConcurrencyPolicy: batchv1.ForbidConcurrent, 588 Suspend: pointer.Bool(true), 589 SuccessfulJobsHistoryLimit: pointer.Int32(5), 590 FailedJobsHistoryLimit: pointer.Int32(5), 591 }, 592 }, 593 }, 594 } 595 596 for name, test := range tests { 597 original := test.original 598 expected := test.expected 599 obj2 := roundTrip(t, runtime.Object(original)) 600 actual, ok := obj2.(*batchv1.CronJob) 601 if !ok { 602 t.Errorf("%s: unexpected object: %v", name, actual) 603 t.FailNow() 604 } 605 if actual.Spec.ConcurrencyPolicy != expected.Spec.ConcurrencyPolicy { 606 t.Errorf("%s: got different concurrencyPolicy than expected: %v %v", name, actual.Spec.ConcurrencyPolicy, expected.Spec.ConcurrencyPolicy) 607 } 608 if *actual.Spec.Suspend != *expected.Spec.Suspend { 609 t.Errorf("%s: got different suspend than expected: %v %v", name, *actual.Spec.Suspend, *expected.Spec.Suspend) 610 } 611 if *actual.Spec.SuccessfulJobsHistoryLimit != *expected.Spec.SuccessfulJobsHistoryLimit { 612 t.Errorf("%s: got different successfulJobsHistoryLimit than expected: %v %v", name, *actual.Spec.SuccessfulJobsHistoryLimit, *expected.Spec.SuccessfulJobsHistoryLimit) 613 } 614 if *actual.Spec.FailedJobsHistoryLimit != *expected.Spec.FailedJobsHistoryLimit { 615 t.Errorf("%s: got different failedJobsHistoryLimit than expected: %v %v", name, *actual.Spec.FailedJobsHistoryLimit, *expected.Spec.FailedJobsHistoryLimit) 616 } 617 } 618 } 619 620 func completionModePtr(m batchv1.CompletionMode) *batchv1.CompletionMode { 621 return &m 622 } 623 624 func podReplacementPtr(m batchv1.PodReplacementPolicy) *batchv1.PodReplacementPolicy { 625 return &m 626 }