Skip to content

Commit 2705a24

Browse files
committed
Add a knob to initiate webhook deletion checks for ip resources
Signed-off-by: Cyclinder Kuo <[email protected]>
1 parent 285e341 commit 2705a24

File tree

13 files changed

+225
-80
lines changed

13 files changed

+225
-80
lines changed

charts/spiderpool/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ helm install spiderpool spiderpool/spiderpool --wait --namespace kube-system \
343343
| `spiderpoolController.healthChecking.readinessProbe.failureThreshold` | the failure threshold of startup probe for spiderpoolController health checking | `3` |
344344
| `spiderpoolController.healthChecking.readinessProbe.periodSeconds` | the period seconds of startup probe for spiderpoolController health checking | `10` |
345345
| `spiderpoolController.webhookPort` | the http port for spiderpoolController webhook | `5722` |
346+
| `spiderpoolController.enableValidatingResourcesDeletedWebhook` | enable validating resources deleted webhook for spiderpoolController | `false` |
346347
| `spiderpoolController.podResourceInject.enabled` | enable pod resource inject | `false` |
347348
| `spiderpoolController.podResourceInject.namespacesExclude` | exclude the namespaces of the pod resource inject | `["kube-system","spiderpool","metallb-system","istio-system"]` |
348349
| `spiderpoolController.podResourceInject.namespacesInclude` | include the namespaces of the pod resource inject, empty means all namespaces but exclude the namespaces in namespacesExclude, not empty means only include the namespaces in namespacesInclude | `[]` |

charts/spiderpool/templates/configmap.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ data:
2424
enableAutoPoolForApplication: {{ .Values.ipam.spiderSubnet.autoPool.enable }}
2525
enableIPConflictDetection: {{ .Values.ipam.enableIPConflictDetection }}
2626
enableGatewayDetection: {{ .Values.ipam.enableGatewayDetection }}
27+
enableValidatingResourcesDeletedWebhook: {{ .Values.spiderpoolController.enableValidatingResourcesDeletedWebhook }}
2728
{{- if and .Values.ipam.spiderSubnet.enable .Values.ipam.spiderSubnet.autoPool.enable }}
2829
clusterSubnetDefaultFlexibleIPNumber: {{ .Values.ipam.spiderSubnet.autoPool.defaultRedundantIPNumber }}
2930
{{- else}}

charts/spiderpool/values.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,9 @@ spiderpoolController:
670670
## @param spiderpoolController.webhookPort the http port for spiderpoolController webhook
671671
webhookPort: 5722
672672

673+
## @param spiderpoolController.enableValidatingResourcesDeletedWebhook enable validating resources deleted webhook for spiderpoolController
674+
enableValidatingResourcesDeletedWebhook: false
675+
673676
podResourceInject:
674677
## @param spiderpoolController.podResourceInject.enabled enable pod resource inject
675678
enabled: false

