Skip to content

Commit 94cc4ba

Browse files
authored
Merge pull request kubernetes#130233 from soltysh/statefulset_api
StatefulSet: add explicit validation for .spec.serviceName and mark the field optional
2 parents 4666b8c + d6e5d4f commit 94cc4ba

File tree

13 files changed

+82
-11
lines changed

13 files changed

+82
-11
lines changed

api/openapi-spec/swagger.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/openapi-spec/v3/apis__apps__v1_openapi.json

Lines changed: 1 addition & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/apps/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ type StatefulSetSpec struct {
198198
// the network identity of the set. Pods get DNS/hostnames that follow the
199199
// pattern: pod-specific-string.serviceName.default.svc.cluster.local
200200
// where "pod-specific-string" is managed by the StatefulSet controller.
201+
// +optional
201202
ServiceName string
202203

203204
// PodManagementPolicy controls how pods are created during initial scale up,

pkg/apis/apps/validation/validation.go

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ import (
3434
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
3535
)
3636

37+
// StatefulSetValidationOptions is a struct that can be passed to ValidateStatefulSetSpec to record the validate options
38+
type StatefulSetValidationOptions struct {
39+
// Allow invalid DNS1123 ServiceName
40+
AllowInvalidServiceName bool
41+
}
42+
3743
// ValidateStatefulSetName can be used to check whether the given StatefulSet name is valid.
3844
// Prefix indicates this name will be used as part of generation, in which case
3945
// trailing dashes are allowed.
@@ -89,7 +95,7 @@ func ValidatePersistentVolumeClaimRetentionPolicy(policy *apps.StatefulSetPersis
8995
}
9096

9197
// ValidateStatefulSetSpec tests if required fields in the StatefulSet spec are set.
92-
func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions) field.ErrorList {
98+
func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, opts apivalidation.PodValidationOptions, setOpts StatefulSetValidationOptions) field.ErrorList {
9399
allErrs := field.ErrorList{}
94100

95101
switch spec.PodManagementPolicy {
@@ -134,6 +140,10 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, op
134140
allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(replicaStartOrdinal), fldPath.Child("ordinals.start"))...)
135141
}
136142

143+
if !setOpts.AllowInvalidServiceName && len(spec.ServiceName) > 0 {
144+
allErrs = append(allErrs, apivalidation.ValidateDNS1123Label(spec.ServiceName, fldPath.Child("serviceName"))...)
145+
}
146+
137147
if spec.Selector == nil {
138148
allErrs = append(allErrs, field.Required(fldPath.Child("selector"), ""))
139149
} else {
@@ -164,7 +174,10 @@ func ValidateStatefulSetSpec(spec *apps.StatefulSetSpec, fldPath *field.Path, op
164174
// ValidateStatefulSet validates a StatefulSet.
165175
func ValidateStatefulSet(statefulSet *apps.StatefulSet, opts apivalidation.PodValidationOptions) field.ErrorList {
166176
allErrs := apivalidation.ValidateObjectMeta(&statefulSet.ObjectMeta, true, ValidateStatefulSetName, field.NewPath("metadata"))
167-
allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
177+
setOpts := StatefulSetValidationOptions{
178+
AllowInvalidServiceName: false, // require valid serviceNames in new StatefulSets
179+
}
180+
allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts, setOpts)...)
168181
return allErrs
169182
}
170183

@@ -177,7 +190,10 @@ func ValidateStatefulSetUpdate(statefulSet, oldStatefulSet *apps.StatefulSet, op
177190
// thing to do it delete such an instance, but if there is a finalizer, it
178191
// would need to pass update validation. Name can't change anyway.
179192
allErrs := apivalidation.ValidateObjectMetaUpdate(&statefulSet.ObjectMeta, &oldStatefulSet.ObjectMeta, field.NewPath("metadata"))
180-
allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts)...)
193+
setOpts := StatefulSetValidationOptions{
194+
AllowInvalidServiceName: true, // serviceName is immutable, tolerate existing invalid names on update
195+
}
196+
allErrs = append(allErrs, ValidateStatefulSetSpec(&statefulSet.Spec, field.NewPath("spec"), opts, setOpts)...)
181197

