Skip to content

Commit ae78491

Browse files
committed
controller: sched: report DedicatedInformerActive behavior
We want to be able to see the informer mode of the scheduler as a preparation step for having the informer default change according to the kubelet version. This commit enables reflecting the informer value (default or configured) as part of the status conditions which is less direct than reporting it in spec and is valid. Signed-off-by: Shereen Haj <[email protected]> (cherry picked from commit 09d76b9) (cherry picked from commit 79be8cb)
1 parent 2a34cb8 commit ae78491

File tree

4 files changed

+357
-10
lines changed

4 files changed

+357
-10
lines changed

controllers/numaresourcesscheduler_controller.go

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,9 @@ func (r *NUMAResourcesSchedulerReconciler) syncNUMASchedulerResources(ctx contex
224224
Duration: cacheResyncPeriod,
225225
}
226226

227+
informerCondition := buildDedicatedInformerCondition(*instance, schedSpec)
228+
schedStatus.Conditions = status.GetUpdatedSchedulerConditions(schedStatus.Conditions, informerCondition)
229+
227230
r.SchedulerManifests.Deployment.Spec.Replicas = r.computeSchedulerReplicas(schedSpec)
228231
klog.V(4).InfoS("using scheduler replicas", "replicas", *r.SchedulerManifests.Deployment.Spec.Replicas)
229232
// TODO: if replicas doesn't make sense (autodetect disabled and user set impossible value) then we
@@ -289,15 +292,39 @@ func platformNormalize(spec *nropv1.NUMAResourcesSchedulerSpec, platInfo Platfor
289292
klog.V(4).InfoS("SchedulerInformer default is overridden", "Platform", platInfo.Platform, "PlatformVersion", platInfo.Version.String(), "SchedulerInformer", &spec.SchedulerInformer)
290293
}
291294
}
295+
296+
func buildDedicatedInformerCondition(instance nropv1.NUMAResourcesScheduler, normalized nropv1.NUMAResourcesSchedulerSpec) metav1.Condition {
297+
condition := metav1.Condition{
298+
Type: status.ConditionDedicatedInformerActive,
299+
Status: metav1.ConditionTrue,
300+
ObservedGeneration: instance.ObjectMeta.Generation,
301+
Reason: status.ConditionDedicatedInformerActive,
302+
}
303+
304+
if *normalized.SchedulerInformer == nropv1.SchedulerInformerShared {
305+
condition.Status = metav1.ConditionFalse
306+
}
307+
308+
return condition
309+
}
310+
292311
func (r *NUMAResourcesSchedulerReconciler) updateStatus(ctx context.Context, initialStatus nropv1.NUMAResourcesSchedulerStatus, sched *nropv1.NUMAResourcesScheduler, condition string, reason string, message string) error {
293-
updatedStatus := *sched.Status.DeepCopy()
312+
c := metav1.Condition{
313+
Type: condition,
314+
Status: metav1.ConditionTrue,
315+
Reason: reason,
316+
Message: message,
317+
}
318+
sched.Status.Conditions = status.GetUpdatedSchedulerConditions(sched.Status.Conditions, c)
294319

295-
updatedStatus.Conditions, _ = status.UpdateConditions(sched.Status.Conditions, condition, reason, message)
296-
if !status.NUMAResourcesSchedulerNeedsUpdate(initialStatus, updatedStatus) {
320+
if !status.NUMAResourcesSchedulerNeedsUpdate(initialStatus, sched.Status) {
297321
return nil
298322
}
299323

300-
sched.Status.Conditions = updatedStatus.Conditions
324+
if status.EqualConditions(initialStatus.Conditions, sched.Status.Conditions) {
325+
sched.Status.Conditions = initialStatus.Conditions
326+
}
327+
301328
if err := r.Client.Status().Update(ctx, sched); err != nil {
302329
return fmt.Errorf("could not update status for object %s: %w", client.ObjectKeyFromObject(sched), err)
303330
}

controllers/numaresourcesscheduler_controller_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,67 @@ var _ = ginkgo.Describe("Test NUMAResourcesScheduler Reconcile", func() {
216216
gomega.Expect(nrs.Status.CacheResyncPeriod.Seconds()).To(gomega.Equal(resyncPeriod.Seconds()))
217217
})
218218

219+
ginkgo.Context("should reflect DedicatedInformerActive in status conditions", func() {
220+
ginkgo.It("with default values", func() {
221+
key := client.ObjectKeyFromObject(nrs)
222+
_, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
223+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
224+
225+
gomega.Expect(reconciler.Client.Get(context.TODO(), key, nrs)).To(gomega.Succeed())
226+
227+
c := getConditionByType(nrs.Status.Conditions, status.ConditionDedicatedInformerActive)
228+
229+
gomega.Expect(c).ToNot(gomega.BeNil())
230+
gomega.Expect(c.Status).To(gomega.Equal(metav1.ConditionTrue))
231+
})
232+
233+
ginkgo.It("with updated values - explicitly configured to Dedicated", func() {
234+
nrs := nrs.DeepCopy()
235+
nrs.Spec.SchedulerInformer = ptr.To(nropv1.SchedulerInformerDedicated)
236+
237+
gomega.Eventually(func() bool {
238+
if err := reconciler.Client.Update(context.TODO(), nrs); err != nil {
239+
klog.Warningf("failed to update the scheduler object; err: %v", err)
240+
return false
241+
}
242+
return true
243+
}, 30*time.Second, 5*time.Second).Should(gomega.BeTrue())
244+
245+
key := client.ObjectKeyFromObject(nrs)
246+
_, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
247+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
248+
gomega.Expect(reconciler.Client.Get(context.TODO(), key, nrs)).To(gomega.Succeed())
249+
250+
c := getConditionByType(nrs.Status.Conditions, status.ConditionDedicatedInformerActive)
251+
252+
gomega.Expect(c).ToNot(gomega.BeNil())
253+
gomega.Expect(c.Status).To(gomega.Equal(metav1.ConditionTrue))
254+
})
255+
256+
ginkgo.It("with updated values - explicitly configured to Shared", func() {
257+
nrs := nrs.DeepCopy()
258+
nrs.Spec.SchedulerInformer = ptr.To(nropv1.SchedulerInformerShared)
259+
260+
gomega.Eventually(func() bool {
261+
if err := reconciler.Client.Update(context.TODO(), nrs); err != nil {
262+
klog.Warningf("failed to update the scheduler object; err: %v", err)
263+
return false
264+
}
265+
return true
266+
}, 30*time.Second, 5*time.Second).Should(gomega.BeTrue())
267+
268+
key := client.ObjectKeyFromObject(nrs)
269+
_, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
270+
gomega.Expect(err).ToNot(gomega.HaveOccurred())
271+
gomega.Expect(reconciler.Client.Get(context.TODO(), key, nrs)).To(gomega.Succeed())
272+
273+
c := getConditionByType(nrs.Status.Conditions, status.ConditionDedicatedInformerActive)
274+
275+
gomega.Expect(c).ToNot(gomega.BeNil())
276+
gomega.Expect(c.Status).To(gomega.Equal(metav1.ConditionFalse))
277+
})
278+
})
279+
219280
ginkgo.It("should have the correct priority class", func() {
220281
key := client.ObjectKeyFromObject(nrs)
221282
_, err := reconciler.Reconcile(context.TODO(), reconcile.Request{NamespacedName: key})
@@ -720,6 +781,17 @@ var _ = ginkgo.Describe("Test NUMAResourcesScheduler Reconcile", func() {
720781
gomega.Expect(err).ToNot(gomega.HaveOccurred())
721782

722783
expectCacheParams(reconciler.Client, depmanifests.CacheResyncAutodetect, depmanifests.CacheResyncOnlyExclusiveResources, expectedInformer)
784+
785+
expectedDedicatedActiveStatus := metav1.ConditionTrue
786+
if expectedInformer == depmanifests.CacheInformerShared {
787+
expectedDedicatedActiveStatus = metav1.ConditionFalse
788+
}
789+
790+
gomega.Expect(reconciler.Client.Get(context.TODO(), key, nrs)).To(gomega.Succeed())
791+
c := getConditionByType(nrs.Status.Conditions, status.ConditionDedicatedInformerActive)
792+
793+
gomega.Expect(c).ToNot(gomega.BeNil())
794+
gomega.Expect(c.Status).To(gomega.Equal(expectedDedicatedActiveStatus))
723795
},
724796
ginkgo.Entry("with fixed Openshift the default informer is Shared", PlatformInfo{
725797
Platform: platform.OpenShift,

pkg/status/status.go

Lines changed: 92 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const (
3737
ConditionUpgradeable = "Upgradeable"
3838
)
3939

40+
const (
41+
ConditionDedicatedInformerActive = "DedicatedInformerActive"
42+
)
43+
4044
// TODO: are we duping these?
4145
const (
4246
ReasonAsExpected = "AsExpected"
@@ -70,18 +74,23 @@ func NUMAResourcesSchedulerNeedsUpdate(oldStatus, newStatus nropv1.NUMAResources
7074
return !reflect.DeepEqual(os, ns)
7175
}
7276

77+
func EqualConditions(current, updated []metav1.Condition) bool {
78+
c := CloneConditions(current)
79+
u := CloneConditions(updated)
80+
81+
resetIncomparableConditionFields(c)
82+
resetIncomparableConditionFields(u)
83+
84+
return reflect.DeepEqual(c, u)
85+
}
86+
7387
// UpdateConditions compute new conditions based on arguments, and then compare with given current conditions.
7488
// Returns the conditions to use, either current or newly computed, and a boolean flag which is `true` if conditions need
7589
// update - so if they are updated since the current conditions.
7690
func UpdateConditions(currentConditions []metav1.Condition, condition string, reason string, message string) ([]metav1.Condition, bool) {
7791
conditions := NewConditions(condition, reason, message)
7892

79-
options := []cmp.Option{
80-
cmpopts.IgnoreFields(metav1.Condition{}, "LastTransitionTime"),
81-
cmpopts.IgnoreFields(metav1.Condition{}, "ObservedGeneration"),
82-
}
83-
84-
if cmp.Equal(conditions, currentConditions, options...) {
93+
if EqualConditions(currentConditions, conditions) {
8594
return currentConditions, false
8695
}
8796
return conditions, true
@@ -145,6 +154,42 @@ func newBaseConditions() []metav1.Condition {
145154
}
146155
}
147156

157+
func NewNUMAResourcesSchedulerBaseConditions() []metav1.Condition {
158+
now := time.Now()
159+
return []metav1.Condition{
160+
{
161+
Type: ConditionAvailable,
162+
Status: metav1.ConditionFalse,
163+
LastTransitionTime: metav1.Time{Time: now},
164+
Reason: ConditionAvailable,
165+
},
166+
{
167+
Type: ConditionUpgradeable,
168+
Status: metav1.ConditionFalse,
169+
LastTransitionTime: metav1.Time{Time: now},
170+
Reason: ConditionUpgradeable,
171+
},
172+
{
173+
Type: ConditionProgressing,
174+
Status: metav1.ConditionFalse,
175+
LastTransitionTime: metav1.Time{Time: now},
176+
Reason: ConditionProgressing,
177+
},
178+
{
179+
Type: ConditionDegraded,
180+
Status: metav1.ConditionFalse,
181+
LastTransitionTime: metav1.Time{Time: now},
182+
Reason: ConditionDegraded,
183+
},
184+
{
185+
Type: ConditionDedicatedInformerActive,
186+
Status: metav1.ConditionUnknown,
187+
LastTransitionTime: metav1.Time{Time: now},
188+
Reason: ConditionDedicatedInformerActive,
189+
},
190+
}
191+
}
192+
148193
func ReasonFromError(err error) string {
149194
if err == nil {
150195
return ReasonAsExpected
@@ -162,3 +207,44 @@ func MessageFromError(err error) string {
162207
}
163208
return unwErr.Error()
164209
}
210+
211+
func resetIncomparableConditionFields(conditions []metav1.Condition) {
212+
for idx := range conditions {
213+
conditions[idx].LastTransitionTime = metav1.Time{}
214+
conditions[idx].ObservedGeneration = 0
215+
}
216+
}
217+
218+
func CloneConditions(conditions []metav1.Condition) []metav1.Condition {
219+
var c = make([]metav1.Condition, len(conditions))
220+
copy(c, conditions)
221+
return c
222+
}
223+
224+
func GetUpdatedSchedulerConditions(conditions []metav1.Condition, condition metav1.Condition) []metav1.Condition {
225+
updatedConditions := NewNUMAResourcesSchedulerBaseConditions()
226+
if len(conditions) != 0 {
227+
updatedConditions = CloneConditions(conditions)
228+
}
229+
230+
now := time.Now()
231+
for idx, cond := range updatedConditions {
232+
if cond.Type == condition.Type {
233+
updatedConditions[idx] = condition
234+
updatedConditions[idx].LastTransitionTime = metav1.Time{Time: now}
235+
break
236+
}
237+
}
238+
239+
if condition.Type == ConditionAvailable {
240+
for idx, cond := range updatedConditions {
241+
if cond.Type == ConditionUpgradeable {
242+
updatedConditions[idx].Status = condition.Status
243+
updatedConditions[idx].LastTransitionTime = metav1.Time{Time: now}
244+
break
245+
}
246+
}
247+
}
248+
249+
return updatedConditions
250+
}

0 commit comments

Comments
 (0)