cmd/spiderpool-controller/cmd/daemon.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,12 @@ func initControllerServiceManagers(ctx context.Context) {
338338

339339
logger.Debug("Begin to set up IPPool webhook")
340340
if err := (&ippoolmanager.IPPoolWebhook{
341-
Client: controllerContext.CRDManager.GetClient(),
342-
APIReader: controllerContext.CRDManager.GetAPIReader(),
343-
EnableIPv4: controllerContext.Cfg.EnableIPv4,
344-
EnableIPv6: controllerContext.Cfg.EnableIPv6,
345-
EnableSpiderSubnet: controllerContext.Cfg.EnableSpiderSubnet,
341+
Client: controllerContext.CRDManager.GetClient(),
342+
APIReader: controllerContext.CRDManager.GetAPIReader(),
343+
EnableIPv4: controllerContext.Cfg.EnableIPv4,
344+
EnableIPv6: controllerContext.Cfg.EnableIPv6,
345+
EnableSpiderSubnet: controllerContext.Cfg.EnableSpiderSubnet,
346+
EnableValidatingResourcesDeletedWebhook: controllerContext.Cfg.EnableValidatingResourcesDeletedWebhook,
346347
}).SetupWebhookWithManager(controllerContext.CRDManager); err != nil {
347348
logger.Fatal(err.Error())
348349
}
@@ -368,10 +369,11 @@ func initControllerServiceManagers(ctx context.Context) {
368369

369370
logger.Debug("Begin to set up Subnet webhook")
370371
if err := (&subnetmanager.SubnetWebhook{
371-
Client: controllerContext.CRDManager.GetClient(),
372-
APIReader: controllerContext.CRDManager.GetAPIReader(),
373-
EnableIPv4: controllerContext.Cfg.EnableIPv4,
374-
EnableIPv6: controllerContext.Cfg.EnableIPv6,
372+
Client: controllerContext.CRDManager.GetClient(),
373+
APIReader: controllerContext.CRDManager.GetAPIReader(),
374+
EnableIPv4: controllerContext.Cfg.EnableIPv4,
375+
EnableIPv6: controllerContext.Cfg.EnableIPv6,
376+
EnableValidatingResourcesDeletedWebhook: controllerContext.Cfg.EnableValidatingResourcesDeletedWebhook,
375377
}).SetupWebhookWithManager(controllerContext.CRDManager); err != nil {
376378
logger.Fatal(err.Error())
377379
}

pkg/ippoolmanager/ippool_webhook.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package ippoolmanager
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"strings"
1011

1112
"go.uber.org/zap"
@@ -30,9 +31,10 @@ type IPPoolWebhook struct {
3031
Client client.Client
3132
APIReader client.Reader
3233

33-
EnableIPv4 bool
34-
EnableIPv6 bool
35-
EnableSpiderSubnet bool
34+
EnableIPv4 bool
35+
EnableIPv6 bool
36+
EnableSpiderSubnet bool
37+
EnableValidatingResourcesDeletedWebhook bool
3638
}
3739

3840
func (iw *IPPoolWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error {
@@ -136,6 +138,10 @@ func (iw *IPPoolWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runt
136138

137139
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type.
138140
func (iw *IPPoolWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
141+
if !iw.EnableValidatingResourcesDeletedWebhook {
142+
return nil, nil
143+
}
144+
139145
ipPool := obj.(*spiderpoolv2beta1.SpiderIPPool)
140146

141147
logger := WebhookLogger.Named("Validating").With(
@@ -149,7 +155,7 @@ func (iw *IPPoolWebhook) ValidateDelete(ctx context.Context, obj runtime.Object)
149155
return nil, apierrors.NewForbidden(
150156
schema.GroupResource{Group: constant.SpiderpoolAPIGroup, Resource: "spiderippools"},
151157
ipPool.Name,
152-
errors.New("cannot delete an IPPool with allocated IPs"),
158+
fmt.Errorf("cannot delete an IPPool with allocated IPs(%v)", *ipPool.Status.AllocatedIPCount),
153159
)
154160
}
155161
return nil, nil

pkg/ippoolmanager/ippool_webhook_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2152,6 +2152,32 @@ var _ = Describe("IPPoolWebhook", Label("ippool_webhook_test"), func() {
21522152
Expect(err).NotTo(HaveOccurred())
21532153
Expect(warns).To(BeNil())
21542154
})
2155+
2156+
It("should allow deletion when EnableValidatingResourcesDeletedWebhook is false", func() {
2157+
ipPoolWebhook.EnableValidatingResourcesDeletedWebhook = false
2158+
warns, err := ipPoolWebhook.ValidateDelete(ctx, ipPoolT)
2159+
Expect(err).NotTo(HaveOccurred())
2160+
Expect(warns).To(BeNil())
2161+
})
2162+
2163+
It("should prevent deletion when IPPool has allocated IPs", func() {
2164+
ipPoolWebhook.EnableValidatingResourcesDeletedWebhook = true
2165+
allocatedIPs := int64(5)
2166+
ipPoolT.Status.AllocatedIPCount = &allocatedIPs
2167+
warns, err := ipPoolWebhook.ValidateDelete(ctx, ipPoolT)
2168+
Expect(err).To(HaveOccurred())
2169+
Expect(apierrors.IsForbidden(err)).To(BeTrue())
2170+
Expect(warns).To(BeNil())
2171+
})
2172+
2173+
It("should allow deletion when IPPool has no allocated IPs", func() {
2174+
ipPoolWebhook.EnableValidatingResourcesDeletedWebhook = true
2175+
allocatedIPs := int64(0)
2176+
ipPoolT.Status.AllocatedIPCount = &allocatedIPs
2177+
warns, err := ipPoolWebhook.ValidateDelete(ctx, ipPoolT)
2178+
Expect(err).NotTo(HaveOccurred())
2179+
Expect(warns).To(BeNil())
2180+
})
21552181
})
21562182
})
21572183
})

