test: address VolumeAttachment DV feedback

This commit is contained in:
Lidang-Jiang 2026-05-16 10:17:46 +08:00 committed by Lidang Jiang
parent 0ed84c6b5d
commit 3274946d52
5 changed files with 49 additions and 81 deletions

View file

@ -22,3 +22,48 @@
errorType: '*'
origin: '*'
reason: Not served by kube-apiserver.
# Scale subresource: no dedicated Strategy in the new tree; tested separately
# via VerifyVersionedValidationEquivalence (which doesn't populate the
# in-process coverage accumulator). extensions/v1beta1/Scale is covered by
# the group-level entry above.
- apiVersion: autoscaling/v1
kind: Scale
path: '*'
errorType: '*'
origin: '*'
reason: Scale subresource has no dedicated Strategy; tested separately.
- apiVersion: apps/v1beta1
kind: Scale
path: '*'
errorType: '*'
origin: '*'
reason: Scale subresource has no dedicated Strategy; tested separately.
- apiVersion: apps/v1beta2
kind: Scale
path: '*'
errorType: '*'
origin: '*'
reason: Scale subresource has no dedicated Strategy; tested separately.
# VolumeAttachment spec is immutable. Updating inlineVolumeSpec.volumeMode is
# rejected at spec before the nested PersistentVolumeSpec rule can be observed
# through the REST strategy.
- apiVersion: storage.k8s.io/v1
kind: VolumeAttachment
path: spec.source.inlineVolumeSpec.volumeMode
errorType: FieldValueInvalid
origin: immutable
reason: VolumeAttachment spec immutability short-circuits this nested update rule.
- apiVersion: storage.k8s.io/v1alpha1
kind: VolumeAttachment
path: spec.source.inlineVolumeSpec.volumeMode
errorType: FieldValueInvalid
origin: immutable
reason: VolumeAttachment spec immutability short-circuits this nested update rule.
- apiVersion: storage.k8s.io/v1beta1
kind: VolumeAttachment
path: spec.source.inlineVolumeSpec.volumeMode
errorType: FieldValueInvalid
origin: immutable
reason: VolumeAttachment spec immutability short-circuits this nested update rule.

View file

