diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go index db2e25ba5ef..027ac8923b1 100644 --- a/pkg/apis/apps/validation/validation.go +++ b/pkg/apis/apps/validation/validation.go @@ -173,10 +173,14 @@ func ValidateStatefulSet(statefulSet *apps.StatefulSet, opts apivalidation.PodVa // ValidateStatefulSetUpdate tests if required fields in the StatefulSet are set. func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList { - // first, validate that the new statefulset is valid - allErrs := ValidateStatefulSet(statefulSet, opts) - - allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))...) + // First, validate that the new statefulset is valid. Don't call + // ValidateStatefulSet() because we don't want to revalidate the name on + // update. This is important here because we used to allow DNS subdomain + // for name, but that can't actually create pods. The only reasonable + // thing to do it delete such an instance, but if there is a finalizer, it + // would need to pass update validation. Name can't change anyway. + allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata")) + allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...) // statefulset updates aren't super common and general updates are likely to be touching spec, so we'll do this // deep copy right away. This avoids mutating our inputs diff --git a/pkg/apis/apps/validation/validation_test.go b/pkg/apis/apps/validation/validation_test.go index eb5bf33168b..9a3b3181a21 100644 --- a/pkg/apis/apps/validation/validation_test.go +++ b/pkg/apis/apps/validation/validation_test.go @@ -1190,6 +1190,27 @@ func TestValidateStatefulSetUpdate(t *testing.T) { }, }, }, + { + name: "update existing instance with now-invalid name", + old: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "abc.123.example", Namespace: metav1.NamespaceDefault, Finalizers: []string{"final"}}, + Spec: apps.StatefulSetSpec{ + PodManagementPolicy: apps.OrderedReadyPodManagement, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + }, + update: apps.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{Name: "abc.123.example", Namespace: metav1.NamespaceDefault, Finalizers: []string{}}, + Spec: apps.StatefulSetSpec{ + PodManagementPolicy: apps.OrderedReadyPodManagement, + Selector: &metav1.LabelSelector{MatchLabels: validLabels}, + Template: validPodTemplate.Template, + UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType}, + }, + }, + }, } errorCases := []testCase{ @@ -1417,7 +1438,11 @@ func TestValidateStatefulSetUpdate(t *testing.T) { }, } - cmpOpts := []cmp.Option{cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail"), cmpopts.SortSlices(func(a, b *field.Error) bool { return a.Error() < b.Error() })} + cmpOpts := []cmp.Option{ + cmpopts.IgnoreFields(field.Error{}, "BadValue", "Detail"), + cmpopts.SortSlices(func(a, b *field.Error) bool { return a.Error() < b.Error() }), + cmpopts.EquateEmpty(), + } for _, testCase := range append(successCases, errorCases...) { name := testCase.name var testTitle string