Update controller based on snapshot v1 apis

This commit is contained in:
xing-yang
2020-11-12 03:35:12 +00:00
parent 09c22517e0
commit 5069c99ec3
21 changed files with 569 additions and 138 deletions

View File

@@ -20,6 +20,7 @@ import (
"fmt"
"reflect"
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1"
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1beta1"
"github.com/kubernetes-csi/external-snapshotter/v3/pkg/utils"
v1 "k8s.io/api/admission/v1"
@@ -28,10 +29,14 @@ import (
)
var (
// SnapshotV1Beta1GVR is GroupVersionResource for volumesnapshots
// SnapshotV1Beta1GVR is GroupVersionResource for v1beta1 VolumeSnapshots
SnapshotV1Beta1GVR = metav1.GroupVersionResource{Group: volumesnapshotv1beta1.GroupName, Version: "v1beta1", Resource: "volumesnapshots"}
// SnapshotContentV1Beta1GVR is GroupVersionResource for volumesnapshotcontents
// 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"}
)
// Add a label {"added-label": "yes"} to the object
@@ -65,7 +70,19 @@ func admitSnapshot(ar v1.AdmissionReview) *v1.AdmissionResponse {
klog.Error(err)
return toV1AdmissionResponse(err)
}
return decideSnapshot(snapshot, oldSnapshot, isUpdate)
return decideSnapshotV1beta1(snapshot, oldSnapshot, isUpdate)
case SnapshotV1GVR:
snapshot := &volumesnapshotv1.VolumeSnapshot{}
if _, _, err := deserializer.Decode(raw, nil, snapshot); err != nil {
klog.Error(err)
return toV1AdmissionResponse(err)
}
oldSnapshot := &volumesnapshotv1.VolumeSnapshot{}
if _, _, err := deserializer.Decode(oldRaw, nil, oldSnapshot); err != nil {
klog.Error(err)
return toV1AdmissionResponse(err)
}
return decideSnapshotV1(snapshot, oldSnapshot, isUpdate)
case SnapshotContentV1Beta1GVR:
snapcontent := &volumesnapshotv1beta1.VolumeSnapshotContent{}
if _, _, err := deserializer.Decode(raw, nil, snapcontent); err != nil {
@@ -77,7 +94,19 @@ func admitSnapshot(ar v1.AdmissionReview) *v1.AdmissionResponse {
klog.Error(err)
return toV1AdmissionResponse(err)
}
return decideSnapshotContent(snapcontent, oldSnapcontent, isUpdate)
return decideSnapshotContentV1beta1(snapcontent, oldSnapcontent, isUpdate)
case SnapshotContentV1GVR:
snapcontent := &volumesnapshotv1.VolumeSnapshotContent{}
if _, _, err := deserializer.Decode(raw, nil, snapcontent); err != nil {
klog.Error(err)
return toV1AdmissionResponse(err)
}
oldSnapcontent := &volumesnapshotv1.VolumeSnapshotContent{}
if _, _, err := deserializer.Decode(oldRaw, nil, oldSnapcontent); err != nil {
klog.Error(err)
return toV1AdmissionResponse(err)
}
return decideSnapshotContentV1(snapcontent, oldSnapcontent, isUpdate)
default:
err := fmt.Errorf("expect resource to be %s or %s", SnapshotV1Beta1GVR, SnapshotContentV1Beta1GVR)
klog.Error(err)
@@ -85,7 +114,7 @@ func admitSnapshot(ar v1.AdmissionReview) *v1.AdmissionResponse {
}
}
func decideSnapshot(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot, isUpdate bool) *v1.AdmissionResponse {
func decideSnapshotV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot, isUpdate bool) *v1.AdmissionResponse {
reviewResponse := &v1.AdmissionResponse{
Allowed: true,
Result: &metav1.Status{},
@@ -97,12 +126,12 @@ func decideSnapshot(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot,
// 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 := utils.ValidateSnapshot(oldSnapshot); err != nil {
if err := utils.ValidateV1Beta1Snapshot(oldSnapshot); err != nil {
return reviewResponse
}
// if it is an UPDATE and oldSnapshot is valid, check immutable fields
if err := checkSnapshotImmutableFields(snapshot, oldSnapshot); err != nil {
if err := checkSnapshotImmutableFieldsV1beta1(snapshot, oldSnapshot); err != nil {
reviewResponse.Allowed = false
reviewResponse.Result.Message = err.Error()
return reviewResponse
@@ -110,14 +139,37 @@ func decideSnapshot(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot,
}
// 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 := utils.ValidateSnapshot(snapshot); err != nil {
if err := utils.ValidateV1Beta1Snapshot(snapshot); err != nil {
reviewResponse.Allowed = false
reviewResponse.Result.Message = err.Error()
}
return reviewResponse
}
func decideSnapshotContent(snapcontent, oldSnapcontent *volumesnapshotv1beta1.VolumeSnapshotContent, isUpdate bool) *v1.AdmissionResponse {
func decideSnapshotV1(snapshot, oldSnapshot *volumesnapshotv1.VolumeSnapshot, isUpdate bool) *v1.AdmissionResponse {
reviewResponse := &v1.AdmissionResponse{
Allowed: true,
Result: &metav1.Status{},
}
if isUpdate {
// if it is an UPDATE and oldSnapshot is valid, check immutable fields
if err := checkSnapshotImmutableFieldsV1(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 := utils.ValidateV1Snapshot(snapshot); err != nil {
reviewResponse.Allowed = false
reviewResponse.Result.Message = err.Error()
}
return reviewResponse
}
func decideSnapshotContentV1beta1(snapcontent, oldSnapcontent *volumesnapshotv1beta1.VolumeSnapshotContent, isUpdate bool) *v1.AdmissionResponse {
reviewResponse := &v1.AdmissionResponse{
Allowed: true,
Result: &metav1.Status{},
@@ -129,12 +181,12 @@ func decideSnapshotContent(snapcontent, oldSnapcontent *volumesnapshotv1beta1.Vo
// 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 := utils.ValidateSnapshotContent(oldSnapcontent); err != nil {
if err := utils.ValidateV1Beta1SnapshotContent(oldSnapcontent); err != nil {
return reviewResponse
}
// if it is an UPDATE and oldSnapcontent is valid, check immutable fields
if err := checkSnapshotContentImmutableFields(snapcontent, oldSnapcontent); err != nil {
if err := checkSnapshotContentImmutableFieldsV1beta1(snapcontent, oldSnapcontent); err != nil {
reviewResponse.Allowed = false
reviewResponse.Result.Message = err.Error()
return reviewResponse
@@ -142,7 +194,30 @@ func decideSnapshotContent(snapcontent, oldSnapcontent *volumesnapshotv1beta1.Vo
}
// 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 := utils.ValidateSnapshotContent(snapcontent); err != nil {
if err := utils.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,
Result: &metav1.Status{},
}
if isUpdate {
// if it is an UPDATE and oldSnapcontent is valid, check immutable fields
if err := checkSnapshotContentImmutableFieldsV1(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 := utils.ValidateV1SnapshotContent(snapcontent); err != nil {
reviewResponse.Allowed = false
reviewResponse.Result.Message = err.Error()
}
@@ -155,7 +230,8 @@ func strPtrDereference(s *string) string {
}
return *s
}
func checkSnapshotImmutableFields(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot) error {
func checkSnapshotImmutableFieldsV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSnapshot) error {
if snapshot == nil {
return fmt.Errorf("VolumeSnapshot is nil")
}
@@ -176,7 +252,48 @@ func checkSnapshotImmutableFields(snapshot, oldSnapshot *volumesnapshotv1beta1.V
return nil
}
func checkSnapshotContentImmutableFields(snapcontent, oldSnapcontent *volumesnapshotv1beta1.VolumeSnapshotContent) error {
func checkSnapshotImmutableFieldsV1(snapshot, oldSnapshot *volumesnapshotv1.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 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")
}

View File

@@ -21,13 +21,14 @@ import (
"fmt"
"testing"
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1"
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v3/apis/volumesnapshot/v1beta1"
v1 "k8s.io/api/admission/v1"
core_v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
)
func TestAdmitVolumeSnapshot(t *testing.T) {
func TestAdmitVolumeSnapshotV1beta1(t *testing.T) {
pvcname := "pvcname1"
mutatedField := "changed-immutable-field"
contentname := "snapcontent1"
@@ -223,7 +224,191 @@ func TestAdmitVolumeSnapshot(t *testing.T) {
})
}
}
func TestAdmitVolumeSnapshotContent(t *testing.T) {
func TestAdmitVolumeSnapshotV1(t *testing.T) {
pvcname := "pvcname1"
mutatedField := "changed-immutable-field"
contentname := "snapcontent1"
volumeSnapshotClassName := "volume-snapshot-class-1"
emptyVolumeSnapshotClassName := ""
testCases := []struct {
name string
volumeSnapshot *volumesnapshotv1.VolumeSnapshot
oldVolumeSnapshot *volumesnapshotv1.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 valid",
volumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
},
},
},
oldVolumeSnapshot: nil,
shouldAdmit: true,
operation: v1.Create,
},
{
name: "Update: old is valid and new is invalid",
volumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
},
VolumeSnapshotClassName: &emptyVolumeSnapshotClassName,
},
},
oldVolumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.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: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
},
VolumeSnapshotClassName: &volumeSnapshotClassName,
},
},
oldVolumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
},
},
},
shouldAdmit: true,
operation: v1.Update,
},
{
name: "Update: old is valid and new is valid but changes immutable field spec.source",
volumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &mutatedField,
},
VolumeSnapshotClassName: &volumeSnapshotClassName,
},
},
oldVolumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.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: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
},
},
},
oldVolumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
PersistentVolumeClaimName: &pvcname,
VolumeSnapshotContentName: &contentname,
},
},
},
shouldAdmit: false,
operation: v1.Update,
msg: fmt.Sprintf("Spec.Source.PersistentVolumeClaimName is immutable but was changed from %s to <nil string pointer>", pvcname),
},
{
// will be handled by schema validation
name: "Update: old is invalid and new is invalid",
volumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.VolumeSnapshotSource{
VolumeSnapshotContentName: &contentname,
PersistentVolumeClaimName: &pvcname,
},
},
},
oldVolumeSnapshot: &volumesnapshotv1.VolumeSnapshot{
Spec: volumesnapshotv1.VolumeSnapshotSpec{
Source: volumesnapshotv1.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: SnapshotV1GVR,
Operation: tc.operation,
},
}
response := admitSnapshot(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 TestAdmitVolumeSnapshotContentV1beta1(t *testing.T) {
volumeHandle := "volumeHandle1"
modifiedField := "modified-field"
snapshotHandle := "snapshotHandle1"
@@ -372,3 +557,147 @@ func TestAdmitVolumeSnapshotContent(t *testing.T) {
})
}
}
func TestAdmitVolumeSnapshotContentV1(t *testing.T) {
volumeHandle := "volumeHandle1"
modifiedField := "modified-field"
snapshotHandle := "snapshotHandle1"
volumeSnapshotClassName := "volume-snapshot-class-1"
validContent := &volumesnapshotv1.VolumeSnapshotContent{
Spec: volumesnapshotv1.VolumeSnapshotContentSpec{
Source: volumesnapshotv1.VolumeSnapshotContentSource{
SnapshotHandle: &snapshotHandle,
},
VolumeSnapshotRef: core_v1.ObjectReference{
Name: "snapshot-ref",
Namespace: "default-ns",
},
VolumeSnapshotClassName: &volumeSnapshotClassName,
},
}
invalidContent := &volumesnapshotv1.VolumeSnapshotContent{
Spec: volumesnapshotv1.VolumeSnapshotContentSpec{
Source: volumesnapshotv1.VolumeSnapshotContentSource{
SnapshotHandle: &snapshotHandle,
VolumeHandle: &volumeHandle,
},
VolumeSnapshotRef: core_v1.ObjectReference{
Name: "",
Namespace: "default-ns",
},
},
}
testCases := []struct {
name string
volumeSnapshotContent *volumesnapshotv1.VolumeSnapshotContent
oldVolumeSnapshotContent *volumesnapshotv1.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 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: &volumesnapshotv1.VolumeSnapshotContent{
Spec: volumesnapshotv1.VolumeSnapshotContentSpec{
Source: volumesnapshotv1.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: false,
operation: v1.Update,
msg: fmt.Sprintf("Spec.Source.VolumeHandle is immutable but was changed from %s to <nil string pointer>", volumeHandle),
},
{
name: "Update: old is invalid and new is invalid",
volumeSnapshotContent: invalidContent,
oldVolumeSnapshotContent: invalidContent,
shouldAdmit: false,
operation: v1.Update,
msg: fmt.Sprintf("both Spec.VolumeSnapshotRef.Name = and Spec.VolumeSnapshotRef.Namespace = default-ns must be set"),
},
}
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: SnapshotContentV1GVR,
Operation: tc.operation,
},
}
response := admitSnapshot(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)
}
})
}
}