Repository: spidernet-io/spiderpool
Pull request #2185 (2023-08-14 03:24:35)

feature : SpiderSubnet could take over orphan IPPool
In the past version, it's invalid to create a orphan IPPool in SpiderSubnet feature, which means you have to create a SpiderSubnet resource firstly.

Now, I make it compatible to create orphan SpiderIPPool resource with SpiderSubnet feature

Signed-off-by: Icarus9913 [icaruswu66@qq.com](mailto:icaruswu66@qq.com)



codecov[bot]: ## [Codecov](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io) Report
> Merging [#2185](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io) (8082775) into [main](https://app.codecov.io/gh/spidernet-io/spiderpool/commit/e09217741696afce0ef2e5600c6634c4945da9c2?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io) (e092177) will **decrease** coverage by `0.12%`.
> Report is 2 commits behind head on main.
> The diff coverage is `55.55%`.

[![Impacted file tree graph](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185/graphs/tree.svg?width=650&height=150&src=pr&token=YKXY2E4Q8G&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io)](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io)

```diff
@@            Coverage Diff             @@
##             main    #2185      +/-   ##
==========================================
- Coverage   81.17%   81.05%   -0.12%     
==========================================
  Files          49       49              
  Lines        5210     5221      +11     
==========================================
+ Hits         4229     4232       +3     
- Misses        823      828       +5     
- Partials      158      161       +3     
```

| Flag | Coverage Δ | |
|---|---|---|
| unittests | `81.05% <55.55%> (-0.12%)` | :arrow_down: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files Changed](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io) | Coverage Δ | |
|---|---|---|
| [pkg/ippoolmanager/ippool\_validate.go](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io#diff-cGtnL2lwcG9vbG1hbmFnZXIvaXBwb29sX3ZhbGlkYXRlLmdv) | `95.25% <ø> (-0.05%)` | :arrow_down: |
| [pkg/subnetmanager/subnet\_informer.go](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io#diff-cGtnL3N1Ym5ldG1hbmFnZXIvc3VibmV0X2luZm9ybWVyLmdv) | `44.62% <ø> (ø)` | |
| [pkg/subnetmanager/subnet\_validate.go](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io#diff-cGtnL3N1Ym5ldG1hbmFnZXIvc3VibmV0X3ZhbGlkYXRlLmdv) | `95.19% <52.94%> (-3.39%)` | :arrow_down: |
| [pkg/ippoolmanager/ippool\_subnet\_validate.go](https://app.codecov.io/gh/spidernet-io/spiderpool/pull/2185?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=spidernet-io#diff-cGtnL2lwcG9vbG1hbmFnZXIvaXBwb29sX3N1Ym5ldF92YWxpZGF0ZS5nbw==) | `77.77% <100.00%> (-0.80%)` | :arrow_down: |

weizhoublue: add  E2E in test/doc  and  modify the usage md
weizhoublue: remove the values in chart
weizhoublue: add E2E in test/doc

Patch:
diff --git a/.github/workflows/lint-license.yaml b/.github/workflows/lint-license.yaml
index 35a46d014..159986420 100644
--- a/.github/workflows/lint-license.yaml
+++ b/.github/workflows/lint-license.yaml
@@ -50,4 +50,4 @@ jobs:
 
       - name: Check License Header
         id: checklicense
-        uses: apache/skywalking-eyes@v0.5.0
+        uses: apache/skywalking-eyes@v0.4.0
diff --git a/Makefile b/Makefile
index ba59fb371..54ff0e6aa 100644
--- a/Makefile
+++ b/Makefile
@@ -308,10 +308,6 @@ e2e_init:
 
 .PHONY: e2e_init_underlay
 e2e_init_underlay:
-	$(QUIET)  make e2e_init -e INSTALL_OVERLAY_CNI=false -e E2E_SPIDERPOOL_ENABLE_SUBNET=false
-
-.PHONY: e2e_init_underlay_subnet
-e2e_init_underlay_subnet:
 	$(QUIET)  make e2e_init -e INSTALL_OVERLAY_CNI=false -e E2E_SPIDERPOOL_ENABLE_SUBNET=true
 
 .PHONY: e2e_init_calico
@@ -329,10 +325,6 @@ e2e_test:
 
 .PHONY: e2e_test_underlay
 e2e_test_underlay:
-	$(QUIET)  make e2e_test -e INSTALL_OVERLAY_CNI=false -e E2E_SPIDERPOOL_ENABLE_SUBNET=false
-
-.PHONY: e2e_test_underlay_subnet
-e2e_test_underlay_subnet:
 	$(QUIET)  make e2e_test -e INSTALL_OVERLAY_CNI=false -e E2E_SPIDERPOOL_ENABLE_SUBNET=true
 
 .PHONY: e2e_test_calico
diff --git a/docs/develop/contributing.md b/docs/develop/contributing.md
index 6431eec62..a52146ab1 100644
--- a/docs/develop/contributing.md
+++ b/docs/develop/contributing.md
@@ -39,12 +39,11 @@ run the following command to check unitest
 
     before start the test, you shoud know there are test scenes as following 
 
-    | kind                                     | setup cluster                    | test                          |
-    |------------------------------------------|----------------------------------|-------------------------------|
-    | test underlay CNI without subnet feature | make    e2e_init_underlay        | make e2e_test_underlay        |
-    | test underlay CNI with subnet feature    | make    e2e_init_underlay_subnet | make e2e_test_underlay_subnet |
-    | test overlay CNI for calico              | make    e2e_init_overlay_calico  | make e2e_test_overlay_calico  |
-    | test overlay CNI for cilium              | make    e2e_init_overlay_cilium  | make e2e_test_overlay_cilium  |
+    | kind                                  | setup cluster                    | test                          |
+    |---------------------------------------|----------------------------------|-------------------------------|
+    | test underlay CNI                     | make    e2e_init_underlay        | make e2e_test_underlay        |
+    | test overlay CNI for calico           | make    e2e_init_overlay_calico  | make e2e_test_overlay_calico  |
+    | test overlay CNI for cilium           | make    e2e_init_overlay_cilium  | make e2e_test_overlay_cilium  |
 
     if you are in China, it could add `-e E2E_CHINA_IMAGE_REGISTRY=true` to pull images from china image registry, add `-e HTTP_PROXY=http://${ADDR}` to get chart
 
@@ -83,9 +82,6 @@ run the following command to check unitest
         # it pulls images from another image registry and just use http proxy to pull chart 
         $ make e2e_init_underlay  -e E2E_CHINA_IMAGE_REGISTRY=true -e HTTP_PROXY=http://${ADDR}
 
-        # setup cluster with subnet feature
-        $ make e2e_init_underlay_subnet -e E2E_CHINA_IMAGE_REGISTRY=true -e HTTP_PROXY=http://${ADDR}
-
         # setup cluster with calico cni
         $ make e2e_init_calico -e E2E_CHINA_IMAGE_REGISTRY=true -e HTTP_PROXY=http://${ADDR}
 
@@ -113,9 +109,6 @@ run the following command to check unitest
         # example: run a case until fails
         $ make e2e_test_underlay -e GINKGO_OPTION=" --label-filter=CaseLabel --until-it-fails "
 
-        # Run all e2e tests for enableSpiderSubnet=false cluster
-        $ make e2e_test_underlay_subnet 
-
         # Run all e2e tests for enableSpiderSubnet=false cluster
         $ make e2e_test_calico
 
diff --git a/docs/usage/install/underlay/get-started-kind-zh_CN.md b/docs/usage/install/underlay/get-started-kind-zh_CN.md
index 1d1dcae95..f155d4892 100644
--- a/docs/usage/install/underlay/get-started-kind-zh_CN.md
+++ b/docs/usage/install/underlay/get-started-kind-zh_CN.md
@@ -26,18 +26,12 @@ Kind 是一个使用 Docker 容器节点运行本地 Kubernetes 集群的工具
 
 如果您在中国大陆，安装时可以额外指定参数 `-e E2E_CHINA_IMAGE_REGISTRY=true` ，以帮助您更快的拉取镜像。
 
-### 安装不带子网功能的 Spiderpool 在 Underlay CNI（Macvlan） 集群
+### 安装 Spiderpool 在 Underlay CNI（Macvlan） 集群
   
   ```bash
   ~# make e2e_init_underlay -e E2E_SPIDERPOOL_TAG=$SPIDERPOOL_LATEST_IMAGE_TAG
   ```
 
-### 安装具有子网功能的 Spiderpool 在 Underlay CNI（Macvlan）集群
-
-  ```bash
-  ~# make e2e_init_underlay_subnet -e E2E_SPIDERPOOL_TAG=$SPIDERPOOL_LATEST_IMAGE_TAG
-  ```
-
 ### 安装 Spiderpool 在 Calico Overlay CNI 集群
 
   ```bash
diff --git a/docs/usage/install/underlay/get-started-kind.md b/docs/usage/install/underlay/get-started-kind.md
index 57449ad7b..6982b761c 100644
--- a/docs/usage/install/underlay/get-started-kind.md
+++ b/docs/usage/install/underlay/get-started-kind.md
@@ -26,18 +26,12 @@ Kind is a tool for running local Kubernetes clusters using Docker container "nod
 
 If you are mainland user who is not available to access ghcr.io, Additional parameter `-e E2E_CHINA_IMAGE_REGISTRY=true` can be specified during installation to help you pull images faster.
 
-### Install Spiderpool without subnet function in Underlay CNI (Macvlan) cluster
+### Install Spiderpool in Underlay CNI (Macvlan) cluster
 
   ```bash
   ~# make e2e_init_underlay -e E2E_SPIDERPOOL_TAG=$SPIDERPOOL_LATEST_IMAGE_TAG
   ```
 
-### Install Spiderpool with subnets in Underlay CNI (Macvlan) cluster
-
-  ```bash
-  ~# make e2e_init_underlay_subnet -e E2E_SPIDERPOOL_TAG=$SPIDERPOOL_LATEST_IMAGE_TAG
-  ```
-
 ### Install Spiderpool on Calico Overlay CNI cluster
 
   ```bash
diff --git a/pkg/ippoolmanager/ippool_subnet_validate.go b/pkg/ippoolmanager/ippool_subnet_validate.go
index 00ba61772..cc68fe753 100644
--- a/pkg/ippoolmanager/ippool_subnet_validate.go
+++ b/pkg/ippoolmanager/ippool_subnet_validate.go
@@ -51,10 +51,7 @@ func (iw *IPPoolWebhook) validateUpdateIPPoolWhileEnableSpiderSubnet(ctx context
 func (iw *IPPoolWebhook) validateSubnetTotalIPsContainsIPPoolTotalIPs(ctx context.Context, ipPool *spiderpoolv2beta1.SpiderIPPool) *field.Error {
 	owner := metav1.GetControllerOf(ipPool)
 	if owner == nil {
-		return field.Forbidden(
-			subnetField,
-			fmt.Sprintf("orphan IPPool, must be controlled by Subnet with the same 'spec.subnet' %s", ipPool.Spec.Subnet),
-		)
+		return nil
 	}
 
 	poolTotalIPs, err := spiderpoolip.AssembleTotalIPs(*ipPool.Spec.IPVersion, ipPool.Spec.IPs, ipPool.Spec.ExcludeIPs)
diff --git a/pkg/ippoolmanager/ippool_validate.go b/pkg/ippoolmanager/ippool_validate.go
index c02e2aa9b..92b4af093 100644
--- a/pkg/ippoolmanager/ippool_validate.go
+++ b/pkg/ippoolmanager/ippool_validate.go
@@ -189,10 +189,6 @@ func (iw *IPPoolWebhook) validateIPPoolCIDR(ctx context.Context, ipPool *spiderp
 		)
 	}
 
-	if iw.EnableSpiderSubnet {
-		return nil
-	}
-
 	// TODO(iiiceoo): Use label selector.
 	var ipPoolList spiderpoolv2beta1.SpiderIPPoolList
 	if err := iw.APIReader.List(ctx, &ipPoolList); err != nil {
diff --git a/pkg/subnetmanager/subnet_informer.go b/pkg/subnetmanager/subnet_informer.go
index 095351f32..05d531205 100644
--- a/pkg/subnetmanager/subnet_informer.go
+++ b/pkg/subnetmanager/subnet_informer.go
@@ -218,6 +218,7 @@ func (sc *SubnetController) enqueueSubnetOnUpdate(oldObj, newObj interface{}) {
 	logger.Debug(messageEnqueueSubnet)
 }
 
+// enqueueSubnetOnIPPoolChange receives the IPPool resources events
 func (sc *SubnetController) enqueueSubnetOnIPPoolChange(obj interface{}) {
 	ipPool := obj.(*spiderpoolv2beta1.SpiderIPPool)
 	ownerSubnet, ok := ipPool.Labels[constant.LabelIPPoolOwnerSpiderSubnet]
@@ -384,6 +385,7 @@ func (sc *SubnetController) syncHandler(ctx context.Context, subnetName string)
 	return nil
 }
 
+// syncMetadata add "ipam.spidernet.io/subnet-cidr" label for the SpiderSubnet object
 func (sc *SubnetController) syncMetadata(ctx context.Context, subnet *spiderpoolv2beta1.SpiderSubnet) error {
 	cidr, err := spiderpoolip.CIDRToLabelValue(*subnet.Spec.IPVersion, subnet.Spec.Subnet)
 	if err != nil {
@@ -406,6 +408,7 @@ func (sc *SubnetController) syncMetadata(ctx context.Context, subnet *spiderpool
 	return nil
 }
 
+// syncControllerSubnet would set ownerReference and add "ipam.spidernet.io/owner-spider-subnet" label for the previous orphan IPPool
 func (sc *SubnetController) syncControllerSubnet(ctx context.Context, subnet *spiderpoolv2beta1.SpiderSubnet) error {
 	ipPools, err := sc.IPPoolsLister.List(labels.Everything())
 	if err != nil {
diff --git a/pkg/subnetmanager/subnet_validate.go b/pkg/subnetmanager/subnet_validate.go
index 770500bdd..84effa05c 100644
--- a/pkg/subnetmanager/subnet_validate.go
+++ b/pkg/subnetmanager/subnet_validate.go
@@ -211,6 +211,49 @@ func (sw *SubnetWebhook) validateSubnetCIDR(ctx context.Context, subnet *spiderp
 		}
 	}
 
+	return sw.validateOrphanIPPool(ctx, subnet)
+}
+
+// validateOrphanIPPool will check the SpiderSubnet.Spec.Subnet whether overlaps with the cluster orphan SpiderIPPool.Spec.Subnet.
+// And we also require the IPPool.Spec.IPs belong to Subnet.Spec.IPs if they are in the same subnet
+func (sw *SubnetWebhook) validateOrphanIPPool(ctx context.Context, subnet *spiderpoolv2beta1.SpiderSubnet) *field.Error {
+	poolList := spiderpoolv2beta1.SpiderIPPoolList{}
+	err := sw.APIReader.List(ctx, &poolList)
+	if nil != err {
+		return field.InternalError(subnetField, fmt.Errorf("failed to list IPPools: %v", err))
+	}
+
+	for _, tmpPool := range poolList.Items {
+		if *tmpPool.Spec.IPVersion != *subnet.Spec.IPVersion {
+			continue
+		}
+
+		// validate the Spec.Subnet whether overlaps or not
+		if tmpPool.Spec.Subnet != subnet.Spec.Subnet {
+			isCIDROverlap, err := spiderpoolip.IsCIDROverlap(*subnet.Spec.IPVersion, tmpPool.Spec.Subnet, subnet.Spec.Subnet)
+			if nil != err {
+				return field.InternalError(subnetField, fmt.Errorf("failed to compare whether 'spec.subnet' overlaps with SpiderIPPool '%s', error: %v", tmpPool.Name, err))
+			}
+			if isCIDROverlap {
+				return field.Invalid(subnetField, subnet.Spec.Subnet, fmt.Sprintf("overlap with SpiderIPPool '%s' resource 'spec.subnet' %s", tmpPool.Name, tmpPool.Spec.Subnet))
+			}
+		} else {
+			// validate the Spec.IPs whether contains or not
+			poolIPs, err := spiderpoolip.AssembleTotalIPs(*tmpPool.Spec.IPVersion, tmpPool.Spec.IPs, tmpPool.Spec.ExcludeIPs)
+			if nil != err {
+				return field.InternalError(ipsField, fmt.Errorf("failed to assemble the total IP addresses of the IPPool '%s', error: %v", tmpPool.Name, err))
+			}
+			subnetIPs, err := spiderpoolip.AssembleTotalIPs(*subnet.Spec.IPVersion, subnet.Spec.IPs, subnet.Spec.ExcludeIPs)
+			if nil != err {
+				return field.InternalError(ipsField, fmt.Errorf("failed to assemble the total IP addresses of the Subnet '%s', error: %v", subnet.Name, err))
+			}
+			diffSet := spiderpoolip.IPsDiffSet(poolIPs, subnetIPs, false)
+			if len(diffSet) != 0 {
+				return field.Invalid(ipsField, subnet.Spec.IPs, fmt.Sprintf("SpiderIPPool '%s' owns some IP addresses that SpiderSubnet '%s' can't control", tmpPool.Name, subnet.Name))
+			}
+		}
+	}
+
 	return nil
 }
 


Test patch:
diff --git a/.github/workflows/e2e-init.yaml b/.github/workflows/e2e-init.yaml
index a97a96024..f63576146 100644
--- a/.github/workflows/e2e-init.yaml
+++ b/.github/workflows/e2e-init.yaml
@@ -37,8 +37,6 @@ jobs:
         include:
           - e2e_init_mode: e2e_init_underlay
             e2e_test_mode: e2e_test_underlay
-          - e2e_init_mode: e2e_init_underlay_subnet
-            e2e_test_mode: e2e_test_underlay_subnet
           - e2e_init_mode: e2e_init_calico
             e2e_test_mode: e2e_test_calico
           - e2e_init_mode: e2e_init_cilium
@@ -186,7 +184,7 @@ jobs:
 
       - name: Get the E2E Performance Result For Dual-stack
         id: get_performance
-        if: ${{ inputs.run_e2e == 'true' && inputs.ip_family == 'dual' && ( matrix.e2e_test_mode == 'e2e_test_underlay' || matrix.e2e_test_mode == 'e2e_test_underlay_subnet' ) }}
+        if: ${{ inputs.run_e2e == 'true' && inputs.ip_family == 'dual' && matrix.e2e_test_mode == 'e2e_test_underlay' }}
         shell: bash
         run: |
           # sudo apt-get install -y jq
diff --git a/pkg/ippoolmanager/ippool_webhook_test.go b/pkg/ippoolmanager/ippool_webhook_test.go
index 356083f7f..556602be4 100644
--- a/pkg/ippoolmanager/ippool_webhook_test.go
+++ b/pkg/ippoolmanager/ippool_webhook_test.go
@@ -1053,7 +1053,7 @@ var _ = Describe("IPPoolWebhook", Label("ippool_webhook_test"), func() {
 					ipPoolWebhook.EnableSpiderSubnet = true
 				})
 
-				It("is orphan IPPool", func() {
+				It("succeed to create orphan IPPool", func() {
 					ipPoolT.Spec.IPVersion = pointer.Int64(constant.IPv4)
 					ipPoolT.Spec.Subnet = "172.18.40.0/24"
 					ipPoolT.Spec.IPs = append(ipPoolT.Spec.IPs,
@@ -1064,7 +1064,7 @@ var _ = Describe("IPPoolWebhook", Label("ippool_webhook_test"), func() {
 					)
 
 					err := ipPoolWebhook.ValidateCreate(ctx, ipPoolT)
-					Expect(apierrors.IsInvalid(err)).To(BeTrue())
+					Expect(err).NotTo(HaveOccurred())
 				})
 
 				It("sets owner reference to non-existent Subnet", func() {
@@ -1786,7 +1786,7 @@ var _ = Describe("IPPoolWebhook", Label("ippool_webhook_test"), func() {
 					ipPoolWebhook.EnableSpiderSubnet = true
 				})
 
-				It("updates orphan IPPool", func() {
+				It("succeed to update orphan IPPool", func() {
 					ipPoolT.Spec.IPVersion = pointer.Int64(constant.IPv4)
 					ipPoolT.Spec.Subnet = "172.18.40.0/24"
 					ipPoolT.Spec.IPs = append(ipPoolT.Spec.IPs, "172.18.40.1-172.18.40.2")
@@ -1795,7 +1795,7 @@ var _ = Describe("IPPoolWebhook", Label("ippool_webhook_test"), func() {
 					newIPPoolT.Spec.IPs = append(newIPPoolT.Spec.IPs, "172.18.40.10")
 
 					err := ipPoolWebhook.ValidateUpdate(ctx, ipPoolT, newIPPoolT)
-					Expect(apierrors.IsInvalid(err)).To(BeTrue())
+					Expect(err).NotTo(HaveOccurred())
 				})
 
 				It("updates the IPPool that sets the owner reference to non-existent Subnet", func() {
diff --git a/pkg/subnetmanager/subnet_informer_test.go b/pkg/subnetmanager/subnet_informer_test.go
index dff20e9e5..7190810b4 100644
--- a/pkg/subnetmanager/subnet_informer_test.go
+++ b/pkg/subnetmanager/subnet_informer_test.go
@@ -197,6 +197,45 @@ var _ = Describe("SubnetController", Label("subnet_controller_test"), func() {
 			}).Should(Succeed())
 		})
 
+		It("sets the owner reference for the orphan IPPool", func() {
+			ipPoolT.Spec.IPVersion = pointer.Int64(constant.IPv4)
+			ipPoolT.Spec.Subnet = "172.18.40.0/24"
+
+			err := fakeClient.Create(ctx, ipPoolT)
+			Expect(err).NotTo(HaveOccurred())
+
+			err = ipPoolIndexer.Add(ipPoolT)
+			Expect(err).NotTo(HaveOccurred())
+
+			subnetT.SetUID(uuid.NewUUID())
+			subnetT.Spec.IPVersion = pointer.Int64(constant.IPv4)
+			subnetT.Spec.Subnet = "172.18.40.0/24"
+
+			err = fakeClient.Create(ctx, subnetT)
+			Expect(err).NotTo(HaveOccurred())
+
+			err = subnetIndexer.Add(subnetT)
+			Expect(err).NotTo(HaveOccurred())
+
+			fakeSubnetWatch.Add(subnetT)
+			Eventually(func(g Gomega) {
+				var subnetR spiderpoolv2beta1.SpiderSubnet
+				err = fakeClient.Get(ctx, types.NamespacedName{Name: subnetT.Name}, &subnetR)
+				g.Expect(err).NotTo(HaveOccurred())
+
+				var ipPoolR spiderpoolv2beta1.SpiderIPPool
+				err = fakeClient.Get(ctx, types.NamespacedName{Name: ipPoolT.Name}, &ipPoolR)
+				g.Expect(err).NotTo(HaveOccurred())
+
+				controlled := metav1.IsControlledBy(&ipPoolR, &subnetR)
+				g.Expect(controlled).To(BeTrue())
+
+				v, ok := ipPoolR.Labels[constant.LabelIPPoolOwnerSpiderSubnet]
+				g.Expect(ok).To(BeTrue())
+				g.Expect(v).To(Equal(subnetR.Name))
+			}).Should(Succeed())
+		})
+
 		It("aggregates pre-allocation status", func() {
 			subnetT.SetUID(uuid.NewUUID())
 			subnetT.Spec.IPVersion = pointer.Int64(constant.IPv4)
@@ -340,5 +379,6 @@ var _ = Describe("SubnetController", Label("subnet_controller_test"), func() {
 				g.Expect(subnetR.Status.ControlledIPPools).To(BeNil())
 			}).Should(Succeed())
 		})
+
 	})
 })
diff --git a/pkg/subnetmanager/subnet_webhook_test.go b/pkg/subnetmanager/subnet_webhook_test.go
index d5be93845..c4d85fa66 100644
--- a/pkg/subnetmanager/subnet_webhook_test.go
+++ b/pkg/subnetmanager/subnet_webhook_test.go
@@ -516,6 +516,47 @@ var _ = Describe("SubnetWebhook", Label("subnet_webhook_test"), func() {
 					err = subnetWebhook.ValidateCreate(ctx, subnetT)
 					Expect(apierrors.IsInvalid(err)).To(BeTrue())
 				})
+
+				It("overlaps with existing orphan IPPool resource", func() {
+					orphanPool := &spiderpoolv2beta1.SpiderIPPool{
+						TypeMeta: metav1.TypeMeta{
+							Kind:       constant.KindSpiderIPPool,
+							APIVersion: fmt.Sprintf("%s/%s", constant.SpiderpoolAPIGroup, constant.SpiderpoolAPIVersion),
+						},
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "orphanPool",
+						},
+						Spec: spiderpoolv2beta1.IPPoolSpec{
+							IPVersion: pointer.Int64(constant.IPv4),
+							Subnet:    "172.18.40.0/25",
+							IPs:       []string{"172.18.40.40"},
+						},
+					}
+					DeferCleanup(func() {
+						err := tracker.Delete(
+							schema.GroupVersionResource{
+								Group:    constant.SpiderpoolAPIGroup,
+								Version:  constant.SpiderpoolAPIVersion,
+								Resource: "spiderippools",
+							}, orphanPool.Namespace, orphanPool.Name)
+						Expect(err).NotTo(HaveOccurred())
+					})
+
+					err := tracker.Add(orphanPool)
+					Expect(err).NotTo(HaveOccurred())
+
+					subnetT.Spec.IPVersion = pointer.Int64(constant.IPv4)
+					subnetT.Spec.Subnet = "172.18.40.0/24"
+					subnetT.Spec.IPs = append(subnetT.Spec.IPs,
+						[]string{
+							"172.18.40.1-172.18.40.2",
+							"172.18.40.10",
+						}...,
+					)
+
+					err = subnetWebhook.ValidateCreate(ctx, subnetT)
+					Expect(apierrors.IsInvalid(err)).To(BeTrue())
+				})
 			})
 
 			When("Validating 'spec.ips'", func() {
@@ -541,6 +582,46 @@ var _ = Describe("SubnetWebhook", Label("subnet_webhook_test"), func() {
 					err := subnetWebhook.ValidateCreate(ctx, subnetT)
 					Expect(apierrors.IsInvalid(err)).To(BeTrue())
 				})
+
+				It("orphan IPPool with extra IPs", func() {
+					orphanPool := &spiderpoolv2beta1.SpiderIPPool{
+						TypeMeta: metav1.TypeMeta{
+							Kind:       constant.KindSpiderIPPool,
+							APIVersion: fmt.Sprintf("%s/%s", constant.SpiderpoolAPIGroup, constant.SpiderpoolAPIVersion),
+						},
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "orphanPool",
+						},
+						Spec: spiderpoolv2beta1.IPPoolSpec{
+							IPVersion: pointer.Int64(constant.IPv4),
+							Subnet:    "172.18.40.0/25",
+							IPs:       []string{"172.18.40.40-172.18.40.45"},
+						},
+					}
+					DeferCleanup(func() {
+						err := tracker.Delete(
+							schema.GroupVersionResource{
+								Group:    constant.SpiderpoolAPIGroup,
+								Version:  constant.SpiderpoolAPIVersion,
+								Resource: "spiderippools",
+							}, orphanPool.Namespace, orphanPool.Name)
+						Expect(err).NotTo(HaveOccurred())
+					})
+
+					err := tracker.Add(orphanPool)
+					Expect(err).NotTo(HaveOccurred())
+
+					subnetT.Spec.IPVersion = pointer.Int64(constant.IPv4)
+					subnetT.Spec.Subnet = "172.18.40.0/25"
+					subnetT.Spec.IPs = append(subnetT.Spec.IPs,
+						[]string{
+							"172.18.40.41-172.18.40.45",
+						}...,
+					)
+
+					err = subnetWebhook.ValidateCreate(ctx, subnetT)
+					Expect(apierrors.IsInvalid(err)).To(BeTrue())
+				})
 			})
 
 			When("Validating 'spec.excludeIPs'", func() {
@@ -789,6 +870,46 @@ var _ = Describe("SubnetWebhook", Label("subnet_webhook_test"), func() {
 				err := subnetWebhook.ValidateCreate(ctx, subnetT)
 				Expect(err).NotTo(HaveOccurred())
 			})
+
+			It("create IPv4 Subnet with orphan IPPool exist", func() {
+				orphanPool := &spiderpoolv2beta1.SpiderIPPool{
+					TypeMeta: metav1.TypeMeta{
+						Kind:       constant.KindSpiderIPPool,
+						APIVersion: fmt.Sprintf("%s/%s", constant.SpiderpoolAPIGroup, constant.SpiderpoolAPIVersion),
+					},
+					ObjectMeta: metav1.ObjectMeta{
+						Name: "orphanPool",
+					},
+					Spec: spiderpoolv2beta1.IPPoolSpec{
+						IPVersion: pointer.Int64(constant.IPv4),
+						Subnet:    "172.18.40.0/25",
+						IPs:       []string{"172.18.40.40-172.18.40.41"},
+					},
+				}
+				DeferCleanup(func() {
+					err := tracker.Delete(
+						schema.GroupVersionResource{
+							Group:    constant.SpiderpoolAPIGroup,
+							Version:  constant.SpiderpoolAPIVersion,
+							Resource: "spiderippools",
+						}, orphanPool.Namespace, orphanPool.Name)
+					Expect(err).NotTo(HaveOccurred())
+				})
+
+				err := tracker.Add(orphanPool)
+				Expect(err).NotTo(HaveOccurred())
+
+				subnetT.Spec.IPVersion = pointer.Int64(constant.IPv4)
+				subnetT.Spec.Subnet = "172.18.40.0/25"
+				subnetT.Spec.IPs = append(subnetT.Spec.IPs,
+					[]string{
+						"172.18.40.40-172.18.40.45",
+					}...,
+				)
+
+				err = subnetWebhook.ValidateCreate(ctx, subnetT)
+				Expect(err).NotTo(HaveOccurred())
+			})
 		})
 
 		Describe("ValidateUpdate", func() {
diff --git a/test/Makefile.defs b/test/Makefile.defs
index 1cf9ca2cf..770b81418 100644
--- a/test/Makefile.defs
+++ b/test/Makefile.defs
@@ -66,7 +66,7 @@ E2E_TIMEOUT ?= 60m
 
 E2E_GINKGO_PROCS ?= 4
 
-E2E_SPIDERPOOL_ENABLE_SUBNET ?= false # consistent with open source, closed by default
+E2E_SPIDERPOOL_ENABLE_SUBNET ?= true # consistent with open source, closed by default
 E2E_SPIDERPOOL_ENABLE_COORDINATOR ?= true
 E2E_SPIDERPOOL_ENABLE_MULTUSCONFIG ?= true
 
diff --git a/test/doc/subnet.md b/test/doc/subnet.md
index b34ca3bf2..b28844216 100644
--- a/test/doc/subnet.md
+++ b/test/doc/subnet.md
@@ -1,26 +1,28 @@
 # E2E Cases for SpiderSubnet
 
-| Case ID | Title                                                                                                                               | Priority | Smoke | Status | Other |
-| ------- | ----------------------------------------------------------------------------------------------------------------------------------- | -------- | ----- | ------ | ----- |
-| I00001  | Add, delete, modify, and query CIDR subnet                                                                                          |   p1     |       |  done  |       |
-| I00002  | Add and remove IP for CIDR subnet                                                                                                   |   p1     |       |  done  |       |
-| I00003  | Automatically create, scale, restart, and delete of ippools for different types of controllers                                      |   p1     |       |  done  |       |
-| I00004  | The excludeIPs in the subnet will not be used by pools created automatically or manually.                                           |   p2     |       |  done  |       |
-| I00005  | If routes and gateway are modified for CIDR, how the manually created ippools are affected?                                         |   p3     |       |  done  |       |
-| I00006  | Multiple automatic creation and recycling of ippools, eventually the free IPs in the subnet should be restored to its initial state |   p1     | true  |  done  |       |
-| I00007  | Multiple manual creation and recycling of ippools, eventually the free IPs in the subnet should be restored to its initial state    |   p1     | true  |  done  |       |
-| I00008  | Scale up and down the number of deployment several times to see if the state of the ippools is eventually stable                    |   p1     |       |  done  |       |
-| I00009  | Create 100 Subnets with the same `Subnet.spec` and see if only one succeeds in the end                                              |   p1     |       |  done  |       |
-| I00010  | Create 100 IPPools with the same `IPPool.spec` and see if only one succeeds in the end                                              |   p1     |       |  done  |       |
-| I00011  | The subnet automatically creates an ippool and allocates IP, and should consider reservedIP                                         |   p1     |       |  done  |       |
-| I00012  | SpiderSubnet supports multiple interfaces                                                                                           |   p1     |       |  done  |       |
-| I00013  | SpiderSubnet supports automatic IP assignment for Pods                                                                              |   p2     |       |  done  |       |
-| I00014  | The default subnet should support different controller types                                                                        |   p2     |       |  done  |       |
-| I00015  | Applications with the same name and type can use the reserved IPPool.      |   p2     |       |  done  |       |
-| I00016  | Applications with the same name and different types cannot use the reserved IPPool.                       |   p3     |       |  done  |       |
-| I00017  | Ability to create fixed IPPools for applications with very long names                      |   p3     |       |  done  |       |
-| I00018  | automatic IPPool IPs are not modifiable                                                    |   p3     |       |  done  |       |
-| I00019  | Change the annotation ipam.spidernet.io/ippool-reclaim to true and the reserved IPPool will be reclaimed.                   |   p3     |       |  done  |       |
-| I00020  | Redundant IPs for automatic IPPool, which cannot be used by other applications             |   p3     |       |  done  |       |
-| I00021  | Pod works correctly when multiple NICs are specified by annotations for applications of the same name             |   p3     |       |  done  |       |
-| I00022  | Dirty data in the subnet should be recycled.             |   p3     |       |  done  |       |
+| Case ID | Title                                                                                                                               | Priority | Smoke | Status  | Other |
+|---------|-------------------------------------------------------------------------------------------------------------------------------------|----------|-------|---------|-------|
+| I00001  | Add, delete, modify, and query CIDR subnet                                                                                          | p1       |       | done    |       |
+| I00002  | Add and remove IP for CIDR subnet                                                                                                   | p1       |       | done    |       |
+| I00003  | Automatically create, scale, restart, and delete of ippools for different types of controllers                                      | p1       |       | done    |       |
+| I00004  | The excludeIPs in the subnet will not be used by pools created automatically or manually.                                           | p2       |       | done    |       |
+| I00005  | If routes and gateway are modified for CIDR, how the manually created ippools are affected?                                         | p3       |       | done    |       |
+| I00006  | Multiple automatic creation and recycling of ippools, eventually the free IPs in the subnet should be restored to its initial state | p1       | true  | done    |       |
+| I00007  | Multiple manual creation and recycling of ippools, eventually the free IPs in the subnet should be restored to its initial state    | p1       | true  | done    |       |
+| I00008  | Scale up and down the number of deployment several times to see if the state of the ippools is eventually stable                    | p1       |       | done    |       |
+| I00009  | Create 100 Subnets with the same `Subnet.spec` and see if only one succeeds in the end                                              | p1       |       | done    |       |
+| I00010  | Create 100 IPPools with the same `IPPool.spec` and see if only one succeeds in the end                                              | p1       |       | done    |       |
+| I00011  | The subnet automatically creates an ippool and allocates IP, and should consider reservedIP                                         | p1       |       | done    |       |
+| I00012  | SpiderSubnet supports multiple interfaces                                                                                           | p1       |       | done    |       |
+| I00013  | SpiderSubnet supports automatic IP assignment for Pods                                                                              | p2       |       | done    |       |
+| I00014  | The default subnet should support different controller types                                                                        | p2       |       | done    |       |
+| I00015  | Applications with the same name and type can use the reserved IPPool.                                                               | p2       |       | done    |       |
+| I00016  | Applications with the same name and different types cannot use the reserved IPPool.                                                 | p3       |       | done    |       |
+| I00017  | Ability to create fixed IPPools for applications with very long names                                                               | p3       |       | done    |       |
+| I00018  | automatic IPPool IPs are not modifiable                                                                                             | p3       |       | done    |       |
+| I00019  | Change the annotation ipam.spidernet.io/ippool-reclaim to true and the reserved IPPool will be reclaimed.                           | p3       |       | done    |       |
+| I00020  | Redundant IPs for automatic IPPool, which cannot be used by other applications                                                      | p3       |       | done    |       |
+| I00021  | Pod works correctly when multiple NICs are specified by annotations for applications of the same name                               | p3       |       | done    |       |
+| I00022  | Dirty data in the subnet should be recycled.                                                                                        | p3       |       | done    |       |
+| I00023  | Orphan IPPool creation in SpiderSubnet feature.(validate spec.subnet overlaps)                                                      | p3       |       | ongoing |       |
+| I00024  | New SpiderSubnet resource controls orphan IPPool resource.(validate OwnerRef, IPs ranges containing, cascade deletion)              | p3       |       | ongoing |       |
diff --git a/test/e2e/affinity/affinity_test.go b/test/e2e/affinity/affinity_test.go
index 5e548afe8..d26661d40 100644
--- a/test/e2e/affinity/affinity_test.go
+++ b/test/e2e/affinity/affinity_test.go
@@ -545,9 +545,6 @@ var _ = Describe("test Affinity", Label("affinity"), func() {
 		nsName1 = "ns1" + tools.RandomName()
 
 		BeforeEach(func() {
-			if frame.Info.SpiderSubnetEnabled {
-				Skip("The subnet function is enabled, the namespace annotation has a lower priority than the default subnet")
-			}
 			// Create another namespace
 			GinkgoWriter.Printf("create another namespace %v \n", nsName1)
 			err := frame.CreateNamespaceUntilDefaultServiceAccountReady(nsName1, common.ServiceAccountReadyTimeout)
diff --git a/test/e2e/annotation/annotation_test.go b/test/e2e/annotation/annotation_test.go
index 100fc33cb..92702dab6 100644
--- a/test/e2e/annotation/annotation_test.go
+++ b/test/e2e/annotation/annotation_test.go
@@ -440,9 +440,6 @@ var _ = Describe("test annotation", Label("annotation"), func() {
 
 			It(`the namespace annotation has precedence over global default ippool`, Label("A00006", "smoke"), func() {
 				// Generate a pod yaml with namespace annotations
-				if frame.Info.SpiderSubnetEnabled {
-					Skip("The subnet function is enabled, the namespace annotation has a lower priority than the default subnet")
-				}
 				podYaml := common.GenerateExamplePodYaml(podName, nsName)
 				Expect(podYaml).NotTo(BeNil())
 				// The namespace annotation has precedence over global default ippool
diff --git a/test/e2e/ippoolcr/ippoolcr_test.go b/test/e2e/ippoolcr/ippoolcr_test.go
index a40ab9d72..21e2e2b56 100644
--- a/test/e2e/ippoolcr/ippoolcr_test.go
+++ b/test/e2e/ippoolcr/ippoolcr_test.go
@@ -378,10 +378,6 @@ var _ = Describe("test ippool CR", Label("ippoolCR"), func() {
 	})
 
 	It("create and delete batch of ippool and check time cost", Label("D00006"), func() {
-		if frame.Info.SpiderSubnetEnabled {
-			Skip("Suitable for no subnets")
-		}
-
 		var ipv4PoolNameList, ipv6PoolNameList []string
 		var err error
 		const ippoolNumber = 10