Move snapshot validations to validation-webhook directory
Signed-off-by: prateekpandey14 <prateekpandey14@gmail.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import (
|
|||||||
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||||
"github.com/kubernetes-csi/external-snapshotter/v4/pkg/metrics"
|
"github.com/kubernetes-csi/external-snapshotter/v4/pkg/metrics"
|
||||||
"github.com/kubernetes-csi/external-snapshotter/v4/pkg/utils"
|
"github.com/kubernetes-csi/external-snapshotter/v4/pkg/utils"
|
||||||
|
webhook "github.com/kubernetes-csi/external-snapshotter/v4/pkg/validation-webhook"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ==================================================================
|
// ==================================================================
|
||||||
@@ -1505,7 +1506,7 @@ func (ctrl *csiSnapshotCommonController) setAnnVolumeSnapshotBeingDeleted(conten
|
|||||||
// checkAndSetInvalidContentLabel adds a label to unlabeled invalid content objects and removes the label from valid ones.
|
// checkAndSetInvalidContentLabel adds a label to unlabeled invalid content objects and removes the label from valid ones.
|
||||||
func (ctrl *csiSnapshotCommonController) checkAndSetInvalidContentLabel(content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshotContent, error) {
|
func (ctrl *csiSnapshotCommonController) checkAndSetInvalidContentLabel(content *crdv1.VolumeSnapshotContent) (*crdv1.VolumeSnapshotContent, error) {
|
||||||
hasLabel := utils.MapContainsKey(content.ObjectMeta.Labels, utils.VolumeSnapshotContentInvalidLabel)
|
hasLabel := utils.MapContainsKey(content.ObjectMeta.Labels, utils.VolumeSnapshotContentInvalidLabel)
|
||||||
err := utils.ValidateV1SnapshotContent(content)
|
err := webhook.ValidateV1SnapshotContent(content)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("syncContent[%s]: Invalid content detected, %s", content.Name, err.Error())
|
klog.Errorf("syncContent[%s]: Invalid content detected, %s", content.Name, err.Error())
|
||||||
}
|
}
|
||||||
@@ -1546,7 +1547,7 @@ func (ctrl *csiSnapshotCommonController) checkAndSetInvalidContentLabel(content
|
|||||||
// checkAndSetInvalidSnapshotLabel adds a label to unlabeled invalid snapshot objects and removes the label from valid ones.
|
// checkAndSetInvalidSnapshotLabel adds a label to unlabeled invalid snapshot objects and removes the label from valid ones.
|
||||||
func (ctrl *csiSnapshotCommonController) checkAndSetInvalidSnapshotLabel(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
func (ctrl *csiSnapshotCommonController) checkAndSetInvalidSnapshotLabel(snapshot *crdv1.VolumeSnapshot) (*crdv1.VolumeSnapshot, error) {
|
||||||
hasLabel := utils.MapContainsKey(snapshot.ObjectMeta.Labels, utils.VolumeSnapshotInvalidLabel)
|
hasLabel := utils.MapContainsKey(snapshot.ObjectMeta.Labels, utils.VolumeSnapshotInvalidLabel)
|
||||||
err := utils.ValidateV1Snapshot(snapshot)
|
err := webhook.ValidateV1Snapshot(snapshot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.Errorf("syncSnapshot[%s]: Invalid snapshot detected, %s", utils.SnapshotKey(snapshot), err.Error())
|
klog.Errorf("syncSnapshot[%s]: Invalid snapshot detected, %s", utils.SnapshotKey(snapshot), err.Error())
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||||
crdv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
v1 "k8s.io/api/core/v1"
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
@@ -122,87 +121,6 @@ var SnapshotterListSecretParams = secretParamsMap{
|
|||||||
secretNamespaceKey: PrefixedSnapshotterListSecretNamespaceKey,
|
secretNamespaceKey: PrefixedSnapshotterListSecretNamespaceKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateV1Snapshot 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 ValidateV1Snapshot(snapshot *crdv1.VolumeSnapshot) error {
|
|
||||||
if snapshot == nil {
|
|
||||||
return fmt.Errorf("VolumeSnapshot is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
vscname := snapshot.Spec.VolumeSnapshotClassName
|
|
||||||
if vscname != nil && *vscname == "" {
|
|
||||||
return fmt.Errorf("Spec.VolumeSnapshotClassName must not be the empty string")
|
|
||||||
}
|
|
||||||
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.
|
|
||||||
func ValidateV1SnapshotContent(snapcontent *crdv1.VolumeSnapshotContent) error {
|
|
||||||
if snapcontent == nil {
|
|
||||||
return fmt.Errorf("VolumeSnapshotContent is nil")
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// MapContainsKey checks if a given map of string to string contains the provided string.
|
// MapContainsKey checks if a given map of string to string contains the provided string.
|
||||||
func MapContainsKey(m map[string]string, s string) bool {
|
func MapContainsKey(m map[string]string, s string) bool {
|
||||||
_, r := m[s]
|
_, r := m[s]
|
||||||
|
@@ -22,7 +22,6 @@ import (
|
|||||||
|
|
||||||
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
volumesnapshotv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||||
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
|
volumesnapshotv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
|
||||||
"github.com/kubernetes-csi/external-snapshotter/v4/pkg/utils"
|
|
||||||
v1 "k8s.io/api/admission/v1"
|
v1 "k8s.io/api/admission/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/klog/v2"
|
"k8s.io/klog/v2"
|
||||||
@@ -126,7 +125,7 @@ func decideSnapshotV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSn
|
|||||||
// Which allows the remover of finalizers and therefore deletion of this object
|
// 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
|
// 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.
|
// The empty struct value. Instead check the operation type.
|
||||||
if err := utils.ValidateV1Beta1Snapshot(oldSnapshot); err != nil {
|
if err := ValidateV1Beta1Snapshot(oldSnapshot); err != nil {
|
||||||
return reviewResponse
|
return reviewResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,7 +138,7 @@ func decideSnapshotV1beta1(snapshot, oldSnapshot *volumesnapshotv1beta1.VolumeSn
|
|||||||
}
|
}
|
||||||
// Enforce strict validation for CREATE requests. Immutable checks don't apply for CREATE requests.
|
// 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.
|
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||||
if err := utils.ValidateV1Beta1Snapshot(snapshot); err != nil {
|
if err := ValidateV1Beta1Snapshot(snapshot); err != nil {
|
||||||
reviewResponse.Allowed = false
|
reviewResponse.Allowed = false
|
||||||
reviewResponse.Result.Message = err.Error()
|
reviewResponse.Result.Message = err.Error()
|
||||||
}
|
}
|
||||||
@@ -162,7 +161,7 @@ func decideSnapshotV1(snapshot, oldSnapshot *volumesnapshotv1.VolumeSnapshot, is
|
|||||||
}
|
}
|
||||||
// Enforce strict validation for CREATE requests. Immutable checks don't apply for CREATE requests.
|
// 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.
|
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||||
if err := utils.ValidateV1Snapshot(snapshot); err != nil {
|
if err := ValidateV1Snapshot(snapshot); err != nil {
|
||||||
reviewResponse.Allowed = false
|
reviewResponse.Allowed = false
|
||||||
reviewResponse.Result.Message = err.Error()
|
reviewResponse.Result.Message = err.Error()
|
||||||
}
|
}
|
||||||
@@ -181,7 +180,7 @@ func decideSnapshotContentV1beta1(snapcontent, oldSnapcontent *volumesnapshotv1b
|
|||||||
// Which allows the remover of finalizers and therefore deletion of this object
|
// 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
|
// 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.
|
// The empty struct value. Instead check the operation type.
|
||||||
if err := utils.ValidateV1Beta1SnapshotContent(oldSnapcontent); err != nil {
|
if err := ValidateV1Beta1SnapshotContent(oldSnapcontent); err != nil {
|
||||||
return reviewResponse
|
return reviewResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -194,7 +193,7 @@ func decideSnapshotContentV1beta1(snapcontent, oldSnapcontent *volumesnapshotv1b
|
|||||||
}
|
}
|
||||||
// Enforce strict validation for all CREATE requests. Immutable checks don't apply for CREATE requests.
|
// 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.
|
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||||
if err := utils.ValidateV1Beta1SnapshotContent(snapcontent); err != nil {
|
if err := ValidateV1Beta1SnapshotContent(snapcontent); err != nil {
|
||||||
reviewResponse.Allowed = false
|
reviewResponse.Allowed = false
|
||||||
reviewResponse.Result.Message = err.Error()
|
reviewResponse.Result.Message = err.Error()
|
||||||
}
|
}
|
||||||
@@ -217,7 +216,7 @@ func decideSnapshotContentV1(snapcontent, oldSnapcontent *volumesnapshotv1.Volum
|
|||||||
}
|
}
|
||||||
// Enforce strict validation for all CREATE requests. Immutable checks don't apply for CREATE requests.
|
// 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.
|
// Enforce strict validation for UPDATE requests where old is valid and passes immutability check.
|
||||||
if err := utils.ValidateV1SnapshotContent(snapcontent); err != nil {
|
if err := ValidateV1SnapshotContent(snapcontent); err != nil {
|
||||||
reviewResponse.Allowed = false
|
reviewResponse.Allowed = false
|
||||||
reviewResponse.Result.Message = err.Error()
|
reviewResponse.Result.Message = err.Error()
|
||||||
}
|
}
|
||||||
|
105
pkg/validation-webhook/validation.go
Normal file
105
pkg/validation-webhook/validation.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2021 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package webhook
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
crdv1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||||
|
crdv1beta1 "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1beta1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ValidateV1Snapshot 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 ValidateV1Snapshot(snapshot *crdv1.VolumeSnapshot) error {
|
||||||
|
if snapshot == nil {
|
||||||
|
return fmt.Errorf("VolumeSnapshot is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
vscname := snapshot.Spec.VolumeSnapshotClassName
|
||||||
|
if vscname != nil && *vscname == "" {
|
||||||
|
return fmt.Errorf("Spec.VolumeSnapshotClassName must not be the empty string")
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
func ValidateV1SnapshotContent(snapcontent *crdv1.VolumeSnapshotContent) error {
|
||||||
|
if snapcontent == nil {
|
||||||
|
return fmt.Errorf("VolumeSnapshotContent is nil")
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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