182198
// statefulset updates aren't super common and general updates are likely to be touching spec, so we'll do this
183199
// deep copy right away. This avoids mutating our inputs

pkg/apis/apps/validation/validation_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,12 @@ func tweakPVCScalePolicy(t apps.PersistentVolumeClaimRetentionPolicyType) pvcPol
195195
}
196196
}
197197

198+
func tweakServiceName(name string) statefulSetTweak {
199+
return func(ss *apps.StatefulSet) {
200+
ss.Spec.ServiceName = name
201+
}
202+
}
203+
198204
func TestValidateStatefulSet(t *testing.T) {
199205
validLabels := map[string]string{"a": "b"}
200206
validPodTemplate := api.PodTemplate{
@@ -520,6 +526,14 @@ func TestValidateStatefulSet(t *testing.T) {
520526
errs: field.ErrorList{
521527
field.Invalid(field.NewPath("spec", "ordinals.start"), nil, ""),
522528
},
529+
}, {
530+
name: "invalid service name",
531+
set: mkStatefulSet(&validPodTemplate,
532+
tweakServiceName("Invalid.Name"),
533+
),
534+
errs: field.ErrorList{
535+
field.Invalid(field.NewPath("spec", "serviceName"), "Invalid.Name", ""),
536+
},
523537
},
524538
}
525539

@@ -595,7 +609,7 @@ func TestValidateStatefulSetMinReadySeconds(t *testing.T) {
595609
for tcName, tc := range testCases {
596610
t.Run(tcName, func(t *testing.T) {
597611
errs := ValidateStatefulSetSpec(tc.ss, field.NewPath("spec", "minReadySeconds"),
598-
corevalidation.PodValidationOptions{})
612+
corevalidation.PodValidationOptions{}, StatefulSetValidationOptions{})
599613
if tc.expectErr && len(errs) == 0 {
600614
t.Errorf("Unexpected success")
601615
}
@@ -864,6 +878,10 @@ func TestValidateStatefulSetUpdate(t *testing.T) {
864878
name: "update existing instance with .spec.ordinals.start",
865879
old: mkStatefulSet(&validPodTemplate),
866880
update: mkStatefulSet(&validPodTemplate, tweakOrdinalsStart(3)),
881+
}, {
882+
name: "update with invalid .spec.serviceName",
883+
old: mkStatefulSet(&validPodTemplate, tweakServiceName("Invalid.Name")),
884+
update: mkStatefulSet(&validPodTemplate, tweakServiceName("Invalid.Name"), tweakReplicas(3)),
867885
},
868886
}
869887

pkg/generated/openapi/zz_generated.openapi.go

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/api/apps/v1/generated.proto

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/api/apps/v1/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ type StatefulSetSpec struct {
220220
// the network identity of the set. Pods get DNS/hostnames that follow the
221221
// pattern: pod-specific-string.serviceName.default.svc.cluster.local
222222
// where "pod-specific-string" is managed by the StatefulSet controller.
223+
// +optional
223224
ServiceName string `json:"serviceName" protobuf:"bytes,5,opt,name=serviceName"`
224225

225226
// podManagementPolicy controls how pods are created during initial scale up,

staging/src/k8s.io/api/apps/v1beta1/generated.proto

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

staging/src/k8s.io/api/apps/v1beta1/types.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ type StatefulSetSpec struct {
259259
// the network identity of the set. Pods get DNS/hostnames that follow the
260260
// pattern: pod-specific-string.serviceName.default.svc.cluster.local
261261
// where "pod-specific-string" is managed by the StatefulSet controller.
262+
// +optional
262263
ServiceName string `json:"serviceName" protobuf:"bytes,5,opt,name=serviceName"`
263264

264265
// podManagementPolicy controls how pods are created during initial scale up,

0 commit comments

Comments
 (0)