@@ -22,8 +22,11 @@ import (
2222
2323 volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1"
2424 volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v6/apis/volumesnapshot/v1beta1"
25+ storagelisters "github.com/kubernetes-csi/external-snapshotter/client/v6/listers/volumesnapshot/v1"
26+ "github.com/kubernetes-csi/external-snapshotter/v6/pkg/utils"
2527 v1 "k8s.io/api/admission/v1"
2628 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
29+ "k8s.io/apimachinery/pkg/labels"
2730 "k8s.io/klog/v2"
2831)
2932
@@ -36,10 +39,26 @@ var (
3639 SnapshotContentV1Beta1GVR = metav1.GroupVersionResource {Group : volumesnapshotv1beta1 .GroupName , Version : "v1beta1" , Resource : "volumesnapshotcontents" }
3740 // SnapshotContentV1GVR is GroupVersionResource for v1 VolumeSnapshotContents
3841 SnapshotContentV1GVR = metav1.GroupVersionResource {Group : volumesnapshotv1 .GroupName , Version : "v1" , Resource : "volumesnapshotcontents" }
42+ // SnapshotContentV1GVR is GroupVersionResource for v1 VolumeSnapshotContents
43+ SnapshotClassV1GVR = metav1.GroupVersionResource {Group : volumesnapshotv1 .GroupName , Version : "v1" , Resource : "volumesnapshotclasses" }
3944)
4045
46+ type SnapshotAdmitter interface {
47+ Admit (v1.AdmissionReview ) * v1.AdmissionResponse
48+ }
49+
50+ type admitter struct {
51+ lister storagelisters.VolumeSnapshotClassLister
52+ }
53+
54+ func NewSnapshotAdmitter (lister storagelisters.VolumeSnapshotClassLister ) SnapshotAdmitter {
55+ return & admitter {
56+ lister : lister ,
57+ }
58+ }
59+
4160// Add a label {"added-label": "yes"} to the object
42- func admitSnapshot (ar v1.AdmissionReview ) * v1.AdmissionResponse {
61+ func ( a admitter ) Admit (ar v1.AdmissionReview ) * v1.AdmissionResponse {
4362 klog .V (2 ).Info ("admitting volumesnapshots or volumesnapshotcontents" )
4463
4564 reviewResponse := & v1.AdmissionResponse {
@@ -106,6 +125,18 @@ func admitSnapshot(ar v1.AdmissionReview) *v1.AdmissionResponse {
106125 return toV1AdmissionResponse (err )
107126 }
108127 return decideSnapshotContentV1 (snapcontent , oldSnapcontent , isUpdate )
128+ case SnapshotClassV1GVR :
129+ snapClass := & volumesnapshotv1.VolumeSnapshotClass {}
130+ if _ , _ , err := deserializer .Decode (raw , nil , snapClass ); err != nil {
131+ klog .Error (err )
132+ return toV1AdmissionResponse (err )
133+ }
134+ oldSnapClass := & volumesnapshotv1.VolumeSnapshotClass {}
135+ if _ , _ , err := deserializer .Decode (oldRaw , nil , oldSnapClass ); err != nil {
136+ klog .Error (err )
137+ return toV1AdmissionResponse (err )
138+ }
139+ return decideSnapshotClassV1 (snapClass , oldSnapClass , a .lister )
109140 default :
110141 err := fmt .Errorf ("expect resource to be %s or %s" , SnapshotV1Beta1GVR , SnapshotContentV1Beta1GVR )
111142 klog .Error (err )
@@ -223,6 +254,43 @@ func decideSnapshotContentV1(snapcontent, oldSnapcontent *volumesnapshotv1.Volum
223254 return reviewResponse
224255}
225256
257+ func decideSnapshotClassV1 (snapClass , oldSnapClass * volumesnapshotv1.VolumeSnapshotClass , lister storagelisters.VolumeSnapshotClassLister ) * v1.AdmissionResponse {
258+ reviewResponse := & v1.AdmissionResponse {
259+ Allowed : true ,
260+ Result : & metav1.Status {},
261+ }
262+
263+ // Only Validate when a new snapClass is being set as a default.
264+ if snapClass .Annotations [utils .IsDefaultSnapshotClassAnnotation ] != "true" {
265+ return reviewResponse
266+ }
267+
268+ // If Old snapshot class has this, then we can assume that it was validated if driver is the same.
269+ if oldSnapClass .Annotations [utils .IsDefaultSnapshotClassAnnotation ] == "true" && oldSnapClass .Driver == snapClass .Driver {
270+ return reviewResponse
271+ }
272+
273+ ret , err := lister .List (labels .Everything ())
274+ if err != nil {
275+ reviewResponse .Allowed = false
276+ reviewResponse .Result .Message = err .Error ()
277+ return reviewResponse
278+ }
279+
280+ for _ , snapshotClass := range ret {
281+ if snapshotClass .Annotations [utils .IsDefaultSnapshotClassAnnotation ] != "true" {
282+ continue
283+ }
284+ if snapshotClass .Driver == snapClass .Driver {
285+ reviewResponse .Allowed = false
286+ reviewResponse .Result .Message = fmt .Sprintf ("default snapshot class: %v already exits for driver: %v" , snapshotClass .Name , snapClass .Driver )
287+ return reviewResponse
288+ }
289+ }
290+
291+ return reviewResponse
292+ }
293+
226294func strPtrDereference (s * string ) string {
227295 if s == nil {
228296 return "<nil string pointer>"
0 commit comments