Skip to content

Commit 294dc15

Browse files
Merge pull request #312 from jottofar/cvo-bug-1794823
Bug 1794823: lib/resourcemerge: Do not attempt to change Job's immutable spec.selector
2 parents 69df6ba + 27dc21f commit 294dc15

File tree

3 files changed

+101
-9
lines changed

3 files changed

+101
-9
lines changed

docs/user/reconciliation.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,10 @@ For subsequent updates, the builder blocks until:
164164
### Job
165165

166166
After pushing the merged Job into the cluster, the builder blocks until the Job succeeds.
167+
168+
The cluster-version operator will panic if spec.selector is set because there are no clear use-cases for setting it in release manifests.
169+
170+
Subsequent updates:
171+
172+
* The cluster-version operator is currently unable to delete and recreate a Job to track changes in release manifests. Please avoid making changes to Job manifests until the cluster-version operator supports Job delete/recreate.
173+
* A Job's spec.selector will never be updated because spec.selector is immutable.

lib/resourcemerge/batch.go

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,16 @@ package resourcemerge
22

33
import (
44
batchv1 "k8s.io/api/batch/v1"
5-
"k8s.io/apimachinery/pkg/api/equality"
65
)
76

87
// EnsureJob ensures that the existing matches the required.
98
// modified is set to true when existing had to be updated with required.
109
func EnsureJob(modified *bool, existing *batchv1.Job, required batchv1.Job) {
11-
EnsureObjectMeta(modified, &existing.ObjectMeta, required.ObjectMeta)
1210

13-
if existing.Spec.Selector == nil {
14-
*modified = true
15-
existing.Spec.Selector = required.Spec.Selector
16-
}
17-
if !equality.Semantic.DeepEqual(existing.Spec.Selector, required.Spec.Selector) {
18-
*modified = true
19-
existing.Spec.Selector = required.Spec.Selector
11+
if required.Spec.Selector != nil {
12+
panic("spec.selector is not supported in Job manifests")
2013
}
14+
EnsureObjectMeta(modified, &existing.ObjectMeta, required.ObjectMeta)
2115
setInt32Ptr(modified, &existing.Spec.Parallelism, required.Spec.Parallelism)
2216
setInt32Ptr(modified, &existing.Spec.Completions, required.Spec.Completions)
2317
setInt64Ptr(modified, &existing.Spec.ActiveDeadlineSeconds, required.Spec.ActiveDeadlineSeconds)

lib/resourcemerge/batch_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package resourcemerge
2+
3+
import (
4+
"testing"
5+
6+
batchv1 "k8s.io/api/batch/v1"
7+
"k8s.io/apimachinery/pkg/api/equality"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
pointer "k8s.io/utils/pointer"
10+
)
11+
12+
func TestEnsureJob(t *testing.T) {
13+
labelSelector := metav1.LabelSelector{}
14+
tests := []struct {
15+
name string
16+
existing batchv1.Job
17+
required batchv1.Job
18+
19+
expectedModified bool
20+
expectedPanic bool
21+
expected batchv1.Job
22+
}{
23+
{
24+
name: "different backofflimit count",
25+
existing: batchv1.Job{
26+
Spec: batchv1.JobSpec{
27+
BackoffLimit: pointer.Int32Ptr(2)}},
28+
required: batchv1.Job{
29+
Spec: batchv1.JobSpec{
30+
BackoffLimit: pointer.Int32Ptr(3)}},
31+
32+
expectedModified: true,
33+
expected: batchv1.Job{
34+
Spec: batchv1.JobSpec{
35+
BackoffLimit: pointer.Int32Ptr(3)}},
36+
},
37+
{
38+
name: "same backofflimit count",
39+
existing: batchv1.Job{
40+
Spec: batchv1.JobSpec{
41+
BackoffLimit: pointer.Int32Ptr(2)}},
42+
required: batchv1.Job{
43+
Spec: batchv1.JobSpec{
44+
BackoffLimit: pointer.Int32Ptr(2)}},
45+
46+
expectedModified: false,
47+
expected: batchv1.Job{
48+
Spec: batchv1.JobSpec{
49+
BackoffLimit: pointer.Int32Ptr(2)}},
50+
},
51+
{
52+
name: "required-Selector not nil",
53+
existing: batchv1.Job{
54+
Spec: batchv1.JobSpec{
55+
Selector: &labelSelector,
56+
ManualSelector: pointer.BoolPtr(false)}},
57+
required: batchv1.Job{
58+
Spec: batchv1.JobSpec{
59+
Selector: &labelSelector,
60+
ManualSelector: pointer.BoolPtr(true)}},
61+
62+
expectedPanic: true,
63+
},
64+
}
65+
66+
for _, test := range tests {
67+
t.Run(test.name, func(t *testing.T) {
68+
defer func() {
69+
switch r := recover(); r {
70+
case nil:
71+
if test.expectedPanic {
72+
t.Errorf(test.name + " should have panicked!")
73+
}
74+
default:
75+
if !test.expectedPanic {
76+
panic(r)
77+
}
78+
}
79+
}()
80+
modified := pointer.BoolPtr(false)
81+
EnsureJob(modified, &test.existing, test.required)
82+
if *modified != test.expectedModified {
83+
t.Errorf("mismatch modified got: %v want: %v", *modified, test.expectedModified)
84+
}
85+
86+
if !equality.Semantic.DeepEqual(test.existing, test.expected) {
87+
t.Errorf("mismatch Job got: %v want: %v", test.existing, test.expected)
88+
}
89+
})
90+
}
91+
}

0 commit comments

Comments
 (0)