Repository: spidernet-io/spiderpool
Pull request #3323 (2024-03-19 10:06:40)

Add upgrade test for spiderpool
## Thanks for contributing!

<!--Before submitting a pull request, make sure you read about our Contribution notice here: <https://spidernet-io.github.io/spiderpool/latest/develop/contributing/>-->

#### What type of PR is this?

<!--
Add one of the following kinds:

Required labels:

- release/none 
- release/bug 
- release/feature

Optional labels:

- kind/bug
- kind/feature
- kind/ci-bug
- kind/doc
-->

**What this PR does / why we need it**:

**Which issue(s) this PR fixes**:
<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
-->
Fixes #

**Special notes for your reviewer**:


ty-dc: rebase #3322 

Patch:
diff --git a/.github/workflows/auto-upgrade-ci.yaml b/.github/workflows/auto-upgrade-ci.yaml
new file mode 100644
index 000000000..f7eae973f
--- /dev/null
+++ b/.github/workflows/auto-upgrade-ci.yaml
@@ -0,0 +1,282 @@
+name: Auto Upgrade Test
+
+permissions: write-all
+
+env:
+  CLUSTER_NAME: spider
+  E2E_TIME_OUT: 60m
+  KUBECONFIG_PATH: /home/runner/work/spiderpool/spiderpool/test/.cluster
+on:
+  schedule:
+    - cron: "0 20 * * *"
+
+  workflow_dispatch:
+    inputs:
+      ref:
+        description: 'sha, tag, branch. also this is the version you will upgrade to.'
+        required: true
+        default: main
+      old_version:
+        description: 'specify your old version, for example: v0.9.2; if not set, the default version set will be run.'
+        required: false
+        type: string
+        default: v0.9.0
+      e2e_enabled:
+        description: 'run e2e test'
+        required: false
+        type: choice
+        default: "false"
+        options:
+          - "true"
+          - "false"
+
+jobs:
+  get_ref:
+    runs-on: ubuntu-latest
+    outputs:
+      ref: ${{ env.RUN_REF }}
+      old_version: ${{ env.OLD_VERSION }}
+      e2e_enabled: ${{ env.RUN_E2E_ENABLED }}
+
+    steps:
+      - name: Get Ref
+        id: get_ref
+        run: |
+          if ${{ github.event_name == 'workflow_dispatch' }} ; then
+            echo "call by self workflow_dispatch"
+            echo "RUN_TAG=${{ github.event.inputs.ref }}" >> $GITHUB_ENV
+            echo "OLD_VERSION=${{ github.event.inputs.old_version }}" >> $GITHUB_ENV
+            if ${{ github.event.inputs.e2e_enabled == 'true' }}; then
+              echo "RUN_E2E_ENABLED=true" >> $GITHUB_ENV
+            else
+              echo "RUN_E2E_ENABLED=false" >> $GITHUB_ENV
+            fi
+          else
+            # schedule event
+            # use main sha for ci image tag
+            echo "trigger by schedule"
+            echo "RUN_TAG=main" >> $GITHUB_ENV
+            LATEST_RELEASE_VERISON=$(curl -s https://api.github.com/repos/spidernet-io/spiderpool/releases | grep '"tag_name":' | grep -Eo "v([0-9]+\.[0-9]+\.[0-9])" | sort -r | head -n 1)
+            echo "OLD_VERSION=${LATEST_RELEASE_VERISON}" >> $GITHUB_ENV
+            echo "RUN_E2E_ENABLED=true" >> $GITHUB_ENV
+          fi
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+          ref: ${{ env.RUN_TAG }}
+
+      - name: Result Ref
+        id: result
+        run: |
+          ref=$( git show -s --format='format:%H')
+          echo "RUN_REF=${ref}" >> $GITHUB_ENV
+
+  call_build_ci_image:
+    needs: [get_ref]
+    uses: ./.github/workflows/build-image-ci.yaml
+    with:
+      ref: ${{ needs.get_ref.outputs.ref }}
+      push: false
+    secrets: inherit
+
+  lint_chart_against_release_image:
+    needs: get_ref
+    uses: ./.github/workflows/call-lint-chart.yaml
+    with:
+      ref: ${{ needs.get_ref.outputs.ref }}
+    secrets: inherit
+
+  trivy_scan_images:
+    needs: [call_build_ci_image, get_ref]
+    uses: ./.github/workflows/trivy-scan-image.yaml
+    with:
+      image_tag: ${{ needs.call_build_ci_image.outputs.imageTag }}
+      ref: ${{ needs.get_ref.outputs.ref }}
+    secrets: inherit
+
+  run_upgrade_tests_on_release_version:
+    needs: [call_build_ci_image, get_ref]
+    runs-on: ubuntu-latest
+    steps:
+      - name: Free Disk Space (Ubuntu)
+        uses: jlumbroso/free-disk-space@main
+        with:
+          tool-cache: false
+          android: true
+          dotnet: true
+          haskell: true
+          large-packages: true
+          docker-images: true
+          swap-storage: true
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+          ref: ${{ needs.get_ref.outputs.old_version }}
+
+      - name: Prepare
+        id: prepare
+        run: |
+          echo "ref: ${{ inputs.ref }} "
+          echo "===== image "
+          echo "ci image tag: ghcr.io/${{ github.repository }}/spiderpool-controller:${{ needs.get_ref.outputs.old_version }}"
+          TMP=` date +%m%d%H%M%S `
+          E2E_CLUSTER_NAME="spiderpool${TMP}"
+          echo "E2E_CLUSTER_NAME=${E2E_CLUSTER_NAME}" >> $GITHUB_ENV
+
+      - name: Setup Golang
+        uses: actions/setup-go@v5
+        with:
+          go-version: 1.21.4
+
+      - name: Install Kind Bin
+        uses: helm/kind-action@v1.8.0
+        with:
+          install_only: true
+
+      - name: Install Tools
+        run: |
+          bash ./test/scripts/install-tools.sh
+
+      - name: Setup Kind Cluster on oldVersion ${{ needs.get_ref.outputs.old_version }}
+        uses: nick-invision/retry@v2
+        with:
+          timeout_minutes: 20
+          max_attempts: 3
+          on_retry_command: |
+            echo "debug"
+            systemctl status docker
+            journalctl -xeu docker
+            echo "restart docker before trying again"
+            systemctl restart docker
+          command: |
+            make e2e_init_calico -e E2E_CLUSTER_NAME=${{ env.E2E_CLUSTER_NAME }} \
+                -e E2E_SPIDERPOOL_TAG=${{ needs.get_ref.outputs.old_version }} \
+                -e PYROSCOPE_LOCAL_PORT=""
+
+      - name: backup kubeconfig from olderVersion ${{ needs.get_ref.outputs.old_version }}
+        run: |
+          cp -r ${{ env.KUBECONFIG_PATH }}/${{ env.E2E_CLUSTER_NAME }}/.kube/config /tmp
+
+      - name: Checkout code
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+          ref: ${{ needs.get_ref.outputs.ref }}
+          fetch-depth: 0
+
+      - name: Move kubeconfig from oldVersion ${{ needs.get_ref.outputs.old_version }} to the upgradeVersion ${{ needs.get_ref.outputs.ref }}
+        run: |
+          mkdir -p ${{ env.KUBECONFIG_PATH }}/${{ env.E2E_CLUSTER_NAME }}/.kube/
+          cp -r /tmp/config ${{ env.KUBECONFIG_PATH }}/${{ env.E2E_CLUSTER_NAME }}/.kube/config
+
+      - name: Download spiderpool-agent image
+        uses: actions/download-artifact@v3
+        with:
+          name: image-tar-spiderpool-agent
+          path: test/.download
+
+      - name: Download spiderpool-controller image
+        uses: actions/download-artifact@v3
+        with:
+          name: image-tar-spiderpool-controller
+          path: test/.download
+
+      - name: Load Images
+        run: |
+          TAR_FILES=` ls test/.download `
+          echo $TAR_FILES
+          for ITEM in $TAR_FILES ; do
+              IMAGE_NAME=${ITEM%*.tar}
+              echo ${IMAGE_NAME}
+              cat test/.download/${ITEM} | docker import - ${IMAGE_NAME}:${{ needs.call_build_ci_image.outputs.imageTag }}
+          done
+
+      - name: Upgrade to version ${{ needs.get_ref.outputs.ref }}
+        id: upgrade
+        continue-on-error: true
+        run: |
+          make upgrade_e2e_spiderpool -e E2E_SPIDERPOOL_TAG=${{ needs.call_build_ci_image.outputs.imageTag }} \
+                -e SPIDERPOOL_AGENT_IMAGE_NAME=spiderpool-agent-race \
+                -e SPIDERPOOL_CONTROLLER_IMAGE_NAME=spiderpool-controller-race \
+
+      - name: Run e2e Test on ${{ needs.get_ref.outputs.ref }}
+        id: run_e2e
+        continue-on-error: true
+        if: ${{ needs.get_ref.outputs.e2e_enabled == 'true' }}
+        run: |
+          RESULT=0
+          export KUBECONFIG=${{ env.KUBECONFIG_PATH }}/${{ env.E2E_CLUSTER_NAME }}/.kube/config
+          export DEBUG_ENV_PATH=${{ env.KUBECONFIG_PATH }}/../scripts/debugEnv.sh
+          kubectl get po -A | grep  "spiderpool"
+          RESULT=$(kubectl get po -A | grep  "spiderpool" | grep -Ev "Running|Completed" | wc -l )
+          kubectl get network-attachment-definitions.k8s.cni.cncf.io -A
+          sleep 60
+          if ((RESULT==0)) ; then
+              kubectl get po -l app=test-pod -oyaml
+              echo "after the upgrade is completed, restart the Pod. The Pod will run normally on the new version."
+              kubectl delete po -l app=test-pod
+              if kubectl wait --for=condition=ready -l app=test-pod --timeout=300s pod; then
+                kubectl get po -l app=test-pod -owide
+                kubectl get po -l app=test-pod -oyaml
+                echo "RUN_E2E_PASS=true" >> $GITHUB_ENV
+              else
+                kubectl get po -l app=test-pod -owide
+                kubectl describe po -l app=test-pod || KIND_CLUSTER_NAME=${{ env.E2E_CLUSTER_NAME }} ${DEBUG_ENV_PATH} ${KUBECONFIG} "detail" ;
+                echo "RUN_E2E_PASS=false" >> $GITHUB_ENV
+              fi
+          else
+              KIND_CLUSTER_NAME=${{ env.E2E_CLUSTER_NAME }} ${DEBUG_ENV_PATH} ${KUBECONFIG} "detail" ; \
+              kubectl describe po -n kube-system -l app.kubernetes.io/instance=spiderpool
+              kubectl logs -n kube-system -l app.kubernetes.io/instance=spiderpool
+              echo "RUN_E2E_PASS=false" >> $GITHUB_ENV
+          fi
+
+      - name: Upload e2e log
+        if: ${{ needs.get_ref.outputs.e2e_enabled == 'true' }}
+        uses: actions/upload-artifact@v3.1.3
+        with:
+          name: ${{ needs.get_ref.outputs.old_version }}-debuglog.txt
+          path: test/e2edebugLog.txt
+          retention-days: 7
+
+      - name: Upload e2e report
+        if: ${{ env.UPLOAD_E2E_REPORT == 'true' }}
+        uses: actions/upload-artifact@v3.1.3
+        with:
+          name: ${{ needs.get_ref.outputs.old_version }}-e2ereport.json
+          path: e2ereport.json
+          retention-days: 1
+
+      - name: Show e2e Result
+        if: ${{ needs.get_ref.outputs.e2e_enabled == 'true' }}
+        run: |
+          if ${{ env.RUN_E2E_PASS == 'true' }} ;then
+              exit 0
+          else
+              exit 1
+          fi
+
+  creat_issue:
+    runs-on: ubuntu-latest
+    needs: [run_upgrade_tests_on_release_version, trivy_scan_images]
+    if: ${{ always() && needs.run_upgrade_tests_on_release_version.result == 'failure' }}
+    steps:
+      - name: echo
+        run: |
+          echo ${{ github.repository }}
+          echo ${{ github.repository_owner }}
+          echo "TIMESTAMP=`date +%Y-%m-%d`" >> $GITHUB_ENV
+
+      - name: create an issue
+        uses: dacbd/create-issue-action@v1.2.1
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+          title: "upgrade CI ${{ ENV.TIMESTAMP }}: Failed"
+          body: |
+            action url: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
+          labels: "kind/ci-bug"
+          assignees: "ty-dc"
diff --git a/.github/workflows/auto-version-release.yaml b/.github/workflows/auto-version-release.yaml
index 22d1fd6a1..7f2c9d0d5 100644
--- a/.github/workflows/auto-version-release.yaml
+++ b/.github/workflows/auto-version-release.yaml
@@ -117,13 +117,6 @@ jobs:
       dest_tag: ${{ needs.get-tag.outputs.tag }}
     secrets: inherit
 
-  update-release-version:
-    needs: [build-release-image, get-tag]
-    uses: ./.github/workflows/call-release-version.yaml
-    with:
-      dest_tag: ${{ needs.get-tag.outputs.tag }}
-    secrets: inherit
-
   create-release:
     needs: [release-chart, release-changelog, get-tag]
     name: create release
diff --git a/.github/workflows/call-lint-chart.yaml b/.github/workflows/call-lint-chart.yaml
index 46607c8de..0524d98f0 100644
--- a/.github/workflows/call-lint-chart.yaml
+++ b/.github/workflows/call-lint-chart.yaml
@@ -73,7 +73,7 @@ jobs:
 
       # https://github.com/actions/setup-python
       - name: Set up Python
-        uses: actions/setup-python@v5.1.0
+        uses: actions/setup-python@v5.0.0
         with:
           python-version: 3.8
           architecture: x64
diff --git a/.github/workflows/call-release-version.yaml b/.github/workflows/call-release-version.yaml
deleted file mode 100644
index 60620823b..000000000
--- a/.github/workflows/call-release-version.yaml
+++ /dev/null
@@ -1,127 +0,0 @@
-name: Update The Release Version Of README
-
-on:
-  workflow_call:
-    inputs:
-      ref:
-        required: true
-        type: string
-  # --- call by manual
-  workflow_dispatch:
-    inputs:
-      ref:
-        description: 'branch, tag, sha, example: v0.9.2'
-        required: true
-        default: v0.9.2
-
-permissions: write-all
-
-env:
-  PR_LABEL: pr/release/doc
-  PR_REVIWER: ty-dc
-
-jobs:
-  release_notes:
-    runs-on: ubuntu-latest
-    outputs:
-      ref: ${{ env.REF }}
-    steps:
-      - name: Get Ref
-        id: get_ref
-        run: |
-          pwd
-          ls
-          if ${{ github.event_name == 'workflow_dispatch' }}; then
-              echo "call by workflow_dispatch"
-              echo "REF=${{ github.event.inputs.ref }}" >> $GITHUB_ENV
-          elif ${{ inputs.ref != '' }}; then
-              echo "call by workflow_call"
-              echo "REF=${{ inputs.ref }}" >> $GITHUB_ENV
-          else
-              echo "unexpected event: ${{ github.event_name }}"
-              exit 1
-          fi
-
-      - name: Checkout Code
-        uses: actions/checkout@v4
-        with:
-          fetch-depth: 0
-          # update the release-note of the main branch after the release.
-          ref: main
-
-      # As versions are released, the versions we maintain will change.
-      # Since we only maintain the latest versions of the three branches,
-      # for minor version(.z version) changes, we will update the release version accordingly.
-      # For major version(.y version) releases, we add a new release version and delete the oldest version.
-      - name: update release version of readme
-        run: |
-          echo "update release version of readme"
-          branchName=` grep -Eo "v[0-9]+\.[0-9]+" <<< ${{ env.REF }}  `
-          if [ -n "${branchName}" ]; then
-              echo "the branch obtained from the version number is: ${branchName}"
-          else
-              echo "error, failed to get ${branchName}, are you sure the version is correct: ${{ env.REF }} ?." && exit 1
-          fi
-          FILE_NAME_LIST=$(grep -Rl '\[Release Notes\]' --exclude-dir .github)
-          echo "FILE_NAME_LIST: ${FILE_NAME_LIST}"
-          if ! grep -r '\[Release Notes\]' *  | grep release-${branchName} ; then
-            echo "release notes for this version: release-${branchName} do not exist"
-            for FILE_NAME in ${FILE_NAME_LIST}
-            do
-              BEGIN_LINE=$(grep '\[Release Notes\]' $FILE_NAME -n |  awk -F ':' '{print $1}' | head -n 1)
-              END_LINE=$(grep '\[Release Notes\]' $FILE_NAME -n |  awk -F ':' '{print $1}' | tail -n 1)
-              MINIMAL_VERSION=$(sed -n -e "${END_LINE}p" $FILE_NAME | awk -F 'release-|]' '{print $2}')
-              if [[ "${branchName}" < "${MINIMAL_VERSION}" ]]; then
-                echo "${branchName} is not a maintenance release and the version information in the readme file will not be updated."
-                echo "SKIP=true" >> $GITHUB_ENV
-                exit 0
-              fi
-              echo "delete file:$FILE_NAME line $END_LINE"
-              sed -i -e "${END_LINE} d" $FILE_NAME
-              echo "succeeded to delete file:$FILE_NAME the $END_LINE"
-              echo "Add file:$FILE_NAME line before $BEGIN_LINE"
-              ASSERT_LINE=$((BEGIN_LINE-1))
-              lineText="| [release-$branchName](https://github.com/spidernet-io/spiderpool/tree/release-${branchName}) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/${{ env.REF }})   |"
-              sed -i -e "${ASSERT_LINE}a $lineText" $FILE_NAME
-              echo "succeeded to add file:$FILE_NAME the $ASSERT_LINE"
-            done
-          else
-            for FILE_NAME in ${FILE_NAME_LIST}
-            do
-              grep '\[Release Notes\]' $FILE_NAME -n | grep release-${branchName}
-              LINE=$(grep '\[Release Notes\]' $FILE_NAME -n | grep release-${branchName} | awk -F ':' '{print $1}'| head -n 1)
-              echo "delete file:$FILE_NAME line $LINE"
-              sed -i -e "${LINE}d" $FILE_NAME
-              echo "succeeded to delete file:$FILE_NAME the $LINE"
-              echo "Add file:$FILE_NAME line before $LINE"
-              ASSERT_LINE=$(($LINE-1))
-              lineText="| [release-$branchName](https://github.com/spidernet-io/spiderpool/tree/release-${branchName}) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/${{ env.REF }})   |"
-              sed -i -e "${ASSERT_LINE}a $lineText" $FILE_NAME
-              echo "succeeded to add file:$FILE_NAME the $ASSERT_LINE"
-            done
-          fi
-
-      - uses: crazy-max/ghaction-import-gpg@v6
-        if: ${{ env.SKIP != 'true' }}
-        with:
-          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
-          passphrase: ${{ secrets.GPG_PASSPHRASE }}
-          git_user_signingkey: true
-          git_commit_gpgsign: true
-
-      # Allow auto-merge on general
-      - name: Create Pull Request
-        if: ${{ env.SKIP != 'true' }}
-        id: create_pr
-        uses: peter-evans/create-pull-request@v5.0.2
-        with:
-          title: "robot updates the release version of the README file based on the release tag: ${{ env.REF }} "
-          commit-message: "robot updates the release version of the README file based on the release tag: ${{ env.REF }}"
-          branch-suffix: timestamp
-          committer: ty-dc <tao.yang@daocloud.io>
-          branch: robot/update_doc
-          delete-branch: true
-          signoff: true
-          token: ${{ secrets.GITHUB_TOKEN }}
-          labels: ${{ env.PR_LABEL }}
-          reviewers: ${{ env.PR_REVIWER }}
diff --git a/.github/workflows/lint-spell.yaml b/.github/workflows/lint-spell.yaml
index 4143a19c6..4b8d445cd 100644
--- a/.github/workflows/lint-spell.yaml
+++ b/.github/workflows/lint-spell.yaml
@@ -51,7 +51,7 @@ jobs:
           ref: ${{ env.ref }}
 
       - name: Set up Python
-        uses: actions/setup-python@v5.1.0
+        uses: actions/setup-python@v5.0.0
         with:
           python-version: 3.8
           architecture: x64
diff --git a/Makefile b/Makefile
index 1cc324880..d22ab56bd 100644
--- a/Makefile
+++ b/Makefile
@@ -428,6 +428,10 @@ clean: clean_e2e
 clean_e2e_spiderpool:
 	-$(QUIET) make -C test uninstall_spiderpool
 
+.PHONY: upgrade_e2e_spiderpool
+upgrade_e2e_spiderpool:
+	-$(QUIET) make -C test upgrade_spiderpool
+
 .PHONY: codegen
 codegen:
 	@echo "Generate k8s SDK with code-generator."
diff --git a/README-zh_CN.md b/README-zh_CN.md
index a80088a25..c5fdf013b 100644
--- a/README-zh_CN.md
+++ b/README-zh_CN.md
@@ -34,7 +34,7 @@ Spiderpool 社区将最新的三个 Spiderpool 补丁版本视为稳定版本，
 
 |                         发布分支                                      |                               发行说明                                              |
 | -------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
-| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.3)   |
+| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.1)   |
 | [release-v0.8](https://github.com/spidernet-io/spiderpool/tree/release-v0.8) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.8.5)   |
 | [release-v0.7](https://github.com/spidernet-io/spiderpool/tree/release-v0.7) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.7.3)   |
 
diff --git a/README.md b/README.md
index 779664bee..162a2f213 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@ Listed below are the release notes for the currently maintained release branches
 
 |                         release branches                             |                               Release Notes                                       |
 | -------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
-| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.3)   |
+| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.1)   |
 | [release-v0.8](https://github.com/spidernet-io/spiderpool/tree/release-v0.8)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.8.5)   |
 | [release-v0.7](https://github.com/spidernet-io/spiderpool/tree/release-v0.7)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.7.3)   |
 
diff --git a/charts/spiderpool/README.md b/charts/spiderpool/README.md
index d4572e75f..018839a33 100644
--- a/charts/spiderpool/README.md
+++ b/charts/spiderpool/README.md
@@ -370,7 +370,6 @@ helm install spiderpool spiderpool/spiderpool --wait --namespace kube-system \
 | `spiderpoolController.tls.auto.certExpiration`                                  | server cert expiration for auto method                                                                                            | `73000`                                         |
 | `spiderpoolController.tls.auto.extraIpAddresses`                                | extra IP addresses of server certificate for auto method                                                                          | `[]`                                            |
 | `spiderpoolController.tls.auto.extraDnsNames`                                   | extra DNS names of server cert for auto method                                                                                    | `[]`                                            |
-| `spiderpoolController.cleanup.enable`                                           | clean up resources when helm uninstall                                                                                            | `true`                                          |
 
 ### spiderpoolInit parameters
 
diff --git a/charts/spiderpool/templates/deleteHook.yaml b/charts/spiderpool/templates/deleteHook.yaml
deleted file mode 100644
index 30ddc37f8..000000000
--- a/charts/spiderpool/templates/deleteHook.yaml
+++ /dev/null
@@ -1,34 +0,0 @@
-{{- if .Values.spiderpoolController.cleanup.enable }}
-apiVersion: batch/v1
-kind: Job
-metadata:
-  name: {{ .Values.spiderpoolController.name | trunc 48 | trimSuffix "-" }}-hook-pre-delete
-  annotations:
-    "helm.sh/hook": pre-delete
-    "helm.sh/hook-delete-policy": hook-succeeded,hook-failed
-spec:
-  template:
-    spec:
-      serviceAccountName: {{ .Values.spiderpoolController.name | trunc 63 | trimSuffix "-" }}
-      containers:
-        - name: pre-delete
-          image: {{ include "spiderpool.spiderpoolController.image" . | quote }}
-          command:
-            - {{ .Values.spiderpoolController.binName }}
-            - clean
-            - --validate
-            - {{ .Values.spiderpoolController.name | trunc 63 | trimSuffix "-" }}
-            - --mutating
-            - {{ .Values.spiderpoolController.name | trunc 63 | trimSuffix "-" }}
-          env:
-            - name: SPIDERPOOL_POD_NAME
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.name
-            - name: SPIDERPOOL_POD_NAMESPACE
-              valueFrom:
-                fieldRef:
-                  fieldPath: metadata.namespace
-      restartPolicy: Never
-  backoffLimit: 2
-{{- end }}
diff --git a/charts/spiderpool/templates/role.yaml b/charts/spiderpool/templates/role.yaml
index 8d928ed34..3aea975e0 100644
--- a/charts/spiderpool/templates/role.yaml
+++ b/charts/spiderpool/templates/role.yaml
@@ -41,31 +41,6 @@ rules:
   - get
   - list
   - watch
-- apiGroups:
-  - admissionregistration.k8s.io
-  resources:
-  - mutatingwebhookconfigurations
-  - validatingwebhookconfigurations
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
-- apiGroups:
-  - apiextensions.k8s.io
-  resources:
-  - customresourcedefinitions
-  verbs:
-  - create
-  - delete
-  - get
-  - list
-  - patch
-  - update
-  - watch
 - apiGroups:
   - apps
   resources:
diff --git a/charts/spiderpool/values.yaml b/charts/spiderpool/values.yaml
index d7bc68203..5304026d5 100644
--- a/charts/spiderpool/values.yaml
+++ b/charts/spiderpool/values.yaml
@@ -757,10 +757,6 @@ spiderpoolController:
       ## @param spiderpoolController.tls.auto.extraDnsNames extra DNS names of server cert for auto method
       extraDnsNames: []
 
-  cleanup:
-    ## @param spiderpoolController.cleanup.enable clean up resources when helm uninstall
-    enable: true
-
 ## @section spiderpoolInit parameters
 ##
 spiderpoolInit:
diff --git a/cmd/spiderpool-controller/cmd/clean.go b/cmd/spiderpool-controller/cmd/clean.go
deleted file mode 100644
index 042d3a38c..000000000
--- a/cmd/spiderpool-controller/cmd/clean.go
+++ /dev/null
@@ -1,221 +0,0 @@
-// Copyright 2022 Authors of spidernet-io
-// SPDX-License-Identifier: Apache-2.0
-
-package cmd
-
-import (
-	"context"
-	"os"
-
-	"github.com/hashicorp/go-multierror"
-	"github.com/spf13/cobra"
-	"github.com/spidernet-io/spiderpool/pkg/constant"
-	spiderpoolv2beta1 "github.com/spidernet-io/spiderpool/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1"
-	webhook "k8s.io/api/admissionregistration/v1"
-	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
-	ctrl "sigs.k8s.io/controller-runtime"
-	"sigs.k8s.io/controller-runtime/pkg/client"
-)
-
-var cleanCmd = &cobra.Command{
-	Use:   "clean",
-	Short: "Clean resources",
-	Long:  "Clean resources with specified parameters.",
-	Run: func(cmd *cobra.Command, args []string) {
-
-		validate, err := cmd.Flags().GetString("validate")
-		if err != nil {
-			logger.Fatal(err.Error())
-			os.Exit(1)
-		}
-		mutating, err := cmd.Flags().GetString("mutating")
-		if err != nil {
-			logger.Fatal(err.Error())
-			os.Exit(1)
-		}
-		logger.Sugar().Infof("validate %s\nmutating %s\n", validate, mutating)
-
-		client, err := NewCoreClient()
-		if err != nil {
-			logger.Fatal(err.Error())
-			os.Exit(1)
-		}
-		err = client.clean(validate, mutating)
-		if err != nil {
-			logger.Fatal(err.Error())
-			os.Exit(1)
-		}
-	},
-}
-
-type CoreClient struct {
-	client.Client
-}
-
-func NewCoreClient() (*CoreClient, error) {
-	c, err := client.New(
-		ctrl.GetConfigOrDie(),
-		client.Options{Scheme: scheme},
-	)
-	if err != nil {
-		return nil, err
-	}
-
-	return &CoreClient{Client: c}, nil
-}
-
-func (c *CoreClient) clean(validate, mutating string) error {
-	var jobResult *multierror.Error
-	ctx := context.Background()
-	vObj := &webhook.ValidatingWebhookConfiguration{}
-	err := c.Get(ctx, client.ObjectKey{Name: validate}, vObj)
-	if err == nil {
-		err := c.Delete(ctx, vObj)
-		if err != nil {
-			logger.Sugar().Errorf("failed to delete ValidatingWebhookConfiguration: %v.", err)
-			jobResult = multierror.Append(jobResult, err)
-		}
-		logger.Sugar().Infof("succeeded to delete ValidatingWebhookConfiguration")
-	}
-
-	mObj := &webhook.MutatingWebhookConfiguration{}
-	err = c.Get(ctx, client.ObjectKey{Name: mutating}, mObj)
-	if err == nil {
-		err := c.Delete(ctx, mObj)
-		if err != nil {
-			logger.Sugar().Errorf("failed to delete MutatingWebhookConfiguration: %v.", err)
-			jobResult = multierror.Append(jobResult, err)
-		}
-		logger.Sugar().Infof("succeeded to delete MutatingWebhookConfiguration")
-	}
-
-	ipPoolList := new(spiderpoolv2beta1.SpiderIPPoolList)
-	err = c.List(ctx, ipPoolList)
-	if err == nil {
-		for _, item := range ipPoolList.Items {
-			item.Finalizers = make([]string, 0)
-			err := c.Update(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to clean the finalizers of ippool: %v, %v", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to clean the finalizers of ippool %v", &item.Name)
-
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete ippool: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete ippool: %v", &item.Name)
-		}
-	}
-
-	subnetList := new(spiderpoolv2beta1.SpiderSubnetList)
-	err = c.List(ctx, subnetList)
-	if err == nil {
-		for _, item := range subnetList.Items {
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete subnet: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete subnet: %v", &item.Name)
-		}
-	}
-
-	spiderEndpointList := new(spiderpoolv2beta1.SpiderEndpointList)
-	err = c.List(ctx, spiderEndpointList)
-	if err == nil {
-		for _, item := range spiderEndpointList.Items {
-			item.Finalizers = make([]string, 0)
-			err := c.Update(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to clean the finalizers of spiderEndpoint: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to clean the finalizers of spiderEndpoint %v", &item.Name)
-
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete spiderEndpoint: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete spiderEndpoint: %v", &item.Name)
-		}
-	}
-
-	reservedIPList := new(spiderpoolv2beta1.SpiderReservedIPList)
-	err = c.List(ctx, reservedIPList)
-	if err == nil {
-		for _, item := range reservedIPList.Items {
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete spiderReservedIP: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete spiderReservedIP: %v", &item.Name)
-		}
-	}
-
-	spiderMultusConfigList := new(spiderpoolv2beta1.SpiderMultusConfigList)
-	err = c.List(ctx, spiderMultusConfigList)
-	if err == nil {
-		for _, item := range spiderMultusConfigList.Items {
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete spiderMultusConfig: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete spiderMultusConfig: %v", &item.Name)
-		}
-	}
-
-	spiderCoordinatorList := new(spiderpoolv2beta1.SpiderCoordinatorList)
-	err = c.List(ctx, spiderCoordinatorList)
-	if err == nil {
-		for _, item := range spiderCoordinatorList.Items {
-			item.Finalizers = make([]string, 0)
-			err := c.Update(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to clean the finalizers of spiderCoordinator: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to clean the finalizers of spiderCoordinator %v", &item.Name)
-
-			err = c.Delete(ctx, &item)
-			if err != nil {
-				logger.Sugar().Errorf("failed to delete spiderCoordinator: %v, %v ", &item.Name, err)
-				jobResult = multierror.Append(jobResult, err)
-				continue
-			}
-			logger.Sugar().Infof("succeeded to delete spiderCoordinator: %v", &item.Name)
-		}
-	}
-
-	// Delete all crds of spiderpool
-	customResourceDefinitionList := new(apiextensionsv1.CustomResourceDefinitionList)
-	err = c.List(ctx, customResourceDefinitionList)
-	if err == nil {
-		for _, item := range customResourceDefinitionList.Items {
-			if item.Spec.Group == constant.SpiderpoolAPIGroup {
-				err = c.Delete(ctx, &item)
-				if err != nil {
-					logger.Sugar().Errorf("failed to delete customResourceDefinitionList: %v, %v ", &item.Name, err)
-					jobResult = multierror.Append(jobResult, err)
-					continue
-				}
-				logger.Sugar().Infof("succeeded to delete customResourceDefinitionList: %v", &item.Name)
-			}
-		}
-	}
-
-	return jobResult.ErrorOrNil()
-}
diff --git a/cmd/spiderpool-controller/cmd/command_root.go b/cmd/spiderpool-controller/cmd/command_root.go
index 9b85b55aa..dc1d9444a 100644
--- a/cmd/spiderpool-controller/cmd/command_root.go
+++ b/cmd/spiderpool-controller/cmd/command_root.go
@@ -24,10 +24,6 @@ var rootCmd = &cobra.Command{
 // Execute adds all child commands to the root command sets flags appropriately.
 // This is called by main.main(). It only needs to happen once to the rootCmd.
 func Execute() {
-	cleanCmd.Flags().String("validate", "", "Specify validate parameter")
-	cleanCmd.Flags().String("mutating", "", "Specify mutating parameter")
-
-	rootCmd.AddCommand(cleanCmd)
 	if err := rootCmd.Execute(); err != nil {
 		logger.Fatal(err.Error())
 	}
diff --git a/docs/README-zh_CN.md b/docs/README-zh_CN.md
index 3c27e7a97..9fbb3007e 100644
--- a/docs/README-zh_CN.md
+++ b/docs/README-zh_CN.md
@@ -24,6 +24,20 @@ Spiderpool 是一个 kubernetes 的 underlay 和 RDMA 网络解决方案，它
 [ipvlan CNI](https://github.com/containernetworking/plugins/tree/main/plugins/main/ipvlan) 和
 [SR-IOV CNI](https://github.com/k8snetworkplumbingwg/sriov-cni) 的功能，满足了各种网络需求，使得 underlay 网络方案可应用在**裸金属、虚拟机和公有云环境**中，可为网络 I/O 密集性、低延时应用带来优秀的网络性能，包括**存储、中间件、AI 等应用**。详细的文档可参考[文档站](https://spidernet-io.github.io/spiderpool/)。
 
+## 稳定版本
+
+Spiderpool 社区将最新的三个 Spiderpool 补丁版本视为稳定版本，并定期维护。之前较旧的 Spiderpool 补丁版本将被视为 EOL（过时版本）。
+
+如需升级到新的补丁版本，请参阅 [Spiderpool 升级指南](./usage/install/upgrade-zh_CN.md)。
+
+下面列出的是当前维护的发布分支及其最新发布的补丁的发布说明：
+
+|                         发布分支                                      |                               发行说明                                              |
+| -------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
+| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.1)   |
+| [release-v0.8](https://github.com/spidernet-io/spiderpool/tree/release-v0.8) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.8.5)   |
+| [release-v0.7](https://github.com/spidernet-io/spiderpool/tree/release-v0.7) | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.7.3)   |
+
 ## Underlay CNI 的优势
 
 underlay CNI 主要指 macvlan、ipvlan、SR-IOV 等能够直接访问宿主机二层网络的 CNI 技术，它有如下优势：
diff --git a/docs/README.md b/docs/README.md
index 342bc4576..e5dbb78e1 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -28,6 +28,20 @@ Spiderpool is an underlay and RDMA network solution for the Kubernetes. It enhan
 [SR-IOV CNI](https://github.com/k8snetworkplumbingwg/sriov-cni), fulfills various networking needs, and supports to run on **bare metal, virtual machine, and public cloud environments**. Spiderpool delivers exceptional network performance, particularly benefiting network I/O-intensive and low-latency applications like **storage, middleware, and AI**.
 It could refer to [website](https://spidernet-io.github.io/spiderpool/) for more details.
 
+## Stable Releases
+
+The Spiderpool community maintains minor stable releases for the last three minor Spiderpool versions. Older Spiderpool stable versions from minor releases prior to that are considered EOL.
+
+For upgrades to new minor releases please consult the [Spiderpool Upgrade Guide](./usage/install/upgrade.md).
+
+Listed below are the release notes for the currently maintained release branches and their latest released patches:
+
+|                         release branches                             |                               Release Notes                                       |
+| -------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
+| [release-v0.9](https://github.com/spidernet-io/spiderpool/tree/release-v0.9)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.9.1)   |
+| [release-v0.8](https://github.com/spidernet-io/spiderpool/tree/release-v0.8)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.8.5)   |
+| [release-v0.7](https://github.com/spidernet-io/spiderpool/tree/release-v0.7)    | [Release Notes](https://github.com/spidernet-io/spiderpool/releases/tag/v0.7.3)   |
+
 ## The Advantages Of Underlay CNI
 
 The underlay CNI is mainly including macvlan, ipvlan, and SR-IOV, which cloud access the layer 2 network of the node. It has some advantages:
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 4ecb6be9d..69e81d99e 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -72,7 +72,6 @@ nav:
           - VWware vSphere: usage/install/cloud/get-started-vmware.md
           - OpenStack: usage/install/cloud/get-started-openstack.md
       - Upgrading: usage/install/upgrade.md
-      - Uninstalling: usage/install/uninstall.md
       - System requirements: usage/install/system-requirements.md
   - Concepts:
       - Architecture: concepts/arch.md
diff --git a/docs/usage/install/uninstall-zh_CN.md b/docs/usage/install/uninstall-zh_CN.md
deleted file mode 100644
index c198b4e1e..000000000
--- a/docs/usage/install/uninstall-zh_CN.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# 卸载指南
-
-[**English**](./uninstall.md) | **简体中文**
-
-本卸载指南适用于在 Kubernetes 上运行的 Spiderpool。如果您有任何疑问，请随时通过 [Spiderpool Community](../../README-zh_CN.md#_6) 联系我们。
-
-## 注意事项
-
-- 在执行卸载之前，请阅读完整的卸载指南以了解所有必要的步骤。
-
-## 卸载
-
-了解正在运行应用，理解卸载 Spiderpool 可能对其他相关组件（如中间件） 产生的影响，请确保完全了解风险后，才开始执行卸载步骤。
-
-1. 通过 `helm ls` 查询集群所安装的 Spiderpool
-
-     ```bash
-    helm ls -A | grep -i spiderpool
-    ```
-
-2. 通过 `helm  uninstall` 卸载 Spiderpool
-
-    ```bash
-    helm uninstall <spiderpool-name> --namespace <spiderpool-namespace>
-    ```
-
-    将 `<spiderpool-name>` 替换为要卸载的 Spiderpool 的名称，将 `<spiderpool-namespace>` 替换为 Spiderpool 所在的命名空间。
-
-### v0.10.0 以上版本
-
-在 v0.10.0 之后引入了自动清理 Spiderpool 资源的功能，它通过 `spiderpoolController.cleanup.enabled` 配置项来启用，该值默认为 `true`，您可以通过如下方式验证与 Spiderpool 相关的资源数量是否自动被清理。
-
-```bash
-kubectl get spidersubnets.spiderpool.spidernet.io -o name | wc -l 
-kubectl get spiderips.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderippools.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderreservedips.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderendpoints.spiderpool.spidernet.io -o name | wc -l
-kubectl get spidercoordinators.spiderpool.spidernet.io -o name | wc -l
-```
-
-### v0.10.0 以下版本
-
-在低于 v0.10.0 的版本中，由于 Spiderpool 的某些 CR 资源中存在 [finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) ，导致 `helm uninstall` 命令无法清理干净，您需要手动清理。可获取如下清理脚本来完成清理，以确保下次部署 Spiderpool 时不会出现意外错误。
-
-```bash
-wget https://raw.githubusercontent.com/spidernet-io/spiderpool/main/tools/scripts/cleanCRD.sh
-chmod +x cleanCRD.sh && ./cleanCRD.sh
-```
diff --git a/docs/usage/install/uninstall.md b/docs/usage/install/uninstall.md
deleted file mode 100644
index 3a69d9c4d..000000000
--- a/docs/usage/install/uninstall.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Uninstall Guide
-
-**English** | [**简体中文**](./uninstall-zh_CN.md)
-
-This uninstall guide is intended for Spiderpool running on Kubernetes. If you have questions, feel free to ping us on [Spiderpool Community](../../README.md#Community).
-
-## Warning
-
-- Read the full uninstall guide to understand all the necessary steps before performing them.
-
-## Uninstall Spiderpool
-
-Understand the running application and understand the impact that uninstalling Spiderpool may have on other related components (such as middleware). Please make sure you fully understand the risks before starting the uninstallation steps.
-
-1. Query the Spiderpool installed in the cluster through `helm ls`
-
-      ```bash
-     helm ls -A | grep -i spiderpool
-     ```
-
-2. Uninstall Spiderpool via `helm uninstall`
-
-     ```bash
-     helm uninstall <spiderpool-name> --namespace <spiderpool-namespace>
-     ```
-
-     Replace `<spiderpool-name>` with the name of the Spiderpool you want to uninstall and `<spiderpool-namespace>` with the namespace of the Spiderpool.
-
-### Above v0.10.0
-
-The function of automatically cleaning Spiderpool resources was introduced after v0.10.0. It is enabled through the `spiderpoolController.cleanup.enabled` configuration item. The value defaults to `true`. You can verify whether the number of resources related to Spiderpool is automatically cleared as follows.
-
-```bash
-kubectl get spidersubnets.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderips.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderippools.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderreservedips.spiderpool.spidernet.io -o name | wc -l
-kubectl get spiderendpoints.spiderpool.spidernet.io -o name | wc -l
-kubectl get spidercoordinators.spiderpool.spidernet.io -o name | wc -l
-```
-
-### Below v0.10.0
-
-In versions lower than v0.10.0, Some CR resources having [finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) prevents complete cleanup via `helm uninstall`. You can download the cleaning script below to perform the necessary cleanup and avoid any unexpected errors during future deployments of Spiderpool.
-
-```bash
-wget https://raw.githubusercontent.com/spidernet-io/spiderpool/main/tools/scripts/cleanCRD.sh
-chmod +x cleanCRD.sh && ./cleanCRD.sh
-```
diff --git a/docs/usage/install/upgrade-zh_CN.md b/docs/usage/install/upgrade-zh_CN.md
index 06c7a69c0..a0eec4c8e 100644
--- a/docs/usage/install/upgrade-zh_CN.md
+++ b/docs/usage/install/upgrade-zh_CN.md
@@ -35,9 +35,13 @@
     helm repo update spiderpool
     ```
 
-3. 删除 spiderpool-init Pod（可选）
+3. 删除 spiderpool-init Pod
 
-    `spiderpool-init` Pod 会帮助初始化环境信息，每次运行完毕后其处于 `complete` 状态。 如果您的环境有 `spiderpool-init` Pod，在升级之前通过 `kubectl delete` 将其删除，否则在 helm upgrade 时，会失败。
+    `spiderpool-init` Pod 会帮助初始化环境信息，每次运行完毕后其处于 `complete` 状态。在 `helm upgrade` 时，由于 `spiderpool-init` 本质是一个 Pod ，当你要升级到的版本发生了资源变更，将会 Patch 失败，介于可能并不清楚具体的变更，建议升级前手动删除一下 spiderpool-init Pod，避免出现 helm upgrade 失败的情况。
+
+    ```bash
+    Error: UPGRADE FAILED: cannot patch "spiderpool-init" with kind Pod: Pod "spiderpool-init" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,`spec.tolerations` (only additions to existing tolerations),`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
+    ```
 
 4. helm upgrade 升级
 
@@ -137,6 +141,10 @@ kubectl patch sp ${auto-pool} --type merge --patch '{"metadata": {"labels": {"ip
 ~# ls | grep '\.yaml$' | xargs -I {} kubectl apply -f {}
 ```
 
+### 低于 0.7.3（包含 0.7.3）升级到更高版本的注意事项
+
+在 0.7.3 以下版本中，Spiderpool 会启用一组 DaemonSet: `spiderpool-multus` 来管理 Multus 相关配置。在更高版本中，弃用了该 DaemonSet，并且将 Multus 的配置移到了 `spiderpool-agent` 中纳管，同时新增了`卸载时自动清理 Muluts 配置`的功能，它默认是启用的。在升级时通过 `--set multus.multusCNI.uninstall=false` 禁用它，避免在升级阶段 CNI 配置文件、CRD 等被删除，从而导致 Pod 创建失败。
+
 ### 低于 0.9.0 (不包含 0.9.0) 升级到最高版本的注意事项
 
 由于在 0.9.0 的版本中，我们给 [SpiderCoordinator CRD](./../../reference/crd-spidercoordinator.md) 补充了 `txQueueLen` 字段，但由于执行升级时 Helm 不支持升级或删除 CRD，因此在升级前需要你手动更新一下 CRD。(建议越过 0.9.0 直接升级至 0.9.1 版本)
@@ -144,3 +152,11 @@ kubectl patch sp ${auto-pool} --type merge --patch '{"metadata": {"labels": {"ip
 ### 更多版本升级的注意事项
 
 *TODO.*
+
+## FAQ
+
+由于您对 Spiderpool 高可用的要求，您在安装时可能会通过 `--set spiderpoolController.replicas=5` 设置 spiderpool-controller Pod 多副本，spiderpool-controller 的 Pod 会默认占用节点的一些端口地址，默认端口占用参考[系统配置](./system-requirements-zh_CN.md)，如果您的副本数与节点数刚好就相同，那么在升级时 Pod 将会因为节点无可用端口而启动失败，您可以参考如下两种方式进行修改。
+
+1. 执行升级命令时，您可以通过附加 helm 参数 `--set spiderpoolController.httpPort` 对端口更改，可以通过 [helm Values.yaml](https://github.com/spidernet-io/spiderpool/blob/main/charts/spiderpool/values.yaml) 和[系统配置](./system-requirements-zh_CN.md) 查看需要修改的端口。
+
+2. spiderpool-controller 的控制器类型是 `Deployment`, 您可以通过缩减副本数，让 Pod 正常启动后，再恢复副本数。
diff --git a/docs/usage/install/upgrade.md b/docs/usage/install/upgrade.md
index c22f7f17d..f36909548 100644
--- a/docs/usage/install/upgrade.md
+++ b/docs/usage/install/upgrade.md
@@ -35,9 +35,13 @@ It is recommended to always upgrade to the latest and maintained patch version o
     helm repo update spiderpool
     ```
 
-3. Remove spiderpool-init Pod (optional)
+3. Remove spiderpool-init Pod
 
-    `spiderpool-init` Pod will help initialize environment information, and it will be in `complete` state after each run. If your environment has a `spiderpool-init` Pod, delete it through `kubectl delete` before upgrading, otherwise it will fail during helm upgrade.
+    `spiderpool-init` Pod will help initialize environment information, and it will be in `complete` state after each run. During `helm upgrade`, since `spiderpool-init` is essentially a Pod, patching some resources will fail. So delete it via `kubectl delete spiderpool-init` before upgrading.
+
+    ```bash
+    Error: UPGRADE FAILED: cannot patch "spiderpool-init" with kind Pod: Pod "spiderpool-init" is invalid: spec: Forbidden: pod updates may not change fields other than `spec.containers[*].image`,`spec.initContainers[*].image`,`spec.activeDeadlineSeconds`,`spec.tolerations` (only additions to existing tolerations),`spec.terminationGracePeriodSeconds` (allow it to be set to 1 if it was previously negative)
+    ```
 
 4. Upgrade via `helm upgrade`
 
@@ -137,6 +141,10 @@ In versions higher than 0.5.0, the [SpiderMultusConfig](../spider-multus-config.
 ~# ls | grep '\.yaml$' | xargs -I {} kubectl apply -f {}
 ```
 
+### Upgrading from a version below 0.7.3 (Includes 0.7.3) to a higher version
+
+In versions below 0.7.3, Spiderpool will enable a set of DaemonSet: `spiderpool-multus` to manage Multus related configurations. In later versions, the DaemonSet was deprecated, and the Muluts configuration was moved to `spiderpool-agent` for management. At the same time, the function of `automatically cleaning up the Muluts configuration during uninstallation` was added, which is enabled by default. Disable it by `--set multus.multusCNI.uninstall=false` when upgrading to avoid CNI configuration files, CRDs, etc. being deleted during the upgrade phase, causing Pod creation to fail.
+
 ### Upgrading from a version below 0.9.0 (Excludes 0.9.0) to a higher version
 
 Due to the addition of the `txQueueLen` field to the [SpiderCoordinator CRD](./../../reference/crd-spidercoordinator.md) in version 0.9.0, you need to manually update the CRD before upgrading as Helm does not support upgrading or deleting CRDs during the upgrade process.(We suggest skipping version 0.9.0 and upgrading directly to version 0.9.1)
@@ -144,3 +152,11 @@ Due to the addition of the `txQueueLen` field to the [SpiderCoordinator CRD](./.
 ### More notes on version upgrades
 
 *TODO.*
+
+## FAQ
+
+Due to your high availability requirements for Spiderpool, you may set multiple replicas of the spiderpool-controller Pod through `--set spiderpoolController.replicas=5` during installation. The Pod of spiderpool-controller will occupy some port addresses of the node by default. The default port Please refer to [System Configuration](./system-requirements-zh_CN.md) for occupancy. If your number of replicas is exactly the same as the number of nodes, then the Pod will fail to start because the node has no available ports during the upgrade. You can refer to the following Modifications can be made in two ways.
+
+1. When executing the upgrade command, you can change the port by appending the helm parameter `--set spiderpoolController.httpPort`, and you can change the port through [helm Values.yaml](https://github.com/spidernet-io/spiderpool/blob/main/charts/spiderpool/values.yaml) and [System Configuration](./system-requirements-zh_CN.md) to check the ports that need to be modified.
+
+2. The type of spiderpool-controller is `Deployment`. You can reduce the number of replicas and restore the number of replicas after the Pod starts normally.
diff --git a/docs/usage/readme-zh_CN.md b/docs/usage/readme-zh_CN.md
index 8e5778e6a..431225c9b 100644
--- a/docs/usage/readme-zh_CN.md
+++ b/docs/usage/readme-zh_CN.md
@@ -52,11 +52,22 @@
 
 ## 卸载 Spiderpool
 
-可参考 [卸载](./install/uninstall-zh_CN.md)
+1. 您可以通过以下方式卸载 Spiderpool ：
+
+    ```bash
+    helm uninstall spiderpool -n kube-system
+    ```
+
+2. Spiderpool 的某些 CR 资源中存在 [finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) ，导致 `helm uninstall` 命令无法清理干净。 可获取如下清理脚本来完成清理，以确保下次部署 Spiderpool 时不会出现意外错误。
+
+    ```bash
+    wget https://raw.githubusercontent.com/spidernet-io/spiderpool/main/tools/scripts/cleanCRD.sh
+    chmod +x cleanCRD.sh && ./cleanCRD.sh
+    ```
 
 ## 升级 Spiderpool
 
-可参考 [升级](./install/upgrade-zh_CN.md)
+可参考 [升级](./install/upgrade.md)
 
 ## 使用 Spiderpool
 
diff --git a/docs/usage/readme.md b/docs/usage/readme.md
index 222d22882..10eede67c 100644
--- a/docs/usage/readme.md
+++ b/docs/usage/readme.md
@@ -54,7 +54,18 @@ During the Spiderpool installation, you can choose a method for TLS certificate
 
 ## Uninstall Spiderpool
 
-For instructions on how to uninstall Spiderpool, please refer to the [uninstall guide](./install/uninstall.md).
+1. To uninstall Spiderpool, you can use the following methods:
+
+    ```bash
+    helm uninstall spiderpool -n kube-system
+    ```
+
+2. Some CR resources having [finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) prevents complete cleanup via `helm uninstall`. You can download the cleaning script below to perform the necessary cleanup and avoid any unexpected errors during future deployments of Spiderpool.
+
+    ```bash
+    wget https://raw.githubusercontent.com/spidernet-io/spiderpool/main/tools/scripts/cleanCRD.sh
+    chmod +x cleanCRD.sh && ./cleanCRD.sh
+    ```
 
 ## Upgrade Spiderpool
 
diff --git a/go.mod b/go.mod
index f1cfd9925..5bc65abb7 100644
--- a/go.mod
+++ b/go.mod
@@ -46,17 +46,17 @@ require (
 	go.uber.org/atomic v1.10.0
 	go.uber.org/multierr v1.11.0
 	go.uber.org/zap v1.25.0
-	golang.org/x/net v0.23.0
+	golang.org/x/net v0.21.0
 	golang.org/x/sync v0.6.0 // indirect
-	golang.org/x/sys v0.18.0
+	golang.org/x/sys v0.17.0
 	golang.org/x/tools v0.18.0
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1
 	gopkg.in/yaml.v2 v2.4.0
 	gopkg.in/yaml.v3 v3.0.1
-	k8s.io/api v0.29.4
-	k8s.io/apiextensions-apiserver v0.29.4
+	k8s.io/api v0.29.1
+	k8s.io/apiextensions-apiserver v0.29.1
 	k8s.io/apimachinery v0.30.0-beta.0
-	k8s.io/client-go v0.29.4
+	k8s.io/client-go v0.29.1
 	k8s.io/code-generator v0.30.0-beta.0
 	k8s.io/kubernetes v1.29.0
 	k8s.io/utils v0.0.0-20230726121419-3b25d923346b
@@ -73,8 +73,6 @@ require (
 
 require github.com/google/go-cmp v0.6.0 // indirect
 
-require github.com/hashicorp/go-multierror v1.1.1
-
 require (
 	github.com/Masterminds/goutils v1.1.1 // indirect
 	github.com/Masterminds/semver/v3 v3.2.0 // indirect
@@ -109,7 +107,6 @@ require (
 	github.com/google/uuid v1.3.0 // indirect
 	github.com/gorilla/handlers v1.5.1 // indirect
 	github.com/grafana/pyroscope-go/godeltaprof v0.1.6 // indirect
-	github.com/hashicorp/errwrap v1.1.0 // indirect
 	github.com/hashicorp/hcl v1.0.0 // indirect
 	github.com/huandu/xstrings v1.3.3 // indirect
 	github.com/imdario/mergo v0.3.13 // indirect
@@ -167,11 +164,11 @@ require (
 	github.com/yusufpapurcu/wmi v1.2.3 // indirect
 	go.mongodb.org/mongo-driver v1.13.1 // indirect
 	go.uber.org/dig v1.17.0 // indirect
-	golang.org/x/crypto v0.21.0 // indirect
+	golang.org/x/crypto v0.19.0 // indirect
 	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
 	golang.org/x/mod v0.15.0 // indirect
 	golang.org/x/oauth2 v0.10.0 // indirect
-	golang.org/x/term v0.18.0 // indirect
+	golang.org/x/term v0.17.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
@@ -179,7 +176,7 @@ require (
 	google.golang.org/protobuf v1.33.0 // indirect
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/ini.v1 v1.67.0 // indirect
-	k8s.io/component-base v0.29.4 // indirect
+	k8s.io/component-base v0.29.1 // indirect
 	k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
 	k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
 	k8s.io/kubectl v0.26.3 // indirect
diff --git a/go.sum b/go.sum
index c6ec402d2..6066a3e70 100644
--- a/go.sum
+++ b/go.sum
@@ -307,11 +307,6 @@ github.com/grafana/pyroscope-go v1.1.1/go.mod h1:Mw26jU7jsL/KStNSGGuuVYdUq7Qghem
 github.com/grafana/pyroscope-go/godeltaprof v0.1.6 h1:nEdZ8louGAplSvIJi1HVp7kWvFvdiiYg3COLlTwJiFo=
 github.com/grafana/pyroscope-go/godeltaprof v0.1.6/go.mod h1:Tk376Nbldo4Cha9RgiU7ik8WKFkNpfds98aUzS8omLE=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
@@ -607,8 +602,8 @@ golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5y
 golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
-golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -696,8 +691,8 @@ golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
-golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -792,14 +787,14 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.9.1-0.20230616193735-e0c3b6e6ae3b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
-golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
-golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1033,22 +1028,22 @@ honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
 honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 k8s.io/api v0.23.2/go.mod h1:sYuDb3flCtRPI8ghn6qFrcK5ZBu2mhbElxRE95qpwlI=
 k8s.io/api v0.23.3/go.mod h1:w258XdGyvCmnBj/vGzQMj6kzdufJZVUwEM1U2fRJwSQ=
-k8s.io/api v0.29.4 h1:WEnF/XdxuCxdG3ayHNRR8yH3cI1B/llkWBma6bq4R3w=
-k8s.io/api v0.29.4/go.mod h1:DetSv0t4FBTcEpfA84NJV3g9a7+rSzlUHk5ADAYHUv0=
-k8s.io/apiextensions-apiserver v0.29.4 h1:M7hbuHU/ckbibR7yPbe6DyNWgTFKNmZDbdZKD8q1Smk=
-k8s.io/apiextensions-apiserver v0.29.4/go.mod h1:TTDC9fB+0kHY2rogf5hgBR03KBKCwED+GHUsXGpR7SM=
+k8s.io/api v0.29.1 h1:DAjwWX/9YT7NQD4INu49ROJuZAAAP/Ijki48GUPzxqw=
+k8s.io/api v0.29.1/go.mod h1:7Kl10vBRUXhnQQI8YR/R327zXC8eJ7887/+Ybta+RoQ=
+k8s.io/apiextensions-apiserver v0.29.1 h1:S9xOtyk9M3Sk1tIpQMu9wXHm5O2MX6Y1kIpPMimZBZw=
+k8s.io/apiextensions-apiserver v0.29.1/go.mod h1:zZECpujY5yTW58co8V2EQR4BD6A9pktVgHhvc0uLfeU=
 k8s.io/apimachinery v0.23.2/go.mod h1:zDqeV0AK62LbCI0CI7KbWCAYdLg+E+8UXJ0rIz5gmS8=
 k8s.io/apimachinery v0.23.3/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
 k8s.io/apimachinery v0.30.0-beta.0 h1:/gaNLWP5ynEG0ExJ+4w2YCj5/L4MU66RsWEAKciy0/g=
 k8s.io/apimachinery v0.30.0-beta.0/go.mod h1:wEJvNDlfxMRaMhyv38SIHIEC9hah/xuzqUUhxIyUv7Y=
 k8s.io/client-go v0.23.2/go.mod h1:k3YbsWg6GWdHF1THHTQP88X9RhB1DWPo3Dq7KfU/D1c=
-k8s.io/client-go v0.29.4 h1:79ytIedxVfyXV8rpH3jCBW0u+un0fxHDwX5F9K8dPR8=
-k8s.io/client-go v0.29.4/go.mod h1:kC1thZQ4zQWYwldsfI088BbK6RkxK+aF5ebV8y9Q4tk=
+k8s.io/client-go v0.29.1 h1:19B/+2NGEwnFLzt0uB5kNJnfTsbV8w6TgQRz9l7ti7A=
+k8s.io/client-go v0.29.1/go.mod h1:TDG/psL9hdet0TI9mGyHJSgRkW3H9JZk2dNEUS7bRks=
 k8s.io/code-generator v0.23.3/go.mod h1:S0Q1JVA+kSzTI1oUvbKAxZY/DYbA/ZUb4Uknog12ETk=
 k8s.io/code-generator v0.30.0-beta.0 h1:p+51J7CG4i6Cu/cyRrpXU7zT/XaHIHv7NK/mujr0gdY=
 k8s.io/code-generator v0.30.0-beta.0/go.mod h1:kvx3eylE/Y/Z2dj8ncw3CR/zjQ37ou9lc3A0Pt8xX54=
-k8s.io/component-base v0.29.4 h1:xeKzuuHI/1tjleu5jycDAcYbhAxeGHCQBZUY2eRIkOo=
-k8s.io/component-base v0.29.4/go.mod h1:pYjt+oEZP9gtmwSikwAJgfSBikqKX2gOqRat0QjmQt0=
+k8s.io/component-base v0.29.1 h1:MUimqJPCRnnHsskTTjKD+IC1EHBbRCVyi37IoFBrkYw=
+k8s.io/component-base v0.29.1/go.mod h1:fP9GFjxYrLERq1GcWWZAE3bqbNcDKDytn2srWuHTtKc=
 k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/gengo v0.0.0-20211129171323-c02415ce4185/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
 k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo=
diff --git a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/rbac.go b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/rbac.go
index 0be7a7328..7529bcbf0 100644
--- a/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/rbac.go
+++ b/pkg/k8s/apis/spiderpool.spidernet.io/v2beta1/rbac.go
@@ -19,7 +19,5 @@
 // +kubebuilder:rbac:groups=k8s.cni.cncf.io,resources=network-attachment-definitions,verbs=get;list;watch;create;update;patch;delete
 // +kubebuilder:rbac:groups=kubevirt.io,resources=virtualmachines,verbs=get;list
 // +kubebuilder:rbac:groups=kubevirt.io,resources=virtualmachineinstances,verbs=get;list
-// +kubebuilder:rbac:groups=admissionregistration.k8s.io,resources=mutatingwebhookconfigurations;validatingwebhookconfigurations,verbs=get;list;watch;create;update;patch;delete
-// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch;create;update;patch;delete
 
 package v2beta1
diff --git a/vendor/github.com/hashicorp/errwrap/LICENSE b/vendor/github.com/hashicorp/errwrap/LICENSE
deleted file mode 100644
index c33dcc7c9..000000000
--- a/vendor/github.com/hashicorp/errwrap/LICENSE
+++ /dev/null
@@ -1,354 +0,0 @@
-Mozilla Public License, version 2.0
-
-1. Definitions
-
-1.1. “Contributor”
-
-     means each individual or legal entity that creates, contributes to the
-     creation of, or owns Covered Software.
-
-1.2. “Contributor Version”
-
-     means the combination of the Contributions of others (if any) used by a
-     Contributor and that particular Contributor’s Contribution.
-
-1.3. “Contribution”
-
-     means Covered Software of a particular Contributor.
-
-1.4. “Covered Software”
-
-     means Source Code Form to which the initial Contributor has attached the
-     notice in Exhibit A, the Executable Form of such Source Code Form, and
-     Modifications of such Source Code Form, in each case including portions
-     thereof.
-
-1.5. “Incompatible With Secondary Licenses”
-     means
-
-     a. that the initial Contributor has attached the notice described in
-        Exhibit B to the Covered Software; or
-
-     b. that the Covered Software was made available under the terms of version
-        1.1 or earlier of the License, but not also under the terms of a
-        Secondary License.
-
-1.6. “Executable Form”
-
-     means any form of the work other than Source Code Form.
-
-1.7. “Larger Work”
-
-     means a work that combines Covered Software with other material, in a separate
-     file or files, that is not Covered Software.
-
-1.8. “License”
-
-     means this document.
-
-1.9. “Licensable”
-
-     means having the right to grant, to the maximum extent possible, whether at the
-     time of the initial grant or subsequently, any and all of the rights conveyed by
-     this License.
-
-1.10. “Modifications”
-
-     means any of the following:
-
-     a. any file in Source Code Form that results from an addition to, deletion
-        from, or modification of the contents of Covered Software; or
-
-     b. any new file in Source Code Form that contains any Covered Software.
-
-1.11. “Patent Claims” of a Contributor
-
-      means any patent claim(s), including without limitation, method, process,
-      and apparatus claims, in any patent Licensable by such Contributor that
-      would be infringed, but for the grant of the License, by the making,
-      using, selling, offering for sale, having made, import, or transfer of
-      either its Contributions or its Contributor Version.
-
-1.12. “Secondary License”
-
-      means either the GNU General Public License, Version 2.0, the GNU Lesser
-      General Public License, Version 2.1, the GNU Affero General Public
-      License, Version 3.0, or any later versions of those licenses.
-
-1.13. “Source Code Form”
-
-      means the form of the work preferred for making modifications.
-
-1.14. “You” (or “Your”)
-
-      means an individual or a legal entity exercising rights under this
-      License. For legal entities, “You” includes any entity that controls, is
-      controlled by, or is under common control with You. For purposes of this
-      definition, “control” means (a) the power, direct or indirect, to cause
-      the direction or management of such entity, whether by contract or
-      otherwise, or (b) ownership of more than fifty percent (50%) of the
-      outstanding shares or beneficial ownership of such entity.
-
-
-2. License Grants and Conditions
-
-2.1. Grants
-
-     Each Contributor hereby grants You a world-wide, royalty-free,
-     non-exclusive license:
-
-     a. under intellectual property rights (other than patent or trademark)
-        Licensable by such Contributor to use, reproduce, make available,
-        modify, display, perform, distribute, and otherwise exploit its
-        Contributions, either on an unmodified basis, with Modifications, or as
-        part of a Larger Work; and
-
-     b. under Patent Claims of such Contributor to make, use, sell, offer for
-        sale, have made, import, and otherwise transfer either its Contributions
-        or its Contributor Version.
-
-2.2. Effective Date
-
-     The licenses granted in Section 2.1 with respect to any Contribution become
-     effective for each Contribution on the date the Contributor first distributes
-     such Contribution.
-
-2.3. Limitations on Grant Scope
-
-     The licenses granted in this Section 2 are the only rights granted under this
-     License. No additional rights or licenses will be implied from the distribution
-     or licensing of Covered Software under this License. Notwithstanding Section
-     2.1(b) above, no patent license is granted by a Contributor:
-
-     a. for any code that a Contributor has removed from Covered Software; or
-
-     b. for infringements caused by: (i) Your and any other third party’s
-        modifications of Covered Software, or (ii) the combination of its
-        Contributions with other software (except as part of its Contributor
-        Version); or
-
-     c. under Patent Claims infringed by Covered Software in the absence of its
-        Contributions.
-
-     This License does not grant any rights in the trademarks, service marks, or
-     logos of any Contributor (except as may be necessary to comply with the
-     notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-     No Contributor makes additional grants as a result of Your choice to
-     distribute the Covered Software under a subsequent version of this License
-     (see Section 10.2) or under the terms of a Secondary License (if permitted
-     under the terms of Section 3.3).
-
-2.5. Representation
-
-     Each Contributor represents that the Contributor believes its Contributions
-     are its original creation(s) or it has sufficient rights to grant the
-     rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-     This License is not intended to limit any rights You have under applicable
-     copyright doctrines of fair use, fair dealing, or other equivalents.
-
-2.7. Conditions
-
-     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
-     Section 2.1.
-
-
-3. Responsibilities
-
-3.1. Distribution of Source Form
-
-     All distribution of Covered Software in Source Code Form, including any
-     Modifications that You create or to which You contribute, must be under the
-     terms of this License. You must inform recipients that the Source Code Form
-     of the Covered Software is governed by the terms of this License, and how
-     they can obtain a copy of this License. You may not attempt to alter or
-     restrict the recipients’ rights in the Source Code Form.
-
-3.2. Distribution of Executable Form
-
-     If You distribute Covered Software in Executable Form then:
-
-     a. such Covered Software must also be made available in Source Code Form,
-        as described in Section 3.1, and You must inform recipients of the
-        Executable Form how they can obtain a copy of such Source Code Form by
-        reasonable means in a timely manner, at a charge no more than the cost
-        of distribution to the recipient; and
-
-     b. You may distribute such Executable Form under the terms of this License,
-        or sublicense it under different terms, provided that the license for
-        the Executable Form does not attempt to limit or alter the recipients’
-        rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-     You may create and distribute a Larger Work under terms of Your choice,
-     provided that You also comply with the requirements of this License for the
-     Covered Software. If the Larger Work is a combination of Covered Software
-     with a work governed by one or more Secondary Licenses, and the Covered
-     Software is not Incompatible With Secondary Licenses, this License permits
-     You to additionally distribute such Covered Software under the terms of
-     such Secondary License(s), so that the recipient of the Larger Work may, at
-     their option, further distribute the Covered Software under the terms of
-     either this License or such Secondary License(s).
-
-3.4. Notices
-
-     You may not remove or alter the substance of any license notices (including
-     copyright notices, patent notices, disclaimers of warranty, or limitations
-     of liability) contained within the Source Code Form of the Covered
-     Software, except that You may alter any license notices to the extent
-     required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-     You may choose to offer, and to charge a fee for, warranty, support,
-     indemnity or liability obligations to one or more recipients of Covered
-     Software. However, You may do so only on Your own behalf, and not on behalf
-     of any Contributor. You must make it absolutely clear that any such
-     warranty, support, indemnity, or liability obligation is offered by You
-     alone, and You hereby agree to indemnify every Contributor for any
-     liability incurred by such Contributor as a result of warranty, support,
-     indemnity or liability terms You offer. You may include additional
-     disclaimers of warranty and limitations of liability specific to any
-     jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
-
-   If it is impossible for You to comply with any of the terms of this License
-   with respect to some or all of the Covered Software due to statute, judicial
-   order, or regulation then You must: (a) comply with the terms of this License
-   to the maximum extent possible; and (b) describe the limitations and the code
-   they affect. Such description must be placed in a text file included with all
-   distributions of the Covered Software under this License. Except to the
-   extent prohibited by statute or regulation, such description must be
-   sufficiently detailed for a recipient of ordinary skill to be able to
-   understand it.
-
-5. Termination
-
-5.1. The rights granted under this License will terminate automatically if You
-     fail to comply with any of its terms. However, if You become compliant,
-     then the rights granted under this License from a particular Contributor
-     are reinstated (a) provisionally, unless and until such Contributor
-     explicitly and finally terminates Your grants, and (b) on an ongoing basis,
-     if such Contributor fails to notify You of the non-compliance by some
-     reasonable means prior to 60 days after You have come back into compliance.
-     Moreover, Your grants from a particular Contributor are reinstated on an
-     ongoing basis if such Contributor notifies You of the non-compliance by
-     some reasonable means, this is the first time You have received notice of
-     non-compliance with this License from such Contributor, and You become
-     compliant prior to 30 days after Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-     infringement claim (excluding declaratory judgment actions, counter-claims,
-     and cross-claims) alleging that a Contributor Version directly or
-     indirectly infringes any patent, then the rights granted to You by any and
-     all Contributors for the Covered Software under Section 2.1 of this License
-     shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
-     license agreements (excluding distributors and resellers) which have been
-     validly granted by You or Your distributors under this License prior to
-     termination shall survive termination.
-
-6. Disclaimer of Warranty
-
-   Covered Software is provided under this License on an “as is” basis, without
-   warranty of any kind, either expressed, implied, or statutory, including,
-   without limitation, warranties that the Covered Software is free of defects,
-   merchantable, fit for a particular purpose or non-infringing. The entire
-   risk as to the quality and performance of the Covered Software is with You.
-   Should any Covered Software prove defective in any respect, You (not any
-   Contributor) assume the cost of any necessary servicing, repair, or
-   correction. This disclaimer of warranty constitutes an essential part of this
-   License. No use of  any Covered Software is authorized under this License
-   except under this disclaimer.
-
-7. Limitation of Liability
-
-   Under no circumstances and under no legal theory, whether tort (including
-   negligence), contract, or otherwise, shall any Contributor, or anyone who
-   distributes Covered Software as permitted above, be liable to You for any
-   direct, indirect, special, incidental, or consequential damages of any
-   character including, without limitation, damages for lost profits, loss of
-   goodwill, work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses, even if such party shall have been
-   informed of the possibility of such damages. This limitation of liability
-   shall not apply to liability for death or personal injury resulting from such
-   party’s negligence to the extent applicable law prohibits such limitation.
-   Some jurisdictions do not allow the exclusion or limitation of incidental or
-   consequential damages, so this exclusion and limitation may not apply to You.
-
-8. Litigation
-
-   Any litigation relating to this License may be brought only in the courts of
-   a jurisdiction where the defendant maintains its principal place of business
-   and such litigation shall be governed by laws of that jurisdiction, without
-   reference to its conflict-of-law provisions. Nothing in this Section shall
-   prevent a party’s ability to bring cross-claims or counter-claims.
-
-9. Miscellaneous
-
-   This License represents the complete agreement concerning the subject matter
-   hereof. If any provision of this License is held to be unenforceable, such
-   provision shall be reformed only to the extent necessary to make it
-   enforceable. Any law or regulation which provides that the language of a
-   contract shall be construed against the drafter shall not be used to construe
-   this License against a Contributor.
-
-
-10. Versions of the License
-
-10.1. New Versions
-
-      Mozilla Foundation is the license steward. Except as provided in Section
-      10.3, no one other than the license steward has the right to modify or
-      publish new versions of this License. Each version will be given a
-      distinguishing version number.
-
-10.2. Effect of New Versions
-
-      You may distribute the Covered Software under the terms of the version of
-      the License under which You originally received the Covered Software, or
-      under the terms of any subsequent version published by the license
-      steward.
-
-10.3. Modified Versions
-
-      If you create software not governed by this License, and you want to
-      create a new license for such software, you may create and use a modified
-      version of this License if you rename the license and remove any
-      references to the name of the license steward (except to note that such
-      modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
-      If You choose to distribute Source Code Form that is Incompatible With
-      Secondary Licenses under the terms of this version of the License, the
-      notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
-
-      This Source Code Form is subject to the
-      terms of the Mozilla Public License, v.
-      2.0. If a copy of the MPL was not
-      distributed with this file, You can
-      obtain one at
-      http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular file, then
-You may include the notice in a location (such as a LICENSE file in a relevant
-directory) where a recipient would be likely to look for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - “Incompatible With Secondary Licenses” Notice
-
-      This Source Code Form is “Incompatible
-      With Secondary Licenses”, as defined by
-      the Mozilla Public License, v. 2.0.
-
diff --git a/vendor/github.com/hashicorp/errwrap/README.md b/vendor/github.com/hashicorp/errwrap/README.md
deleted file mode 100644
index 444df08f8..000000000
--- a/vendor/github.com/hashicorp/errwrap/README.md
+++ /dev/null
@@ -1,89 +0,0 @@
-# errwrap
-
-`errwrap` is a package for Go that formalizes the pattern of wrapping errors
-and checking if an error contains another error.
-
-There is a common pattern in Go of taking a returned `error` value and
-then wrapping it (such as with `fmt.Errorf`) before returning it. The problem
-with this pattern is that you completely lose the original `error` structure.
-
-Arguably the _correct_ approach is that you should make a custom structure
-implementing the `error` interface, and have the original error as a field
-on that structure, such [as this example](http://golang.org/pkg/os/#PathError).
-This is a good approach, but you have to know the entire chain of possible
-rewrapping that happens, when you might just care about one.
-
-`errwrap` formalizes this pattern (it doesn't matter what approach you use
-above) by giving a single interface for wrapping errors, checking if a specific
-error is wrapped, and extracting that error.
-
-## Installation and Docs
-
-Install using `go get github.com/hashicorp/errwrap`.
-
-Full documentation is available at
-http://godoc.org/github.com/hashicorp/errwrap
-
-## Usage
-
-#### Basic Usage
-
-Below is a very basic example of its usage:
-
-```go
-// A function that always returns an error, but wraps it, like a real
-// function might.
-func tryOpen() error {
-	_, err := os.Open("/i/dont/exist")
-	if err != nil {
-		return errwrap.Wrapf("Doesn't exist: {{err}}", err)
-	}
-
-	return nil
-}
-
-func main() {
-	err := tryOpen()
-
-	// We can use the Contains helpers to check if an error contains
-	// another error. It is safe to do this with a nil error, or with
-	// an error that doesn't even use the errwrap package.
-	if errwrap.Contains(err, "does not exist") {
-		// Do something
-	}
-	if errwrap.ContainsType(err, new(os.PathError)) {
-		// Do something
-	}
-
-	// Or we can use the associated `Get` functions to just extract
-	// a specific error. This would return nil if that specific error doesn't
-	// exist.
-	perr := errwrap.GetType(err, new(os.PathError))
-}
-```
-
-#### Custom Types
-
-If you're already making custom types that properly wrap errors, then
-you can get all the functionality of `errwraps.Contains` and such by
-implementing the `Wrapper` interface with just one function. Example:
-
-```go
-type AppError {
-  Code ErrorCode
-  Err  error
-}
-
-func (e *AppError) WrappedErrors() []error {
-  return []error{e.Err}
-}
-```
-
-Now this works:
-
-```go
-err := &AppError{Err: fmt.Errorf("an error")}
-if errwrap.ContainsType(err, fmt.Errorf("")) {
-	// This will work!
-}
-```
diff --git a/vendor/github.com/hashicorp/errwrap/errwrap.go b/vendor/github.com/hashicorp/errwrap/errwrap.go
deleted file mode 100644
index 44e368e56..000000000
--- a/vendor/github.com/hashicorp/errwrap/errwrap.go
+++ /dev/null
@@ -1,178 +0,0 @@
-// Package errwrap implements methods to formalize error wrapping in Go.
-//
-// All of the top-level functions that take an `error` are built to be able
-// to take any error, not just wrapped errors. This allows you to use errwrap
-// without having to type-check and type-cast everywhere.
-package errwrap
-
-import (
-	"errors"
-	"reflect"
-	"strings"
-)
-
-// WalkFunc is the callback called for Walk.
-type WalkFunc func(error)
-
-// Wrapper is an interface that can be implemented by custom types to
-// have all the Contains, Get, etc. functions in errwrap work.
-//
-// When Walk reaches a Wrapper, it will call the callback for every
-// wrapped error in addition to the wrapper itself. Since all the top-level
-// functions in errwrap use Walk, this means that all those functions work
-// with your custom type.
-type Wrapper interface {
-	WrappedErrors() []error
-}
-
-// Wrap defines that outer wraps inner, returning an error type that
-// can be cleanly used with the other methods in this package, such as
-// Contains, GetAll, etc.
-//
-// This function won't modify the error message at all (the outer message
-// will be used).
-func Wrap(outer, inner error) error {
-	return &wrappedError{
-		Outer: outer,
-		Inner: inner,
-	}
-}
-
-// Wrapf wraps an error with a formatting message. This is similar to using
-// `fmt.Errorf` to wrap an error. If you're using `fmt.Errorf` to wrap
-// errors, you should replace it with this.
-//
-// format is the format of the error message. The string '{{err}}' will
-// be replaced with the original error message.
-//
-// Deprecated: Use fmt.Errorf()
-func Wrapf(format string, err error) error {
-	outerMsg := "<nil>"
-	if err != nil {
-		outerMsg = err.Error()
-	}
-
-	outer := errors.New(strings.Replace(
-		format, "{{err}}", outerMsg, -1))
-
-	return Wrap(outer, err)
-}
-
-// Contains checks if the given error contains an error with the
-// message msg. If err is not a wrapped error, this will always return
-// false unless the error itself happens to match this msg.
-func Contains(err error, msg string) bool {
-	return len(GetAll(err, msg)) > 0
-}
-
-// ContainsType checks if the given error contains an error with
-// the same concrete type as v. If err is not a wrapped error, this will
-// check the err itself.
-func ContainsType(err error, v interface{}) bool {
-	return len(GetAllType(err, v)) > 0
-}
-
-// Get is the same as GetAll but returns the deepest matching error.
-func Get(err error, msg string) error {
-	es := GetAll(err, msg)
-	if len(es) > 0 {
-		return es[len(es)-1]
-	}
-
-	return nil
-}
-
-// GetType is the same as GetAllType but returns the deepest matching error.
-func GetType(err error, v interface{}) error {
-	es := GetAllType(err, v)
-	if len(es) > 0 {
-		return es[len(es)-1]
-	}
-
-	return nil
-}
-
-// GetAll gets all the errors that might be wrapped in err with the
-// given message. The order of the errors is such that the outermost
-// matching error (the most recent wrap) is index zero, and so on.
-func GetAll(err error, msg string) []error {
-	var result []error
-
-	Walk(err, func(err error) {
-		if err.Error() == msg {
-			result = append(result, err)
-		}
-	})
-
-	return result
-}
-
-// GetAllType gets all the errors that are the same type as v.
-//
-// The order of the return value is the same as described in GetAll.
-func GetAllType(err error, v interface{}) []error {
-	var result []error
-
-	var search string
-	if v != nil {
-		search = reflect.TypeOf(v).String()
-	}
-	Walk(err, func(err error) {
-		var needle string
-		if err != nil {
-			needle = reflect.TypeOf(err).String()
-		}
-
-		if needle == search {
-			result = append(result, err)
-		}
-	})
-
-	return result
-}
-
-// Walk walks all the wrapped errors in err and calls the callback. If
-// err isn't a wrapped error, this will be called once for err. If err
-// is a wrapped error, the callback will be called for both the wrapper
-// that implements error as well as the wrapped error itself.
-func Walk(err error, cb WalkFunc) {
-	if err == nil {
-		return
-	}
-
-	switch e := err.(type) {
-	case *wrappedError:
-		cb(e.Outer)
-		Walk(e.Inner, cb)
-	case Wrapper:
-		cb(err)
-
-		for _, err := range e.WrappedErrors() {
-			Walk(err, cb)
-		}
-	case interface{ Unwrap() error }:
-		cb(err)
-		Walk(e.Unwrap(), cb)
-	default:
-		cb(err)
-	}
-}
-
-// wrappedError is an implementation of error that has both the
-// outer and inner errors.
-type wrappedError struct {
-	Outer error
-	Inner error
-}
-
-func (w *wrappedError) Error() string {
-	return w.Outer.Error()
-}
-
-func (w *wrappedError) WrappedErrors() []error {
-	return []error{w.Outer, w.Inner}
-}
-
-func (w *wrappedError) Unwrap() error {
-	return w.Inner
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/LICENSE b/vendor/github.com/hashicorp/go-multierror/LICENSE
deleted file mode 100644
index 82b4de97c..000000000
--- a/vendor/github.com/hashicorp/go-multierror/LICENSE
+++ /dev/null
@@ -1,353 +0,0 @@
-Mozilla Public License, version 2.0
-
-1. Definitions
-
-1.1. “Contributor”
-
-     means each individual or legal entity that creates, contributes to the
-     creation of, or owns Covered Software.
-
-1.2. “Contributor Version”
-
-     means the combination of the Contributions of others (if any) used by a
-     Contributor and that particular Contributor’s Contribution.
-
-1.3. “Contribution”
-
-     means Covered Software of a particular Contributor.
-
-1.4. “Covered Software”
-
-     means Source Code Form to which the initial Contributor has attached the
-     notice in Exhibit A, the Executable Form of such Source Code Form, and
-     Modifications of such Source Code Form, in each case including portions
-     thereof.
-
-1.5. “Incompatible With Secondary Licenses”
-     means
-
-     a. that the initial Contributor has attached the notice described in
-        Exhibit B to the Covered Software; or
-
-     b. that the Covered Software was made available under the terms of version
-        1.1 or earlier of the License, but not also under the terms of a
-        Secondary License.
-
-1.6. “Executable Form”
-
-     means any form of the work other than Source Code Form.
-
-1.7. “Larger Work”
-
-     means a work that combines Covered Software with other material, in a separate
-     file or files, that is not Covered Software.
-
-1.8. “License”
-
-     means this document.
-
-1.9. “Licensable”
-
-     means having the right to grant, to the maximum extent possible, whether at the
-     time of the initial grant or subsequently, any and all of the rights conveyed by
-     this License.
-
-1.10. “Modifications”
-
-     means any of the following:
-
-     a. any file in Source Code Form that results from an addition to, deletion
-        from, or modification of the contents of Covered Software; or
-
-     b. any new file in Source Code Form that contains any Covered Software.
-
-1.11. “Patent Claims” of a Contributor
-
-      means any patent claim(s), including without limitation, method, process,
-      and apparatus claims, in any patent Licensable by such Contributor that
-      would be infringed, but for the grant of the License, by the making,
-      using, selling, offering for sale, having made, import, or transfer of
-      either its Contributions or its Contributor Version.
-
-1.12. “Secondary License”
-
-      means either the GNU General Public License, Version 2.0, the GNU Lesser
-      General Public License, Version 2.1, the GNU Affero General Public
-      License, Version 3.0, or any later versions of those licenses.
-
-1.13. “Source Code Form”
-
-      means the form of the work preferred for making modifications.
-
-1.14. “You” (or “Your”)
-
-      means an individual or a legal entity exercising rights under this
-      License. For legal entities, “You” includes any entity that controls, is
-      controlled by, or is under common control with You. For purposes of this
-      definition, “control” means (a) the power, direct or indirect, to cause
-      the direction or management of such entity, whether by contract or
-      otherwise, or (b) ownership of more than fifty percent (50%) of the
-      outstanding shares or beneficial ownership of such entity.
-
-
-2. License Grants and Conditions
-
-2.1. Grants
-
-     Each Contributor hereby grants You a world-wide, royalty-free,
-     non-exclusive license:
-
-     a. under intellectual property rights (other than patent or trademark)
-        Licensable by such Contributor to use, reproduce, make available,
-        modify, display, perform, distribute, and otherwise exploit its
-        Contributions, either on an unmodified basis, with Modifications, or as
-        part of a Larger Work; and
-
-     b. under Patent Claims of such Contributor to make, use, sell, offer for
-        sale, have made, import, and otherwise transfer either its Contributions
-        or its Contributor Version.
-
-2.2. Effective Date
-
-     The licenses granted in Section 2.1 with respect to any Contribution become
-     effective for each Contribution on the date the Contributor first distributes
-     such Contribution.
-
-2.3. Limitations on Grant Scope
-
-     The licenses granted in this Section 2 are the only rights granted under this
-     License. No additional rights or licenses will be implied from the distribution
-     or licensing of Covered Software under this License. Notwithstanding Section
-     2.1(b) above, no patent license is granted by a Contributor:
-
-     a. for any code that a Contributor has removed from Covered Software; or
-
-     b. for infringements caused by: (i) Your and any other third party’s
-        modifications of Covered Software, or (ii) the combination of its
-        Contributions with other software (except as part of its Contributor
-        Version); or
-
-     c. under Patent Claims infringed by Covered Software in the absence of its
-        Contributions.
-
-     This License does not grant any rights in the trademarks, service marks, or
-     logos of any Contributor (except as may be necessary to comply with the
-     notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-     No Contributor makes additional grants as a result of Your choice to
-     distribute the Covered Software under a subsequent version of this License
-     (see Section 10.2) or under the terms of a Secondary License (if permitted
-     under the terms of Section 3.3).
-
-2.5. Representation
-
-     Each Contributor represents that the Contributor believes its Contributions
-     are its original creation(s) or it has sufficient rights to grant the
-     rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-     This License is not intended to limit any rights You have under applicable
-     copyright doctrines of fair use, fair dealing, or other equivalents.
-
-2.7. Conditions
-
-     Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
-     Section 2.1.
-
-
-3. Responsibilities
-
-3.1. Distribution of Source Form
-
-     All distribution of Covered Software in Source Code Form, including any
-     Modifications that You create or to which You contribute, must be under the
-     terms of this License. You must inform recipients that the Source Code Form
-     of the Covered Software is governed by the terms of this License, and how
-     they can obtain a copy of this License. You may not attempt to alter or
-     restrict the recipients’ rights in the Source Code Form.
-
-3.2. Distribution of Executable Form
-
-     If You distribute Covered Software in Executable Form then:
-
-     a. such Covered Software must also be made available in Source Code Form,
-        as described in Section 3.1, and You must inform recipients of the
-        Executable Form how they can obtain a copy of such Source Code Form by
-        reasonable means in a timely manner, at a charge no more than the cost
-        of distribution to the recipient; and
-
-     b. You may distribute such Executable Form under the terms of this License,
-        or sublicense it under different terms, provided that the license for
-        the Executable Form does not attempt to limit or alter the recipients’
-        rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-     You may create and distribute a Larger Work under terms of Your choice,
-     provided that You also comply with the requirements of this License for the
-     Covered Software. If the Larger Work is a combination of Covered Software
-     with a work governed by one or more Secondary Licenses, and the Covered
-     Software is not Incompatible With Secondary Licenses, this License permits
-     You to additionally distribute such Covered Software under the terms of
-     such Secondary License(s), so that the recipient of the Larger Work may, at
-     their option, further distribute the Covered Software under the terms of
-     either this License or such Secondary License(s).
-
-3.4. Notices
-
-     You may not remove or alter the substance of any license notices (including
-     copyright notices, patent notices, disclaimers of warranty, or limitations
-     of liability) contained within the Source Code Form of the Covered
-     Software, except that You may alter any license notices to the extent
-     required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-     You may choose to offer, and to charge a fee for, warranty, support,
-     indemnity or liability obligations to one or more recipients of Covered
-     Software. However, You may do so only on Your own behalf, and not on behalf
-     of any Contributor. You must make it absolutely clear that any such
-     warranty, support, indemnity, or liability obligation is offered by You
-     alone, and You hereby agree to indemnify every Contributor for any
-     liability incurred by such Contributor as a result of warranty, support,
-     indemnity or liability terms You offer. You may include additional
-     disclaimers of warranty and limitations of liability specific to any
-     jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
-
-   If it is impossible for You to comply with any of the terms of this License
-   with respect to some or all of the Covered Software due to statute, judicial
-   order, or regulation then You must: (a) comply with the terms of this License
-   to the maximum extent possible; and (b) describe the limitations and the code
-   they affect. Such description must be placed in a text file included with all
-   distributions of the Covered Software under this License. Except to the
-   extent prohibited by statute or regulation, such description must be
-   sufficiently detailed for a recipient of ordinary skill to be able to
-   understand it.
-
-5. Termination
-
-5.1. The rights granted under this License will terminate automatically if You
-     fail to comply with any of its terms. However, if You become compliant,
-     then the rights granted under this License from a particular Contributor
-     are reinstated (a) provisionally, unless and until such Contributor
-     explicitly and finally terminates Your grants, and (b) on an ongoing basis,
-     if such Contributor fails to notify You of the non-compliance by some
-     reasonable means prior to 60 days after You have come back into compliance.
-     Moreover, Your grants from a particular Contributor are reinstated on an
-     ongoing basis if such Contributor notifies You of the non-compliance by
-     some reasonable means, this is the first time You have received notice of
-     non-compliance with this License from such Contributor, and You become
-     compliant prior to 30 days after Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-     infringement claim (excluding declaratory judgment actions, counter-claims,
-     and cross-claims) alleging that a Contributor Version directly or
-     indirectly infringes any patent, then the rights granted to You by any and
-     all Contributors for the Covered Software under Section 2.1 of this License
-     shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
-     license agreements (excluding distributors and resellers) which have been
-     validly granted by You or Your distributors under this License prior to
-     termination shall survive termination.
-
-6. Disclaimer of Warranty
-
-   Covered Software is provided under this License on an “as is” basis, without
-   warranty of any kind, either expressed, implied, or statutory, including,
-   without limitation, warranties that the Covered Software is free of defects,
-   merchantable, fit for a particular purpose or non-infringing. The entire
-   risk as to the quality and performance of the Covered Software is with You.
-   Should any Covered Software prove defective in any respect, You (not any
-   Contributor) assume the cost of any necessary servicing, repair, or
-   correction. This disclaimer of warranty constitutes an essential part of this
-   License. No use of  any Covered Software is authorized under this License
-   except under this disclaimer.
-
-7. Limitation of Liability
-
-   Under no circumstances and under no legal theory, whether tort (including
-   negligence), contract, or otherwise, shall any Contributor, or anyone who
-   distributes Covered Software as permitted above, be liable to You for any
-   direct, indirect, special, incidental, or consequential damages of any
-   character including, without limitation, damages for lost profits, loss of
-   goodwill, work stoppage, computer failure or malfunction, or any and all
-   other commercial damages or losses, even if such party shall have been
-   informed of the possibility of such damages. This limitation of liability
-   shall not apply to liability for death or personal injury resulting from such
-   party’s negligence to the extent applicable law prohibits such limitation.
-   Some jurisdictions do not allow the exclusion or limitation of incidental or
-   consequential damages, so this exclusion and limitation may not apply to You.
-
-8. Litigation
-
-   Any litigation relating to this License may be brought only in the courts of
-   a jurisdiction where the defendant maintains its principal place of business
-   and such litigation shall be governed by laws of that jurisdiction, without
-   reference to its conflict-of-law provisions. Nothing in this Section shall
-   prevent a party’s ability to bring cross-claims or counter-claims.
-
-9. Miscellaneous
-
-   This License represents the complete agreement concerning the subject matter
-   hereof. If any provision of this License is held to be unenforceable, such
-   provision shall be reformed only to the extent necessary to make it
-   enforceable. Any law or regulation which provides that the language of a
-   contract shall be construed against the drafter shall not be used to construe
-   this License against a Contributor.
-
-
-10. Versions of the License
-
-10.1. New Versions
-
-      Mozilla Foundation is the license steward. Except as provided in Section
-      10.3, no one other than the license steward has the right to modify or
-      publish new versions of this License. Each version will be given a
-      distinguishing version number.
-
-10.2. Effect of New Versions
-
-      You may distribute the Covered Software under the terms of the version of
-      the License under which You originally received the Covered Software, or
-      under the terms of any subsequent version published by the license
-      steward.
-
-10.3. Modified Versions
-
-      If you create software not governed by this License, and you want to
-      create a new license for such software, you may create and use a modified
-      version of this License if you rename the license and remove any
-      references to the name of the license steward (except to note that such
-      modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
-      If You choose to distribute Source Code Form that is Incompatible With
-      Secondary Licenses under the terms of this version of the License, the
-      notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
-
-      This Source Code Form is subject to the
-      terms of the Mozilla Public License, v.
-      2.0. If a copy of the MPL was not
-      distributed with this file, You can
-      obtain one at
-      http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular file, then
-You may include the notice in a location (such as a LICENSE file in a relevant
-directory) where a recipient would be likely to look for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - “Incompatible With Secondary Licenses” Notice
-
-      This Source Code Form is “Incompatible
-      With Secondary Licenses”, as defined by
-      the Mozilla Public License, v. 2.0.
diff --git a/vendor/github.com/hashicorp/go-multierror/Makefile b/vendor/github.com/hashicorp/go-multierror/Makefile
deleted file mode 100644
index b97cd6ed0..000000000
--- a/vendor/github.com/hashicorp/go-multierror/Makefile
+++ /dev/null
@@ -1,31 +0,0 @@
-TEST?=./...
-
-default: test
-
-# test runs the test suite and vets the code.
-test: generate
-	@echo "==> Running tests..."
-	@go list $(TEST) \
-		| grep -v "/vendor/" \
-		| xargs -n1 go test -timeout=60s -parallel=10 ${TESTARGS}
-
-# testrace runs the race checker
-testrace: generate
-	@echo "==> Running tests (race)..."
-	@go list $(TEST) \
-		| grep -v "/vendor/" \
-		| xargs -n1 go test -timeout=60s -race ${TESTARGS}
-
-# updatedeps installs all the dependencies needed to run and build.
-updatedeps:
-	@sh -c "'${CURDIR}/scripts/deps.sh' '${NAME}'"
-
-# generate runs `go generate` to build the dynamically generated source files.
-generate:
-	@echo "==> Generating..."
-	@find . -type f -name '.DS_Store' -delete
-	@go list ./... \
-		| grep -v "/vendor/" \
-		| xargs -n1 go generate
-
-.PHONY: default test testrace updatedeps generate
diff --git a/vendor/github.com/hashicorp/go-multierror/README.md b/vendor/github.com/hashicorp/go-multierror/README.md
deleted file mode 100644
index 71dd308ed..000000000
--- a/vendor/github.com/hashicorp/go-multierror/README.md
+++ /dev/null
@@ -1,150 +0,0 @@
-# go-multierror
-
-[![CircleCI](https://img.shields.io/circleci/build/github/hashicorp/go-multierror/master)](https://circleci.com/gh/hashicorp/go-multierror)
-[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-multierror.svg)](https://pkg.go.dev/github.com/hashicorp/go-multierror)
-![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/hashicorp/go-multierror)
-
-[circleci]: https://app.circleci.com/pipelines/github/hashicorp/go-multierror
-[godocs]: https://pkg.go.dev/github.com/hashicorp/go-multierror
-
-`go-multierror` is a package for Go that provides a mechanism for
-representing a list of `error` values as a single `error`.
-
-This allows a function in Go to return an `error` that might actually
-be a list of errors. If the caller knows this, they can unwrap the
-list and access the errors. If the caller doesn't know, the error
-formats to a nice human-readable format.
-
-`go-multierror` is fully compatible with the Go standard library
-[errors](https://golang.org/pkg/errors/) package, including the
-functions `As`, `Is`, and `Unwrap`. This provides a standardized approach
-for introspecting on error values.
-
-## Installation and Docs
-
-Install using `go get github.com/hashicorp/go-multierror`.
-
-Full documentation is available at
-https://pkg.go.dev/github.com/hashicorp/go-multierror
-
-### Requires go version 1.13 or newer
-
-`go-multierror` requires go version 1.13 or newer. Go 1.13 introduced
-[error wrapping](https://golang.org/doc/go1.13#error_wrapping), which
-this library takes advantage of.
-
-If you need to use an earlier version of go, you can use the
-[v1.0.0](https://github.com/hashicorp/go-multierror/tree/v1.0.0)
-tag, which doesn't rely on features in go 1.13.
-
-If you see compile errors that look like the below, it's likely that
-you're on an older version of go:
-
-```
-/go/src/github.com/hashicorp/go-multierror/multierror.go:112:9: undefined: errors.As
-/go/src/github.com/hashicorp/go-multierror/multierror.go:117:9: undefined: errors.Is
-```
-
-## Usage
-
-go-multierror is easy to use and purposely built to be unobtrusive in
-existing Go applications/libraries that may not be aware of it.
-
-**Building a list of errors**
-
-The `Append` function is used to create a list of errors. This function
-behaves a lot like the Go built-in `append` function: it doesn't matter
-if the first argument is nil, a `multierror.Error`, or any other `error`,
-the function behaves as you would expect.
-
-```go
-var result error
-
-if err := step1(); err != nil {
-	result = multierror.Append(result, err)
-}
-if err := step2(); err != nil {
-	result = multierror.Append(result, err)
-}
-
-return result
-```
-
-**Customizing the formatting of the errors**
-
-By specifying a custom `ErrorFormat`, you can customize the format
-of the `Error() string` function:
-
-```go
-var result *multierror.Error
-
-// ... accumulate errors here, maybe using Append
-
-if result != nil {
-	result.ErrorFormat = func([]error) string {
-		return "errors!"
-	}
-}
-```
-
-**Accessing the list of errors**
-
-`multierror.Error` implements `error` so if the caller doesn't know about
-multierror, it will work just fine. But if you're aware a multierror might
-be returned, you can use type switches to access the list of errors:
-
-```go
-if err := something(); err != nil {
-	if merr, ok := err.(*multierror.Error); ok {
-		// Use merr.Errors
-	}
-}
-```
-
-You can also use the standard [`errors.Unwrap`](https://golang.org/pkg/errors/#Unwrap)
-function. This will continue to unwrap into subsequent errors until none exist.
-
-**Extracting an error**
-
-The standard library [`errors.As`](https://golang.org/pkg/errors/#As)
-function can be used directly with a multierror to extract a specific error:
-
-```go
-// Assume err is a multierror value
-err := somefunc()
-
-// We want to know if "err" has a "RichErrorType" in it and extract it.
-var errRich RichErrorType
-if errors.As(err, &errRich) {
-	// It has it, and now errRich is populated.
-}
-```
-
-**Checking for an exact error value**
-
-Some errors are returned as exact errors such as the [`ErrNotExist`](https://golang.org/pkg/os/#pkg-variables)
-error in the `os` package. You can check if this error is present by using
-the standard [`errors.Is`](https://golang.org/pkg/errors/#Is) function.
-
-```go
-// Assume err is a multierror value
-err := somefunc()
-if errors.Is(err, os.ErrNotExist) {
-	// err contains os.ErrNotExist
-}
-```
-
-**Returning a multierror only if there are errors**
-
-If you build a `multierror.Error`, you can use the `ErrorOrNil` function
-to return an `error` implementation only if there are errors to return:
-
-```go
-var result *multierror.Error
-
-// ... accumulate errors here
-
-// Return the `error` only if errors were added to the multierror, otherwise
-// return nil since there are no errors.
-return result.ErrorOrNil()
-```
diff --git a/vendor/github.com/hashicorp/go-multierror/append.go b/vendor/github.com/hashicorp/go-multierror/append.go
deleted file mode 100644
index 3e2589bfd..000000000
--- a/vendor/github.com/hashicorp/go-multierror/append.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package multierror
-
-// Append is a helper function that will append more errors
-// onto an Error in order to create a larger multi-error.
-//
-// If err is not a multierror.Error, then it will be turned into
-// one. If any of the errs are multierr.Error, they will be flattened
-// one level into err.
-// Any nil errors within errs will be ignored. If err is nil, a new
-// *Error will be returned.
-func Append(err error, errs ...error) *Error {
-	switch err := err.(type) {
-	case *Error:
-		// Typed nils can reach here, so initialize if we are nil
-		if err == nil {
-			err = new(Error)
-		}
-
-		// Go through each error and flatten
-		for _, e := range errs {
-			switch e := e.(type) {
-			case *Error:
-				if e != nil {
-					err.Errors = append(err.Errors, e.Errors...)
-				}
-			default:
-				if e != nil {
-					err.Errors = append(err.Errors, e)
-				}
-			}
-		}
-
-		return err
-	default:
-		newErrs := make([]error, 0, len(errs)+1)
-		if err != nil {
-			newErrs = append(newErrs, err)
-		}
-		newErrs = append(newErrs, errs...)
-
-		return Append(&Error{}, newErrs...)
-	}
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/flatten.go b/vendor/github.com/hashicorp/go-multierror/flatten.go
deleted file mode 100644
index aab8e9abe..000000000
--- a/vendor/github.com/hashicorp/go-multierror/flatten.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package multierror
-
-// Flatten flattens the given error, merging any *Errors together into
-// a single *Error.
-func Flatten(err error) error {
-	// If it isn't an *Error, just return the error as-is
-	if _, ok := err.(*Error); !ok {
-		return err
-	}
-
-	// Otherwise, make the result and flatten away!
-	flatErr := new(Error)
-	flatten(err, flatErr)
-	return flatErr
-}
-
-func flatten(err error, flatErr *Error) {
-	switch err := err.(type) {
-	case *Error:
-		for _, e := range err.Errors {
-			flatten(e, flatErr)
-		}
-	default:
-		flatErr.Errors = append(flatErr.Errors, err)
-	}
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/format.go b/vendor/github.com/hashicorp/go-multierror/format.go
deleted file mode 100644
index 47f13c49a..000000000
--- a/vendor/github.com/hashicorp/go-multierror/format.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package multierror
-
-import (
-	"fmt"
-	"strings"
-)
-
-// ErrorFormatFunc is a function callback that is called by Error to
-// turn the list of errors into a string.
-type ErrorFormatFunc func([]error) string
-
-// ListFormatFunc is a basic formatter that outputs the number of errors
-// that occurred along with a bullet point list of the errors.
-func ListFormatFunc(es []error) string {
-	if len(es) == 1 {
-		return fmt.Sprintf("1 error occurred:\n\t* %s\n\n", es[0])
-	}
-
-	points := make([]string, len(es))
-	for i, err := range es {
-		points[i] = fmt.Sprintf("* %s", err)
-	}
-
-	return fmt.Sprintf(
-		"%d errors occurred:\n\t%s\n\n",
-		len(es), strings.Join(points, "\n\t"))
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/group.go b/vendor/github.com/hashicorp/go-multierror/group.go
deleted file mode 100644
index 9c29efb7f..000000000
--- a/vendor/github.com/hashicorp/go-multierror/group.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package multierror
-
-import "sync"
-
-// Group is a collection of goroutines which return errors that need to be
-// coalesced.
-type Group struct {
-	mutex sync.Mutex
-	err   *Error
-	wg    sync.WaitGroup
-}
-
-// Go calls the given function in a new goroutine.
-//
-// If the function returns an error it is added to the group multierror which
-// is returned by Wait.
-func (g *Group) Go(f func() error) {
-	g.wg.Add(1)
-
-	go func() {
-		defer g.wg.Done()
-
-		if err := f(); err != nil {
-			g.mutex.Lock()
-			g.err = Append(g.err, err)
-			g.mutex.Unlock()
-		}
-	}()
-}
-
-// Wait blocks until all function calls from the Go method have returned, then
-// returns the multierror.
-func (g *Group) Wait() *Error {
-	g.wg.Wait()
-	g.mutex.Lock()
-	defer g.mutex.Unlock()
-	return g.err
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/multierror.go b/vendor/github.com/hashicorp/go-multierror/multierror.go
deleted file mode 100644
index f54574326..000000000
--- a/vendor/github.com/hashicorp/go-multierror/multierror.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package multierror
-
-import (
-	"errors"
-	"fmt"
-)
-
-// Error is an error type to track multiple errors. This is used to
-// accumulate errors in cases and return them as a single "error".
-type Error struct {
-	Errors      []error
-	ErrorFormat ErrorFormatFunc
-}
-
-func (e *Error) Error() string {
-	fn := e.ErrorFormat
-	if fn == nil {
-		fn = ListFormatFunc
-	}
-
-	return fn(e.Errors)
-}
-
-// ErrorOrNil returns an error interface if this Error represents
-// a list of errors, or returns nil if the list of errors is empty. This
-// function is useful at the end of accumulation to make sure that the value
-// returned represents the existence of errors.
-func (e *Error) ErrorOrNil() error {
-	if e == nil {
-		return nil
-	}
-	if len(e.Errors) == 0 {
-		return nil
-	}
-
-	return e
-}
-
-func (e *Error) GoString() string {
-	return fmt.Sprintf("*%#v", *e)
-}
-
-// WrappedErrors returns the list of errors that this Error is wrapping. It is
-// an implementation of the errwrap.Wrapper interface so that multierror.Error
-// can be used with that library.
-//
-// This method is not safe to be called concurrently. Unlike accessing the
-// Errors field directly, this function also checks if the multierror is nil to
-// prevent a null-pointer panic. It satisfies the errwrap.Wrapper interface.
-func (e *Error) WrappedErrors() []error {
-	if e == nil {
-		return nil
-	}
-	return e.Errors
-}
-
-// Unwrap returns an error from Error (or nil if there are no errors).
-// This error returned will further support Unwrap to get the next error,
-// etc. The order will match the order of Errors in the multierror.Error
-// at the time of calling.
-//
-// The resulting error supports errors.As/Is/Unwrap so you can continue
-// to use the stdlib errors package to introspect further.
-//
-// This will perform a shallow copy of the errors slice. Any errors appended
-// to this error after calling Unwrap will not be available until a new
-// Unwrap is called on the multierror.Error.
-func (e *Error) Unwrap() error {
-	// If we have no errors then we do nothing
-	if e == nil || len(e.Errors) == 0 {
-		return nil
-	}
-
-	// If we have exactly one error, we can just return that directly.
-	if len(e.Errors) == 1 {
-		return e.Errors[0]
-	}
-
-	// Shallow copy the slice
-	errs := make([]error, len(e.Errors))
-	copy(errs, e.Errors)
-	return chain(errs)
-}
-
-// chain implements the interfaces necessary for errors.Is/As/Unwrap to
-// work in a deterministic way with multierror. A chain tracks a list of
-// errors while accounting for the current represented error. This lets
-// Is/As be meaningful.
-//
-// Unwrap returns the next error. In the cleanest form, Unwrap would return
-// the wrapped error here but we can't do that if we want to properly
-// get access to all the errors. Instead, users are recommended to use
-// Is/As to get the correct error type out.
-//
-// Precondition: []error is non-empty (len > 0)
-type chain []error
-
-// Error implements the error interface
-func (e chain) Error() string {
-	return e[0].Error()
-}
-
-// Unwrap implements errors.Unwrap by returning the next error in the
-// chain or nil if there are no more errors.
-func (e chain) Unwrap() error {
-	if len(e) == 1 {
-		return nil
-	}
-
-	return e[1:]
-}
-
-// As implements errors.As by attempting to map to the current value.
-func (e chain) As(target interface{}) bool {
-	return errors.As(e[0], target)
-}
-
-// Is implements errors.Is by comparing the current value directly.
-func (e chain) Is(target error) bool {
-	return errors.Is(e[0], target)
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/prefix.go b/vendor/github.com/hashicorp/go-multierror/prefix.go
deleted file mode 100644
index 5c477abe4..000000000
--- a/vendor/github.com/hashicorp/go-multierror/prefix.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package multierror
-
-import (
-	"fmt"
-
-	"github.com/hashicorp/errwrap"
-)
-
-// Prefix is a helper function that will prefix some text
-// to the given error. If the error is a multierror.Error, then
-// it will be prefixed to each wrapped error.
-//
-// This is useful to use when appending multiple multierrors
-// together in order to give better scoping.
-func Prefix(err error, prefix string) error {
-	if err == nil {
-		return nil
-	}
-
-	format := fmt.Sprintf("%s {{err}}", prefix)
-	switch err := err.(type) {
-	case *Error:
-		// Typed nils can reach here, so initialize if we are nil
-		if err == nil {
-			err = new(Error)
-		}
-
-		// Wrap each of the errors
-		for i, e := range err.Errors {
-			err.Errors[i] = errwrap.Wrapf(format, e)
-		}
-
-		return err
-	default:
-		return errwrap.Wrapf(format, err)
-	}
-}
diff --git a/vendor/github.com/hashicorp/go-multierror/sort.go b/vendor/github.com/hashicorp/go-multierror/sort.go
deleted file mode 100644
index fecb14e81..000000000
--- a/vendor/github.com/hashicorp/go-multierror/sort.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package multierror
-
-// Len implements sort.Interface function for length
-func (err Error) Len() int {
-	return len(err.Errors)
-}
-
-// Swap implements sort.Interface function for swapping elements
-func (err Error) Swap(i, j int) {
-	err.Errors[i], err.Errors[j] = err.Errors[j], err.Errors[i]
-}
-
-// Less implements sort.Interface function for determining order
-func (err Error) Less(i, j int) bool {
-	return err.Errors[i].Error() < err.Errors[j].Error()
-}
diff --git a/vendor/golang.org/x/net/http2/frame.go b/vendor/golang.org/x/net/http2/frame.go
index 43557ab7e..e2b298d85 100644
--- a/vendor/golang.org/x/net/http2/frame.go
+++ b/vendor/golang.org/x/net/http2/frame.go
@@ -1564,7 +1564,6 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 		if size > remainSize {
 			hdec.SetEmitEnabled(false)
 			mh.Truncated = true
-			remainSize = 0
 			return
 		}
 		remainSize -= size
@@ -1577,36 +1576,6 @@ func (fr *Framer) readMetaFrame(hf *HeadersFrame) (*MetaHeadersFrame, error) {
 	var hc headersOrContinuation = hf
 	for {
 		frag := hc.HeaderBlockFragment()
-
-		// Avoid parsing large amounts of headers that we will then discard.
-		// If the sender exceeds the max header list size by too much,
-		// skip parsing the fragment and close the connection.
-		//
-		// "Too much" is either any CONTINUATION frame after we've already
-		// exceeded the max header list size (in which case remainSize is 0),
-		// or a frame whose encoded size is more than twice the remaining
-		// header list bytes we're willing to accept.
-		if int64(len(frag)) > int64(2*remainSize) {
-			if VerboseLogs {
-				log.Printf("http2: header list too large")
-			}
-			// It would be nice to send a RST_STREAM before sending the GOAWAY,
-			// but the structure of the server's frame writer makes this difficult.
-			return nil, ConnectionError(ErrCodeProtocol)
-		}
-
-		// Also close the connection after any CONTINUATION frame following an
-		// invalid header, since we stop tracking the size of the headers after
-		// an invalid one.
-		if invalid != nil {
-			if VerboseLogs {
-				log.Printf("http2: invalid header: %v", invalid)
-			}
-			// It would be nice to send a RST_STREAM before sending the GOAWAY,
-			// but the structure of the server's frame writer makes this difficult.
-			return nil, ConnectionError(ErrCodeProtocol)
-		}
-
 		if _, err := hdec.Write(frag); err != nil {
 			return nil, ConnectionError(ErrCodeCompression)
 		}
diff --git a/vendor/golang.org/x/net/http2/pipe.go b/vendor/golang.org/x/net/http2/pipe.go
index 3b9f06b96..684d984fd 100644
--- a/vendor/golang.org/x/net/http2/pipe.go
+++ b/vendor/golang.org/x/net/http2/pipe.go
@@ -77,10 +77,7 @@ func (p *pipe) Read(d []byte) (n int, err error) {
 	}
 }
 
-var (
-	errClosedPipeWrite        = errors.New("write on closed buffer")
-	errUninitializedPipeWrite = errors.New("write on uninitialized buffer")
-)
+var errClosedPipeWrite = errors.New("write on closed buffer")
 
 // Write copies bytes from p into the buffer and wakes a reader.
 // It is an error to write more data than the buffer can hold.
@@ -94,12 +91,6 @@ func (p *pipe) Write(d []byte) (n int, err error) {
 	if p.err != nil || p.breakErr != nil {
 		return 0, errClosedPipeWrite
 	}
-	// pipe.setBuffer is never invoked, leaving the buffer uninitialized.
-	// We shouldn't try to write to an uninitialized pipe,
-	// but returning an error is better than panicking.
-	if p.b == nil {
-		return 0, errUninitializedPipeWrite
-	}
 	return p.b.Write(d)
 }
 
diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go
index ce2e8b40e..ae94c6408 100644
--- a/vendor/golang.org/x/net/http2/server.go
+++ b/vendor/golang.org/x/net/http2/server.go
@@ -124,7 +124,6 @@ type Server struct {
 	// IdleTimeout specifies how long until idle clients should be
 	// closed with a GOAWAY frame. PING frames are not considered
 	// activity for the purposes of IdleTimeout.
-	// If zero or negative, there is no timeout.
 	IdleTimeout time.Duration
 
 	// MaxUploadBufferPerConnection is the size of the initial flow
@@ -435,7 +434,7 @@ func (s *Server) ServeConn(c net.Conn, opts *ServeConnOpts) {
 	// passes the connection off to us with the deadline already set.
 	// Write deadlines are set per stream in serverConn.newStream.
 	// Disarm the net.Conn write deadline here.
-	if sc.hs.WriteTimeout > 0 {
+	if sc.hs.WriteTimeout != 0 {
 		sc.conn.SetWriteDeadline(time.Time{})
 	}
 
@@ -925,7 +924,7 @@ func (sc *serverConn) serve() {
 	sc.setConnState(http.StateActive)
 	sc.setConnState(http.StateIdle)
 
-	if sc.srv.IdleTimeout > 0 {
+	if sc.srv.IdleTimeout != 0 {
 		sc.idleTimer = time.AfterFunc(sc.srv.IdleTimeout, sc.onIdleTimer)
 		defer sc.idleTimer.Stop()
 	}
@@ -1638,7 +1637,7 @@ func (sc *serverConn) closeStream(st *stream, err error) {
 	delete(sc.streams, st.id)
 	if len(sc.streams) == 0 {
 		sc.setConnState(http.StateIdle)
-		if sc.srv.IdleTimeout > 0 {
+		if sc.srv.IdleTimeout != 0 {
 			sc.idleTimer.Reset(sc.srv.IdleTimeout)
 		}
 		if h1ServerKeepAlivesDisabled(sc.hs) {
@@ -2018,7 +2017,7 @@ func (sc *serverConn) processHeaders(f *MetaHeadersFrame) error {
 	// similar to how the http1 server works. Here it's
 	// technically more like the http1 Server's ReadHeaderTimeout
 	// (in Go 1.8), though. That's a more sane option anyway.
-	if sc.hs.ReadTimeout > 0 {
+	if sc.hs.ReadTimeout != 0 {
 		sc.conn.SetReadDeadline(time.Time{})
 		st.readDeadline = time.AfterFunc(sc.hs.ReadTimeout, st.onReadTimeout)
 	}
@@ -2039,7 +2038,7 @@ func (sc *serverConn) upgradeRequest(req *http.Request) {
 
 	// Disable any read deadline set by the net/http package
 	// prior to the upgrade.
-	if sc.hs.ReadTimeout > 0 {
+	if sc.hs.ReadTimeout != 0 {
 		sc.conn.SetReadDeadline(time.Time{})
 	}
 
@@ -2117,7 +2116,7 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream
 	st.flow.conn = &sc.flow // link to conn-level counter
 	st.flow.add(sc.initialStreamSendWindowSize)
 	st.inflow.init(sc.srv.initialStreamRecvWindowSize())
-	if sc.hs.WriteTimeout > 0 {
+	if sc.hs.WriteTimeout != 0 {
 		st.writeDeadline = time.AfterFunc(sc.hs.WriteTimeout, st.onWriteTimeout)
 	}
 
diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go
index ce375c8c7..df578b86c 100644
--- a/vendor/golang.org/x/net/http2/transport.go
+++ b/vendor/golang.org/x/net/http2/transport.go
@@ -147,12 +147,6 @@ type Transport struct {
 	// waiting for their turn.
 	StrictMaxConcurrentStreams bool
 
-	// IdleConnTimeout is the maximum amount of time an idle
-	// (keep-alive) connection will remain idle before closing
-	// itself.
-	// Zero means no limit.
-	IdleConnTimeout time.Duration
-
 	// ReadIdleTimeout is the timeout after which a health check using ping
 	// frame will be carried out if no frame is received on the connection.
 	// Note that a ping response will is considered a received frame, so if
@@ -184,8 +178,6 @@ type Transport struct {
 
 	connPoolOnce  sync.Once
 	connPoolOrDef ClientConnPool // non-nil version of ConnPool
-
-	syncHooks *testSyncHooks
 }
 
 func (t *Transport) maxHeaderListSize() uint32 {
@@ -310,7 +302,7 @@ type ClientConn struct {
 	readerErr  error         // set before readerDone is closed
 
 	idleTimeout time.Duration // or 0 for never
-	idleTimer   timer
+	idleTimer   *time.Timer
 
 	mu              sync.Mutex // guards following
 	cond            *sync.Cond // hold mu; broadcast on flow/closed changes
@@ -352,60 +344,6 @@ type ClientConn struct {
 	werr error        // first write error that has occurred
 	hbuf bytes.Buffer // HPACK encoder writes into this
 	henc *hpack.Encoder
-
-	syncHooks *testSyncHooks // can be nil
-}
-
-// Hook points used for testing.
-// Outside of tests, cc.syncHooks is nil and these all have minimal implementations.
-// Inside tests, see the testSyncHooks function docs.
-
-// goRun starts a new goroutine.
-func (cc *ClientConn) goRun(f func()) {
-	if cc.syncHooks != nil {
-		cc.syncHooks.goRun(f)
-		return
-	}
-	go f()
-}
-
-// condBroadcast is cc.cond.Broadcast.
-func (cc *ClientConn) condBroadcast() {
-	if cc.syncHooks != nil {
-		cc.syncHooks.condBroadcast(cc.cond)
-	}
-	cc.cond.Broadcast()
-}
-
-// condWait is cc.cond.Wait.
-func (cc *ClientConn) condWait() {
-	if cc.syncHooks != nil {
-		cc.syncHooks.condWait(cc.cond)
-	}
-	cc.cond.Wait()
-}
-
-// newTimer creates a new time.Timer, or a synthetic timer in tests.
-func (cc *ClientConn) newTimer(d time.Duration) timer {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.newTimer(d)
-	}
-	return newTimeTimer(d)
-}
-
-// afterFunc creates a new time.AfterFunc timer, or a synthetic timer in tests.
-func (cc *ClientConn) afterFunc(d time.Duration, f func()) timer {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.afterFunc(d, f)
-	}
-	return newTimeAfterFunc(d, f)
-}
-
-func (cc *ClientConn) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
-	if cc.syncHooks != nil {
-		return cc.syncHooks.contextWithTimeout(ctx, d)
-	}
-	return context.WithTimeout(ctx, d)
 }
 
 // clientStream is the state for a single HTTP/2 stream. One of these
@@ -487,7 +425,7 @@ func (cs *clientStream) abortStreamLocked(err error) {
 	// TODO(dneil): Clean up tests where cs.cc.cond is nil.
 	if cs.cc.cond != nil {
 		// Wake up writeRequestBody if it is waiting on flow control.
-		cs.cc.condBroadcast()
+		cs.cc.cond.Broadcast()
 	}
 }
 
@@ -497,7 +435,7 @@ func (cs *clientStream) abortRequestBodyWrite() {
 	defer cc.mu.Unlock()
 	if cs.reqBody != nil && cs.reqBodyClosed == nil {
 		cs.closeReqBodyLocked()
-		cc.condBroadcast()
+		cc.cond.Broadcast()
 	}
 }
 
@@ -507,10 +445,10 @@ func (cs *clientStream) closeReqBodyLocked() {
 	}
 	cs.reqBodyClosed = make(chan struct{})
 	reqBodyClosed := cs.reqBodyClosed
-	cs.cc.goRun(func() {
+	go func() {
 		cs.reqBody.Close()
 		close(reqBodyClosed)
-	})
+	}()
 }
 
 type stickyErrWriter struct {
@@ -599,6 +537,15 @@ func authorityAddr(scheme string, authority string) (addr string) {
 	return net.JoinHostPort(host, port)
 }
 
+var retryBackoffHook func(time.Duration) *time.Timer
+
+func backoffNewTimer(d time.Duration) *time.Timer {
+	if retryBackoffHook != nil {
+		return retryBackoffHook(d)
+	}
+	return time.NewTimer(d)
+}
+
 // RoundTripOpt is like RoundTrip, but takes options.
 func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Response, error) {
 	if !(req.URL.Scheme == "https" || (req.URL.Scheme == "http" && t.AllowHTTP)) {
@@ -626,27 +573,13 @@ func (t *Transport) RoundTripOpt(req *http.Request, opt RoundTripOpt) (*http.Res
 				backoff := float64(uint(1) << (uint(retry) - 1))
 				backoff += backoff * (0.1 * mathrand.Float64())
 				d := time.Second * time.Duration(backoff)
-				var tm timer
-				if t.syncHooks != nil {
-					tm = t.syncHooks.newTimer(d)
-					t.syncHooks.blockUntil(func() bool {
-						select {
-						case <-tm.C():
-						case <-req.Context().Done():
-						default:
-							return false
-						}
-						return true
-					})
-				} else {
-					tm = newTimeTimer(d)
-				}
+				timer := backoffNewTimer(d)
 				select {
-				case <-tm.C():
+				case <-timer.C:
 					t.vlogf("RoundTrip retrying after failure: %v", roundTripErr)
 					continue
 				case <-req.Context().Done():
-					tm.Stop()
+					timer.Stop()
 					err = req.Context().Err()
 				}
 			}
@@ -725,9 +658,6 @@ func canRetryError(err error) bool {
 }
 
 func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse bool) (*ClientConn, error) {
-	if t.syncHooks != nil {
-		return t.newClientConn(nil, singleUse, t.syncHooks)
-	}
 	host, _, err := net.SplitHostPort(addr)
 	if err != nil {
 		return nil, err
@@ -736,7 +666,7 @@ func (t *Transport) dialClientConn(ctx context.Context, addr string, singleUse b
 	if err != nil {
 		return nil, err
 	}
-	return t.newClientConn(tconn, singleUse, nil)
+	return t.newClientConn(tconn, singleUse)
 }
 
 func (t *Transport) newTLSConfig(host string) *tls.Config {
@@ -802,10 +732,10 @@ func (t *Transport) maxEncoderHeaderTableSize() uint32 {
 }
 
 func (t *Transport) NewClientConn(c net.Conn) (*ClientConn, error) {
-	return t.newClientConn(c, t.disableKeepAlives(), nil)
+	return t.newClientConn(c, t.disableKeepAlives())
 }
 
-func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHooks) (*ClientConn, error) {
+func (t *Transport) newClientConn(c net.Conn, singleUse bool) (*ClientConn, error) {
 	cc := &ClientConn{
 		t:                     t,
 		tconn:                 c,
@@ -820,15 +750,10 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo
 		wantSettingsAck:       true,
 		pings:                 make(map[[8]byte]chan struct{}),
 		reqHeaderMu:           make(chan struct{}, 1),
-		syncHooks:             hooks,
-	}
-	if hooks != nil {
-		hooks.newclientconn(cc)
-		c = cc.tconn
 	}
 	if d := t.idleConnTimeout(); d != 0 {
 		cc.idleTimeout = d
-		cc.idleTimer = cc.afterFunc(d, cc.onIdleTimeout)
+		cc.idleTimer = time.AfterFunc(d, cc.onIdleTimeout)
 	}
 	if VerboseLogs {
 		t.vlogf("http2: Transport creating client conn %p to %v", cc, c.RemoteAddr())
@@ -893,7 +818,7 @@ func (t *Transport) newClientConn(c net.Conn, singleUse bool, hooks *testSyncHoo
 		return nil, cc.werr
 	}
 
-	cc.goRun(cc.readLoop)
+	go cc.readLoop()
 	return cc, nil
 }
 
@@ -901,7 +826,7 @@ func (cc *ClientConn) healthCheck() {
 	pingTimeout := cc.t.pingTimeout()
 	// We don't need to periodically ping in the health check, because the readLoop of ClientConn will
 	// trigger the healthCheck again if there is no frame received.
-	ctx, cancel := cc.contextWithTimeout(context.Background(), pingTimeout)
+	ctx, cancel := context.WithTimeout(context.Background(), pingTimeout)
 	defer cancel()
 	cc.vlogf("http2: Transport sending health check")
 	err := cc.Ping(ctx)
@@ -1131,7 +1056,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 	// Wait for all in-flight streams to complete or connection to close
 	done := make(chan struct{})
 	cancelled := false // guarded by cc.mu
-	cc.goRun(func() {
+	go func() {
 		cc.mu.Lock()
 		defer cc.mu.Unlock()
 		for {
@@ -1143,9 +1068,9 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 			if cancelled {
 				break
 			}
-			cc.condWait()
+			cc.cond.Wait()
 		}
-	})
+	}()
 	shutdownEnterWaitStateHook()
 	select {
 	case <-done:
@@ -1155,7 +1080,7 @@ func (cc *ClientConn) Shutdown(ctx context.Context) error {
 		cc.mu.Lock()
 		// Free the goroutine above
 		cancelled = true
-		cc.condBroadcast()
+		cc.cond.Broadcast()
 		cc.mu.Unlock()
 		return ctx.Err()
 	}
@@ -1193,7 +1118,7 @@ func (cc *ClientConn) closeForError(err error) {
 	for _, cs := range cc.streams {
 		cs.abortStreamLocked(err)
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	cc.mu.Unlock()
 	cc.closeConn()
 }
@@ -1290,10 +1215,6 @@ func (cc *ClientConn) decrStreamReservationsLocked() {
 }
 
 func (cc *ClientConn) RoundTrip(req *http.Request) (*http.Response, error) {
-	return cc.roundTrip(req, nil)
-}
-
-func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) (*http.Response, error) {
 	ctx := req.Context()
 	cs := &clientStream{
 		cc:                   cc,
@@ -1308,23 +1229,9 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream))
 		respHeaderRecv:       make(chan struct{}),
 		donec:                make(chan struct{}),
 	}
-	cc.goRun(func() {
-		cs.doRequest(req)
-	})
+	go cs.doRequest(req)
 
 	waitDone := func() error {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.donec:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.donec:
 			return nil
@@ -1385,24 +1292,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream))
 		return err
 	}
 
-	if streamf != nil {
-		streamf(cs)
-	}
-
 	for {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.respHeaderRecv:
-				case <-cs.abort:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.respHeaderRecv:
 			return handleResponseHeaders()
@@ -1458,21 +1348,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	if cc.reqHeaderMu == nil {
 		panic("RoundTrip on uninitialized ClientConn") // for tests
 	}
-	var newStreamHook func(*clientStream)
-	if cc.syncHooks != nil {
-		newStreamHook = cc.syncHooks.newstream
-		cc.syncHooks.blockUntil(func() bool {
-			select {
-			case cc.reqHeaderMu <- struct{}{}:
-				<-cc.reqHeaderMu
-			case <-cs.reqCancel:
-			case <-ctx.Done():
-			default:
-				return false
-			}
-			return true
-		})
-	}
 	select {
 	case cc.reqHeaderMu <- struct{}{}:
 	case <-cs.reqCancel:
@@ -1497,10 +1372,6 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	}
 	cc.mu.Unlock()
 
-	if newStreamHook != nil {
-		newStreamHook(cs)
-	}
-
 	// TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere?
 	if !cc.t.disableCompression() &&
 		req.Header.Get("Accept-Encoding") == "" &&
@@ -1581,30 +1452,15 @@ func (cs *clientStream) writeRequest(req *http.Request) (err error) {
 	var respHeaderTimer <-chan time.Time
 	var respHeaderRecv chan struct{}
 	if d := cc.responseHeaderTimeout(); d != 0 {
-		timer := cc.newTimer(d)
+		timer := time.NewTimer(d)
 		defer timer.Stop()
-		respHeaderTimer = timer.C()
+		respHeaderTimer = timer.C
 		respHeaderRecv = cs.respHeaderRecv
 	}
 	// Wait until the peer half-closes its end of the stream,
 	// or until the request is aborted (via context, error, or otherwise),
 	// whichever comes first.
 	for {
-		if cc.syncHooks != nil {
-			cc.syncHooks.blockUntil(func() bool {
-				select {
-				case <-cs.peerClosed:
-				case <-respHeaderTimer:
-				case <-respHeaderRecv:
-				case <-cs.abort:
-				case <-ctx.Done():
-				case <-cs.reqCancel:
-				default:
-					return false
-				}
-				return true
-			})
-		}
 		select {
 		case <-cs.peerClosed:
 			return nil
@@ -1753,7 +1609,7 @@ func (cc *ClientConn) awaitOpenSlotForStreamLocked(cs *clientStream) error {
 			return nil
 		}
 		cc.pendingRequests++
-		cc.condWait()
+		cc.cond.Wait()
 		cc.pendingRequests--
 		select {
 		case <-cs.abort:
@@ -2015,24 +1871,8 @@ func (cs *clientStream) awaitFlowControl(maxBytes int) (taken int32, err error)
 			cs.flow.take(take)
 			return take, nil
 		}
-		cc.condWait()
-	}
-}
-
-func validateHeaders(hdrs http.Header) string {
-	for k, vv := range hdrs {
-		if !httpguts.ValidHeaderFieldName(k) {
-			return fmt.Sprintf("name %q", k)
-		}
-		for _, v := range vv {
-			if !httpguts.ValidHeaderFieldValue(v) {
-				// Don't include the value in the error,
-				// because it may be sensitive.
-				return fmt.Sprintf("value for header %q", k)
-			}
-		}
+		cc.cond.Wait()
 	}
-	return ""
 }
 
 var errNilRequestURL = errors.New("http2: Request.URI is nil")
@@ -2072,14 +1912,19 @@ func (cc *ClientConn) encodeHeaders(req *http.Request, addGzipHeader bool, trail
 		}
 	}
 
-	// Check for any invalid headers+trailers and return an error before we
+	// Check for any invalid headers and return an error before we
 	// potentially pollute our hpack state. (We want to be able to
 	// continue to reuse the hpack encoder for future requests)
-	if err := validateHeaders(req.Header); err != "" {
-		return nil, fmt.Errorf("invalid HTTP header %s", err)
-	}
-	if err := validateHeaders(req.Trailer); err != "" {
-		return nil, fmt.Errorf("invalid HTTP trailer %s", err)
+	for k, vv := range req.Header {
+		if !httpguts.ValidHeaderFieldName(k) {
+			return nil, fmt.Errorf("invalid HTTP header name %q", k)
+		}
+		for _, v := range vv {
+			if !httpguts.ValidHeaderFieldValue(v) {
+				// Don't include the value in the error, because it may be sensitive.
+				return nil, fmt.Errorf("invalid HTTP header value for header %q", k)
+			}
+		}
 	}
 
 	enumerateHeaders := func(f func(name, value string)) {
@@ -2298,7 +2143,7 @@ func (cc *ClientConn) forgetStreamID(id uint32) {
 	}
 	// Wake up writeRequestBody via clientStream.awaitFlowControl and
 	// wake up RoundTrip if there is a pending request.
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 
 	closeOnIdle := cc.singleUse || cc.doNotReuse || cc.t.disableKeepAlives() || cc.goAway != nil
 	if closeOnIdle && cc.streamsReserved == 0 && len(cc.streams) == 0 {
@@ -2386,7 +2231,7 @@ func (rl *clientConnReadLoop) cleanup() {
 			cs.abortStreamLocked(err)
 		}
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	cc.mu.Unlock()
 }
 
@@ -2421,9 +2266,10 @@ func (rl *clientConnReadLoop) run() error {
 	cc := rl.cc
 	gotSettings := false
 	readIdleTimeout := cc.t.ReadIdleTimeout
-	var t timer
+	var t *time.Timer
 	if readIdleTimeout != 0 {
-		t = cc.afterFunc(readIdleTimeout, cc.healthCheck)
+		t = time.AfterFunc(readIdleTimeout, cc.healthCheck)
+		defer t.Stop()
 	}
 	for {
 		f, err := cc.fr.ReadFrame()
@@ -2838,7 +2684,7 @@ func (rl *clientConnReadLoop) processData(f *DataFrame) error {
 		})
 		return nil
 	}
-	if !cs.pastHeaders {
+	if !cs.firstByte {
 		cc.logf("protocol error: received DATA before a HEADERS frame")
 		rl.endStreamError(cs, StreamError{
 			StreamID: f.StreamID,
@@ -3021,7 +2867,7 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error {
 			for _, cs := range cc.streams {
 				cs.flow.add(delta)
 			}
-			cc.condBroadcast()
+			cc.cond.Broadcast()
 
 			cc.initialWindowSize = s.Val
 		case SettingHeaderTableSize:
@@ -3065,18 +2911,9 @@ func (rl *clientConnReadLoop) processWindowUpdate(f *WindowUpdateFrame) error {
 		fl = &cs.flow
 	}
 	if !fl.add(int32(f.Increment)) {
-		// For stream, the sender sends RST_STREAM with an error code of FLOW_CONTROL_ERROR
-		if cs != nil {
-			rl.endStreamError(cs, StreamError{
-				StreamID: f.StreamID,
-				Code:     ErrCodeFlowControl,
-			})
-			return nil
-		}
-
 		return ConnectionError(ErrCodeFlowControl)
 	}
-	cc.condBroadcast()
+	cc.cond.Broadcast()
 	return nil
 }
 
@@ -3118,38 +2955,24 @@ func (cc *ClientConn) Ping(ctx context.Context) error {
 		}
 		cc.mu.Unlock()
 	}
-	var pingError error
-	errc := make(chan struct{})
-	cc.goRun(func() {
+	errc := make(chan error, 1)
+	go func() {
 		cc.wmu.Lock()
 		defer cc.wmu.Unlock()
-		if pingError = cc.fr.WritePing(false, p); pingError != nil {
-			close(errc)
+		if err := cc.fr.WritePing(false, p); err != nil {
+			errc <- err
 			return
 		}
-		if pingError = cc.bw.Flush(); pingError != nil {
-			close(errc)
+		if err := cc.bw.Flush(); err != nil {
+			errc <- err
 			return
 		}
-	})
-	if cc.syncHooks != nil {
-		cc.syncHooks.blockUntil(func() bool {
-			select {
-			case <-c:
-			case <-errc:
-			case <-ctx.Done():
-			case <-cc.readerDone:
-			default:
-				return false
-			}
-			return true
-		})
-	}
+	}()
 	select {
 	case <-c:
 		return nil
-	case <-errc:
-		return pingError
+	case err := <-errc:
+		return err
 	case <-ctx.Done():
 		return ctx.Err()
 	case <-cc.readerDone:
@@ -3318,17 +3141,9 @@ func (rt noDialH2RoundTripper) RoundTrip(req *http.Request) (*http.Response, err
 }
 
 func (t *Transport) idleConnTimeout() time.Duration {
-	// to keep things backwards compatible, we use non-zero values of
-	// IdleConnTimeout, followed by using the IdleConnTimeout on the underlying
-	// http1 transport, followed by 0
-	if t.IdleConnTimeout != 0 {
-		return t.IdleConnTimeout
-	}
-
 	if t.t1 != nil {
 		return t.t1.IdleConnTimeout
 	}
-
 	return 0
 }
 
diff --git a/vendor/golang.org/x/sys/unix/aliases.go b/vendor/golang.org/x/sys/unix/aliases.go
index b0e419857..e7d3df4bd 100644
--- a/vendor/golang.org/x/sys/unix/aliases.go
+++ b/vendor/golang.org/x/sys/unix/aliases.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
+//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
 
 package unix
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go
index 2f0fa76e4..16dc69937 100644
--- a/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go
+++ b/vendor/golang.org/x/sys/unix/syscall_darwin_libSystem.go
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
-//go:build darwin
+//go:build darwin && go1.12
 
 package unix
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_freebsd.go b/vendor/golang.org/x/sys/unix/syscall_freebsd.go
index 2b57e0f73..64d1bb4db 100644
--- a/vendor/golang.org/x/sys/unix/syscall_freebsd.go
+++ b/vendor/golang.org/x/sys/unix/syscall_freebsd.go
@@ -13,7 +13,6 @@
 package unix
 
 import (
-	"errors"
 	"sync"
 	"unsafe"
 )
@@ -170,26 +169,25 @@ func Getfsstat(buf []Statfs_t, flags int) (n int, err error) {
 func Uname(uname *Utsname) error {
 	mib := []_C_int{CTL_KERN, KERN_OSTYPE}
 	n := unsafe.Sizeof(uname.Sysname)
-	// Suppress ENOMEM errors to be compatible with the C library __xuname() implementation.
-	if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
+	if err := sysctl(mib, &uname.Sysname[0], &n, nil, 0); err != nil {
 		return err
 	}
 
 	mib = []_C_int{CTL_KERN, KERN_HOSTNAME}
 	n = unsafe.Sizeof(uname.Nodename)
-	if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
+	if err := sysctl(mib, &uname.Nodename[0], &n, nil, 0); err != nil {
 		return err
 	}
 
 	mib = []_C_int{CTL_KERN, KERN_OSRELEASE}
 	n = unsafe.Sizeof(uname.Release)
-	if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
+	if err := sysctl(mib, &uname.Release[0], &n, nil, 0); err != nil {
 		return err
 	}
 
 	mib = []_C_int{CTL_KERN, KERN_VERSION}
 	n = unsafe.Sizeof(uname.Version)
-	if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
+	if err := sysctl(mib, &uname.Version[0], &n, nil, 0); err != nil {
 		return err
 	}
 
@@ -207,7 +205,7 @@ func Uname(uname *Utsname) error {
 
 	mib = []_C_int{CTL_HW, HW_MACHINE}
 	n = unsafe.Sizeof(uname.Machine)
-	if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil && !errors.Is(err, ENOMEM) {
+	if err := sysctl(mib, &uname.Machine[0], &n, nil, 0); err != nil {
 		return err
 	}
 
diff --git a/vendor/golang.org/x/sys/unix/syscall_linux.go b/vendor/golang.org/x/sys/unix/syscall_linux.go
index 5682e2628..0f85e29e6 100644
--- a/vendor/golang.org/x/sys/unix/syscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/syscall_linux.go
@@ -1849,105 +1849,6 @@ func Dup2(oldfd, newfd int) error {
 //sys	Fsmount(fd int, flags int, mountAttrs int) (fsfd int, err error)
 //sys	Fsopen(fsName string, flags int) (fd int, err error)
 //sys	Fspick(dirfd int, pathName string, flags int) (fd int, err error)
-
-//sys	fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error)
-
-func fsconfigCommon(fd int, cmd uint, key string, value *byte, aux int) (err error) {
-	var keyp *byte
-	if keyp, err = BytePtrFromString(key); err != nil {
-		return
-	}
-	return fsconfig(fd, cmd, keyp, value, aux)
-}
-
-// FsconfigSetFlag is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_FLAG.
-//
-// fd is the filesystem context to act upon.
-// key the parameter key to set.
-func FsconfigSetFlag(fd int, key string) (err error) {
-	return fsconfigCommon(fd, FSCONFIG_SET_FLAG, key, nil, 0)
-}
-
-// FsconfigSetString is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_STRING.
-//
-// fd is the filesystem context to act upon.
-// key the parameter key to set.
-// value is the parameter value to set.
-func FsconfigSetString(fd int, key string, value string) (err error) {
-	var valuep *byte
-	if valuep, err = BytePtrFromString(value); err != nil {
-		return
-	}
-	return fsconfigCommon(fd, FSCONFIG_SET_STRING, key, valuep, 0)
-}
-
-// FsconfigSetBinary is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_BINARY.
-//
-// fd is the filesystem context to act upon.
-// key the parameter key to set.
-// value is the parameter value to set.
-func FsconfigSetBinary(fd int, key string, value []byte) (err error) {
-	if len(value) == 0 {
-		return EINVAL
-	}
-	return fsconfigCommon(fd, FSCONFIG_SET_BINARY, key, &value[0], len(value))
-}
-
-// FsconfigSetPath is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_PATH.
-//
-// fd is the filesystem context to act upon.
-// key the parameter key to set.
-// path is a non-empty path for specified key.
-// atfd is a file descriptor at which to start lookup from or AT_FDCWD.
-func FsconfigSetPath(fd int, key string, path string, atfd int) (err error) {
-	var valuep *byte
-	if valuep, err = BytePtrFromString(path); err != nil {
-		return
-	}
-	return fsconfigCommon(fd, FSCONFIG_SET_PATH, key, valuep, atfd)
-}
-
-// FsconfigSetPathEmpty is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_PATH_EMPTY. The same as
-// FconfigSetPath but with AT_PATH_EMPTY implied.
-func FsconfigSetPathEmpty(fd int, key string, path string, atfd int) (err error) {
-	var valuep *byte
-	if valuep, err = BytePtrFromString(path); err != nil {
-		return
-	}
-	return fsconfigCommon(fd, FSCONFIG_SET_PATH_EMPTY, key, valuep, atfd)
-}
-
-// FsconfigSetFd is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_SET_FD.
-//
-// fd is the filesystem context to act upon.
-// key the parameter key to set.
-// value is a file descriptor to be assigned to specified key.
-func FsconfigSetFd(fd int, key string, value int) (err error) {
-	return fsconfigCommon(fd, FSCONFIG_SET_FD, key, nil, value)
-}
-
-// FsconfigCreate is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_CMD_CREATE.
-//
-// fd is the filesystem context to act upon.
-func FsconfigCreate(fd int) (err error) {
-	return fsconfig(fd, FSCONFIG_CMD_CREATE, nil, nil, 0)
-}
-
-// FsconfigReconfigure is equivalent to fsconfig(2) called
-// with cmd == FSCONFIG_CMD_RECONFIGURE.
-//
-// fd is the filesystem context to act upon.
-func FsconfigReconfigure(fd int) (err error) {
-	return fsconfig(fd, FSCONFIG_CMD_RECONFIGURE, nil, nil, 0)
-}
-
 //sys	Getdents(fd int, buf []byte) (n int, err error) = SYS_GETDENTS64
 //sysnb	Getpgid(pid int) (pgid int, err error)
 
diff --git a/vendor/golang.org/x/sys/unix/zsyscall_linux.go b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
index 87d8612a1..1488d2712 100644
--- a/vendor/golang.org/x/sys/unix/zsyscall_linux.go
+++ b/vendor/golang.org/x/sys/unix/zsyscall_linux.go
@@ -906,16 +906,6 @@ func Fspick(dirfd int, pathName string, flags int) (fd int, err error) {
 
 // THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
 
-func fsconfig(fd int, cmd uint, key *byte, value *byte, aux int) (err error) {
-	_, _, e1 := Syscall6(SYS_FSCONFIG, uintptr(fd), uintptr(cmd), uintptr(unsafe.Pointer(key)), uintptr(unsafe.Pointer(value)), uintptr(aux), 0)
-	if e1 != 0 {
-		err = errnoErr(e1)
-	}
-	return
-}
-
-// THIS FILE IS GENERATED BY THE COMMAND AT THE TOP; DO NOT EDIT
-
 func Getdents(fd int, buf []byte) (n int, err error) {
 	var _p0 unsafe.Pointer
 	if len(buf) > 0 {
diff --git a/vendor/golang.org/x/sys/unix/ztypes_linux.go b/vendor/golang.org/x/sys/unix/ztypes_linux.go
index eff6bcdef..dc0c955ee 100644
--- a/vendor/golang.org/x/sys/unix/ztypes_linux.go
+++ b/vendor/golang.org/x/sys/unix/ztypes_linux.go
@@ -836,15 +836,6 @@ const (
 	FSPICK_EMPTY_PATH       = 0x8
 
 	FSMOUNT_CLOEXEC = 0x1
-
-	FSCONFIG_SET_FLAG        = 0x0
-	FSCONFIG_SET_STRING      = 0x1
-	FSCONFIG_SET_BINARY      = 0x2
-	FSCONFIG_SET_PATH        = 0x3
-	FSCONFIG_SET_PATH_EMPTY  = 0x4
-	FSCONFIG_SET_FD          = 0x5
-	FSCONFIG_CMD_CREATE      = 0x6
-	FSCONFIG_CMD_RECONFIGURE = 0x7
 )
 
 type OpenHow struct {
@@ -1559,7 +1550,6 @@ const (
 	IFLA_DEVLINK_PORT                          = 0x3e
 	IFLA_GSO_IPV4_MAX_SIZE                     = 0x3f
 	IFLA_GRO_IPV4_MAX_SIZE                     = 0x40
-	IFLA_DPLL_PIN                              = 0x41
 	IFLA_PROTO_DOWN_REASON_UNSPEC              = 0x0
 	IFLA_PROTO_DOWN_REASON_MASK                = 0x1
 	IFLA_PROTO_DOWN_REASON_VALUE               = 0x2
@@ -1575,7 +1565,6 @@ const (
 	IFLA_INET6_ICMP6STATS                      = 0x6
 	IFLA_INET6_TOKEN                           = 0x7
 	IFLA_INET6_ADDR_GEN_MODE                   = 0x8
-	IFLA_INET6_RA_MTU                          = 0x9
 	IFLA_BR_UNSPEC                             = 0x0
 	IFLA_BR_FORWARD_DELAY                      = 0x1
 	IFLA_BR_HELLO_TIME                         = 0x2
@@ -1623,9 +1612,6 @@ const (
 	IFLA_BR_MCAST_MLD_VERSION                  = 0x2c
 	IFLA_BR_VLAN_STATS_PER_PORT                = 0x2d
 	IFLA_BR_MULTI_BOOLOPT                      = 0x2e
-	IFLA_BR_MCAST_QUERIER_STATE                = 0x2f
-	IFLA_BR_FDB_N_LEARNED                      = 0x30
-	IFLA_BR_FDB_MAX_LEARNED                    = 0x31
 	IFLA_BRPORT_UNSPEC                         = 0x0
 	IFLA_BRPORT_STATE                          = 0x1
 	IFLA_BRPORT_PRIORITY                       = 0x2
@@ -1663,14 +1649,6 @@ const (
 	IFLA_BRPORT_BACKUP_PORT                    = 0x22
 	IFLA_BRPORT_MRP_RING_OPEN                  = 0x23
 	IFLA_BRPORT_MRP_IN_OPEN                    = 0x24
-	IFLA_BRPORT_MCAST_EHT_HOSTS_LIMIT          = 0x25
-	IFLA_BRPORT_MCAST_EHT_HOSTS_CNT            = 0x26
-	IFLA_BRPORT_LOCKED                         = 0x27
-	IFLA_BRPORT_MAB                            = 0x28
-	IFLA_BRPORT_MCAST_N_GROUPS                 = 0x29
-	IFLA_BRPORT_MCAST_MAX_GROUPS               = 0x2a
-	IFLA_BRPORT_NEIGH_VLAN_SUPPRESS            = 0x2b
-	IFLA_BRPORT_BACKUP_NHID                    = 0x2c
 	IFLA_INFO_UNSPEC                           = 0x0
 	IFLA_INFO_KIND                             = 0x1
 	IFLA_INFO_DATA                             = 0x2
@@ -1692,9 +1670,6 @@ const (
 	IFLA_MACVLAN_MACADDR                       = 0x4
 	IFLA_MACVLAN_MACADDR_DATA                  = 0x5
 	IFLA_MACVLAN_MACADDR_COUNT                 = 0x6
-	IFLA_MACVLAN_BC_QUEUE_LEN                  = 0x7
-	IFLA_MACVLAN_BC_QUEUE_LEN_USED             = 0x8
-	IFLA_MACVLAN_BC_CUTOFF                     = 0x9
 	IFLA_VRF_UNSPEC                            = 0x0
 	IFLA_VRF_TABLE                             = 0x1
 	IFLA_VRF_PORT_UNSPEC                       = 0x0
@@ -1718,22 +1693,9 @@ const (
 	IFLA_XFRM_UNSPEC                           = 0x0
 	IFLA_XFRM_LINK                             = 0x1
 	IFLA_XFRM_IF_ID                            = 0x2
-	IFLA_XFRM_COLLECT_METADATA                 = 0x3
 	IFLA_IPVLAN_UNSPEC                         = 0x0
 	IFLA_IPVLAN_MODE                           = 0x1
 	IFLA_IPVLAN_FLAGS                          = 0x2
-	NETKIT_NEXT                                = -0x1
-	NETKIT_PASS                                = 0x0
-	NETKIT_DROP                                = 0x2
-	NETKIT_REDIRECT                            = 0x7
-	NETKIT_L2                                  = 0x0
-	NETKIT_L3                                  = 0x1
-	IFLA_NETKIT_UNSPEC                         = 0x0
-	IFLA_NETKIT_PEER_INFO                      = 0x1
-	IFLA_NETKIT_PRIMARY                        = 0x2
-	IFLA_NETKIT_POLICY                         = 0x3
-	IFLA_NETKIT_PEER_POLICY                    = 0x4
-	IFLA_NETKIT_MODE                           = 0x5
 	IFLA_VXLAN_UNSPEC                          = 0x0
 	IFLA_VXLAN_ID                              = 0x1
 	IFLA_VXLAN_GROUP                           = 0x2
@@ -1764,8 +1726,6 @@ const (
 	IFLA_VXLAN_GPE                             = 0x1b
 	IFLA_VXLAN_TTL_INHERIT                     = 0x1c
 	IFLA_VXLAN_DF                              = 0x1d
-	IFLA_VXLAN_VNIFILTER                       = 0x1e
-	IFLA_VXLAN_LOCALBYPASS                     = 0x1f
 	IFLA_GENEVE_UNSPEC                         = 0x0
 	IFLA_GENEVE_ID                             = 0x1
 	IFLA_GENEVE_REMOTE                         = 0x2
@@ -1780,7 +1740,6 @@ const (
 	IFLA_GENEVE_LABEL                          = 0xb
 	IFLA_GENEVE_TTL_INHERIT                    = 0xc
 	IFLA_GENEVE_DF                             = 0xd
-	IFLA_GENEVE_INNER_PROTO_INHERIT            = 0xe
 	IFLA_BAREUDP_UNSPEC                        = 0x0
 	IFLA_BAREUDP_PORT                          = 0x1
 	IFLA_BAREUDP_ETHERTYPE                     = 0x2
@@ -1793,8 +1752,6 @@ const (
 	IFLA_GTP_FD1                               = 0x2
 	IFLA_GTP_PDP_HASHSIZE                      = 0x3
 	IFLA_GTP_ROLE                              = 0x4
-	IFLA_GTP_CREATE_SOCKETS                    = 0x5
-	IFLA_GTP_RESTART_COUNT                     = 0x6
 	IFLA_BOND_UNSPEC                           = 0x0
 	IFLA_BOND_MODE                             = 0x1
 	IFLA_BOND_ACTIVE_SLAVE                     = 0x2
@@ -1824,9 +1781,6 @@ const (
 	IFLA_BOND_AD_ACTOR_SYSTEM                  = 0x1a
 	IFLA_BOND_TLB_DYNAMIC_LB                   = 0x1b
 	IFLA_BOND_PEER_NOTIF_DELAY                 = 0x1c
-	IFLA_BOND_AD_LACP_ACTIVE                   = 0x1d
-	IFLA_BOND_MISSED_MAX                       = 0x1e
-	IFLA_BOND_NS_IP6_TARGET                    = 0x1f
 	IFLA_BOND_AD_INFO_UNSPEC                   = 0x0
 	IFLA_BOND_AD_INFO_AGGREGATOR               = 0x1
 	IFLA_BOND_AD_INFO_NUM_PORTS                = 0x2
@@ -1842,7 +1796,6 @@ const (
 	IFLA_BOND_SLAVE_AD_AGGREGATOR_ID           = 0x6
 	IFLA_BOND_SLAVE_AD_ACTOR_OPER_PORT_STATE   = 0x7
 	IFLA_BOND_SLAVE_AD_PARTNER_OPER_PORT_STATE = 0x8
-	IFLA_BOND_SLAVE_PRIO                       = 0x9
 	IFLA_VF_INFO_UNSPEC                        = 0x0
 	IFLA_VF_INFO                               = 0x1
 	IFLA_VF_UNSPEC                             = 0x0
@@ -1901,16 +1854,8 @@ const (
 	IFLA_STATS_LINK_XSTATS_SLAVE               = 0x3
 	IFLA_STATS_LINK_OFFLOAD_XSTATS             = 0x4
 	IFLA_STATS_AF_SPEC                         = 0x5
-	IFLA_STATS_GETSET_UNSPEC                   = 0x0
-	IFLA_STATS_GET_FILTERS                     = 0x1
-	IFLA_STATS_SET_OFFLOAD_XSTATS_L3_STATS     = 0x2
 	IFLA_OFFLOAD_XSTATS_UNSPEC                 = 0x0
 	IFLA_OFFLOAD_XSTATS_CPU_HIT                = 0x1
-	IFLA_OFFLOAD_XSTATS_HW_S_INFO              = 0x2
-	IFLA_OFFLOAD_XSTATS_L3_STATS               = 0x3
-	IFLA_OFFLOAD_XSTATS_HW_S_INFO_UNSPEC       = 0x0
-	IFLA_OFFLOAD_XSTATS_HW_S_INFO_REQUEST      = 0x1
-	IFLA_OFFLOAD_XSTATS_HW_S_INFO_USED         = 0x2
 	IFLA_XDP_UNSPEC                            = 0x0
 	IFLA_XDP_FD                                = 0x1
 	IFLA_XDP_ATTACHED                          = 0x2
@@ -1940,11 +1885,6 @@ const (
 	IFLA_RMNET_UNSPEC                          = 0x0
 	IFLA_RMNET_MUX_ID                          = 0x1
 	IFLA_RMNET_FLAGS                           = 0x2
-	IFLA_MCTP_UNSPEC                           = 0x0
-	IFLA_MCTP_NET                              = 0x1
-	IFLA_DSA_UNSPEC                            = 0x0
-	IFLA_DSA_CONDUIT                           = 0x1
-	IFLA_DSA_MASTER                            = 0x1
 )
 
 const (
diff --git a/vendor/k8s.io/api/core/v1/generated.proto b/vendor/k8s.io/api/core/v1/generated.proto
index d099238cd..cf9b6e6eb 100644
--- a/vendor/k8s.io/api/core/v1/generated.proto
+++ b/vendor/k8s.io/api/core/v1/generated.proto
@@ -3286,7 +3286,7 @@ message PersistentVolumeStatus {
 
   // lastPhaseTransitionTime is the time the phase transitioned from one to another
   // and automatically resets to current time everytime a volume phase transitions.
-  // This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
+  // This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.
   // +featureGate=PersistentVolumeLastPhaseTransitionTime
   // +optional
   optional k8s.io.apimachinery.pkg.apis.meta.v1.Time lastPhaseTransitionTime = 4;
diff --git a/vendor/k8s.io/api/core/v1/types.go b/vendor/k8s.io/api/core/v1/types.go
index 61ba21bca..1aade3806 100644
--- a/vendor/k8s.io/api/core/v1/types.go
+++ b/vendor/k8s.io/api/core/v1/types.go
@@ -423,7 +423,7 @@ type PersistentVolumeStatus struct {
 	Reason string `json:"reason,omitempty" protobuf:"bytes,3,opt,name=reason"`
 	// lastPhaseTransitionTime is the time the phase transitioned from one to another
 	// and automatically resets to current time everytime a volume phase transitions.
-	// This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).
+	// This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.
 	// +featureGate=PersistentVolumeLastPhaseTransitionTime
 	// +optional
 	LastPhaseTransitionTime *metav1.Time `json:"lastPhaseTransitionTime,omitempty" protobuf:"bytes,4,opt,name=lastPhaseTransitionTime"`
diff --git a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
index fd6f7dc61..01152a096 100644
--- a/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
+++ b/vendor/k8s.io/api/core/v1/types_swagger_doc_generated.go
@@ -1478,7 +1478,7 @@ var map_PersistentVolumeStatus = map[string]string{
 	"phase":                   "phase indicates if a volume is available, bound to a claim, or released by a claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#phase",
 	"message":                 "message is a human-readable message indicating details about why the volume is in this state.",
 	"reason":                  "reason is a brief CamelCase string that describes any failure and is meant for machine parsing and tidy display in the CLI.",
-	"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is a beta field and requires the PersistentVolumeLastPhaseTransitionTime feature to be enabled (enabled by default).",
+	"lastPhaseTransitionTime": "lastPhaseTransitionTime is the time the phase transitioned from one to another and automatically resets to current time everytime a volume phase transitions. This is an alpha field and requires enabling PersistentVolumeLastPhaseTransitionTime feature.",
 }
 
 func (PersistentVolumeStatus) SwaggerDoc() map[string]string {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 14c235be0..d7202e1eb 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -297,12 +297,6 @@ github.com/grafana/pyroscope-go/upstream/remote
 ## explicit; go 1.16
 github.com/grafana/pyroscope-go/godeltaprof
 github.com/grafana/pyroscope-go/godeltaprof/internal/pprof
-# github.com/hashicorp/errwrap v1.1.0
-## explicit
-github.com/hashicorp/errwrap
-# github.com/hashicorp/go-multierror v1.1.1
-## explicit; go 1.13
-github.com/hashicorp/go-multierror
 # github.com/hashicorp/hcl v1.0.0
 ## explicit
 github.com/hashicorp/hcl
@@ -673,7 +667,7 @@ go.uber.org/zap/internal/color
 go.uber.org/zap/internal/exit
 go.uber.org/zap/internal/pool
 go.uber.org/zap/zapcore
-# golang.org/x/crypto v0.21.0
+# golang.org/x/crypto v0.19.0
 ## explicit; go 1.18
 golang.org/x/crypto/bcrypt
 golang.org/x/crypto/blowfish
@@ -689,7 +683,7 @@ golang.org/x/exp/slices
 golang.org/x/mod/internal/lazyregexp
 golang.org/x/mod/module
 golang.org/x/mod/semver
-# golang.org/x/net v0.23.0
+# golang.org/x/net v0.21.0
 ## explicit; go 1.18
 golang.org/x/net/bpf
 golang.org/x/net/context
@@ -714,13 +708,13 @@ golang.org/x/oauth2/internal
 ## explicit; go 1.18
 golang.org/x/sync/errgroup
 golang.org/x/sync/semaphore
-# golang.org/x/sys v0.18.0
+# golang.org/x/sys v0.17.0
 ## explicit; go 1.18
 golang.org/x/sys/plan9
 golang.org/x/sys/unix
 golang.org/x/sys/windows
 golang.org/x/sys/windows/registry
-# golang.org/x/term v0.18.0
+# golang.org/x/term v0.17.0
 ## explicit; go 1.18
 golang.org/x/term
 # golang.org/x/text v0.14.0
@@ -839,7 +833,7 @@ gopkg.in/yaml.v2
 # gopkg.in/yaml.v3 v3.0.1
 ## explicit
 gopkg.in/yaml.v3
-# k8s.io/api v0.29.4
+# k8s.io/api v0.29.1
 ## explicit; go 1.21
 k8s.io/api/admission/v1
 k8s.io/api/admission/v1beta1
@@ -895,7 +889,7 @@ k8s.io/api/scheduling/v1beta1
 k8s.io/api/storage/v1
 k8s.io/api/storage/v1alpha1
 k8s.io/api/storage/v1beta1
-# k8s.io/apiextensions-apiserver v0.29.4
+# k8s.io/apiextensions-apiserver v0.29.1
 ## explicit; go 1.21
 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions
 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1
@@ -953,7 +947,7 @@ k8s.io/apimachinery/pkg/version
 k8s.io/apimachinery/pkg/watch
 k8s.io/apimachinery/third_party/forked/golang/json
 k8s.io/apimachinery/third_party/forked/golang/reflect
-# k8s.io/client-go v0.29.4
+# k8s.io/client-go v0.29.1
 ## explicit; go 1.21
 k8s.io/client-go/applyconfigurations/admissionregistration/v1
 k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1
@@ -1301,7 +1295,7 @@ k8s.io/code-generator/cmd/register-gen/generators
 k8s.io/code-generator/pkg/namer
 k8s.io/code-generator/pkg/util
 k8s.io/code-generator/third_party/forked/golang/reflect
-# k8s.io/component-base v0.29.4
+# k8s.io/component-base v0.29.1
 ## explicit; go 1.21
 k8s.io/component-base/config
 k8s.io/component-base/config/v1alpha1


Test patch:
diff --git a/.github/workflows/e2e-init.yaml b/.github/workflows/e2e-init.yaml
index e6011aca5..fecc4d030 100644
--- a/.github/workflows/e2e-init.yaml
+++ b/.github/workflows/e2e-init.yaml
@@ -210,7 +210,7 @@ jobs:
           path: e2ereport.json
           retention-days: 1
 
-      - name: uninstalls spiderpool
+      - name: helm uninstalls spiderpool and cleans spiderpool CRD
         id: clean
         if: ${{ inputs.run_e2e == 'true' }}
         run: |
diff --git a/test/Makefile b/test/Makefile
index 45a185bae..4f3cd1290 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -360,21 +360,60 @@ setup_spiderpool:
 		fi ; \
 		exit 0
 
-.PHONY: uninstall_spiderpool
-uninstall_spiderpool:
-	@echo -e "\033[35m [uninstall spiderpool] \033[0m"
+.PHONY: clean_spiderpool_crd
+clean_spiderpool_crd:
+	@echo -e "\033[35m [clean spiderpool crd] \033[0m"
+	@-$(QUIET) export KUBECONFIG=$(E2E_KUBECONFIG); $(ROOT_DIR)/tools/scripts/cleanCRD.sh
+	@ALL_CRD=`kubectl get crd -n $(RELEASE_NAMESPACE) --kubeconfig $(E2E_KUBECONFIG)  | awk '{print $$1 } ' ` ; FAIL="false" ; \
+	for CRD in $$ALL_CRD ; do \
+		echo $${CRD} | grep -q 'spiderpool.spidernet.io' && FAIL="true" && echo "error, failed to clean spiderpool crd: $${CRD}" ; \
+	    if [ "$$FAIL" == "true" ] ; then \
+           echo -e "\033[35m [===== describe crd: $${CRD} =====] \033[0m" ; \
+		   kubectl describe crd $${CRD} --kubeconfig $(E2E_KUBECONFIG) ; \
+		   echo -e "\033[35m [===== get crd: $${CRD} -ojson =====] \033[0m" ; \
+           kubectl get crd $${CRD} -ojson --kubeconfig $(E2E_KUBECONFIG) ; \
+		fi ; \
+	done ; \
+	[ "$$FAIL" == "true" ] && echo "spiderpool crd cleanup failed" && exit 1 ; \
+	echo -e "\033[35m [done] all spiderpool crd cleaned up !!! \033[0m"
+
+.PHONY: helm_uninstall_spiderpool
+helm_uninstall_spiderpool:
 	@echo -e "\033[35m [helm uninstall spiderpool] \033[0m"
 	helm uninstall $(RELEASE_NAME) --wait --debug -n $(RELEASE_NAMESPACE) \
 	     --kubeconfig $(E2E_KUBECONFIG)  || { KIND_CLUSTER_NAME=$(E2E_CLUSTER_NAME) ./scripts/debugEnv.sh $(E2E_KUBECONFIG) "detail"   ; exit 1 ; } ; \
-	for ((i=0; i<100; i++)); do \
-    	if ! kubectl --kubeconfig=$(E2E_KUBECONFIG) get all --all-namespaces | grep -q "spiderpool" ; then \
-			echo "All resources successfully cleared." ; \
-			exit 0; \
-		fi ;\
-		echo "found spiderpool resources, waiting..." ; \
-		sleep 1 ; \
-	done; \
-	echo "error: found spiderpool resources" ; exit 1 ;
+
+.PHONY: uninstall_spiderpool
+uninstall_spiderpool:
+	@echo -e "\033[35m [uninstall spiderpool] \033[0m"
+	@ make helm_uninstall_spiderpool
+	@ make clean_spiderpool_crd
+
+.PHONY: upgrade_spiderpool
+upgrade_spiderpool:
+	@echo -e "\033[35m [upgrade spiderpool] \033[0m"
+	@make helm_upgrade_spiderpool
+
+.PHONY: helm_upgrade_spiderpool
+helm_upgrade_spiderpool:
+	@echo -e "\033[35m [helm upgrade spiderpool] \033[0m"
+	kubectl delete po -n $(RELEASE_NAMESPACE) spiderpool-init --kubeconfig $(E2E_KUBECONFIG) || true ;\
+	HELM_OPTION="";\
+	HELM_OPTION+=" --set spiderpoolController.replicas=1 " ; \
+	HELM_OPTION+=" 	--set spiderpoolAgent.image.registry="" \
+		--set spiderpoolAgent.image.repository=$(SPIDERPOOL_AGENT_IMAGE_NAME) \
+		--set spiderpoolAgent.image.tag=$(E2E_SPIDERPOOL_TAG) \
+		--set spiderpoolController.image.registry="" \
+		--set spiderpoolController.image.repository=$(SPIDERPOOL_CONTROLLER_IMAGE_NAME) \
+		--set spiderpoolController.image.tag=$(E2E_SPIDERPOOL_TAG) \
+		--set spiderpoolInit.image.registry="" \
+		--set spiderpoolInit.image.repository=$(SPIDERPOOL_CONTROLLER_IMAGE_NAME) \
+		--set spiderpoolInit.image.tag=$(E2E_SPIDERPOOL_TAG) " \
+	helm --kubeconfig $(E2E_KUBECONFIG) upgrade $(RELEASE_NAME) $(ROOT_DIR)/charts/spiderpool $${HELM_OPTION} \
+		-n $(RELEASE_NAMESPACE) ; \
+    cd $(ROOT_DIR)/charts/spiderpool/crds ; \
+	ls | grep '\.yaml$$' | xargs -I {} kubectl apply -f {} --kubeconfig $(E2E_KUBECONFIG) ; \
+	kubectl wait --for=condition=ready -l app.kubernetes.io/instance=spiderpool --timeout=300s pod -n kube-system --kubeconfig $(E2E_KUBECONFIG) || true; \
 
 .PHONY: clean
 clean:
diff --git a/vendor/golang.org/x/net/http2/testsync.go b/vendor/golang.org/x/net/http2/testsync.go
deleted file mode 100644
index 61075bd16..000000000
--- a/vendor/golang.org/x/net/http2/testsync.go
+++ /dev/null
@@ -1,331 +0,0 @@
-// Copyright 2024 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-package http2
-
-import (
-	"context"
-	"sync"
-	"time"
-)
-
-// testSyncHooks coordinates goroutines in tests.
-//
-// For example, a call to ClientConn.RoundTrip involves several goroutines, including:
-//   - the goroutine running RoundTrip;
-//   - the clientStream.doRequest goroutine, which writes the request; and
-//   - the clientStream.readLoop goroutine, which reads the response.
-//
-// Using testSyncHooks, a test can start a RoundTrip and identify when all these goroutines
-// are blocked waiting for some condition such as reading the Request.Body or waiting for
-// flow control to become available.
-//
-// The testSyncHooks also manage timers and synthetic time in tests.
-// This permits us to, for example, start a request and cause it to time out waiting for
-// response headers without resorting to time.Sleep calls.
-type testSyncHooks struct {
-	// active/inactive act as a mutex and condition variable.
-	//
-	//  - neither chan contains a value: testSyncHooks is locked.
-	//  - active contains a value: unlocked, and at least one goroutine is not blocked
-	//  - inactive contains a value: unlocked, and all goroutines are blocked
-	active   chan struct{}
-	inactive chan struct{}
-
-	// goroutine counts
-	total    int                     // total goroutines
-	condwait map[*sync.Cond]int      // blocked in sync.Cond.Wait
-	blocked  []*testBlockedGoroutine // otherwise blocked
-
-	// fake time
-	now    time.Time
-	timers []*fakeTimer
-
-	// Transport testing: Report various events.
-	newclientconn func(*ClientConn)
-	newstream     func(*clientStream)
-}
-
-// testBlockedGoroutine is a blocked goroutine.
-type testBlockedGoroutine struct {
-	f  func() bool   // blocked until f returns true
-	ch chan struct{} // closed when unblocked
-}
-
-func newTestSyncHooks() *testSyncHooks {
-	h := &testSyncHooks{
-		active:   make(chan struct{}, 1),
-		inactive: make(chan struct{}, 1),
-		condwait: map[*sync.Cond]int{},
-	}
-	h.inactive <- struct{}{}
-	h.now = time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)
-	return h
-}
-
-// lock acquires the testSyncHooks mutex.
-func (h *testSyncHooks) lock() {
-	select {
-	case <-h.active:
-	case <-h.inactive:
-	}
-}
-
-// waitInactive waits for all goroutines to become inactive.
-func (h *testSyncHooks) waitInactive() {
-	for {
-		<-h.inactive
-		if !h.unlock() {
-			break
-		}
-	}
-}
-
-// unlock releases the testSyncHooks mutex.
-// It reports whether any goroutines are active.
-func (h *testSyncHooks) unlock() (active bool) {
-	// Look for a blocked goroutine which can be unblocked.
-	blocked := h.blocked[:0]
-	unblocked := false
-	for _, b := range h.blocked {
-		if !unblocked && b.f() {
-			unblocked = true
-			close(b.ch)
-		} else {
-			blocked = append(blocked, b)
-		}
-	}
-	h.blocked = blocked
-
-	// Count goroutines blocked on condition variables.
-	condwait := 0
-	for _, count := range h.condwait {
-		condwait += count
-	}
-
-	if h.total > condwait+len(blocked) {
-		h.active <- struct{}{}
-		return true
-	} else {
-		h.inactive <- struct{}{}
-		return false
-	}
-}
-
-// goRun starts a new goroutine.
-func (h *testSyncHooks) goRun(f func()) {
-	h.lock()
-	h.total++
-	h.unlock()
-	go func() {
-		defer func() {
-			h.lock()
-			h.total--
-			h.unlock()
-		}()
-		f()
-	}()
-}
-
-// blockUntil indicates that a goroutine is blocked waiting for some condition to become true.
-// It waits until f returns true before proceeding.
-//
-// Example usage:
-//
-//	h.blockUntil(func() bool {
-//		// Is the context done yet?
-//		select {
-//		case <-ctx.Done():
-//		default:
-//			return false
-//		}
-//		return true
-//	})
-//	// Wait for the context to become done.
-//	<-ctx.Done()
-//
-// The function f passed to blockUntil must be non-blocking and idempotent.
-func (h *testSyncHooks) blockUntil(f func() bool) {
-	if f() {
-		return
-	}
-	ch := make(chan struct{})
-	h.lock()
-	h.blocked = append(h.blocked, &testBlockedGoroutine{
-		f:  f,
-		ch: ch,
-	})
-	h.unlock()
-	<-ch
-}
-
-// broadcast is sync.Cond.Broadcast.
-func (h *testSyncHooks) condBroadcast(cond *sync.Cond) {
-	h.lock()
-	delete(h.condwait, cond)
-	h.unlock()
-	cond.Broadcast()
-}
-
-// broadcast is sync.Cond.Wait.
-func (h *testSyncHooks) condWait(cond *sync.Cond) {
-	h.lock()
-	h.condwait[cond]++
-	h.unlock()
-}
-
-// newTimer creates a new fake timer.
-func (h *testSyncHooks) newTimer(d time.Duration) timer {
-	h.lock()
-	defer h.unlock()
-	t := &fakeTimer{
-		hooks: h,
-		when:  h.now.Add(d),
-		c:     make(chan time.Time),
-	}
-	h.timers = append(h.timers, t)
-	return t
-}
-
-// afterFunc creates a new fake AfterFunc timer.
-func (h *testSyncHooks) afterFunc(d time.Duration, f func()) timer {
-	h.lock()
-	defer h.unlock()
-	t := &fakeTimer{
-		hooks: h,
-		when:  h.now.Add(d),
-		f:     f,
-	}
-	h.timers = append(h.timers, t)
-	return t
-}
-
-func (h *testSyncHooks) contextWithTimeout(ctx context.Context, d time.Duration) (context.Context, context.CancelFunc) {
-	ctx, cancel := context.WithCancel(ctx)
-	t := h.afterFunc(d, cancel)
-	return ctx, func() {
-		t.Stop()
-		cancel()
-	}
-}
-
-func (h *testSyncHooks) timeUntilEvent() time.Duration {
-	h.lock()
-	defer h.unlock()
-	var next time.Time
-	for _, t := range h.timers {
-		if next.IsZero() || t.when.Before(next) {
-			next = t.when
-		}
-	}
-	if d := next.Sub(h.now); d > 0 {
-		return d
-	}
-	return 0
-}
-
-// advance advances time and causes synthetic timers to fire.
-func (h *testSyncHooks) advance(d time.Duration) {
-	h.lock()
-	defer h.unlock()
-	h.now = h.now.Add(d)
-	timers := h.timers[:0]
-	for _, t := range h.timers {
-		t := t // remove after go.mod depends on go1.22
-		t.mu.Lock()
-		switch {
-		case t.when.After(h.now):
-			timers = append(timers, t)
-		case t.when.IsZero():
-			// stopped timer
-		default:
-			t.when = time.Time{}
-			if t.c != nil {
-				close(t.c)
-			}
-			if t.f != nil {
-				h.total++
-				go func() {
-					defer func() {
-						h.lock()
-						h.total--
-						h.unlock()
-					}()
-					t.f()
-				}()
-			}
-		}
-		t.mu.Unlock()
-	}
-	h.timers = timers
-}
-
-// A timer wraps a time.Timer, or a synthetic equivalent in tests.
-// Unlike time.Timer, timer is single-use: The timer channel is closed when the timer expires.
-type timer interface {
-	C() <-chan time.Time
-	Stop() bool
-	Reset(d time.Duration) bool
-}
-
-// timeTimer implements timer using real time.
-type timeTimer struct {
-	t *time.Timer
-	c chan time.Time
-}
-
-// newTimeTimer creates a new timer using real time.
-func newTimeTimer(d time.Duration) timer {
-	ch := make(chan time.Time)
-	t := time.AfterFunc(d, func() {
-		close(ch)
-	})
-	return &timeTimer{t, ch}
-}
-
-// newTimeAfterFunc creates an AfterFunc timer using real time.
-func newTimeAfterFunc(d time.Duration, f func()) timer {
-	return &timeTimer{
-		t: time.AfterFunc(d, f),
-	}
-}
-
-func (t timeTimer) C() <-chan time.Time        { return t.c }
-func (t timeTimer) Stop() bool                 { return t.t.Stop() }
-func (t timeTimer) Reset(d time.Duration) bool { return t.t.Reset(d) }
-
-// fakeTimer implements timer using fake time.
-type fakeTimer struct {
-	hooks *testSyncHooks
-
-	mu   sync.Mutex
-	when time.Time      // when the timer will fire
-	c    chan time.Time // closed when the timer fires; mutually exclusive with f
-	f    func()         // called when the timer fires; mutually exclusive with c
-}
-
-func (t *fakeTimer) C() <-chan time.Time { return t.c }
-
-func (t *fakeTimer) Stop() bool {
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	stopped := t.when.IsZero()
-	t.when = time.Time{}
-	return stopped
-}
-
-func (t *fakeTimer) Reset(d time.Duration) bool {
-	if t.c != nil || t.f == nil {
-		panic("fakeTimer only supports Reset on AfterFunc timers")
-	}
-	t.mu.Lock()
-	defer t.mu.Unlock()
-	t.hooks.lock()
-	defer t.hooks.unlock()
-	active := !t.when.IsZero()
-	t.when = t.hooks.now.Add(d)
-	if !active {
-		t.hooks.timers = append(t.hooks.timers, t)
-	}
-	return active
-}