Repository: spidernet-io/spiderpool
Pull request #1267 (2023-01-10 06:05:49)

build(deps): bump github.com/onsi/ginkgo/v2 from 2.6.1 to 2.7.0
Bumps [github.com/onsi/ginkgo/v2](https://github.com/onsi/ginkgo) from 2.6.1 to 2.7.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/onsi/ginkgo/releases">github.com/onsi/ginkgo/v2's releases</a>.</em></p>
<blockquote>
<h2>v2.7.0</h2>
<h2>2.7.0</h2>
<h3>Features</h3>
<ul>
<li>Introduce ContinueOnFailure for Ordered containers [e0123ca] - Ordered containers that are also decorated with ContinueOnFailure will not stop running specs after the first spec fails.</li>
<li>Support for bootstrap commands to use custom data for templates (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1110">#1110</a>) [7a2b242]</li>
<li>Support for labels and pending decorator in ginkgo outline output (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1113">#1113</a>) [e6e3b98]</li>
<li>Color aliases for custom color support (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1101">#1101</a>) [49fab7a]</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>correctly ensure deterministic spec order, even if specs are generated by iterating over a map [89dda20]</li>
<li>Fix a bug where timedout specs were not correctly treated as failures when determining whether or not to run AfterAlls in an Ordered container.</li>
<li>Ensure go test coverprofile outputs to the expected location (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1105">#1105</a>) [b0bd77b]</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/onsi/ginkgo/blob/master/CHANGELOG.md">github.com/onsi/ginkgo/v2's changelog</a>.</em></p>
<blockquote>
<h2>2.7.0</h2>
<h3>Features</h3>
<ul>
<li>Introduce ContinueOnFailure for Ordered containers [e0123ca] - Ordered containers that are also decorated with ContinueOnFailure will not stop running specs after the first spec fails.</li>
<li>Support for bootstrap commands to use custom data for templates (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1110">#1110</a>) [7a2b242]</li>
<li>Support for labels and pending decorator in ginkgo outline output (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1113">#1113</a>) [e6e3b98]</li>
<li>Color aliases for custom color support (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1101">#1101</a>) [49fab7a]</li>
</ul>
<h3>Fixes</h3>
<ul>
<li>correctly ensure deterministic spec order, even if specs are generated by iterating over a map [89dda20]</li>
<li>Fix a bug where timedout specs were not correctly treated as failures when determining whether or not to run AfterAlls in an Ordered container.</li>
<li>Ensure go test coverprofile outputs to the expected location (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1105">#1105</a>) [b0bd77b]</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/onsi/ginkgo/commit/310a73f427c5d16ab6fd069182644e5e65b8e32b"><code>310a73f</code></a> v2.7.0</li>
<li><a href="https://github.com/onsi/ginkgo/commit/e0123caf2fcf1c9ae14e830e7b0cdb5ba1609a7b"><code>e0123ca</code></a> Introduce ContinueOnFailure for Ordered containers</li>
<li><a href="https://github.com/onsi/ginkgo/commit/89dda203832c75e84fcfcca8963e6242c00f1a5b"><code>89dda20</code></a> correctly ensure deterministic spec order, even if specs are generated by ite...</li>
<li><a href="https://github.com/onsi/ginkgo/commit/7a2b2428689bdba35afdf9eb77d5ef0b89586aba"><code>7a2b242</code></a> Support for bootstrap commands to use custom data for templates (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1110">#1110</a>)</li>
<li><a href="https://github.com/onsi/ginkgo/commit/62fff9539c30ca989d2fe6eeb6a23b0a86dea94b"><code>62fff95</code></a> ensure deterministic order even if specs are generated by iterating over a map</li>
<li><a href="https://github.com/onsi/ginkgo/commit/e6e3b989075c2a9e645402aee442976a327e0397"><code>e6e3b98</code></a> Support for labels and pending decorator in ginkgo outline output (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1113">#1113</a>)</li>
<li><a href="https://github.com/onsi/ginkgo/commit/b0bd77b89e41a35634d4d2841e5b54918299dfda"><code>b0bd77b</code></a> Ensure go test coverprofile outputs to the expected location (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1105">#1105</a>)</li>
<li><a href="https://github.com/onsi/ginkgo/commit/49fab7a2900dda49091a746520b1e86e9286c0bc"><code>49fab7a</code></a> Color aliases for custom color support (<a href="https://github-redirect.dependabot.com/onsi/ginkgo/issues/1101">#1101</a>)</li>
<li>See full diff in <a href="https://github.com/onsi/ginkgo/compare/v2.6.1...v2.7.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/onsi/ginkgo/v2&package-manager=go_modules&previous-version=2.6.1&new-version=2.7.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/.github/.spelling b/.github/.spelling
index d6599320c..226391488 100644
--- a/.github/.spelling
+++ b/.github/.spelling
@@ -278,6 +278,3 @@ default_ipv4_ippool
 default_ipv6_ippool
 clusterDefaultIPv4Subnet
 clusterDefaultIPv6Subnet
-SpiderEndpoint.Status.OwnerControllerType
-OwnerControllerType
-subresource
diff --git a/.github/workflows/auto-nightly-ci.yaml b/.github/workflows/auto-nightly-ci.yaml
index a392d23eb..5b7f83670 100644
--- a/.github/workflows/auto-nightly-ci.yaml
+++ b/.github/workflows/auto-nightly-ci.yaml
@@ -286,7 +286,7 @@ jobs:
 
       - name: Upload e2e log For Dual-stack
         if: ${{ needs.get_ref.outputs.ipfamily_dual_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: dualstack-debuglog.txt
           path: test/e2edebugLog.txt
@@ -294,7 +294,7 @@ jobs:
 
       - name: Upload e2e report For Dual-stack
         if: ${{ steps.dualstack_e2e.outputs.upload == 'true' && needs.get_ref.outputs.ipfamily_dual_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: dualstack-e2ereport.json
           path: e2ereport.json
@@ -378,7 +378,7 @@ jobs:
 
       - name: Upload e2e log For ipv4-only
         if: ${{ needs.get_ref.outputs.ipfamily_ipv4only_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ipv4Only-debuglog.txt
           path: test/e2edebugLog.txt
@@ -386,7 +386,7 @@ jobs:
 
       - name: Upload e2e report For ipv4-only
         if: ${{ steps.ipv4only_e2e.outputs.upload == 'true' && needs.get_ref.outputs.ipfamily_ipv4only_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ipv4Only-e2ereport.json
           path: e2ereport.json
@@ -447,7 +447,7 @@ jobs:
 
       - name: Upload e2e log For ipv6-only
         if: ${{ needs.get_ref.outputs.ipfamily_ipv6only_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ipv6Only-debuglog.txt
           path: test/e2edebugLog.txt
@@ -455,7 +455,7 @@ jobs:
 
       - name: Upload e2e report For ipv6-only
         if: ${{ steps.ipv6only_e2e.outputs.upload == 'true' && needs.get_ref.outputs.ipfamily_ipv6only_e2e == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ipv6Only-e2ereport.json
           path: e2ereport.json
diff --git a/.github/workflows/build-image-base.yaml b/.github/workflows/build-image-base.yaml
index 5f7bbcab8..016fa749d 100644
--- a/.github/workflows/build-image-base.yaml
+++ b/.github/workflows/build-image-base.yaml
@@ -130,7 +130,7 @@ jobs:
 
       - name: Upload artifact digests
         if: ${{ steps.tag-in-repositories.outputs.exists == 'false' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: image-digest ${{ env.IMAGE_NAME }}
           path: image-digest
diff --git a/.github/workflows/build-image-ci.yaml b/.github/workflows/build-image-ci.yaml
index bb5d7f087..4fb382295 100644
--- a/.github/workflows/build-image-ci.yaml
+++ b/.github/workflows/build-image-ci.yaml
@@ -244,7 +244,7 @@ jobs:
 
       # Upload artifact digests
       - name: Upload artifact digests
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: image-digest-${{ matrix.name }}
           path: image-digest
@@ -252,7 +252,7 @@ jobs:
 
       # Upload artifact race images tar
       - name: Upload artifact race image tar
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: image-tar-${{ matrix.name }}
           path: /tmp/${{ matrix.name }}-race.tar
diff --git a/.github/workflows/call-clean-ghcr.yaml b/.github/workflows/call-clean-ghcr.yaml
index b170870d7..31f861ec5 100644
--- a/.github/workflows/call-clean-ghcr.yaml
+++ b/.github/workflows/call-clean-ghcr.yaml
@@ -68,7 +68,7 @@ jobs:
 
       - name: Delete CI image
         if: ${{ steps.args.outputs.tag_regex != '' }}
-        uses: vlaurin/action-ghcr-prune@v0.5.0
+        uses: vlaurin/action-ghcr-prune@v0.4.0
         with:
           token: ${{ secrets.WELAN_PAT }}
           organization: ${{ github.repository_owner }}
@@ -81,7 +81,7 @@ jobs:
 
       - name: Delete CI image
         if: ${{ steps.args.outputs.tag_regex == '' }}
-        uses: vlaurin/action-ghcr-prune@v0.5.0
+        uses: vlaurin/action-ghcr-prune@v0.4.0
         with:
           token: ${{ secrets.WELAN_PAT }}
           organization: ${{ github.repository_owner }}
diff --git a/.github/workflows/call-release-changelog.yaml b/.github/workflows/call-release-changelog.yaml
index 584b11f60..3060687ae 100644
--- a/.github/workflows/call-release-changelog.yaml
+++ b/.github/workflows/call-release-changelog.yaml
@@ -108,7 +108,7 @@ jobs:
           cat ${FILE_PATH}
 
       - name: Upload Changelog
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: changelog_artifact
           path: ${{ env.FILE_PATH }}
diff --git a/.github/workflows/call-release-chart.yaml b/.github/workflows/call-release-chart.yaml
index 63777891b..87d5c08ed 100644
--- a/.github/workflows/call-release-chart.yaml
+++ b/.github/workflows/call-release-chart.yaml
@@ -90,7 +90,7 @@ jobs:
           mv charts/*.tgz tmp
 
       - name: Upload Artifact
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: chart_package_artifact
           path: tmp/*
diff --git a/.github/workflows/call-release-doc.yaml b/.github/workflows/call-release-doc.yaml
index ddf8cbd05..97824c77b 100644
--- a/.github/workflows/call-release-doc.yaml
+++ b/.github/workflows/call-release-doc.yaml
@@ -66,7 +66,7 @@ jobs:
           make build_doc
 
       - name: Upload Artifact
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: website_package_artifact
           path: docs/site.tar.gz
diff --git a/.github/workflows/call-release-image.yaml b/.github/workflows/call-release-image.yaml
index 328026e96..dd4d2250c 100644
--- a/.github/workflows/call-release-image.yaml
+++ b/.github/workflows/call-release-image.yaml
@@ -125,14 +125,14 @@ jobs:
           cd ..
 
       - name: Upload artifact digests
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: image-digest-artifact-${{ steps.ref.outputs.imagetag }}
           path: image-digest-output.txt
           retention-days: 1
 
       - name: Upload artifact digests
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: makefile-digest-artifact-${{ steps.ref.outputs.imagetag }}
           path: Makefile.digests
diff --git a/.github/workflows/ci-image-gc-by-pr.yaml b/.github/workflows/ci-image-gc-by-pr.yaml
index c1c198a32..bf559adaa 100644
--- a/.github/workflows/ci-image-gc-by-pr.yaml
+++ b/.github/workflows/ci-image-gc-by-pr.yaml
@@ -32,7 +32,7 @@ jobs:
           fi
 
       - name: Delete CI image
-        uses: vlaurin/action-ghcr-prune@v0.5.0
+        uses: vlaurin/action-ghcr-prune@v0.4.0
         with:
           token: ${{ secrets.WELAN_PAT }}
           organization: ${{ github.repository_owner }}
diff --git a/.github/workflows/lint-golang.yaml b/.github/workflows/lint-golang.yaml
index 4d53a5ab1..387f30b5e 100644
--- a/.github/workflows/lint-golang.yaml
+++ b/.github/workflows/lint-golang.yaml
@@ -214,7 +214,7 @@ jobs:
 
       - name: Upload Coverage Artifact
         if: ${{ steps.unitest.outcome == 'failure' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: coverage.out
           path: coverage.out
@@ -222,7 +222,7 @@ jobs:
 
       - name: Upload Report Artifact
         if: ${{ steps.unitest.outcome == 'failure' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: unitestreport.json
           path: unitestreport.json
diff --git a/.github/workflows/lint-yaml.yaml b/.github/workflows/lint-yaml.yaml
index b24df1f49..3c67d3a05 100644
--- a/.github/workflows/lint-yaml.yaml
+++ b/.github/workflows/lint-yaml.yaml
@@ -50,7 +50,7 @@ jobs:
 
       - name: Upload artifact digests
         if: ${{ steps.yaml-lint.outcome == 'failure' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: log
           path: ${{ steps.yaml-lint.outputs.logfile }}
diff --git a/cmd/spiderpool-agent/cmd/daemon.go b/cmd/spiderpool-agent/cmd/daemon.go
index 00897553b..52fcc6227 100644
--- a/cmd/spiderpool-agent/cmd/daemon.go
+++ b/cmd/spiderpool-agent/cmd/daemon.go
@@ -151,8 +151,8 @@ func DaemonMain() {
 			MaxQueueSize: &agentContext.Cfg.LimiterMaxQueueSize,
 			MaxWaitTime:  &limiterMaxWaitTime,
 		},
-		OperationRetries:     agentContext.Cfg.UpdateCRMaxRetries,
-		OperationGapDuration: time.Duration(agentContext.Cfg.WaitSubnetPoolTime) * time.Second,
+		WaitSubnetPoolRetries: agentContext.Cfg.UpdateCRMaxRetries,
+		WaitSubnetPoolTime:    time.Duration(agentContext.Cfg.WaitSubnetPoolTime) * time.Second,
 	}, agentContext.IPPoolManager, agentContext.EndpointManager, agentContext.NodeManager, agentContext.NSManager, agentContext.PodManager, agentContext.StsManager, agentContext.SubnetManager)
 	agentContext.IPAM = ipam
 
diff --git a/docs/develop/upgrade.md b/docs/develop/upgrade.md
index 3355e5065..1570f6b6a 100644
--- a/docs/develop/upgrade.md
+++ b/docs/develop/upgrade.md
@@ -35,15 +35,3 @@ The key is `ipam.spidernet.io/interface`, and the value is the corresponding int
    ```
 
 4. Update your Spiderpool components version and restart them all.
-
-## Upgrade to 0.4.0 from (<0.4.0)
-
-### Description
-
-Due to the architecture adjustment, the SpiderEndpoint.Status.OwnerControllerType property is changed from `None` to `Pod`.
-
-### Operation steps
-
-1. Find all SpiderEndpoint objects that their Status OwnerControllerType is `None`
-
-2. Replace the subresource SpiderEndpoint.Status.OwnerControllerType property from `None` to `Pod`
diff --git a/go.mod b/go.mod
index 33cc8aa09..2b6a75dd2 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
 	github.com/gogo/protobuf v1.3.2
 	github.com/google/gops v0.3.26
 	github.com/jessevdk/go-flags v1.5.0
-	github.com/onsi/ginkgo/v2 v2.6.1
+	github.com/onsi/ginkgo/v2 v2.7.0
 	github.com/onsi/gomega v1.24.2
 	github.com/pyroscope-io/client v0.4.0
 	github.com/sasha-s/go-deadlock v0.3.1
diff --git a/go.sum b/go.sum
index 3a6b23ad2..28d8d0a6a 100644
--- a/go.sum
+++ b/go.sum
@@ -466,8 +466,8 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108
 github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
 github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
 github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
-github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q=
-github.com/onsi/ginkgo/v2 v2.6.1/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
+github.com/onsi/ginkgo/v2 v2.7.0 h1:/XxtEV3I3Eif/HobnVx9YmJgk8ENdRsuUmM+fLCFNow=
+github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
 github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
 github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
 github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
diff --git a/pkg/constant/k8s.go b/pkg/constant/k8s.go
index 52aa32e65..ef7d34d72 100644
--- a/pkg/constant/k8s.go
+++ b/pkg/constant/k8s.go
@@ -30,14 +30,14 @@ const (
 )
 
 const (
-	KindPod         string = "Pod"
-	KindDeployment  string = "Deployment"
-	KindStatefulSet string = "StatefulSet"
-	KindDaemonSet   string = "DaemonSet"
-	KindUnknown     string = "Unknown"
-	KindReplicaSet  string = "ReplicaSet"
-	KindJob         string = "Job"
-	KindCronJob     string = "CronJob"
+	OwnerNone        string = "None"
+	OwnerDeployment  string = "Deployment"
+	OwnerStatefulSet string = "StatefulSet"
+	OwnerDaemonSet   string = "DaemonSet"
+	OwnerUnknown     string = "Unknown"
+	OwnerReplicaSet  string = "ReplicaSet"
+	OwnerJob         string = "Job"
+	OwnerCronJob     string = "CronJob"
 )
 
 const (
diff --git a/pkg/gcmanager/pod_cache.go b/pkg/gcmanager/pod_cache.go
index 04e2b29c0..5cd07c68b 100644
--- a/pkg/gcmanager/pod_cache.go
+++ b/pkg/gcmanager/pod_cache.go
@@ -141,7 +141,7 @@ func (s *SpiderGC) buildPodEntry(oldPod, currentPod *corev1.Pod, deleted bool) (
 	// deleted pod
 	if deleted {
 		// check StatefulSet pod, we will trace it if its controller StatefulSet object was deleted or decreased its replicas and the pod index was out of the replicas.
-		if s.gcConfig.EnableStatefulSet && ownerRef != nil && ownerRef.Kind == constant.KindStatefulSet {
+		if s.gcConfig.EnableStatefulSet && ownerRef != nil && ownerRef.Kind == constant.OwnerStatefulSet {
 			isValidStsPod, err := s.stsMgr.IsValidStatefulSetPod(context.TODO(), currentPod.Namespace, currentPod.Name, ownerRef.Kind)
 			if nil != err {
 				return nil, err
@@ -168,7 +168,7 @@ func (s *SpiderGC) buildPodEntry(oldPod, currentPod *corev1.Pod, deleted bool) (
 		return podEntry, nil
 	} else {
 		// no need to trace Terminating StatefulSet pod.
-		if ownerRef != nil && ownerRef.Kind == constant.KindStatefulSet {
+		if ownerRef != nil && ownerRef.Kind == constant.OwnerStatefulSet {
 			return nil, nil
 		}
 
diff --git a/pkg/gcmanager/scanAll_IPPool.go b/pkg/gcmanager/scanAll_IPPool.go
index c35cc460e..a3183b8d1 100644
--- a/pkg/gcmanager/scanAll_IPPool.go
+++ b/pkg/gcmanager/scanAll_IPPool.go
@@ -93,7 +93,7 @@ func (s *SpiderGC) executeScanAll(ctx context.Context) {
 				// case: The pod in IPPool's ip-allocationDetail is not exist in k8s
 				if apierrors.IsNotFound(err) {
 					// check StatefulSet pod whether need to clean up its IP and Endpoint or not
-					if s.gcConfig.EnableStatefulSet && poolIPAllocation.OwnerControllerType == constant.KindStatefulSet {
+					if s.gcConfig.EnableStatefulSet && poolIPAllocation.OwnerControllerType == constant.OwnerStatefulSet {
 						isValidStsPod, err := s.stsMgr.IsValidStatefulSetPod(ctx, poolIPAllocation.Namespace, poolIPAllocation.Pod, poolIPAllocation.OwnerControllerType)
 						if nil != err {
 							scanAllLogger.Sugar().Errorf("failed to check StatefulSet pod '%s/%s' IP '%s' should be cleaned or not, error: %v",
diff --git a/pkg/gcmanager/tracePod_worker.go b/pkg/gcmanager/tracePod_worker.go
index d7a7af185..d8787c507 100644
--- a/pkg/gcmanager/tracePod_worker.go
+++ b/pkg/gcmanager/tracePod_worker.go
@@ -103,7 +103,7 @@ func (s *SpiderGC) releaseIPPoolIPExecutor(ctx context.Context, workerIndex int)
 			loggerReleaseIP.Sugar().Infof("release IPPoolIP task '%+v' successfully", *podCache)
 
 			// delete StatefulSet wep (other controller wep has OwnerReference, its lifecycle is same with pod)
-			if endpoint.Status.OwnerControllerType == constant.KindStatefulSet {
+			if endpoint.Status.OwnerControllerType == constant.OwnerStatefulSet {
 				err = s.wepMgr.DeleteEndpoint(ctx, endpoint)
 				if nil != err {
 					loggerReleaseIP.Sugar().Errorf("failed to delete StatefulSet wep '%s/%s', error: '%v'",
diff --git a/pkg/ipam/config.go b/pkg/ipam/config.go
index 54eeb6e97..54ca1ba22 100644
--- a/pkg/ipam/config.go
+++ b/pkg/ipam/config.go
@@ -24,9 +24,9 @@ type IPAMConfig struct {
 	EnableSpiderSubnet bool
 	EnableStatefulSet  bool
 
-	OperationRetries     int
-	OperationGapDuration time.Duration
-	LimiterConfig        limiter.LimiterConfig
+	WaitSubnetPoolRetries int
+	WaitSubnetPoolTime    time.Duration
+	LimiterConfig         limiter.LimiterConfig
 }
 
 func (c *IPAMConfig) getClusterDefaultPool(ctx context.Context, nic string, cleanGateway bool) (*ToBeAllocated, error) {
diff --git a/pkg/ipam/ipam.go b/pkg/ipam/ipam.go
index 4de0cfd6f..402452e53 100644
--- a/pkg/ipam/ipam.go
+++ b/pkg/ipam/ipam.go
@@ -11,10 +11,7 @@ import (
 	"sync"
 	"time"
 
-	"go.uber.org/multierr"
 	corev1 "k8s.io/api/core/v1"
-	apierrors "k8s.io/apimachinery/pkg/api/errors"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	utilerrors "k8s.io/apimachinery/pkg/util/errors"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
@@ -28,7 +25,6 @@ import (
 	"github.com/spidernet-io/spiderpool/pkg/namespacemanager"
 	"github.com/spidernet-io/spiderpool/pkg/nodemanager"
 	"github.com/spidernet-io/spiderpool/pkg/podmanager"
-	"github.com/spidernet-io/spiderpool/pkg/singletons"
 	"github.com/spidernet-io/spiderpool/pkg/statefulsetmanager"
 	subnetmanagercontrollers "github.com/spidernet-io/spiderpool/pkg/subnetmanager/controllers"
 	subnetmanagertypes "github.com/spidernet-io/spiderpool/pkg/subnetmanager/types"
@@ -112,15 +108,14 @@ func (i *ipam) Allocate(ctx context.Context, addArgs *models.IpamAddArgs) (*mode
 		return nil, fmt.Errorf("failed to get Endpoint %s/%s: %v", *addArgs.PodNamespace, *addArgs.PodName, err)
 	}
 
-	podTopController, err := i.podManager.GetPodTopController(ctx, pod)
-	if nil != err {
+	ownerKind, owner, err := i.podManager.GetPodTopController(ctx, pod)
+	if err != nil {
 		return nil, err
 	}
-
-	if i.config.EnableStatefulSet && podTopController.Kind == constant.KindStatefulSet {
+	if i.config.EnableStatefulSet && ownerKind == constant.OwnerStatefulSet {
 		addResp, err := i.retrieveStsIPAllocation(ctx, *addArgs.ContainerID, *addArgs.IfName, pod, endpoint)
 		if err != nil {
-			return nil, fmt.Errorf("failed to retrieve the IP allocation of StatefulSet %s/%s: %w", podTopController.Namespace, podTopController.Name, err)
+			return nil, fmt.Errorf("failed to retrieve the IP allocation of StatefulSet %s/%s: %w", *addArgs.PodNamespace, owner.GetName(), err)
 		}
 		if addResp != nil {
 			return addResp, nil
@@ -135,7 +130,7 @@ func (i *ipam) Allocate(ctx context.Context, addArgs *models.IpamAddArgs) (*mode
 		}
 	}
 
-	addResp, err := i.allocateInStandardMode(ctx, addArgs, pod, endpoint, podTopController)
+	addResp, err := i.allocateInStandardMode(ctx, addArgs, pod, endpoint)
 	if err != nil {
 		return nil, fmt.Errorf("failed to allocate IP addresses in standard mode: %w", err)
 	}
@@ -215,11 +210,11 @@ func (i *ipam) retrieveMultiNICIPAllocation(ctx context.Context, containerID, ni
 	return addResp, nil
 }
 
-func (i *ipam) allocateInStandardMode(ctx context.Context, addArgs *models.IpamAddArgs, pod *corev1.Pod, endpoint *spiderpoolv1.SpiderEndpoint, podController types.PodTopController) (*models.IpamAddResponse, error) {
+func (i *ipam) allocateInStandardMode(ctx context.Context, addArgs *models.IpamAddArgs, pod *corev1.Pod, endpoint *spiderpoolv1.SpiderEndpoint) (*models.IpamAddResponse, error) {
 	logger := logutils.FromContext(ctx)
 	logger.Info("Allocate IP addresses in standard mode")
 
-	toBeAllocatedSet, err := i.genToBeAllocatedSet(ctx, *addArgs.IfName, addArgs.DefaultIPV4IPPool, addArgs.DefaultIPV6IPPool, addArgs.CleanGateway, pod, podController)
+	toBeAllocatedSet, err := i.genToBeAllocatedSet(ctx, *addArgs.IfName, addArgs.DefaultIPV4IPPool, addArgs.DefaultIPV6IPPool, addArgs.CleanGateway, pod)
 	if err != nil {
 		return nil, err
 	}
@@ -265,10 +260,10 @@ func (i *ipam) allocateInStandardMode(ctx context.Context, addArgs *models.IpamA
 	return addResp, nil
 }
 
-func (i *ipam) genToBeAllocatedSet(ctx context.Context, nic string, defaultIPV4IPPool, defaultIPV6IPPool []string, cleanGateway bool, pod *corev1.Pod, podController types.PodTopController) ([]*ToBeAllocated, error) {
+func (i *ipam) genToBeAllocatedSet(ctx context.Context, nic string, defaultIPV4IPPool, defaultIPV6IPPool []string, cleanGateway bool, pod *corev1.Pod) ([]*ToBeAllocated, error) {
 	logger := logutils.FromContext(ctx)
 
-	preliminary, err := i.getPoolCandidates(ctx, nic, defaultIPV4IPPool, defaultIPV6IPPool, cleanGateway, pod, podController)
+	preliminary, err := i.getPoolCandidates(ctx, nic, defaultIPV4IPPool, defaultIPV6IPPool, cleanGateway, pod)
 	if err != nil {
 		return nil, err
 	}
@@ -390,11 +385,11 @@ func (i *ipam) allocateIPFromPoolCandidates(ctx context.Context, c *PoolCandidat
 	return result, nil
 }
 
-func (i *ipam) getPoolCandidates(ctx context.Context, nic string, netConfV4Pool, netConfV6Pool []string, cleanGateway bool, pod *corev1.Pod, podController types.PodTopController) ([]*ToBeAllocated, error) {
+func (i *ipam) getPoolCandidates(ctx context.Context, nic string, netConfV4Pool, netConfV6Pool []string, cleanGateway bool, pod *corev1.Pod) ([]*ToBeAllocated, error) {
 	// Select candidate IPPools through the Pod annotations "ipam.spidernet.io/subnets" or "ipam.spidernet.io/subnet"
 	// if we enable to use SpiderSubnet feature
 	if i.config.EnableSpiderSubnet {
-		fromSubnet, err := i.getPoolFromSubnetAnno(ctx, pod, nic, cleanGateway, podController)
+		fromSubnet, err := i.getPoolFromSubnet(ctx, pod, nic, cleanGateway)
 		if nil != err {
 			return nil, fmt.Errorf("failed to get IPPool from SpiderSubnet, error: %v", err)
 		}
@@ -417,18 +412,6 @@ func (i *ipam) getPoolCandidates(ctx context.Context, nic string, netConfV4Pool,
 		return []*ToBeAllocated{t}, nil
 	}
 
-	// Select candidate IPPools through Cluster Default Subnet with Configmap Spiderpool-conf
-	// if we enable to use SpiderSubnet feature
-	if i.config.EnableSpiderSubnet {
-		fromClusterDefaultSubnet, err := i.getPoolFromClusterDefaultSubnet(ctx, pod, nic, cleanGateway, podController)
-		if nil != err {
-			return nil, err
-		}
-		if fromClusterDefaultSubnet != nil {
-			return []*ToBeAllocated{fromClusterDefaultSubnet}, nil
-		}
-	}
-
 	// Select candidate IPPools through the Namespace annotations
 	// "ipam.spidernet.io/defaultv4ippool" and "ipam.spidernet.io/defaultv6ippool".
 	t, err := i.getPoolFromNS(ctx, pod.Namespace, nic, cleanGateway)
@@ -453,10 +436,9 @@ func (i *ipam) getPoolCandidates(ctx context.Context, nic string, netConfV4Pool,
 	return []*ToBeAllocated{t}, nil
 }
 
-func (i *ipam) getPoolFromSubnetAnno(ctx context.Context, pod *corev1.Pod, nic string, cleanGateway bool, podController types.PodTopController) (*ToBeAllocated, error) {
+func (i *ipam) getPoolFromSubnet(ctx context.Context, pod *corev1.Pod, nic string, cleanGateway bool) (*ToBeAllocated, error) {
 	logger := logutils.FromContext(ctx)
 
-	// get SpiderSubnet configuration from pod annotation
 	subnetAnnoConfig, err := subnetmanagercontrollers.GetSubnetAnnoConfig(pod.Annotations, logger)
 	if nil != err {
 		return nil, err
@@ -488,8 +470,11 @@ func (i *ipam) getPoolFromSubnetAnno(ctx context.Context, pod *corev1.Pod, nic s
 		return nil, fmt.Errorf("the pod subnetAnnotation doesn't specify IPv6 SpiderSubnet")
 	}
 
-	// TODO(Icarus9913): add third party controller support
-	if podController.Kind == constant.KindUnknown {
+	podTopControllerKind, podTopController, err := i.podManager.GetPodTopController(ctx, pod)
+	if nil != err {
+		return nil, fmt.Errorf("failed to get pod top owner reference, error: %v", err)
+	}
+	if podTopControllerKind == constant.OwnerNone || podTopControllerKind == constant.OwnerUnknown {
 		return nil, fmt.Errorf("spider subnet doesn't support pod '%s/%s' owner controller", pod.Namespace, pod.Name)
 	}
 
@@ -498,294 +483,82 @@ func (i *ipam) getPoolFromSubnetAnno(ctx context.Context, pod *corev1.Pod, nic s
 		CleanGateway: cleanGateway,
 	}
 
-	// this function will find the IPPool with the given match labels.
-	// The first return parameter represents the IPPool name, and the second parameter represents whether you need to create IPPool for orphan pod.
-	// If the application is an orphan pod and do not find any IPPool, it will return immediately to inform you to create IPPool.
-	findSubnetIPPool := func(matchLabels client.MatchingLabels) (string, bool, error) {
+	findSubnetIPPool := func(ctx context.Context, matchLabels client.MatchingLabels) (string, error) {
 		var poolName string
-		subnetName := matchLabels[constant.LabelIPPoolOwnerSpiderSubnet]
-		for j := 0; j <= i.config.OperationRetries; j++ {
+		for j := 0; j <= i.config.WaitSubnetPoolRetries; j++ {
 			poolList, err := i.ipPoolManager.ListIPPools(ctx, matchLabels)
 			if nil != err {
-				return "", false, fmt.Errorf("failed to get IPPoolList with labels '%v', error: %v", matchLabels, err)
+				if j == i.config.WaitSubnetPoolRetries {
+					return "", err
+				}
+
+				logger.Sugar().Errorf("failed to get IPPoolList with labels '%v', error: %v", matchLabels, err)
+				continue
 			}
 
 			// validation
 			if poolList == nil || len(poolList.Items) == 0 {
-				// the orphan pod should create its auto IPPool immediately if no IPPool found
-				if podController.Kind == constant.KindPod {
-					return "", true, nil
-				}
-
 				logger.Sugar().Errorf("no '%s' IPPool retrieved from SpiderSubnet '%s', wait for a second and get a retry",
-					matchLabels[constant.LabelIPPoolVersion], subnetName)
-				time.Sleep(i.config.OperationGapDuration)
+					matchLabels[constant.LabelIPPoolVersion], matchLabels[constant.LabelIPPoolOwnerSpiderSubnet])
+				time.Sleep(i.config.WaitSubnetPoolTime)
 				continue
-			} else if len(poolList.Items) == 1 {
-				// check whether the auto IPPool need to scale it desiredIPNumber or not for orphan pod
-				if podController.Kind == constant.KindPod {
-					pool := poolList.Items[0]
-					logger.Sugar().Debugf("found SpiderSubnet '%s' IPPool '%s' and check it whether need to be scaled", subnetName, pool.Name)
-					err = i.subnetManager.CheckScaleIPPool(ctx, &pool, subnetName, 1)
-					if nil != err {
-						return "", false, fmt.Errorf("failed to check IPPool %s whether need to be scaled: %v", pool.Name, err)
-					}
-				}
-			} else {
-				return "", false, fmt.Errorf("it's invalid for '%s/%s/%s' corresponding SpiderSubnet '%s' owns multiple IPPools '%v' for one specify application",
-					podController.Kind, podController.Namespace, podController.Name, subnetName, poolList.Items)
+			} else if len(poolList.Items) > 1 {
+				return "", fmt.Errorf("it's invalid for '%s/%s/%s' corresponding SpiderSubnet '%s' owns multiple IPPools '%v' for one specify application",
+					podTopControllerKind, podTopController.GetNamespace(), podTopController.GetName(), matchLabels[constant.LabelIPPoolOwnerSpiderSubnet], poolList.Items)
 			}
 
 			poolName = poolList.Items[0].Name
 			break
 		}
 		if len(poolName) == 0 {
-			return "", false, fmt.Errorf("no matching IPPool candidate with labels '%v'", matchLabels)
+			return "", fmt.Errorf("no matching IPPool candidate with labels '%v'", matchLabels)
 		}
-		return poolName, false, nil
-	}
-
-	var v4PoolCandidate, v6PoolCandidate string
-	var shouldCreateForOrphanPodV4, shouldCreateForOrphanPodV6 bool
-	var errV4, errV6 error
-	var wg sync.WaitGroup
-
-	// get pod annotation "ipam.spidernet.io/reclaim-ippool"
-	reclaimIPPool, err := subnetmanagercontrollers.ShouldReclaimIPPool(pod.Annotations)
-	if nil != err {
-		return nil, err
+		return poolName, nil
 	}
 
 	// if enableIPv4 is off and get the specified SpiderSubnet IPv4 name, just filter it out
 	if i.config.EnableIPv4 && len(subnetItem.IPv4) != 0 {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-
-			v4PoolCandidate, shouldCreateForOrphanPodV4, errV4 = findSubnetIPPool(client.MatchingLabels{
-				constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
-				constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV4,
-				constant.LabelIPPoolOwnerSpiderSubnet:   subnetItem.IPv4[0],
-				constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
-				constant.LabelIPPoolInterface:           subnetItem.Interface,
-			})
-			if nil != errV4 {
-				return
-			}
-
-			if shouldCreateForOrphanPodV4 {
-				podSelector := &metav1.LabelSelector{MatchLabels: pod.Labels}
-				v4Pool, err := i.subnetManager.AllocateEmptyIPPool(ctx, subnetItem.IPv4[0], podController, podSelector, 1, constant.IPv4, reclaimIPPool, nic)
-				if nil != err {
-					errV4 = err
-					return
-				}
-				v4PoolCandidate = v4Pool.Name
-				// wait for a second to make the spiderpool-controller allocates IP for the IPPool from SpiderSubnet
-				time.Sleep(i.config.OperationGapDuration)
-			}
-		}()
-	}
-
-	// if enableIPv6 is off and get the specified SpiderSubnet IPv6 name, just filter it out
-	if i.config.EnableIPv6 && len(subnetItem.IPv6) != 0 {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-
-			v6PoolCandidate, shouldCreateForOrphanPodV6, errV6 = findSubnetIPPool(client.MatchingLabels{
-				constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
-				constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV6,
-				constant.LabelIPPoolOwnerSpiderSubnet:   subnetItem.IPv6[0],
-				constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
-				constant.LabelIPPoolInterface:           subnetItem.Interface,
-			})
-			if nil != errV6 {
-				return
-			}
-
-			if shouldCreateForOrphanPodV6 {
-				podSelector := &metav1.LabelSelector{MatchLabels: pod.Labels}
-				v6Pool, err := i.subnetManager.AllocateEmptyIPPool(ctx, subnetItem.IPv6[0], podController, podSelector, 1, constant.IPv6, reclaimIPPool, nic)
-				if nil != err {
-					errV6 = err
-					return
-				}
-				v6PoolCandidate = v6Pool.Name
-				// wait for a second to make the spiderpool-controller allocates IP for the IPPool from SpiderSubnet
-				time.Sleep(i.config.OperationGapDuration)
-			}
-		}()
-	}
-
-	wg.Wait()
-
-	if errV4 != nil || errV6 != nil {
-		return nil, multierr.Append(errV4, errV6)
-	}
-
-	if v4PoolCandidate != "" {
-		logger.Sugar().Debugf("add IPv4 subnet IPPool '%s' to PoolCandidates", v4PoolCandidate)
-		result.PoolCandidates = append(result.PoolCandidates, &PoolCandidate{IPVersion: constant.IPv4, Pools: []string{v4PoolCandidate}})
-	}
-	if v6PoolCandidate != "" {
-		logger.Sugar().Debugf("add IPv6 subnet IPPool '%s' to PoolCandidates", v6PoolCandidate)
-		result.PoolCandidates = append(result.PoolCandidates, &PoolCandidate{IPVersion: constant.IPv6, Pools: []string{v6PoolCandidate}})
-	}
-
-	return result, nil
-}
-
-func (i *ipam) getPoolFromClusterDefaultSubnet(ctx context.Context, pod *corev1.Pod, nic string, cleanGateway bool, podController types.PodTopController) (*ToBeAllocated, error) {
-	log := logutils.FromContext(ctx)
-
-	poolIPNum, podSelector, err := getAutoPoolIPNumberAndSelector(pod, podController)
-	if nil != err {
-		return nil, err
-	}
-
-	// get pod annotation "ipam.spidernet.io/reclaim-ippool"
-	reclaimIPPool, err := subnetmanagercontrollers.ShouldReclaimIPPool(pod.Annotations)
-	if nil != err {
-		return nil, err
-	}
-
-	var v4PoolName, v6PoolName string
-	for j := 0; j <= i.config.OperationRetries; j++ {
-		v4PoolName, v6PoolName, err = i.findOrApplyClusterSubnetDefaultIPPool(ctx, podController, podSelector, nic, poolIPNum, reclaimIPPool)
+		v4PoolCandidate, err := findSubnetIPPool(ctx, client.MatchingLabels{
+			constant.LabelIPPoolOwnerApplicationUID: string(podTopController.GetUID()),
+			constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV4,
+			constant.LabelIPPoolOwnerSpiderSubnet:   subnetItem.IPv4[0],
+			constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podTopControllerKind, podTopController.GetNamespace(), podTopController.GetName()),
+			constant.LabelIPPoolInterface:           subnetItem.Interface,
+		})
 		if nil != err {
-			if j == i.config.OperationRetries {
-				return nil, fmt.Errorf("exhaust all retries to find or apply auto-created IPPool: %v", err)
-			}
-			log.Sugar().Errorf("failed to find or apply auto-created IPPool with %d times: %v", j, err)
-			time.Sleep(i.config.OperationGapDuration)
-			continue
+			return nil, err
 		}
-		break
-	}
-
-	// no cluster default subnets
-	if v4PoolName == "" && v6PoolName == "" {
-		return nil, nil
-	}
-
-	result := &ToBeAllocated{
-		NIC:          nic,
-		CleanGateway: cleanGateway,
-	}
 
-	if v4PoolName != "" {
+		logger.Sugar().Debugf("add IPv4 subnet IPPool '%s' to PoolCandidates", v4PoolCandidate)
 		result.PoolCandidates = append(result.PoolCandidates, &PoolCandidate{
 			IPVersion: constant.IPv4,
-			Pools:     []string{v4PoolName},
+			Pools:     []string{v4PoolCandidate},
 		})
 	}
 
-	if v6PoolName != "" {
+	// if enableIPv6 is off and get the specified SpiderSubnet IPv6 name, just filter it out
+	if i.config.EnableIPv6 && len(subnetItem.IPv6) != 0 {
+		v6PoolCandidate, err := findSubnetIPPool(ctx, client.MatchingLabels{
+			constant.LabelIPPoolOwnerApplicationUID: string(podTopController.GetUID()),
+			constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV6,
+			constant.LabelIPPoolOwnerSpiderSubnet:   subnetItem.IPv6[0],
+			constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podTopControllerKind, podTopController.GetNamespace(), podTopController.GetName()),
+			constant.LabelIPPoolInterface:           subnetItem.Interface,
+		})
+		if nil != err {
+			return nil, err
+		}
+
+		logger.Sugar().Debugf("add IPv6 subnet IPPool '%s' to PoolCandidates", v6PoolCandidate)
 		result.PoolCandidates = append(result.PoolCandidates, &PoolCandidate{
 			IPVersion: constant.IPv6,
-			Pools:     []string{v6PoolName},
+			Pools:     []string{v6PoolCandidate},
 		})
 	}
 
 	return result, nil
 }
 
-// findOrApplyClusterSubnetDefaultIPPool serves for cluster default subnet usage.
-// This will create auto-created IPPool or update auto-created IPPool desired IP number
-func (i *ipam) findOrApplyClusterSubnetDefaultIPPool(ctx context.Context, podController types.PodTopController, podSelector *metav1.LabelSelector, ifName string, poolIPNum int, reclaimIPPool bool) (v4PoolName, v6PoolName string, err error) {
-	log := logutils.FromContext(ctx)
-
-	var clusterDefaultV4Subnet, clusterDefaultV6Subnet string
-
-	if len(singletons.ClusterDefaultPool.ClusterDefaultIPv4Subnet) != 0 {
-		clusterDefaultV4Subnet = singletons.ClusterDefaultPool.ClusterDefaultIPv4Subnet[0]
-	}
-	if len(singletons.ClusterDefaultPool.ClusterDefaultIPv6Subnet) != 0 {
-		clusterDefaultV6Subnet = singletons.ClusterDefaultPool.ClusterDefaultIPv6Subnet[0]
-	}
-	// no cluster default subnet specified
-	if (i.config.EnableIPv4 && clusterDefaultV4Subnet == "") || (i.config.EnableIPv6 && clusterDefaultV6Subnet == "") {
-		return "", "", nil
-	}
-
-	fn := func(poolList *spiderpoolv1.SpiderIPPoolList, subnetName string, ipVersion types.IPVersion) (string, error) {
-		if poolList == nil || len(poolList.Items) == 0 {
-			log.Sugar().Debugf("there's no 'IPv%d' IPPoolList retrieved from cluster default SpiderSubent '%s'", ipVersion, subnetName)
-			pool, err := i.subnetManager.AllocateEmptyIPPool(ctx, subnetName, podController, podSelector, poolIPNum, ipVersion, reclaimIPPool, ifName)
-			if nil != err {
-				return "", err
-			}
-			return pool.Name, nil
-		} else if len(poolList.Items) == 1 {
-			pool := poolList.Items[0].DeepCopy()
-			log.Sugar().Debugf("found cluster default SpiderSubnet '%s' IPPool '%s' and check it whether need to be scaled", subnetName, pool.Name)
-			err = i.subnetManager.CheckScaleIPPool(ctx, pool, subnetName, poolIPNum)
-			if nil != err {
-				return "", err
-			}
-			return pool.Name, nil
-		} else {
-			return "", fmt.Errorf("%w: it's invalid that SpiderSubnet '%s' owns multiple IPPools '%v' for one specify application", constant.ErrWrongInput, subnetName, poolList.Items)
-		}
-	}
-
-	var errV4, errV6 error
-	var wg sync.WaitGroup
-
-	if i.config.EnableIPv4 && clusterDefaultV4Subnet != "" {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-
-			matchLabels := client.MatchingLabels{
-				constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
-				constant.LabelIPPoolOwnerSpiderSubnet:   clusterDefaultV4Subnet,
-				constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
-				constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV4,
-				constant.LabelIPPoolInterface:           ifName,
-			}
-			v4PoolList, err := i.ipPoolManager.ListIPPools(ctx, matchLabels)
-			if nil != err {
-				errV4 = fmt.Errorf("failed to get IPv4 IPPoolList with labels '%v', error: %v", matchLabels, err)
-				return
-			}
-
-			v4PoolName, errV4 = fn(v4PoolList.DeepCopy(), clusterDefaultV4Subnet, constant.IPv4)
-		}()
-	}
-
-	if i.config.EnableIPv6 && clusterDefaultV6Subnet != "" {
-		wg.Add(1)
-		go func() {
-			defer wg.Done()
-
-			matchLabels := client.MatchingLabels{
-				constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
-				constant.LabelIPPoolOwnerSpiderSubnet:   clusterDefaultV6Subnet,
-				constant.LabelIPPoolOwnerApplication:    subnetmanagercontrollers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
-				constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV6,
-				constant.LabelIPPoolInterface:           ifName,
-			}
-			v6PoolList, err := i.ipPoolManager.ListIPPools(ctx, matchLabels)
-			if nil != err {
-				errV6 = fmt.Errorf("failed to get IPv6 IPPoolList with labels '%v', error: %v", matchLabels, err)
-				return
-			}
-
-			v6PoolName, errV6 = fn(v6PoolList.DeepCopy(), clusterDefaultV6Subnet, constant.IPv6)
-		}()
-	}
-
-	wg.Wait()
-
-	if errV4 != nil || errV6 != nil {
-		return "", "", multierr.Append(errV4, errV6)
-	}
-
-	return
-}
-
 func (i *ipam) getPoolFromNS(ctx context.Context, namespace, nic string, cleanGateway bool) (*ToBeAllocated, error) {
 	logger := logutils.FromContext(ctx)
 
@@ -936,41 +709,32 @@ func (i *ipam) Release(ctx context.Context, delArgs *models.IpamDelArgs) error {
 	allocation, currently := workloadendpointmanager.RetrieveIPAllocation(*delArgs.ContainerID, *delArgs.IfName, true, endpoint)
 	if allocation == nil {
 		logger.Info("Nothing retrieved for releasing")
-	} else {
-		if !currently {
-			logger.Warn("Request to release non current IP allocation, concurrency may exist between the same Pod")
-		}
-
-		// check whether the StatefulSet pod need to release
-		// ref: https://github.com/spidernet-io/spiderpool/issues/1045
-		if i.config.EnableStatefulSet && endpoint.Status.OwnerControllerType == constant.KindStatefulSet {
-			shouldCleanSts, err := i.shouldReleaseStatefulSet(ctx, endpoint.Namespace, endpoint.Name, allocation, currently)
-			if nil != err {
-				return err
-			}
-			if !shouldCleanSts {
-				logger.Info("No need to release the IP allocation of an StatefulSet whose scale is not reduced")
-				return nil
-			}
-		}
+		return nil
+	}
+	if !currently {
+		logger.Warn("Request to release non current IP allocation, concurrency may exist between the same Pod")
+	}
 
-		if err = i.release(ctx, allocation.ContainerID, allocation.IPs); err != nil {
+	// check whether the StatefulSet pod need to release
+	// ref: https://github.com/spidernet-io/spiderpool/issues/1045
+	if i.config.EnableStatefulSet && endpoint.Status.OwnerControllerType == constant.OwnerStatefulSet {
+		shouldCleanSts, err := i.shouldReleaseStatefulSet(ctx, endpoint.Namespace, endpoint.Name, allocation, currently)
+		if nil != err {
 			return err
 		}
-		if err := i.weManager.ClearCurrentIPAllocation(ctx, *delArgs.ContainerID, endpoint); err != nil {
-			return fmt.Errorf("failed to clear current IP allocation: %v", err)
+		if !shouldCleanSts {
+			logger.Info("No need to release the IP allocation of an StatefulSet whose scale is not reduced")
+			return nil
 		}
-		logger.Sugar().Infof("Succeed to release: %+v", allocation.IPs)
 	}
 
-	// this serves for orphan pod if SpiderSubnet feature is enabled
-	if i.config.EnableSpiderSubnet {
-		logger.Info("try to check whether need to delete dead orphan pod's auto-created IPPool")
-		err = i.deleteDeadOrphanPodAutoIPPool(ctx, *delArgs.PodNamespace, *delArgs.PodName, *delArgs.IfName)
-		if nil != err {
-			return fmt.Errorf("failed to delete dead orphan pod auto-created IPPool: %v", err)
-		}
+	if err = i.release(ctx, allocation.ContainerID, allocation.IPs); err != nil {
+		return err
+	}
+	if err := i.weManager.ClearCurrentIPAllocation(ctx, *delArgs.ContainerID, endpoint); err != nil {
+		return fmt.Errorf("failed to clear current IP allocation: %v", err)
 	}
+	logger.Sugar().Infof("Succeed to release: %+v", allocation.IPs)
 
 	return nil
 }
@@ -981,7 +745,7 @@ func (i *ipam) Release(ctx context.Context, delArgs *models.IpamDelArgs) error {
 func (i *ipam) shouldReleaseStatefulSet(ctx context.Context, podNamespace, podName string, allocation *spiderpoolv1.PodIPAllocation, currently bool) (bool, error) {
 	log := logutils.FromContext(ctx)
 
-	isValidStsPod, err := i.stsManager.IsValidStatefulSetPod(ctx, podNamespace, podName, constant.KindStatefulSet)
+	isValidStsPod, err := i.stsManager.IsValidStatefulSetPod(ctx, podNamespace, podName, constant.OwnerStatefulSet)
 	if err != nil {
 		return false, err
 	}
@@ -1053,48 +817,3 @@ func (i *ipam) release(ctx context.Context, containerID string, details []spider
 func (i *ipam) Start(ctx context.Context) error {
 	return i.ipamLimiter.Start(ctx)
 }
-
-// deleteDeadOrphanPodAutoIPPool will delete orphan pod corresponding IPPools
-func (i *ipam) deleteDeadOrphanPodAutoIPPool(ctx context.Context, podNS, podName, ifName string) error {
-	log := logutils.FromContext(ctx)
-
-	isAlive := true
-	pod, err := i.podManager.GetPodByName(ctx, podNS, podName)
-	if nil != err {
-		if apierrors.IsNotFound(err) {
-			isAlive = false
-			log.Debug("pod is already deleted, try to delete its auto-created IPPool")
-		} else {
-			return fmt.Errorf("failed to get pod: %v", err)
-		}
-	} else {
-		if pod.DeletionTimestamp != nil {
-			isAlive = false
-			log.Debug("pod is terminating, try to delete its auto-created IPPool")
-		}
-	}
-
-	if !isAlive {
-		matchLabels := client.MatchingLabels{
-			// this label make it sure to find orphan pod corresponding IPPool
-			constant.LabelIPPoolOwnerApplication: subnetmanagercontrollers.AppLabelValue(constant.KindPod, podNS, podName),
-			// TODO(Icarus9913): should we delete all interfaces auto-created IPPool in the first cmdDel?
-			constant.LabelIPPoolInterface:     ifName,
-			constant.LabelIPPoolReclaimIPPool: constant.True,
-		}
-		poolList, err := i.ipPoolManager.ListIPPools(ctx, matchLabels)
-		if nil != err {
-			return fmt.Errorf("failed to get IPPoolList with the given label '%v', error: %v", matchLabels, err)
-		}
-
-		log.Sugar().Infof("found orphan pod corresponding IPPool list: %v, try to delete them", poolList.Items)
-		for index := range poolList.Items {
-			err = i.ipPoolManager.DeleteIPPool(ctx, &poolList.Items[index])
-			if nil != err {
-				return err
-			}
-		}
-	}
-
-	return nil
-}
diff --git a/pkg/ipam/utils.go b/pkg/ipam/utils.go
index e4eda96c0..88f4488b8 100644
--- a/pkg/ipam/utils.go
+++ b/pkg/ipam/utils.go
@@ -9,17 +9,12 @@ import (
 	"fmt"
 	"net"
 
-	appsv1 "k8s.io/api/apps/v1"
-	batchv1 "k8s.io/api/batch/v1"
 	corev1 "k8s.io/api/core/v1"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 
 	"github.com/spidernet-io/spiderpool/api/v1/agent/models"
 	"github.com/spidernet-io/spiderpool/pkg/constant"
 	spiderpoolip "github.com/spidernet-io/spiderpool/pkg/ip"
 	"github.com/spidernet-io/spiderpool/pkg/logutils"
-	"github.com/spidernet-io/spiderpool/pkg/singletons"
-	subnetmanagercontrollers "github.com/spidernet-io/spiderpool/pkg/subnetmanager/controllers"
 	"github.com/spidernet-io/spiderpool/pkg/types"
 )
 
@@ -185,78 +180,3 @@ func groupCustomRoutesByGW(ctx context.Context, customRoutes *[]*models.Route, i
 
 	return routes, nil
 }
-
-// getAutoPoolIPNumberAndSelector calculates the auto-created IPPool IP number with the given params pod and pod top controller.
-// If it's an orphan pod, it will return 1.
-func getAutoPoolIPNumberAndSelector(pod *corev1.Pod, podController types.PodTopController) (int, *metav1.LabelSelector, error) {
-	var appReplicas int
-	var podSelector *metav1.LabelSelector
-	var isThirdPartyController bool
-
-	switch podController.Kind {
-	// orphan pod
-	case constant.KindPod:
-		return 1, &metav1.LabelSelector{MatchLabels: pod.Labels}, nil
-	case constant.KindDeployment:
-		deployment := podController.App.(*appsv1.Deployment)
-		appReplicas = subnetmanagercontrollers.GetAppReplicas(deployment.Spec.Replicas)
-		podSelector = deployment.Spec.Selector
-	case constant.KindReplicaSet:
-		replicaSet := podController.App.(*appsv1.ReplicaSet)
-		podSelector = replicaSet.Spec.Selector
-		appReplicas = subnetmanagercontrollers.GetAppReplicas(replicaSet.Spec.Replicas)
-	case constant.KindStatefulSet:
-		statefulSet := podController.App.(*appsv1.StatefulSet)
-		appReplicas = subnetmanagercontrollers.GetAppReplicas(statefulSet.Spec.Replicas)
-		podSelector = statefulSet.Spec.Selector
-	case constant.KindDaemonSet:
-		daemonSet := podController.App.(*appsv1.DaemonSet)
-		appReplicas = int(daemonSet.Status.DesiredNumberScheduled)
-		podSelector = daemonSet.Spec.Selector
-	case constant.KindJob:
-		job := podController.App.(*batchv1.Job)
-		appReplicas = subnetmanagercontrollers.CalculateJobPodNum(job.Spec.Parallelism, job.Spec.Completions)
-		podSelector = job.Spec.Selector
-	case constant.KindCronJob:
-		cronJob := podController.App.(*batchv1.CronJob)
-		appReplicas = subnetmanagercontrollers.CalculateJobPodNum(cronJob.Spec.JobTemplate.Spec.Parallelism, cronJob.Spec.JobTemplate.Spec.Completions)
-		podSelector = cronJob.Spec.JobTemplate.Spec.Selector
-	default:
-		isThirdPartyController = true
-	}
-
-	var flexibleIPNum int
-	poolIPNumStr, ok := pod.Annotations[constant.AnnoSpiderSubnetPoolIPNumber]
-	if ok {
-		isFlexible, ipNum, err := subnetmanagercontrollers.GetPoolIPNumber(poolIPNumStr)
-		if nil != err {
-			return -1, nil, err
-		}
-
-		// check out negative number
-		if ipNum < 0 {
-			return -1, nil, fmt.Errorf("subnet '%s' value must equal or greater than 0", constant.AnnoSpiderSubnetPoolIPNumber)
-		}
-
-		// fixed IP number, just return it
-		if !isFlexible {
-			return ipNum, podSelector, nil
-		}
-
-		// flexible IP Number
-		flexibleIPNum = ipNum
-	} else {
-		// third party controller only supports fixed auto-created IPPool IP number
-		if isThirdPartyController {
-			return -1, nil, fmt.Errorf("%s/%s/%s only supports fixed auto-created IPPool IP Number", podController.Kind, podController.Namespace, podController.Name)
-		}
-
-		// use cluster subnet default flexible IP number
-		flexibleIPNum = singletons.ClusterDefaultPool.ClusterSubnetDefaultFlexibleIPNumber
-	}
-
-	// collect application replicas and custom flexible IP number
-	poolIPNum := appReplicas + flexibleIPNum
-
-	return poolIPNum, podSelector, nil
-}
diff --git a/pkg/ippoolmanager/ippool_informer.go b/pkg/ippoolmanager/ippool_informer.go
index 13941d8c0..08c6ce464 100644
--- a/pkg/ippoolmanager/ippool_informer.go
+++ b/pkg/ippoolmanager/ippool_informer.go
@@ -482,22 +482,20 @@ func (c *poolInformerController) cleanAutoIPPoolLegacy(ctx context.Context, pool
 
 		var object client.Object
 		switch kind {
-		case constant.KindDeployment:
+		case constant.OwnerDeployment:
 			object = &appsv1.Deployment{}
-		case constant.KindReplicaSet:
+		case constant.OwnerReplicaSet:
 			object = &appsv1.ReplicaSet{}
-		case constant.KindDaemonSet:
+		case constant.OwnerDaemonSet:
 			object = &appsv1.DaemonSet{}
-		case constant.KindStatefulSet:
+		case constant.OwnerStatefulSet:
 			object = &appsv1.StatefulSet{}
-		case constant.KindJob:
+		case constant.OwnerJob:
 			object = &batchv1.Job{}
-		case constant.KindCronJob:
+		case constant.OwnerCronJob:
 			object = &batchv1.CronJob{}
 		default:
-			// pod and other controllers will clean up legacy ippools in IPAM
-			return false, nil
-			//return false, fmt.Errorf("%w: unmatched application kind '%s'", constant.ErrWrongInput, kind)
+			return false, fmt.Errorf("%w: unmatched application kind '%s'", constant.ErrWrongInput, kind)
 		}
 
 		enableDelete := false
diff --git a/pkg/ippoolmanager/ippool_manager.go b/pkg/ippoolmanager/ippool_manager.go
index 8ef8004ed..2a88d362f 100644
--- a/pkg/ippoolmanager/ippool_manager.go
+++ b/pkg/ippoolmanager/ippool_manager.go
@@ -136,7 +136,7 @@ func (im *ipPoolManager) AllocateIP(ctx context.Context, poolName, containerID,
 			ipPool.Status.AllocatedIPs = spiderpoolv1.PoolIPAllocations{}
 		}
 
-		podTopController, err := im.podManager.GetPodTopController(ctx, pod)
+		ownerKind, owner, err := im.podManager.GetPodTopController(ctx, pod)
 		if err != nil {
 			return nil, nil, err
 		}
@@ -146,10 +146,11 @@ func (im *ipPoolManager) AllocateIP(ctx context.Context, poolName, containerID,
 			Node:                pod.Spec.NodeName,
 			Namespace:           pod.Namespace,
 			Pod:                 pod.Name,
-			OwnerControllerType: podTopController.Kind,
+			OwnerControllerType: ownerKind,
+		}
+		if owner != nil {
+			allocation.OwnerControllerName = owner.GetName()
 		}
-		allocation.OwnerControllerName = podTopController.Name
-
 		ipPool.Status.AllocatedIPs[allocatedIP.String()] = allocation
 
 		if ipPool.Status.AllocatedIPCount == nil {
@@ -318,7 +319,7 @@ func (im *ipPoolManager) UpdateAllocatedIPs(ctx context.Context, containerID str
 			Node:                pod.Spec.NodeName,
 			Namespace:           pod.Namespace,
 			Pod:                 pod.Name,
-			OwnerControllerType: constant.KindStatefulSet,
+			OwnerControllerType: constant.OwnerStatefulSet,
 		}
 
 		err = im.client.Status().Update(ctx, pool)
@@ -430,7 +431,7 @@ func (im *ipPoolManager) UpdateDesiredIPNumber(ctx context.Context, pool *spider
 	*pool.Status.AutoDesiredIPCount = int64(ipNum)
 	err := im.client.Status().Update(ctx, pool)
 	if nil != err {
-		return fmt.Errorf("failed to update IPPool '%s' auto desired IP count to %d : %v", pool.Name, ipNum, err)
+		return err
 	}
 
 	return nil
diff --git a/pkg/podmanager/mock/pod_manager_mock.go b/pkg/podmanager/mock/pod_manager_mock.go
index ec361bc3c..5df9a809f 100644
--- a/pkg/podmanager/mock/pod_manager_mock.go
+++ b/pkg/podmanager/mock/pod_manager_mock.go
@@ -2,7 +2,7 @@
 // SPDX-License-Identifier: Apache-2.0
 
 // Code generated by MockGen. DO NOT EDIT.
-// Source: pod_manager.go
+// Source: ./pkg/podmanager/pod_manager.go
 
 // Package mock_podmanager is a generated GoMock package.
 package mock_podmanager
@@ -12,7 +12,6 @@ import (
 	reflect "reflect"
 
 	gomock "github.com/golang/mock/gomock"
-	types "github.com/spidernet-io/spiderpool/pkg/types"
 	v1 "k8s.io/api/core/v1"
 	v10 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	client "sigs.k8s.io/controller-runtime/pkg/client"
@@ -57,12 +56,13 @@ func (mr *MockPodManagerMockRecorder) GetPodByName(ctx, namespace, podName inter
 }
 
 // GetPodTopController mocks base method.
-func (m *MockPodManager) GetPodTopController(ctx context.Context, pod *v1.Pod) (types.PodTopController, error) {
+func (m *MockPodManager) GetPodTopController(ctx context.Context, pod *v1.Pod) (string, v10.Object, error) {
 	m.ctrl.T.Helper()
 	ret := m.ctrl.Call(m, "GetPodTopController", ctx, pod)
-	ret0, _ := ret[0].(types.PodTopController)
-	ret1, _ := ret[1].(error)
-	return ret0, ret1
+	ret0, _ := ret[0].(string)
+	ret1, _ := ret[1].(v10.Object)
+	ret2, _ := ret[2].(error)
+	return ret0, ret1, ret2
 }
 
 // GetPodTopController indicates an expected call of GetPodTopController.
diff --git a/pkg/podmanager/pod_manager.go b/pkg/podmanager/pod_manager.go
index 57933978a..be40bec4f 100644
--- a/pkg/podmanager/pod_manager.go
+++ b/pkg/podmanager/pod_manager.go
@@ -19,7 +19,6 @@ import (
 
 	"github.com/spidernet-io/spiderpool/pkg/constant"
 	"github.com/spidernet-io/spiderpool/pkg/logutils"
-	"github.com/spidernet-io/spiderpool/pkg/types"
 )
 
 type PodManager interface {
@@ -27,7 +26,7 @@ type PodManager interface {
 	ListPods(ctx context.Context, opts ...client.ListOption) (*corev1.PodList, error)
 	MatchLabelSelector(ctx context.Context, namespace, podName string, labelSelector *metav1.LabelSelector) (bool, error)
 	MergeAnnotations(ctx context.Context, namespace, podName string, annotations map[string]string) error
-	GetPodTopController(ctx context.Context, pod *corev1.Pod) (types.PodTopController, error)
+	GetPodTopController(ctx context.Context, pod *corev1.Pod) (appKind string, app metav1.Object, err error)
 }
 
 type podManager struct {
@@ -125,31 +124,13 @@ func (pm *podManager) MergeAnnotations(ctx context.Context, namespace, podName s
 // GetPodTopController will find the pod top owner controller with the given pod.
 // For example, once we create a deployment then it will create replicaset and the replicaset will create pods.
 // So, the pods' top owner is deployment. That's what the method implements.
-// Notice: if the application is a third party controller, the types.PodTopController property App would be nil!
-func (pm *podManager) GetPodTopController(ctx context.Context, pod *corev1.Pod) (types.PodTopController, error) {
+func (pm *podManager) GetPodTopController(ctx context.Context, pod *corev1.Pod) (appKind string, app metav1.Object, err error) {
 	logger := logutils.FromContext(ctx)
-
 	var ownerErr = fmt.Errorf("failed to get pod '%s/%s' owner", pod.Namespace, pod.Name)
 
 	podOwner := metav1.GetControllerOf(pod)
 	if podOwner == nil {
-		return types.PodTopController{
-			Kind:      constant.KindPod,
-			Namespace: pod.Namespace,
-			Name:      pod.Name,
-			Uid:       pod.UID,
-			App:       pod,
-		}, nil
-	}
-
-	// third party controller
-	if podOwner.APIVersion != appsv1.SchemeGroupVersion.String() && podOwner.APIVersion != batchv1.SchemeGroupVersion.String() {
-		return types.PodTopController{
-			Kind:      constant.KindUnknown,
-			Namespace: pod.Namespace,
-			Name:      podOwner.Name,
-			Uid:       podOwner.UID,
-		}, nil
+		return constant.OwnerNone, nil, nil
 	}
 
 	namespacedName := apitypes.NamespacedName{
@@ -158,119 +139,68 @@ func (pm *podManager) GetPodTopController(ctx context.Context, pod *corev1.Pod)
 	}
 
 	switch podOwner.Kind {
-	case constant.KindReplicaSet:
+	case constant.OwnerReplicaSet:
 		var replicaset appsv1.ReplicaSet
-		err := pm.client.Get(ctx, namespacedName, &replicaset)
+		err = pm.client.Get(ctx, namespacedName, &replicaset)
 		if nil != err {
-			return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+			return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 		}
 
 		replicasetOwner := metav1.GetControllerOf(&replicaset)
 		if replicasetOwner != nil {
-			if replicasetOwner.Kind == constant.KindDeployment {
+			if replicasetOwner.Kind == constant.OwnerDeployment {
 				var deployment appsv1.Deployment
 				err = pm.client.Get(ctx, apitypes.NamespacedName{Namespace: replicaset.Namespace, Name: replicasetOwner.Name}, &deployment)
 				if nil != err {
-					return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+					return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 				}
-				return types.PodTopController{
-					Kind:      constant.KindDeployment,
-					Namespace: deployment.Namespace,
-					Name:      deployment.Name,
-					Uid:       deployment.UID,
-					App:       &deployment,
-				}, nil
+				return constant.OwnerDeployment, &deployment, nil
 			}
 
 			logger.Sugar().Warnf("the controller type '%s' of pod '%s/%s' is unknown", replicasetOwner.Kind, pod.Namespace, pod.Name)
-			return types.PodTopController{
-				Kind:      constant.KindUnknown,
-				Namespace: pod.Namespace,
-				Name:      replicasetOwner.Name,
-				Uid:       replicasetOwner.UID,
-			}, nil
+			return constant.OwnerUnknown, nil, nil
 		}
-		return types.PodTopController{
-			Kind:      constant.KindReplicaSet,
-			Namespace: replicaset.Namespace,
-			Name:      replicaset.Name,
-			Uid:       replicaset.UID,
-			App:       &replicaset,
-		}, nil
-
-	case constant.KindJob:
+		return constant.OwnerReplicaSet, &replicaset, nil
+
+	case constant.OwnerJob:
 		var job batchv1.Job
-		err := pm.client.Get(ctx, namespacedName, &job)
+		err = pm.client.Get(ctx, namespacedName, &job)
 		if nil != err {
-			return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+			return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 		}
 		jobOwner := metav1.GetControllerOf(&job)
 		if jobOwner != nil {
-			if jobOwner.Kind == constant.KindCronJob {
+			if jobOwner.Kind == constant.OwnerCronJob {
 				var cronJob batchv1.CronJob
 				err = pm.client.Get(ctx, apitypes.NamespacedName{Namespace: job.Namespace, Name: jobOwner.Name}, &cronJob)
 				if nil != err {
-					return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+					return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 				}
-				return types.PodTopController{
-					Kind:      constant.KindCronJob,
-					Namespace: cronJob.Namespace,
-					Name:      cronJob.Name,
-					Uid:       cronJob.UID,
-					App:       &cronJob,
-				}, nil
+				return constant.OwnerCronJob, &cronJob, nil
 			}
 
 			logger.Sugar().Warnf("the controller type '%s' of pod '%s/%s' is unknown", jobOwner.Kind, pod.Namespace, pod.Name)
-			return types.PodTopController{
-				Kind:      constant.KindUnknown,
-				Namespace: job.Namespace,
-				Name:      jobOwner.Name,
-				Uid:       jobOwner.UID,
-			}, nil
+			return constant.OwnerUnknown, nil, nil
 		}
-		return types.PodTopController{
-			Kind:      constant.KindJob,
-			Namespace: job.Namespace,
-			Name:      job.Name,
-			Uid:       job.UID,
-			App:       &job,
-		}, nil
-
-	case constant.KindDaemonSet:
+		return constant.OwnerJob, &job, nil
+
+	case constant.OwnerDaemonSet:
 		var daemonSet appsv1.DaemonSet
-		err := pm.client.Get(ctx, namespacedName, &daemonSet)
+		err = pm.client.Get(ctx, namespacedName, &daemonSet)
 		if nil != err {
-			return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+			return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 		}
-		return types.PodTopController{
-			Kind:      constant.KindDaemonSet,
-			Namespace: daemonSet.Namespace,
-			Name:      daemonSet.Name,
-			Uid:       daemonSet.UID,
-			App:       &daemonSet,
-		}, nil
-
-	case constant.KindStatefulSet:
+		return constant.OwnerDaemonSet, &daemonSet, nil
+
+	case constant.OwnerStatefulSet:
 		var statefulSet appsv1.StatefulSet
-		err := pm.client.Get(ctx, namespacedName, &statefulSet)
+		err = pm.client.Get(ctx, namespacedName, &statefulSet)
 		if nil != err {
-			return types.PodTopController{}, fmt.Errorf("%w: %v", ownerErr, err)
+			return "", nil, fmt.Errorf("%w: %v", ownerErr, err)
 		}
-		return types.PodTopController{
-			Kind:      constant.KindStatefulSet,
-			Namespace: statefulSet.Namespace,
-			Name:      statefulSet.Name,
-			Uid:       statefulSet.UID,
-			App:       &statefulSet,
-		}, nil
+		return constant.OwnerStatefulSet, &statefulSet, nil
 	}
 
 	logger.Sugar().Warnf("the controller type '%s' of pod '%s/%s' is unknown", podOwner.Kind, pod.Namespace, pod.Name)
-	return types.PodTopController{
-		Kind:      constant.KindUnknown,
-		Namespace: pod.Namespace,
-		Name:      podOwner.Name,
-		Uid:       podOwner.UID,
-	}, nil
+	return constant.OwnerUnknown, nil, nil
 }
diff --git a/pkg/statefulsetmanager/sts_manager.go b/pkg/statefulsetmanager/sts_manager.go
index 524e36354..7f887c245 100644
--- a/pkg/statefulsetmanager/sts_manager.go
+++ b/pkg/statefulsetmanager/sts_manager.go
@@ -56,7 +56,7 @@ func (sm *statefulSetManager) ListStatefulSets(ctx context.Context, opts ...clie
 // Once the pod's controller StatefulSet was deleted, the pod's corresponding IPPool IP and Endpoint need to be cleaned up.
 // Or the pod's controller StatefulSet decreased its replicas and the pod's index is out of replicas, it needs to be cleaned up too.
 func (sm *statefulSetManager) IsValidStatefulSetPod(ctx context.Context, namespace, podName, podControllerType string) (bool, error) {
-	if podControllerType != constant.KindStatefulSet {
+	if podControllerType != constant.OwnerStatefulSet {
 		return false, fmt.Errorf("pod '%s/%s' is controlled by '%s' instead of StatefulSet", namespace, podName, podControllerType)
 	}
 
diff --git a/pkg/subnetmanager/app_controller.go b/pkg/subnetmanager/app_controller.go
index b120ffa48..9d399501d 100644
--- a/pkg/subnetmanager/app_controller.go
+++ b/pkg/subnetmanager/app_controller.go
@@ -97,7 +97,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 
 		switch newObject := newObj.(type) {
 		case *appsv1.Deployment:
-			appKind = constant.KindDeployment
+			appKind = constant.OwnerDeployment
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -146,7 +146,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 			}
 
 		case *appsv1.ReplicaSet:
-			appKind = constant.KindReplicaSet
+			appKind = constant.OwnerReplicaSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -195,7 +195,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 			}
 
 		case *appsv1.StatefulSet:
-			appKind = constant.KindStatefulSet
+			appKind = constant.OwnerStatefulSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -244,7 +244,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 			}
 
 		case *batchv1.Job:
-			appKind = constant.KindJob
+			appKind = constant.OwnerJob
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -293,7 +293,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 			}
 
 		case *batchv1.CronJob:
-			appKind = constant.KindCronJob
+			appKind = constant.OwnerCronJob
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -342,7 +342,7 @@ func (c *appController) ControllerAddOrUpdateHandler() controllers.AppInformersA
 			}
 
 		case *appsv1.DaemonSet:
-			appKind = constant.KindDaemonSet
+			appKind = constant.OwnerDaemonSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", newObject.GetNamespace(), newObject.GetName())))
 
 			// no need reconcile for HostNetwork application
@@ -612,7 +612,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 	var appReplicas int
 
 	switch appKey.AppKind {
-	case constant.KindDeployment:
+	case constant.OwnerDeployment:
 		deployment, err := c.deploymentsLister.Deployments(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -627,7 +627,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 		appReplicas = controllers.GetAppReplicas(deployment.Spec.Replicas)
 		app = deployment.DeepCopy()
 
-	case constant.KindReplicaSet:
+	case constant.OwnerReplicaSet:
 		replicaSet, err := c.replicaSetLister.ReplicaSets(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -642,7 +642,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 		appReplicas = controllers.GetAppReplicas(replicaSet.Spec.Replicas)
 		app = replicaSet.DeepCopy()
 
-	case constant.KindDaemonSet:
+	case constant.OwnerDaemonSet:
 		daemonSet, err := c.daemonSetLister.DaemonSets(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -657,7 +657,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 		appReplicas = int(daemonSet.Status.DesiredNumberScheduled)
 		app = daemonSet.DeepCopy()
 
-	case constant.KindStatefulSet:
+	case constant.OwnerStatefulSet:
 		statefulSet, err := c.statefulSetLister.StatefulSets(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -672,7 +672,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 		appReplicas = controllers.GetAppReplicas(statefulSet.Spec.Replicas)
 		app = statefulSet.DeepCopy()
 
-	case constant.KindJob:
+	case constant.OwnerJob:
 		job, err := c.jobLister.Jobs(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -687,7 +687,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 		appReplicas = controllers.CalculateJobPodNum(job.Spec.Parallelism, job.Spec.Completions)
 		app = job.DeepCopy()
 
-	case constant.KindCronJob:
+	case constant.OwnerCronJob:
 		cronJob, err := c.cronJobLister.CronJobs(namespace).Get(name)
 		if nil != err {
 			if apierrors.IsNotFound(err) {
@@ -712,17 +712,7 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 	}
 
 	log.Debug("Going to create IPPool or mark IPPool desired IP number")
-	err = c.createOrMarkIPPool(logutils.IntoContext(context.TODO(), log),
-		*subnetConfig,
-		types.PodTopController{
-			Kind:      appKey.AppKind,
-			Namespace: app.GetNamespace(),
-			Name:      app.GetName(),
-			Uid:       app.GetUID(),
-			App:       app,
-		},
-		podSelector,
-		appReplicas)
+	err = c.createOrMarkIPPool(logutils.IntoContext(context.TODO(), log), *subnetConfig, appKey.AppKind, app, podSelector, appReplicas)
 	if nil != err {
 		return fmt.Errorf("failed to create or scale IPPool, error: %w", err)
 	}
@@ -732,11 +722,11 @@ func (c *appController) syncHandler(appKey appWorkQueueKey, log *zap.Logger) (er
 
 // createOrMarkIPPool try to create an IPPool or mark IPPool desired IP number with the give SpiderSubnet configuration
 func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig types.PodSubnetAnnoConfig,
-	podController types.PodTopController, podSelector *metav1.LabelSelector, appReplicas int) error {
+	appKind string, app metav1.Object, podSelector *metav1.LabelSelector, appReplicas int) error {
 	log := logutils.FromContext(ctx)
 
 	// retrieve application pools
-	fn := func(poolList *spiderpoolv1.SpiderIPPoolList, subnetName string, ipVersion types.IPVersion, ifName string) (err error) {
+	fn := func(ctx context.Context, poolList *spiderpoolv1.SpiderIPPoolList, subnetName string, ipVersion types.IPVersion, ifName string) (err error) {
 		var ipNum int
 		if podSubnetConfig.FlexibleIPNum != nil {
 			ipNum = appReplicas + *(podSubnetConfig.FlexibleIPNum)
@@ -749,7 +739,7 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 			log.Sugar().Debugf("there's no 'IPv%d' IPPoolList retrieved from SpiderSubent '%s'", ipVersion, subnetName)
 			// create an empty IPPool and mark the desired IP number when the subnet name was specified,
 			// and the IPPool informer will implement the scale action
-			_, err = c.subnetMgr.AllocateEmptyIPPool(ctx, subnetName, podController, podSelector, ipNum, ipVersion, podSubnetConfig.ReclaimIPPool, ifName)
+			err = c.subnetMgr.AllocateEmptyIPPool(ctx, subnetName, appKind, app, podSelector, ipNum, ipVersion, podSubnetConfig.ReclaimIPPool, ifName)
 		} else if len(poolList.Items) == 1 {
 			pool := poolList.Items[0]
 			log.Sugar().Debugf("found SpiderSubnet '%s' IPPool '%s' and check it whether need to be scaled", subnetName, pool.Name)
@@ -778,9 +768,9 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 
 				var v4PoolList *spiderpoolv1.SpiderIPPoolList
 				v4PoolList, errV4 = c.subnetMgr.ipPoolManager.ListIPPools(ctx, client.MatchingLabels{
-					constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
+					constant.LabelIPPoolOwnerApplicationUID: string(app.GetUID()),
 					constant.LabelIPPoolOwnerSpiderSubnet:   item.IPv4[0],
-					constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
+					constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(appKind, app.GetNamespace(), app.GetName()),
 					constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV4,
 					constant.LabelIPPoolInterface:           item.Interface,
 				})
@@ -788,7 +778,7 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 					return
 				}
 
-				errV4 = fn(v4PoolList, item.IPv4[0], constant.IPv4, item.Interface)
+				errV4 = fn(ctx, v4PoolList, item.IPv4[0], constant.IPv4, item.Interface)
 			}()
 		}
 
@@ -799,9 +789,9 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 
 				var v6PoolList *spiderpoolv1.SpiderIPPoolList
 				v6PoolList, errV6 = c.subnetMgr.ipPoolManager.ListIPPools(ctx, client.MatchingLabels{
-					constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
+					constant.LabelIPPoolOwnerApplicationUID: string(app.GetUID()),
 					constant.LabelIPPoolOwnerSpiderSubnet:   item.IPv6[0],
-					constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
+					constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(appKind, app.GetNamespace(), app.GetName()),
 					constant.LabelIPPoolVersion:             constant.LabelIPPoolVersionV6,
 					constant.LabelIPPoolInterface:           item.Interface,
 				})
@@ -809,7 +799,7 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 					return
 				}
 
-				errV6 = fn(v6PoolList, item.IPv6[0], constant.IPv6, item.Interface)
+				errV6 = fn(ctx, v6PoolList, item.IPv6[0], constant.IPv6, item.Interface)
 			}()
 		}
 
@@ -837,7 +827,7 @@ func (c *appController) createOrMarkIPPool(ctx context.Context, podSubnetConfig
 		}
 	} else {
 		return fmt.Errorf("%w: no subnets specified to create or mark auto-created IPPool for application '%s/%s/%s', the pod subnet configuration is %v",
-			constant.ErrWrongInput, podController.Kind, podController.Namespace, podController.Name, podSubnetConfig)
+			constant.ErrWrongInput, appKind, app.GetNamespace(), app.GetName(), podSubnetConfig)
 	}
 
 	return nil
@@ -904,7 +894,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 
 		switch object := obj.(type) {
 		case *appsv1.Deployment:
-			appKind = constant.KindDeployment
+			appKind = constant.OwnerDeployment
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
@@ -916,7 +906,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 			app = object
 
 		case *appsv1.ReplicaSet:
-			appKind = constant.KindReplicaSet
+			appKind = constant.OwnerReplicaSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
@@ -928,7 +918,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 			app = object
 
 		case *appsv1.StatefulSet:
-			appKind = constant.KindStatefulSet
+			appKind = constant.OwnerStatefulSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
@@ -940,7 +930,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 			app = object
 
 		case *batchv1.Job:
-			appKind = constant.KindJob
+			appKind = constant.OwnerJob
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
@@ -952,7 +942,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 			app = object
 
 		case *batchv1.CronJob:
-			appKind = constant.KindCronJob
+			appKind = constant.OwnerCronJob
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
@@ -964,7 +954,7 @@ func (c *appController) ControllerDeleteHandler() controllers.APPInformersDelFun
 			app = object
 
 		case *appsv1.DaemonSet:
-			appKind = constant.KindDaemonSet
+			appKind = constant.OwnerDaemonSet
 			log = log.With(zap.String(appKind, fmt.Sprintf("%s/%s", object.Namespace, object.Name)))
 
 			owner := metav1.GetControllerOf(object)
diff --git a/pkg/subnetmanager/controllers/utils.go b/pkg/subnetmanager/controllers/utils.go
index 7c23886e1..7b2aaaaa5 100644
--- a/pkg/subnetmanager/controllers/utils.go
+++ b/pkg/subnetmanager/controllers/utils.go
@@ -101,17 +101,29 @@ func GetSubnetAnnoConfig(podAnnotations map[string]string, log *zap.Logger) (*ty
 		}
 	} else {
 		// annotation: ipam.spidernet.io/subnet
+		subnetAnnoConfig.SingleSubnet = new(types.AnnoSubnetItem)
 		subnet, ok := podAnnotations[constant.AnnoSpiderSubnet]
-		if ok {
+		if !ok {
+			// default IPAM mode
+			_, useIPPools := podAnnotations[constant.AnnoPodIPPools]
+			_, useIPPool := podAnnotations[constant.AnnoPodIPPool]
+			if useIPPools || useIPPool {
+				log.Debug("no SpiderSubnet feature annotation found, use default IPAM mode")
+				return nil, nil
+			}
+
+			// default cluster subnet
+			log.Sugar().Infof("no IPPool or Subnet annotations found, use cluster default subnet IPv4: '%v', IPv6: '%v'",
+				singletons.ClusterDefaultPool.ClusterDefaultIPv4Subnet, singletons.ClusterDefaultPool.ClusterDefaultIPv6Subnet)
+
+			subnetAnnoConfig.SingleSubnet.IPv4 = singletons.ClusterDefaultPool.ClusterDefaultIPv4Subnet
+			subnetAnnoConfig.SingleSubnet.IPv6 = singletons.ClusterDefaultPool.ClusterDefaultIPv6Subnet
+		} else {
 			log.Sugar().Debugf("found SpiderSubnet feature annotation '%s' value '%s'", constant.AnnoSpiderSubnet, subnets)
-			subnetAnnoConfig.SingleSubnet = new(types.AnnoSubnetItem)
 			err := json.Unmarshal([]byte(subnet), &subnetAnnoConfig.SingleSubnet)
 			if nil != err {
 				return nil, fmt.Errorf("failed to parse anntation '%s' value '%s', error: %v", constant.AnnoSpiderSubnet, subnet, err)
 			}
-		} else {
-			log.Debug("no SpiderSubnet feature annotation found, use default IPAM mode")
-			return nil, nil
 		}
 	}
 
@@ -123,7 +135,7 @@ func GetSubnetAnnoConfig(podAnnotations map[string]string, log *zap.Logger) (*ty
 	poolIPNum, ok := podAnnotations[constant.AnnoSpiderSubnetPoolIPNumber]
 	if ok {
 		log.Sugar().Debugf("use IPPool IP number '%s'", poolIPNum)
-		isFlexible, ipNum, err = GetPoolIPNumber(poolIPNum)
+		isFlexible, ipNum, err = getPoolIPNumber(poolIPNum)
 		if nil != err {
 			return nil, err
 		}
@@ -146,11 +158,18 @@ func GetSubnetAnnoConfig(podAnnotations map[string]string, log *zap.Logger) (*ty
 	}
 
 	// annotation: "ipam.spidernet.io/reclaim-ippool", reclaim IPPool or not (default true)
-	reclaimPool, err := ShouldReclaimIPPool(podAnnotations)
-	if nil != err {
-		return nil, err
+	reclaimPool, ok := podAnnotations[constant.AnnoSpiderSubnetReclaimIPPool]
+	if ok {
+		log.Sugar().Debugf("determine to reclaim IPPool '%s'", reclaimPool)
+		parseBool, err := strconv.ParseBool(reclaimPool)
+		if nil != err {
+			return nil, fmt.Errorf("failed to parse spider subnet '%s', error: %v", constant.AnnoSpiderSubnetReclaimIPPool, err)
+		}
+		subnetAnnoConfig.ReclaimIPPool = parseBool
+	} else {
+		log.Debug("no specified reclaim-IPPool, default to set it true")
+		subnetAnnoConfig.ReclaimIPPool = true
 	}
-	subnetAnnoConfig.ReclaimIPPool = reclaimPool
 
 	err = mutateAndValidateSubnetAnno(&subnetAnnoConfig)
 	if nil != err {
@@ -169,8 +188,6 @@ func mutateAndValidateSubnetAnno(subnetConfig *types.PodSubnetAnnoConfig) error
 		var ifNameArray []string
 
 		for index := range subnetConfig.MultipleSubnets {
-			ifNameArray = append(ifNameArray, subnetConfig.MultipleSubnets[index].Interface)
-
 			if len(subnetConfig.MultipleSubnets[index].IPv4) != 0 {
 				subnetConfig.MultipleSubnets[index].IPv4 = []string{subnetConfig.MultipleSubnets[index].IPv4[0]}
 				if subnetConfig.MultipleSubnets[index].IPv4[0] == "" {
@@ -186,10 +203,7 @@ func mutateAndValidateSubnetAnno(subnetConfig *types.PodSubnetAnnoConfig) error
 				v6SubnetsArray = append(v6SubnetsArray, subnetConfig.MultipleSubnets[index].IPv6[0])
 			}
 
-			// all none
-			if len(subnetConfig.MultipleSubnets[index].IPv4) == 0 && len(subnetConfig.MultipleSubnets[index].IPv6) == 0 {
-				return fmt.Errorf("it's invalid to set dual empty subnet with multiple interfaces: %v", subnetConfig)
-			}
+			ifNameArray = append(ifNameArray, subnetConfig.MultipleSubnets[index].Interface)
 		}
 
 		// validate duplicate subnet
@@ -205,20 +219,15 @@ func mutateAndValidateSubnetAnno(subnetConfig *types.PodSubnetAnnoConfig) error
 		if len(subnetConfig.SingleSubnet.IPv4) != 0 {
 			subnetConfig.SingleSubnet.IPv4 = []string{subnetConfig.SingleSubnet.IPv4[0]}
 			if subnetConfig.SingleSubnet.IPv4[0] == "" {
-				return fmt.Errorf("it's invalid to set an empty IPv4 subnet with single interface: %v", subnetConfig)
+				return fmt.Errorf("it's invalid to set an empty IPv4 subnet with single interface")
 			}
 		}
 		if len(subnetConfig.SingleSubnet.IPv6) != 0 {
 			subnetConfig.SingleSubnet.IPv6 = []string{subnetConfig.SingleSubnet.IPv6[0]}
 			if subnetConfig.SingleSubnet.IPv6[0] == "" {
-				return fmt.Errorf("it's invalid to set an empty IPv6 subnet with single interface: %v", subnetConfig)
+				return fmt.Errorf("it's invalid to set an empty IPv6 subnet with single interface")
 			}
 		}
-
-		// all none
-		if len(subnetConfig.SingleSubnet.IPv4) == 0 && len(subnetConfig.SingleSubnet.IPv6) == 0 {
-			return fmt.Errorf("it's invalid to set dual empty subnet with single interface: %v", subnetConfig)
-		}
 		// specify 'eth0' as the default single interface if it's none.
 		if subnetConfig.SingleSubnet.Interface == "" {
 			subnetConfig.SingleSubnet.Interface = constant.ClusterDefaultInterfaceName
@@ -230,8 +239,8 @@ func mutateAndValidateSubnetAnno(subnetConfig *types.PodSubnetAnnoConfig) error
 	return nil
 }
 
-// GetPoolIPNumber judges the given parameter is fixed or flexible
-func GetPoolIPNumber(str string) (isFlexible bool, ipNum int, err error) {
+// getPoolIPNumber judges the given parameter is fixed or flexible
+func getPoolIPNumber(str string) (isFlexible bool, ipNum int, err error) {
 	tmp := str
 
 	// the '+' sign counts must be '0' or '1'
@@ -295,14 +304,15 @@ func IsDefaultIPPoolMode(subnetConfig *types.PodSubnetAnnoConfig) bool {
 		return true
 	}
 
-	// SpiderSubnet with multiple interfaces
 	if len(subnetConfig.MultipleSubnets) != 0 {
 		return false
 	}
 
-	// SpiderSubnet with single interface
+	// if we do not set the cluster default SpiderSubnet, the dual stack subnets configuration will be empty
 	if subnetConfig.SingleSubnet != nil {
-		return false
+		if len(subnetConfig.SingleSubnet.IPv4) == 0 && len(subnetConfig.SingleSubnet.IPv6) == 0 {
+			return true
+		}
 	}
 
 	return false
@@ -318,18 +328,3 @@ func containsDuplicate(arr []string) bool {
 	}
 	return false
 }
-
-// ShouldReclaimIPPool
-func ShouldReclaimIPPool(anno map[string]string) (bool, error) {
-	reclaimPool, ok := anno[constant.AnnoSpiderSubnetReclaimIPPool]
-	if ok {
-		parseBool, err := strconv.ParseBool(reclaimPool)
-		if nil != err {
-			return false, fmt.Errorf("failed to parse spider subnet '%s', error: %v", constant.AnnoSpiderSubnetReclaimIPPool, err)
-		}
-		return parseBool, nil
-	}
-
-	// no specified reclaim-IPPool, default to set it true
-	return true, nil
-}
diff --git a/pkg/subnetmanager/subnet_manager.go b/pkg/subnetmanager/subnet_manager.go
index 25d4a71c6..d31beea56 100644
--- a/pkg/subnetmanager/subnet_manager.go
+++ b/pkg/subnetmanager/subnet_manager.go
@@ -209,28 +209,28 @@ func (sm *subnetManager) GenerateIPsFromSubnetWhenScaleUpIP(ctx context.Context,
 
 // AllocateEmptyIPPool will create an empty IPPool and mark the status.AutoDesiredIPCount
 // notice: this function only serves for auto-created IPPool
-func (sm *subnetManager) AllocateEmptyIPPool(ctx context.Context, subnetName string, podController types.PodTopController,
-	podSelector *metav1.LabelSelector, ipNum int, ipVersion types.IPVersion, reclaimIPPool bool, ifName string) (*spiderpoolv1.SpiderIPPool, error) {
+func (sm *subnetManager) AllocateEmptyIPPool(ctx context.Context, subnetName string, appKind string, app metav1.Object,
+	podSelector *metav1.LabelSelector, ipNum int, ipVersion types.IPVersion, reclaimIPPool bool, ifName string) error {
 	if len(subnetName) == 0 {
-		return nil, fmt.Errorf("%w: spider subnet name must be specified", constant.ErrWrongInput)
+		return fmt.Errorf("%w: spider subnet name must be specified", constant.ErrWrongInput)
 	}
 	if ipNum < 0 {
-		return nil, fmt.Errorf("%w: the required IP numbers '%d' is invalid", constant.ErrWrongInput, ipNum)
+		return fmt.Errorf("%w: the required IP numbers '%d' is invalid", constant.ErrWrongInput, ipNum)
 	}
 
 	subnet, err := sm.GetSubnetByName(ctx, subnetName)
 	if nil != err {
-		return nil, err
+		return err
 	}
 
 	if subnet.DeletionTimestamp != nil {
-		return nil, fmt.Errorf("%w: SpiderSubnet '%s' is terminating, we can't create a corresponding IPPool", constant.ErrWrongInput, subnet.Name)
+		return fmt.Errorf("%w: SpiderSubnet '%s' is terminating, we can't create a corresponding IPPool", constant.ErrWrongInput, subnet.Name)
 	}
 
 	poolLabels := map[string]string{
 		constant.LabelIPPoolOwnerSpiderSubnet:   subnet.Name,
-		constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(podController.Kind, podController.Namespace, podController.Name),
-		constant.LabelIPPoolOwnerApplicationUID: string(podController.Uid),
+		constant.LabelIPPoolOwnerApplication:    controllers.AppLabelValue(appKind, app.GetNamespace(), app.GetName()),
+		constant.LabelIPPoolOwnerApplicationUID: string(app.GetUID()),
 		constant.LabelIPPoolInterface:           ifName,
 	}
 
@@ -246,7 +246,7 @@ func (sm *subnetManager) AllocateEmptyIPPool(ctx context.Context, subnetName str
 
 	sp := &spiderpoolv1.SpiderIPPool{
 		ObjectMeta: metav1.ObjectMeta{
-			Name:   controllers.SubnetPoolName(podController.Kind, podController.Namespace, podController.Name, ipVersion, ifName, podController.Uid),
+			Name:   controllers.SubnetPoolName(appKind, app.GetNamespace(), app.GetName(), ipVersion, ifName, app.GetUID()),
 			Labels: poolLabels,
 		},
 		Spec: spiderpoolv1.IPPoolSpec{
@@ -261,16 +261,16 @@ func (sm *subnetManager) AllocateEmptyIPPool(ctx context.Context, subnetName str
 	logger.Sugar().Infof("try to create IPPool '%v'", sp)
 	err = sm.ipPoolManager.CreateIPPool(ctx, sp)
 	if nil != err {
-		return nil, err
+		return err
 	}
 
 	logger.Sugar().Infof("try to update IPPool '%v' status DesiredIPNumber '%d'", sp, ipNum)
 	err = sm.ipPoolManager.UpdateDesiredIPNumber(ctx, sp, ipNum)
 	if nil != err {
-		return nil, err
+		return err
 	}
 
-	return sp, nil
+	return nil
 }
 
 // CheckScaleIPPool will fetch some IPs from the specified subnet manager to expand the pool IPs
diff --git a/pkg/subnetmanager/types/interface.go b/pkg/subnetmanager/types/interface.go
index b7bfe1438..c6cd8118a 100644
--- a/pkg/subnetmanager/types/interface.go
+++ b/pkg/subnetmanager/types/interface.go
@@ -21,7 +21,7 @@ type SubnetManager interface {
 	GetSubnetByName(ctx context.Context, subnetName string) (*spiderpoolv1.SpiderSubnet, error)
 	ListSubnets(ctx context.Context, opts ...client.ListOption) (*spiderpoolv1.SpiderSubnetList, error)
 	SetupControllers(client kubernetes.Interface) error
-	AllocateEmptyIPPool(ctx context.Context, subnetMgrName string, podController types.PodTopController, podSelector *metav1.LabelSelector, ipNum int, ipVersion types.IPVersion, reclaimIPPool bool, ifName string) (*spiderpoolv1.SpiderIPPool, error)
+	AllocateEmptyIPPool(ctx context.Context, subnetMgrName string, appKind string, app metav1.Object, podSelector *metav1.LabelSelector, ipNum int, ipVersion types.IPVersion, reclaimIPPool bool, ifName string) error
 	CheckScaleIPPool(ctx context.Context, pool *spiderpoolv1.SpiderIPPool, subnetManagerName string, ipNum int) error
 	GenerateIPsFromSubnetWhenScaleUpIP(ctx context.Context, subnetName string, pool *spiderpoolv1.SpiderIPPool, cursor bool) ([]string, error)
 }
diff --git a/pkg/types/k8s.go b/pkg/types/k8s.go
index 40c1629d1..8140da7d5 100644
--- a/pkg/types/k8s.go
+++ b/pkg/types/k8s.go
@@ -3,11 +3,6 @@
 
 package types
 
-import (
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	apitypes "k8s.io/apimachinery/pkg/types"
-)
-
 type PodStatus string
 
 type AnnoPodIPPoolValue struct {
@@ -66,11 +61,3 @@ type AnnoSubnetItem struct {
 	IPv4      []string `json:"ipv4,omitempty"`
 	IPv6      []string `json:"ipv6,omitempty"`
 }
-
-type PodTopController struct {
-	Kind      string
-	Namespace string
-	Name      string
-	Uid       apitypes.UID
-	App       metav1.Object
-}
diff --git a/pkg/workloadendpointmanager/workloadendpoint_manager.go b/pkg/workloadendpointmanager/workloadendpoint_manager.go
index f1da9367c..69481de3a 100644
--- a/pkg/workloadendpointmanager/workloadendpoint_manager.go
+++ b/pkg/workloadendpointmanager/workloadendpoint_manager.go
@@ -122,7 +122,7 @@ func (em *workloadEndpointManager) MarkIPAllocation(ctx context.Context, contain
 		},
 	}
 
-	podTopController, err := em.podManager.GetPodTopController(ctx, pod)
+	ownerKind, owner, err := em.podManager.GetPodTopController(ctx, pod)
 	if err != nil {
 		return nil, err
 	}
@@ -131,7 +131,7 @@ func (em *workloadEndpointManager) MarkIPAllocation(ctx context.Context, contain
 	// controlled by StatefulSet. Once the Pod of StatefulSet is recreated,
 	// we can immediately retrieve the old IP allocation results from the
 	// Endpoint without worrying about the cascading deletion of the Endpoint.
-	if podTopController.Kind != constant.KindStatefulSet {
+	if ownerKind != constant.OwnerStatefulSet {
 		if err := controllerutil.SetOwnerReference(pod, endpoint, em.config.scheme); err != nil {
 			return nil, err
 		}
@@ -149,9 +149,10 @@ func (em *workloadEndpointManager) MarkIPAllocation(ctx context.Context, contain
 
 	endpoint.Status.Current = allocation
 	endpoint.Status.History = []spiderpoolv1.PodIPAllocation{*allocation}
-	endpoint.Status.OwnerControllerType = podTopController.Kind
-	endpoint.Status.OwnerControllerName = podTopController.Name
-
+	endpoint.Status.OwnerControllerType = ownerKind
+	if owner != nil {
+		endpoint.Status.OwnerControllerName = owner.GetName()
+	}
 	if err := em.client.Status().Update(ctx, endpoint); err != nil {
 		return nil, err
 	}
diff --git a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
index d777b793b..43c44435f 100644
--- a/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
+++ b/vendor/github.com/onsi/ginkgo/v2/CHANGELOG.md
@@ -1,3 +1,16 @@
+## 2.7.0
+
+### Features
+- Introduce ContinueOnFailure for Ordered containers [e0123ca] - Ordered containers that are also decorated with ContinueOnFailure will not stop running specs after the first spec fails.
+- Support for bootstrap commands to use custom data for templates (#1110) [7a2b242]
+- Support for labels and pending decorator in ginkgo outline output (#1113) [e6e3b98]
+- Color aliases for custom color support (#1101) [49fab7a]
+
+### Fixes
+- correctly ensure deterministic spec order, even if specs are generated by iterating over a map [89dda20]
+- Fix a bug where timedout specs were not correctly treated as failures when determining whether or not to run AfterAlls in an Ordered container.
+- Ensure go test coverprofile outputs to the expected location (#1105) [b0bd77b]
+
 ## 2.6.1
 
 ### Features
diff --git a/vendor/github.com/onsi/ginkgo/v2/decorator_dsl.go b/vendor/github.com/onsi/ginkgo/v2/decorator_dsl.go
index e43d9cbbb..c65af4ce1 100644
--- a/vendor/github.com/onsi/ginkgo/v2/decorator_dsl.go
+++ b/vendor/github.com/onsi/ginkgo/v2/decorator_dsl.go
@@ -46,7 +46,7 @@ const Pending = internal.Pending
 
 /*
 Serial is a decorator that allows you to mark a spec or container as serial.  These specs will never run in parallel with other specs.
-Tests in ordered containers cannot be marked as serial - mark the ordered container instead.
+Specs in ordered containers cannot be marked as serial - mark the ordered container instead.
 
 You can learn more here: https://onsi.github.io/ginkgo/#serial-specs
 You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorator-reference
@@ -54,7 +54,7 @@ You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorat
 const Serial = internal.Serial
 
 /*
-Ordered is a decorator that allows you to mark a container as ordered.  Tests in the container will always run in the order they appear.
+Ordered is a decorator that allows you to mark a container as ordered.  Specs in the container will always run in the order they appear.
 They will never be randomized and they will never run in parallel with one another, though they may run in parallel with other specs.
 
 You can learn more here: https://onsi.github.io/ginkgo/#ordered-containers
@@ -62,6 +62,16 @@ You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorat
 */
 const Ordered = internal.Ordered
 
+/*
+ContinueOnFailure is a decorator that allows you to mark an Ordered container to continue running specs even if failures occur.  Ordinarily an ordered container will stop running specs after the first failure occurs.  Note that if a BeforeAll or a BeforeEach/JustBeforeEach annotated with OncePerOrdered fails then no specs will run as the precondition for the Ordered container will consider to be failed.
+
+ContinueOnFailure only applies to the outermost Ordered container.  Attempting to place ContinueOnFailure in a nested container will result in an error.
+
+You can learn more here: https://onsi.github.io/ginkgo/#ordered-containers
+You can learn more about decorators here: https://onsi.github.io/ginkgo/#decorator-reference
+*/
+const ContinueOnFailure = internal.ContinueOnFailure
+
 /*
 OncePerOrdered is a decorator that allows you to mark outer BeforeEach, AfterEach, JustBeforeEach, and JustAfterEach setup nodes to run once
 per ordered context.  Normally these setup nodes run around each individual spec, with OncePerOrdered they will run once around the set of specs in an ordered container.
diff --git a/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go
index da224eab6..ece35d17b 100644
--- a/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go
+++ b/vendor/github.com/onsi/ginkgo/v2/formatter/formatter.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 	"regexp"
+	"strconv"
 	"strings"
 )
 
@@ -50,13 +51,35 @@ func NewWithNoColorBool(noColor bool) Formatter {
 }
 
 func New(colorMode ColorMode) Formatter {
+	colorAliases := map[string]int{
+		"black":   0,
+		"red":     1,
+		"green":   2,
+		"yellow":  3,
+		"blue":    4,
+		"magenta": 5,
+		"cyan":    6,
+		"white":   7,
+	}
+	for colorAlias, n := range colorAliases {
+		colorAliases[fmt.Sprintf("bright-%s", colorAlias)] = n + 8
+	}
+
 	getColor := func(color, defaultEscapeCode string) string {
 		color = strings.ToUpper(strings.ReplaceAll(color, "-", "_"))
 		envVar := fmt.Sprintf("GINKGO_CLI_COLOR_%s", color)
-		if escapeCode := os.Getenv(envVar); escapeCode != "" {
-			return escapeCode
+		envVarColor := os.Getenv(envVar)
+		if envVarColor == "" {
+			return defaultEscapeCode
+		}
+		if colorCode, ok := colorAliases[envVarColor]; ok {
+			return fmt.Sprintf("\x1b[38;5;%dm", colorCode)
+		}
+		colorCode, err := strconv.Atoi(envVarColor)
+		if err != nil || colorCode < 0 || colorCode > 255 {
+			return defaultEscapeCode
 		}
-		return defaultEscapeCode
+		return fmt.Sprintf("\x1b[38;5;%dm", colorCode)
 	}
 
 	f := Formatter{
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
index 0273abe9c..73aff0b7a 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/bootstrap_command.go
@@ -2,6 +2,7 @@ package generators
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"os"
 	"text/template"
@@ -25,6 +26,9 @@ func BuildBootstrapCommand() command.Command {
 			{Name: "template", KeyPath: "CustomTemplate",
 				UsageArgument: "template-file",
 				Usage:         "If specified, generate will use the contents of the file passed as the bootstrap template"},
+			{Name: "template-data", KeyPath: "CustomTemplateData",
+				UsageArgument: "template-data-file",
+				Usage:         "If specified, generate will use the contents of the file passed as data to be rendered in the bootstrap template"},
 		},
 		&conf,
 		types.GinkgoFlagSections{},
@@ -57,6 +61,7 @@ type bootstrapData struct {
 	GomegaImport  string
 	GinkgoPackage string
 	GomegaPackage string
+	CustomData    map[string]any
 }
 
 func generateBootstrap(conf GeneratorsConfig) {
@@ -95,17 +100,32 @@ func generateBootstrap(conf GeneratorsConfig) {
 		tpl, err := os.ReadFile(conf.CustomTemplate)
 		command.AbortIfError("Failed to read custom bootstrap file:", err)
 		templateText = string(tpl)
+		if conf.CustomTemplateData != "" {
+			var tplCustomDataMap map[string]any
+			tplCustomData, err := os.ReadFile(conf.CustomTemplateData)
+			command.AbortIfError("Failed to read custom boostrap data file:", err)
+			if !json.Valid([]byte(tplCustomData)) {
+				command.AbortWith("Invalid JSON object in custom data file.")
+			}
+			//create map from the custom template data
+			json.Unmarshal(tplCustomData, &tplCustomDataMap)
+			data.CustomData = tplCustomDataMap
+		}
 	} else if conf.Agouti {
 		templateText = agoutiBootstrapText
 	} else {
 		templateText = bootstrapText
 	}
 
-	bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Parse(templateText)
+	//Setting the option to explicitly fail if template is rendered trying to access missing key
+	bootstrapTemplate, err := template.New("bootstrap").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText)
 	command.AbortIfError("Failed to parse bootstrap template:", err)
 
 	buf := &bytes.Buffer{}
-	bootstrapTemplate.Execute(buf, data)
+	//Being explicit about failing sooner during template rendering
+	//when accessing custom data rather than during the go fmt command
+	err = bootstrapTemplate.Execute(buf, data)
+	command.AbortIfError("Failed to render bootstrap template:", err)
 
 	buf.WriteTo(f)
 
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
index 93b0b4b25..48d23f919 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generate_command.go
@@ -2,6 +2,7 @@ package generators
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"os"
 	"path/filepath"
@@ -28,6 +29,9 @@ func BuildGenerateCommand() command.Command {
 			{Name: "template", KeyPath: "CustomTemplate",
 				UsageArgument: "template-file",
 				Usage:         "If specified, generate will use the contents of the file passed as the test file template"},
+			{Name: "template-data", KeyPath: "CustomTemplateData",
+				UsageArgument: "template-data-file",
+				Usage:         "If specified, generate will use the contents of the file passed as data to be rendered in the test file template"},
 		},
 		&conf,
 		types.GinkgoFlagSections{},
@@ -64,6 +68,7 @@ type specData struct {
 	GomegaImport  string
 	GinkgoPackage string
 	GomegaPackage string
+	CustomData    map[string]any
 }
 
 func generateTestFiles(conf GeneratorsConfig, args []string) {
@@ -122,16 +127,31 @@ func generateTestFileForSubject(subject string, conf GeneratorsConfig) {
 		tpl, err := os.ReadFile(conf.CustomTemplate)
 		command.AbortIfError("Failed to read custom template file:", err)
 		templateText = string(tpl)
+		if conf.CustomTemplateData != "" {
+			var tplCustomDataMap map[string]any
+			tplCustomData, err := os.ReadFile(conf.CustomTemplateData)
+			command.AbortIfError("Failed to read custom template data file:", err)
+			if !json.Valid([]byte(tplCustomData)) {
+				command.AbortWith("Invalid JSON object in custom data file.")
+			}
+			//create map from the custom template data
+			json.Unmarshal(tplCustomData, &tplCustomDataMap)
+			data.CustomData = tplCustomDataMap
+		}
 	} else if conf.Agouti {
 		templateText = agoutiSpecText
 	} else {
 		templateText = specText
 	}
 
-	specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Parse(templateText)
+	//Setting the option to explicitly fail if template is rendered trying to access missing key
+	specTemplate, err := template.New("spec").Funcs(sprig.TxtFuncMap()).Option("missingkey=error").Parse(templateText)
 	command.AbortIfError("Failed to read parse test template:", err)
 
-	specTemplate.Execute(f, data)
+	//Being explicit about failing sooner during template rendering
+	//when accessing custom data rather than during the go fmt command
+	err = specTemplate.Execute(f, data)
+	command.AbortIfError("Failed to render bootstrap template:", err)
 	internal.GoFmt(targetFile)
 }
 
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
index 3086e6056..3046a4487 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/generators/generators_common.go
@@ -13,6 +13,7 @@ import (
 type GeneratorsConfig struct {
 	Agouti, NoDot, Internal bool
 	CustomTemplate          string
+	CustomTemplateData      string
 }
 
 func getPackageAndFormattedName() (string, string, string) {
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
index cad238671..41052ea19 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/internal/run.go
@@ -6,6 +6,7 @@ import (
 	"io"
 	"os"
 	"os/exec"
+	"path/filepath"
 	"regexp"
 	"strings"
 	"syscall"
@@ -63,6 +64,12 @@ func checkForNoTestsWarning(buf *bytes.Buffer) bool {
 }
 
 func runGoTest(suite TestSuite, cliConfig types.CLIConfig, goFlagsConfig types.GoFlagsConfig) TestSuite {
+	// As we run the go test from the suite directory, make sure the cover profile is absolute
+	// and placed into the expected output directory when one is configured.
+	if goFlagsConfig.Cover && !filepath.IsAbs(goFlagsConfig.CoverProfile) {
+		goFlagsConfig.CoverProfile = AbsPathForGeneratedAsset(goFlagsConfig.CoverProfile, suite, cliConfig, 0)
+	}
+
 	args, err := types.GenerateGoTestRunArgs(goFlagsConfig)
 	command.AbortIfError("Failed to generate test run arguments", err)
 	cmd, buf := buildAndStartCommand(suite, args, true)
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
index c197bb686..0b9b19fe7 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/ginkgo.go
@@ -1,6 +1,7 @@
 package outline
 
 import (
+	"github.com/onsi/ginkgo/v2/types"
 	"go/ast"
 	"go/token"
 	"strconv"
@@ -25,9 +26,10 @@ type ginkgoMetadata struct {
 	// End is the position of first character immediately after the spec or container block
 	End int `json:"end"`
 
-	Spec    bool `json:"spec"`
-	Focused bool `json:"focused"`
-	Pending bool `json:"pending"`
+	Spec    bool     `json:"spec"`
+	Focused bool     `json:"focused"`
+	Pending bool     `json:"pending"`
+	Labels  []string `json:"labels"`
 }
 
 // ginkgoNode is used to construct the outline as a tree
@@ -145,27 +147,35 @@ func ginkgoNodeFromCallExpr(fset *token.FileSet, ce *ast.CallExpr, ginkgoPackage
 	case "It", "Specify", "Entry":
 		n.Spec = true
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
+		n.Pending = pendingFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "FIt", "FSpecify", "FEntry":
 		n.Spec = true
 		n.Focused = true
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "PIt", "PSpecify", "XIt", "XSpecify", "PEntry", "XEntry":
 		n.Spec = true
 		n.Pending = true
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "Context", "Describe", "When", "DescribeTable":
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
+		n.Pending = pendingFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "FContext", "FDescribe", "FWhen", "FDescribeTable":
 		n.Focused = true
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "PContext", "PDescribe", "PWhen", "XContext", "XDescribe", "XWhen", "PDescribeTable", "XDescribeTable":
 		n.Pending = true
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
+		n.Labels = labelFromCallExpr(ce)
 		return &n, ginkgoPackageName != nil && *ginkgoPackageName == packageName
 	case "By":
 		n.Text = textOrAltFromCallExpr(ce, undefinedTextAlt)
@@ -216,3 +226,77 @@ func textFromCallExpr(ce *ast.CallExpr) (string, bool) {
 		return text.Value, true
 	}
 }
+
+func labelFromCallExpr(ce *ast.CallExpr) []string {
+
+	labels := []string{}
+	if len(ce.Args) < 2 {
+		return labels
+	}
+
+	for _, arg := range ce.Args[1:] {
+		switch expr := arg.(type) {
+		case *ast.CallExpr:
+			id, ok := expr.Fun.(*ast.Ident)
+			if !ok {
+				// to skip over cases where the expr.Fun. is actually *ast.SelectorExpr
+				continue
+			}
+			if id.Name == "Label" {
+				ls := extractLabels(expr)
+				for _, label := range ls {
+					labels = append(labels, label)
+				}
+			}
+		}
+	}
+	return labels
+}
+
+func extractLabels(expr *ast.CallExpr) []string {
+	out := []string{}
+	for _, arg := range expr.Args {
+		switch expr := arg.(type) {
+		case *ast.BasicLit:
+			if expr.Kind == token.STRING {
+				unquoted, err := strconv.Unquote(expr.Value)
+				if err != nil {
+					unquoted = expr.Value
+				}
+				validated, err := types.ValidateAndCleanupLabel(unquoted, types.CodeLocation{})
+				if err == nil {
+					out = append(out, validated)
+				}
+			}
+		}
+	}
+
+	return out
+}
+
+func pendingFromCallExpr(ce *ast.CallExpr) bool {
+
+	pending := false
+	if len(ce.Args) < 2 {
+		return pending
+	}
+
+	for _, arg := range ce.Args[1:] {
+		switch expr := arg.(type) {
+		case *ast.CallExpr:
+			id, ok := expr.Fun.(*ast.Ident)
+			if !ok {
+				// to skip over cases where the expr.Fun. is actually *ast.SelectorExpr
+				continue
+			}
+			if id.Name == "Pending" {
+				pending = true
+			}
+		case *ast.Ident:
+			if expr.Name == "Pending" {
+				pending = true
+			}
+		}
+	}
+	return pending
+}
diff --git a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
index 4b45e7627..c2327cda8 100644
--- a/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
+++ b/vendor/github.com/onsi/ginkgo/v2/ginkgo/outline/outline.go
@@ -85,12 +85,19 @@ func (o *outline) String() string {
 // one 'width' of spaces for every level of nesting.
 func (o *outline) StringIndent(width int) string {
 	var b strings.Builder
-	b.WriteString("Name,Text,Start,End,Spec,Focused,Pending\n")
+	b.WriteString("Name,Text,Start,End,Spec,Focused,Pending,Labels\n")
 
 	currentIndent := 0
 	pre := func(n *ginkgoNode) {
 		b.WriteString(fmt.Sprintf("%*s", currentIndent, ""))
-		b.WriteString(fmt.Sprintf("%s,%s,%d,%d,%t,%t,%t\n", n.Name, n.Text, n.Start, n.End, n.Spec, n.Focused, n.Pending))
+		var labels string
+		if len(n.Labels) == 1 {
+			labels = n.Labels[0]
+		} else {
+			labels = strings.Join(n.Labels, ", ")
+		}
+		//enclosing labels in a double quoted comma separate listed so that when inmported into a CSV app the Labels column has comma separate strings
+		b.WriteString(fmt.Sprintf("%s,%s,%d,%d,%t,%t,%t,\"%s\"\n", n.Name, n.Text, n.Start, n.End, n.Spec, n.Focused, n.Pending, labels))
 		currentIndent += width
 	}
 	post := func(n *ginkgoNode) {
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/group.go b/vendor/github.com/onsi/ginkgo/v2/internal/group.go
index 5c782d3ff..ae1b7b011 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/group.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/group.go
@@ -94,15 +94,19 @@ type group struct {
 	runOncePairs   map[uint]runOncePairs
 	runOnceTracker map[runOncePair]types.SpecState
 
-	succeeded bool
+	succeeded              bool
+	failedInARunOnceBefore bool
+	continueOnFailure      bool
 }
 
 func newGroup(suite *Suite) *group {
 	return &group{
-		suite:          suite,
-		runOncePairs:   map[uint]runOncePairs{},
-		runOnceTracker: map[runOncePair]types.SpecState{},
-		succeeded:      true,
+		suite:                  suite,
+		runOncePairs:           map[uint]runOncePairs{},
+		runOnceTracker:         map[runOncePair]types.SpecState{},
+		succeeded:              true,
+		failedInARunOnceBefore: false,
+		continueOnFailure:      false,
 	}
 }
 
@@ -137,10 +141,14 @@ func (g *group) evaluateSkipStatus(spec Spec) (types.SpecState, types.Failure) {
 	if !g.suite.deadline.IsZero() && g.suite.deadline.Before(time.Now()) {
 		return types.SpecStateSkipped, types.Failure{}
 	}
-	if !g.succeeded {
+	if !g.succeeded && !g.continueOnFailure {
 		return types.SpecStateSkipped, g.suite.failureForLeafNodeWithMessage(spec.FirstNodeWithType(types.NodeTypeIt),
 			"Spec skipped because an earlier spec in an ordered container failed")
 	}
+	if g.failedInARunOnceBefore && g.continueOnFailure {
+		return types.SpecStateSkipped, g.suite.failureForLeafNodeWithMessage(spec.FirstNodeWithType(types.NodeTypeIt),
+			"Spec skipped because a BeforeAll node failed")
+	}
 	beforeOncePairs := g.runOncePairs[spec.SubjectID()].withType(types.NodeTypeBeforeAll | types.NodeTypeBeforeEach | types.NodeTypeJustBeforeEach)
 	for _, pair := range beforeOncePairs {
 		if g.runOnceTracker[pair].Is(types.SpecStateSkipped) {
@@ -168,7 +176,8 @@ func (g *group) isLastSpecWithPair(specID uint, pair runOncePair) bool {
 	return lastSpecID == specID
 }
 
-func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) {
+func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) bool {
+	failedInARunOnceBefore := false
 	pairs := g.runOncePairs[spec.SubjectID()]
 
 	nodes := spec.Nodes.WithType(types.NodeTypeBeforeAll)
@@ -194,6 +203,7 @@ func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) {
 		}
 		if g.suite.currentSpecReport.State != types.SpecStatePassed {
 			terminatingNode, terminatingPair = node, oncePair
+			failedInARunOnceBefore = !terminatingPair.isZero()
 			break
 		}
 	}
@@ -216,7 +226,7 @@ func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) {
 				//this node has already been run on this attempt, don't rerun it
 				return false
 			}
-			pair := runOncePair{}
+			var pair runOncePair
 			switch node.NodeType {
 			case types.NodeTypeCleanupAfterEach, types.NodeTypeCleanupAfterAll:
 				// check if we were generated in an AfterNode that has already run
@@ -246,9 +256,13 @@ func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) {
 				if !terminatingPair.isZero() && terminatingNode.NestingLevel == node.NestingLevel {
 					return true //...or, a run-once node at our nesting level was skipped which means this is our last chance to run
 				}
-			case types.SpecStateFailed, types.SpecStatePanicked: // the spec has failed...
+			case types.SpecStateFailed, types.SpecStatePanicked, types.SpecStateTimedout: // the spec has failed...
 				if isFinalAttempt {
-					return true //...if this was the last attempt then we're the last spec to run and so the AfterNode should run
+					if g.continueOnFailure {
+						return isLastSpecWithPair || failedInARunOnceBefore //...we're configured to continue on failures - so we should only run if we're the last spec for this pair or if we failed in a runOnceBefore (which means we _are_ the last spec to run)
+					} else {
+						return true //...this was the last attempt and continueOnFailure is false therefore we are the last spec to run and so the AfterNode should run
+					}
 				}
 				if !terminatingPair.isZero() { // ...and it failed in a run-once.  which will be running again
 					if node.NodeType.Is(types.NodeTypeCleanupAfterEach | types.NodeTypeCleanupAfterAll) {
@@ -281,10 +295,12 @@ func (g *group) attemptSpec(isFinalAttempt bool, spec Spec) {
 		includeDeferCleanups = true
 	}
 
+	return failedInARunOnceBefore
 }
 
 func (g *group) run(specs Specs) {
 	g.specs = specs
+	g.continueOnFailure = specs[0].Nodes.FirstNodeMarkedOrdered().MarkedContinueOnFailure
 	for _, spec := range g.specs {
 		g.runOncePairs[spec.SubjectID()] = runOncePairsForSpec(spec)
 	}
@@ -301,8 +317,8 @@ func (g *group) run(specs Specs) {
 		skip := g.suite.config.DryRun || g.suite.currentSpecReport.State.Is(types.SpecStateFailureStates|types.SpecStateSkipped|types.SpecStatePending)
 
 		g.suite.currentSpecReport.StartTime = time.Now()
+		failedInARunOnceBefore := false
 		if !skip {
-
 			var maxAttempts = 1
 
 			if g.suite.currentSpecReport.MaxMustPassRepeatedly > 0 {
@@ -327,7 +343,7 @@ func (g *group) run(specs Specs) {
 					}
 				}
 
-				g.attemptSpec(attempt == maxAttempts-1, spec)
+				failedInARunOnceBefore = g.attemptSpec(attempt == maxAttempts-1, spec)
 
 				g.suite.currentSpecReport.EndTime = time.Now()
 				g.suite.currentSpecReport.RunTime = g.suite.currentSpecReport.EndTime.Sub(g.suite.currentSpecReport.StartTime)
@@ -355,6 +371,7 @@ func (g *group) run(specs Specs) {
 		g.suite.processCurrentSpecReport()
 		if g.suite.currentSpecReport.State.Is(types.SpecStateFailureStates) {
 			g.succeeded = false
+			g.failedInARunOnceBefore = g.failedInARunOnceBefore || failedInARunOnceBefore
 		}
 		g.suite.selectiveLock.Lock()
 		g.suite.currentSpecReport = types.SpecReport{}
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/node.go b/vendor/github.com/onsi/ginkgo/v2/internal/node.go
index 69928eed6..9bbd258c0 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/node.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/node.go
@@ -47,19 +47,20 @@ type Node struct {
 	ReportEachBody  func(types.SpecReport)
 	ReportSuiteBody func(types.Report)
 
-	MarkedFocus          bool
-	MarkedPending        bool
-	MarkedSerial         bool
-	MarkedOrdered        bool
-	MarkedOncePerOrdered bool
-	FlakeAttempts        int
-	MustPassRepeatedly   int
-	Labels               Labels
-	PollProgressAfter    time.Duration
-	PollProgressInterval time.Duration
-	NodeTimeout          time.Duration
-	SpecTimeout          time.Duration
-	GracePeriod          time.Duration
+	MarkedFocus             bool
+	MarkedPending           bool
+	MarkedSerial            bool
+	MarkedOrdered           bool
+	MarkedContinueOnFailure bool
+	MarkedOncePerOrdered    bool
+	FlakeAttempts           int
+	MustPassRepeatedly      int
+	Labels                  Labels
+	PollProgressAfter       time.Duration
+	PollProgressInterval    time.Duration
+	NodeTimeout             time.Duration
+	SpecTimeout             time.Duration
+	GracePeriod             time.Duration
 
 	NodeIDWhereCleanupWasGenerated uint
 }
@@ -69,6 +70,7 @@ type focusType bool
 type pendingType bool
 type serialType bool
 type orderedType bool
+type continueOnFailureType bool
 type honorsOrderedType bool
 type suppressProgressReporting bool
 
@@ -76,6 +78,7 @@ const Focus = focusType(true)
 const Pending = pendingType(true)
 const Serial = serialType(true)
 const Ordered = orderedType(true)
+const ContinueOnFailure = continueOnFailureType(true)
 const OncePerOrdered = honorsOrderedType(true)
 const SuppressProgressReporting = suppressProgressReporting(true)
 
@@ -133,6 +136,8 @@ func isDecoration(arg interface{}) bool {
 		return true
 	case t == reflect.TypeOf(Ordered):
 		return true
+	case t == reflect.TypeOf(ContinueOnFailure):
+		return true
 	case t == reflect.TypeOf(OncePerOrdered):
 		return true
 	case t == reflect.TypeOf(SuppressProgressReporting):
@@ -241,6 +246,11 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
 			if !nodeType.Is(types.NodeTypeContainer) {
 				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "Ordered"))
 			}
+		case t == reflect.TypeOf(ContinueOnFailure):
+			node.MarkedContinueOnFailure = bool(arg.(continueOnFailureType))
+			if !nodeType.Is(types.NodeTypeContainer) {
+				appendError(types.GinkgoErrors.InvalidDecoratorForNodeType(node.CodeLocation, nodeType, "ContinueOnFailure"))
+			}
 		case t == reflect.TypeOf(OncePerOrdered):
 			node.MarkedOncePerOrdered = bool(arg.(honorsOrderedType))
 			if !nodeType.Is(types.NodeTypeBeforeEach | types.NodeTypeJustBeforeEach | types.NodeTypeAfterEach | types.NodeTypeJustAfterEach) {
@@ -386,6 +396,10 @@ func NewNode(deprecationTracker *types.DeprecationTracker, nodeType types.NodeTy
 		appendError(types.GinkgoErrors.InvalidDeclarationOfFocusedAndPending(node.CodeLocation, nodeType))
 	}
 
+	if node.MarkedContinueOnFailure && !node.MarkedOrdered {
+		appendError(types.GinkgoErrors.InvalidContinueOnFailureDecoration(node.CodeLocation))
+	}
+
 	hasContext := node.HasContext || node.SynchronizedAfterSuiteProc1BodyHasContext || node.SynchronizedAfterSuiteAllProcsBodyHasContext || node.SynchronizedBeforeSuiteProc1BodyHasContext || node.SynchronizedBeforeSuiteAllProcsBodyHasContext
 
 	if !hasContext && (node.NodeTimeout > 0 || node.SpecTimeout > 0 || node.GracePeriod > 0) && len(errors) == 0 {
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/ordering.go b/vendor/github.com/onsi/ginkgo/v2/internal/ordering.go
index 161be820c..7ed43c7fd 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/ordering.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/ordering.go
@@ -7,6 +7,58 @@ import (
 	"github.com/onsi/ginkgo/v2/types"
 )
 
+type SortableSpecs struct {
+	Specs   Specs
+	Indexes []int
+}
+
+func NewSortableSpecs(specs Specs) *SortableSpecs {
+	indexes := make([]int, len(specs))
+	for i := range specs {
+		indexes[i] = i
+	}
+	return &SortableSpecs{
+		Specs:   specs,
+		Indexes: indexes,
+	}
+}
+func (s *SortableSpecs) Len() int      { return len(s.Indexes) }
+func (s *SortableSpecs) Swap(i, j int) { s.Indexes[i], s.Indexes[j] = s.Indexes[j], s.Indexes[i] }
+func (s *SortableSpecs) Less(i, j int) bool {
+	a, b := s.Specs[s.Indexes[i]], s.Specs[s.Indexes[j]]
+
+	firstOrderedA := a.Nodes.FirstNodeMarkedOrdered()
+	firstOrderedB := b.Nodes.FirstNodeMarkedOrdered()
+	if firstOrderedA.ID == firstOrderedB.ID && !firstOrderedA.IsZero() {
+		// strictly preserve order in ordered containers.  ID will track this as IDs are generated monotonically
+		return a.FirstNodeWithType(types.NodeTypeIt).ID < b.FirstNodeWithType(types.NodeTypeIt).ID
+	}
+
+	aCLs := a.Nodes.WithType(types.NodeTypesForContainerAndIt).CodeLocations()
+	bCLs := b.Nodes.WithType(types.NodeTypesForContainerAndIt).CodeLocations()
+	for i := 0; i < len(aCLs) && i < len(bCLs); i++ {
+		aCL, bCL := aCLs[i], bCLs[i]
+		if aCL.FileName < bCL.FileName {
+			return true
+		} else if aCL.FileName > bCL.FileName {
+			return false
+		}
+		if aCL.LineNumber < bCL.LineNumber {
+			return true
+		} else if aCL.LineNumber > bCL.LineNumber {
+			return false
+		}
+	}
+	// either everything is equal or we have different lengths of CLs
+	if len(aCLs) < len(bCLs) {
+		return true
+	} else if len(aCLs) > len(bCLs) {
+		return false
+	}
+	// ok, now we are sure everything was equal. so we use the spec text to break ties
+	return a.Text() < b.Text()
+}
+
 type GroupedSpecIndices []SpecIndices
 type SpecIndices []int
 
@@ -28,12 +80,17 @@ func OrderSpecs(specs Specs, suiteConfig types.SuiteConfig) (GroupedSpecIndices,
 	// Seed a new random source based on thee configured random seed.
 	r := rand.New(rand.NewSource(suiteConfig.RandomSeed))
 
-	// first break things into execution groups
+	// first, we sort the entire suite to ensure a deterministic order.  the sort is performed by filename, then line number, and then spec text.  this ensures every parallel process has the exact same spec order and is only necessary to cover the edge case where the user iterates over a map to generate specs.
+	sortableSpecs := NewSortableSpecs(specs)
+	sort.Sort(sortableSpecs)
+
+	// then we break things into execution groups
 	// a group represents a single unit of execution and is a collection of SpecIndices
 	// usually a group is just a single spec, however ordered containers must be preserved as a single group
 	executionGroupIDs := []uint{}
 	executionGroups := map[uint]SpecIndices{}
-	for idx, spec := range specs {
+	for _, idx := range sortableSpecs.Indexes {
+		spec := specs[idx]
 		groupNode := spec.Nodes.FirstNodeMarkedOrdered()
 		if groupNode.IsZero() {
 			groupNode = spec.Nodes.FirstNodeWithType(types.NodeTypeIt)
@@ -48,7 +105,6 @@ func OrderSpecs(specs Specs, suiteConfig types.SuiteConfig) (GroupedSpecIndices,
 	// we shuffle outermost containers.  so we need to form shufflable groupings of GroupIDs
 	shufflableGroupingIDs := []uint{}
 	shufflableGroupingIDToGroupIDs := map[uint][]uint{}
-	shufflableGroupingsIDToSortKeys := map[uint]string{}
 
 	// for each execution group we're going to have to pick a node to represent how the
 	// execution group is grouped for shuffling:
@@ -57,7 +113,7 @@ func OrderSpecs(specs Specs, suiteConfig types.SuiteConfig) (GroupedSpecIndices,
 		nodeTypesToShuffle = types.NodeTypeIt
 	}
 
-	//so, fo reach execution group:
+	//so, for each execution group:
 	for _, groupID := range executionGroupIDs {
 		// pick out a representative spec
 		representativeSpec := specs[executionGroups[groupID][0]]
@@ -72,22 +128,9 @@ func OrderSpecs(specs Specs, suiteConfig types.SuiteConfig) (GroupedSpecIndices,
 		if len(shufflableGroupingIDToGroupIDs[shufflableGroupingNode.ID]) == 1 {
 			// record the shuffleable group ID
 			shufflableGroupingIDs = append(shufflableGroupingIDs, shufflableGroupingNode.ID)
-			// and record the sort key to use
-			shufflableGroupingsIDToSortKeys[shufflableGroupingNode.ID] = shufflableGroupingNode.CodeLocation.String()
 		}
 	}
 
-	// now we sort the shufflable groups by the sort key.  We use the shufflable group nodes code location and break ties using its node id
-	sort.SliceStable(shufflableGroupingIDs, func(i, j int) bool {
-		keyA := shufflableGroupingsIDToSortKeys[shufflableGroupingIDs[i]]
-		keyB := shufflableGroupingsIDToSortKeys[shufflableGroupingIDs[j]]
-		if keyA == keyB {
-			return shufflableGroupingIDs[i] < shufflableGroupingIDs[j]
-		} else {
-			return keyA < keyB
-		}
-	})
-
 	// now we permute the sorted shufflable grouping IDs and build the ordered Groups
 	orderedGroups := GroupedSpecIndices{}
 	permutation := r.Perm(len(shufflableGroupingIDs))
diff --git a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
index f470641a6..5ddf10072 100644
--- a/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
+++ b/vendor/github.com/onsi/ginkgo/v2/internal/suite.go
@@ -151,6 +151,13 @@ func (suite *Suite) PushNode(node Node) error {
 		}
 	}
 
+	if node.MarkedContinueOnFailure {
+		firstOrderedNode := suite.tree.AncestorNodeChain().FirstNodeMarkedOrdered()
+		if !firstOrderedNode.IsZero() {
+			return types.GinkgoErrors.InvalidContinueOnFailureDecoration(node.CodeLocation)
+		}
+	}
+
 	if node.NodeType == types.NodeTypeContainer {
 		// During PhaseBuildTopLevel we only track the top level containers without entering them
 		// We only enter the top level container nodes during PhaseBuildTree
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/errors.go b/vendor/github.com/onsi/ginkgo/v2/types/errors.go
index a0f59fb4b..1e0dbfd9d 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/errors.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/errors.go
@@ -298,6 +298,15 @@ func (g ginkgoErrors) SetupNodeNotInOrderedContainer(cl CodeLocation, nodeType N
 	}
 }
 
+func (g ginkgoErrors) InvalidContinueOnFailureDecoration(cl CodeLocation) error {
+	return GinkgoError{
+		Heading:      "ContinueOnFailure not decorating an outermost Ordered Container",
+		Message:      "ContinueOnFailure can only decorate an Ordered container, and this Ordered container must be the outermost Ordered container.",
+		CodeLocation: cl,
+		DocLink:      "ordered-containers",
+	}
+}
+
 /* DeferCleanup errors */
 func (g ginkgoErrors) DeferCleanupInvalidFunction(cl CodeLocation) error {
 	return GinkgoError{
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/types.go b/vendor/github.com/onsi/ginkgo/v2/types/types.go
index 3e979ba8c..d048a8ada 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/types.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/types.go
@@ -604,6 +604,9 @@ var ssEnumSupport = NewEnumSupport(map[uint]string{
 func (ss SpecState) String() string {
 	return ssEnumSupport.String(uint(ss))
 }
+func (ss SpecState) GomegaString() string {
+	return ssEnumSupport.String(uint(ss))
+}
 func (ss *SpecState) UnmarshalJSON(b []byte) error {
 	out, err := ssEnumSupport.UnmarshJSON(b)
 	*ss = SpecState(out)
diff --git a/vendor/github.com/onsi/ginkgo/v2/types/version.go b/vendor/github.com/onsi/ginkgo/v2/types/version.go
index 03d5ff98a..ef61d5e7a 100644
--- a/vendor/github.com/onsi/ginkgo/v2/types/version.go
+++ b/vendor/github.com/onsi/ginkgo/v2/types/version.go
@@ -1,3 +1,3 @@
 package types
 
-const VERSION = "2.6.1"
+const VERSION = "2.7.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 9902b2985..c1e8d27a8 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -295,7 +295,7 @@ github.com/munnerz/goautoneg
 # github.com/oklog/ulid v1.3.1
 ## explicit
 github.com/oklog/ulid
-# github.com/onsi/ginkgo/v2 v2.6.1
+# github.com/onsi/ginkgo/v2 v2.7.0
 ## explicit; go 1.18
 github.com/onsi/ginkgo/v2
 github.com/onsi/ginkgo/v2/config


Test patch:
diff --git a/.github/workflows/e2e-init.yaml b/.github/workflows/e2e-init.yaml
index f43544519..3a1dbb48b 100644
--- a/.github/workflows/e2e-init.yaml
+++ b/.github/workflows/e2e-init.yaml
@@ -130,7 +130,7 @@ jobs:
           fi
 
       - name: Upload e2e log
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ${{ inputs.ip_family }}-debuglog.txt
           path: test/e2edebugLog.txt
@@ -138,7 +138,7 @@ jobs:
 
       - name: Upload e2e report
         if: ${{ steps.run_e2e.outputs.upload == 'true' }}
-        uses: actions/upload-artifact@v3.1.2
+        uses: actions/upload-artifact@v3.1.1
         with:
           name: ${{ inputs.ip_family }}-e2ereport.json
           path: e2ereport.json
diff --git a/pkg/statefulsetmanager/sts_manager_test.go b/pkg/statefulsetmanager/sts_manager_test.go
index 7ad6d0fea..380824f65 100644
--- a/pkg/statefulsetmanager/sts_manager_test.go
+++ b/pkg/statefulsetmanager/sts_manager_test.go
@@ -196,14 +196,14 @@ var _ = Describe("StatefulSetManager", Label("sts_manager_test"), func() {
 
 			It("is not a Pod controlled by StatefulSet", func() {
 				ctx := context.TODO()
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, nonStsPodName, constant.KindUnknown)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, nonStsPodName, constant.OwnerUnknown)
 				Expect(err).To(HaveOccurred())
 				Expect(valid).To(BeFalse())
 			})
 
 			It("is a Pod controlled by StatefulSet, but the Pod name is invalid", func() {
 				ctx := context.TODO()
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, nonStsPodName, constant.KindStatefulSet)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, nonStsPodName, constant.OwnerStatefulSet)
 				Expect(err).To(HaveOccurred())
 				Expect(valid).To(BeFalse())
 			})
@@ -213,14 +213,14 @@ var _ = Describe("StatefulSetManager", Label("sts_manager_test"), func() {
 				defer patches.Reset()
 
 				ctx := context.TODO()
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.KindStatefulSet)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.OwnerStatefulSet)
 				Expect(err).To(HaveOccurred())
 				Expect(valid).To(BeFalse())
 			})
 
 			It("is a valid Pod controlled by StatefulSet, but the StatefulSet no longer exists", func() {
 				ctx := context.TODO()
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.KindStatefulSet)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.OwnerStatefulSet)
 				Expect(err).NotTo(HaveOccurred())
 				Expect(valid).To(BeFalse())
 			})
@@ -232,7 +232,7 @@ var _ = Describe("StatefulSetManager", Label("sts_manager_test"), func() {
 				err := fakeClient.Create(ctx, stsT)
 				Expect(err).NotTo(HaveOccurred())
 
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.KindStatefulSet)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.OwnerStatefulSet)
 				Expect(err).NotTo(HaveOccurred())
 				Expect(valid).To(BeFalse())
 			})
@@ -245,7 +245,7 @@ var _ = Describe("StatefulSetManager", Label("sts_manager_test"), func() {
 				err := fakeClient.Create(ctx, stsT)
 				Expect(err).NotTo(HaveOccurred())
 
-				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.KindStatefulSet)
+				valid, err := stsManager.IsValidStatefulSetPod(ctx, namespace, stsPodName, constant.OwnerStatefulSet)
 				Expect(err).NotTo(HaveOccurred())
 				Expect(valid).To(BeTrue())
 			})
diff --git a/pkg/workloadendpointmanager/workloadendpoint_manager_test.go b/pkg/workloadendpointmanager/workloadendpoint_manager_test.go
index cb0ccadef..b920a4914 100644
--- a/pkg/workloadendpointmanager/workloadendpoint_manager_test.go
+++ b/pkg/workloadendpointmanager/workloadendpoint_manager_test.go
@@ -27,7 +27,6 @@ import (
 
 	"github.com/spidernet-io/spiderpool/pkg/constant"
 	spiderpoolv1 "github.com/spidernet-io/spiderpool/pkg/k8s/apis/spiderpool.spidernet.io/v1"
-	spiderpooltypes "github.com/spidernet-io/spiderpool/pkg/types"
 	"github.com/spidernet-io/spiderpool/pkg/workloadendpointmanager"
 )
 
@@ -328,7 +327,7 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("failed to get the top controller of Pod due to some unknown errors", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{}, constant.ErrUnknown).
+					Return("", nil, constant.ErrUnknown).
 					Times(1)
 
 				ctx := context.TODO()
@@ -340,13 +339,7 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("failed to set ownerReference to Pod due to some unknown errors", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{
-						Kind:      constant.KindPod,
-						Namespace: podT.Namespace,
-						Name:      podT.Name,
-						Uid:       podT.UID,
-						App:       podT,
-					}, nil).
+					Return(constant.OwnerNone, nil, nil).
 					Times(1)
 
 				patches := gomonkey.ApplyFuncReturn(controllerutil.SetOwnerReference, constant.ErrUnknown)
@@ -361,13 +354,7 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("failed to create Endpoint due to some unknown errors", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{
-						Kind:      constant.KindPod,
-						Namespace: podT.Namespace,
-						Name:      podT.Name,
-						Uid:       podT.UID,
-						App:       podT,
-					}, nil).
+					Return(constant.OwnerNone, nil, nil).
 					Times(1)
 
 				patches := gomonkey.ApplyMethodReturn(fakeClient, "Create", constant.ErrUnknown)
@@ -382,13 +369,7 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("failed to update the status of Endpoint due to some unknown errors", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{
-						Kind:      constant.KindPod,
-						Namespace: podT.Namespace,
-						Name:      podT.Name,
-						Uid:       podT.UID,
-						App:       podT,
-					}, nil).
+					Return(constant.OwnerNone, nil, nil).
 					Times(1)
 
 				patches := gomonkey.ApplyMethodReturn(fakeClient.Status(), "Update", constant.ErrUnknown)
@@ -403,13 +384,7 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("marks the IP allocation for orphan Pod", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{
-						Kind:      constant.KindPod,
-						Namespace: podT.Namespace,
-						Name:      podT.Name,
-						Uid:       podT.UID,
-						App:       podT,
-					}, nil).
+					Return(constant.OwnerNone, nil, nil).
 					Times(1)
 
 				ctx := context.TODO()
@@ -421,13 +396,20 @@ var _ = Describe("WorkloadEndpointManager", Label("workloadendpoint_manager_test
 			It("marks the IP allocation for StatefulSet's Pod", func() {
 				mockPodManager.EXPECT().
 					GetPodTopController(gomock.All(), gomock.All()).
-					Return(spiderpooltypes.PodTopController{
-						Kind:      constant.KindStatefulSet,
-						Namespace: namespace,
-						Name:      fmt.Sprintf("%s-sts", endpointName),
-						Uid:       uuid.NewUUID(),
-						App:       &appsv1.StatefulSet{},
-					}, nil).
+					Return(
+						constant.OwnerStatefulSet,
+						&appsv1.StatefulSet{
+							TypeMeta: metav1.TypeMeta{
+								Kind:       "StatefulSet",
+								APIVersion: appsv1.SchemeGroupVersion.String(),
+							},
+							ObjectMeta: metav1.ObjectMeta{
+								Name:      "statefulset",
+								Namespace: namespace,
+							},
+						},
+						nil,
+					).
 					Times(1)
 
 				ctx := context.TODO()
diff --git a/test/e2e/kruise/kruise_test.go b/test/e2e/kruise/kruise_test.go
index 500be1f42..68eceb600 100644
--- a/test/e2e/kruise/kruise_test.go
+++ b/test/e2e/kruise/kruise_test.go
@@ -16,7 +16,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
-var _ = PDescribe("Third party control:OpenKruise", Label("kruise"), func() {
+var _ = Describe("Third party control:OpenKruise", Label("kruise"), func() {
 	var namespace, kruiseCloneSetName, v4SubnetName, v6SubnetName, v4PoolName, v6PoolName string
 	var v4SubnetObject, v6SubnetObject *spiderpool.SpiderSubnet
 	var v4PoolObj, v6PoolObj *spiderpool.SpiderIPPool