Repository: metallb/metallb
Pull request #2072 (2023-09-11 02:08:03)

Bump actions/checkout from 3 to 4
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/releases">actions/checkout's releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Update default runtime to node20 by <a href="https://github.com/takost"><code>@​takost</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1436">actions/checkout#1436</a></li>
<li>Support fetching without the --progress option by <a href="https://github.com/simonbaird"><code>@​simonbaird</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1067">actions/checkout#1067</a></li>
<li>Release 4.0.0 by <a href="https://github.com/takost"><code>@​takost</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1447">actions/checkout#1447</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/takost"><code>@​takost</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1436">actions/checkout#1436</a></li>
<li><a href="https://github.com/simonbaird"><code>@​simonbaird</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1067">actions/checkout#1067</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3...v4.0.0">https://github.com/actions/checkout/compare/v3...v4.0.0</a></p>
<h2>v3.6.0</h2>
<h2>What's Changed</h2>
<ul>
<li>Mark test scripts with Bash'isms to be run via Bash by <a href="https://github.com/dscho"><code>@​dscho</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1377">actions/checkout#1377</a></li>
<li>Add option to fetch tags even if fetch-depth &gt; 0 by <a href="https://github.com/RobertWieczoreck"><code>@​RobertWieczoreck</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/579">actions/checkout#579</a></li>
<li>Release 3.6.0 by <a href="https://github.com/luketomlinson"><code>@​luketomlinson</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1437">actions/checkout#1437</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/RobertWieczoreck"><code>@​RobertWieczoreck</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/579">actions/checkout#579</a></li>
<li><a href="https://github.com/luketomlinson"><code>@​luketomlinson</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1437">actions/checkout#1437</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3.5.3...v3.6.0">https://github.com/actions/checkout/compare/v3.5.3...v3.6.0</a></p>
<h2>v3.5.3</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix: Checkout Issue in self hosted runner due to faulty submodule check-ins by <a href="https://github.com/megamanics"><code>@​megamanics</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1196">actions/checkout#1196</a></li>
<li>Fix typos found by codespell by <a href="https://github.com/DimitriPapadopoulos"><code>@​DimitriPapadopoulos</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1287">actions/checkout#1287</a></li>
<li>Add support for sparse checkouts by <a href="https://github.com/dscho"><code>@​dscho</code></a> and <a href="https://github.com/dfdez"><code>@​dfdez</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1369">actions/checkout#1369</a></li>
<li>Release v3.5.3 by <a href="https://github.com/TingluoHuang"><code>@​TingluoHuang</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1376">actions/checkout#1376</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/megamanics"><code>@​megamanics</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1196">actions/checkout#1196</a></li>
<li><a href="https://github.com/DimitriPapadopoulos"><code>@​DimitriPapadopoulos</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1287">actions/checkout#1287</a></li>
<li><a href="https://github.com/dfdez"><code>@​dfdez</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1369">actions/checkout#1369</a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3...v3.5.3">https://github.com/actions/checkout/compare/v3...v3.5.3</a></p>
<h2>v3.5.2</h2>
<h2>What's Changed</h2>
<ul>
<li>Fix: Use correct API url / endpoint in GHES by <a href="https://github.com/fhammerl"><code>@​fhammerl</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1289">actions/checkout#1289</a> based on <a href="https://redirect.github.com/actions/checkout/issues/1286">#1286</a> by <a href="https://github.com/1newsr"><code>@​1newsr</code></a></li>
</ul>
<p><strong>Full Changelog</strong>: <a href="https://github.com/actions/checkout/compare/v3.5.1...v3.5.2">https://github.com/actions/checkout/compare/v3.5.1...v3.5.2</a></p>
<h2>v3.5.1</h2>
<h2>What's Changed</h2>
<ul>
<li>Improve checkout performance on Windows runners by upgrading <code>@​actions/github</code> dependency by <a href="https://github.com/BrettDong"><code>@​BrettDong</code></a> in <a href="https://redirect.github.com/actions/checkout/pull/1246">actions/checkout#1246</a></li>
</ul>
<h2>New Contributors</h2>
<ul>
<li><a href="https://github.com/BrettDong"><code>@​BrettDong</code></a> made their first contribution in <a href="https://redirect.github.com/actions/checkout/pull/1246">actions/checkout#1246</a></li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/actions/checkout/blob/main/CHANGELOG.md">actions/checkout's changelog</a>.</em></p>
<blockquote>
<h1>Changelog</h1>
<h2>v4.0.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1067">Support fetching without the --progress option</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1436">Update to node20</a></li>
</ul>
<h2>v3.6.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1377">Fix: Mark test scripts with Bash'isms to be run via Bash</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/579">Add option to fetch tags even if fetch-depth &gt; 0</a></li>
</ul>
<h2>v3.5.3</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1196">Fix: Checkout fail in self-hosted runners when faulty submodule are checked-in</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1287">Fix typos found by codespell</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1369">Add support for sparse checkouts</a></li>
</ul>
<h2>v3.5.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1289">Fix api endpoint for GHES</a></li>
</ul>
<h2>v3.5.1</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1246">Fix slow checkout on Windows</a></li>
</ul>
<h2>v3.5.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1237">Add new public key for known_hosts</a></li>
</ul>
<h2>v3.4.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1209">Upgrade codeql actions to v2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1210">Upgrade dependencies</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1225">Upgrade <code>@​actions/io</code></a></li>
</ul>
<h2>v3.3.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/1045">Implement branch list using callbacks from exec function</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1050">Add in explicit reference to private checkout options</a></li>
<li>[Fix comment typos (that got added in <a href="https://redirect.github.com/actions/checkout/issues/770">#770</a>)](<a href="https://redirect.github.com/actions/checkout/pull/1057">actions/checkout#1057</a>)</li>
</ul>
<h2>v3.2.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/942">Add GitHub Action to perform release</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/967">Fix status badge</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1002">Replace datadog/squid with ubuntu/squid Docker image</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/964">Wrap pipeline commands for submoduleForeach in quotes</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1029">Update <code>@​actions/io</code> to 1.1.2</a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/1039">Upgrading version to 3.2.0</a></li>
</ul>
<h2>v3.1.0</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/939">Use <code>@​actions/core</code> <code>saveState</code> and <code>getState</code></a></li>
<li><a href="https://redirect.github.com/actions/checkout/pull/922">Add <code>github-server-url</code> input</a></li>
</ul>
<h2>v3.0.2</h2>
<ul>
<li><a href="https://redirect.github.com/actions/checkout/pull/770">Add input <code>set-safe-directory</code></a></li>
</ul>
<h2>v3.0.1</h2>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/checkout/commit/3df4ab11eba7bda6032a0b82a6bb43b11571feac"><code>3df4ab1</code></a> Release 4.0.0 (<a href="https://redirect.github.com/actions/checkout/issues/1447">#1447</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/8b5e8b768746b50394015010d25e690bfab9dfbc"><code>8b5e8b7</code></a> Support fetching without the --progress option (<a href="https://redirect.github.com/actions/checkout/issues/1067">#1067</a>)</li>
<li><a href="https://github.com/actions/checkout/commit/97a652b80035363df47baee5031ec8670b8878ac"><code>97a652b</code></a> Update default runtime to node20 (<a href="https://redirect.github.com/actions/checkout/issues/1436">#1436</a>)</li>
<li>See full diff in <a href="https://github.com/actions/checkout/compare/v3...v4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/checkout&package-manager=github_actions&previous-version=3&new-version=4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

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

---

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

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


</details>


Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 6166f9d4..1c466eaf 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -26,7 +26,7 @@ jobs:
       GO111MODULE: on
     steps:
       - name: Checkout Source
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Run Gosec Security Scanner
         uses: securego/gosec@master
         with:
@@ -40,7 +40,7 @@ jobs:
   commitlint:
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@v3
+      - uses: actions/checkout@v4
         with:
           fetch-depth: 0
       - uses: wagoid/commitlint-github-action@v5
@@ -54,7 +54,7 @@ jobs:
           access_token: ${{ github.token }}
 
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - uses: actions/setup-go@v4
         with:
@@ -91,7 +91,7 @@ jobs:
         image: [speaker, controller, configmaptocrs]
     steps:
       - name: Code checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Setup docker buildx
         uses: docker/setup-buildx-action@v2
 
@@ -115,7 +115,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           # Required for chart-testing
           fetch-depth: 0
@@ -178,7 +178,7 @@ jobs:
           prometheus: disabled
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Install kernel modules
         run: |
@@ -233,7 +233,7 @@ jobs:
       - commitlint
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
@@ -241,7 +241,7 @@ jobs:
         uses: ./.github/workflows/composite/setup
 
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: "0"
           path: metallb-v0.12.1
@@ -294,7 +294,7 @@ jobs:
           sudo apt-get install linux-modules-extra-$(uname -r)
 
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
@@ -302,14 +302,14 @@ jobs:
         uses: ./.github/workflows/composite/setup
 
       - name: Checkout Metal LB Operator
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           repository: metallb/metallb-operator
           path: metallboperator
           ref: main
 
       - name: Checkout MetalLB
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           path: metallb
           fetch-depth: 0
@@ -375,7 +375,7 @@ jobs:
           sudo apt-get install linux-modules-extra-$(uname -r)
 
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           fetch-depth: 0
 
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index 599caf3b..3878a731 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -17,7 +17,7 @@ jobs:
           access_token: ${{ github.token }}
 
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - uses: actions/setup-go@v4
         with:
@@ -59,7 +59,7 @@ jobs:
         image: [speaker, controller, configmaptocrs]
     steps:
       - name: Set up QEMU
-        uses: docker/setup-qemu-action@v2
+        uses: docker/setup-qemu-action@v3
 
       - name: Install Cosign
         uses: sigstore/cosign-installer@main
@@ -67,13 +67,13 @@ jobs:
           cosign-release: "v1.13.1"
 
       - name: Code checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Setup docker buildx
         uses: docker/setup-buildx-action@v2
 
       - name: Log into Quay
-        uses: docker/login-action@v2
+        uses: docker/login-action@v3
         with:
           registry: quay.io
           username: ${{ secrets.QUAY_USER }}
@@ -120,7 +120,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           # Fetch entire history. Required for chart-releaser; see https://github.com/helm/chart-releaser-action/issues/13#issuecomment-602063896
           fetch-depth: 0
diff --git a/.github/workflows/site-build.yaml b/.github/workflows/site-build.yaml
index a757bfb7..ee93b78f 100644
--- a/.github/workflows/site-build.yaml
+++ b/.github/workflows/site-build.yaml
@@ -15,7 +15,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
         with:
           submodules: true  # Fetch Hugo themes (true OR recursive)
           fetch-depth: 0    # Fetch all history for .GitInfo and .Lastmod
diff --git a/.github/workflows/vulncheck_periodic.yaml b/.github/workflows/vulncheck_periodic.yaml
index a9af352a..3a2ad405 100644
--- a/.github/workflows/vulncheck_periodic.yaml
+++ b/.github/workflows/vulncheck_periodic.yaml
@@ -11,7 +11,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
       - name: Golang Vulncheck
         uses: Templum/govulncheck-action@v0.10.1
 
diff --git a/charts/metallb/charts/crds/templates/crds.yaml b/charts/metallb/charts/crds/templates/crds.yaml
index 9b415acf..4e1861f1 100644
--- a/charts/metallb/charts/crds/templates/crds.yaml
+++ b/charts/metallb/charts/crds/templates/crds.yaml
@@ -2,220 +2,182 @@ apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: addresspools.metallb.io
 spec:
-  group: metallb.io
-  names:
-    kind: AddressPool
-    listKind: AddressPoolList
-    plural: addresspools
-    singular: addresspool
-  scope: Namespaced
   conversion:
     strategy: Webhook
     webhook:
-      conversionReviewVersions: ["v1alpha1", "v1beta1"]
       clientConfig:
-        # this is a valid pem format, otherwise the apiserver will reject the deletion of the crds
-        # with "unable to parse bytes as PEM block", The controller will patch it with the right content after it starts
         caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGWlRDQ0EwMmdBd0lCQWdJVU5GRW1XcTM3MVpKdGkrMmlSQzk1WmpBV1MxZ3dEUVlKS29aSWh2Y05BUUVMDQpCUUF3UWpFTE1Ba0dBMVVFQmhNQ1dGZ3hGVEFUQmdOVkJBY01ERVJsWm1GMWJIUWdRMmwwZVRFY01Cb0dBMVVFDQpDZ3dUUkdWbVlYVnNkQ0JEYjIxd1lXNTVJRXgwWkRBZUZ3MHlNakEzTVRrd09UTXlNek5hRncweU1qQTRNVGd3DQpPVE15TXpOYU1FSXhDekFKQmdOVkJBWVRBbGhZTVJVd0V3WURWUVFIREF4RVpXWmhkV3gwSUVOcGRIa3hIREFhDQpCZ05WQkFvTUUwUmxabUYxYkhRZ1EyOXRjR0Z1ZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDDQpEd0F3Z2dJS0FvSUNBUUNxVFpxMWZRcC9vYkdlenhES0o3OVB3Ny94azJwellualNzMlkzb1ZYSm5sRmM4YjVlDQpma2ZZQnY2bndscW1keW5PL2phWFBaQmRQSS82aFdOUDBkdVhadEtWU0NCUUpyZzEyOGNXb3F0MGNTN3pLb1VpDQpvcU1tQ0QvRXVBeFFNZjhRZDF2c1gvVllkZ0poVTZBRXJLZEpIaXpFOUJtUkNkTDBGMW1OVW55Rk82UnRtWFZUDQpidkxsTDVYeTc2R0FaQVBLOFB4aVlDa0NtbDdxN0VnTWNiOXlLWldCYmlxQ3VkTXE5TGJLNmdKNzF6YkZnSXV4DQo1L1pXK2JraTB2RlplWk9ZODUxb1psckFUNzJvMDI4NHNTWW9uN0pHZVZkY3NoUnh5R1VpSFpSTzdkaXZVTDVTDQpmM2JmSDFYbWY1ZDQzT0NWTWRuUUV2NWVaOG8zeWVLa3ZrbkZQUGVJMU9BbjdGbDlFRVNNR2dhOGFaSG1URSttDQpsLzlMSmdDYjBnQmtPT0M0WnV4bWh2aERKV1EzWnJCS3pMQlNUZXN0NWlLNVlwcXRWVVk2THRyRW9FelVTK1lsDQpwWndXY2VQWHlHeHM5ZURsR3lNVmQraW15Y3NTU1UvVno2Mmx6MnZCS21NTXBkYldDQWhud0RsRTVqU2dyMjRRDQp0eGNXLys2N3d5KzhuQlI3UXdqVTFITndVRjBzeERWdEwrZ1NHVERnSEVZSlhZelYvT05zMy94TkpoVFNPSkxNDQpoeXNVdyttaGdackdhbUdXcHVIVU1DUitvTWJzMTc1UkcrQjJnUFFHVytPTjJnUTRyOXN2b0ZBNHBBQm8xd1dLDQpRYjRhY3pmeVVscElBOVFoSmFsZEY3S3dPSHVlV3gwRUNrNXg0T2tvVDBvWVp0dzFiR0JjRGtaSmF3SURBUUFCDQpvMU13VVRBZEJnTlZIUTRFRmdRVW90UlNIUm9IWTEyRFZ4R0NCdEhpb1g2ZmVFQXdId1lEVlIwakJCZ3dGb0FVDQpvdFJTSFJvSFkxMkRWeEdDQnRIaW9YNmZlRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCDQpBUXNGQUFPQ0FnRUFSbkpsWWRjMTFHd0VxWnh6RDF2R3BDR2pDN2VWTlQ3aVY1d3IybXlybHdPYi9aUWFEa0xYDQpvVStaOVVXT1VlSXJTdzUydDdmQUpvVVAwSm5iYkMveVIrU1lqUGhvUXNiVHduOTc2ZldBWTduM3FMOXhCd1Y0DQphek41OXNjeUp0dlhMeUtOL2N5ak1ReDRLajBIMFg0bWJ6bzVZNUtzWWtYVU0vOEFPdWZMcEd0S1NGVGgrSEFDDQpab1Q5YnZHS25adnNHd0tYZFF0Wnh0akhaUjVqK3U3ZGtQOTJBT051RFNabS8rWVV4b2tBK09JbzdSR3BwSHNXDQo1ZTdNY0FTVXRtb1FORXd6dVFoVkJaRWQ1OGtKYjUrV0VWbGNzanlXNnRTbzErZ25tTWNqR1BsMWgxR2hVbjV4DQpFY0lWRnBIWXM5YWo1NmpBSjk1MVQvZjhMaWxmTlVnanBLQ0c1bnl0SUt3emxhOHNtdGlPdm1UNEpYbXBwSkI2DQo4bmdHRVluVjUrUTYwWFJ2OEhSSGp1VG9CRHVhaERrVDA2R1JGODU1d09FR2V4bkZpMXZYWUxLVllWb1V2MXRKDQo4dVdUR1pwNllDSVJldlBqbzg5ZytWTlJSaVFYUThJd0dybXE5c0RoVTlqTjA0SjdVL1RvRDFpNHE3VnlsRUc5DQorV1VGNkNLaEdBeTJIaEhwVncyTGFoOS9lUzdZMUZ1YURrWmhPZG1laG1BOCtqdHNZamJadnR5Mm1SWlF0UUZzDQpUU1VUUjREbUR2bVVPRVRmeStpRHdzK2RkWXVNTnJGeVVYV2dkMnpBQU4ydVl1UHFGY2pRcFNPODFzVTJTU3R3DQoxVzAyeUtYOGJEYmZFdjBzbUh3UzliQnFlSGo5NEM1Mjg0YXpsdTBmaUdpTm1OUEM4ckJLRmhBPQ0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
         service:
+          name: webhook-service
           namespace: {{ .Release.Namespace }}
-          name: metallb-webhook-service
           path: /convert
+      conversionReviewVersions:
+        - v1alpha1
+        - v1beta1
+  group: metallb.io
+  names:
+    kind: AddressPool
+    listKind: AddressPoolList
+    plural: addresspools
+    singular: addresspool
+  scope: Namespaced
   versions:
-  - deprecated: true
-    deprecationWarning: metallb.io v1alpha1 AddressPool is deprecated
-    name: v1alpha1
-    schema:
-      openAPIV3Schema:
-        description: AddressPool is the Schema for the addresspools API.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: AddressPoolSpec defines the desired state of AddressPool.
-            properties:
-              addresses:
-                description: A list of IP address ranges over which MetalLB has authority.
-                  You can list multiple ranges in a single pool, they will all share
-                  the same settings. Each range can be either a CIDR prefix, or an
-                  explicit start-end range of IPs.
-                items:
+    - deprecated: true
+      deprecationWarning: metallb.io v1alpha1 AddressPool is deprecated
+      name: v1alpha1
+      schema:
+        openAPIV3Schema:
+          description: AddressPool is the Schema for the addresspools API.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: AddressPoolSpec defines the desired state of AddressPool.
+              properties:
+                addresses:
+                  description: A list of IP address ranges over which MetalLB has authority. You can list multiple ranges in a single pool, they will all share the same settings. Each range can be either a CIDR prefix, or an explicit start-end range of IPs.
+                  items:
+                    type: string
+                  type: array
+                autoAssign:
+                  default: true
+                  description: AutoAssign flag used to prevent MetallB from automatic allocation for a pool.
+                  type: boolean
+                bgpAdvertisements:
+                  description: When an IP is allocated from this pool, how should it be translated into BGP announcements?
+                  items:
+                    properties:
+                      aggregationLength:
+                        default: 32
+                        description: The aggregation-length advertisement option lets you “roll up” the /32s into a larger prefix.
+                        format: int32
+                        minimum: 1
+                        type: integer
+                      aggregationLengthV6:
+                        default: 128
+                        description: Optional, defaults to 128 (i.e. no aggregation) if not specified.
+                        format: int32
+                        type: integer
+                      communities:
+                        description: BGP communities
+                        items:
+                          type: string
+                        type: array
+                      localPref:
+                        description: BGP LOCAL_PREF attribute which is used by BGP best path algorithm, Path with higher localpref is preferred over one with lower localpref.
+                        format: int32
+                        type: integer
+                    type: object
+                  type: array
+                protocol:
+                  description: Protocol can be used to select how the announcement is done.
+                  enum:
+                    - layer2
+                    - bgp
                   type: string
-                type: array
-              autoAssign:
-                default: true
-                description: AutoAssign flag used to prevent MetallB from automatic
-                  allocation for a pool.
-                type: boolean
-              bgpAdvertisements:
-                description: When an IP is allocated from this pool, how should it
-                  be translated into BGP announcements?
-                items:
-                  properties:
-                    aggregationLength:
-                      default: 32
-                      description: The aggregation-length advertisement option lets
-                        you “roll up” the /32s into a larger prefix.
-                      format: int32
-                      minimum: 1
-                      type: integer
-                    aggregationLengthV6:
-                      default: 128
-                      description: Optional, defaults to 128 (i.e. no aggregation)
-                        if not specified.
-                      format: int32
-                      type: integer
-                    communities:
-                      description: BGP communities
-                      items:
-                        type: string
-                      type: array
-                    localPref:
-                      description: BGP LOCAL_PREF attribute which is used by BGP best
-                        path algorithm, Path with higher localpref is preferred over
-                        one with lower localpref.
-                      format: int32
-                      type: integer
-                  type: object
-                type: array
-              protocol:
-                description: Protocol can be used to select how the announcement is
-                  done.
-                enum:
-                - layer2
-                - bgp
-                type: string
-            required:
-            - addresses
-            - protocol
-            type: object
-          status:
-            description: AddressPoolStatus defines the observed state of AddressPool.
-            type: object
-        required:
-        - spec
-        type: object
-    served: true
-    storage: false
-    subresources:
-      status: {}
-  - deprecated: true
-    deprecationWarning: metallb.io v1beta1 AddressPool is deprecated, consider using
-      IPAddressPool
-    name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: AddressPool represents a pool of IP addresses that can be allocated
-          to LoadBalancer services. AddressPool is deprecated and being replaced by
-          IPAddressPool.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: AddressPoolSpec defines the desired state of AddressPool.
-            properties:
-              addresses:
-                description: A list of IP address ranges over which MetalLB has authority.
-                  You can list multiple ranges in a single pool, they will all share
-                  the same settings. Each range can be either a CIDR prefix, or an
-                  explicit start-end range of IPs.
-                items:
+              required:
+                - addresses
+                - protocol
+              type: object
+            status:
+              description: AddressPoolStatus defines the observed state of AddressPool.
+              type: object
+          required:
+            - spec
+          type: object
+      served: true
+      storage: false
+      subresources:
+        status: {}
+    - deprecated: true
+      deprecationWarning: metallb.io v1beta1 AddressPool is deprecated, consider using IPAddressPool
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: AddressPool represents a pool of IP addresses that can be allocated to LoadBalancer services. AddressPool is deprecated and being replaced by IPAddressPool.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: AddressPoolSpec defines the desired state of AddressPool.
+              properties:
+                addresses:
+                  description: A list of IP address ranges over which MetalLB has authority. You can list multiple ranges in a single pool, they will all share the same settings. Each range can be either a CIDR prefix, or an explicit start-end range of IPs.
+                  items:
+                    type: string
+                  type: array
+                autoAssign:
+                  default: true
+                  description: AutoAssign flag used to prevent MetallB from automatic allocation for a pool.
+                  type: boolean
+                bgpAdvertisements:
+                  description: Drives how an IP allocated from this pool should translated into BGP announcements.
+                  items:
+                    properties:
+                      aggregationLength:
+                        default: 32
+                        description: The aggregation-length advertisement option lets you “roll up” the /32s into a larger prefix.
+                        format: int32
+                        minimum: 1
+                        type: integer
+                      aggregationLengthV6:
+                        default: 128
+                        description: Optional, defaults to 128 (i.e. no aggregation) if not specified.
+                        format: int32
+                        type: integer
+                      communities:
+                        description: BGP communities to be associated with the given advertisement.
+                        items:
+                          type: string
+                        type: array
+                      localPref:
+                        description: BGP LOCAL_PREF attribute which is used by BGP best path algorithm, Path with higher localpref is preferred over one with lower localpref.
+                        format: int32
+                        type: integer
+                    type: object
+                  type: array
+                protocol:
+                  description: Protocol can be used to select how the announcement is done.
+                  enum:
+                    - layer2
+                    - bgp
                   type: string
-                type: array
-              autoAssign:
-                default: true
-                description: AutoAssign flag used to prevent MetallB from automatic
-                  allocation for a pool.
-                type: boolean
-              bgpAdvertisements:
-                description: Drives how an IP allocated from this pool should translated
-                  into BGP announcements.
-                items:
-                  properties:
-                    aggregationLength:
-                      default: 32
-                      description: The aggregation-length advertisement option lets
-                        you “roll up” the /32s into a larger prefix.
-                      format: int32
-                      minimum: 1
-                      type: integer
-                    aggregationLengthV6:
-                      default: 128
-                      description: Optional, defaults to 128 (i.e. no aggregation)
-                        if not specified.
-                      format: int32
-                      type: integer
-                    communities:
-                      description: BGP communities to be associated with the given
-                        advertisement.
-                      items:
-                        type: string
-                      type: array
-                    localPref:
-                      description: BGP LOCAL_PREF attribute which is used by BGP best
-                        path algorithm, Path with higher localpref is preferred over
-                        one with lower localpref.
-                      format: int32
-                      type: integer
-                  type: object
-                type: array
-              protocol:
-                description: Protocol can be used to select how the announcement is
-                  done.
-                enum:
-                - layer2
-                - bgp
-                type: string
-            required:
-            - addresses
-            - protocol
-            type: object
-          status:
-            description: AddressPoolStatus defines the observed state of AddressPool.
-            type: object
-        required:
-        - spec
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+              required:
+                - addresses
+                - protocol
+              type: object
+            status:
+              description: AddressPoolStatus defines the observed state of AddressPool.
+              type: object
+          required:
+            - spec
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: bfdprofiles.metallb.io
 spec:
@@ -227,95 +189,86 @@ spec:
     singular: bfdprofile
   scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: BFDProfile represents the settings of the bfd session that can
-          be optionally associated with a BGP session.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: BFDProfileSpec defines the desired state of BFDProfile.
-            properties:
-              detectMultiplier:
-                description: Configures the detection multiplier to determine packet
-                  loss. The remote transmission interval will be multiplied by this
-                  value to determine the connection loss detection timer.
-                format: int32
-                maximum: 255
-                minimum: 2
-                type: integer
-              echoInterval:
-                description: Configures the minimal echo receive transmission interval
-                  that this system is capable of handling in milliseconds. Defaults
-                  to 50ms
-                format: int32
-                maximum: 60000
-                minimum: 10
-                type: integer
-              echoMode:
-                description: Enables or disables the echo transmission mode. This
-                  mode is disabled by default, and not supported on multi hops setups.
-                type: boolean
-              minimumTtl:
-                description: 'For multi hop sessions only: configure the minimum expected
-                  TTL for an incoming BFD control packet.'
-                format: int32
-                maximum: 254
-                minimum: 1
-                type: integer
-              passiveMode:
-                description: 'Mark session as passive: a passive session will not
-                  attempt to start the connection and will wait for control packets
-                  from peer before it begins replying.'
-                type: boolean
-              receiveInterval:
-                description: The minimum interval that this system is capable of receiving
-                  control packets in milliseconds. Defaults to 300ms.
-                format: int32
-                maximum: 60000
-                minimum: 10
-                type: integer
-              transmitInterval:
-                description: The minimum transmission interval (less jitter) that
-                  this system wants to use to send BFD control packets in milliseconds.
-                  Defaults to 300ms
-                format: int32
-                maximum: 60000
-                minimum: 10
-                type: integer
-            type: object
-          status:
-            description: BFDProfileStatus defines the observed state of BFDProfile.
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+    - additionalPrinterColumns:
+        - jsonPath: .spec.passiveMode
+          name: Passive Mode
+          type: boolean
+        - jsonPath: .spec.transmitInterval
+          name: Transmit Interval
+          type: integer
+        - jsonPath: .spec.receiveInterval
+          name: Receive Interval
+          type: integer
+        - jsonPath: .spec.detectMultiplier
+          name: Multiplier
+          type: integer
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: BFDProfile represents the settings of the bfd session that can be optionally associated with a BGP session.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: BFDProfileSpec defines the desired state of BFDProfile.
+              properties:
+                detectMultiplier:
+                  description: Configures the detection multiplier to determine packet loss. The remote transmission interval will be multiplied by this value to determine the connection loss detection timer.
+                  format: int32
+                  maximum: 255
+                  minimum: 2
+                  type: integer
+                echoInterval:
+                  description: Configures the minimal echo receive transmission interval that this system is capable of handling in milliseconds. Defaults to 50ms
+                  format: int32
+                  maximum: 60000
+                  minimum: 10
+                  type: integer
+                echoMode:
+                  description: Enables or disables the echo transmission mode. This mode is disabled by default, and not supported on multi hops setups.
+                  type: boolean
+                minimumTtl:
+                  description: 'For multi hop sessions only: configure the minimum expected TTL for an incoming BFD control packet.'
+                  format: int32
+                  maximum: 254
+                  minimum: 1
+                  type: integer
+                passiveMode:
+                  description: 'Mark session as passive: a passive session will not attempt to start the connection and will wait for control packets from peer before it begins replying.'
+                  type: boolean
+                receiveInterval:
+                  description: The minimum interval that this system is capable of receiving control packets in milliseconds. Defaults to 300ms.
+                  format: int32
+                  maximum: 60000
+                  minimum: 10
+                  type: integer
+                transmitInterval:
+                  description: The minimum transmission interval (less jitter) that this system wants to use to send BFD control packets in milliseconds. Defaults to 300ms
+                  format: int32
+                  maximum: 60000
+                  minimum: 10
+                  type: integer
+              type: object
+            status:
+              description: BFDProfileStatus defines the observed state of BFDProfile.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: bgpadvertisements.metallb.io
 spec:
@@ -327,907 +280,747 @@ spec:
     singular: bgpadvertisement
   scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: BGPAdvertisement allows to advertise the IPs coming from the
-          selected IPAddressPools via BGP, setting the parameters of the BGP Advertisement.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement.
-            properties:
-              aggregationLength:
-                default: 32
-                description: The aggregation-length advertisement option lets you
-                  “roll up” the /32s into a larger prefix. Defaults to 32. Works for
-                  IPv4 addresses.
-                format: int32
-                minimum: 1
-                type: integer
-              aggregationLengthV6:
-                default: 128
-                description: The aggregation-length advertisement option lets you
-                  “roll up” the /128s into a larger prefix. Defaults to 128. Works
-                  for IPv6 addresses.
-                format: int32
-                type: integer
-              communities:
-                description: The BGP communities to be associated with the announcement.
-                  Each item can be a community of the form 1234:1234 or the name of
-                  an alias defined in the Community CRD.
-                items:
-                  type: string
-                type: array
-              ipAddressPoolSelectors:
-                description: A selector for the IPAddressPools which would get advertised
-                  via this advertisement. If no IPAddressPool is selected by this
-                  or by the list, the advertisement is applied to all the IPAddressPools.
-                items:
-                  description: A label selector is a label query over a set of resources.
-                    The result of matchLabels and matchExpressions are ANDed. An empty
-                    label selector matches all objects. A null label selector matches
-                    no objects.
-                  properties:
-                    matchExpressions:
-                      description: matchExpressions is a list of label selector requirements.
-                        The requirements are ANDed.
-                      items:
-                        description: A label selector requirement is a selector that
-                          contains values, a key, and an operator that relates the
-                          key and values.
-                        properties:
-                          key:
-                            description: key is the label key that the selector applies
-                              to.
-                            type: string
-                          operator:
-                            description: operator represents a key's relationship
-                              to a set of values. Valid operators are In, NotIn, Exists
-                              and DoesNotExist.
-                            type: string
-                          values:
-                            description: values is an array of string values. If the
-                              operator is In or NotIn, the values array must be non-empty.
-                              If the operator is Exists or DoesNotExist, the values
-                              array must be empty. This array is replaced during a
-                              strategic merge patch.
-                            items:
+    - additionalPrinterColumns:
+        - jsonPath: .spec.ipAddressPools
+          name: IPAddressPools
+          type: string
+        - jsonPath: .spec.ipAddressPoolSelectors
+          name: IPAddressPool Selectors
+          type: string
+        - jsonPath: .spec.peers
+          name: Peers
+          type: string
+        - jsonPath: .spec.nodeSelectors
+          name: Node Selectors
+          priority: 10
+          type: string
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: BGPAdvertisement allows to advertise the IPs coming from the selected IPAddressPools via BGP, setting the parameters of the BGP Advertisement.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: BGPAdvertisementSpec defines the desired state of BGPAdvertisement.
+              properties:
+                aggregationLength:
+                  default: 32
+                  description: The aggregation-length advertisement option lets you “roll up” the /32s into a larger prefix. Defaults to 32. Works for IPv4 addresses.
+                  format: int32
+                  minimum: 1
+                  type: integer
+                aggregationLengthV6:
+                  default: 128
+                  description: The aggregation-length advertisement option lets you “roll up” the /128s into a larger prefix. Defaults to 128. Works for IPv6 addresses.
+                  format: int32
+                  type: integer
+                communities:
+                  description: The BGP communities to be associated with the announcement. Each item can be a standard community of the form 1234:1234, a large community of the form large:1234:1234:1234 or the name of an alias defined in the Community CRD.
+                  items:
+                    type: string
+                  type: array
+                ipAddressPoolSelectors:
+                  description: A selector for the IPAddressPools which would get advertised via this advertisement. If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools.
+                  items:
+                    description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                    properties:
+                      matchExpressions:
+                        description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                        items:
+                          description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                          properties:
+                            key:
+                              description: key is the label key that the selector applies to.
                               type: string
-                            type: array
-                        required:
-                        - key
-                        - operator
+                            operator:
+                              description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                              type: string
+                            values:
+                              description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                              items:
+                                type: string
+                              type: array
+                          required:
+                            - key
+                            - operator
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
+                        description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
                         type: object
-                      type: array
-                    matchLabels:
-                      additionalProperties:
-                        type: string
-                      description: matchLabels is a map of {key,value} pairs. A single
-                        {key,value} in the matchLabels map is equivalent to an element
-                        of matchExpressions, whose key field is "key", the operator
-                        is "In", and the values array contains only "value". The requirements
-                        are ANDed.
-                      type: object
-                  type: object
-                type: array
-              ipAddressPools:
-                description: The list of IPAddressPools to advertise via this advertisement,
-                  selected by name.
-                items:
-                  type: string
-                type: array
-              localPref:
-                description: The BGP LOCAL_PREF attribute which is used by BGP best
-                  path algorithm, Path with higher localpref is preferred over one
-                  with lower localpref.
-                format: int32
-                type: integer
-              nodeSelectors:
-                description: NodeSelectors allows to limit the nodes to announce as
-                  next hops for the LoadBalancer IP. When empty, all the nodes having  are
-                  announced as next hops.
-                items:
-                  description: A label selector is a label query over a set of resources.
-                    The result of matchLabels and matchExpressions are ANDed. An empty
-                    label selector matches all objects. A null label selector matches
-                    no objects.
-                  properties:
-                    matchExpressions:
-                      description: matchExpressions is a list of label selector requirements.
-                        The requirements are ANDed.
-                      items:
-                        description: A label selector requirement is a selector that
-                          contains values, a key, and an operator that relates the
-                          key and values.
-                        properties:
-                          key:
-                            description: key is the label key that the selector applies
-                              to.
-                            type: string
-                          operator:
-                            description: operator represents a key's relationship
-                              to a set of values. Valid operators are In, NotIn, Exists
-                              and DoesNotExist.
-                            type: string
-                          values:
-                            description: values is an array of string values. If the
-                              operator is In or NotIn, the values array must be non-empty.
-                              If the operator is Exists or DoesNotExist, the values
-                              array must be empty. This array is replaced during a
-                              strategic merge patch.
-                            items:
+                    type: object
+                    x-kubernetes-map-type: atomic
+                  type: array
+                ipAddressPools:
+                  description: The list of IPAddressPools to advertise via this advertisement, selected by name.
+                  items:
+                    type: string
+                  type: array
+                localPref:
+                  description: The BGP LOCAL_PREF attribute which is used by BGP best path algorithm, Path with higher localpref is preferred over one with lower localpref.
+                  format: int32
+                  type: integer
+                nodeSelectors:
+                  description: NodeSelectors allows to limit the nodes to announce as next hops for the LoadBalancer IP. When empty, all the nodes having  are announced as next hops.
+                  items:
+                    description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                    properties:
+                      matchExpressions:
+                        description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                        items:
+                          description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                          properties:
+                            key:
+                              description: key is the label key that the selector applies to.
                               type: string
-                            type: array
-                        required:
-                        - key
-                        - operator
+                            operator:
+                              description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                              type: string
+                            values:
+                              description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                              items:
+                                type: string
+                              type: array
+                          required:
+                            - key
+                            - operator
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
+                        description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
                         type: object
-                      type: array
-                    matchLabels:
-                      additionalProperties:
-                        type: string
-                      description: matchLabels is a map of {key,value} pairs. A single
-                        {key,value} in the matchLabels map is equivalent to an element
-                        of matchExpressions, whose key field is "key", the operator
-                        is "In", and the values array contains only "value". The requirements
-                        are ANDed.
-                      type: object
-                  type: object
-                type: array
-              peers:
-                description: Peers limits the bgppeer to advertise the ips of the
-                  selected pools to. When empty, the loadbalancer IP is announced
-                  to all the BGPPeers configured.
-                items:
-                  type: string
-                type: array
-            type: object
-          status:
-            description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement.
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+                    type: object
+                    x-kubernetes-map-type: atomic
+                  type: array
+                peers:
+                  description: Peers limits the bgppeer to advertise the ips of the selected pools to. When empty, the loadbalancer IP is announced to all the BGPPeers configured.
+                  items:
+                    type: string
+                  type: array
+              type: object
+            status:
+              description: BGPAdvertisementStatus defines the observed state of BGPAdvertisement.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
   name: bgppeers.metallb.io
 spec:
-  group: metallb.io
-  names:
-    kind: BGPPeer
-    listKind: BGPPeerList
-    plural: bgppeers
-    singular: bgppeer
-  scope: Namespaced
   conversion:
     strategy: Webhook
     webhook:
-      conversionReviewVersions: ["v1beta1", "v1beta2"]
       clientConfig:
-        # this is a valid pem format, otherwise the apiserver will reject the deletion of the crds
-        # with "unable to parse bytes as PEM block", The controller will patch it with the right content after it starts
         caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlGWlRDQ0EwMmdBd0lCQWdJVU5GRW1XcTM3MVpKdGkrMmlSQzk1WmpBV1MxZ3dEUVlKS29aSWh2Y05BUUVMDQpCUUF3UWpFTE1Ba0dBMVVFQmhNQ1dGZ3hGVEFUQmdOVkJBY01ERVJsWm1GMWJIUWdRMmwwZVRFY01Cb0dBMVVFDQpDZ3dUUkdWbVlYVnNkQ0JEYjIxd1lXNTVJRXgwWkRBZUZ3MHlNakEzTVRrd09UTXlNek5hRncweU1qQTRNVGd3DQpPVE15TXpOYU1FSXhDekFKQmdOVkJBWVRBbGhZTVJVd0V3WURWUVFIREF4RVpXWmhkV3gwSUVOcGRIa3hIREFhDQpCZ05WQkFvTUUwUmxabUYxYkhRZ1EyOXRjR0Z1ZVNCTWRHUXdnZ0lpTUEwR0NTcUdTSWIzRFFFQkFRVUFBNElDDQpEd0F3Z2dJS0FvSUNBUUNxVFpxMWZRcC9vYkdlenhES0o3OVB3Ny94azJwellualNzMlkzb1ZYSm5sRmM4YjVlDQpma2ZZQnY2bndscW1keW5PL2phWFBaQmRQSS82aFdOUDBkdVhadEtWU0NCUUpyZzEyOGNXb3F0MGNTN3pLb1VpDQpvcU1tQ0QvRXVBeFFNZjhRZDF2c1gvVllkZ0poVTZBRXJLZEpIaXpFOUJtUkNkTDBGMW1OVW55Rk82UnRtWFZUDQpidkxsTDVYeTc2R0FaQVBLOFB4aVlDa0NtbDdxN0VnTWNiOXlLWldCYmlxQ3VkTXE5TGJLNmdKNzF6YkZnSXV4DQo1L1pXK2JraTB2RlplWk9ZODUxb1psckFUNzJvMDI4NHNTWW9uN0pHZVZkY3NoUnh5R1VpSFpSTzdkaXZVTDVTDQpmM2JmSDFYbWY1ZDQzT0NWTWRuUUV2NWVaOG8zeWVLa3ZrbkZQUGVJMU9BbjdGbDlFRVNNR2dhOGFaSG1URSttDQpsLzlMSmdDYjBnQmtPT0M0WnV4bWh2aERKV1EzWnJCS3pMQlNUZXN0NWlLNVlwcXRWVVk2THRyRW9FelVTK1lsDQpwWndXY2VQWHlHeHM5ZURsR3lNVmQraW15Y3NTU1UvVno2Mmx6MnZCS21NTXBkYldDQWhud0RsRTVqU2dyMjRRDQp0eGNXLys2N3d5KzhuQlI3UXdqVTFITndVRjBzeERWdEwrZ1NHVERnSEVZSlhZelYvT05zMy94TkpoVFNPSkxNDQpoeXNVdyttaGdackdhbUdXcHVIVU1DUitvTWJzMTc1UkcrQjJnUFFHVytPTjJnUTRyOXN2b0ZBNHBBQm8xd1dLDQpRYjRhY3pmeVVscElBOVFoSmFsZEY3S3dPSHVlV3gwRUNrNXg0T2tvVDBvWVp0dzFiR0JjRGtaSmF3SURBUUFCDQpvMU13VVRBZEJnTlZIUTRFRmdRVW90UlNIUm9IWTEyRFZ4R0NCdEhpb1g2ZmVFQXdId1lEVlIwakJCZ3dGb0FVDQpvdFJTSFJvSFkxMkRWeEdDQnRIaW9YNmZlRUF3RHdZRFZSMFRBUUgvQkFVd0F3RUIvekFOQmdrcWhraUc5dzBCDQpBUXNGQUFPQ0FnRUFSbkpsWWRjMTFHd0VxWnh6RDF2R3BDR2pDN2VWTlQ3aVY1d3IybXlybHdPYi9aUWFEa0xYDQpvVStaOVVXT1VlSXJTdzUydDdmQUpvVVAwSm5iYkMveVIrU1lqUGhvUXNiVHduOTc2ZldBWTduM3FMOXhCd1Y0DQphek41OXNjeUp0dlhMeUtOL2N5ak1ReDRLajBIMFg0bWJ6bzVZNUtzWWtYVU0vOEFPdWZMcEd0S1NGVGgrSEFDDQpab1Q5YnZHS25adnNHd0tYZFF0Wnh0akhaUjVqK3U3ZGtQOTJBT051RFNabS8rWVV4b2tBK09JbzdSR3BwSHNXDQo1ZTdNY0FTVXRtb1FORXd6dVFoVkJaRWQ1OGtKYjUrV0VWbGNzanlXNnRTbzErZ25tTWNqR1BsMWgxR2hVbjV4DQpFY0lWRnBIWXM5YWo1NmpBSjk1MVQvZjhMaWxmTlVnanBLQ0c1bnl0SUt3emxhOHNtdGlPdm1UNEpYbXBwSkI2DQo4bmdHRVluVjUrUTYwWFJ2OEhSSGp1VG9CRHVhaERrVDA2R1JGODU1d09FR2V4bkZpMXZYWUxLVllWb1V2MXRKDQo4dVdUR1pwNllDSVJldlBqbzg5ZytWTlJSaVFYUThJd0dybXE5c0RoVTlqTjA0SjdVL1RvRDFpNHE3VnlsRUc5DQorV1VGNkNLaEdBeTJIaEhwVncyTGFoOS9lUzdZMUZ1YURrWmhPZG1laG1BOCtqdHNZamJadnR5Mm1SWlF0UUZzDQpUU1VUUjREbUR2bVVPRVRmeStpRHdzK2RkWXVNTnJGeVVYV2dkMnpBQU4ydVl1UHFGY2pRcFNPODFzVTJTU3R3DQoxVzAyeUtYOGJEYmZFdjBzbUh3UzliQnFlSGo5NEM1Mjg0YXpsdTBmaUdpTm1OUEM4ckJLRmhBPQ0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
         service:
+          name: webhook-service
           namespace: {{ .Release.Namespace }}
-          name: metallb-webhook-service
           path: /convert
+      conversionReviewVersions:
+        - v1beta1
+        - v1beta2
+  group: metallb.io
+  names:
+    kind: BGPPeer
+    listKind: BGPPeerList
+    plural: bgppeers
+    singular: bgppeer
+  scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: BGPPeer is the Schema for the peers API.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: BGPPeerSpec defines the desired state of Peer.
-            properties:
-              bfdProfile:
-                type: string
-              ebgpMultiHop:
-                description: EBGP peer is multi-hops away
-                type: boolean
-              holdTime:
-                description: Requested BGP hold time, per RFC4271.
-                type: string
-              keepaliveTime:
-                description: Requested BGP keepalive time, per RFC4271.
-                type: string
-              myASN:
-                description: AS number to use for the local end of the session.
-                format: int32
-                maximum: 4294967295
-                minimum: 0
-                type: integer
-              nodeSelectors:
-                description: Only connect to this peer on nodes that match one of
-                  these selectors.
-                items:
-                  properties:
-                    matchExpressions:
-                      items:
-                        properties:
-                          key:
-                            type: string
-                          operator:
-                            type: string
-                          values:
-                            items:
+    - additionalPrinterColumns:
+        - jsonPath: .spec.peerAddress
+          name: Address
+          type: string
+        - jsonPath: .spec.peerASN
+          name: ASN
+          type: string
+        - jsonPath: .spec.bfdProfile
+          name: BFD Profile
+          type: string
+        - jsonPath: .spec.ebgpMultiHop
+          name: Multi Hops
+          type: string
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: BGPPeer is the Schema for the peers API.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: BGPPeerSpec defines the desired state of Peer.
+              properties:
+                bfdProfile:
+                  type: string
+                ebgpMultiHop:
+                  description: EBGP peer is multi-hops away
+                  type: boolean
+                holdTime:
+                  description: Requested BGP hold time, per RFC4271.
+                  type: string
+                keepaliveTime:
+                  description: Requested BGP keepalive time, per RFC4271.
+                  type: string
+                myASN:
+                  description: AS number to use for the local end of the session.
+                  format: int32
+                  maximum: 4294967295
+                  minimum: 0
+                  type: integer
+                nodeSelectors:
+                  description: Only connect to this peer on nodes that match one of these selectors.
+                  items:
+                    properties:
+                      matchExpressions:
+                        items:
+                          properties:
+                            key:
                               type: string
-                            minItems: 1
-                            type: array
-                        required:
-                        - key
-                        - operator
-                        - values
+                            operator:
+                              type: string
+                            values:
+                              items:
+                                type: string
+                              minItems: 1
+                              type: array
+                          required:
+                            - key
+                            - operator
+                            - values
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
                         type: object
-                      type: array
-                    matchLabels:
-                      additionalProperties:
-                        type: string
-                      type: object
-                  type: object
-                type: array
-              password:
-                description: Authentication password for routers enforcing TCP MD5
-                  authenticated sessions
-                type: string
-              peerASN:
-                description: AS number to expect from the remote end of the session.
-                format: int32
-                maximum: 4294967295
-                minimum: 0
-                type: integer
-              peerAddress:
-                description: Address to dial when establishing the session.
-                type: string
-              peerPort:
-                description: Port to dial when establishing the session.
-                maximum: 16384
-                minimum: 0
-                type: integer
-              routerID:
-                description: BGP router ID to advertise to the peer
-                type: string
-              sourceAddress:
-                description: Source address to use when establishing the session.
-                type: string
-            required:
-            - myASN
-            - peerASN
-            - peerAddress
-            type: object
-          status:
-            description: BGPPeerStatus defines the observed state of Peer.
-            type: object
-        type: object
-    served: true
-    storage: false
-    subresources:
-      status: {}
-  - name: v1beta2
-    schema:
-      openAPIV3Schema:
-        description: BGPPeer is the Schema for the peers API.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: BGPPeerSpec defines the desired state of Peer.
-            properties:
-              bfdProfile:
-                description: The name of the BFD Profile to be used for the BFD session
-                  associated to the BGP session. If not set, the BFD session won't
-                  be set up.
-                type: string
-              ebgpMultiHop:
-                description: To set if the BGPPeer is multi-hops away. Needed for
-                  FRR mode only.
-                type: boolean
-              holdTime:
-                description: Requested BGP hold time, per RFC4271.
-                type: string
-              keepaliveTime:
-                description: Requested BGP keepalive time, per RFC4271.
-                type: string
-              myASN:
-                description: AS number to use for the local end of the session.
-                format: int32
-                maximum: 4294967295
-                minimum: 0
-                type: integer
-              nodeSelectors:
-                description: Only connect to this peer on nodes that match one of
-                  these selectors.
-                items:
-                  description: A label selector is a label query over a set of resources.
-                    The result of matchLabels and matchExpressions are ANDed. An empty
-                    label selector matches all objects. A null label selector matches
-                    no objects.
-                  properties:
-                    matchExpressions:
-                      description: matchExpressions is a list of label selector requirements.
-                        The requirements are ANDed.
-                      items:
-                        description: A label selector requirement is a selector that
-                          contains values, a key, and an operator that relates the
-                          key and values.
-                        properties:
-                          key:
-                            description: key is the label key that the selector applies
-                              to.
-                            type: string
-                          operator:
-                            description: operator represents a key's relationship
-                              to a set of values. Valid operators are In, NotIn, Exists
-                              and DoesNotExist.
-                            type: string
-                          values:
-                            description: values is an array of string values. If the
-                              operator is In or NotIn, the values array must be non-empty.
-                              If the operator is Exists or DoesNotExist, the values
-                              array must be empty. This array is replaced during a
-                              strategic merge patch.
-                            items:
+                    type: object
+                  type: array
+                password:
+                  description: Authentication password for routers enforcing TCP MD5 authenticated sessions
+                  type: string
+                peerASN:
+                  description: AS number to expect from the remote end of the session.
+                  format: int32
+                  maximum: 4294967295
+                  minimum: 0
+                  type: integer
+                peerAddress:
+                  description: Address to dial when establishing the session.
+                  type: string
+                peerPort:
+                  description: Port to dial when establishing the session.
+                  maximum: 16384
+                  minimum: 0
+                  type: integer
+                routerID:
+                  description: BGP router ID to advertise to the peer
+                  type: string
+                sourceAddress:
+                  description: Source address to use when establishing the session.
+                  type: string
+              required:
+                - myASN
+                - peerASN
+                - peerAddress
+              type: object
+            status:
+              description: BGPPeerStatus defines the observed state of Peer.
+              type: object
+          type: object
+      served: true
+      storage: false
+      subresources:
+        status: {}
+    - additionalPrinterColumns:
+        - jsonPath: .spec.peerAddress
+          name: Address
+          type: string
+        - jsonPath: .spec.peerASN
+          name: ASN
+          type: string
+        - jsonPath: .spec.bfdProfile
+          name: BFD Profile
+          type: string
+        - jsonPath: .spec.ebgpMultiHop
+          name: Multi Hops
+          type: string
+      name: v1beta2
+      schema:
+        openAPIV3Schema:
+          description: BGPPeer is the Schema for the peers API.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: BGPPeerSpec defines the desired state of Peer.
+              properties:
+                bfdProfile:
+                  description: The name of the BFD Profile to be used for the BFD session associated to the BGP session. If not set, the BFD session won't be set up.
+                  type: string
+                ebgpMultiHop:
+                  description: To set if the BGPPeer is multi-hops away. Needed for FRR mode only.
+                  type: boolean
+                holdTime:
+                  description: Requested BGP hold time, per RFC4271.
+                  type: string
+                keepaliveTime:
+                  description: Requested BGP keepalive time, per RFC4271.
+                  type: string
+                myASN:
+                  description: AS number to use for the local end of the session.
+                  format: int32
+                  maximum: 4294967295
+                  minimum: 0
+                  type: integer
+                nodeSelectors:
+                  description: Only connect to this peer on nodes that match one of these selectors.
+                  items:
+                    description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                    properties:
+                      matchExpressions:
+                        description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                        items:
+                          description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                          properties:
+                            key:
+                              description: key is the label key that the selector applies to.
                               type: string
-                            type: array
-                        required:
-                        - key
-                        - operator
+                            operator:
+                              description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                              type: string
+                            values:
+                              description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                              items:
+                                type: string
+                              type: array
+                          required:
+                            - key
+                            - operator
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
+                        description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
                         type: object
-                      type: array
-                    matchLabels:
-                      additionalProperties:
-                        type: string
-                      description: matchLabels is a map of {key,value} pairs. A single
-                        {key,value} in the matchLabels map is equivalent to an element
-                        of matchExpressions, whose key field is "key", the operator
-                        is "In", and the values array contains only "value". The requirements
-                        are ANDed.
-                      type: object
+                    type: object
+                    x-kubernetes-map-type: atomic
+                  type: array
+                password:
+                  description: Authentication password for routers enforcing TCP MD5 authenticated sessions
+                  type: string
+                passwordSecret:
+                  description: passwordSecret is name of the authentication secret for BGP Peer. the secret must be of type "kubernetes.io/basic-auth", and created in the same namespace as the MetalLB deployment. The password is stored in the secret as the key "password".
+                  properties:
+                    name:
+                      description: name is unique within a namespace to reference a secret resource.
+                      type: string
+                    namespace:
+                      description: namespace defines the space within which the secret name must be unique.
+                      type: string
                   type: object
-                type: array
-              password:
-                description: Authentication password for routers enforcing TCP MD5
-                  authenticated sessions
-                type: string
-              passwordSecret:
-                description: passwordSecret is name of the authentication secret for
-                  BGP Peer. the secret must be of type "kubernetes.io/basic-auth",
-                  and created in the same namespace as the MetalLB deployment. The
-                  password is stored in the secret as the key "password".
-                properties:
-                  name:
-                    description: Name is unique within a namespace to reference a
-                      secret resource.
-                    type: string
-                  namespace:
-                    description: Namespace defines the space within which the secret
-                      name must be unique.
-                    type: string
-                type: object
-              peerASN:
-                description: AS number to expect from the remote end of the session.
-                format: int32
-                maximum: 4294967295
-                minimum: 0
-                type: integer
-              peerAddress:
-                description: Address to dial when establishing the session.
-                type: string
-              peerPort:
-                default: 179
-                description: Port to dial when establishing the session.
-                maximum: 16384
-                minimum: 0
-                type: integer
-              routerID:
-                description: BGP router ID to advertise to the peer
-                type: string
-              sourceAddress:
-                description: Source address to use when establishing the session.
-                type: string
-              vrf:
-                description: To set if we want to peer with the BGPPeer using an interface
-                  belonging to a host vrf
-                type: string
-            required:
-            - myASN
-            - peerASN
-            - peerAddress
-            type: object
-          status:
-            description: BGPPeerStatus defines the observed state of Peer.
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
----
-
+                  x-kubernetes-map-type: atomic
+                peerASN:
+                  description: AS number to expect from the remote end of the session.
+                  format: int32
+                  maximum: 4294967295
+                  minimum: 0
+                  type: integer
+                peerAddress:
+                  description: Address to dial when establishing the session.
+                  type: string
+                peerPort:
+                  default: 179
+                  description: Port to dial when establishing the session.
+                  maximum: 16384
+                  minimum: 0
+                  type: integer
+                routerID:
+                  description: BGP router ID to advertise to the peer
+                  type: string
+                sourceAddress:
+                  description: Source address to use when establishing the session.
+                  type: string
+                vrf:
+                  description: To set if we want to peer with the BGPPeer using an interface belonging to a host vrf
+                  type: string
+              required:
+                - myASN
+                - peerASN
+                - peerAddress
+              type: object
+            status:
+              description: BGPPeerStatus defines the observed state of Peer.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
-  name: ipaddresspools.metallb.io
+  name: communities.metallb.io
 spec:
   group: metallb.io
   names:
-    kind: IPAddressPool
-    listKind: IPAddressPoolList
-    plural: ipaddresspools
-    singular: ipaddresspool
+    kind: Community
+    listKind: CommunityList
+    plural: communities
+    singular: community
   scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: IPAddressPool represents a pool of IP addresses that can be allocated
-          to LoadBalancer services.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: IPAddressPoolSpec defines the desired state of IPAddressPool.
-            properties:
-              addresses:
-                description: A list of IP address ranges over which MetalLB has authority.
-                  You can list multiple ranges in a single pool, they will all share
-                  the same settings. Each range can be either a CIDR prefix, or an
-                  explicit start-end range of IPs.
-                items:
-                  type: string
-                type: array
-              autoAssign:
-                default: true
-                description: AutoAssign flag used to prevent MetallB from automatic
-                  allocation for a pool.
-                type: boolean
-              avoidBuggyIPs:
-                default: false
-                description: AvoidBuggyIPs prevents addresses ending with .0 and .255
-                  to be used by a pool.
-                type: boolean
-              serviceAllocation:
-                description: AllocateTo makes ip pool allocation to specific namespace
-                  and/or service. The controller will use the pool with lowest value
-                  of priority in case of multiple matches. A pool with no priority
-                  set will be used only if the pools with priority can't be used.
-                  If multiple matching IPAddressPools are available it will check
-                  for the availability of IPs sorting the matching IPAddressPools
-                  by priority, starting from the highest to the lowest. If multiple
-                  IPAddressPools have the same priority, choice will be random.
-                properties:
-                  namespaceSelectors:
-                    description: NamespaceSelectors list of label selectors to select
-                      namespace(s) for ip pool, an alternative to using namespace
-                      list.
-                    items:
-                      description: A label selector is a label query over a set of
-                        resources. The result of matchLabels and matchExpressions
-                        are ANDed. An empty label selector matches all objects. A
-                        null label selector matches no objects.
-                      properties:
-                        matchExpressions:
-                          description: matchExpressions is a list of label selector
-                            requirements. The requirements are ANDed.
-                          items:
-                            description: A label selector requirement is a selector
-                              that contains values, a key, and an operator that relates
-                              the key and values.
-                            properties:
-                              key:
-                                description: key is the label key that the selector
-                                  applies to.
-                                type: string
-                              operator:
-                                description: operator represents a key's relationship
-                                  to a set of values. Valid operators are In, NotIn,
-                                  Exists and DoesNotExist.
-                                type: string
-                              values:
-                                description: values is an array of string values.
-                                  If the operator is In or NotIn, the values array
-                                  must be non-empty. If the operator is Exists or
-                                  DoesNotExist, the values array must be empty. This
-                                  array is replaced during a strategic merge patch.
-                                items:
-                                  type: string
-                                type: array
-                            required:
-                            - key
-                            - operator
-                            type: object
-                          type: array
-                        matchLabels:
-                          additionalProperties:
-                            type: string
-                          description: matchLabels is a map of {key,value} pairs.
-                            A single {key,value} in the matchLabels map is equivalent
-                            to an element of matchExpressions, whose key field is
-                            "key", the operator is "In", and the values array contains
-                            only "value". The requirements are ANDed.
-                          type: object
-                      type: object
-                    type: array
-                  namespaces:
-                    description: Namespaces list of namespace(s) on which ip pool
-                      can be attached.
-                    items:
-                      type: string
-                    type: array
-                  priority:
-                    description: Priority priority given for ip pool while ip allocation
-                      on a service.
-                    type: integer
-                  serviceSelectors:
-                    description: ServiceSelectors list of label selector to select
-                      service(s) for which ip pool can be used for ip allocation.
-                    items:
-                      description: A label selector is a label query over a set of
-                        resources. The result of matchLabels and matchExpressions
-                        are ANDed. An empty label selector matches all objects. A
-                        null label selector matches no objects.
-                      properties:
-                        matchExpressions:
-                          description: matchExpressions is a list of label selector
-                            requirements. The requirements are ANDed.
-                          items:
-                            description: A label selector requirement is a selector
-                              that contains values, a key, and an operator that relates
-                              the key and values.
-                            properties:
-                              key:
-                                description: key is the label key that the selector
-                                  applies to.
-                                type: string
-                              operator:
-                                description: operator represents a key's relationship
-                                  to a set of values. Valid operators are In, NotIn,
-                                  Exists and DoesNotExist.
-                                type: string
-                              values:
-                                description: values is an array of string values.
-                                  If the operator is In or NotIn, the values array
-                                  must be non-empty. If the operator is Exists or
-                                  DoesNotExist, the values array must be empty. This
-                                  array is replaced during a strategic merge patch.
-                                items:
-                                  type: string
-                                type: array
-                            required:
-                            - key
-                            - operator
-                            type: object
-                          type: array
-                        matchLabels:
-                          additionalProperties:
-                            type: string
-                          description: matchLabels is a map of {key,value} pairs.
-                            A single {key,value} in the matchLabels map is equivalent
-                            to an element of matchExpressions, whose key field is
-                            "key", the operator is "In", and the values array contains
-                            only "value". The requirements are ANDed.
-                          type: object
-                      type: object
-                    type: array
-                type: object
-            required:
-            - addresses
-            type: object
-          status:
-            description: IPAddressPoolStatus defines the observed state of IPAddressPool.
-            type: object
-        required:
-        - spec
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+    - name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: Community is a collection of aliases for communities. Users can define named aliases to be used in the BGPPeer CRD.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: CommunitySpec defines the desired state of Community.
+              properties:
+                communities:
+                  items:
+                    properties:
+                      name:
+                        description: The name of the alias for the community.
+                        type: string
+                      value:
+                        description: The BGP community value corresponding to the given name. Can be a standard community of the form 1234:1234 or a large community of the form large:1234:1234:1234.
+                        type: string
+                    type: object
+                  type: array
+              type: object
+            status:
+              description: CommunityStatus defines the observed state of Community.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
-  name: l2advertisements.metallb.io
+  name: ipaddresspools.metallb.io
 spec:
   group: metallb.io
   names:
-    kind: L2Advertisement
-    listKind: L2AdvertisementList
-    plural: l2advertisements
-    singular: l2advertisement
+    kind: IPAddressPool
+    listKind: IPAddressPoolList
+    plural: ipaddresspools
+    singular: ipaddresspool
   scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: L2Advertisement allows to advertise the LoadBalancer IPs provided
-          by the selected pools via L2.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: L2AdvertisementSpec defines the desired state of L2Advertisement.
-            properties:
-              interfaces:
-                description: A list of interfaces to announce from. The LB IP will
-                  be announced only from these interfaces. If the field is not set,
-                  we advertise from all the interfaces on the host.
-                items:
-                  type: string
-                type: array
-              ipAddressPoolSelectors:
-                description: A selector for the IPAddressPools which would get advertised
-                  via this advertisement. If no IPAddressPool is selected by this
-                  or by the list, the advertisement is applied to all the IPAddressPools.
-                items:
-                  description: A label selector is a label query over a set of resources.
-                    The result of matchLabels and matchExpressions are ANDed. An empty
-                    label selector matches all objects. A null label selector matches
-                    no objects.
+    - additionalPrinterColumns:
+        - jsonPath: .spec.autoAssign
+          name: Auto Assign
+          type: boolean
+        - jsonPath: .spec.avoidBuggyIPs
+          name: Avoid Buggy IPs
+          type: boolean
+        - jsonPath: .spec.addresses
+          name: Addresses
+          type: string
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: IPAddressPool represents a pool of IP addresses that can be allocated to LoadBalancer services.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: IPAddressPoolSpec defines the desired state of IPAddressPool.
+              properties:
+                addresses:
+                  description: A list of IP address ranges over which MetalLB has authority. You can list multiple ranges in a single pool, they will all share the same settings. Each range can be either a CIDR prefix, or an explicit start-end range of IPs.
+                  items:
+                    type: string
+                  type: array
+                autoAssign:
+                  default: true
+                  description: AutoAssign flag used to prevent MetallB from automatic allocation for a pool.
+                  type: boolean
+                avoidBuggyIPs:
+                  default: false
+                  description: AvoidBuggyIPs prevents addresses ending with .0 and .255 to be used by a pool.
+                  type: boolean
+                serviceAllocation:
+                  description: AllocateTo makes ip pool allocation to specific namespace and/or service. The controller will use the pool with lowest value of priority in case of multiple matches. A pool with no priority set will be used only if the pools with priority can't be used. If multiple matching IPAddressPools are available it will check for the availability of IPs sorting the matching IPAddressPools by priority, starting from the highest to the lowest. If multiple IPAddressPools have the same priority, choice will be random.
                   properties:
-                    matchExpressions:
-                      description: matchExpressions is a list of label selector requirements.
-                        The requirements are ANDed.
+                    namespaceSelectors:
+                      description: NamespaceSelectors list of label selectors to select namespace(s) for ip pool, an alternative to using namespace list.
                       items:
-                        description: A label selector requirement is a selector that
-                          contains values, a key, and an operator that relates the
-                          key and values.
+                        description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
                         properties:
-                          key:
-                            description: key is the label key that the selector applies
-                              to.
-                            type: string
-                          operator:
-                            description: operator represents a key's relationship
-                              to a set of values. Valid operators are In, NotIn, Exists
-                              and DoesNotExist.
-                            type: string
-                          values:
-                            description: values is an array of string values. If the
-                              operator is In or NotIn, the values array must be non-empty.
-                              If the operator is Exists or DoesNotExist, the values
-                              array must be empty. This array is replaced during a
-                              strategic merge patch.
+                          matchExpressions:
+                            description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
                             items:
-                              type: string
+                              description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                              properties:
+                                key:
+                                  description: key is the label key that the selector applies to.
+                                  type: string
+                                operator:
+                                  description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                                  type: string
+                                values:
+                                  description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                                  items:
+                                    type: string
+                                  type: array
+                              required:
+                                - key
+                                - operator
+                              type: object
                             type: array
-                        required:
-                        - key
-                        - operator
+                          matchLabels:
+                            additionalProperties:
+                              type: string
+                            description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                            type: object
                         type: object
+                        x-kubernetes-map-type: atomic
                       type: array
-                    matchLabels:
-                      additionalProperties:
+                    namespaces:
+                      description: Namespaces list of namespace(s) on which ip pool can be attached.
+                      items:
                         type: string
-                      description: matchLabels is a map of {key,value} pairs. A single
-                        {key,value} in the matchLabels map is equivalent to an element
-                        of matchExpressions, whose key field is "key", the operator
-                        is "In", and the values array contains only "value". The requirements
-                        are ANDed.
-                      type: object
-                  type: object
-                type: array
-              ipAddressPools:
-                description: The list of IPAddressPools to advertise via this advertisement,
-                  selected by name.
-                items:
-                  type: string
-                type: array
-              nodeSelectors:
-                description: NodeSelectors allows to limit the nodes to announce as
-                  next hops for the LoadBalancer IP. When empty, all the nodes having  are
-                  announced as next hops.
-                items:
-                  description: A label selector is a label query over a set of resources.
-                    The result of matchLabels and matchExpressions are ANDed. An empty
-                    label selector matches all objects. A null label selector matches
-                    no objects.
-                  properties:
-                    matchExpressions:
-                      description: matchExpressions is a list of label selector requirements.
-                        The requirements are ANDed.
+                      type: array
+                    priority:
+                      description: Priority priority given for ip pool while ip allocation on a service.
+                      type: integer
+                    serviceSelectors:
+                      description: ServiceSelectors list of label selector to select service(s) for which ip pool can be used for ip allocation.
                       items:
-                        description: A label selector requirement is a selector that
-                          contains values, a key, and an operator that relates the
-                          key and values.
+                        description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
                         properties:
-                          key:
-                            description: key is the label key that the selector applies
-                              to.
-                            type: string
-                          operator:
-                            description: operator represents a key's relationship
-                              to a set of values. Valid operators are In, NotIn, Exists
-                              and DoesNotExist.
-                            type: string
-                          values:
-                            description: values is an array of string values. If the
-                              operator is In or NotIn, the values array must be non-empty.
-                              If the operator is Exists or DoesNotExist, the values
-                              array must be empty. This array is replaced during a
-                              strategic merge patch.
+                          matchExpressions:
+                            description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
                             items:
-                              type: string
+                              description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                              properties:
+                                key:
+                                  description: key is the label key that the selector applies to.
+                                  type: string
+                                operator:
+                                  description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                                  type: string
+                                values:
+                                  description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                                  items:
+                                    type: string
+                                  type: array
+                              required:
+                                - key
+                                - operator
+                              type: object
                             type: array
-                        required:
-                        - key
-                        - operator
+                          matchLabels:
+                            additionalProperties:
+                              type: string
+                            description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                            type: object
                         type: object
+                        x-kubernetes-map-type: atomic
                       type: array
-                    matchLabels:
-                      additionalProperties:
-                        type: string
-                      description: matchLabels is a map of {key,value} pairs. A single
-                        {key,value} in the matchLabels map is equivalent to an element
-                        of matchExpressions, whose key field is "key", the operator
-                        is "In", and the values array contains only "value". The requirements
-                        are ANDed.
-                      type: object
                   type: object
-                type: array
-            type: object
-          status:
-            description: L2AdvertisementStatus defines the observed state of L2Advertisement.
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+              required:
+                - addresses
+              type: object
+            status:
+              description: IPAddressPoolStatus defines the observed state of IPAddressPool.
+              type: object
+          required:
+            - spec
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
 ---
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
 metadata:
   annotations:
-    controller-gen.kubebuilder.io/version: v0.7.0
+    controller-gen.kubebuilder.io/version: v0.11.1
   creationTimestamp: null
-  name: communities.metallb.io
+  name: l2advertisements.metallb.io
 spec:
   group: metallb.io
   names:
-    kind: Community
-    listKind: CommunityList
-    plural: communities
-    singular: community
+    kind: L2Advertisement
+    listKind: L2AdvertisementList
+    plural: l2advertisements
+    singular: l2advertisement
   scope: Namespaced
   versions:
-  - name: v1beta1
-    schema:
-      openAPIV3Schema:
-        description: Community is a collection of aliases for communities. Users can
-          define named aliases to be used in the BGPPeer CRD.
-        properties:
-          apiVersion:
-            description: 'APIVersion defines the versioned schema of this representation
-              of an object. Servers should convert recognized schemas to the latest
-              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
-            type: string
-          kind:
-            description: 'Kind is a string value representing the REST resource this
-              object represents. Servers may infer this from the endpoint the client
-              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
-            type: string
-          metadata:
-            type: object
-          spec:
-            description: CommunitySpec defines the desired state of Community.
-            properties:
-              communities:
-                items:
-                  properties:
-                    name:
-                      description: The name of the alias for the community.
-                      type: string
-                    value:
-                      description: The BGP community value corresponding to the given
-                        name.
-                      type: string
-                  type: object
-                type: array
-            type: object
-          status:
-            description: CommunityStatus defines the observed state of Community.
-            type: object
-        type: object
-    served: true
-    storage: true
-    subresources:
-      status: {}
-status:
-  acceptedNames:
-    kind: ""
-    plural: ""
-  conditions: []
-  storedVersions: []
+    - additionalPrinterColumns:
+        - jsonPath: .spec.ipAddressPools
+          name: IPAddressPools
+          type: string
+        - jsonPath: .spec.ipAddressPoolSelectors
+          name: IPAddressPool Selectors
+          type: string
+        - jsonPath: .spec.interfaces
+          name: Interfaces
+          type: string
+        - jsonPath: .spec.nodeSelectors
+          name: Node Selectors
+          priority: 10
+          type: string
+      name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: L2Advertisement allows to advertise the LoadBalancer IPs provided by the selected pools via L2.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: L2AdvertisementSpec defines the desired state of L2Advertisement.
+              properties:
+                interfaces:
+                  description: A list of interfaces to announce from. The LB IP will be announced only from these interfaces. If the field is not set, we advertise from all the interfaces on the host.
+                  items:
+                    type: string
+                  type: array
+                ipAddressPoolSelectors:
+                  description: A selector for the IPAddressPools which would get advertised via this advertisement. If no IPAddressPool is selected by this or by the list, the advertisement is applied to all the IPAddressPools.
+                  items:
+                    description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                    properties:
+                      matchExpressions:
+                        description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                        items:
+                          description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                          properties:
+                            key:
+                              description: key is the label key that the selector applies to.
+                              type: string
+                            operator:
+                              description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                              type: string
+                            values:
+                              description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                              items:
+                                type: string
+                              type: array
+                          required:
+                            - key
+                            - operator
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
+                        description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                        type: object
+                    type: object
+                    x-kubernetes-map-type: atomic
+                  type: array
+                ipAddressPools:
+                  description: The list of IPAddressPools to advertise via this advertisement, selected by name.
+                  items:
+                    type: string
+                  type: array
+                nodeSelectors:
+                  description: NodeSelectors allows to limit the nodes to announce as next hops for the LoadBalancer IP. When empty, all the nodes having  are announced as next hops.
+                  items:
+                    description: A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.
+                    properties:
+                      matchExpressions:
+                        description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                        items:
+                          description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                          properties:
+                            key:
+                              description: key is the label key that the selector applies to.
+                              type: string
+                            operator:
+                              description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                              type: string
+                            values:
+                              description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                              items:
+                                type: string
+                              type: array
+                          required:
+                            - key
+                            - operator
+                          type: object
+                        type: array
+                      matchLabels:
+                        additionalProperties:
+                          type: string
+                        description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                        type: object
+                    type: object
+                    x-kubernetes-map-type: atomic
+                  type: array
+              type: object
+            status:
+              description: L2AdvertisementStatus defines the observed state of L2Advertisement.
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
diff --git a/configmaptocrs/Dockerfile b/configmaptocrs/Dockerfile
index 57b408f3..b3092070 100644
--- a/configmaptocrs/Dockerfile
+++ b/configmaptocrs/Dockerfile
@@ -1,6 +1,6 @@
 # syntax=docker/dockerfile:1.2
 
-FROM --platform=$BUILDPLATFORM docker.io/golang:1.19.5 AS builder
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.20.10 AS builder
 ARG GIT_COMMIT=dev
 ARG GIT_BRANCH=dev
 WORKDIR $GOPATH/go.universe.tf/metallb
diff --git a/controller/Dockerfile b/controller/Dockerfile
index f1f1a30e..bb0a68d2 100644
--- a/controller/Dockerfile
+++ b/controller/Dockerfile
@@ -1,6 +1,6 @@
 # syntax=docker/dockerfile:1.2
 
-FROM --platform=$BUILDPLATFORM docker.io/golang:1.19.5 AS builder
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.20.10 AS builder
 ARG GIT_COMMIT=dev
 ARG GIT_BRANCH=dev
 
diff --git a/controller/main.go b/controller/main.go
index 3385b1b0..2f1ff2be 100644
--- a/controller/main.go
+++ b/controller/main.go
@@ -74,22 +74,29 @@ func (c *controller) SetBalancer(l log.Logger, name string, svcRo *v1.Service, _
 	// a reason.
 	svc := svcRo.DeepCopy()
 	syncStateRes := controllers.SyncStateSuccess
-	wasAllocated := c.isServiceAllocated(name)
+
+	prevIPs := c.ips.IPs(name)
+
 	if c.convergeBalancer(l, name, svc) != nil {
 		syncStateRes = controllers.SyncStateErrorNoRetry
 	}
 
-	if wasAllocated && !c.isServiceAllocated(name) { // convergeBalancer may deallocate our service and this means it did it.
-		// if the service was deallocated, it may have have left room
-		// for another one, so we reprocess
-		level.Info(l).Log("event", "serviceUpdated", "msg", "removed loadbalancer from service, services will be reprocessed")
-		syncStateRes = controllers.SyncStateReprocessAll
-	}
 	if reflect.DeepEqual(svcRo, svc) {
 		level.Debug(l).Log("event", "noChange", "msg", "service converged, no change")
 		return syncStateRes
 	}
 
+	if len(prevIPs) != 0 && !c.isServiceAllocated(name) {
+		// Only reprocess all if the previous IP(s) are still contained within a pool.
+		if c.ips.PoolForIP(prevIPs) != nil {
+			// convergeBalancer may deallocate our service and this means it did it.
+			// if the service was deallocated, it may have left room
+			// for another one, so we reprocess
+			level.Info(l).Log("event", "serviceUpdated", "msg", "removed loadbalancer from service, services will be reprocessed")
+			syncStateRes = controllers.SyncStateReprocessAll
+		}
+	}
+
 	toWrite := svcRo.DeepCopy()
 	if !reflect.DeepEqual(svcRo.Status, svc.Status) {
 		toWrite.Status = svc.Status
@@ -121,11 +128,9 @@ func (c *controller) SetPools(l log.Logger, pools *config.Pools) controllers.Syn
 		return controllers.SyncStateErrorNoRetry
 	}
 
-	if err := c.ips.SetPools(pools); err != nil {
-		level.Error(l).Log("op", "setConfig", "error", err, "msg", "applying new configuration failed")
-		return controllers.SyncStateError
-	}
+	c.ips.SetPools(pools)
 	c.pools = pools
+
 	return controllers.SyncStateReprocessAll
 }
 
@@ -143,6 +148,7 @@ func main() {
 		certServiceName     = flag.String("cert-service-name", "webhook-service", "The service name used to generate the TLS cert's hostname")
 		loadBalancerClass   = flag.String("lb-class", "", "load balancer class. When enabled, metallb will handle only services whose spec.loadBalancerClass matches the given lb class")
 		webhookMode         = flag.String("webhook-mode", "enabled", "webhook mode: can be enabled, disabled or only webhook if we want the controller to act as webhook endpoint only")
+		webhookSecretName   = flag.String("webhook-secret", "webhook-server-cert", "webhook secret: the name of webhook secret, default is webhook-server-cert")
 	)
 	flag.Parse()
 
@@ -189,6 +195,7 @@ func main() {
 		ValidateConfig:      validation,
 		EnableWebhook:       true,
 		DisableCertRotation: *disableCertRotation,
+		WebhookSecretName:   *webhookSecretName,
 		CertDir:             *certDir,
 		CertServiceName:     *certServiceName,
 		LoadBalancerClass:   *loadBalancerClass,
diff --git a/controller/service.go b/controller/service.go
index b48393ba..c22a4c88 100644
--- a/controller/service.go
+++ b/controller/service.go
@@ -30,9 +30,10 @@ import (
 )
 
 const (
-	annotationAddressPool        = "metallb.universe.tf/address-pool"
-	annotationLoadBalancerIPs    = "metallb.universe.tf/loadBalancerIPs"
-	annotationIPAllocateFromPool = "metallb.universe.tf/ip-allocated-from-pool"
+	AnnotationPrefix             = "metallb.universe.tf"
+	AnnotationAddressPool        = AnnotationPrefix + "/" + "address-pool"
+	AnnotationLoadBalancerIPs    = AnnotationPrefix + "/" + "loadBalancerIPs"
+	AnnotationIPAllocateFromPool = AnnotationPrefix + "/" + "ip-allocated-from-pool"
 )
 
 var ErrConverge = fmt.Errorf("failed to converge")
@@ -50,6 +51,13 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 		return nil
 	}
 
+	// Return if pools are empty.
+	if len(c.pools.ByName) == 0 {
+		level.Debug(l).Log("event", "clearAssignment", "reason", "noConfig", "msg", "pools are empty")
+		c.clearServiceState(key, svc)
+		return ErrConverge
+	}
+
 	// If the ClusterIPs is malformed or not set we can't determine the
 	// ipFamily to use.
 	if len(svc.Spec.ClusterIPs) == 0 && svc.Spec.ClusterIP == "" {
@@ -106,7 +114,7 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 		// The user might also have changed the pool annotation, and
 		// requested a different pool than the one that is currently
 		// allocated.
-		desiredPool := svc.Annotations[annotationAddressPool]
+		desiredPool := svc.Annotations[AnnotationAddressPool]
 		if len(lbIPs) != 0 && desiredPool != "" && c.ips.Pool(key) != desiredPool {
 			level.Info(l).Log("event", "clearAssignment", "reason", "differentPoolRequested", "msg", "user requested a different pool than the one currently assigned")
 			c.clearServiceState(key, svc)
@@ -168,7 +176,7 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 	if svc.Annotations == nil {
 		svc.Annotations = make(map[string]string)
 	}
-	svc.Annotations[annotationIPAllocateFromPool] = pool
+	svc.Annotations[AnnotationIPAllocateFromPool] = pool
 
 	return nil
 }
@@ -177,7 +185,7 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 // this controller.
 func (c *controller) clearServiceState(key string, svc *v1.Service) {
 	c.ips.Unassign(key)
-	delete(svc.Annotations, annotationIPAllocateFromPool)
+	delete(svc.Annotations, AnnotationIPAllocateFromPool)
 	svc.Status.LoadBalancer = v1.LoadBalancerStatus{}
 }
 
@@ -197,7 +205,7 @@ func (c *controller) allocateIPs(key string, svc *v1.Service) ([]net.IP, error)
 		return nil, err
 	}
 
-	desiredPool := svc.Annotations[annotationAddressPool]
+	desiredPool := svc.Annotations[AnnotationAddressPool]
 
 	// If the user asked for a specific IPs, try that.
 	if len(desiredLbIPs) > 0 {
@@ -236,12 +244,12 @@ func (c *controller) isServiceAllocated(key string) bool {
 
 func getDesiredLbIPs(svc *v1.Service) ([]net.IP, ipfamily.Family, error) {
 	var desiredLbIPs []net.IP
-	desiredLbIPsStr := svc.Annotations[annotationLoadBalancerIPs]
+	desiredLbIPsStr := svc.Annotations[AnnotationLoadBalancerIPs]
 
 	if desiredLbIPsStr == "" && svc.Spec.LoadBalancerIP == "" {
 		return nil, "", nil
 	} else if desiredLbIPsStr != "" && svc.Spec.LoadBalancerIP != "" {
-		return nil, "", fmt.Errorf("service can not have both %s and svc.Spec.LoadBalancerIP", annotationLoadBalancerIPs)
+		return nil, "", fmt.Errorf("service can not have both %s and svc.Spec.LoadBalancerIP", AnnotationLoadBalancerIPs)
 	}
 
 	if desiredLbIPsStr != "" {
@@ -249,7 +257,7 @@ func getDesiredLbIPs(svc *v1.Service) ([]net.IP, ipfamily.Family, error) {
 		for _, desiredLbIPStr := range desiredLbIPsSlice {
 			desiredLbIP := net.ParseIP(strings.TrimSpace(desiredLbIPStr))
 			if desiredLbIP == nil {
-				return nil, "", fmt.Errorf("invalid %s: %q", annotationLoadBalancerIPs, desiredLbIPsStr)
+				return nil, "", fmt.Errorf("invalid %s: %q", AnnotationLoadBalancerIPs, desiredLbIPsStr)
 			}
 			desiredLbIPs = append(desiredLbIPs, desiredLbIP)
 		}
diff --git a/go.mod b/go.mod
index 04778ccf..90a7c474 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
 module go.universe.tf/metallb
 
-go 1.19
+go 1.20
 
 require (
 	github.com/davecgh/go-spew v1.1.1
diff --git a/internal/allocator/allocator.go b/internal/allocator/allocator.go
index 167860d2..4fb3ff21 100644
--- a/internal/allocator/allocator.go
+++ b/internal/allocator/allocator.go
@@ -66,18 +66,7 @@ func New() *Allocator {
 }
 
 // SetPools updates the set of address pools that the allocator owns.
-func (a *Allocator) SetPools(pools *config.Pools) error {
-	// All the fancy sharing stuff only influences how new allocations
-	// can be created. For changing the underlying configuration, the
-	// only question we have to answer is: can we fit all allocated
-	// IPs into address pools under the new configuration?
-	for svc, alloc := range a.allocated {
-		pool := poolFor(pools.ByName, alloc.ips)
-		if pool == nil {
-			return fmt.Errorf("new config not compatible with assigned IPs: service %q cannot own %q under new config", svc, alloc.ips)
-		}
-	}
-
+func (a *Allocator) SetPools(pools *config.Pools) {
 	for n := range a.pools.ByName {
 		if pools.ByName[n] == nil {
 			stats.poolCapacity.DeleteLabelValues(n)
@@ -92,7 +81,8 @@ func (a *Allocator) SetPools(pools *config.Pools) error {
 	for svc, alloc := range a.allocated {
 		pool := poolFor(a.pools.ByName, alloc.ips)
 		if pool == nil {
-			return fmt.Errorf("can't retrieve new pool for assigned IPs: service %q cannot own %q under new config", svc, alloc.ips)
+			a.Unassign(svc)
+			continue
 		}
 		if pool.Name != alloc.pool {
 			a.Unassign(svc)
@@ -108,8 +98,6 @@ func (a *Allocator) SetPools(pools *config.Pools) error {
 		stats.poolCapacity.WithLabelValues(n).Set(float64(poolCount(p)))
 		stats.poolActive.WithLabelValues(n).Set(float64(len(a.poolIPsInUse[n])))
 	}
-
-	return nil
 }
 
 // assign unconditionally updates internal state to reflect svc's
@@ -355,6 +343,19 @@ func (a *Allocator) Pool(svc string) string {
 	return ""
 }
 
+// IPs returns the allocated IPs of a service.
+func (a *Allocator) IPs(svc string) []net.IP {
+	if alloc := a.allocated[svc]; alloc != nil {
+		return alloc.ips
+	}
+	return nil
+}
+
+// PoolForIP returns the pool structure associated with an IP.
+func (a *Allocator) PoolForIP(ips []net.IP) *config.Pool {
+	return poolFor(a.pools.ByName, ips)
+}
+
 func sortPools(pools []*config.Pool) {
 	// A lower value for pool priority equals a higher priority and sort
 	// pools from higher to low priority. when no priority (0) set on
diff --git a/internal/bgp/frr/config.go b/internal/bgp/frr/config.go
index 119edfb4..46edd8ad 100644
--- a/internal/bgp/frr/config.go
+++ b/internal/bgp/frr/config.go
@@ -110,17 +110,14 @@ func neighborName(peerAddr string, ASN uint32, vrfName string) string {
 // templateConfig uses the template library to template
 // 'globalConfigTemplate' using 'data'.
 func templateConfig(data interface{}) (string, error) {
-	i := 0
-	currentCounterName := ""
+	counterMap := map[string]int{}
 	t, err := template.New("frr.tmpl").Funcs(
 		template.FuncMap{
 			"counter": func(counterName string) int {
-				if currentCounterName != counterName {
-					currentCounterName = counterName
-					i = 0
-				}
-				i++
-				return i
+				counter := counterMap[counterName]
+				counter++
+				counterMap[counterName] = counter
+				return counter
 			},
 			"frrIPFamily": func(ipFamily ipfamily.Family) string {
 				if ipFamily == "ipv6" {
diff --git a/internal/bgp/frr/templates/filters.tmpl b/internal/bgp/frr/templates/filters.tmpl
index 1e26f0b4..ce3dd86e 100644
--- a/internal/bgp/frr/templates/filters.tmpl
+++ b/internal/bgp/frr/templates/filters.tmpl
@@ -1,5 +1,6 @@
 {{- define "localpreffilter" -}}
-{{frrIPFamily .advertisement.IPFamily}} prefix-list {{localPrefPrefixList .neighbor .advertisement.LocalPref}} permit {{.advertisement.Prefix}}
+{{$localPrefixListName :=localPrefPrefixList .neighbor .advertisement.LocalPref}}
+{{frrIPFamily .advertisement.IPFamily}} prefix-list {{$localPrefixListName}} seq {{counter $localPrefixListName}} permit {{.advertisement.Prefix}}
 route-map {{.neighbor.ID}}-out permit {{counter .neighbor.ID}}
   match {{frrIPFamily .advertisement.IPFamily}} address prefix-list {{localPrefPrefixList .neighbor .advertisement.LocalPref}}
   set local-preference {{.advertisement.LocalPref}}
@@ -7,7 +8,8 @@ route-map {{.neighbor.ID}}-out permit {{counter .neighbor.ID}}
 {{- end -}}
 
 {{- define "communityfilter" -}}
-{{frrIPFamily .advertisement.IPFamily}} prefix-list {{communityPrefixList .neighbor .community}} permit {{.advertisement.Prefix}}
+{{$communityPrefixlistName :=communityPrefixList .neighbor .community}}
+{{frrIPFamily .advertisement.IPFamily}} prefix-list {{$communityPrefixlistName}} seq {{counter $communityPrefixlistName}} permit {{.advertisement.Prefix}}
 route-map {{.neighbor.ID}}-out permit {{counter .neighbor.ID}}
   match {{frrIPFamily .advertisement.IPFamily}} address prefix-list {{communityPrefixList .neighbor .community}}
   set community {{.community}} additive
@@ -43,20 +45,24 @@ route-map {{.neighbor.ID}}-in deny 20
 {{template "largecommunityfilter" dict "advertisement" $a "neighbor" $.neighbor "largecommunity" $lc}}
 {{- end }}
 {{/* this advertisement is allowed to the specific neighbor  */}}
-{{frrIPFamily $a.IPFamily}} prefix-list {{allowedPrefixList $.neighbor}} permit {{$a.Prefix}}
+{{$plistName:=allowedPrefixList $.neighbor}}
+ {{frrIPFamily $a.IPFamily}} prefix-list {{$plistName}} seq {{counter $plistName}} permit {{$a.Prefix}}
 {{- end }}
 
-route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
-  match ip address prefix-list {{allowedPrefixList $.neighbor}}
-route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
-  match ipv6 address prefix-list {{allowedPrefixList $.neighbor}}
 
 {{/* If the neighbor does not have an advertisement, we need to add a prefix to deny
 for when we have a prefix but a given peer is not selected for any prefixes */}}
+{{$plistName:=allowedPrefixList $.neighbor}}
 {{- if not .neighbor.HasV4Advertisements}}
-ip prefix-list {{allowedPrefixList $.neighbor }} deny any
+ip prefix-list {{$plistName}} seq {{counter $plistName}} deny any
 {{- end }}
 {{- if not .neighbor.HasV6Advertisements}}
-ipv6 prefix-list {{allowedPrefixList $.neighbor}} deny any
-{{- end -}}
+ipv6 prefix-list {{$plistName}} seq {{counter $plistName}} deny any
+{{- end }}
+
+route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
+  match ip address prefix-list {{allowedPrefixList $.neighbor}}
+route-map {{$.neighbor.ID}}-out permit {{counter $.neighbor.ID}}
+  match ipv6 address prefix-list {{allowedPrefixList $.neighbor}}
+
 {{- end -}}
diff --git a/internal/k8s/controllers/service_controller.go b/internal/k8s/controllers/service_controller.go
index 744d9a33..7fa83bbf 100644
--- a/internal/k8s/controllers/service_controller.go
+++ b/internal/k8s/controllers/service_controller.go
@@ -47,6 +47,10 @@ type ServiceReconciler struct {
 	Endpoints         NeedEndPoints
 	LoadBalancerClass string
 	Reload            chan event.GenericEvent
+	// initialLoadPerformed is set after the first time we call reprocessAll.
+	// This is required because we want the first time we load the services to follow the assigned first, non assigned later order.
+	// This allows avoiding to have services with already assigned IP to get their IP stolen by other services.
+	initialLoadPerformed bool
 }
 
 func (r *ServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -63,6 +67,11 @@ func (r *ServiceReconciler) reconcileService(ctx context.Context, req ctrl.Reque
 
 	var service *v1.Service
 
+	if !r.initialLoadPerformed {
+		level.Debug(r.Logger).Log("controller", "ServiceReconciler", "message", "filtered service, still waiting for the initial load to be performed")
+		return ctrl.Result{}, nil
+	}
+
 	service, err := r.serviceFor(ctx, req.NamespacedName)
 	if err != nil {
 		level.Error(r.Logger).Log("controller", "ServiceReconciler", "message", "failed to get service", "service", req.NamespacedName, "error", err)
diff --git a/internal/k8s/controllers/service_controller_reload.go b/internal/k8s/controllers/service_controller_reload.go
index 7c2aeb9a..e679e85a 100644
--- a/internal/k8s/controllers/service_controller_reload.go
+++ b/internal/k8s/controllers/service_controller_reload.go
@@ -18,6 +18,7 @@ package controllers
 
 import (
 	"context"
+	"sort"
 
 	"github.com/go-kit/log/level"
 
@@ -67,8 +68,14 @@ func (r *ServiceReconciler) reprocessAll(ctx context.Context, req ctrl.Request)
 		return ctrl.Result{}, err
 	}
 
+	// Make it process the already assigned services first
+	sortedServices := services.Items
+	sort.Slice(sortedServices, func(i, j int) bool {
+		return len(sortedServices[i].Status.LoadBalancer.Ingress) > len(sortedServices[j].Status.LoadBalancer.Ingress)
+	})
+
 	retry := false
-	for _, service := range services.Items {
+	for _, service := range sortedServices {
 		service := service // so we can use &service
 		if filterByLoadBalancerClass(&service, r.LoadBalancerClass) {
 			level.Debug(r.Logger).Log("controller", "ServiceReconciler", "filtered service", req.NamespacedName)
@@ -101,6 +108,8 @@ func (r *ServiceReconciler) reprocessAll(ctx context.Context, req ctrl.Request)
 		level.Info(r.Logger).Log("controller", "ServiceReconciler - reprocessAll", "event", "force service reload")
 		return ctrl.Result{}, errRetry
 	}
+	r.initialLoadPerformed = true
+
 	return ctrl.Result{}, nil
 }
 
diff --git a/internal/k8s/epslices/endpoint_slices.go b/internal/k8s/epslices/endpoint_slices.go
index 872c6977..c4b11af8 100644
--- a/internal/k8s/epslices/endpoint_slices.go
+++ b/internal/k8s/epslices/endpoint_slices.go
@@ -26,13 +26,16 @@ const (
 
 const SlicesServiceIndexName = "ServiceName"
 
-// IsConditionReady tells if the conditions represent a ready state, interpreting
-// nil ready as ready.
-func IsConditionReady(conditions discovery.EndpointConditions) bool {
-	if conditions.Ready == nil {
-		return true
+// IsConditionServing tells if the conditions represent a serving state, deferring
+// to ready state if serving == nil.
+func IsConditionServing(conditions discovery.EndpointConditions) bool {
+	if conditions.Serving == nil {
+		if conditions.Ready == nil {
+			return true
+		}
+		return *conditions.Ready
 	}
-	return *conditions.Ready
+	return *conditions.Serving
 }
 
 func ServiceKeyForSlice(endpointSlice *discovery.EndpointSlice) (types.NamespacedName, error) {
diff --git a/internal/k8s/k8s.go b/internal/k8s/k8s.go
index 10ed5ca9..bddffe6e 100644
--- a/internal/k8s/k8s.go
+++ b/internal/k8s/k8s.go
@@ -61,7 +61,6 @@ var (
 	validatingWebhookName           = "metallb-webhook-configuration"
 	addresspoolConvertingWebhookCRD = "addresspools.metallb.io"
 	bgppeerConvertingWebhookCRD     = "bgppeers.metallb.io"
-	webhookSecretName               = "webhook-server-cert" //#nosec G101
 )
 
 func init() {
@@ -108,6 +107,7 @@ type Config struct {
 	ValidateConfig      config.Validate
 	EnableWebhook       bool
 	DisableCertRotation bool
+	WebhookSecretName   string
 	CertDir             string
 	CertServiceName     string
 	LoadBalancerClass   string
diff --git a/internal/k8s/nodes/nodes.go b/internal/k8s/nodes/nodes.go
index 5d335f47..415eda06 100644
--- a/internal/k8s/nodes/nodes.go
+++ b/internal/k8s/nodes/nodes.go
@@ -6,8 +6,13 @@ import (
 	corev1 "k8s.io/api/core/v1"
 )
 
-// ConditionStatus returns the status of the condition for a given node.
-func ConditionStatus(n *corev1.Node, ct corev1.NodeConditionType) corev1.ConditionStatus {
+// IsNetworkUnavailable returns true if the given node NodeNetworkUnavailable condition status is true.
+func IsNetworkUnavailable(n *corev1.Node) bool {
+	return conditionStatus(n, corev1.NodeNetworkUnavailable) == corev1.ConditionTrue
+}
+
+// conditionStatus returns the status of the condition for a given node.
+func conditionStatus(n *corev1.Node, ct corev1.NodeConditionType) corev1.ConditionStatus {
 	if n == nil {
 		return corev1.ConditionUnknown
 	}
@@ -20,8 +25,3 @@ func ConditionStatus(n *corev1.Node, ct corev1.NodeConditionType) corev1.Conditi
 
 	return corev1.ConditionUnknown
 }
-
-// IsNetworkUnavailable returns true if the given node NodeNetworkUnavailable condition status is true.
-func IsNetworkUnavailable(n *corev1.Node) bool {
-	return ConditionStatus(n, corev1.NodeNetworkUnavailable) == corev1.ConditionTrue
-}
diff --git a/internal/k8s/webhook.go b/internal/k8s/webhook.go
index d4a99fcc..9dc194f4 100644
--- a/internal/k8s/webhook.go
+++ b/internal/k8s/webhook.go
@@ -35,7 +35,7 @@ func enableCertRotation(notifyFinished chan struct{}, cfg *Config, mgr manager.M
 	err := rotator.AddRotator(mgr, &rotator.CertRotator{
 		SecretKey: types.NamespacedName{
 			Namespace: cfg.Namespace,
-			Name:      webhookSecretName,
+			Name:      cfg.WebhookSecretName,
 		},
 		CertDir:        cfg.CertDir,
 		CAName:         caName,
diff --git a/speaker/Dockerfile b/speaker/Dockerfile
index bd39c7b7..e2ae863b 100644
--- a/speaker/Dockerfile
+++ b/speaker/Dockerfile
@@ -1,6 +1,6 @@
 # syntax=docker/dockerfile:1.2
 
-FROM --platform=$BUILDPLATFORM docker.io/golang:1.19.5 AS builder
+FROM --platform=$BUILDPLATFORM docker.io/golang:1.20.10 AS builder
 ARG GIT_COMMIT=dev
 ARG GIT_BRANCH=dev
 WORKDIR $GOPATH/go.universe.tf/metallb
diff --git a/speaker/bgp_controller.go b/speaker/bgp_controller.go
index d6829bd7..5d54b787 100644
--- a/speaker/bgp_controller.go
+++ b/speaker/bgp_controller.go
@@ -137,13 +137,13 @@ func hasHealthyEndpoint(eps epslices.EpsOrSlices, filterNode func(*string) bool)
 					continue
 				}
 				for _, addr := range ep.Addresses {
-					if _, ok := ready[addr]; !ok && epslices.IsConditionReady(ep.Conditions) {
+					if _, ok := ready[addr]; !ok && epslices.IsConditionServing(ep.Conditions) {
 						// Only set true if nothing else has expressed an
 						// opinion. This means that false will take precedence
 						// if there's any unready ports for a given endpoint.
 						ready[addr] = true
 					}
-					if !epslices.IsConditionReady(ep.Conditions) {
+					if !epslices.IsConditionServing(ep.Conditions) {
 						ready[addr] = false
 					}
 				}
diff --git a/speaker/layer2_controller.go b/speaker/layer2_controller.go
index ab61fba4..d4155cf5 100644
--- a/speaker/layer2_controller.go
+++ b/speaker/layer2_controller.go
@@ -66,7 +66,7 @@ func usableNodes(eps epslices.EpsOrSlices, speakers map[string]bool) []string {
 	case epslices.Slices:
 		for _, slice := range eps.SlicesVal {
 			for _, ep := range slice.Endpoints {
-				if !epslices.IsConditionReady(ep.Conditions) {
+				if !epslices.IsConditionServing(ep.Conditions) {
 					continue
 				}
 				if ep.NodeName == nil {
@@ -209,7 +209,7 @@ func activeEndpointExists(eps epslices.EpsOrSlices) bool {
 	case epslices.Slices:
 		for _, slice := range eps.SlicesVal {
 			for _, ep := range slice.Endpoints {
-				if !epslices.IsConditionReady(ep.Conditions) {
+				if !epslices.IsConditionServing(ep.Conditions) {
 					continue
 				}
 				return true
diff --git a/tasks.py b/tasks.py
index a3b36183..17336fd3 100644
--- a/tasks.py
+++ b/tasks.py
@@ -898,6 +898,14 @@ def generatemanifests(ctx):
     generate_manifest(ctx, bgp_type="frr", with_prometheus=True, output="config/manifests/metallb-frr-prometheus.yaml")
     generate_manifest(ctx, bgp_type="native", with_prometheus=True, output="config/manifests/metallb-native-prometheus.yaml")
 
+    _align_helm_crds(
+        source='config/manifests/metallb-frr.yaml',
+        output='charts/metallb/charts/crds/templates/crds.yaml')
+
+def _align_helm_crds(source, output):
+    run("""yq eval-all 'select(.kind == "CustomResourceDefinition")' {} > {}""".format(source, output))
+    run("sed -i 's/metallb-system/{{{{ .Release.Namespace }}}}/g' {}".format(output))
+
 @task
 def generateapidocs(ctx):
     """Generates the docs for the CRDs"""
diff --git a/website/content/installation/_index.md b/website/content/installation/_index.md
index 2f7967ff..a9857cde 100644
--- a/website/content/installation/_index.md
+++ b/website/content/installation/_index.md
@@ -188,6 +188,8 @@ and change the `BGP_TYPE` environment variable of the `manager` container to `fr
   value: frr
 ```
 
+{{% /notice %}}
+
 ## FRR daemons logging level
 
 The FRR daemons logging level are configured using the speaker `--log-level` argument following the below mapping:


Test patch:
diff --git a/controller/controller_test.go b/controller/controller_test.go
index dbbc3a07..274299d4 100644
--- a/controller/controller_test.go
+++ b/controller/controller_test.go
@@ -269,7 +269,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -280,7 +280,7 @@ func TestControllerMutation(t *testing.T) {
 			want: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -296,7 +296,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -325,8 +325,8 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
-						annotationAddressPool:     "pool2",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationAddressPool:     "pool2",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -342,7 +342,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "please sir may I have an IP address thank you",
+						AnnotationLoadBalancerIPs: "please sir may I have an IP address thank you",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -358,7 +358,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1,1.2.3.2",
+						AnnotationLoadBalancerIPs: "1.2.3.1,1.2.3.2",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -394,7 +394,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool1",
+						AnnotationAddressPool: "pool1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -405,7 +405,7 @@ func TestControllerMutation(t *testing.T) {
 			want: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool1",
+						AnnotationAddressPool: "pool1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -421,7 +421,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool2",
+						AnnotationAddressPool: "pool2",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -433,7 +433,7 @@ func TestControllerMutation(t *testing.T) {
 			want: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool2",
+						AnnotationAddressPool: "pool2",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -449,7 +449,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "does-not-exist",
+						AnnotationAddressPool: "does-not-exist",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -591,7 +591,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -619,7 +619,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1000::",
+						AnnotationLoadBalancerIPs: "1000::",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -690,7 +690,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool5",
+						AnnotationAddressPool: "pool5",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -701,7 +701,7 @@ func TestControllerMutation(t *testing.T) {
 			want: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationAddressPool: "pool5",
+						AnnotationAddressPool: "pool5",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -727,7 +727,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.1",
+						AnnotationLoadBalancerIPs: "1.2.3.1",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -742,7 +742,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.0,1000::",
+						AnnotationLoadBalancerIPs: "1.2.3.0,1000::",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -753,7 +753,7 @@ func TestControllerMutation(t *testing.T) {
 			want: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.0,1000::",
+						AnnotationLoadBalancerIPs: "1.2.3.0,1000::",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -796,7 +796,7 @@ func TestControllerMutation(t *testing.T) {
 			in: &v1.Service{
 				ObjectMeta: metav1.ObjectMeta{
 					Annotations: map[string]string{
-						annotationLoadBalancerIPs: "1.2.3.0,1000::",
+						AnnotationLoadBalancerIPs: "1.2.3.0,1000::",
 					},
 				},
 				Spec: v1.ServiceSpec{
@@ -1040,12 +1040,23 @@ func TestControllerConfig(t *testing.T) {
 		t.Errorf("SetBalancer produced unexpected mutation (-want +got)\n%s", diff)
 	}
 
-	// Now that an IP is allocated, removing the IP pool is not allowed.
-	if c.SetPools(l, &config.Pools{ByName: map[string]*config.Pool{}}) != controllers.SyncStateError {
-		t.Fatalf("SetPools that deletes allocated IPs was accepted")
+	// Removing the IP pool should be accepted even if the IP is allocated
+	// This triggers a reprocess all event to force the service to get a new IP
+	if c.SetPools(l, &config.Pools{ByName: map[string]*config.Pool{}}) != controllers.SyncStateReprocessAll {
+		t.Fatalf("SetPools that deletes allocated IPs was not accepted")
 	}
 
-	// Deleting the config also makes MetalLB sad.
+	// Balancer should clear the service. Not reprocess all because previous ip is not longer assignable.
+	if c.SetBalancer(l, "test", gotSvc, epslices.EpsOrSlices{}) != controllers.SyncStateErrorNoRetry {
+		t.Fatalf("SetBalancer failed")
+	}
+
+	gotSvc = k.gotService(gotSvc)
+	if diff := diffService(svc, gotSvc); diff != "" {
+		t.Errorf("SetBalancer produced unexpected mutation (-want +got)\n%s", diff)
+	}
+
+	// Deleting the config makes MetalLB sad.
 	if c.SetPools(l, nil) != controllers.SyncStateErrorNoRetry {
 		t.Fatalf("SetPools that deletes the config was accepted")
 	}
@@ -1097,7 +1108,7 @@ func TestDeleteRecyclesIP(t *testing.T) {
 		},
 	}
 	if c.SetBalancer(l, "test2", svc2, epslices.EpsOrSlices{}) == controllers.SyncStateError {
-		t.Fatal("SetBalancer svc2 failed")
+		t.Fatal("SetBalancer svc2 was supposed to fail with no retry")
 	}
 	if k.gotService(svc2) != nil {
 		t.Fatal("SetBalancer svc2 mutated svc2 even though it should not have allocated")
@@ -1121,6 +1132,102 @@ func TestDeleteRecyclesIP(t *testing.T) {
 		t.Fatal("svc2 didn't get an IP")
 	}
 }
+func TestControllerReassign(t *testing.T) {
+	k := &testK8S{t: t}
+	c := &controller{
+		ips:    allocator.New(),
+		client: k,
+	}
+
+	l := log.NewNopLogger()
+
+	svc := &v1.Service{
+		Spec: v1.ServiceSpec{
+			Type:       "LoadBalancer",
+			ClusterIPs: []string{"1.2.3.4"},
+		},
+	}
+
+	// Set a config with some IPs.
+	pools := &config.Pools{ByName: map[string]*config.Pool{
+		"default": {
+			Name:       "default",
+			AutoAssign: true,
+			CIDR:       []*net.IPNet{ipnet("1.2.3.0/24"), ipnet("1000::1/127")},
+		},
+	}}
+
+	if c.SetPools(l, pools) != controllers.SyncStateReprocessAll {
+		t.Fatalf("SetPools failed")
+	}
+
+	if c.SetBalancer(l, "test", svc, epslices.EpsOrSlices{}) != controllers.SyncStateSuccess {
+		t.Fatalf("SetBalancer failed")
+	}
+
+	gotSvc := k.gotService(svc)
+	wantSvc := new(v1.Service)
+	*wantSvc = *svc
+	wantSvc.Status = statusAssigned([]string{"1.2.3.0"})
+	if diff := diffService(wantSvc, gotSvc); diff != "" {
+		t.Errorf("SetBalancer produced unexpected mutation (-want +got)\n%s", diff)
+	}
+
+	// Set a config with some 2 pools.
+	pools = &config.Pools{ByName: map[string]*config.Pool{
+		"default": {
+			Name:       "default",
+			AutoAssign: true,
+			CIDR:       []*net.IPNet{ipnet("1.2.3.0/24"), ipnet("1000::1/127")},
+		},
+		"second": {
+			Name:       "second",
+			AutoAssign: true,
+			CIDR:       []*net.IPNet{ipnet("4.5.6.0/24")},
+		},
+	}}
+
+	if c.SetPools(l, pools) == controllers.SyncStateError {
+		t.Fatalf("SetPools failed")
+	}
+
+	// Allocation shouldn't change.
+	if c.SetBalancer(l, "test", gotSvc, epslices.EpsOrSlices{}) != controllers.SyncStateSuccess {
+		t.Fatalf("SetBalancer failed")
+	}
+
+	k.reset()
+
+	if k.gotService(gotSvc) != nil {
+		t.Errorf("unsynced SetBalancer mutated service%s", gotSvc)
+	}
+
+	// Delete default pool.
+	pools = &config.Pools{ByName: map[string]*config.Pool{
+		"second": {
+			Name:       "second",
+			AutoAssign: true,
+			CIDR:       []*net.IPNet{ipnet("4.5.6.0/24")},
+		},
+	}}
+
+	if c.SetPools(l, pools) != controllers.SyncStateReprocessAll {
+		t.Fatalf("SetPools failed")
+	}
+
+	// It should get an IP from the second pool
+	if c.SetBalancer(l, "test", gotSvc, epslices.EpsOrSlices{}) != controllers.SyncStateSuccess {
+		t.Fatalf("SetBalancer failed")
+	}
+
+	gotSvc = k.gotService(svc)
+	*wantSvc = *svc
+	wantSvc.Status = statusAssigned([]string{"4.5.6.0"})
+	if diff := diffService(wantSvc, gotSvc); diff != "" {
+		t.Errorf("SetBalancer produced unexpected mutation (-want +got)\n%s", diff)
+	}
+}
+
 func TestControllerDualStackConfig(t *testing.T) {
 	k := &testK8S{t: t}
 	c := &controller{
@@ -1194,14 +1301,4 @@ func TestControllerDualStackConfig(t *testing.T) {
 	if diff := diffService(wantSvc, gotSvc); diff != "" {
 		t.Errorf("SetBalancer produced unexpected mutation (-want +got)\n%s", diff)
 	}
-
-	// Now that an IP is allocated, removing the IP pool is not allowed.
-	if c.SetPools(l, &config.Pools{ByName: map[string]*config.Pool{}}) != controllers.SyncStateError {
-		t.Fatalf("SetPools that deletes allocated IPs was accepted")
-	}
-
-	// Deleting the config also makes MetalLB sad.
-	if c.SetPools(l, nil) != controllers.SyncStateErrorNoRetry {
-		t.Fatalf("SetPools that deletes the config was accepted")
-	}
 }
diff --git a/e2etest/bgptests/bgp.go b/e2etest/bgptests/bgp.go
index 3d2761bf..1632b93c 100644
--- a/e2etest/bgptests/bgp.go
+++ b/e2etest/bgptests/bgp.go
@@ -123,7 +123,7 @@ var _ = ginkgo.Describe("BGP", func() {
 
 		allNodes, err := cs.CoreV1().Nodes().List(context.Background(), metav1.ListOptions{})
 		framework.ExpectNoError(err)
-		validateDesiredLB(svc)
+		testservice.ValidateDesiredLB(svc)
 
 		for _, c := range FRRContainers {
 			validateService(svc, allNodes.Items, c)
@@ -156,7 +156,7 @@ var _ = ginkgo.Describe("BGP", func() {
 				testservice.TrafficPolicyCluster(svc)
 			})
 			defer testservice.Delete(cs, svc)
-			validateDesiredLB(svc)
+			testservice.ValidateDesiredLB(svc)
 
 			for _, c := range FRRContainers {
 				validateService(svc, allNodes.Items, c)
@@ -193,7 +193,7 @@ var _ = ginkgo.Describe("BGP", func() {
 		})
 		defer testservice.Delete(cs, svc)
 
-		validateDesiredLB(svc)
+		testservice.ValidateDesiredLB(svc)
 
 		err := jig.Scale(2)
 		framework.ExpectNoError(err)
@@ -280,8 +280,8 @@ var _ = ginkgo.Describe("BGP", func() {
 			})
 		defer testservice.Delete(cs, svc1)
 
-		validateDesiredLB(svc)
-		validateDesiredLB(svc1)
+		testservice.ValidateDesiredLB(svc)
+		testservice.ValidateDesiredLB(svc1)
 
 		for _, c := range FRRContainers {
 			validateService(svc, allNodes.Items, c)
diff --git a/e2etest/bgptests/validate.go b/e2etest/bgptests/validate.go
index b9f38a1b..725770e0 100644
--- a/e2etest/bgptests/validate.go
+++ b/e2etest/bgptests/validate.go
@@ -12,7 +12,6 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 	. "github.com/onsi/gomega"
-	metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
 	"go.universe.tf/e2etest/pkg/executor"
 	"go.universe.tf/e2etest/pkg/frr"
 	frrcontainer "go.universe.tf/e2etest/pkg/frr/container"
@@ -21,6 +20,7 @@ import (
 	"go.universe.tf/e2etest/pkg/metallb"
 	"go.universe.tf/e2etest/pkg/routes"
 	"go.universe.tf/e2etest/pkg/wget"
+	metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	clientset "k8s.io/client-go/kubernetes"
@@ -168,14 +168,6 @@ func checkBFDConfigPropagated(nodeConfig metallbv1beta1.BFDProfile, peerConfig f
 	return nil
 }
 
-func validateDesiredLB(svc *corev1.Service) {
-	desiredLbIPs := svc.Annotations["metallb.universe.tf/loadBalancerIPs"]
-	if desiredLbIPs == "" {
-		return
-	}
-	framework.ExpectEqual(desiredLbIPs, strings.Join(getIngressIPs(svc.Status.LoadBalancer.Ingress), ","))
-}
-
 func checkServiceOnlyOnNodes(svc *corev1.Service, expectedNodes []corev1.Node, ipFamily ipfamily.Family) {
 	if len(expectedNodes) == 0 {
 		return
@@ -286,14 +278,6 @@ OUTER:
 	return nonSelectedNodes
 }
 
-func getIngressIPs(ingresses []corev1.LoadBalancerIngress) []string {
-	var ips []string
-	for _, ingress := range ingresses {
-		ips = append(ips, ingress.IP)
-	}
-	return ips
-}
-
 func validateServiceNotAdvertised(svc *corev1.Service, frrContainers []*frrcontainer.FRR, advertised string, ipFamily ipfamily.Family) {
 	for _, c := range frrContainers {
 		if c.Name != advertised {
diff --git a/e2etest/go.mod b/e2etest/go.mod
index 1b63149e..8272d25a 100644
--- a/e2etest/go.mod
+++ b/e2etest/go.mod
@@ -1,6 +1,6 @@
 module go.universe.tf/e2etest
 
-go 1.19
+go 1.20
 
 replace (
 	github.com/onsi/ginkgo/v2 => github.com/onsi/ginkgo/v2 v2.4.0
diff --git a/e2etest/go.work b/e2etest/go.work
index 8a9dca5c..125ff0b7 100644
--- a/e2etest/go.work
+++ b/e2etest/go.work
@@ -1,4 +1,4 @@
-go 1.19
+go 1.20
 
 use (
 	./
diff --git a/e2etest/go.work.sum b/e2etest/go.work.sum
index dc723774..5e9cd376 100644
--- a/e2etest/go.work.sum
+++ b/e2etest/go.work.sum
@@ -147,12 +147,10 @@ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
 github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=
 github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8=
 github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
-github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
 github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
 github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
@@ -172,6 +170,7 @@ github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
 github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
 github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
diff --git a/e2etest/l2tests/assignment.go b/e2etest/l2tests/assignment.go
index d7ec631e..9d325fc9 100644
--- a/e2etest/l2tests/assignment.go
+++ b/e2etest/l2tests/assignment.go
@@ -9,10 +9,12 @@ import (
 
 	"github.com/onsi/ginkgo/v2"
 	"github.com/onsi/gomega"
-	metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
 	"go.universe.tf/e2etest/pkg/config"
 	"go.universe.tf/e2etest/pkg/k8s"
+	"go.universe.tf/e2etest/pkg/metallb"
+
 	"go.universe.tf/e2etest/pkg/service"
+	metallbv1beta1 "go.universe.tf/metallb/api/v1beta1"
 
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -20,6 +22,7 @@ import (
 	"k8s.io/kubernetes/test/e2e/framework"
 	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
 	admissionapi "k8s.io/pod-security-admission/api"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 )
 
 const secondNamespace = "test-namespace"
@@ -145,6 +148,163 @@ var _ = ginkgo.Describe("IP Assignment", func() {
 					err := cs.CoreV1().Services(svc.Namespace).Delete(context.Background(), svc.Name, metav1.DeleteOptions{})
 					return err
 				}))
+
+		ginkgo.It("should preseve the same external ip after controller restart", func() {
+			const numOfRestarts = 5
+			resources := config.Resources{
+				Pools: []metallbv1beta1.IPAddressPool{
+					{
+						ObjectMeta: metav1.ObjectMeta{
+							Name: "assignment-controller-reset-test-pool",
+						},
+						Spec: metallbv1beta1.IPAddressPoolSpec{
+							Addresses: []string{"192.168.10.100/32", "192.168.20.200/32"},
+						},
+					},
+				},
+			}
+			err := ConfigUpdater.Update(resources)
+			framework.ExpectNoError(err)
+
+			ginkgo.By("creating 4 LB services")
+			jig := e2eservice.NewTestJig(cs, f.Namespace.Name, "service-a")
+			serviceA, err := jig.CreateLoadBalancerService(30*time.Second, nil)
+			framework.ExpectNoError(err)
+			defer service.Delete(cs, serviceA)
+			service.ValidateDesiredLB(serviceA)
+
+			jig = e2eservice.NewTestJig(cs, f.Namespace.Name, "service-b")
+			serviceB, err := jig.CreateLoadBalancerService(30*time.Second, nil)
+			framework.ExpectNoError(err)
+			defer service.Delete(cs, serviceB)
+			service.ValidateDesiredLB(serviceB)
+
+			jig = e2eservice.NewTestJig(cs, f.Namespace.Name, "service-c")
+			serviceC, err := jig.CreateLoadBalancerServiceWaitForClusterIPOnly(nil)
+			framework.ExpectNoError(err)
+			defer service.Delete(cs, serviceC)
+
+			jig = e2eservice.NewTestJig(cs, f.Namespace.Name, "service-d")
+			serviceD, err := jig.CreateLoadBalancerServiceWaitForClusterIPOnly(nil)
+			framework.ExpectNoError(err)
+			defer service.Delete(cs, serviceD)
+
+			restartAndAssert := func() {
+				metallb.RestartController(cs)
+				gomega.Consistently(func() error {
+					serviceA, err = cs.CoreV1().Services(serviceA.Namespace).Get(context.TODO(), serviceA.Name, metav1.GetOptions{})
+					framework.ExpectNoError(err)
+
+					err = service.ValidateAssignedWith(serviceA, "192.168.10.100")
+					if err != nil {
+						return err
+					}
+					serviceB, err = cs.CoreV1().Services(serviceB.Namespace).Get(context.TODO(), serviceB.Name, metav1.GetOptions{})
+					framework.ExpectNoError(err)
+
+					err = service.ValidateAssignedWith(serviceB, "192.168.20.200")
+					if err != nil {
+						return err
+					}
+
+					return nil
+				}, 10*time.Second, 2*time.Second).ShouldNot(gomega.HaveOccurred())
+			}
+
+			ginkgo.By("restarting the controller and validating that the service keeps the same ip")
+			for i := 0; i < numOfRestarts; i++ {
+				restartAndAssert()
+			}
+		})
+	})
+
+	ginkgo.Context("IPV4 removing pools", func() {
+		var pool1 metallbv1beta1.IPAddressPool
+		var pool2 metallbv1beta1.IPAddressPool
+
+		ginkgo.AfterEach(func() {
+			// Clean previous configuration.
+			err := ConfigUpdater.Clean()
+			framework.ExpectNoError(err)
+		})
+
+		ginkgo.BeforeEach(func() {
+			pool1 = metallbv1beta1.IPAddressPool{
+				ObjectMeta: metav1.ObjectMeta{Name: "test-ns-pool-1"},
+				Spec: metallbv1beta1.IPAddressPoolSpec{
+					Addresses: []string{
+						"192.168.5.0/32",
+					},
+					AllocateTo: &metallbv1beta1.ServiceAllocation{Priority: 20, Namespaces: []string{f.Namespace.Name}},
+				},
+			}
+			pool2 = metallbv1beta1.IPAddressPool{
+				ObjectMeta: metav1.ObjectMeta{Name: "test-ns-pool-2"},
+				Spec: metallbv1beta1.IPAddressPoolSpec{
+					Addresses: []string{
+						"192.168.10.0/32",
+					},
+				},
+			}
+
+			resources := config.Resources{
+				Pools: []metallbv1beta1.IPAddressPool{pool1, pool2},
+			}
+
+			err := ConfigUpdater.Update(resources)
+			framework.ExpectNoError(err)
+		})
+
+		ginkgo.It("removes all pools", func() {
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-1")
+			defer func() {
+				service.Delete(cs, svc1)
+			}()
+
+			ginkgo.By("validate LoadBalancer IP is allocated from pool1")
+			err := config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{pool1}, e2eservice.GetIngressPoint(
+				&svc1.Status.LoadBalancer.Ingress[0]))
+			framework.ExpectNoError(err)
+
+			ginkgo.By("deleting all pools")
+			err = ConfigUpdater.Client().DeleteAllOf(context.Background(), &metallbv1beta1.IPAddressPool{}, client.InNamespace(ConfigUpdater.Namespace()))
+			framework.ExpectNoError(err)
+
+			ginkgo.By("validate LoadBalancer IP is removed from the svc")
+			gomega.Eventually(func() int {
+				s, err := cs.CoreV1().Services(svc1.Namespace).Get(context.Background(), svc1.Name, metav1.GetOptions{})
+				framework.ExpectNoError(err)
+				return len(s.Status.LoadBalancer.Ingress)
+			}, time.Minute, 1*time.Second).Should(gomega.Equal(0))
+		})
+
+		ginkgo.It("reallocates svc after deleting a pool", func() {
+			svc1, _ := service.CreateWithBackend(cs, f.Namespace.Name, "svc-test-ns-pool-1")
+			defer func() {
+				service.Delete(cs, svc1)
+			}()
+
+			ginkgo.By("validate LoadBalancer IP is allocated from pool1")
+			err := config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{pool1}, e2eservice.GetIngressPoint(
+				&svc1.Status.LoadBalancer.Ingress[0]))
+			framework.ExpectNoError(err)
+
+			ginkgo.By("deleting pool 1")
+			p := &metallbv1beta1.IPAddressPool{}
+			err = ConfigUpdater.Client().Get(context.Background(), client.ObjectKey{Namespace: ConfigUpdater.Namespace(), Name: pool1.Name}, p)
+			framework.ExpectNoError(err)
+			err = ConfigUpdater.Client().Delete(context.Background(), p)
+			framework.ExpectNoError(err)
+
+			ginkgo.By("validate LoadBalancer IP is re-allocated from pool2")
+			gomega.Eventually(func() error {
+				svc1, err := cs.CoreV1().Services(svc1.Namespace).Get(context.Background(), svc1.Name, metav1.GetOptions{})
+				framework.ExpectNoError(err)
+				err = config.ValidateIPInRange([]metallbv1beta1.IPAddressPool{pool2}, e2eservice.GetIngressPoint(
+					&svc1.Status.LoadBalancer.Ingress[0]))
+				return err
+			}, time.Minute, 1*time.Second).ShouldNot(gomega.HaveOccurred())
+		})
 	})
 
 	ginkgo.Context("IPV4 - Validate service allocation in address pools", func() {
diff --git a/e2etest/pkg/frr/parse.go b/e2etest/pkg/frr/parse.go
index fcf9bb4f..3db92d38 100644
--- a/e2etest/pkg/frr/parse.go
+++ b/e2etest/pkg/frr/parse.go
@@ -13,15 +13,17 @@ import (
 )
 
 type Neighbor struct {
-	IP             net.IP
-	VRF            string
-	Connected      bool
-	LocalAS        string
-	RemoteAS       string
-	PrefixSent     int
-	Port           int
-	RemoteRouterID string
-	MsgStats       MessageStats
+	IP                      net.IP
+	VRF                     string
+	Connected               bool
+	LocalAS                 string
+	RemoteAS                string
+	PrefixSent              int
+	Port                    int
+	RemoteRouterID          string
+	MsgStats                MessageStats
+	ConfiguredHoldTime      int
+	ConfiguredKeepAliveTime int
 }
 
 type Route struct {
@@ -34,15 +36,17 @@ type Route struct {
 const bgpConnected = "Established"
 
 type FRRNeighbor struct {
-	RemoteAs          int          `json:"remoteAs"`
-	LocalAs           int          `json:"localAs"`
-	RemoteRouterID    string       `json:"remoteRouterId"`
-	BgpVersion        int          `json:"bgpVersion"`
-	BgpState          string       `json:"bgpState"`
-	PortForeign       int          `json:"portForeign"`
-	MsgStats          MessageStats `json:"messageStats"`
-	VRFName           string       `json:"vrf"`
-	AddressFamilyInfo map[string]struct {
+	RemoteAs                     int          `json:"remoteAs"`
+	LocalAs                      int          `json:"localAs"`
+	RemoteRouterID               string       `json:"remoteRouterId"`
+	BgpVersion                   int          `json:"bgpVersion"`
+	BgpState                     string       `json:"bgpState"`
+	PortForeign                  int          `json:"portForeign"`
+	MsgStats                     MessageStats `json:"messageStats"`
+	VRFName                      string       `json:"vrf"`
+	ConfiguredHoldTimeMSecs      int          `json:"bgpTimerConfiguredHoldTimeMsecs"`
+	ConfiguredKeepAliveTimeMSecs int          `json:"bgpTimerConfiguredKeepAliveIntervalMsecs"`
+	AddressFamilyInfo            map[string]struct {
 		SentPrefixCounter int `json:"sentPrefixCounter"`
 	} `json:"addressFamilyInfo"`
 }
@@ -128,14 +132,16 @@ func ParseNeighbour(vtyshRes string) (*Neighbor, error) {
 			prefixSent += s.SentPrefixCounter
 		}
 		return &Neighbor{
-			IP:             ip,
-			Connected:      connected,
-			LocalAS:        strconv.Itoa(n.LocalAs),
-			RemoteAS:       strconv.Itoa(n.RemoteAs),
-			PrefixSent:     prefixSent,
-			Port:           n.PortForeign,
-			RemoteRouterID: n.RemoteRouterID,
-			MsgStats:       n.MsgStats,
+			IP:                      ip,
+			Connected:               connected,
+			LocalAS:                 strconv.Itoa(n.LocalAs),
+			RemoteAS:                strconv.Itoa(n.RemoteAs),
+			PrefixSent:              prefixSent,
+			Port:                    n.PortForeign,
+			RemoteRouterID:          n.RemoteRouterID,
+			MsgStats:                n.MsgStats,
+			ConfiguredKeepAliveTime: n.ConfiguredKeepAliveTimeMSecs,
+			ConfiguredHoldTime:      n.ConfiguredHoldTimeMSecs,
 		}, nil
 	}
 	return nil, errors.New("no peers were returned")
@@ -165,14 +171,16 @@ func ParseNeighbours(vtyshRes string) ([]*Neighbor, error) {
 			prefixSent += s.SentPrefixCounter
 		}
 		res = append(res, &Neighbor{
-			IP:             ip,
-			Connected:      connected,
-			LocalAS:        strconv.Itoa(n.LocalAs),
-			RemoteAS:       strconv.Itoa(n.RemoteAs),
-			PrefixSent:     prefixSent,
-			Port:           n.PortForeign,
-			RemoteRouterID: n.RemoteRouterID,
-			MsgStats:       n.MsgStats,
+			IP:                      ip,
+			Connected:               connected,
+			LocalAS:                 strconv.Itoa(n.LocalAs),
+			RemoteAS:                strconv.Itoa(n.RemoteAs),
+			PrefixSent:              prefixSent,
+			Port:                    n.PortForeign,
+			RemoteRouterID:          n.RemoteRouterID,
+			MsgStats:                n.MsgStats,
+			ConfiguredKeepAliveTime: n.ConfiguredKeepAliveTimeMSecs,
+			ConfiguredHoldTime:      n.ConfiguredHoldTimeMSecs,
 		})
 	}
 	return res, nil
diff --git a/e2etest/pkg/frr/parse_test.go b/e2etest/pkg/frr/parse_test.go
new file mode 100644
index 00000000..8833218d
--- /dev/null
+++ b/e2etest/pkg/frr/parse_test.go
@@ -0,0 +1,792 @@
+// SPDX-License-Identifier:Apache-2.0
+
+package frr
+
+import (
+	"bytes"
+	"fmt"
+	"net"
+	"sort"
+	"testing"
+
+	"github.com/google/go-cmp/cmp"
+)
+
+var expectedStats = MessageStats{
+	OpensSent:          1,
+	OpensReceived:      2,
+	NotificationsSent:  3,
+	UpdatesSent:        4,
+	UpdatesReceived:    5,
+	KeepalivesSent:     6,
+	KeepalivesReceived: 7,
+	RouteRefreshSent:   8,
+	TotalSent:          9,
+	TotalReceived:      10,
+}
+
+func TestNeighbour(t *testing.T) {
+	sample := `{
+    "%s":{
+      "remoteAs":%s,
+      "localAs":%s,
+      "nbrInternalLink":true,
+      "bgpVersion":4,
+      "remoteRouterId":"0.0.0.0",
+      "localRouterId":"172.18.0.5",
+      "bgpState":"%s",
+      "bgpTimerLastRead":253000,
+      "bgpTimerLastWrite":3405000,
+      "bgpTimerConfiguredHoldTimeMsecs":90000,
+      "bgpTimerConfiguredKeepAliveIntervalMsecs":30000,  
+      "bgpInUpdateElapsedTimeMsecs":3405000,
+      "bgpTimerHoldTimeMsecs":180000,
+      "bgpTimerKeepAliveIntervalMsecs":60000,
+      "gracefulRestartInfo":{
+        "endOfRibSend":{
+        },
+        "endOfRibRecv":{
+        },
+        "localGrMode":"Helper*",
+        "remoteGrMode":"NotApplicable",
+        "rBit":false,
+        "timers":{
+          "configuredRestartTimer":120,
+          "receivedRestartTimer":0
+        }
+      },
+      "messageStats":{
+        "depthInq":0,
+        "depthOutq":0,
+        "opensSent":1,
+        "opensRecv":2,
+        "notificationsSent":3,
+        "notificationsRecv":0,
+        "updatesSent":4,
+        "updatesRecv":5,
+        "keepalivesSent":6,
+        "keepalivesRecv":7,
+        "routeRefreshSent":8,
+        "routeRefreshRecv":0,
+        "capabilitySent":0,
+        "capabilityRecv":0,
+        "totalSent":9,
+        "totalRecv":10
+      },
+      "minBtwnAdvertisementRunsTimerMsecs":0,
+      "addressFamilyInfo":{
+        "ipv4Unicast":{
+          "routerAlwaysNextHop":true,
+          "commAttriSentToNbr":"extendedAndStandard",
+          "acceptedPrefixCounter":0,
+          "sentPrefixCounter":%d
+        },
+        "ipv6Unicast":{
+          "routerAlwaysNextHop":true,
+          "commAttriSentToNbr":"extendedAndStandard",
+          "acceptedPrefixCounter":0,
+          "sentPrefixCounter":%d
+        }
+      },
+      "connectionsEstablished":0,
+      "connectionsDropped":0,
+      "lastResetTimerMsecs":253000,
+      "lastResetDueTo":"Waiting for peer OPEN",
+      "lastResetCode":32,
+      "portForeign":%d,
+      "connectRetryTimer":120,
+      "nextConnectTimerDueInMsecs":107000,
+      "readThread":"off",
+      "writeThread":"off"
+    }
+  }`
+
+	tests := []struct {
+		name           string
+		neighborIP     string
+		remoteAS       string
+		localAS        string
+		status         string
+		ipv4PrefixSent int
+		ipv6PrefixSent int
+		port           int
+		expectedError  string
+	}{
+		{
+			"ipv4, connected",
+			"172.18.0.5",
+			"64512",
+			"64512",
+			"Established",
+			1,
+			0,
+			179,
+			"",
+		},
+		{
+			"ipv4, connected",
+			"172.18.0.5",
+			"64512",
+			"64512",
+			"Active",
+			0,
+			0,
+			180,
+			"",
+		},
+		{
+			"ipv6, connected",
+			"2620:52:0:1302::8af5",
+			"64512",
+			"64512",
+			"Established",
+			2,
+			1,
+			181,
+			"",
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			n, err := ParseNeighbour(fmt.Sprintf(sample, tt.neighborIP, tt.remoteAS, tt.localAS, tt.status, tt.ipv4PrefixSent, tt.ipv6PrefixSent, tt.port))
+			if err != nil {
+				t.Fatal("Failed to parse ", err)
+			}
+			if !n.IP.Equal(net.ParseIP(tt.neighborIP)) {
+				t.Fatal("Expected neighbour ip", tt.neighborIP, "got", n.IP.String())
+			}
+			if n.RemoteAS != tt.remoteAS {
+				t.Fatal("Expected remote as", tt.remoteAS, "got", n.RemoteAS)
+			}
+			if n.LocalAS != tt.localAS {
+				t.Fatal("Expected local as", tt.localAS, "got", n.LocalAS)
+			}
+			if tt.status == "Established" && n.Connected != true {
+				t.Fatal("Expected connected", true, "got", n.Connected)
+			}
+			if tt.status != "Established" && n.Connected == true {
+				t.Fatal("Expected connected", false, "got", n.Connected)
+			}
+			if tt.ipv4PrefixSent+tt.ipv6PrefixSent != n.PrefixSent {
+				t.Fatal("Expected prefix sent", tt.ipv4PrefixSent+tt.ipv6PrefixSent, "got", n.PrefixSent)
+			}
+			if tt.port != n.Port {
+				t.Fatal("Expected port", tt.port, "got", n.Port)
+			}
+			if n.RemoteRouterID != "0.0.0.0" {
+				t.Fatal("Expected remote routerid 0.0.0.0")
+			}
+			if !cmp.Equal(expectedStats, n.MsgStats) {
+				t.Fatal("unexpected BGP messages stats (-want +got)\n", cmp.Diff(expectedStats, n.MsgStats))
+			}
+			if n.ConfiguredHoldTime != 90000 {
+				t.Fatal("unexpected configured hold time", n.ConfiguredHoldTime)
+			}
+			if n.ConfiguredKeepAliveTime != 30000 {
+				t.Fatal("unexpected configured keepalivetime time", n.ConfiguredKeepAliveTime)
+			}
+		})
+	}
+}
+
+const fourNeighbours = `
+{
+  "172.18.0.2":{
+    "remoteAs":64512,
+    "localAs":64512,
+    "nbrInternalLink":true,
+    "bgpVersion":4,
+    "remoteRouterId":"0.0.0.0",
+    "localRouterId":"172.18.0.5",
+    "bgpState":"Active",
+    "bgpTimerLastRead":14000,
+    "bgpTimerLastWrite":3166000,
+    "bgpInUpdateElapsedTimeMsecs":3166000,
+    "bgpTimerHoldTimeMsecs":180000,
+    "bgpTimerKeepAliveIntervalMsecs":60000,
+    "gracefulRestartInfo":{
+      "endOfRibSend":{
+      },
+      "endOfRibRecv":{
+      },
+      "localGrMode":"Helper*",
+      "remoteGrMode":"NotApplicable",
+      "rBit":false,
+      "timers":{
+        "configuredRestartTimer":120,
+        "receivedRestartTimer":0
+      }
+    },
+    "messageStats":{
+      "depthInq":0,
+      "depthOutq":0,
+      "opensSent":1,
+      "opensRecv":2,
+      "notificationsSent":3,
+      "notificationsRecv":0,
+      "updatesSent":4,
+      "updatesRecv":5,
+      "keepalivesSent":6,
+      "keepalivesRecv":7,
+      "routeRefreshSent":8,
+      "routeRefreshRecv":0,
+      "capabilitySent":0,
+      "capabilityRecv":0,
+      "totalSent":9,
+      "totalRecv":10
+    },
+    "minBtwnAdvertisementRunsTimerMsecs":0,
+    "addressFamilyInfo":{
+      "ipv4Unicast":{
+        "routerAlwaysNextHop":true,
+        "commAttriSentToNbr":"extendedAndStandard",
+        "acceptedPrefixCounter":0
+      }
+    },
+    "connectionsEstablished":0,
+    "connectionsDropped":0,
+    "lastResetTimerMsecs":14000,
+    "lastResetDueTo":"Waiting for peer OPEN",
+    "lastResetCode":32,
+    "connectRetryTimer":120,
+    "nextConnectTimerDueInMsecs":107000,
+    "readThread":"off",
+    "writeThread":"off"
+  },
+  "172.18.0.3":{
+    "remoteAs":64512,
+    "localAs":64512,
+    "nbrInternalLink":true,
+    "bgpVersion":4,
+    "remoteRouterId":"0.0.0.0",
+    "localRouterId":"172.18.0.5",
+    "bgpState":"Active",
+    "bgpTimerLastRead":14000,
+    "bgpTimerLastWrite":3166000,
+    "bgpInUpdateElapsedTimeMsecs":3166000,
+    "bgpTimerHoldTimeMsecs":180000,
+    "bgpTimerKeepAliveIntervalMsecs":60000,
+    "gracefulRestartInfo":{
+      "endOfRibSend":{
+      },
+      "endOfRibRecv":{
+      },
+      "localGrMode":"Helper*",
+      "remoteGrMode":"NotApplicable",
+      "rBit":false,
+      "timers":{
+        "configuredRestartTimer":120,
+        "receivedRestartTimer":0
+      }
+    },
+    "messageStats":{
+      "depthInq":0,
+      "depthOutq":0,
+      "opensSent":1,
+      "opensRecv":2,
+      "notificationsSent":3,
+      "notificationsRecv":0,
+      "updatesSent":4,
+      "updatesRecv":5,
+      "keepalivesSent":6,
+      "keepalivesRecv":7,
+      "routeRefreshSent":8,
+      "routeRefreshRecv":0,
+      "capabilitySent":0,
+      "capabilityRecv":0,
+      "totalSent":9,
+      "totalRecv":10
+    },
+    "minBtwnAdvertisementRunsTimerMsecs":0,
+    "addressFamilyInfo":{
+      "ipv4Unicast":{
+        "routerAlwaysNextHop":true,
+        "commAttriSentToNbr":"extendedAndStandard",
+        "acceptedPrefixCounter":0
+      }
+    },
+    "connectionsEstablished":0,
+    "connectionsDropped":0,
+    "lastResetTimerMsecs":14000,
+    "lastResetDueTo":"Waiting for peer OPEN",
+    "lastResetCode":32,
+    "connectRetryTimer":120,
+    "nextConnectTimerDueInMsecs":107000,
+    "readThread":"off",
+    "writeThread":"off"
+  },
+  "172.18.0.4":{
+    "remoteAs":64512,
+    "localAs":64512,
+    "nbrInternalLink":true,
+    "bgpVersion":4,
+    "remoteRouterId":"0.0.0.0",
+    "localRouterId":"172.18.0.5",
+    "bgpState":"Active",
+    "bgpTimerLastRead":14000,
+    "bgpTimerLastWrite":3166000,
+    "bgpInUpdateElapsedTimeMsecs":3166000,
+    "bgpTimerHoldTimeMsecs":180000,
+    "bgpTimerKeepAliveIntervalMsecs":60000,
+    "gracefulRestartInfo":{
+      "endOfRibSend":{
+      },
+      "endOfRibRecv":{
+      },
+      "localGrMode":"Helper*",
+      "remoteGrMode":"NotApplicable",
+      "rBit":false,
+      "timers":{
+        "configuredRestartTimer":120,
+        "receivedRestartTimer":0
+      }
+    },
+    "messageStats":{
+      "depthInq":0,
+      "depthOutq":0,
+      "opensSent":1,
+      "opensRecv":2,
+      "notificationsSent":3,
+      "notificationsRecv":0,
+      "updatesSent":4,
+      "updatesRecv":5,
+      "keepalivesSent":6,
+      "keepalivesRecv":7,
+      "routeRefreshSent":8,
+      "routeRefreshRecv":0,
+      "capabilitySent":0,
+      "capabilityRecv":0,
+      "totalSent":9,
+      "totalRecv":10
+    },
+    "minBtwnAdvertisementRunsTimerMsecs":0,
+    "addressFamilyInfo":{
+      "ipv4Unicast":{
+        "routerAlwaysNextHop":true,
+        "commAttriSentToNbr":"extendedAndStandard",
+        "acceptedPrefixCounter":0
+      }
+    },
+    "connectionsEstablished":0,
+    "connectionsDropped":0,
+    "lastResetTimerMsecs":14000,
+    "lastResetDueTo":"Waiting for peer OPEN",
+    "lastResetCode":32,
+    "connectRetryTimer":120,
+    "nextConnectTimerDueInMsecs":107000,
+    "readThread":"off",
+    "writeThread":"off"
+  },
+  "fc00:f853:ccd:e793::4":{
+    "remoteAs":64512,
+    "localAs":64513,
+    "nbrExternalLink":true,
+    "hostname":"kind-control-plane",
+    "bgpVersion":4,
+    "remoteRouterId":"11.11.11.11",
+    "localRouterId":"172.18.0.5",
+    "bgpState":"Established",
+    "bgpTimerUpMsec":0,
+    "bgpTimerUpString":"00:00:00",
+    "bgpTimerUpEstablishedEpoch":1636386709,
+    "bgpTimerLastRead":4000,
+    "bgpTimerLastWrite":0,
+    "bgpInUpdateElapsedTimeMsecs":78272000,
+    "bgpTimerHoldTimeMsecs":90000,
+    "bgpTimerKeepAliveIntervalMsecs":30000,
+    "neighborCapabilities":{
+      "4byteAs":"advertisedAndReceived",
+      "extendedMessage":"advertisedAndReceived",
+      "addPath":{
+        "ipv6Unicast":{
+          "rxAdvertisedAndReceived":true
+        }
+      },
+      "routeRefresh":"advertisedAndReceivedOldNew",
+      "enhancedRouteRefresh":"advertisedAndReceived",
+      "multiprotocolExtensions":{
+        "ipv4Unicast":{
+          "received":true
+        },
+        "ipv6Unicast":{
+          "advertisedAndReceived":true
+        }
+      },
+      "hostName":{
+        "advHostName":"85e811e29230",
+        "advDomainName":"n\/a",
+        "rcvHostName":"kind-control-plane",
+        "rcvDomainName":"n\/a"
+      },
+      "gracefulRestart":"advertisedAndReceived",
+      "gracefulRestartRemoteTimerMsecs":120000,
+      "addressFamiliesByPeer":"none"
+    },
+    "gracefulRestartInfo":{
+      "endOfRibSend":{
+        "ipv6Unicast":true
+      },
+      "endOfRibRecv":{
+      },
+      "localGrMode":"Helper*",
+      "remoteGrMode":"Helper",
+      "rBit":true,
+      "timers":{
+        "configuredRestartTimer":120,
+        "receivedRestartTimer":120
+      },
+      "ipv6Unicast":{
+        "fBit":false,
+        "endOfRibStatus":{
+          "endOfRibSend":true,
+          "endOfRibSentAfterUpdate":true,
+          "endOfRibRecv":false
+        },
+        "timers":{
+          "stalePathTimer":360
+        }
+      }
+    },
+    "messageStats":{
+      "depthInq":0,
+      "depthOutq":0,
+      "opensSent":1,
+      "opensRecv":2,
+      "notificationsSent":3,
+      "notificationsRecv":0,
+      "updatesSent":4,
+      "updatesRecv":5,
+      "keepalivesSent":6,
+      "keepalivesRecv":7,
+      "routeRefreshSent":8,
+      "routeRefreshRecv":0,
+      "capabilitySent":0,
+      "capabilityRecv":0,
+      "totalSent":9,
+      "totalRecv":10
+    },
+    "minBtwnAdvertisementRunsTimerMsecs":0,
+    "addressFamilyInfo":{
+      "ipv6Unicast":{
+        "updateGroupId":1,
+        "subGroupId":1,
+        "packetQueueLength":0,
+        "routerAlwaysNextHop":true,
+        "commAttriSentToNbr":"extendedAndStandard",
+        "acceptedPrefixCounter":0,
+        "sentPrefixCounter":0
+      }
+    },
+    "connectionsEstablished":1,
+    "connectionsDropped":0,
+    "lastResetTimerMsecs":4000,
+    "lastResetDueTo":"No AFI\/SAFI activated for peer",
+    "lastResetCode":30,
+    "hostLocal":"fc00:f853:ccd:e793::5",
+    "portLocal":180,
+    "hostForeign":"fc00:f853:ccd:e793::4",
+    "portForeign":53568,
+    "nexthop":"172.18.0.5",
+    "nexthopGlobal":"fc00:f853:ccd:e793::5",
+    "nexthopLocal":"fe80::42:acff:fe12:5",
+    "bgpConnection":"sharedNetwork",
+    "connectRetryTimer":120,
+    "authenticationEnabled":1,
+    "readThread":"on",
+    "writeThread":"on"
+  }
+}`
+
+func TestNeighbours(t *testing.T) {
+	nn, err := ParseNeighbours(fourNeighbours)
+	if err != nil {
+		t.Fatalf("Failed to parse %s", err)
+	}
+	if len(nn) != 4 {
+		t.Fatalf("Expected 4 neighbours, got %d", len(nn))
+	}
+	sort.Slice(nn, func(i, j int) bool {
+		return (bytes.Compare(nn[i].IP, nn[j].IP) < 0)
+	})
+
+	if !nn[0].IP.Equal(net.ParseIP("172.18.0.2")) {
+		t.Fatal("neighbour ip not matching")
+	}
+	if !nn[1].IP.Equal(net.ParseIP("172.18.0.3")) {
+		t.Fatal("neighbour ip not matching")
+	}
+	if !nn[2].IP.Equal(net.ParseIP("172.18.0.4")) {
+		t.Fatal("neighbour ip not matching")
+	}
+
+	for i, n := range nn {
+		if !cmp.Equal(expectedStats, n.MsgStats) {
+			t.Fatal("unexpected BGP messages stats for neightbor", i, "(-want +got)\n", cmp.Diff(expectedStats, n.MsgStats))
+		}
+	}
+}
+
+const routes = `{
+  "vrfId": 0,
+  "vrfName": "default",
+  "tableVersion": 7,
+  "routerId": "172.18.0.5",
+  "defaultLocPrf": 100,
+  "localAS": 64512,
+  "routes": { "192.168.10.0/32": [
+   {
+     "valid":true,
+     "multipath":true,
+     "pathFrom":"internal",
+     "prefix":"192.168.10.0",
+     "prefixLen":32,
+     "network":"192.168.10.0\/32",
+     "locPrf":0,
+     "weight":0,
+     "peerId":"172.18.0.4",
+     "path":"",
+     "origin":"incomplete",
+     "nexthops":[
+       {
+         "ip":"172.18.0.4",
+         "afi":"ipv4",
+         "used":true
+       }
+     ]
+   },
+   {
+     "valid":true,
+     "bestpath":true,
+     "pathFrom":"internal",
+     "prefix":"192.168.10.0",
+     "prefixLen":32,
+     "network":"192.168.10.0\/32",
+     "locPrf":0,
+     "weight":0,
+     "peerId":"172.18.0.2",
+     "path":"",
+     "origin":"incomplete",
+     "nexthops":[
+       {
+         "ip":"172.18.0.2",
+         "afi":"ipv4",
+         "used":true
+       }
+     ]
+   },
+   {
+     "valid":true,
+     "multipath":true,
+     "pathFrom":"internal",
+     "prefix":"192.168.10.0",
+     "prefixLen":32,
+     "network":"192.168.10.0\/32",
+     "locPrf":0,
+     "weight":0,
+     "peerId":"172.18.0.3",
+     "path":"",
+     "origin":"incomplete",
+     "nexthops":[
+       {
+         "ip":"172.18.0.3",
+         "afi":"ipv4",
+         "used":true
+       }
+     ]
+   }
+ ] }  }`
+
+func TestRoutes(t *testing.T) {
+	rr, err := ParseRoutes(routes)
+	if err != nil {
+		t.Fatalf("Failed to parse %s", err)
+	}
+
+	ipRoutes, ok := rr["192.168.10.0"]
+	if !ok {
+		t.Fatalf("Routes for 192.168.10.0/32 not found")
+	}
+
+	ips := make([]net.IP, 0)
+	ips = append(ips, ipRoutes.NextHops...)
+
+	sort.Slice(ips, func(i, j int) bool {
+		return (bytes.Compare(ips[i], ips[j]) < 0)
+	})
+	if !ips[0].Equal(net.ParseIP("172.18.0.2")) {
+		t.Fatal("neighbour ip not matching")
+	}
+	if !ips[1].Equal(net.ParseIP("172.18.0.3")) {
+		t.Fatal("neighbour ip not matching")
+	}
+	if !ips[2].Equal(net.ParseIP("172.18.0.4")) {
+		t.Fatal("neighbour ip not matching")
+	}
+}
+
+const bfdPeers = `[
+   {
+      "multihop":false,
+      "peer":"172.18.0.4",
+      "local":"172.18.0.5",
+      "vrf":"default",
+      "interface":"eth0",
+      "id":632314921,
+      "remote-id":2999817552,
+      "passive-mode":false,
+      "status":"up",
+      "uptime":52,
+      "diagnostic":"ok",
+      "remote-diagnostic":"ok",
+      "receive-interval":300,
+      "transmit-interval":300,
+      "echo-receive-interval":50,
+      "echo-transmit-interval":0,
+      "detect-multiplier":3,
+      "remote-receive-interval":300,
+      "remote-transmit-interval":300,
+      "remote-echo-receive-interval":50,
+      "remote-detect-multiplier":3
+   },
+   {
+      "multihop":false,
+      "peer":"172.18.0.2",
+      "local":"172.18.0.5",
+      "vrf":"default",
+      "interface":"eth0",
+      "id":3048501273,
+      "remote-id":2977557242,
+      "passive-mode":false,
+      "status":"up",
+      "uptime":52,
+      "diagnostic":"ok",
+      "remote-diagnostic":"ok",
+      "receive-interval":300,
+      "transmit-interval":300,
+      "echo-receive-interval":50,
+      "echo-transmit-interval":0,
+      "detect-multiplier":3,
+      "remote-receive-interval":300,
+      "remote-transmit-interval":300,
+      "remote-echo-receive-interval":50,
+      "remote-detect-multiplier":3
+   },
+   {
+      "multihop":false,
+      "peer":"172.18.0.3",
+      "local":"172.18.0.5",
+      "vrf":"default",
+      "interface":"eth0",
+      "id":2114932580,
+      "remote-id":493597049,
+      "passive-mode":false,
+      "status":"up",
+      "uptime":52,
+      "diagnostic":"ok",
+      "remote-diagnostic":"ok",
+      "receive-interval":300,
+      "transmit-interval":300,
+      "echo-receive-interval":50,
+      "echo-transmit-interval":0,
+      "detect-multiplier":3,
+      "remote-receive-interval":300,
+      "remote-transmit-interval":300,
+      "remote-echo-interval":50,
+      "remote-detect-multiplier":3
+   }
+]`
+
+func TestBFDPeers(t *testing.T) {
+	peers, err := ParseBFDPeers(bfdPeers)
+	if err != nil {
+		t.Fatalf("Failed to parse %s", err)
+	}
+	if len(peers) != 3 {
+		t.Fatal("Unexpected peer number", len(peers))
+	}
+	if peers[2].Peer != "172.18.0.3" {
+		t.Fatal("Peer not found")
+	}
+	if peers[2].Status != "up" {
+		t.Fatal("wrong status")
+	}
+	if peers[2].RemoteEchoInterval != 50 {
+		t.Fatal("wrong echo interval")
+	}
+}
+
+const vrfs = `{
+"default":{
+ "vrfId": 0,
+ "vrfName": "default",
+ "tableVersion": 1,
+ "routerId": "172.18.0.3",
+ "defaultLocPrf": 100,
+ "localAS": 64512,
+ "routes": { "192.168.10.0/32": [
+  {
+    "valid":true,
+    "bestpath":true,
+    "pathFrom":"external",
+    "prefix":"192.168.10.0",
+    "prefixLen":32,
+    "network":"192.168.10.0\/32",
+    "metric":0,
+    "weight":32768,
+    "peerId":"(unspec)",
+    "path":"",
+    "origin":"IGP",
+    "nexthops":[
+      {
+        "ip":"0.0.0.0",
+        "hostname":"kind-control-plane",
+        "afi":"ipv4",
+        "used":true
+      }
+    ]
+  }
+] }  }
+,
+"red":{
+ "vrfId": 5,
+ "vrfName": "red",
+ "tableVersion": 1,
+ "routerId": "172.31.0.4",
+ "defaultLocPrf": 100,
+ "localAS": 64512,
+ "routes": { "192.168.10.0/32": [
+  {
+    "valid":true,
+    "bestpath":true,
+    "pathFrom":"external",
+    "prefix":"192.168.10.0",
+    "prefixLen":32,
+    "network":"192.168.10.0\/32",
+    "metric":0,
+    "weight":32768,
+    "peerId":"(unspec)",
+    "path":"",
+    "origin":"IGP",
+    "nexthops":[
+      {
+        "ip":"0.0.0.0",
+        "hostname":"kind-control-plane",
+        "afi":"ipv4",
+        "used":true
+      }
+    ]
+  }
+] }  }
+}`
+
+func TestVRFs(t *testing.T) {
+	parsed, err := ParseVRFs(vrfs)
+	if err != nil {
+		t.Fatalf("Failed to parse %s", err)
+	}
+	expected := []string{"default", "red"}
+	if !cmp.Equal(parsed, expected) {
+		t.Fatalf("unexpected vrf list: %s", cmp.Diff(parsed, expected))
+	}
+}
diff --git a/e2etest/pkg/k8s/pods.go b/e2etest/pkg/k8s/pods.go
index 59a1e508..4006b233 100644
--- a/e2etest/pkg/k8s/pods.go
+++ b/e2etest/pkg/k8s/pods.go
@@ -37,3 +37,23 @@ func PodLogs(cs clientset.Interface, pod *corev1.Pod, podLogOpts corev1.PodLogOp
 	str := buf.String()
 	return str, nil
 }
+
+// PodIsReady returns the given pod's PodReady condition.
+func PodIsReady(p *corev1.Pod) bool {
+	return podConditionStatus(p, corev1.PodReady) == corev1.ConditionTrue
+}
+
+// podConditionStatus returns the status of the condition for a given pod.
+func podConditionStatus(p *corev1.Pod, condition corev1.PodConditionType) corev1.ConditionStatus {
+	if p == nil {
+		return corev1.ConditionUnknown
+	}
+
+	for _, c := range p.Status.Conditions {
+		if c.Type == condition {
+			return c.Status
+		}
+	}
+
+	return corev1.ConditionUnknown
+}
diff --git a/e2etest/pkg/metallb/metallb.go b/e2etest/pkg/metallb/metallb.go
index 3dd27b1b..846b0d3d 100644
--- a/e2etest/pkg/metallb/metallb.go
+++ b/e2etest/pkg/metallb/metallb.go
@@ -6,11 +6,15 @@ import (
 	"context"
 	"fmt"
 	"os"
+	"time"
 
 	"github.com/pkg/errors"
+	"go.universe.tf/e2etest/pkg/k8s"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/util/wait"
 	clientset "k8s.io/client-go/kubernetes"
+	"k8s.io/kubernetes/test/e2e/framework"
 )
 
 var (
@@ -53,7 +57,7 @@ func ControllerPod(cs clientset.Interface) (*corev1.Pod, error) {
 		LabelSelector: ControllerLabelSelector,
 	})
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to fetch controller pods")
+		framework.ExpectNoError(err, "failed to fetch controller pods")
 	}
 	if len(pods.Items) != 1 {
 		return nil, fmt.Errorf("expected one controller pod, found %d", len(pods.Items))
@@ -74,3 +78,26 @@ func SpeakerPodInNode(cs clientset.Interface, node string) (*corev1.Pod, error)
 	}
 	return nil, errors.Errorf("no speaker pod run in the node %s", node)
 }
+
+// RestartController restarts metallb's controller pod and waits for it to be running and ready.
+func RestartController(cs clientset.Interface) {
+	controllerPod, err := ControllerPod(cs)
+	framework.ExpectNoError(err)
+
+	err = cs.CoreV1().Pods(controllerPod.Namespace).Delete(context.TODO(), controllerPod.Name, metav1.DeleteOptions{})
+	framework.ExpectNoError(err)
+
+	err = wait.PollImmediate(5*time.Second, 3*time.Minute, func() (bool, error) {
+		pod, err := ControllerPod(cs)
+		if err != nil {
+			return false, nil
+		}
+		if controllerPod.Name == pod.Name {
+			return false, nil
+		}
+		isReady := (pod.Status.Phase == corev1.PodRunning) && (k8s.PodIsReady(pod))
+
+		return isReady, nil
+	})
+	framework.ExpectNoError(err)
+}
diff --git a/e2etest/pkg/service/validate.go b/e2etest/pkg/service/validate.go
index 5ed70258..9f957090 100644
--- a/e2etest/pkg/service/validate.go
+++ b/e2etest/pkg/service/validate.go
@@ -6,10 +6,12 @@ import (
 	"fmt"
 	"net"
 	"strconv"
+	"strings"
 
 	"go.universe.tf/e2etest/pkg/executor"
 	"go.universe.tf/e2etest/pkg/wget"
 	corev1 "k8s.io/api/core/v1"
+	"k8s.io/kubernetes/test/e2e/framework"
 	e2eservice "k8s.io/kubernetes/test/e2e/framework/service"
 )
 
@@ -25,3 +27,35 @@ func ValidateL2(svc *corev1.Service) error {
 	}
 	return nil
 }
+
+func ValidateDesiredLB(svc *corev1.Service) {
+	desiredLbIPs := svc.Annotations["metallb.universe.tf/loadBalancerIPs"]
+	if desiredLbIPs == "" {
+		return
+	}
+	framework.ExpectEqual(desiredLbIPs, strings.Join(getIngressIPs(svc.Status.LoadBalancer.Ingress), ","))
+}
+
+// ValidateAssignedWith validates that the service is assigned with the given ip.
+func ValidateAssignedWith(svc *corev1.Service, ip string) error {
+	if ip == "" {
+		return nil
+	}
+
+	ingressIPs := getIngressIPs(svc.Status.LoadBalancer.Ingress)
+	for _, ingressIP := range ingressIPs {
+		if ingressIP == ip {
+			return nil
+		}
+	}
+
+	return fmt.Errorf("validation failed: ip %s is not assigned to service %s", ip, svc.Name)
+}
+
+func getIngressIPs(ingresses []corev1.LoadBalancerIngress) []string {
+	var ips []string
+	for _, ingress := range ingresses {
+		ips = append(ips, ingress.IP)
+	}
+	return ips
+}
diff --git a/internal/allocator/allocator_test.go b/internal/allocator/allocator_test.go
index 651feeab..039d0800 100644
--- a/internal/allocator/allocator_test.go
+++ b/internal/allocator/allocator_test.go
@@ -45,7 +45,7 @@ func selector(s string) labels.Selector {
 
 func TestAssignment(t *testing.T) {
 	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"test": {
 			Name:       "test",
 			AutoAssign: true,
@@ -105,9 +105,7 @@ func TestAssignment(t *testing.T) {
 				ipnet("1000::9:0/120"),
 			},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	tests := []struct {
 		desc       string
@@ -651,7 +649,7 @@ func TestPoolAllocation(t *testing.T) {
 	// This test only allocates from the "test" pool, so it will run
 	// out of IPs quickly even though there are tons available in
 	// other pools.
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"not_this_one": {
 			Name:       "not_this_one",
 			AutoAssign: true,
@@ -672,9 +670,7 @@ func TestPoolAllocation(t *testing.T) {
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("10.20.30.0/24"), ipnet("fc00::2:0/120")},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	validIP4s := map[string]bool{
 		"1.2.3.4":  true,
@@ -1103,7 +1099,7 @@ func TestPoolAllocation(t *testing.T) {
 
 func TestAllocation(t *testing.T) {
 	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"test1": {
 			Name:       "test1",
 			AutoAssign: true,
@@ -1114,9 +1110,7 @@ func TestAllocation(t *testing.T) {
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("1.2.3.10/31"), ipnet("1000::10/127")},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	validIP4s := map[string]bool{
 		"1.2.3.4":  true,
@@ -1476,7 +1470,7 @@ func TestAllocation(t *testing.T) {
 
 func TestBuggyIPs(t *testing.T) {
 	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"test": {
 			Name:       "test",
 			AutoAssign: true,
@@ -1499,9 +1493,7 @@ func TestBuggyIPs(t *testing.T) {
 			AutoAssign:    true,
 			CIDR:          []*net.IPNet{ipnet("1.2.4.254/31")},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	validIPs := map[string]bool{
 		"1.2.3.0":   true,
@@ -1550,27 +1542,11 @@ func TestBuggyIPs(t *testing.T) {
 }
 
 func TestConfigReload(t *testing.T) {
-	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
-		"test": {
-			Name:       "test",
-			AutoAssign: true,
-			CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::/126")},
-		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
-	if err := alloc.Assign("s1", svc, []net.IP{net.ParseIP("1.2.3.0")}, nil, "", ""); err != nil {
-		t.Fatalf("Assign(s1, 1.2.3.0): %s", err)
-	}
-	if err := alloc.Assign("s2", svc, []net.IP{net.ParseIP("1000::")}, nil, "", ""); err != nil {
-		t.Fatalf("Assign(s2, 1000::): %s", err)
-	}
 	tests := []struct {
-		desc    string
-		pools   map[string]*config.Pool
-		wantErr bool
-		pool    string // Pool that 1.2.3.0 and 1000:: should be in
+		desc   string
+		pools  map[string]*config.Pool
+		poolS1 string // Pool that 1.2.3.0 should be in
+		poolS2 string
 	}{
 		{
 			desc: "set same config is no-op",
@@ -1581,7 +1557,8 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::/126")},
 				},
 			},
-			pool: "test",
+			poolS1: "test",
+			poolS2: "test",
 		},
 		{
 			desc: "expand pool",
@@ -1592,7 +1569,8 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.0/24"), ipnet("1000::/120")},
 				},
 			},
-			pool: "test",
+			poolS1: "test",
+			poolS2: "test",
 		},
 		{
 			desc: "shrink pool",
@@ -1603,10 +1581,11 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::/126")},
 				},
 			},
-			pool: "test",
+			poolS1: "test",
+			poolS2: "test",
 		},
 		{
-			desc: "can't shrink further",
+			desc: "can't shrink further, removing the service allocation",
 			pools: map[string]*config.Pool{
 				"test": {
 					Name:       "test",
@@ -1614,11 +1593,11 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.2/31"), ipnet("1000::0/126")},
 				},
 			},
-			pool:    "test",
-			wantErr: true,
+			poolS1: "",
+			poolS2: "test",
 		},
 		{
-			desc: "can't shrink further ipv6",
+			desc: "can't shrink further ipv6, removing the service allocation",
 			pools: map[string]*config.Pool{
 				"test": {
 					Name:       "test",
@@ -1626,8 +1605,8 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::2/127")},
 				},
 			},
-			pool:    "test",
-			wantErr: true,
+			poolS1: "test",
+			poolS2: "",
 		},
 		{
 			desc: "rename the pool",
@@ -1638,7 +1617,8 @@ func TestConfigReload(t *testing.T) {
 					CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::0/126")},
 				},
 			},
-			pool: "test2",
+			poolS1: "test2",
+			poolS2: "test2",
 		},
 		{
 			desc: "split pool",
@@ -1646,66 +1626,16 @@ func TestConfigReload(t *testing.T) {
 				"test": {
 					Name:       "test",
 					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.0/31"), ipnet("1000::/127")},
+					CIDR:       []*net.IPNet{ipnet("1.2.3.0/31"), ipnet("1000::2/127")},
 				},
 				"test2": {
 					Name:       "test2",
 					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.2/31"), ipnet("1000::2/127")},
+					CIDR:       []*net.IPNet{ipnet("1.2.3.2/31"), ipnet("1000::/127")},
 				},
 			},
-			pool: "test",
-		},
-		{
-			desc: "swap pool names",
-			pools: map[string]*config.Pool{
-				"test2": {
-					Name:       "test2",
-					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.0/31"), ipnet("1000::/127")},
-				},
-				"test": {
-					Name:       "test",
-					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.2/31"), ipnet("1000::2/127")},
-				},
-			},
-			pool: "test2",
-		},
-		{
-			desc: "delete used pool",
-			pools: map[string]*config.Pool{
-				"test": {
-					Name:       "test",
-					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.2/31"), ipnet("1000::/126")},
-				},
-			},
-			pool:    "test2",
-			wantErr: true,
-		},
-		{
-			desc: "delete used pool ipv6",
-			pools: map[string]*config.Pool{
-				"test": {
-					Name:       "test",
-					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::2/127")},
-				},
-			},
-			pool:    "test2",
-			wantErr: true,
-		},
-		{
-			desc: "delete unused pool",
-			pools: map[string]*config.Pool{
-				"test2": {
-					Name:       "test2",
-					AutoAssign: true,
-					CIDR:       []*net.IPNet{ipnet("1.2.3.0/31"), ipnet("1000::/127")},
-				},
-			},
-			pool: "test2",
+			poolS1: "test",
+			poolS2: "test2",
 		},
 		{
 			desc: "enable buggy IPs not allowed",
@@ -1717,30 +1647,47 @@ func TestConfigReload(t *testing.T) {
 					CIDR:          []*net.IPNet{ipnet("1.2.3.0/31"), ipnet("1000::/127")},
 				},
 			},
-			pool:    "test2",
-			wantErr: true,
+			poolS1: "",
+			poolS2: "test2",
 		},
 	}
 
 	for _, test := range tests {
-		err := alloc.SetPools(&config.Pools{ByName: test.pools})
-		if test.wantErr {
-			if err == nil {
-				t.Errorf("%q should have failed to SetPools, but succeeded", test.desc)
-			}
-		} else if err != nil {
-			t.Errorf("%q failed to SetPools: %s", test.desc, err)
+		alloc := New()
+		alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+			"test": {
+				Name:       "test",
+				AutoAssign: true,
+				CIDR:       []*net.IPNet{ipnet("1.2.3.0/30"), ipnet("1000::/126")},
+			},
+			"testunused": {
+				Name:       "unused",
+				AutoAssign: true,
+				CIDR:       []*net.IPNet{ipnet("1.2.4.0/30"), ipnet("1001::/126")},
+			},
+		}})
+		if err := alloc.Assign("s1", svc, []net.IP{net.ParseIP("1.2.3.0")}, nil, "", ""); err != nil {
+			t.Fatalf("Assign(s1, 1.2.3.0): %s", err)
+		}
+		if err := alloc.Assign("s2", svc, []net.IP{net.ParseIP("1000::")}, nil, "", ""); err != nil {
+			t.Fatalf("Assign(s2, 1000::): %s", err)
 		}
+
+		alloc.SetPools(&config.Pools{ByName: test.pools})
 		gotPool := alloc.Pool("s1")
-		if gotPool != test.pool {
-			t.Errorf("%q: s1 is in wrong pool, want %q, got %q", test.desc, test.pool, gotPool)
+		if gotPool != test.poolS1 {
+			t.Errorf("%q: s1 is in wrong pool, want %q, got %q", test.desc, test.poolS1, gotPool)
+		}
+		gotPool = alloc.Pool("s2")
+		if gotPool != test.poolS2 {
+			t.Errorf("%q: s2 is in wrong pool, want %q, got %q", test.desc, test.poolS2, gotPool)
 		}
 	}
 }
 
 func TestAutoAssign(t *testing.T) {
 	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"test1": {
 			Name:       "test1",
 			AutoAssign: false,
@@ -1751,9 +1698,7 @@ func TestAutoAssign(t *testing.T) {
 			AutoAssign: true,
 			CIDR:       []*net.IPNet{ipnet("1.2.3.10/31"), ipnet("1000::10/127")},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	validIP4s := map[string]bool{
 		"1.2.3.4":  false,
@@ -1988,7 +1933,7 @@ func TestPoolCount(t *testing.T) {
 
 func TestPoolMetrics(t *testing.T) {
 	alloc := New()
-	if err := alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
+	alloc.SetPools(&config.Pools{ByName: map[string]*config.Pool{
 		"test": {
 			Name:       "test",
 			AutoAssign: true,
@@ -1997,9 +1942,7 @@ func TestPoolMetrics(t *testing.T) {
 				ipnet("1000::4/126"),
 			},
 		},
-	}}); err != nil {
-		t.Fatalf("SetPools: %s", err)
-	}
+	}})
 
 	tests := []struct {
 		desc       string
diff --git a/internal/bgp/frr/frr_test.go b/internal/bgp/frr/frr_test.go
index 16132249..bfda0913 100644
--- a/internal/bgp/frr/frr_test.go
+++ b/internal/bgp/frr/frr_test.go
@@ -859,7 +859,7 @@ func TestTwoAdvertisementsTwoSessions(t *testing.T) {
 					SessionName:   "test-peer1"},
 			}
 			seed := time.Now().UnixNano()
-			rand.Seed(seed)
+			rand.New(rand.NewSource(seed))
 
 			rand.Shuffle(len(sessionsParameters), func(i, j int) {
 				sessionsParameters[i], sessionsParameters[j] = sessionsParameters[j], sessionsParameters[i]
diff --git a/internal/bgp/frr/testdata/TestBFDWithSession.golden b/internal/bgp/frr/testdata/TestBFDWithSession.golden
index 2607f5ea..7e534f1c 100644
--- a/internal/bgp/frr/testdata/TestBFDWithSession.golden
+++ b/internal/bgp/frr/testdata/TestBFDWithSession.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestLargeCommunities.golden b/internal/bgp/frr/testdata/TestLargeCommunities.golden
index 33b1141f..4603c28a 100644
--- a/internal/bgp/frr/testdata/TestLargeCommunities.golden
+++ b/internal/bgp/frr/testdata/TestLargeCommunities.golden
@@ -5,12 +5,14 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-300-ipv4-localpref-prefixes
   set local-preference 300
   on-match next
-ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes
   set community 3333:4444 additive
@@ -26,16 +28,19 @@ route-map 10.2.2.254-out permit 4
   set large-community 2222:3333:4444 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-out permit 5
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 6
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
index 0f9917cc..adc97c57 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisement.golden
@@ -5,32 +5,38 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-300-ipv4-localpref-prefixes
   set local-preference 300
   on-match next
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
-ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 3
   match ip address prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes
   set community 3333:4444 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-out permit 4
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 5
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
index 9902a985..13aeed6b 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChange.golden
@@ -6,16 +6,19 @@ ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
index 3db3b494..d566b087 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementChangeVRF.golden
@@ -6,16 +6,19 @@ ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 2
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
index 4aa7a366..935ccec6 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementNoRouterID.golden
@@ -6,16 +6,19 @@ ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
index 025535d0..4c19a659 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementNonExistingPeer.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
index 025535d0..4c19a659 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementStop.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
index ed618c0c..9a1f7e2c 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementVRF.golden
@@ -5,32 +5,38 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
-ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes
   set local-preference 300
   on-match next
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 2
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
-ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 3
   match ip address prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes
   set community 3333:4444 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 4
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 5
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
index 0f9917cc..adc97c57 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelector.golden
@@ -5,32 +5,38 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-300-ipv4-localpref-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-300-ipv4-localpref-prefixes
   set local-preference 300
   on-match next
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
-ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 3
   match ip address prefix-list 10.2.2.254-3333:4444-ipv4-community-prefixes
   set community 3333:4444 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-out permit 4
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 5
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
index ed618c0c..9a1f7e2c 100644
--- a/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
+++ b/internal/bgp/frr/testdata/TestSingleAdvertisementWithPeerSelectorVRF.golden
@@ -5,32 +5,38 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
-ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-300-ipv4-localpref-prefixes
   set local-preference 300
   on-match next
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 2
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
-ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 3
   match ip address prefix-list 10.2.2.254-red-3333:4444-ipv4-community-prefixes
   set community 3333:4444 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 4
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 5
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
index 025535d0..4c19a659 100644
--- a/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionMultiHop.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
index ade9336c..61b39cb5 100644
--- a/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleEBGPSessionOneHop.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 127.0.0.2-in deny 20
 
+
+
+
+ip prefix-list 127.0.0.2-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 127.0.0.2-pl-ipv4 seq 2 deny any
+
 route-map 127.0.0.2-out permit 1
   match ip address prefix-list 127.0.0.2-pl-ipv4
 route-map 127.0.0.2-out permit 2
   match ipv6 address prefix-list 127.0.0.2-pl-ipv4
 
-
-ip prefix-list 127.0.0.2-pl-ipv4 deny any
-ipv6 prefix-list 127.0.0.2-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
index 9a6e241e..98b23ed3 100644
--- a/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleIBGPSession.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden b/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
index 8e625ad1..75df6054 100644
--- a/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
+++ b/internal/bgp/frr/testdata/TestSingleIPv6EBGPSessionOneHop.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 127:0:0::2-in deny 20
 
+
+
+
+ip prefix-list 127:0:0::2-pl-ipv6 seq 1 deny any
+ipv6 prefix-list 127:0:0::2-pl-ipv6 seq 2 deny any
+
 route-map 127:0:0::2-out permit 1
   match ip address prefix-list 127:0:0::2-pl-ipv6
 route-map 127:0:0::2-out permit 2
   match ipv6 address prefix-list 127:0:0::2-pl-ipv6
 
-
-ip prefix-list 127:0:0::2-pl-ipv6 deny any
-ipv6 prefix-list 127:0:0::2-pl-ipv6 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden b/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
index 7bbef022..c036cbba 100644
--- a/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleIPv6IBGPSession.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10:2:2::254-in deny 20
 
+
+
+
+ip prefix-list 10:2:2::254-pl-ipv6 seq 1 deny any
+ipv6 prefix-list 10:2:2::254-pl-ipv6 seq 2 deny any
+
 route-map 10:2:2::254-out permit 1
   match ip address prefix-list 10:2:2::254-pl-ipv6
 route-map 10:2:2::254-out permit 2
   match ipv6 address prefix-list 10:2:2::254-pl-ipv6
 
-
-ip prefix-list 10:2:2::254-pl-ipv6 deny any
-ipv6 prefix-list 10:2:2::254-pl-ipv6 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleSessionExtras.golden b/internal/bgp/frr/testdata/TestSingleSessionExtras.golden
index 3f7e0eeb..26c3f21f 100644
--- a/internal/bgp/frr/testdata/TestSingleSessionExtras.golden
+++ b/internal/bgp/frr/testdata/TestSingleSessionExtras.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 127.0.0.2-in deny 20
 
+
+
+
+ip prefix-list 127.0.0.2-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 127.0.0.2-pl-ipv4 seq 2 deny any
+
 route-map 127.0.0.2-out permit 1
   match ip address prefix-list 127.0.0.2-pl-ipv4
 route-map 127.0.0.2-out permit 2
   match ipv6 address prefix-list 127.0.0.2-pl-ipv4
 
-
-ip prefix-list 127.0.0.2-pl-ipv4 deny any
-ipv6 prefix-list 127.0.0.2-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
index bf67975b..d0d9736f 100644
--- a/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
+++ b/internal/bgp/frr/testdata/TestSingleVRFIBGPSession.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 2
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
index 35808ed0..201b976f 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisements.golden
@@ -5,25 +5,30 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 2 permit 172.16.1.11/24
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 3
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
index 4f2ca952..7b0b85b1 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessions.golden
@@ -5,65 +5,79 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 3
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-out permit 4
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 5
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
-
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
 route-map 10.2.2.255-in deny 20
 
-ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.255-out permit 1
   match ip address prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.255-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 2
   match ip address prefix-list 10.2.2.255-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 3
   match ip address prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.255-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.255-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.255-out permit 4
   match ip address prefix-list 10.2.2.255-pl-ipv4
 route-map 10.2.2.255-out permit 5
   match ipv6 address prefix-list 10.2.2.255-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.255-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
index a25e4b91..ae06504a 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneVRF.golden
@@ -5,65 +5,79 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 3
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-out permit 4
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 5
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
-
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
 route-map 10.2.2.255-red-in deny 20
 
-ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.255-red-out permit 1
   match ip address prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-red-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.255-red-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.255-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.255-red-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-red-out permit 2
   match ip address prefix-list 10.2.2.255-red-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.255-red-out permit 3
   match ip address prefix-list 10.2.2.255-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-red-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.255-red-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.255-red-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.255-red-out permit 4
   match ip address prefix-list 10.2.2.255-red-pl-ipv4
 route-map 10.2.2.255-red-out permit 5
   match ipv6 address prefix-list 10.2.2.255-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.255-red-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
index 9c4745ef..87c8e7ae 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelector.golden
@@ -5,57 +5,69 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.254-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 2
   match ip address prefix-list 10.2.2.254-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.254-out permit 3
   match ip address prefix-list 10.2.2.254-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-out permit 4
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 5
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
-
-
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
 route-map 10.2.2.255-in deny 20
 
-ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 1
   match ip address prefix-list 10.2.2.255-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 2
   match ip address prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.255-pl-ipv4 seq 1 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.255-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.255-out permit 3
   match ip address prefix-list 10.2.2.255-pl-ipv4
 route-map 10.2.2.255-out permit 4
   match ipv6 address prefix-list 10.2.2.255-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.255-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
index 334a1575..36a8428a 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsOneWithPeerSelectorAndVRF.golden
@@ -5,57 +5,69 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.255-in deny 20
 
-ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 1
   match ip address prefix-list 10.2.2.255-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-out permit 2
   match ip address prefix-list 10.2.2.255-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.255-pl-ipv4 seq 1 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.255-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.255-out permit 3
   match ip address prefix-list 10.2.2.255-pl-ipv4
 route-map 10.2.2.255-out permit 4
   match ipv6 address prefix-list 10.2.2.255-pl-ipv4
-
-
-ipv6 prefix-list 10.2.2.255-pl-ipv4 deny any
 route-map 10.2.2.254-red-in deny 20
 
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
 
-ip prefix-list 10.2.2.254-red-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+ip prefix-list 10.2.2.254-red-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.254-red-out permit 2
   match ip address prefix-list 10.2.2.254-red-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 2 permit 172.16.1.11/24
 route-map 10.2.2.254-red-out permit 3
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 2 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-red-out permit 4
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 5
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
index 395a50e9..f559e768 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsTwoSessionsVRFWithPeerSelector.golden
@@ -5,44 +5,53 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.255-blue-in deny 20
 
-ip prefix-list 10.2.2.255-blue-2-ipv4-localpref-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-blue-2-ipv4-localpref-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-blue-out permit 1
   match ip address prefix-list 10.2.2.255-blue-2-ipv4-localpref-prefixes
   set local-preference 2
   on-match next
-ip prefix-list 10.2.2.255-blue-1111:2222-ipv4-community-prefixes permit 172.16.1.11/24
+
+ip prefix-list 10.2.2.255-blue-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.11/24
 route-map 10.2.2.255-blue-out permit 2
   match ip address prefix-list 10.2.2.255-blue-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.255-blue-pl-ipv4 permit 172.16.1.11/24
+
+ ip prefix-list 10.2.2.255-blue-pl-ipv4 seq 1 permit 172.16.1.11/24
+
+
+
+
+ipv6 prefix-list 10.2.2.255-blue-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.255-blue-out permit 3
   match ip address prefix-list 10.2.2.255-blue-pl-ipv4
 route-map 10.2.2.255-blue-out permit 4
   match ipv6 address prefix-list 10.2.2.255-blue-pl-ipv4
-
-
-ipv6 prefix-list 10.2.2.255-blue-pl-ipv4 deny any
 route-map 10.2.2.254-red-in deny 20
 
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 2
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 3
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf blue
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
index 4da8638f..5ebe0fed 100644
--- a/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoAdvertisementsVRF.golden
@@ -5,25 +5,30 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
-ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes permit 172.16.1.10/24
+
+ip prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes seq 1 permit 172.16.1.10/24
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-1111:2222-ipv4-community-prefixes
   set community 1111:2222 additive
   on-match next
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.10/24
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 permit 172.16.1.10/24
+
+
+
+ ip prefix-list 10.2.2.254-red-pl-ipv4 seq 2 permit 172.16.1.11/24
 
 
-ip prefix-list 10.2.2.254-red-pl-ipv4 permit 172.16.1.11/24
+
+
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 3 deny any
 
 route-map 10.2.2.254-red-out permit 2
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 3
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden b/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
index 81d4f7d8..605e4645 100644
--- a/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoIPv6Sessions.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10:2:2::254-in deny 20
 
+
+
+
+ip prefix-list 10:2:2::254-pl-ipv6 seq 1 deny any
+ipv6 prefix-list 10:2:2::254-pl-ipv6 seq 2 deny any
+
 route-map 10:2:2::254-out permit 1
   match ip address prefix-list 10:2:2::254-pl-ipv6
 route-map 10:2:2::254-out permit 2
   match ipv6 address prefix-list 10:2:2::254-pl-ipv6
+route-map 10:4:4::255-in deny 20
 
 
-ip prefix-list 10:2:2::254-pl-ipv6 deny any
-ipv6 prefix-list 10:2:2::254-pl-ipv6 deny any
-route-map 10:4:4::255-in deny 20
+
+
+ip prefix-list 10:4:4::255-pl-ipv6 seq 1 deny any
+ipv6 prefix-list 10:4:4::255-pl-ipv6 seq 2 deny any
 
 route-map 10:4:4::255-out permit 1
   match ip address prefix-list 10:4:4::255-pl-ipv6
 route-map 10:4:4::255-out permit 2
   match ipv6 address prefix-list 10:4:4::255-pl-ipv6
 
-
-ip prefix-list 10:4:4::255-pl-ipv6 deny any
-ipv6 prefix-list 10:4:4::255-pl-ipv6 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessions.golden b/internal/bgp/frr/testdata/TestTwoSessions.golden
index d06b0f0b..c41e6789 100644
--- a/internal/bgp/frr/testdata/TestTwoSessions.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessions.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.4.4.255-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-route-map 10.4.4.255-in deny 20
+
+
+ip prefix-list 10.4.4.255-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.4.4.255-pl-ipv4 seq 2 deny any
 
 route-map 10.4.4.255-out permit 1
   match ip address prefix-list 10.4.4.255-pl-ipv4
 route-map 10.4.4.255-out permit 2
   match ipv6 address prefix-list 10.4.4.255-pl-ipv4
 
-
-ip prefix-list 10.4.4.255-pl-ipv4 deny any
-ipv6 prefix-list 10.4.4.255-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
index 025535d0..4c19a659 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicate.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
index a332eeea..6df4c184 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsDuplicateRouter.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.4.4.255-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-route-map 10.4.4.255-in deny 20
+
+
+ip prefix-list 10.4.4.255-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.4.4.255-pl-ipv4 seq 2 deny any
 
 route-map 10.4.4.255-out permit 1
   match ip address prefix-list 10.4.4.255-pl-ipv4
 route-map 10.4.4.255-out permit 2
   match ipv6 address prefix-list 10.4.4.255-pl-ipv4
 
-
-ip prefix-list 10.4.4.255-pl-ipv4 deny any
-ipv6 prefix-list 10.4.4.255-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
index b515bf70..b151246b 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsOneVRF.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.4.4.255-red-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-route-map 10.4.4.255-red-in deny 20
+
+
+ip prefix-list 10.4.4.255-red-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.4.4.255-red-pl-ipv4 seq 2 deny any
 
 route-map 10.4.4.255-red-out permit 1
   match ip address prefix-list 10.4.4.255-red-pl-ipv4
 route-map 10.4.4.255-red-out permit 2
   match ipv6 address prefix-list 10.4.4.255-red-pl-ipv4
 
-
-ip prefix-list 10.4.4.255-red-pl-ipv4 deny any
-ipv6 prefix-list 10.4.4.255-red-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
index 1fc2d5e3..9a25d276 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsSameIPRouterIDASNVRF.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.2.2.254-red-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-route-map 10.2.2.254-red-in deny 20
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 2
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
index 921d1eec..b93421ea 100644
--- a/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
+++ b/internal/bgp/frr/testdata/TestTwoSessionsSameIPVRF.golden
@@ -5,25 +5,29 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-out permit 1
   match ip address prefix-list 10.2.2.254-pl-ipv4
 route-map 10.2.2.254-out permit 2
   match ipv6 address prefix-list 10.2.2.254-pl-ipv4
+route-map 10.2.2.254-red-in deny 20
 
 
-ip prefix-list 10.2.2.254-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-pl-ipv4 deny any
-route-map 10.2.2.254-red-in deny 20
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
 
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 2
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
index 72f5c65a..b96d6816 100644
--- a/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
+++ b/internal/bgp/frr/testdata/TestVRFSingleEBGPSessionMultiHop.golden
@@ -5,15 +5,17 @@ ip nht resolve-via-default
 ipv6 nht resolve-via-default
 route-map 10.2.2.254-red-in deny 20
 
+
+
+
+ip prefix-list 10.2.2.254-red-pl-ipv4 seq 1 deny any
+ipv6 prefix-list 10.2.2.254-red-pl-ipv4 seq 2 deny any
+
 route-map 10.2.2.254-red-out permit 1
   match ip address prefix-list 10.2.2.254-red-pl-ipv4
 route-map 10.2.2.254-red-out permit 2
   match ipv6 address prefix-list 10.2.2.254-red-pl-ipv4
 
-
-ip prefix-list 10.2.2.254-red-pl-ipv4 deny any
-ipv6 prefix-list 10.2.2.254-red-pl-ipv4 deny any
-
 router bgp 100 vrf red
   no bgp ebgp-requires-policy
   no bgp network import-check
diff --git a/internal/k8s/controllers/config_conversion_test.go b/internal/k8s/controllers/config_conversion_test.go
index 73092c0e..63cb1e9a 100644
--- a/internal/k8s/controllers/config_conversion_test.go
+++ b/internal/k8s/controllers/config_conversion_test.go
@@ -222,7 +222,7 @@ func TestConversionIsStable(t *testing.T) {
 		t.Fatalf("conversion failed, err %v", err)
 	}
 	seed := time.Now().UnixNano()
-	rand.Seed(seed)
+	rand.New(rand.NewSource(seed))
 
 	// Here we check the stability of the conversion, by shuffling the
 	// order of the resources and checking that the output is the same.
diff --git a/internal/k8s/controllers/service_controller_test.go b/internal/k8s/controllers/service_controller_test.go
index bdc60c7f..fc6e6e94 100644
--- a/internal/k8s/controllers/service_controller_test.go
+++ b/internal/k8s/controllers/service_controller_test.go
@@ -70,6 +70,7 @@ func TestServiceController(t *testing.T) {
 		shouldReprocessAll      bool
 		expectReconcileFails    bool
 		expectForceReloadCalled bool
+		initialLoadPerformed    bool
 	}{
 		{
 			desc:                    "call reconcileService, handler returns SyncStateSuccess",
@@ -79,6 +80,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reconcileService, handler returns SyncStateSuccess - with endpoints",
@@ -88,6 +90,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reconcileService, handler returns SyncStateSuccess - with endpointSlices",
@@ -97,6 +100,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reconcileService, handler returns SyncStateError",
@@ -106,6 +110,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    true,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reconcileService, handler returns SyncStateErrorNoRetry",
@@ -115,6 +120,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reconcileService, handler returns SyncStateReprocessAll",
@@ -124,6 +130,17 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      false,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: true,
+			initialLoadPerformed:    true,
+		},
+		{
+			desc:                    "call reconcileService, initialLoadPerformed initiated to false",
+			handlerRes:              SyncStateReprocessAll,
+			needEndPoints:           NoNeed,
+			initObjects:             []client.Object{testService},
+			shouldReprocessAll:      false,
+			expectReconcileFails:    false,
+			expectForceReloadCalled: false,
+			initialLoadPerformed:    false,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateSuccess",
@@ -133,6 +150,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateSuccess - with endpoints",
@@ -142,6 +160,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateSuccess - with endpointSlices",
@@ -151,6 +170,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateError",
@@ -160,6 +180,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    true,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateErrorNoRetry",
@@ -169,6 +190,7 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    false,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
 		},
 		{
 			desc:                    "call reprocessAll, handler returns SyncStateReprocessAll",
@@ -178,6 +200,17 @@ func TestServiceController(t *testing.T) {
 			shouldReprocessAll:      true,
 			expectReconcileFails:    true,
 			expectForceReloadCalled: false,
+			initialLoadPerformed:    true,
+		},
+		{
+			desc:                    "call reprocessAll, initialLoadPerformed initiated to false",
+			handlerRes:              SyncStateSuccess,
+			needEndPoints:           NoNeed,
+			initObjects:             []client.Object{testService},
+			shouldReprocessAll:      true,
+			expectReconcileFails:    false,
+			expectForceReloadCalled: false,
+			initialLoadPerformed:    false,
 		},
 	}
 	for _, test := range tests {
@@ -207,15 +240,16 @@ func TestServiceController(t *testing.T) {
 		mockReload := make(chan event.GenericEvent, 1)
 
 		r := &ServiceReconciler{
-			Client:    fakeClient,
-			Logger:    log.NewNopLogger(),
-			Scheme:    scheme,
-			Namespace: testNamespace,
-			Handler:   mockHandler,
-			Endpoints: test.needEndPoints,
-			Reload:    mockReload,
+			Client:               fakeClient,
+			Logger:               log.NewNopLogger(),
+			Scheme:               scheme,
+			Namespace:            testNamespace,
+			Handler:              mockHandler,
+			Endpoints:            test.needEndPoints,
+			Reload:               mockReload,
+			initialLoadPerformed: false,
 		}
-
+		r.initialLoadPerformed = test.initialLoadPerformed
 		var req reconcile.Request
 		if test.shouldReprocessAll {
 			req = reconcile.Request{
@@ -254,6 +288,9 @@ func TestServiceController(t *testing.T) {
 			t.Errorf("test %s failed: call force reload expected: %v, got: %v",
 				test.desc, test.expectForceReloadCalled, calledForceReload)
 		}
+		if test.shouldReprocessAll && !r.initialLoadPerformed {
+			t.Errorf("test %s failed: reconciler's initialLoadPerformed flag didn't change to true", test.desc)
+		}
 	}
 }
 
diff --git a/speaker/bgp_controller_test.go b/speaker/bgp_controller_test.go
index af324184..6db53b7f 100644
--- a/speaker/bgp_controller_test.go
+++ b/speaker/bgp_controller_test.go
@@ -1505,6 +1505,44 @@ func TestBGPSpeakerEPSlices(t *testing.T) {
 			},
 		},
 
+		{
+			desc:     "Endpoint list contains serving but not ready endpoints",
+			balancer: "test1",
+			svc: &v1.Service{
+				Spec: v1.ServiceSpec{
+					Type:                  "LoadBalancer",
+					ExternalTrafficPolicy: "Cluster",
+				},
+				Status: statusAssigned("10.20.30.1"),
+			},
+			eps: epslices.EpsOrSlices{
+				SlicesVal: []discovery.EndpointSlice{
+					{
+						Endpoints: []discovery.Endpoint{
+							{
+								Addresses: []string{
+									"2.3.4.5",
+								},
+								NodeName: stringPtr("iris"),
+								Conditions: discovery.EndpointConditions{
+									Ready:   pointer.BoolPtr(false),
+									Serving: pointer.BoolPtr(true),
+								},
+							},
+						},
+					},
+				},
+				Type: epslices.Slices,
+			},
+			wantAds: map[string][]*bgp.Advertisement{
+				"1.2.3.4:0": {
+					{
+						Prefix: ipnet("10.20.30.1/32"),
+					},
+				},
+			},
+		},
+
 		{
 			desc: "Multiple advertisement config",
 			config: &config.Config{
diff --git a/speaker/layer2_controller_test.go b/speaker/layer2_controller_test.go
index 4c8815de..7f2fbef3 100644
--- a/speaker/layer2_controller_test.go
+++ b/speaker/layer2_controller_test.go
@@ -151,6 +151,11 @@ func TestUsableNodes(t *testing.T) {
 }
 
 func TestUsableNodesEPSlices(t *testing.T) {
+	b := &fakeBGP{
+		t: t,
+	}
+	newBGP = b.NewSessionManager
+
 	c, err := newController(controllerConfig{
 		MyNode: "iris1",
 		Logger: log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)),
@@ -302,6 +307,40 @@ func TestUsableNodesEPSlices(t *testing.T) {
 			usableSpeakers:  map[string]bool{"iris1": true, "iris2": true},
 			cExpectedResult: []string{"iris1"},
 		},
+		{
+			desc: "Two endpoints, different hosts, not ready but serving",
+			eps: epslices.EpsOrSlices{
+				SlicesVal: []discovery.EndpointSlice{
+					{
+						Endpoints: []discovery.Endpoint{
+							{
+								Addresses: []string{
+									"2.3.4.5",
+								},
+								NodeName: stringPtr("iris1"),
+								Conditions: discovery.EndpointConditions{
+									Ready:   pointer.BoolPtr(false),
+									Serving: pointer.BoolPtr(true),
+								},
+							},
+							{
+								Addresses: []string{
+									"2.3.4.15",
+								},
+								NodeName: stringPtr("iris2"),
+								Conditions: discovery.EndpointConditions{
+									Ready:   pointer.BoolPtr(false),
+									Serving: pointer.BoolPtr(true),
+								},
+							},
+						},
+					},
+				},
+				Type: epslices.Slices,
+			},
+			usableSpeakers:  map[string]bool{"iris1": true, "iris2": true},
+			cExpectedResult: []string{"iris1", "iris2"},
+		},
 	}
 
 	for _, test := range tests {
@@ -1367,7 +1406,8 @@ func TestShouldAnnounceEPSlices(t *testing.T) {
 									},
 									NodeName: stringPtr("iris2"),
 									Conditions: discovery.EndpointConditions{
-										Ready: pointer.BoolPtr(true),
+										Ready:   pointer.BoolPtr(false),
+										Serving: pointer.BoolPtr(true),
 									},
 								},
 							},