Remove v1beta1 references from validation webhook
This commit is contained in:
@@ -21,7 +21,6 @@ import (
|
||||
"reflect"
|
||||
|
||||
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
||||
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1beta1"
|
||||
storagelisters "github.com/kubernetes-csi/external-snapshotter/client/v6/listers/volumesnapshot/v1"
|
||||
"github.com/kubernetes-csi/external-snapshotter/v6/pkg/utils"
|
||||
v1 "k8s.io/api/admission/v1"
|
||||
@@ -31,12 +30,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// SnapshotV1Beta1GVR is GroupVersionResource for v1beta1 VolumeSnapshots
|
||||
SnapshotV1Beta1GVR = metav1.GroupVersionResource{Group: volumesnapshotv1beta1.GroupName, Version: "v1beta1", Resource: "volumesnapshots"}
|
||||
// SnapshotV1GVR is GroupVersionResource for v1 VolumeSnapshots
|
||||
SnapshotV1GVR = metav1.GroupVersionResource{Group: volumesnapshotv1.GroupName, Version: "v1", Resource: "volumesnapshots"}
|
||||
// SnapshotContentV1Beta1GVR is GroupVersionResource for v1beta1 VolumeSnapshotContents
|
||||
SnapshotContentV1Beta1GVR = metav1.GroupVersionResource{Group: volumesnapshotv1beta1.GroupName, Version: "v1beta1", Resource: "volumesnapshotcontents"}
|
||||
// SnapshotContentV1GVR is GroupVersionResource for v1 VolumeSnapshotContents
|
||||
SnapshotContentV1GVR = metav1.GroupVersionResource{Group: volumesnapshotv1.GroupName, Version: "v1", Resource: "volumesnapshotcontents"}
|
||||
// SnapshotContentV1GVR is GroupVersionResource for v1 VolumeSnapshotContents
|
||||
@@ -77,18 +72,6 @@ func (a admitter) Admit(ar v1.AdmissionReview) *v1.AdmissionResponse {
|
||||
|
||||
deserializer := codecs.UniversalDeserializer()
|
||||
switch ar.Request.Resource {
|
||||
case SnapshotV1Beta1GVR:
|
||||
snapshot := &volumesnapshotv1beta1.VolumeSnapshot{}
|
||||
if _, _, err := deserializer.Decode(raw, nil, snapshot); err != nil {
|
||||
klog.Error(err)
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
oldSnapshot := &volumesnapshotv1beta1.VolumeSnapshot{}
|
||||
if _, _, err := deserializer.Decode(oldRaw, nil, oldSnapshot); err != nil {
|
||||
klog.Error(err)
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
return decideSnapshotV1beta1(snapshot, oldSnapshot, isUpdate)
|
||||
case SnapshotV1GVR:
|
||||
snapshot := &volumesnapshotv1.VolumeSnapshot{}
|
||||
if _, _, err := deserializer.Decode(raw, nil, snapshot); err != nil {
|
||||
@@ -101,18 +84,6 @@ func (a admitter) Admit(ar v1.AdmissionReview) *v1.AdmissionResponse {
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
return decideSnapshotV1(snapshot, oldSnapshot, isUpdate)
|
||||
case SnapshotContentV1Beta1GVR:
|
||||
snapcontent := &volumesnapshotv1beta1.VolumeSnapshotContent{}
|
||||
if _, _, err := deserializer.Decode(raw, nil, snapcontent); err != nil {
|
||||
klog.Error(err)
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
oldSnapcontent := &volumesnapshotv1beta1.VolumeSnapshotContent{}
|
||||
if _, _, err := deserializer.Decode(oldRaw, nil, oldSnapcontent); err != nil {
|
||||
klog.Error(err)
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
return decideSnapshotContentV1beta1(snapcontent, oldSnapcontent, isUpdate)
|
||||
case SnapshotContentV1GVR:
|
||||
snapcontent := &volumesnapshotv1.VolumeSnapshotContent{}
|
||||
if _, _, err := deserializer.Decode(raw, nil, snapcontent); err != nil {
|
||||
@@ -138,44 +109,12 @@ func (a admitter) Admit(ar v1.AdmissionReview) *v1.AdmissionResponse {
|
||||
}
|
||||
return decideSnapshotClassV1(snapClass, oldSnapClass, a.lister)
|
||||
default:
|
||||
err := fmt.Errorf("expect resource to be %s or %s", SnapshotV1Beta1GVR, SnapshotContentV1Beta1GVR)
|
||||
err := fmt.Errorf("expect resource to be %s, %s or %s", SnapshotV1GVR, SnapshotContentV1GVR, SnapshotClassV1GVR)
|
||||
klog.Error(err)
|
||||
return toV1AdmissionResponse(err)
|
||||
}
|
||||
}
|
||||
|
||||
func decideSnapshotV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot, isUpdate bool) *v1.AdmissionResponse {
|
||||
reviewResponse := &v1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{},
|
||||
}
|
||||
|
||||
if isUpdate {
|
||||
// if it is an UPDATE and oldSnapshot is not valid, then don't enforce strict validation
|
||||
// This allows no-op updates to occur on snapshot resources which fail strict validation
|
||||
// Which allows the remover of finalizers and therefore deletion of this object
|
||||
// Don't rely on the pointers to be nil, because the deserialization method will convert it to
|
||||
// The empty struct value. Instead check the operation type.
|
||||
if err := ValidateV1Beta1Snapshot(oldSnapshot); err != nil {
|
||||
return reviewResponse
|
||||
}
|
||||
|
||||
// if it is an UPDATE and oldSnapshot is valid, check immutable fields
|
||||
if err := checkSnapshotImmutableFieldsV1beta1(snapshot, oldSnapshot); err != nil {
|
||||
reviewResponse.Allowed = false
|
||||
reviewResponse.Result.Message = err.Error()
|
||||
return reviewResponse
|
||||
}
|
||||
}
|
||||
// Enforce strict validation for CREATE requests. Immutable checks don't apply for CREATE requests.
|
||||
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||
if err := ValidateV1Beta1Snapshot(snapshot); err != nil {
|
||||
reviewResponse.Allowed = false
|
||||
reviewResponse.Result.Message = err.Error()
|
||||
}
|
||||
return reviewResponse
|
||||
}
|
||||
|
||||
func decideSnapshotV1(snapshot, oldSnapshot *volumesnapshotv1.VolumeSnapshot, isUpdate bool) *v1.AdmissionResponse {
|
||||
reviewResponse := &v1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
@@ -199,38 +138,6 @@ func decideSnapshotV1(snapshot, oldSnapshot *volumesnapshotv1.VolumeSnapshot, is
|
||||
return reviewResponse
|
||||
}
|
||||
|
||||
func decideSnapshotContentV1beta1(snapcontent, oldSnapcontent *volumesnapshotv1beta1.VolumeSnapshotContent, isUpdate bool) *v1.AdmissionResponse {
|
||||
reviewResponse := &v1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
Result: &metav1.Status{},
|
||||
}
|
||||
|
||||
if isUpdate {
|
||||
// if it is an UPDATE and oldSnapcontent is not valid, then don't enforce strict validation
|
||||
// This allows no-op updates to occur on snapshot resources which fail strict validation
|
||||
// Which allows the remover of finalizers and therefore deletion of this object
|
||||
// Don't rely on the pointers to be nil, because the deserialization method will convert it to
|
||||
// The empty struct value. Instead check the operation type.
|
||||
if err := ValidateV1Beta1SnapshotContent(oldSnapcontent); err != nil {
|
||||
return reviewResponse
|
||||
}
|
||||
|
||||
// if it is an UPDATE and oldSnapcontent is valid, check immutable fields
|
||||
if err := checkSnapshotContentImmutableFieldsV1beta1(snapcontent, oldSnapcontent); err != nil {
|
||||
reviewResponse.Allowed = false
|
||||
reviewResponse.Result.Message = err.Error()
|
||||
return reviewResponse
|
||||
}
|
||||
}
|
||||
// Enforce strict validation for all CREATE requests. Immutable checks don't apply for CREATE requests.
|
||||
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||
if err := ValidateV1Beta1SnapshotContent(snapcontent); err != nil {
|
||||
reviewResponse.Allowed = false
|
||||
reviewResponse.Result.Message = err.Error()
|
||||
}
|
||||
return reviewResponse
|
||||
}
|
||||
|
||||
func decideSnapshotContentV1(snapcontent, oldSnapcontent *volumesnapshotv1.VolumeSnapshotContent, isUpdate bool) *v1.AdmissionResponse {
|
||||
reviewResponse := &v1.AdmissionResponse{
|
||||
Allowed: true,
|
||||
@@ -298,27 +205,6 @@ func strPtrDereference(s *string) string {
|
||||
return *s
|
||||
}
|
||||
|
||||
func checkSnapshotImmutableFieldsV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot) error {
|
||||
if snapshot == nil {
|
||||
return fmt.Errorf("VolumeSnapshot is nil")
|
||||
}
|
||||
if oldSnapshot == nil {
|
||||
return fmt.Errorf("old VolumeSnapshot is nil")
|
||||
}
|
||||
|
||||
source := snapshot.Spec.Source
|
||||
oldSource := oldSnapshot.Spec.Source
|
||||
|
||||
if !reflect.DeepEqual(source.PersistentVolumeClaimName, oldSource.PersistentVolumeClaimName) {
|
||||
return fmt.Errorf("Spec.Source.PersistentVolumeClaimName is immutable but was changed from %s to %s", strPtrDereference(oldSource.PersistentVolumeClaimName), strPtrDereference(source.PersistentVolumeClaimName))
|
||||
}
|
||||
if !reflect.DeepEqual(source.VolumeSnapshotContentName, oldSource.VolumeSnapshotContentName) {
|
||||
return fmt.Errorf("Spec.Source.VolumeSnapshotContentName is immutable but was changed from %s to %s", strPtrDereference(oldSource.VolumeSnapshotContentName), strPtrDereference(source.VolumeSnapshotContentName))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSnapshotImmutableFieldsV1(snapshot, oldSnapshot *volumesnapshotv1.VolumeSnapshot) error {
|
||||
if snapshot == nil {
|
||||
return fmt.Errorf("VolumeSnapshot is nil")
|
||||
@@ -340,26 +226,6 @@ func checkSnapshotImmutableFieldsV1(snapshot, oldSnapshot *volumesnapshotv1.Volu
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSnapshotContentImmutableFieldsV1beta1(snapcontent, oldSnapcontent *volumesnapshotv1beta1.VolumeSnapshotContent) error {
|
||||
if snapcontent == nil {
|
||||
return fmt.Errorf("VolumeSnapshotContent is nil")
|
||||
}
|
||||
if oldSnapcontent == nil {
|
||||
return fmt.Errorf("old VolumeSnapshotContent is nil")
|
||||
}
|
||||
|
||||
source := snapcontent.Spec.Source
|
||||
oldSource := oldSnapcontent.Spec.Source
|
||||
|
||||
if !reflect.DeepEqual(source.VolumeHandle, oldSource.VolumeHandle) {
|
||||
return fmt.Errorf("Spec.Source.VolumeHandle is immutable but was changed from %s to %s", strPtrDereference(oldSource.VolumeHandle), strPtrDereference(source.VolumeHandle))
|
||||
}
|
||||
if !reflect.DeepEqual(source.SnapshotHandle, oldSource.SnapshotHandle) {
|
||||
return fmt.Errorf("Spec.Source.SnapshotHandle is immutable but was changed from %s to %s", strPtrDereference(oldSource.SnapshotHandle), strPtrDereference(source.SnapshotHandle))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkSnapshotContentImmutableFieldsV1(snapcontent, oldSnapcontent *volumesnapshotv1.VolumeSnapshotContent) error {
|
||||
if snapcontent == nil {
|
||||
return fmt.Errorf("VolumeSnapshotContent is nil")
|
||||
|
@@ -22,7 +22,6 @@ import (
|
||||
"testing"
|
||||
|
||||
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
||||
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1beta1"
|
||||
storagelisters "github.com/kubernetes-csi/external-snapshotter/client/v6/listers/volumesnapshot/v1"
|
||||
"github.com/kubernetes-csi/external-snapshotter/v6/pkg/utils"
|
||||
v1 "k8s.io/api/admission/v1"
|
||||
@@ -32,204 +31,6 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
func TestAdmitVolumeSnapshotV1beta1(t *testing.T) {
|
||||
pvcname := "pvcname1"
|
||||
mutatedField := "changed-immutable-field"
|
||||
contentname := "snapcontent1"
|
||||
volumeSnapshotClassName := "volume-snapshot-class-1"
|
||||
emptyVolumeSnapshotClassName := ""
|
||||
invalidErrorMsg := fmt.Sprintf("only one of Spec.Source.PersistentVolumeClaimName = %s and Spec.Source.VolumeSnapshotContentName = %s should be set", pvcname, contentname)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
volumeSnapshot *volumesnapshotv1beta1.VolumeSnapshot
|
||||
oldVolumeSnapshot *volumesnapshotv1beta1.VolumeSnapshot
|
||||
shouldAdmit bool
|
||||
msg string
|
||||
operation v1.Operation
|
||||
}{
|
||||
{
|
||||
name: "Delete: new and old are nil. Should admit",
|
||||
volumeSnapshot: nil,
|
||||
oldVolumeSnapshot: nil,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Delete,
|
||||
},
|
||||
{
|
||||
name: "Create: old is nil and new is invalid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcname,
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: nil,
|
||||
shouldAdmit: false,
|
||||
operation: v1.Create,
|
||||
msg: invalidErrorMsg,
|
||||
},
|
||||
{
|
||||
name: "Create: old is nil and new is valid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: nil,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Create,
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is invalid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
VolumeSnapshotClassName: &emptyVolumeSnapshotClassName,
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldAdmit: false,
|
||||
operation: v1.Update,
|
||||
msg: "Spec.VolumeSnapshotClassName must not be the empty string",
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is valid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
VolumeSnapshotClassName: &volumeSnapshotClassName,
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is valid but changes immutable field spec.source",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &mutatedField,
|
||||
},
|
||||
VolumeSnapshotClassName: &volumeSnapshotClassName,
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldAdmit: false,
|
||||
operation: v1.Update,
|
||||
msg: fmt.Sprintf("Spec.Source.VolumeSnapshotContentName is immutable but was changed from %s to %s", contentname, mutatedField),
|
||||
},
|
||||
{
|
||||
name: "Update: old is invalid and new is valid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcname,
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
{
|
||||
name: "Update: old is invalid and new is invalid",
|
||||
volumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
PersistentVolumeClaimName: &pvcname,
|
||||
},
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshot: &volumesnapshotv1beta1.VolumeSnapshot{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotSource{
|
||||
PersistentVolumeClaimName: &pvcname,
|
||||
VolumeSnapshotContentName: &contentname,
|
||||
},
|
||||
},
|
||||
},
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
snapshot := tc.volumeSnapshot
|
||||
raw, err := json.Marshal(snapshot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
oldSnapshot := tc.oldVolumeSnapshot
|
||||
oldRaw, err := json.Marshal(oldSnapshot)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
review := v1.AdmissionReview{
|
||||
Request: &v1.AdmissionRequest{
|
||||
Object: runtime.RawExtension{
|
||||
Raw: raw,
|
||||
},
|
||||
OldObject: runtime.RawExtension{
|
||||
Raw: oldRaw,
|
||||
},
|
||||
Resource: SnapshotV1Beta1GVR,
|
||||
Operation: tc.operation,
|
||||
},
|
||||
}
|
||||
sa := NewSnapshotAdmitter(nil)
|
||||
response := sa.Admit(review)
|
||||
shouldAdmit := response.Allowed
|
||||
msg := response.Result.Message
|
||||
|
||||
expectedResponse := tc.shouldAdmit
|
||||
expectedMsg := tc.msg
|
||||
|
||||
if shouldAdmit != expectedResponse {
|
||||
t.Errorf("expected \"%v\" to equal \"%v\"", shouldAdmit, expectedResponse)
|
||||
}
|
||||
if msg != expectedMsg {
|
||||
t.Errorf("expected \"%v\" to equal \"%v\"", msg, expectedMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdmitVolumeSnapshotV1(t *testing.T) {
|
||||
pvcname := "pvcname1"
|
||||
mutatedField := "changed-immutable-field"
|
||||
@@ -414,157 +215,6 @@ func TestAdmitVolumeSnapshotV1(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdmitVolumeSnapshotContentV1beta1(t *testing.T) {
|
||||
volumeHandle := "volumeHandle1"
|
||||
modifiedField := "modified-field"
|
||||
snapshotHandle := "snapshotHandle1"
|
||||
volumeSnapshotClassName := "volume-snapshot-class-1"
|
||||
validContent := &volumesnapshotv1beta1.VolumeSnapshotContent{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotContentSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotContentSource{
|
||||
SnapshotHandle: &snapshotHandle,
|
||||
},
|
||||
VolumeSnapshotRef: core_v1.ObjectReference{
|
||||
Name: "snapshot-ref",
|
||||
Namespace: "default-ns",
|
||||
},
|
||||
VolumeSnapshotClassName: &volumeSnapshotClassName,
|
||||
},
|
||||
}
|
||||
invalidContent := &volumesnapshotv1beta1.VolumeSnapshotContent{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotContentSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotContentSource{
|
||||
SnapshotHandle: &snapshotHandle,
|
||||
VolumeHandle: &volumeHandle,
|
||||
},
|
||||
VolumeSnapshotRef: core_v1.ObjectReference{
|
||||
Name: "",
|
||||
Namespace: "default-ns",
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidErrorMsg := fmt.Sprintf("only one of Spec.Source.VolumeHandle = %s and Spec.Source.SnapshotHandle = %s should be set", volumeHandle, snapshotHandle)
|
||||
testCases := []struct {
|
||||
name string
|
||||
volumeSnapshotContent *volumesnapshotv1beta1.VolumeSnapshotContent
|
||||
oldVolumeSnapshotContent *volumesnapshotv1beta1.VolumeSnapshotContent
|
||||
shouldAdmit bool
|
||||
msg string
|
||||
operation v1.Operation
|
||||
}{
|
||||
{
|
||||
name: "Delete: both new and old are nil",
|
||||
volumeSnapshotContent: nil,
|
||||
oldVolumeSnapshotContent: nil,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Delete,
|
||||
},
|
||||
{
|
||||
name: "Create: old is nil and new is invalid",
|
||||
volumeSnapshotContent: invalidContent,
|
||||
oldVolumeSnapshotContent: nil,
|
||||
shouldAdmit: false,
|
||||
operation: v1.Create,
|
||||
msg: invalidErrorMsg,
|
||||
},
|
||||
{
|
||||
name: "Create: old is nil and new is valid",
|
||||
volumeSnapshotContent: validContent,
|
||||
oldVolumeSnapshotContent: nil,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Create,
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is invalid",
|
||||
volumeSnapshotContent: invalidContent,
|
||||
oldVolumeSnapshotContent: validContent,
|
||||
shouldAdmit: false,
|
||||
operation: v1.Update,
|
||||
msg: fmt.Sprintf("Spec.Source.VolumeHandle is immutable but was changed from %s to %s", strPtrDereference(nil), volumeHandle),
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is valid",
|
||||
volumeSnapshotContent: validContent,
|
||||
oldVolumeSnapshotContent: validContent,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
{
|
||||
name: "Update: old is valid and new is valid but modifies immutable field",
|
||||
volumeSnapshotContent: &volumesnapshotv1beta1.VolumeSnapshotContent{
|
||||
Spec: volumesnapshotv1beta1.VolumeSnapshotContentSpec{
|
||||
Source: volumesnapshotv1beta1.VolumeSnapshotContentSource{
|
||||
SnapshotHandle: &modifiedField,
|
||||
},
|
||||
VolumeSnapshotRef: core_v1.ObjectReference{
|
||||
Name: "snapshot-ref",
|
||||
Namespace: "default-ns",
|
||||
},
|
||||
},
|
||||
},
|
||||
oldVolumeSnapshotContent: validContent,
|
||||
shouldAdmit: false,
|
||||
operation: v1.Update,
|
||||
msg: fmt.Sprintf("Spec.Source.SnapshotHandle is immutable but was changed from %s to %s", snapshotHandle, modifiedField),
|
||||
},
|
||||
{
|
||||
name: "Update: old is invalid and new is valid",
|
||||
volumeSnapshotContent: validContent,
|
||||
oldVolumeSnapshotContent: invalidContent,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
{
|
||||
name: "Update: old is invalid and new is invalid",
|
||||
volumeSnapshotContent: invalidContent,
|
||||
oldVolumeSnapshotContent: invalidContent,
|
||||
shouldAdmit: true,
|
||||
operation: v1.Update,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
snapshotContent := tc.volumeSnapshotContent
|
||||
raw, err := json.Marshal(snapshotContent)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
oldSnapshotContent := tc.oldVolumeSnapshotContent
|
||||
oldRaw, err := json.Marshal(oldSnapshotContent)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
review := v1.AdmissionReview{
|
||||
Request: &v1.AdmissionRequest{
|
||||
Object: runtime.RawExtension{
|
||||
Raw: raw,
|
||||
},
|
||||
OldObject: runtime.RawExtension{
|
||||
Raw: oldRaw,
|
||||
},
|
||||
Resource: SnapshotContentV1Beta1GVR,
|
||||
Operation: tc.operation,
|
||||
},
|
||||
}
|
||||
sa := NewSnapshotAdmitter(nil)
|
||||
response := sa.Admit(review)
|
||||
shouldAdmit := response.Allowed
|
||||
msg := response.Result.Message
|
||||
|
||||
expectedResponse := tc.shouldAdmit
|
||||
expectedMsg := tc.msg
|
||||
|
||||
if shouldAdmit != expectedResponse {
|
||||
t.Errorf("expected \"%v\" to equal \"%v\"", shouldAdmit, expectedResponse)
|
||||
}
|
||||
if msg != expectedMsg {
|
||||
t.Errorf("expected \"%v\" to equal \"%v\"", msg, expectedMsg)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdmitVolumeSnapshotContentV1(t *testing.T) {
|
||||
volumeHandle := "volumeHandle1"
|
||||
modifiedField := "modified-field"
|
||||
|
@@ -20,7 +20,6 @@ import (
|
||||
"fmt"
|
||||
|
||||
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
|
||||
crdv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1beta1"
|
||||
)
|
||||
|
||||
// ValidateV1Snapshot performs additional strict validation.
|
||||
@@ -38,29 +37,6 @@ func ValidateV1Snapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateV1Beta1Snapshot performs additional strict validation.
|
||||
// Do NOT rely on this function to fully validate snapshot objects.
|
||||
// This function will only check the additional rules provided by the webhook.
|
||||
func ValidateV1Beta1Snapshot(snapshot *crdv1beta1.VolumeSnapshot) error {
|
||||
if snapshot == nil {
|
||||
return fmt.Errorf("VolumeSnapshot is nil")
|
||||
}
|
||||
|
||||
source := snapshot.Spec.Source
|
||||
|
||||
if source.PersistentVolumeClaimName != nil && source.VolumeSnapshotContentName != nil {
|
||||
return fmt.Errorf("only one of Spec.Source.PersistentVolumeClaimName = %s and Spec.Source.VolumeSnapshotContentName = %s should be set", *source.PersistentVolumeClaimName, *source.VolumeSnapshotContentName)
|
||||
}
|
||||
if source.PersistentVolumeClaimName == nil && source.VolumeSnapshotContentName == nil {
|
||||
return fmt.Errorf("one of Spec.Source.PersistentVolumeClaimName and Spec.Source.VolumeSnapshotContentName should be set")
|
||||
}
|
||||
vscname := snapshot.Spec.VolumeSnapshotClassName
|
||||
if vscname != nil && *vscname == "" {
|
||||
return fmt.Errorf("Spec.VolumeSnapshotClassName must not be the empty string")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateV1SnapshotContent performs additional strict validation.
|
||||
// Do NOT rely on this function to fully validate snapshot content objects.
|
||||
// This function will only check the additional rules provided by the webhook.
|
||||
@@ -77,29 +53,3 @@ func ValidateV1SnapshotContent(snapcontent *crdv1.VolumeSnapshotContent) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateV1Beta1SnapshotContent performs additional strict validation.
|
||||
// Do NOT rely on this function to fully validate snapshot content objects.
|
||||
// This function will only check the additional rules provided by the webhook.
|
||||
func ValidateV1Beta1SnapshotContent(snapcontent *crdv1beta1.VolumeSnapshotContent) error {
|
||||
if snapcontent == nil {
|
||||
return fmt.Errorf("VolumeSnapshotContent is nil")
|
||||
}
|
||||
|
||||
source := snapcontent.Spec.Source
|
||||
|
||||
if source.VolumeHandle != nil && source.SnapshotHandle != nil {
|
||||
return fmt.Errorf("only one of Spec.Source.VolumeHandle = %s and Spec.Source.SnapshotHandle = %s should be set", *source.VolumeHandle, *source.SnapshotHandle)
|
||||
}
|
||||
if source.VolumeHandle == nil && source.SnapshotHandle == nil {
|
||||
return fmt.Errorf("one of Spec.Source.VolumeHandle and Spec.Source.SnapshotHandle should be set")
|
||||
}
|
||||
|
||||
vsref := snapcontent.Spec.VolumeSnapshotRef
|
||||
|
||||
if vsref.Name == "" || vsref.Namespace == "" {
|
||||
return fmt.Errorf("both Spec.VolumeSnapshotRef.Name = %s and Spec.VolumeSnapshotRef.Namespace = %s must be set", vsref.Name, vsref.Namespace)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user