Skip to content

Commit 0747c87

Browse files
committed
VPA updater: fix unready DaemonSet updates
Signed-off-by: Luke Addison <lukeaddison785@gmail.com>
1 parent 5e07dd5 commit 0747c87

2 files changed

Lines changed: 57 additions & 4 deletions

File tree

vertical-pod-autoscaler/pkg/updater/restriction/pods_restriction_factory.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,15 @@ func (f *PodsRestrictionFactoryImpl) getReplicaCount(creator podReplicaCreator)
148148
if !ok {
149149
return 0, errors.New("failed to parse DaemonSet")
150150
}
151-
if ds.Status.NumberReady == 0 {
152-
return 0, fmt.Errorf("daemon set %s/%s has no number ready pods", creator.Namespace, creator.Name)
151+
// DaemonSets have no replicas config, so we use the number of nodes the daemon
152+
// pod is expected to run on. This must not be derived from readiness (e.g.
153+
// Status.NumberReady): a DaemonSet whose pods are all unready (for example
154+
// crash looping because the recommendation is too low) would otherwise be
155+
// skipped here and could never be updated to recover.
156+
if ds.Status.DesiredNumberScheduled == 0 {
157+
return 0, fmt.Errorf("daemon set %s/%s has no desired scheduled pods", creator.Namespace, creator.Name)
153158
}
154-
return int(ds.Status.NumberReady), nil
159+
return int(ds.Status.DesiredNumberScheduled), nil
155160
}
156161
return 0, nil
157162
}

vertical-pod-autoscaler/pkg/updater/restriction/pods_restriction_factory_test.go

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -650,7 +650,8 @@ func TestEvictReplicatedByDaemonSet(t *testing.T) {
650650
Kind: "DaemonSet",
651651
},
652652
Status: appsv1.DaemonSetStatus{
653-
NumberReady: livePods,
653+
DesiredNumberScheduled: livePods,
654+
NumberReady: livePods,
654655
},
655656
}
656657

@@ -680,6 +681,53 @@ func TestEvictReplicatedByDaemonSet(t *testing.T) {
680681
}
681682
}
682683

684+
func TestEvictReplicatedByDaemonSetWithNoReadyPods(t *testing.T) {
685+
// A DaemonSet whose pods are all unready (e.g. crash looping because the applied
686+
// recommendation is too low) must still be updatable, otherwise it can never
687+
// recover.
688+
livePods := int32(5)
689+
690+
ds := appsv1.DaemonSet{
691+
ObjectMeta: metav1.ObjectMeta{
692+
Name: "ds",
693+
Namespace: "default",
694+
},
695+
TypeMeta: metav1.TypeMeta{
696+
Kind: "DaemonSet",
697+
},
698+
Status: appsv1.DaemonSetStatus{
699+
DesiredNumberScheduled: livePods,
700+
NumberReady: 0,
701+
},
702+
}
703+
704+
pods := make([]*corev1.Pod, livePods)
705+
for i := range pods {
706+
pods[i] = test.Pod().WithName(getTestPodName(i)).WithCreator(&ds.ObjectMeta, &ds.TypeMeta).Get()
707+
}
708+
709+
basicVpa := getBasicVpa()
710+
factory, err := getRestrictionFactory(nil, nil, nil, &ds, 2, 0.5, nil, nil, nil, false)
711+
assert.NoError(t, err)
712+
creatorToSingleGroupStatsMap, podToReplicaCreatorMap, err := factory.GetCreatorMaps(pods, basicVpa)
713+
assert.NoError(t, err)
714+
assert.Len(t, creatorToSingleGroupStatsMap, 1, "DaemonSet should not be skipped when it has no ready pods")
715+
eviction := factory.NewPodsEvictionRestriction(creatorToSingleGroupStatsMap, podToReplicaCreatorMap)
716+
717+
for _, pod := range pods {
718+
assert.True(t, eviction.CanEvict(pod))
719+
}
720+
721+
for _, pod := range pods[:2] {
722+
err := eviction.Evict(pod, basicVpa, test.FakeEventRecorder())
723+
assert.Nil(t, err, "Should evict with no error")
724+
}
725+
for _, pod := range pods[2:] {
726+
err := eviction.Evict(pod, basicVpa, test.FakeEventRecorder())
727+
assert.Error(t, err, "Error expected")
728+
}
729+
}
730+
683731
func TestEvictReplicatedByJob(t *testing.T) {
684732
job := batchv1.Job{
685733
ObjectMeta: metav1.ObjectMeta{

0 commit comments

Comments
 (0)