pkg/canary/daemonset: fix ready condition according to kubectl

This commit is contained in:
mathetake
2020-03-27 19:31:12 +09:00
parent 70475d475b
commit 4242bf0f07
3 changed files with 70 additions and 40 deletions

View File

@@ -44,21 +44,31 @@ func (c *DaemonSetController) IsCanaryReady(cd *flaggerv1.Canary) (bool, error)
}
// isDaemonSetReady determines if a daemonset is ready by checking the number of old version daemons
// reference: https://github.com/kubernetes/kubernetes/blob/5232ad4a00ec93942d0b2c6359ee6cd1201b46bc/pkg/kubectl/rollout_status.go#L110
func (c *DaemonSetController) isDaemonSetReady(cd *flaggerv1.Canary, daemonSet *appsv1.DaemonSet) (bool, error) {
if diff := daemonSet.Status.DesiredNumberScheduled - daemonSet.Status.UpdatedNumberScheduled; diff > 0 || daemonSet.Status.NumberUnavailable > 0 {
if daemonSet.Generation <= daemonSet.Status.ObservedGeneration {
// calculate conditions
newCond := daemonSet.Status.UpdatedNumberScheduled < daemonSet.Status.DesiredNumberScheduled
availableCond := daemonSet.Status.NumberAvailable < daemonSet.Status.DesiredNumberScheduled
if !newCond && !availableCond {
return true, nil
}
// check if deadline exceeded
from := cd.Status.LastTransitionTime
delta := time.Duration(cd.GetProgressDeadlineSeconds()) * time.Second
dl := from.Add(delta)
if dl.Before(time.Now()) {
if from.Add(delta).Before(time.Now()) {
return false, fmt.Errorf("exceeded its progressDeadlineSeconds: %d", cd.GetProgressDeadlineSeconds())
} else {
return true, fmt.Errorf(
"waiting for rollout to finish: desiredNumberScheduled=%d, updatedNumberScheduled=%d, numberUnavailable=%d",
daemonSet.Status.DesiredNumberScheduled,
daemonSet.Status.UpdatedNumberScheduled,
daemonSet.Status.NumberUnavailable,
)
}
// retryable
if newCond {
return true, fmt.Errorf("waiting for rollout to finish: %d out of %d new pods have been updated",
daemonSet.Status.UpdatedNumberScheduled, daemonSet.Status.DesiredNumberScheduled)
} else if availableCond {
return true, fmt.Errorf("waiting for rollout to finish: %d of %d updated pods are available",
daemonSet.Status.NumberAvailable, daemonSet.Status.DesiredNumberScheduled)
}
}
return true, nil
return true, fmt.Errorf("waiting for rollout to finish: observed daemonset generation less then desired generation")
}

View File

@@ -1,6 +1,7 @@
package canary
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
@@ -24,39 +25,57 @@ func TestDaemonSetController_IsReady(t *testing.T) {
}
func TestDaemonSetController_isDaemonSetReady(t *testing.T) {
ds := &appsv1.DaemonSet{
Status: appsv1.DaemonSetStatus{
DesiredNumberScheduled: 1,
UpdatedNumberScheduled: 1,
},
}
cd := &flaggerv1.Canary{}
cd.Spec.ProgressDeadlineSeconds = int32p(1e5)
cd.Status.LastTransitionTime = metav1.Now()
// ready
mocks := newDaemonSetFixture()
_, err := mocks.controller.isDaemonSetReady(cd, ds)
cd := &flaggerv1.Canary{}
// observed generation is less than desired generation
ds := &appsv1.DaemonSet{Status: appsv1.DaemonSetStatus{}}
ds.Status.ObservedGeneration--
retyable, err := mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.True(t, retyable)
// succeeded
ds = &appsv1.DaemonSet{Status: appsv1.DaemonSetStatus{
UpdatedNumberScheduled: 1,
DesiredNumberScheduled: 1,
NumberAvailable: 1,
}}
retyable, err = mocks.controller.isDaemonSetReady(cd, ds)
require.NoError(t, err)
require.True(t, retyable)
// not ready but retriable
ds.Status.NumberUnavailable++
retrieable, err := mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.True(t, retrieable)
ds.Status.NumberUnavailable--
ds.Status.DesiredNumberScheduled++
retrieable, err = mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.True(t, retrieable)
// not ready and not retriable
// deadline exceeded
ds = &appsv1.DaemonSet{Status: appsv1.DaemonSetStatus{
UpdatedNumberScheduled: 0,
DesiredNumberScheduled: 1,
}}
cd.Status.LastTransitionTime = metav1.Now()
cd.Spec.ProgressDeadlineSeconds = int32p(-1e5)
retrieable, err = mocks.controller.isDaemonSetReady(cd, ds)
cd.Spec.ProgressDeadlineSeconds = int32p(-1e6)
retyable, err = mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.False(t, retrieable)
require.False(t, retyable)
// only newCond not satisfied
ds = &appsv1.DaemonSet{Status: appsv1.DaemonSetStatus{
UpdatedNumberScheduled: 0,
DesiredNumberScheduled: 1,
NumberAvailable: 1,
}}
cd.Spec.ProgressDeadlineSeconds = int32p(1e6)
retyable, err = mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.True(t, retyable)
require.True(t, strings.Contains(err.Error(), "new pods"))
// only availableCond not satisfied
ds = &appsv1.DaemonSet{Status: appsv1.DaemonSetStatus{
UpdatedNumberScheduled: 1,
DesiredNumberScheduled: 1,
NumberAvailable: 0,
}}
retyable, err = mocks.controller.isDaemonSetReady(cd, ds)
require.Error(t, err)
require.True(t, retyable)
require.True(t, strings.Contains(err.Error(), "available"))
}