Skip to content

Commit

Permalink
add: DeletionProtection mechanism reject Namespace deletion when PVCs…
Browse files Browse the repository at this point in the history
… in Bound status are included under NS (openkruise#1228) (openkruise#1228)

Signed-off-by: kevin1689 <[email protected]>
  • Loading branch information
kevin1689-cloud authored and diannaowa committed Jun 2, 2023
1 parent 8cdeb99 commit 103f2c6
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 1 deletion.
18 changes: 17 additions & 1 deletion pkg/webhook/util/deletionprotection/deletion_protection.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (

policyv1alpha1 "github.com/openkruise/kruise/apis/policy/v1alpha1"
"github.com/openkruise/kruise/pkg/features"
utilclient "github.com/openkruise/kruise/pkg/util/client"
utilfeature "github.com/openkruise/kruise/pkg/util/feature"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
Expand Down Expand Up @@ -58,7 +59,7 @@ func ValidateNamespaceDeletion(c client.Client, namespace *v1.Namespace) error {
return fmt.Errorf("forbidden by ResourcesProtectionDeletion for %s=%s", policyv1alpha1.DeletionProtectionKey, val)
case policyv1alpha1.DeletionProtectionTypeCascading:
pods := v1.PodList{}
if err := c.List(context.TODO(), &pods, client.InNamespace(namespace.Name)); err != nil {
if err := c.List(context.TODO(), &pods, client.InNamespace(namespace.Name), utilclient.DisableDeepCopy); err != nil {
return fmt.Errorf("forbidden by ResourcesProtectionDeletion for list pods error: %v", err)
}
var activeCount int
Expand All @@ -71,6 +72,21 @@ func ValidateNamespaceDeletion(c client.Client, namespace *v1.Namespace) error {
if activeCount > 0 {
return fmt.Errorf("forbidden by ResourcesProtectionDeletion for %s=%s and active pods %d>0", policyv1alpha1.DeletionProtectionKey, val, activeCount)
}

pvcs := v1.PersistentVolumeClaimList{}
if err := c.List(context.TODO(), &pvcs, client.InNamespace(namespace.Name), utilclient.DisableDeepCopy); err != nil {
return fmt.Errorf("forbidden by ResourcesProtectionDeletion for list pvc error: %v", err)
}
var boundCount int
for i := range pvcs.Items {
pvc := &pvcs.Items[i]
if pvc.DeletionTimestamp == nil && pvc.Status.Phase == v1.ClaimBound {
boundCount++
}
}
if boundCount > 0 {
return fmt.Errorf("forbidden by ResourcesProtectionDeletion for %s=%s and \"Bound\" status pvc %d>0", policyv1alpha1.DeletionProtectionKey, val, boundCount)
}
default:
}
return nil
Expand Down
73 changes: 73 additions & 0 deletions test/e2e/policy/deletionprotection.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime/schema"
Expand Down Expand Up @@ -111,6 +112,78 @@ var _ = SIGDescribe("DeletionProtection", func() {
return cs.Status.Replicas
}, 5*time.Second, time.Second).Should(gomega.Equal(int32(0)))

ginkgo.By("Create a PVC in this namespace")
pvcName := "pvc-" + randStr
storageClassName := ""
pvc := &v1.PersistentVolumeClaim{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns.Name,
Name: pvcName,
},
Spec: v1.PersistentVolumeClaimSpec{
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
Resources: v1.ResourceRequirements{
Requests: v1.ResourceList{
v1.ResourceStorage: resource.MustParse("1Gi"),
},
},
StorageClassName: &storageClassName,
},
}
_, err = c.CoreV1().PersistentVolumeClaims(ns.Name).Create(context.TODO(), pvc, metav1.CreateOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())

ginkgo.By("Create a PV bound to the PVC just created")
pvName := "pv-" + randStr
pv := &v1.PersistentVolume{
ObjectMeta: metav1.ObjectMeta{
Name: pvName,
},
Spec: v1.PersistentVolumeSpec{
Capacity: v1.ResourceList{
v1.ResourceStorage: resource.MustParse("1Gi"),
},
AccessModes: []v1.PersistentVolumeAccessMode{
v1.ReadWriteOnce,
},
StorageClassName: "",
ClaimRef: &v1.ObjectReference{
Namespace: ns.Name,
Name: pvcName,
},
PersistentVolumeSource: v1.PersistentVolumeSource{
HostPath: &v1.HostPathVolumeSource{
Path: "/mnt",
},
},
},
}
_, err = framework.CreatePV(c, pv)
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Eventually(func() bool {
pvc, err := c.CoreV1().PersistentVolumeClaims(ns.Name).Get(context.TODO(), pvcName, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
return pvc.Status.Phase == v1.ClaimBound
}, 15*time.Second, time.Second).Should(gomega.BeTrue())

ginkgo.By("Delete the namespace should be rejected")
err = c.CoreV1().Namespaces().Delete(context.TODO(), ns.Name, metav1.DeleteOptions{})
gomega.Expect(err).To(gomega.HaveOccurred())
gomega.Expect(err.Error()).Should(gomega.ContainSubstring(deleteForbiddenMessage))

ginkgo.By("Delete the PV bounded to PVC")
err = c.CoreV1().PersistentVolumes().Delete(context.TODO(), pvName, metav1.DeleteOptions{})
_, err = c.CoreV1().PersistentVolumes().Patch(context.TODO(), pvName, types.StrategicMergePatchType,
[]byte(`{"metadata":{"finalizers":null}}`), metav1.PatchOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
gomega.Eventually(func() bool {
pvc, err := c.CoreV1().PersistentVolumeClaims(ns.Name).Get(context.TODO(), pvcName, metav1.GetOptions{})
gomega.Expect(err).NotTo(gomega.HaveOccurred())
return pvc.Status.Phase != v1.ClaimBound
}, 5*time.Second, time.Second).Should(gomega.BeTrue())

time.Sleep(time.Second)
ginkgo.By("Delete the namespace successfully")
err = c.CoreV1().Namespaces().Delete(context.TODO(), ns.Name, metav1.DeleteOptions{})
Expand Down

0 comments on commit 103f2c6

Please sign in to comment.