mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 14:07:14 +00:00
Promote DS MaxSurge to GA
This commit is contained in:
parent
5108b0a3a0
commit
7397c029e8
@ -28,11 +28,9 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
"k8s.io/apimachinery/pkg/util/validation"
|
"k8s.io/apimachinery/pkg/util/validation"
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
|
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
|
||||||
@ -373,7 +371,7 @@ func ValidateDaemonSetSpec(spec *apps.DaemonSetSpec, fldPath *field.Path, opts a
|
|||||||
// ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
|
// ValidateRollingUpdateDaemonSet validates a given RollingUpdateDaemonSet.
|
||||||
func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
|
func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet, fldPath *field.Path) field.ErrorList {
|
||||||
var allErrs field.ErrorList
|
var allErrs field.ErrorList
|
||||||
if utilfeature.DefaultFeatureGate.Enabled(features.DaemonSetUpdateSurge) {
|
|
||||||
// Validate both fields are positive ints or have a percentage value
|
// Validate both fields are positive ints or have a percentage value
|
||||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
||||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
|
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxSurge, fldPath.Child("maxSurge"))...)
|
||||||
@ -392,15 +390,6 @@ func ValidateRollingUpdateDaemonSet(rollingUpdate *apps.RollingUpdateDaemonSet,
|
|||||||
allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
|
allErrs = append(allErrs, field.Required(fldPath.Child("maxUnavailable"), "cannot be 0 when maxSurge is 0"))
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
|
||||||
allErrs = append(allErrs, ValidatePositiveIntOrPercent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
|
||||||
if getIntOrPercentValue(rollingUpdate.MaxUnavailable) == 0 {
|
|
||||||
// MaxUnavailable cannot be 0.
|
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("maxUnavailable"), rollingUpdate.MaxUnavailable, "cannot be 0"))
|
|
||||||
}
|
|
||||||
// Validate that MaxUnavailable is not more than 100%.
|
|
||||||
allErrs = append(allErrs, IsNotMoreThan100Percent(rollingUpdate.MaxUnavailable, fldPath.Child("maxUnavailable"))...)
|
|
||||||
}
|
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3591,7 +3591,6 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
||||||
testCases := map[string]struct {
|
testCases := map[string]struct {
|
||||||
ds *apps.RollingUpdateDaemonSet
|
ds *apps.RollingUpdateDaemonSet
|
||||||
enableSurge bool
|
|
||||||
expectError bool
|
expectError bool
|
||||||
}{
|
}{
|
||||||
"invalid: unset": {
|
"invalid: unset": {
|
||||||
@ -3637,45 +3636,40 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
MaxUnavailable: intstr.FromString("1%"),
|
MaxUnavailable: intstr.FromString("1%"),
|
||||||
MaxSurge: intstr.FromString("1%"),
|
MaxSurge: intstr.FromString("1%"),
|
||||||
},
|
},
|
||||||
|
expectError: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
"invalid: surge enabled, unavailable zero percent": {
|
"invalid: surge enabled, unavailable zero percent": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromString("0%"),
|
MaxUnavailable: intstr.FromString("0%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, unavailable zero": {
|
"invalid: surge enabled, unavailable zero": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromInt(0),
|
MaxUnavailable: intstr.FromInt(0),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"valid: surge enabled, unavailable one": {
|
"valid: surge enabled, unavailable one": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromInt(1),
|
MaxUnavailable: intstr.FromInt(1),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"valid: surge enabled, unavailable one percent": {
|
"valid: surge enabled, unavailable one percent": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromString("1%"),
|
MaxUnavailable: intstr.FromString("1%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"valid: surge enabled, unavailable 100%": {
|
"valid: surge enabled, unavailable 100%": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromString("100%"),
|
MaxUnavailable: intstr.FromString("100%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, unavailable greater than 100%": {
|
"invalid: surge enabled, unavailable greater than 100%": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxUnavailable: intstr.FromString("101%"),
|
MaxUnavailable: intstr.FromString("101%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -3683,39 +3677,33 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromString("0%"),
|
MaxSurge: intstr.FromString("0%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, surge zero": {
|
"invalid: surge enabled, surge zero": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromInt(0),
|
MaxSurge: intstr.FromInt(0),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"valid: surge enabled, surge one": {
|
"valid: surge enabled, surge one": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromInt(1),
|
MaxSurge: intstr.FromInt(1),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"valid: surge enabled, surge one percent": {
|
"valid: surge enabled, surge one percent": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromString("1%"),
|
MaxSurge: intstr.FromString("1%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"valid: surge enabled, surge 100%": {
|
"valid: surge enabled, surge 100%": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromString("100%"),
|
MaxSurge: intstr.FromString("100%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, surge greater than 100%": {
|
"invalid: surge enabled, surge greater than 100%": {
|
||||||
ds: &apps.RollingUpdateDaemonSet{
|
ds: &apps.RollingUpdateDaemonSet{
|
||||||
MaxSurge: intstr.FromString("101%"),
|
MaxSurge: intstr.FromString("101%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -3724,7 +3712,6 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
MaxUnavailable: intstr.FromString("1%"),
|
MaxUnavailable: intstr.FromString("1%"),
|
||||||
MaxSurge: intstr.FromString("1%"),
|
MaxSurge: intstr.FromString("1%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -3733,7 +3720,6 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
MaxUnavailable: intstr.FromString("0%"),
|
MaxUnavailable: intstr.FromString("0%"),
|
||||||
MaxSurge: intstr.FromString("0%"),
|
MaxSurge: intstr.FromString("0%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, surge and unavailable zero": {
|
"invalid: surge enabled, surge and unavailable zero": {
|
||||||
@ -3741,7 +3727,6 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
MaxUnavailable: intstr.FromInt(0),
|
MaxUnavailable: intstr.FromInt(0),
|
||||||
MaxSurge: intstr.FromInt(0),
|
MaxSurge: intstr.FromInt(0),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
"invalid: surge enabled, surge and unavailable mixed zero": {
|
"invalid: surge enabled, surge and unavailable mixed zero": {
|
||||||
@ -3749,13 +3734,11 @@ func TestDaemonSetUpdateMaxSurge(t *testing.T) {
|
|||||||
MaxUnavailable: intstr.FromInt(0),
|
MaxUnavailable: intstr.FromInt(0),
|
||||||
MaxSurge: intstr.FromString("0%"),
|
MaxSurge: intstr.FromString("0%"),
|
||||||
},
|
},
|
||||||
enableSurge: true,
|
|
||||||
expectError: true,
|
expectError: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for tcName, tc := range testCases {
|
for tcName, tc := range testCases {
|
||||||
t.Run(tcName, func(t *testing.T) {
|
t.Run(tcName, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, tc.enableSurge)()
|
|
||||||
errs := ValidateRollingUpdateDaemonSet(tc.ds, field.NewPath("spec", "updateStrategy", "rollingUpdate"))
|
errs := ValidateRollingUpdateDaemonSet(tc.ds, field.NewPath("spec", "updateStrategy", "rollingUpdate"))
|
||||||
if tc.expectError && len(errs) == 0 {
|
if tc.expectError && len(errs) == 0 {
|
||||||
t.Errorf("Unexpected success")
|
t.Errorf("Unexpected success")
|
||||||
|
@ -36,7 +36,6 @@ import (
|
|||||||
"k8s.io/apimachinery/pkg/util/uuid"
|
"k8s.io/apimachinery/pkg/util/uuid"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
"k8s.io/apiserver/pkg/storage/names"
|
"k8s.io/apiserver/pkg/storage/names"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
"k8s.io/client-go/kubernetes/fake"
|
"k8s.io/client-go/kubernetes/fake"
|
||||||
core "k8s.io/client-go/testing"
|
core "k8s.io/client-go/testing"
|
||||||
@ -44,14 +43,12 @@ import (
|
|||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
"k8s.io/client-go/util/flowcontrol"
|
"k8s.io/client-go/util/flowcontrol"
|
||||||
"k8s.io/client-go/util/workqueue"
|
"k8s.io/client-go/util/workqueue"
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/apis/scheduling"
|
"k8s.io/kubernetes/pkg/apis/scheduling"
|
||||||
"k8s.io/kubernetes/pkg/controller"
|
"k8s.io/kubernetes/pkg/controller"
|
||||||
"k8s.io/kubernetes/pkg/controller/daemon/util"
|
"k8s.io/kubernetes/pkg/controller/daemon/util"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"k8s.io/kubernetes/pkg/securitycontext"
|
"k8s.io/kubernetes/pkg/securitycontext"
|
||||||
labelsutil "k8s.io/kubernetes/pkg/util/labels"
|
labelsutil "k8s.io/kubernetes/pkg/util/labels"
|
||||||
testingclock "k8s.io/utils/clock/testing"
|
testingclock "k8s.io/utils/clock/testing"
|
||||||
@ -453,9 +450,6 @@ func clearExpectations(t *testing.T, manager *daemonSetsController, ds *apps.Dae
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDeleteFinalStateUnknown(t *testing.T) {
|
func TestDeleteFinalStateUnknown(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -472,7 +466,6 @@ func TestDeleteFinalStateUnknown(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestExpectationsOnRecreate(t *testing.T) {
|
func TestExpectationsOnRecreate(t *testing.T) {
|
||||||
client := fake.NewSimpleClientset()
|
client := fake.NewSimpleClientset()
|
||||||
@ -679,9 +672,6 @@ func markPodReady(pod *v1.Pod) {
|
|||||||
|
|
||||||
// DaemonSets without node selectors should launch pods on every node.
|
// DaemonSets without node selectors should launch pods on every node.
|
||||||
func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
|
func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -698,14 +688,10 @@ func TestSimpleDaemonSetLaunchesPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 5, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 5, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets without node selectors should launch pods on every node by NodeAffinity.
|
// DaemonSets without node selectors should launch pods on every node by NodeAffinity.
|
||||||
func TestSimpleDaemonSetScheduleDaemonSetPodsLaunchesPods(t *testing.T) {
|
func TestSimpleDaemonSetScheduleDaemonSetPodsLaunchesPods(t *testing.T) {
|
||||||
nodeNum := 5
|
nodeNum := 5
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -778,14 +764,10 @@ func TestSimpleDaemonSetScheduleDaemonSetPodsLaunchesPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Simulate a cluster with 100 nodes, but simulate a limit (like a quota limit)
|
// Simulate a cluster with 100 nodes, but simulate a limit (like a quota limit)
|
||||||
// of 10 pods, and verify that the ds doesn't make 100 create calls per sync pass
|
// of 10 pods, and verify that the ds doesn't make 100 create calls per sync pass
|
||||||
func TestSimpleDaemonSetPodCreateErrors(t *testing.T) {
|
func TestSimpleDaemonSetPodCreateErrors(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -811,12 +793,8 @@ func TestSimpleDaemonSetPodCreateErrors(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestDaemonSetPodCreateExpectationsError(t *testing.T) {
|
func TestDaemonSetPodCreateExpectationsError(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
strategies := updateStrategies()
|
strategies := updateStrategies()
|
||||||
for _, strategy := range strategies {
|
for _, strategy := range strategies {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
@ -845,12 +823,8 @@ func TestDaemonSetPodCreateExpectationsError(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleDaemonSetUpdatesStatusAfterLaunchingPods(t *testing.T) {
|
func TestSimpleDaemonSetUpdatesStatusAfterLaunchingPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -881,13 +855,9 @@ func TestSimpleDaemonSetUpdatesStatusAfterLaunchingPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should do nothing if there aren't any nodes
|
// DaemonSets should do nothing if there aren't any nodes
|
||||||
func TestNoNodesDoesNothing(t *testing.T) {
|
func TestNoNodesDoesNothing(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, podControl, _, err := newTestController()
|
manager, podControl, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -903,14 +873,10 @@ func TestNoNodesDoesNothing(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets without node selectors should launch on a single node in a
|
// DaemonSets without node selectors should launch on a single node in a
|
||||||
// single node cluster.
|
// single node cluster.
|
||||||
func TestOneNodeDaemonLaunchesPod(t *testing.T) {
|
func TestOneNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -930,13 +896,9 @@ func TestOneNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should place onto NotReady nodes
|
// DaemonSets should place onto NotReady nodes
|
||||||
func TestNotReadyNodeDaemonDoesLaunchPod(t *testing.T) {
|
func TestNotReadyNodeDaemonDoesLaunchPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -961,7 +923,6 @@ func TestNotReadyNodeDaemonDoesLaunchPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func resourcePodSpec(nodeName, memory, cpu string) v1.PodSpec {
|
func resourcePodSpec(nodeName, memory, cpu string) v1.PodSpec {
|
||||||
return v1.PodSpec{
|
return v1.PodSpec{
|
||||||
@ -1000,9 +961,6 @@ func allocatableResources(memory, cpu string) v1.ResourceList {
|
|||||||
|
|
||||||
// DaemonSets should not unschedule a daemonset pod from a node with insufficient free resource
|
// DaemonSets should not unschedule a daemonset pod from a node with insufficient free resource
|
||||||
func TestInsufficientCapacityNodeDaemonDoesNotUnscheduleRunningPod(t *testing.T) {
|
func TestInsufficientCapacityNodeDaemonDoesNotUnscheduleRunningPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
podSpec := resourcePodSpec("too-much-mem", "75M", "75m")
|
podSpec := resourcePodSpec("too-much-mem", "75M", "75m")
|
||||||
podSpec.NodeName = "too-much-mem"
|
podSpec.NodeName = "too-much-mem"
|
||||||
@ -1039,7 +997,6 @@ func TestInsufficientCapacityNodeDaemonDoesNotUnscheduleRunningPod(t *testing.T)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should only place onto nodes with sufficient free resource and matched node selector
|
// DaemonSets should only place onto nodes with sufficient free resource and matched node selector
|
||||||
func TestInsufficientCapacityNodeSufficientCapacityWithNodeLabelDaemonLaunchPod(t *testing.T) {
|
func TestInsufficientCapacityNodeSufficientCapacityWithNodeLabelDaemonLaunchPod(t *testing.T) {
|
||||||
@ -1076,9 +1033,6 @@ func TestInsufficientCapacityNodeSufficientCapacityWithNodeLabelDaemonLaunchPod(
|
|||||||
|
|
||||||
// DaemonSet should launch a pod on a node with taint NetworkUnavailable condition.
|
// DaemonSet should launch a pod on a node with taint NetworkUnavailable condition.
|
||||||
func TestNetworkUnavailableNodeDaemonLaunchesPod(t *testing.T) {
|
func TestNetworkUnavailableNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("simple")
|
ds := newDaemonSet("simple")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1103,13 +1057,9 @@ func TestNetworkUnavailableNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets not take any actions when being deleted
|
// DaemonSets not take any actions when being deleted
|
||||||
func TestDontDoAnythingIfBeingDeleted(t *testing.T) {
|
func TestDontDoAnythingIfBeingDeleted(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m")
|
podSpec := resourcePodSpec("not-too-much-mem", "75M", "75m")
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
@ -1140,12 +1090,8 @@ func TestDontDoAnythingIfBeingDeleted(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestDontDoAnythingIfBeingDeletedRace(t *testing.T) {
|
func TestDontDoAnythingIfBeingDeletedRace(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
// Bare client says it IS deleted.
|
// Bare client says it IS deleted.
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
@ -1176,16 +1122,12 @@ func TestDontDoAnythingIfBeingDeletedRace(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Test that if the node is already scheduled with a pod using a host port
|
// Test that if the node is already scheduled with a pod using a host port
|
||||||
// but belonging to the same daemonset, we don't delete that pod
|
// but belonging to the same daemonset, we don't delete that pod
|
||||||
//
|
//
|
||||||
// Issue: https://github.com/kubernetes/kubernetes/issues/22309
|
// Issue: https://github.com/kubernetes/kubernetes/issues/22309
|
||||||
func TestPortConflictWithSameDaemonPodDoesNotDeletePod(t *testing.T) {
|
func TestPortConflictWithSameDaemonPodDoesNotDeletePod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
podSpec := v1.PodSpec{
|
podSpec := v1.PodSpec{
|
||||||
NodeName: "port-conflict",
|
NodeName: "port-conflict",
|
||||||
@ -1219,13 +1161,9 @@ func TestPortConflictWithSameDaemonPodDoesNotDeletePod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should place onto nodes that would not cause port conflicts
|
// DaemonSets should place onto nodes that would not cause port conflicts
|
||||||
func TestNoPortConflictNodeDaemonLaunchesPod(t *testing.T) {
|
func TestNoPortConflictNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
podSpec1 := v1.PodSpec{
|
podSpec1 := v1.PodSpec{
|
||||||
NodeName: "no-port-conflict",
|
NodeName: "no-port-conflict",
|
||||||
@ -1268,7 +1206,6 @@ func TestNoPortConflictNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSetController should not sync DaemonSets with empty pod selectors.
|
// DaemonSetController should not sync DaemonSets with empty pod selectors.
|
||||||
//
|
//
|
||||||
@ -1283,9 +1220,6 @@ func TestPodIsNotDeletedByDaemonsetWithEmptyLabelSelector(t *testing.T) {
|
|||||||
// this case even though it's empty pod selector matches all pods. The DaemonSetController
|
// this case even though it's empty pod selector matches all pods. The DaemonSetController
|
||||||
// should detect this misconfiguration and choose not to sync the DaemonSet. We should
|
// should detect this misconfiguration and choose not to sync the DaemonSet. We should
|
||||||
// not observe a deletion of the pod on node1.
|
// not observe a deletion of the pod on node1.
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1322,13 +1256,9 @@ func TestPodIsNotDeletedByDaemonsetWithEmptyLabelSelector(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 1)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Controller should not create pods on nodes which have daemon pods, and should remove excess pods from nodes that have extra pods.
|
// Controller should not create pods on nodes which have daemon pods, and should remove excess pods from nodes that have extra pods.
|
||||||
func TestDealsWithExistingPods(t *testing.T) {
|
func TestDealsWithExistingPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1348,13 +1278,9 @@ func TestDealsWithExistingPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 2, 5, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 2, 5, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Daemon with node selector should launch pods on nodes matching selector.
|
// Daemon with node selector should launch pods on nodes matching selector.
|
||||||
func TestSelectorDaemonLaunchesPods(t *testing.T) {
|
func TestSelectorDaemonLaunchesPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
daemon := newDaemonSet("foo")
|
daemon := newDaemonSet("foo")
|
||||||
daemon.Spec.UpdateStrategy = *strategy
|
daemon.Spec.UpdateStrategy = *strategy
|
||||||
@ -1372,13 +1298,9 @@ func TestSelectorDaemonLaunchesPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, daemon, podControl, 3, 0, 0)
|
expectSyncDaemonSets(t, manager, daemon, podControl, 3, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Daemon with node selector should delete pods from nodes that do not satisfy selector.
|
// Daemon with node selector should delete pods from nodes that do not satisfy selector.
|
||||||
func TestSelectorDaemonDeletesUnselectedPods(t *testing.T) {
|
func TestSelectorDaemonDeletesUnselectedPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1400,13 +1322,9 @@ func TestSelectorDaemonDeletesUnselectedPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 5, 4, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 5, 4, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node selector should launch pods on nodes matching selector, but also deal with existing pods on nodes.
|
// DaemonSet with node selector should launch pods on nodes matching selector, but also deal with existing pods on nodes.
|
||||||
func TestSelectorDaemonDealsWithExistingPods(t *testing.T) {
|
func TestSelectorDaemonDealsWithExistingPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1432,13 +1350,9 @@ func TestSelectorDaemonDealsWithExistingPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 3, 20, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 3, 20, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node selector which does not match any node labels should not launch pods.
|
// DaemonSet with node selector which does not match any node labels should not launch pods.
|
||||||
func TestBadSelectorDaemonDoesNothing(t *testing.T) {
|
func TestBadSelectorDaemonDoesNothing(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, podControl, _, err := newTestController()
|
manager, podControl, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1456,13 +1370,9 @@ func TestBadSelectorDaemonDoesNothing(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node name should launch pod on node with corresponding name.
|
// DaemonSet with node name should launch pod on node with corresponding name.
|
||||||
func TestNameDaemonSetLaunchesPods(t *testing.T) {
|
func TestNameDaemonSetLaunchesPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1479,13 +1389,9 @@ func TestNameDaemonSetLaunchesPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node name that does not exist should not launch pods.
|
// DaemonSet with node name that does not exist should not launch pods.
|
||||||
func TestBadNameDaemonSetDoesNothing(t *testing.T) {
|
func TestBadNameDaemonSetDoesNothing(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1502,13 +1408,9 @@ func TestBadNameDaemonSetDoesNothing(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node selector, and node name, matching a node, should launch a pod on the node.
|
// DaemonSet with node selector, and node name, matching a node, should launch a pod on the node.
|
||||||
func TestNameAndSelectorDaemonSetLaunchesPods(t *testing.T) {
|
func TestNameAndSelectorDaemonSetLaunchesPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1527,13 +1429,9 @@ func TestNameAndSelectorDaemonSetLaunchesPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node selector that matches some nodes, and node name that matches a different node, should do nothing.
|
// DaemonSet with node selector that matches some nodes, and node name that matches a different node, should do nothing.
|
||||||
func TestInconsistentNameSelectorDaemonSetDoesNothing(t *testing.T) {
|
func TestInconsistentNameSelectorDaemonSetDoesNothing(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1552,7 +1450,6 @@ func TestInconsistentNameSelectorDaemonSetDoesNothing(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet with node selector, matching some nodes, should launch pods on all the nodes.
|
// DaemonSet with node selector, matching some nodes, should launch pods on all the nodes.
|
||||||
func TestSelectorDaemonSetLaunchesPods(t *testing.T) {
|
func TestSelectorDaemonSetLaunchesPods(t *testing.T) {
|
||||||
@ -1573,9 +1470,6 @@ func TestSelectorDaemonSetLaunchesPods(t *testing.T) {
|
|||||||
|
|
||||||
// Daemon with node affinity should launch pods on nodes matching affinity.
|
// Daemon with node affinity should launch pods on nodes matching affinity.
|
||||||
func TestNodeAffinityDaemonLaunchesPods(t *testing.T) {
|
func TestNodeAffinityDaemonLaunchesPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
daemon := newDaemonSet("foo")
|
daemon := newDaemonSet("foo")
|
||||||
daemon.Spec.UpdateStrategy = *strategy
|
daemon.Spec.UpdateStrategy = *strategy
|
||||||
@ -1610,12 +1504,8 @@ func TestNodeAffinityDaemonLaunchesPods(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, daemon, podControl, 3, 0, 0)
|
expectSyncDaemonSets(t, manager, daemon, podControl, 3, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestNumberReadyStatus(t *testing.T) {
|
func TestNumberReadyStatus(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1659,12 +1549,8 @@ func TestNumberReadyStatus(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestObservedGeneration(t *testing.T) {
|
func TestObservedGeneration(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1697,7 +1583,6 @@ func TestObservedGeneration(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet controller should kill all failed pods and create at most 1 pod on every node.
|
// DaemonSet controller should kill all failed pods and create at most 1 pod on every node.
|
||||||
func TestDaemonKillFailedPods(t *testing.T) {
|
func TestDaemonKillFailedPods(t *testing.T) {
|
||||||
@ -1735,9 +1620,6 @@ func TestDaemonKillFailedPods(t *testing.T) {
|
|||||||
|
|
||||||
// DaemonSet controller needs to backoff when killing failed pods to avoid hot looping and fighting with kubelet.
|
// DaemonSet controller needs to backoff when killing failed pods to avoid hot looping and fighting with kubelet.
|
||||||
func TestDaemonKillFailedPodsBackoff(t *testing.T) {
|
func TestDaemonKillFailedPodsBackoff(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
t.Run(string(strategy.Type), func(t *testing.T) {
|
t.Run(string(strategy.Type), func(t *testing.T) {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
@ -1804,14 +1686,10 @@ func TestDaemonKillFailedPodsBackoff(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Daemonset should not remove a running pod from a node if the pod doesn't
|
// Daemonset should not remove a running pod from a node if the pod doesn't
|
||||||
// tolerate the nodes NoSchedule taint
|
// tolerate the nodes NoSchedule taint
|
||||||
func TestNoScheduleTaintedDoesntEvicitRunningIntolerantPod(t *testing.T) {
|
func TestNoScheduleTaintedDoesntEvicitRunningIntolerantPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("intolerant")
|
ds := newDaemonSet("intolerant")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1838,14 +1716,10 @@ func TestNoScheduleTaintedDoesntEvicitRunningIntolerantPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Daemonset should remove a running pod from a node if the pod doesn't
|
// Daemonset should remove a running pod from a node if the pod doesn't
|
||||||
// tolerate the nodes NoExecute taint
|
// tolerate the nodes NoExecute taint
|
||||||
func TestNoExecuteTaintedDoesEvicitRunningIntolerantPod(t *testing.T) {
|
func TestNoExecuteTaintedDoesEvicitRunningIntolerantPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("intolerant")
|
ds := newDaemonSet("intolerant")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1872,13 +1746,9 @@ func TestNoExecuteTaintedDoesEvicitRunningIntolerantPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 1, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 1, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should not launch a pod on a tainted node when the pod doesn't tolerate that taint.
|
// DaemonSet should not launch a pod on a tainted node when the pod doesn't tolerate that taint.
|
||||||
func TestTaintedNodeDaemonDoesNotLaunchIntolerantPod(t *testing.T) {
|
func TestTaintedNodeDaemonDoesNotLaunchIntolerantPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("intolerant")
|
ds := newDaemonSet("intolerant")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1901,13 +1771,9 @@ func TestTaintedNodeDaemonDoesNotLaunchIntolerantPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should launch a pod on a tainted node when the pod can tolerate that taint.
|
// DaemonSet should launch a pod on a tainted node when the pod can tolerate that taint.
|
||||||
func TestTaintedNodeDaemonLaunchesToleratePod(t *testing.T) {
|
func TestTaintedNodeDaemonLaunchesToleratePod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("tolerate")
|
ds := newDaemonSet("tolerate")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1931,13 +1797,9 @@ func TestTaintedNodeDaemonLaunchesToleratePod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should launch a pod on a not ready node with taint notReady:NoExecute.
|
// DaemonSet should launch a pod on a not ready node with taint notReady:NoExecute.
|
||||||
func TestNotReadyNodeDaemonLaunchesPod(t *testing.T) {
|
func TestNotReadyNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("simple")
|
ds := newDaemonSet("simple")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1963,13 +1825,9 @@ func TestNotReadyNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should launch a pod on an unreachable node with taint unreachable:NoExecute.
|
// DaemonSet should launch a pod on an unreachable node with taint unreachable:NoExecute.
|
||||||
func TestUnreachableNodeDaemonLaunchesPod(t *testing.T) {
|
func TestUnreachableNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("simple")
|
ds := newDaemonSet("simple")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -1995,13 +1853,9 @@ func TestUnreachableNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should launch a pod on an untainted node when the pod has tolerations.
|
// DaemonSet should launch a pod on an untainted node when the pod has tolerations.
|
||||||
func TestNodeDaemonLaunchesToleratePod(t *testing.T) {
|
func TestNodeDaemonLaunchesToleratePod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("tolerate")
|
ds := newDaemonSet("tolerate")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -2019,13 +1873,9 @@ func TestNodeDaemonLaunchesToleratePod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSet should launch a pod on a not ready node with taint notReady:NoExecute.
|
// DaemonSet should launch a pod on a not ready node with taint notReady:NoExecute.
|
||||||
func TestDaemonSetRespectsTermination(t *testing.T) {
|
func TestDaemonSetRespectsTermination(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -2049,7 +1899,6 @@ func TestDaemonSetRespectsTermination(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func setNodeTaint(node *v1.Node, taints []v1.Taint) {
|
func setNodeTaint(node *v1.Node, taints []v1.Taint) {
|
||||||
node.Spec.Taints = taints
|
node.Spec.Taints = taints
|
||||||
@ -2061,9 +1910,6 @@ func setDaemonSetToleration(ds *apps.DaemonSet, tolerations []v1.Toleration) {
|
|||||||
|
|
||||||
// DaemonSet should launch a pod even when the node with MemoryPressure/DiskPressure/PIDPressure taints.
|
// DaemonSet should launch a pod even when the node with MemoryPressure/DiskPressure/PIDPressure taints.
|
||||||
func TestTaintPressureNodeDaemonLaunchesPod(t *testing.T) {
|
func TestTaintPressureNodeDaemonLaunchesPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("critical")
|
ds := newDaemonSet("critical")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -2095,7 +1941,6 @@ func TestTaintPressureNodeDaemonLaunchesPod(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 1, 0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func setDaemonSetCritical(ds *apps.DaemonSet) {
|
func setDaemonSetCritical(ds *apps.DaemonSet) {
|
||||||
ds.Namespace = api.NamespaceSystem
|
ds.Namespace = api.NamespaceSystem
|
||||||
@ -2378,9 +2223,6 @@ func TestNodeShouldRunDaemonPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i, c := range cases {
|
for i, c := range cases {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
node := newNode("test-node", simpleDaemonSetLabel)
|
node := newNode("test-node", simpleDaemonSetLabel)
|
||||||
node.Status.Conditions = append(node.Status.Conditions, c.nodeCondition...)
|
node.Status.Conditions = append(node.Status.Conditions, c.nodeCondition...)
|
||||||
@ -2408,7 +2250,6 @@ func TestNodeShouldRunDaemonPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should be resynced when node labels or taints changed
|
// DaemonSets should be resynced when node labels or taints changed
|
||||||
func TestUpdateNode(t *testing.T) {
|
func TestUpdateNode(t *testing.T) {
|
||||||
@ -2489,9 +2330,6 @@ func TestUpdateNode(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, podControl, _, err := newTestController()
|
manager, podControl, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2531,7 +2369,6 @@ func TestUpdateNode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// DaemonSets should be resynced when non-daemon pods was deleted.
|
// DaemonSets should be resynced when non-daemon pods was deleted.
|
||||||
func TestDeleteNoDaemonPod(t *testing.T) {
|
func TestDeleteNoDaemonPod(t *testing.T) {
|
||||||
@ -2674,9 +2511,6 @@ func TestDeleteNoDaemonPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, podControl, _, err := newTestController()
|
manager, podControl, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2712,12 +2546,8 @@ func TestDeleteNoDaemonPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeleteUnscheduledPodForNotExistingNode(t *testing.T) {
|
func TestDeleteUnscheduledPodForNotExistingNode(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -2758,12 +2588,8 @@ func TestDeleteUnscheduledPodForNotExistingNode(t *testing.T) {
|
|||||||
expectSyncDaemonSets(t, manager, ds, podControl, 0, 1, 0)
|
expectSyncDaemonSets(t, manager, ds, podControl, 0, 1, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestGetNodesToDaemonPods(t *testing.T) {
|
func TestGetNodesToDaemonPods(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -2834,7 +2660,6 @@ func TestGetNodesToDaemonPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddNode(t *testing.T) {
|
func TestAddNode(t *testing.T) {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
@ -2866,9 +2691,6 @@ func TestAddNode(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestAddPod(t *testing.T) {
|
func TestAddPod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2916,12 +2738,8 @@ func TestAddPod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestAddPodOrphan(t *testing.T) {
|
func TestAddPodOrphan(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -2958,12 +2776,8 @@ func TestAddPodOrphan(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePod(t *testing.T) {
|
func TestUpdatePod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3015,12 +2829,8 @@ func TestUpdatePod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePodOrphanSameLabels(t *testing.T) {
|
func TestUpdatePodOrphanSameLabels(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3048,12 +2858,8 @@ func TestUpdatePodOrphanSameLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePodOrphanWithNewLabels(t *testing.T) {
|
func TestUpdatePodOrphanWithNewLabels(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3085,12 +2891,8 @@ func TestUpdatePodOrphanWithNewLabels(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePodChangeControllerRef(t *testing.T) {
|
func TestUpdatePodChangeControllerRef(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = *strategy
|
ds.Spec.UpdateStrategy = *strategy
|
||||||
@ -3119,12 +2921,8 @@ func TestUpdatePodChangeControllerRef(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestUpdatePodControllerRefRemoved(t *testing.T) {
|
func TestUpdatePodControllerRefRemoved(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3153,12 +2951,8 @@ func TestUpdatePodControllerRefRemoved(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeletePod(t *testing.T) {
|
func TestDeletePod(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3206,12 +3000,8 @@ func TestDeletePod(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func TestDeletePodOrphan(t *testing.T) {
|
func TestDeletePodOrphan(t *testing.T) {
|
||||||
dsMaxSurgeFeatureFlags := []bool{false, true}
|
|
||||||
for _, isEnabled := range dsMaxSurgeFeatureFlags {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, isEnabled)()
|
|
||||||
for _, strategy := range updateStrategies() {
|
for _, strategy := range updateStrategies() {
|
||||||
manager, _, _, err := newTestController()
|
manager, _, _, err := newTestController()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -3244,7 +3034,6 @@ func TestDeletePodOrphan(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func bumpResourceVersion(obj metav1.Object) {
|
func bumpResourceVersion(obj metav1.Object) {
|
||||||
ver, _ := strconv.ParseInt(obj.GetResourceVersion(), 10, 32)
|
ver, _ := strconv.ParseInt(obj.GetResourceVersion(), 10, 32)
|
||||||
@ -3285,7 +3074,6 @@ func TestSurgeDealsWithExistingPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSurgePreservesReadyOldPods(t *testing.T) {
|
func TestSurgePreservesReadyOldPods(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
||||||
manager, podControl, _, err := newTestController(ds)
|
manager, podControl, _, err := newTestController(ds)
|
||||||
@ -3325,7 +3113,6 @@ func TestSurgePreservesReadyOldPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSurgeCreatesNewPodWhenAtMaxSurgeAndOldPodDeleted(t *testing.T) {
|
func TestSurgeCreatesNewPodWhenAtMaxSurgeAndOldPodDeleted(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
||||||
manager, podControl, _, err := newTestController(ds)
|
manager, podControl, _, err := newTestController(ds)
|
||||||
@ -3372,7 +3159,6 @@ func TestSurgeCreatesNewPodWhenAtMaxSurgeAndOldPodDeleted(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSurgeDeletesUnreadyOldPods(t *testing.T) {
|
func TestSurgeDeletesUnreadyOldPods(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
||||||
manager, podControl, _, err := newTestController(ds)
|
manager, podControl, _, err := newTestController(ds)
|
||||||
@ -3412,7 +3198,6 @@ func TestSurgeDeletesUnreadyOldPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSurgePreservesOldReadyWithUnsatisfiedMinReady(t *testing.T) {
|
func TestSurgePreservesOldReadyWithUnsatisfiedMinReady(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.MinReadySeconds = 15
|
ds.Spec.MinReadySeconds = 15
|
||||||
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
||||||
@ -3457,7 +3242,6 @@ func TestSurgePreservesOldReadyWithUnsatisfiedMinReady(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestSurgeDeletesOldReadyWithUnsatisfiedMinReady(t *testing.T) {
|
func TestSurgeDeletesOldReadyWithUnsatisfiedMinReady(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
ds.Spec.MinReadySeconds = 15
|
ds.Spec.MinReadySeconds = 15
|
||||||
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
ds.Spec.UpdateStrategy = newUpdateSurge(intstr.FromInt(1))
|
||||||
|
@ -27,12 +27,9 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
"k8s.io/apimachinery/pkg/util/intstr"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
podutil "k8s.io/kubernetes/pkg/api/v1/pod"
|
||||||
"k8s.io/kubernetes/pkg/controller/daemon/util"
|
"k8s.io/kubernetes/pkg/controller/daemon/util"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
testingclock "k8s.io/utils/clock/testing"
|
testingclock "k8s.io/utils/clock/testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -78,7 +75,6 @@ func TestDaemonSetUpdatesPods(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDaemonSetUpdatesPodsWithMaxSurge(t *testing.T) {
|
func TestDaemonSetUpdatesPodsWithMaxSurge(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
manager, podControl, _, err := newTestController(ds)
|
manager, podControl, _, err := newTestController(ds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -191,7 +187,6 @@ func TestDaemonSetUpdatesAllOldPodsNotReady(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestDaemonSetUpdatesAllOldPodsNotReadyMaxSurge(t *testing.T) {
|
func TestDaemonSetUpdatesAllOldPodsNotReadyMaxSurge(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, true)()
|
|
||||||
ds := newDaemonSet("foo")
|
ds := newDaemonSet("foo")
|
||||||
manager, podControl, _, err := newTestController(ds)
|
manager, podControl, _, err := newTestController(ds)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -381,7 +376,6 @@ func TestGetUnavailableNumbers(t *testing.T) {
|
|||||||
Manager *daemonSetsController
|
Manager *daemonSetsController
|
||||||
ds *apps.DaemonSet
|
ds *apps.DaemonSet
|
||||||
nodeToPods map[string][]*v1.Pod
|
nodeToPods map[string][]*v1.Pod
|
||||||
enableSurge bool
|
|
||||||
maxSurge int
|
maxSurge int
|
||||||
maxUnavailable int
|
maxUnavailable int
|
||||||
emptyNodes int
|
emptyNodes int
|
||||||
@ -536,7 +530,6 @@ func TestGetUnavailableNumbers(t *testing.T) {
|
|||||||
mapping["node-1"] = []*v1.Pod{pod1}
|
mapping["node-1"] = []*v1.Pod{pod1}
|
||||||
return mapping
|
return mapping
|
||||||
}(),
|
}(),
|
||||||
enableSurge: true,
|
|
||||||
maxSurge: 1,
|
maxSurge: 1,
|
||||||
maxUnavailable: 0,
|
maxUnavailable: 0,
|
||||||
emptyNodes: 0,
|
emptyNodes: 0,
|
||||||
@ -566,7 +559,6 @@ func TestGetUnavailableNumbers(t *testing.T) {
|
|||||||
mapping["node-1"] = []*v1.Pod{pod1}
|
mapping["node-1"] = []*v1.Pod{pod1}
|
||||||
return mapping
|
return mapping
|
||||||
}(),
|
}(),
|
||||||
enableSurge: true,
|
|
||||||
maxSurge: 2,
|
maxSurge: 2,
|
||||||
maxUnavailable: 0,
|
maxUnavailable: 0,
|
||||||
emptyNodes: 0,
|
emptyNodes: 0,
|
||||||
@ -605,8 +597,6 @@ func TestGetUnavailableNumbers(t *testing.T) {
|
|||||||
|
|
||||||
for _, c := range cases {
|
for _, c := range cases {
|
||||||
t.Run(c.name, func(t *testing.T) {
|
t.Run(c.name, func(t *testing.T) {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, c.enableSurge)()
|
|
||||||
|
|
||||||
c.Manager.dsStore.Add(c.ds)
|
c.Manager.dsStore.Add(c.ds)
|
||||||
nodeList, err := c.Manager.nodeLister.List(labels.Everything())
|
nodeList, err := c.Manager.nodeLister.List(labels.Everything())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -25,9 +25,7 @@ import (
|
|||||||
extensions "k8s.io/api/extensions/v1beta1"
|
extensions "k8s.io/api/extensions/v1beta1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
|
intstrutil "k8s.io/apimachinery/pkg/util/intstr"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetTemplateGeneration gets the template generation associated with a v1.DaemonSet by extracting it from the
|
// GetTemplateGeneration gets the template generation associated with a v1.DaemonSet by extracting it from the
|
||||||
@ -137,9 +135,7 @@ func SurgeCount(ds *apps.DaemonSet, numberToSchedule int) (int, error) {
|
|||||||
if ds.Spec.UpdateStrategy.Type != apps.RollingUpdateDaemonSetStrategyType {
|
if ds.Spec.UpdateStrategy.Type != apps.RollingUpdateDaemonSetStrategyType {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.DaemonSetUpdateSurge) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
r := ds.Spec.UpdateStrategy.RollingUpdate
|
r := ds.Spec.UpdateStrategy.RollingUpdate
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
|
@ -215,6 +215,7 @@ const (
|
|||||||
// owner: @smarterclayton
|
// owner: @smarterclayton
|
||||||
// alpha: v1.21
|
// alpha: v1.21
|
||||||
// beta: v1.22
|
// beta: v1.22
|
||||||
|
// GA: v1.25
|
||||||
// DaemonSets allow workloads to maintain availability during update per node
|
// DaemonSets allow workloads to maintain availability during update per node
|
||||||
DaemonSetUpdateSurge featuregate.Feature = "DaemonSetUpdateSurge"
|
DaemonSetUpdateSurge featuregate.Feature = "DaemonSetUpdateSurge"
|
||||||
|
|
||||||
@ -860,7 +861,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
CronJobTimeZone: {Default: false, PreRelease: featuregate.Alpha},
|
CronJobTimeZone: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
DaemonSetUpdateSurge: {Default: true, PreRelease: featuregate.Beta}, // on by default in 1.22
|
DaemonSetUpdateSurge: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.27
|
||||||
|
|
||||||
DefaultPodTopologySpread: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
|
DefaultPodTopologySpread: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.26
|
||||||
|
|
||||||
|
@ -24,17 +24,14 @@ import (
|
|||||||
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
apivalidation "k8s.io/apimachinery/pkg/api/validation"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
"k8s.io/apiserver/pkg/registry/rest"
|
"k8s.io/apiserver/pkg/registry/rest"
|
||||||
"k8s.io/apiserver/pkg/storage/names"
|
"k8s.io/apiserver/pkg/storage/names"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
"k8s.io/kubernetes/pkg/api/legacyscheme"
|
||||||
"k8s.io/kubernetes/pkg/api/pod"
|
"k8s.io/kubernetes/pkg/api/pod"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
"k8s.io/kubernetes/pkg/apis/apps/validation"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -82,7 +79,6 @@ func (daemonSetStrategy) PrepareForCreate(ctx context.Context, obj runtime.Objec
|
|||||||
daemonSet.Spec.TemplateGeneration = 1
|
daemonSet.Spec.TemplateGeneration = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
dropDaemonSetDisabledFields(daemonSet, nil)
|
|
||||||
pod.DropDisabledTemplateFields(&daemonSet.Spec.Template, nil)
|
pod.DropDisabledTemplateFields(&daemonSet.Spec.Template, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +87,6 @@ func (daemonSetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
|
|||||||
newDaemonSet := obj.(*apps.DaemonSet)
|
newDaemonSet := obj.(*apps.DaemonSet)
|
||||||
oldDaemonSet := old.(*apps.DaemonSet)
|
oldDaemonSet := old.(*apps.DaemonSet)
|
||||||
|
|
||||||
dropDaemonSetDisabledFields(newDaemonSet, oldDaemonSet)
|
|
||||||
pod.DropDisabledTemplateFields(&newDaemonSet.Spec.Template, &oldDaemonSet.Spec.Template)
|
pod.DropDisabledTemplateFields(&newDaemonSet.Spec.Template, &oldDaemonSet.Spec.Template)
|
||||||
|
|
||||||
// update is not allowed to set status
|
// update is not allowed to set status
|
||||||
@ -121,35 +116,6 @@ func (daemonSetStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropDaemonSetDisabledFields drops fields that are not used if their associated feature gates
|
|
||||||
// are not enabled. The typical pattern is:
|
|
||||||
// if !utilfeature.DefaultFeatureGate.Enabled(features.MyFeature) && !myFeatureInUse(oldSvc) {
|
|
||||||
// newSvc.Spec.MyFeature = nil
|
|
||||||
// }
|
|
||||||
func dropDaemonSetDisabledFields(newDS *apps.DaemonSet, oldDS *apps.DaemonSet) {
|
|
||||||
if !utilfeature.DefaultFeatureGate.Enabled(features.DaemonSetUpdateSurge) {
|
|
||||||
if r := newDS.Spec.UpdateStrategy.RollingUpdate; r != nil {
|
|
||||||
if daemonSetSurgeFieldsInUse(oldDS) {
|
|
||||||
// we need to ensure that MaxUnavailable is non-zero to preserve previous behavior
|
|
||||||
if r.MaxUnavailable.IntVal == 0 && r.MaxUnavailable.StrVal == "0%" {
|
|
||||||
r.MaxUnavailable = intstr.FromInt(1)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// clear the MaxSurge field and let validation deal with MaxUnavailable
|
|
||||||
r.MaxSurge = intstr.IntOrString{}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// daemonSetSurgeFieldsInUse returns true if fields related to daemonset update surge are set
|
|
||||||
func daemonSetSurgeFieldsInUse(ds *apps.DaemonSet) bool {
|
|
||||||
if ds == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return ds.Spec.UpdateStrategy.RollingUpdate != nil && (ds.Spec.UpdateStrategy.RollingUpdate.MaxSurge.IntVal != 0 || ds.Spec.UpdateStrategy.RollingUpdate.MaxSurge.StrVal != "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate validates a new daemon set.
|
// Validate validates a new daemon set.
|
||||||
func (daemonSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (daemonSetStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
daemonSet := obj.(*apps.DaemonSet)
|
daemonSet := obj.(*apps.DaemonSet)
|
||||||
|
@ -21,15 +21,10 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/diff"
|
|
||||||
"k8s.io/apimachinery/pkg/util/intstr"
|
|
||||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
||||||
featuregatetesting "k8s.io/component-base/featuregate/testing"
|
|
||||||
"k8s.io/kubernetes/pkg/apis/apps"
|
"k8s.io/kubernetes/pkg/apis/apps"
|
||||||
api "k8s.io/kubernetes/pkg/apis/core"
|
api "k8s.io/kubernetes/pkg/apis/core"
|
||||||
"k8s.io/kubernetes/pkg/features"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -139,207 +134,3 @@ func newDaemonSetWithSelectorLabels(selectorLabels map[string]string, templateGe
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeDaemonSetWithSurge(unavailable intstr.IntOrString, surge intstr.IntOrString) *apps.DaemonSet {
|
|
||||||
return &apps.DaemonSet{
|
|
||||||
Spec: apps.DaemonSetSpec{
|
|
||||||
UpdateStrategy: apps.DaemonSetUpdateStrategy{
|
|
||||||
Type: apps.RollingUpdateDaemonSetStrategyType,
|
|
||||||
RollingUpdate: &apps.RollingUpdateDaemonSet{
|
|
||||||
MaxUnavailable: unavailable,
|
|
||||||
MaxSurge: surge,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestDropDisabledField(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
name string
|
|
||||||
enableSurge bool
|
|
||||||
ds *apps.DaemonSet
|
|
||||||
old *apps.DaemonSet
|
|
||||||
expect *apps.DaemonSet
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "not surge, no update",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: &apps.DaemonSet{},
|
|
||||||
old: nil,
|
|
||||||
expect: &apps.DaemonSet{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field not used",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
old: nil,
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field not used in old and new",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field used",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field used, percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field used and cleared",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, field used and cleared, percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, field not used",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
old: nil,
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, field not used in old and new",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, field used",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
old: nil,
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, field used, percent",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromString("1%")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, field used in old and new",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, allows both fields (validation must catch)",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.FromInt(1)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, allows change from unavailable to surge",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "surge, allows change from surge to unvailable",
|
|
||||||
enableSurge: true,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, allows change from unavailable to surge",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, allows change from surge to unvailable",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromInt(1)),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromInt(2), intstr.IntOrString{}),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, allows change from unavailable to surge, percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromString("2%"), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromString("2%"), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, allows change from surge to unvailable, percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.FromString("1%")),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromString("2%"), intstr.IntOrString{}),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.IntOrString{}, intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, resets zero percent, one percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.FromString("1%")),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.FromString("1%")),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, resets and clears when zero percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.IntOrString{}),
|
|
||||||
old: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.FromString("1%")),
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromInt(1), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, sets zero percent, one percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.FromString("1%")),
|
|
||||||
old: nil,
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "not surge, sets and clears zero percent",
|
|
||||||
enableSurge: false,
|
|
||||||
ds: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.IntOrString{}),
|
|
||||||
old: nil,
|
|
||||||
expect: makeDaemonSetWithSurge(intstr.FromString("0%"), intstr.IntOrString{}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tc := range testCases {
|
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DaemonSetUpdateSurge, tc.enableSurge)()
|
|
||||||
old := tc.old.DeepCopy()
|
|
||||||
|
|
||||||
dropDaemonSetDisabledFields(tc.ds, tc.old)
|
|
||||||
|
|
||||||
// old obj should never be changed
|
|
||||||
if !reflect.DeepEqual(tc.old, old) {
|
|
||||||
t.Fatalf("old ds changed: %v", diff.ObjectReflectDiff(tc.old, old))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(tc.ds, tc.expect) {
|
|
||||||
t.Fatalf("unexpected ds spec: %v", diff.ObjectReflectDiff(tc.expect, tc.ds))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user