pkg/podmanager/pod_manager_suite_test.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,19 @@
44
package podmanager_test
55

66
import (
7+
"net/http"
78
"testing"
89

910
. "github.com/onsi/ginkgo/v2"
1011
. "github.com/onsi/gomega"
12+
"github.com/spidernet-io/spiderpool/pkg/podmanager"
1113
corev1 "k8s.io/api/core/v1"
1214
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1315
"k8s.io/apimachinery/pkg/runtime"
1416
k8sscheme "k8s.io/client-go/kubernetes/scheme"
1517
k8stesting "k8s.io/client-go/testing"
1618
"sigs.k8s.io/controller-runtime/pkg/client"
1719
"sigs.k8s.io/controller-runtime/pkg/client/fake"
18-
19-
"github.com/spidernet-io/spiderpool/pkg/podmanager"
2020
)
2121

2222
var scheme *runtime.Scheme
@@ -58,4 +58,5 @@ var _ = BeforeSuite(func() {
5858
fakeAPIReader,
5959
)
6060
Expect(err).NotTo(HaveOccurred())
61+
6162
})

pkg/podmanager/pod_webhook.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ type PodWebhook interface {
3030
admission.CustomValidator
3131
}
3232

33-
type podWebhook struct {
33+
type PWebhook struct {
3434
spiderClient crdclientset.Interface
3535
}
3636

@@ -46,7 +46,7 @@ func InitPodWebhook(mgr ctrl.Manager) error {
4646
return err
4747
}
4848

49-
pw := &podWebhook{
49+
pw := &PWebhook{
5050
spiderClient: spiderClient,
5151
}
5252

@@ -67,7 +67,7 @@ func InitPodWebhook(mgr ctrl.Manager) error {
6767
// - obj: The runtime object (expected to be a Pod)
6868
//
6969
// Returns an error if defaulting fails.
70-
func (pw *podWebhook) Default(ctx context.Context, obj runtime.Object) error {
70+
func (pw *PWebhook) Default(ctx context.Context, obj runtime.Object) error {
7171
logger := logutils.FromContext(ctx)
7272
pod := obj.(*corev1.Pod)
7373
mutateLogger := logger.Named("PodMutating").With(
@@ -97,18 +97,18 @@ func (pw *podWebhook) Default(ctx context.Context, obj runtime.Object) error {
9797

9898
// ValidateCreate implements the validation webhook for pod creation.
9999
// Currently, it performs no validation and always returns nil.
100-
func (pw *podWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
100+
func (pw *PWebhook) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
101101
return nil, nil
102102
}
103103

104104
// ValidateUpdate implements the validation webhook for pod updates.
105105
// Currently, it performs no validation and always returns nil.
106-
func (pw *podWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
106+
func (pw *PWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
107107
return nil, nil
108108
}
109109

110110
// ValidateDelete implements the validation webhook for pod deletion.
111111
// Currently, it performs no validation and always returns nil.
112-
func (pw *podWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
112+
func (pw *PWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
113113
return nil, nil
114114
}

pkg/podmanager/pod_webhook_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2025 Authors of spidernet-io
2+
// SPDX-License-Identifier: Apache-2.0
3+
package podmanager_test
4+
5+
// import (
6+
// "context"
7+
// "testing"
8+
9+
// . "github.com/onsi/ginkgo/v2"
10+
// . "github.com/onsi/gomega"
11+
// "github.com/spidernet-io/spiderpool/pkg/podmanager"
12+
// corev1 "k8s.io/api/core/v1"
13+
// metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
14+
// "k8s.io/client-go/rest"
15+
// "sigs.k8s.io/controller-runtime/pkg/client"
16+
// "sigs.k8s.io/controller-runtime/pkg/manager"
17+
// )
18+
19+
// func TestPodWebhook(t *testing.T) {
20+
// RegisterFailHandler(Fail)
21+
// RunSpecs(t, "Pod Webhook Suite")
22+
// }
23+
24+
// var _ = Describe("Pod Webhook", Label("pod_webhook_test"), func() {
25+
// var (
26+
// err error
27+
// mgr manager.Manager
28+
// ctx context.Context
29+
// pod *corev1.Pod
30+
// )
31+
32+
// BeforeEach(func() {
33+
// // Create a new manager
34+
35+
// mgr, err = manager.New(cfg, manager.Options{
36+
// NewClient: func(config *rest.Config, options client.Options) (client.Client, error) {
37+
// return nil, nil
38+
// },
39+
// })
40+
// Expect(err).NotTo(HaveOccurred())
41+
42+
// pod = &corev1.Pod{
43+
// ObjectMeta: metav1.ObjectMeta{
44+
// Namespace: "default",
45+
// GenerateName: "test-pod",
46+
// Annotations: map[string]string{},
47+
// },
48+
// }
49+
// })
50+
51+
// Context("InitPodWebhook", func() {
52+
// It("should initialize without error", func() {
53+
// err := podmanager.InitPodWebhook(mgr)
54+
// Expect(err).NotTo(HaveOccurred())
55+
// })
56+
// })
57+
58+
// Context("Default", func() {
59+
// It("should not inject resources if no annotations are present", func() {
60+
// pw := &podmanager.PWebhook{}
61+
// err := pw.Default(ctx, pod)
62+
// Expect(err).NotTo(HaveOccurred())
63+
// })
64+
65+
// It("should inject resources if annotations are present", func() {
66+
// pod.Annotations["spiderpool.io/pod-resource-inject"] = "true"
67+
// pw := podmanager.PWebhook{}
68+
// err := pw.Default(ctx, pod)
69+
// Expect(err).NotTo(HaveOccurred())
70+
// })
71+
// })
72+
// })

pkg/subnetmanager/subnet_webhook.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package subnetmanager
66
import (
77
"context"
88
"errors"
9+
"fmt"
910
"strings"
1011

1112
"go.uber.org/zap"
@@ -30,8 +31,9 @@ type SubnetWebhook struct {
3031
Client client.Client
3132
APIReader client.Reader
3233

33-
EnableIPv4 bool
34-
EnableIPv6 bool
34+
EnableIPv4 bool
35+
EnableIPv6 bool
36+
EnableValidatingResourcesDeletedWebhook bool
3537
}
3638

3739
func (sw *SubnetWebhook) SetupWebhookWithManager(mgr ctrl.Manager) error {
@@ -135,6 +137,10 @@ func (sw *SubnetWebhook) ValidateUpdate(ctx context.Context, oldObj, newObj runt
135137

136138
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type.
137139
func (sw *SubnetWebhook) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
140+
if !sw.EnableValidatingResourcesDeletedWebhook {
141+
return nil, nil
142+
}
143+
138144
subnet := obj.(*spiderpoolv2beta1.SpiderSubnet)
139145

140146
logger := WebhookLogger.Named("Validating").With(
@@ -148,7 +154,7 @@ func (sw *SubnetWebhook) ValidateDelete(ctx context.Context, obj runtime.Object)
148154
return nil, apierrors.NewForbidden(
149155
schema.GroupResource{Group: constant.SpiderpoolAPIGroup, Resource: "spidersubnets"},
150156
subnet.Name,
151-
errors.New("cannot delete an Subnet with allocated IPs"),
157+
fmt.Errorf("cannot delete an Subnet with allocated IPs(%v)", *subnet.Status.AllocatedIPCount),
152158
)
153159
}
154160
return nil, nil

0 commit comments

Comments
 (0)