@ -17,27 +17,15 @@ limitations under the License.
package volumeattachment
import (
"context"
"strings"
"testing"
corev1 "k8s.io/api/core/v1"
apistoragev1 "k8s.io/api/storage/v1"
apistoragev1alpha1 "k8s.io/api/storage/v1alpha1"
apistoragev1beta1 "k8s.io/api/storage/v1beta1"
"k8s.io/apimachinery/pkg/api/operation"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/test/coverage"
"k8s.io/apimachinery/pkg/util/validation/field"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
"k8s.io/apiserver/pkg/registry/rest"
apitesting "k8s.io/kubernetes/pkg/api/testing"
core "k8s.io/kubernetes/pkg/apis/core"
storage "k8s.io/kubernetes/pkg/apis/storage"
validationstoragev1 "k8s.io/kubernetes/pkg/apis/storage/v1"
validationstoragev1alpha1 "k8s.io/kubernetes/pkg/apis/storage/v1alpha1"
validationstoragev1beta1 "k8s.io/kubernetes/pkg/apis/storage/v1beta1"
registry "k8s.io/kubernetes/pkg/registry/storage/volumeattachment"
)
@ -118,10 +106,9 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) {
})
testCases := map[string]struct {
oldInput storage.VolumeAttachment
newInput storage.VolumeAttachment
expectedErrs field.ErrorList
verifyAllRules func(t *testing.T, apiVersion string)
oldInput storage.VolumeAttachment
newInput storage.VolumeAttachment
expectedErrs field.ErrorList
}{
"valid update": {
oldInput: mkValidVolumeAttachment(),
@ -140,18 +127,12 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) {
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec"), nil, "field is immutable").WithOrigin("immutable").MarkAlpha(),
},
// The object-level update path short-circuits on immutable spec, so
// cover the nested inline PersistentVolumeSpec rule explicitly here.
verifyAllRules: verifyInlineVolumeSpecVolumeMode,
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.newInput, &tc.oldInput, registry.Strategy, tc.expectedErrs)
if tc.verifyAllRules != nil {
tc.verifyAllRules(t, apiVersion)
}
})
}
}
@ -164,6 +145,7 @@ func TweakAttacher(attacher string) func(obj *storage.VolumeAttachment) {
func TweakInlineVolumeSpec(spec *core.PersistentVolumeSpec) func(obj *storage.VolumeAttachment) {
return func(obj *storage.VolumeAttachment) {
// VolumeAttachmentSource is a union; clear PersistentVolumeName so the inline spec is selected.
obj.Spec.Source.PersistentVolumeName = nil
obj.Spec.Source.InlineVolumeSpec = spec
}
@ -201,53 +183,3 @@ func mkInlineVolumeSpec(volumeMode *core.PersistentVolumeMode) *core.PersistentV
VolumeMode: volumeMode,
}
}
func verifyInlineVolumeSpecVolumeMode(t *testing.T, apiVersion string) {
t.Helper()
blockMode := corev1.PersistentVolumeBlock
filesystemMode := corev1.PersistentVolumeFilesystem
ctx := rest.WithAllDeclarativeEnforcedForTest(context.Background())
op := operation.Operation{Type: operation.Update}
fldPath := field.NewPath("spec", "source")
var errs field.ErrorList
switch apiVersion {
case "v1":
oldObj := &apistoragev1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&filesystemMode)}
newObj := &apistoragev1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&blockMode)}
errs = validationstoragev1.Validate_VolumeAttachmentSource(ctx, op, fldPath, newObj, oldObj)
case "v1alpha1":
oldObj := &apistoragev1alpha1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&filesystemMode)}
newObj := &apistoragev1alpha1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&blockMode)}
errs = validationstoragev1alpha1.Validate_VolumeAttachmentSource(ctx, op, fldPath, newObj, oldObj)
case "v1beta1":
oldObj := &apistoragev1beta1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&filesystemMode)}
newObj := &apistoragev1beta1.VolumeAttachmentSource{InlineVolumeSpec: mkVersionedInlineVolumeSpec(&blockMode)}
errs = validationstoragev1beta1.Validate_VolumeAttachmentSource(ctx, op, fldPath, newObj, oldObj)
default:
t.Fatalf("unexpected apiVersion %q", apiVersion)
}
expectedErrs := field.ErrorList{
field.Invalid(field.NewPath("spec", "source", "inlineVolumeSpec", "volumeMode"), nil, "").WithOrigin("immutable").MarkAlpha(),
}
field.ErrorMatcher{}.ByType().ByOrigin().ByField().ByValidationStabilityLevel().BySource().Test(t, expectedErrs, errs)
coverage.RecordObservedRules(schema.GroupVersionKind{
Group: "storage.k8s.io",
Version: apiVersion,
Kind: "VolumeAttachment",
}, errs)
}
func mkVersionedInlineVolumeSpec(volumeMode *corev1.PersistentVolumeMode) *corev1.PersistentVolumeSpec {
return &corev1.PersistentVolumeSpec{
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
PersistentVolumeSource: corev1.PersistentVolumeSource{
CSI: &corev1.CSIPersistentVolumeSource{
Driver: "com.test.foo",
VolumeHandle: "valid-volume",
},
},
VolumeMode: volumeMode,
}
}

View file

@ -38,9 +38,6 @@ func init() {
{ErrorType: "FieldValueRequired"},
{ErrorType: "FieldValueTooLong", Origin: "maxLength"},
},
"spec.source.inlineVolumeSpec.volumeMode": {
{ErrorType: "FieldValueInvalid", Origin: "immutable"},
},
},
)
}

View file

@ -38,9 +38,6 @@ func init() {
{ErrorType: "FieldValueRequired"},
{ErrorType: "FieldValueTooLong", Origin: "maxLength"},
},
"spec.source.inlineVolumeSpec.volumeMode": {
{ErrorType: "FieldValueInvalid", Origin: "immutable"},
},
},
)
}

View file

@ -38,9 +38,6 @@ func init() {
{ErrorType: "FieldValueRequired"},
{ErrorType: "FieldValueTooLong", Origin: "maxLength"},
},
"spec.source.inlineVolumeSpec.volumeMode": {
{ErrorType: "FieldValueInvalid", Origin: "immutable"},
},
},
)
}