Repository: metallb/metallb
Pull request #1841 (2023-02-21 00:52:36)

enable gofmt linters and improve code format
Improve code format

pperiyasamy: @my-git9 Can you also add a task for format check in [tasks.py](https://github.com/metallb/metallb/blob/main/tasks.py) and have it run from [CI](https://github.com/metallb/metallb/blob/main/.github/workflows/ci.yaml) as well ?
fedepaol: > @my-git9 Can you also add a task for format check in [tasks.py](https://github.com/metallb/metallb/blob/main/tasks.py) and have it run from [CI](https://github.com/metallb/metallb/blob/main/.github/workflows/ci.yaml) as well ?

I added this https://github.com/metallb/metallb/issues/1860
I think the majority of these checks have a linter that will find them (same for https://github.com/metallb/metallb/pull/1827  and https://github.com/metallb/metallb/pull/1826 I guess)

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index ae08778d..a22cb0b1 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -56,7 +56,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
 
-      - uses: actions/setup-go@v3
+      - uses: actions/setup-go@v4
         with:
           go-version-file: "go.mod"
           cache: true
@@ -96,7 +96,7 @@ jobs:
         uses: docker/setup-buildx-action@v2
 
       - name: Build and export ${{ matrix.image }}
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         with:
           context: .
           tags: quay.io/metallb/${{ matrix.image }}:dev-amd64
diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml
index 86fe71f1..599caf3b 100644
--- a/.github/workflows/publish.yaml
+++ b/.github/workflows/publish.yaml
@@ -19,7 +19,7 @@ jobs:
       - name: Checkout
         uses: actions/checkout@v3
 
-      - uses: actions/setup-go@v3
+      - uses: actions/setup-go@v4
         with:
           go-version-file: "go.mod"
           cache: true
@@ -94,7 +94,7 @@ jobs:
             org.opencontainers.image.description=${{ matrix.image }} for metallb, a network load-balancer implementation for Kubernetes using standard routing protocols
 
       - name: Build and push ${{ matrix.image }}
-        uses: docker/build-push-action@v3
+        uses: docker/build-push-action@v4
         id: build-and-push
         with:
           context: .
diff --git a/.golangci.yml b/.golangci.yml
index 8dfb1a06..b6cc9ad8 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -2,6 +2,7 @@ linters:
   enable:
   - godot
   - goimports
+  - gofmt
 linters-settings:
   errcheck:
     exclude: .golangci.exclude
diff --git a/charts/metallb/README.md b/charts/metallb/README.md
index 11a9b077..29f7eb12 100644
--- a/charts/metallb/README.md
+++ b/charts/metallb/README.md
@@ -110,6 +110,7 @@ Kubernetes: `>= 1.19.0-0`
 | rbac.create | bool | `true` |  |
 | speaker.affinity | object | `{}` |  |
 | speaker.enabled | bool | `true` |  |
+| speaker.excludeInterfaces.enabled | bool | `true` |  |
 | speaker.frr.enabled | bool | `false` |  |
 | speaker.frr.image.pullPolicy | string | `nil` |  |
 | speaker.frr.image.repository | string | `"quay.io/frrouting/frr"` |  |
diff --git a/charts/metallb/templates/exclude-l2-config.yaml b/charts/metallb/templates/exclude-l2-config.yaml
new file mode 100644
index 00000000..7cc2ff34
--- /dev/null
+++ b/charts/metallb/templates/exclude-l2-config.yaml
@@ -0,0 +1,22 @@
+{{- if .Values.speaker.excludeInterfaces.enabled }}
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude:
+    - docker.*
+    - cbr.*
+    - dummy.*
+    - virbr.*
+    - lxcbr.*
+    - veth.*
+    - lo
+    - ^cali.*
+    - ^tunl.*
+    - flannel.*
+    - kube-ipvs.*
+    - cni.*
+    - ^nodelocaldns.*
+{{- end }}
\ No newline at end of file
diff --git a/charts/metallb/templates/servicemonitor.yaml b/charts/metallb/templates/servicemonitor.yaml
index 5c2ab422..43825b46 100644
--- a/charts/metallb/templates/servicemonitor.yaml
+++ b/charts/metallb/templates/servicemonitor.yaml
@@ -58,14 +58,6 @@ spec:
   selector:
     matchLabels:
       name: {{ template "metallb.fullname" . }}-speaker-monitor-service
-{{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
-  metricRelabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 4 }}
-{{- end }}
-{{- if .Values.prometheus.serviceMonitor.relabelings }}
-  relabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 4 }}
-{{- end }}      
 ---
 apiVersion: v1
 kind: Service
@@ -112,6 +104,14 @@ metadata:
 spec:
   endpoints:
     - port: {{ template "metrics.exposedportname" . }}
+      {{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
+      metricRelabelings:
+      {{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 8 }}
+      {{- end -}}
+      {{- if .Values.prometheus.serviceMonitor.relabelings }}
+      relabelings:
+      {{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 8 }}
+      {{- end }}
       {{- if .Values.prometheus.serviceMonitor.interval }}
       interval: {{ .Values.prometheus.serviceMonitor.interval }}
       {{- end }}
@@ -131,14 +131,6 @@ spec:
   selector:
     matchLabels:
       name: {{ template "metallb.fullname" . }}-controller-monitor-service
-{{- if .Values.prometheus.serviceMonitor.metricRelabelings }}
-  metricRelabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.metricRelabelings | nindent 4 }}
-{{- end }}
-{{- if .Values.prometheus.serviceMonitor.relabelings }}
-  relabelings:
-{{- toYaml .Values.prometheus.serviceMonitor.relabelings | nindent 4 }}
-{{- end }}      
 ---
 apiVersion: v1
 kind: Service
diff --git a/charts/metallb/templates/speaker.yaml b/charts/metallb/templates/speaker.yaml
index c94eb615..d982f2f5 100644
--- a/charts/metallb/templates/speaker.yaml
+++ b/charts/metallb/templates/speaker.yaml
@@ -159,6 +159,12 @@ spec:
             secretName: {{ include "metallb.secretName" . }}
             defaultMode: 420
       {{- end }}
+      {{- if .Values.speaker.excludeInterfaces.enabled }}
+        - name: metallb-excludel2
+          configMap:
+            defaultMode: 256
+            name: metallb-excludel2
+      {{- end }}
       {{- if .Values.speaker.frr.enabled }}
         - name: frr-sockets
           emptyDir: {}
@@ -297,7 +303,7 @@ spec:
             - ALL
             add:
             - NET_RAW
-        {{- if or .Values.speaker.frr.enabled .Values.speaker.memberlist.enabled }}
+        {{- if or .Values.speaker.frr.enabled .Values.speaker.memberlist.enabled .Values.speaker.excludeInterfaces.enabled }}
         volumeMounts:
           {{- if .Values.speaker.memberlist.enabled }}
           - name: memberlist 
@@ -307,6 +313,10 @@ spec:
           - name: reloader
             mountPath: /etc/frr_reloader
           {{- end }}
+          {{- if .Values.speaker.excludeInterfaces.enabled }}
+          - name: metallb-excludel2
+            mountPath: /etc/metallb
+          {{- end }}
         {{- end }}
       {{- if .Values.speaker.frr.enabled }}
       - name: frr
diff --git a/charts/metallb/values.schema.json b/charts/metallb/values.schema.json
index 9d3516d0..5a92e56a 100644
--- a/charts/metallb/values.schema.json
+++ b/charts/metallb/values.schema.json
@@ -322,6 +322,14 @@
                 }
               }
             },
+            "excludeInterfaces": {
+              "type": "object",
+              "properties": {
+                "enabled": {
+                  "type": "boolean"
+                }
+              }
+            },
             "updateStrategy": {
               "type": "object",
               "properties": {
diff --git a/charts/metallb/values.yaml b/charts/metallb/values.yaml
index 1844f8fb..689424ca 100644
--- a/charts/metallb/values.yaml
+++ b/charts/metallb/values.yaml
@@ -261,6 +261,8 @@ speaker:
     enabled: true
     mlBindPort: 7946
     mlSecretKeyPath: "/etc/ml_secret_key"
+  excludeInterfaces:
+    enabled: true
   image:
     repository: quay.io/metallb/speaker
     tag:
diff --git a/config/controllers/speaker.yaml b/config/controllers/speaker.yaml
index bdf73c96..e45a2d32 100644
--- a/config/controllers/speaker.yaml
+++ b/config/controllers/speaker.yaml
@@ -85,6 +85,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -102,3 +105,7 @@ spec:
         secret: 
           secretName: memberlist
           defaultMode: 420
+      - name: metallb-excludel2
+        configMap:
+          defaultMode: 256
+          name: metallb-excludel2
diff --git a/config/manifests/metallb-frr-prometheus.yaml b/config/manifests/metallb-frr-prometheus.yaml
index 3443f9e5..93529105 100644
--- a/config/manifests/metallb-frr-prometheus.yaml
+++ b/config/manifests/metallb-frr-prometheus.yaml
@@ -1813,6 +1813,15 @@ metadata:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -2194,6 +2203,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       initContainers:
       - command:
@@ -2256,6 +2268,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: monitoring.coreos.com/v1
 kind: ServiceMonitor
diff --git a/config/manifests/metallb-frr.yaml b/config/manifests/metallb-frr.yaml
index 404daaca..d2710bd5 100644
--- a/config/manifests/metallb-frr.yaml
+++ b/config/manifests/metallb-frr.yaml
@@ -1758,6 +1758,15 @@ metadata:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -2017,6 +2026,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       initContainers:
       - command:
@@ -2079,6 +2091,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
diff --git a/config/manifests/metallb-native-prometheus.yaml b/config/manifests/metallb-native-prometheus.yaml
index 3232cd84..45025fdc 100644
--- a/config/manifests/metallb-native-prometheus.yaml
+++ b/config/manifests/metallb-native-prometheus.yaml
@@ -1715,6 +1715,15 @@ subjects:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -1988,6 +1997,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -2005,6 +2017,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: monitoring.coreos.com/v1
 kind: ServiceMonitor
diff --git a/config/manifests/metallb-native.yaml b/config/manifests/metallb-native.yaml
index ffdf2416..26131219 100644
--- a/config/manifests/metallb-native.yaml
+++ b/config/manifests/metallb-native.yaml
@@ -1660,6 +1660,15 @@ subjects:
   namespace: metallb-system
 ---
 apiVersion: v1
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+---
+apiVersion: v1
 kind: Secret
 metadata:
   name: webhook-server-cert
@@ -1842,6 +1851,9 @@ spec:
         - mountPath: /etc/ml_secret_key
           name: memberlist
           readOnly: true
+        - mountPath: /etc/metallb
+          name: metallb-excludel2
+          readOnly: true
       hostNetwork: true
       nodeSelector:
         kubernetes.io/os: linux
@@ -1859,6 +1871,10 @@ spec:
         secret:
           defaultMode: 420
           secretName: memberlist
+      - configMap:
+          defaultMode: 256
+          name: metallb-excludel2
+        name: metallb-excludel2
 ---
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
diff --git a/config/native/exclude-l2-config.yaml b/config/native/exclude-l2-config.yaml
new file mode 100644
index 00000000..25a80a00
--- /dev/null
+++ b/config/native/exclude-l2-config.yaml
@@ -0,0 +1,8 @@
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: metallb-excludel2
+  namespace: metallb-system
+data:
+  excludel2.yaml: |
+    announcedInterfacesToExclude: ["docker.*", "cbr.*", "dummy.*", "virbr.*", "lxcbr.*", "veth.*", "lo", "^cali.*", "^tunl.*", "flannel.*", "kube-ipvs.*", "cni.*", "^nodelocaldns.*"]
diff --git a/config/native/kustomization.yaml b/config/native/kustomization.yaml
index 2c741aaa..6085bacc 100644
--- a/config/native/kustomization.yaml
+++ b/config/native/kustomization.yaml
@@ -2,6 +2,7 @@ apiVersion: kustomize.config.k8s.io/v1beta1
 kind: Kustomization
 resources:
 - ns.yaml
+- exclude-l2-config.yaml
 - ../crd
 - ../rbac
 - ../controllers
diff --git a/controller/service.go b/controller/service.go
index f78f6ebc..e6ccdae7 100644
--- a/controller/service.go
+++ b/controller/service.go
@@ -96,6 +96,7 @@ func (c *controller) convergeBalancer(l log.Logger, key string, svc *v1.Service)
 		// otherwise it'll fail and tell us why.
 		if err = c.ips.Assign(key, svc, lbIPs, k8salloc.Ports(svc), k8salloc.SharingKey(svc), k8salloc.BackendKey(svc)); err != nil {
 			level.Info(l).Log("event", "clearAssignment", "error", err, "msg", "current IP not allowed by config, clearing")
+			c.client.Infof(svc, "ClearAssignment", "current IP for %q not allowed by config, will attempt for new IP assignment: %s", key, err)
 			c.clearServiceState(key, svc)
 			lbIPs = []net.IP{}
 		}
diff --git a/go.mod b/go.mod
index 3ce21dae..81015b3d 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/mikioh/ipaddr v0.0.0-20190404000644-d465c8ab6721
 	github.com/onsi/ginkgo/v2 v2.6.0
 	github.com/onsi/gomega v1.24.1
-	github.com/open-policy-agent/cert-controller v0.6.0
+	github.com/open-policy-agent/cert-controller v0.7.0
 	github.com/openshift-kni/k8sreporter v1.0.2
 	github.com/ory/dockertest/v3 v3.9.1
 	github.com/osrg/gobgp v2.0.0+incompatible
@@ -23,15 +23,15 @@ require (
 	github.com/prometheus/client_model v0.3.0
 	github.com/prometheus/common v0.39.0
 	golang.org/x/sync v0.1.0
-	golang.org/x/sys v0.4.0
+	golang.org/x/sys v0.6.0
 	k8s.io/api v0.26.1
 	k8s.io/apiextensions-apiserver v0.26.1
 	k8s.io/apimachinery v0.26.1
-	k8s.io/client-go v1.5.2
+	k8s.io/client-go v0.26.1
 	k8s.io/klog v1.0.0
 	k8s.io/kubernetes v1.26.1
 	k8s.io/pod-security-admission v0.0.0
-	sigs.k8s.io/controller-runtime v0.14.1
+	sigs.k8s.io/controller-runtime v0.14.5
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -135,10 +135,10 @@ require (
 	go.uber.org/multierr v1.7.0 // indirect
 	go.uber.org/zap v1.24.0 // indirect
 	golang.org/x/crypto v0.5.0 // indirect
-	golang.org/x/net v0.5.0 // indirect
+	golang.org/x/net v0.7.0 // indirect
 	golang.org/x/oauth2 v0.4.0 // indirect
-	golang.org/x/term v0.4.0 // indirect
-	golang.org/x/text v0.6.0 // indirect
+	golang.org/x/term v0.5.0 // indirect
+	golang.org/x/text v0.7.0 // indirect
 	golang.org/x/time v0.3.0 // indirect
 	gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 11b6d18b..e14d1a1d 100644
--- a/go.sum
+++ b/go.sum
@@ -400,8 +400,9 @@ github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ
 github.com/onsi/gomega v1.23.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
 github.com/onsi/gomega v1.24.1 h1:KORJXNNTzJXzu4ScJWssJfJMnJ+2QJqhoQSRwNlze9E=
 github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
-github.com/open-policy-agent/cert-controller v0.6.0 h1:HBhe1kS0GTk5dRHdklwgJKoGIctWisueIYnIYJu65Q0=
-github.com/open-policy-agent/cert-controller v0.6.0/go.mod h1:uOQW+2tMU51vSxy1Yt162oVUTMdqLuotC0aObQxrh6k=
+github.com/open-policy-agent/cert-controller v0.7.0 h1:5ggZjSQJ1YgkT+ngNAGBGHaOYfGuYq97IrNldchCoHI=
+github.com/open-policy-agent/cert-controller v0.7.0/go.mod h1:Dkkdcr1BeSUig/62dYLqgvx3lIN0XtEikKTGW1lKSQo=
+github.com/open-policy-agent/frameworks/constraint v0.0.0-20230201235642-777dc99a6669 h1:vKt4PhZXBxYHeLujYraNVpkoILQ/NISiifzaq1DkMXk=
 github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
 github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -670,8 +671,8 @@ golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
 golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.3.1-0.20221206200815-1e63c2f08a10/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
-golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
+golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g=
+golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -753,14 +754,14 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
+golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
-golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
-golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
+golang.org/x/term v0.5.0 h1:n2a8QNdAb0sZNpU9R1ALUXBbY+w51fCQDN+7EdxNBsY=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -771,8 +772,8 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1019,8 +1020,8 @@ rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33 h1:LYqFq+6Cj2D0gFfrJvL7iElD4ET6ir3VDdhDdTK7rgc=
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.33/go.mod h1:soWkSNf2tZC7aMibXEqVhCd73GOY5fJikn8qbdzemB0=
-sigs.k8s.io/controller-runtime v0.14.1 h1:vThDes9pzg0Y+UbCPY3Wj34CGIYPgdmspPm2GIpxpzM=
-sigs.k8s.io/controller-runtime v0.14.1/go.mod h1:GaRkrY8a7UZF0kqFFbUKG7n9ICiTY5T55P1RiE3UZlU=
+sigs.k8s.io/controller-runtime v0.14.5 h1:6xaWFqzT5KuAQ9ufgUaj1G/+C4Y1GRkhrxl+BJ9i+5s=
+sigs.k8s.io/controller-runtime v0.14.5/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
 sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
diff --git a/internal/bgp/native/native.go b/internal/bgp/native/native.go
index a67b957f..d6084775 100644
--- a/internal/bgp/native/native.go
+++ b/internal/bgp/native/native.go
@@ -15,7 +15,6 @@ import (
 	"sync"
 	"syscall"
 	"time"
-	"unsafe"
 
 	"github.com/go-kit/log"
 	"github.com/go-kit/log/level"
@@ -489,25 +488,6 @@ func (s *session) Close() error {
 	return nil
 }
 
-const (
-	// TCP MD5 Signature (RFC2385).
-	tcpMD5SIG = 14
-)
-
-// This  struct is defined at; linux-kernel: include/uapi/linux/tcp.h,
-// It  must be kept in sync with that definition, see current version:
-// https://github.com/torvalds/linux/blob/v4.16/include/uapi/linux/tcp.h#L253.
-
-//nolint:structcheck
-type tcpmd5sig struct {
-	ssFamily uint16
-	ss       [126]byte
-	pad1     uint16
-	keylen   uint16
-	pad2     uint32
-	key      [80]byte
-}
-
 // DialTCP does the part of creating a connection manually,  including setting the
 // proper TCP MD5 options when the password is not empty. Works by manupulating
 // the low level FD's, skipping the net.Conn API as it has not hooks to set
@@ -592,9 +572,8 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 
 	if password != "" {
 		sig := buildTCPMD5Sig(raddr.IP, password)
-		b := *(*[unsafe.Sizeof(sig)]byte)(unsafe.Pointer(&sig))
 		// Better way may be available in  Go 1.11, see go-review.googlesource.com/c/go/+/72810
-		if err = os.NewSyscallError("setsockopt", unix.SetsockoptString(fd, unix.IPPROTO_TCP, tcpMD5SIG, string(b[:]))); err != nil {
+		if err = os.NewSyscallError("setsockopt", unix.SetsockoptTCPMD5Sig(fd, unix.IPPROTO_TCP, unix.TCP_MD5SIG, sig)); err != nil {
 			return nil, err
 		}
 	}
@@ -665,20 +644,20 @@ func dialMD5(ctx context.Context, addr string, srcAddr net.IP, password string)
 	}
 }
 
-func buildTCPMD5Sig(addr net.IP, key string) tcpmd5sig {
-	t := tcpmd5sig{}
+func buildTCPMD5Sig(addr net.IP, key string) *unix.TCPMD5Sig {
+	t := unix.TCPMD5Sig{}
 	if addr.To4() != nil {
-		t.ssFamily = unix.AF_INET
-		copy(t.ss[2:], addr.To4())
+		t.Addr.Family = unix.AF_INET
+		copy(t.Addr.Data[2:], addr.To4())
 	} else {
-		t.ssFamily = unix.AF_INET6
-		copy(t.ss[6:], addr.To16())
+		t.Addr.Family = unix.AF_INET6
+		copy(t.Addr.Data[6:], addr.To16())
 	}
 
-	t.keylen = uint16(len(key))
-	copy(t.key[0:], []byte(key))
+	t.Keylen = uint16(len(key))
+	copy(t.Key[0:], []byte(key))
 
-	return t
+	return &t
 }
 
 // localAddressExists returns true if the address addr exists on any of the
diff --git a/internal/config/config.go b/internal/config/config.go
index dfd2600f..83b6f67d 100644
--- a/internal/config/config.go
+++ b/internal/config/config.go
@@ -524,8 +524,22 @@ func addressPoolServiceAllocationsFromCR(p metallbv1beta1.IPAddressPool, namespa
 	if p.Spec.AllocateTo == nil {
 		return nil, nil
 	}
-	serviceAllocations := &ServiceAllocation{Priority: p.Spec.AllocateTo.Priority,
-		Namespaces: sets.New(p.Spec.AllocateTo.Namespaces...)}
+	poolNamespaces := sets.Set[string]{}
+	for _, poolNs := range p.Spec.AllocateTo.Namespaces {
+		if poolNamespaces.Has(poolNs) {
+			return nil, errors.New("duplicate definition in namespaces field")
+		}
+		poolNamespaces.Insert(poolNs)
+	}
+	err := validateLabelSelectorDuplicate(p.Spec.AllocateTo.NamespaceSelectors, "namespaceSelectors")
+	if err != nil {
+		return nil, err
+	}
+	err = validateLabelSelectorDuplicate(p.Spec.AllocateTo.ServiceSelectors, "serviceSelectors")
+	if err != nil {
+		return nil, err
+	}
+	serviceAllocations := &ServiceAllocation{Priority: p.Spec.AllocateTo.Priority, Namespaces: poolNamespaces}
 	for i := range p.Spec.AllocateTo.NamespaceSelectors {
 		l, err := metav1.LabelSelectorAsSelector(&p.Spec.AllocateTo.NamespaceSelectors[i])
 		if err != nil {
diff --git a/internal/layer2/announcer.go b/internal/layer2/announcer.go
index 65a0fbb3..4ae3a46d 100644
--- a/internal/layer2/announcer.go
+++ b/internal/layer2/announcer.go
@@ -5,6 +5,7 @@ package layer2
 import (
 	"net"
 	"os"
+	"regexp"
 	"strconv"
 	"sync"
 	"time"
@@ -26,11 +27,12 @@ type Announce struct {
 
 	// This channel can block - do not write to it while holding the mutex
 	// to avoid deadlocking.
-	spamCh chan IPAdvertisement
+	spamCh        chan IPAdvertisement
+	excludeRegexp *regexp.Regexp
 }
 
 // New returns an initialized Announce.
-func New(l log.Logger) (*Announce, error) {
+func New(l log.Logger, excludeRegexp *regexp.Regexp) (*Announce, error) {
 	ret := &Announce{
 		logger:         l,
 		nodeInterfaces: []string{},
@@ -39,7 +41,9 @@ func New(l log.Logger) (*Announce, error) {
 		ips:            map[string][]IPAdvertisement{},
 		ipRefcnt:       map[string]int{},
 		spamCh:         make(chan IPAdvertisement, 1024),
+		excludeRegexp:  excludeRegexp,
 	}
+
 	go ret.interfaceScan()
 	go ret.spamLoop()
 
@@ -67,6 +71,12 @@ func (a *Announce) updateInterfaces() {
 	curIfs := make([]string, 0, len(ifs))
 	for _, intf := range ifs {
 		ifi := intf
+
+		if (a.excludeRegexp != nil) && a.excludeRegexp.MatchString(ifi.Name) {
+			level.Info(a.logger).Log("event", "announced interface to exclude", "interface", ifi.Name)
+			continue
+		}
+
 		curIfs = append(curIfs, ifi.Name)
 		l := log.With(a.logger, "interface", ifi.Name)
 		addrs, err := ifi.Addrs()
diff --git a/speaker/main.go b/speaker/main.go
index e372fc71..8b944df3 100644
--- a/speaker/main.go
+++ b/speaker/main.go
@@ -21,6 +21,8 @@ import (
 	"os"
 	"os/signal"
 	"path/filepath"
+	"regexp"
+	"strings"
 	"syscall"
 
 	"github.com/go-kit/log"
@@ -37,6 +39,7 @@ import (
 	"go.universe.tf/metallb/internal/speakerlist"
 	"go.universe.tf/metallb/internal/version"
 	v1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/yaml"
 )
 
 var announcing = prometheus.NewGaugeVec(prometheus.GaugeOpts{
@@ -51,6 +54,10 @@ var announcing = prometheus.NewGaugeVec(prometheus.GaugeOpts{
 	"ip",
 })
 
+const (
+	excludeL2ConfigPath = "/etc/metallb/excludel2.yaml"
+)
+
 // Service offers methods to mutate a Kubernetes service object.
 type service interface {
 	UpdateStatus(svc *v1.Service) error
@@ -133,13 +140,21 @@ func main() {
 		os.Exit(1)
 	}
 
+	var interfacesToExclude *regexp.Regexp
+	interfacesToExclude, err = parseAnnouncedInterfacesToExclude()
+	if err != nil {
+		level.Error(logger).Log("op", "startup", "msg", "failed to parse announcedInterfacesToExclude from configMap", "error", err)
+		os.Exit(1)
+	}
+
 	// Setup all clients and speakers, config decides what is being done runtime.
 	ctrl, err := newController(controllerConfig{
-		MyNode:   *myNode,
-		Logger:   logger,
-		LogLevel: logging.Level(*logLevel),
-		SList:    sList,
-		bgpType:  bgpImplementation(bgpType),
+		MyNode:                 *myNode,
+		Logger:                 logger,
+		LogLevel:               logging.Level(*logLevel),
+		SList:                  sList,
+		bgpType:                bgpImplementation(bgpType),
+		InterfaceExcludeRegexp: interfacesToExclude,
 	})
 	if err != nil {
 		level.Error(logger).Log("op", "startup", "error", err, "msg", "failed to create MetalLB controller")
@@ -212,8 +227,10 @@ type controllerConfig struct {
 
 	// For testing only, and will be removed in a future release.
 	// See: https://github.com/metallb/metallb/issues/152.
-	DisableLayer2      bool
-	SupportedProtocols []config.Proto
+	DisableLayer2                bool
+	SupportedProtocols           []config.Proto
+	AnnouncedInterfacesToExclude []string `yaml:"announcedInterfacesToExclude"`
+	InterfaceExcludeRegexp       *regexp.Regexp
 }
 
 func newController(cfg controllerConfig) (*controller, error) {
@@ -229,7 +246,7 @@ func newController(cfg controllerConfig) (*controller, error) {
 	protocols := []config.Proto{config.BGP}
 
 	if !cfg.DisableLayer2 {
-		a, err := layer2.New(cfg.Logger)
+		a, err := layer2.New(cfg.Logger, cfg.InterfaceExcludeRegexp)
 		if err != nil {
 			return nil, fmt.Errorf("making layer2 announcer: %s", err)
 		}
@@ -443,6 +460,27 @@ func compareIPs(ips1, ips2 []net.IP) bool {
 	return true
 }
 
+func parseAnnouncedInterfacesToExclude() (*regexp.Regexp, error) {
+	configmapBytes, err := os.ReadFile(filepath.Clean(excludeL2ConfigPath))
+	if err != nil && !os.IsNotExist(err) {
+		return nil, err
+	}
+
+	var conf controllerConfig
+	if err = yaml.Unmarshal(configmapBytes, &conf); err != nil {
+		return nil, err
+	}
+
+	var excludeRegexp *regexp.Regexp
+	if len(conf.AnnouncedInterfacesToExclude) > 0 {
+		excludeRegexp, err = regexp.Compile("(" + strings.Join(conf.AnnouncedInterfacesToExclude, ")|(") + ")")
+		if err != nil {
+			return nil, err
+		}
+	}
+	return excludeRegexp, nil
+}
+
 func (c *controller) SetConfig(l log.Logger, cfg *config.Config) controllers.SyncState {
 
 	level.Debug(l).Log("event", "startUpdate", "msg", "start of config update")


Test patch:
diff --git a/e2etest/bgptests/node_selector.go b/e2etest/bgptests/node_selector.go
index ff6c4a4e..5d45bffe 100644
--- a/e2etest/bgptests/node_selector.go
+++ b/e2etest/bgptests/node_selector.go
@@ -315,18 +315,18 @@ var _ = ginkgo.Describe("BGP Node Selector", func() {
 
 		},
 			ginkgo.Entry("First to one, second to two", map[int][]int{
-				0: []int{0},
-				1: []int{1, 2},
+				0: {0},
+				1: {1, 2},
 			}),
 			ginkgo.Entry("First to one, second to one, third to three", map[int][]int{
-				0: []int{0},
-				1: []int{1},
-				2: []int{0, 1, 2},
+				0: {0},
+				1: {1},
+				2: {0, 1, 2},
 			}),
 			ginkgo.Entry("First to one, second to one, third to same as first", map[int][]int{
-				0: []int{0},
-				1: []int{1},
-				2: []int{0},
+				0: {0},
+				1: {1},
+				2: {0},
 			}),
 		)
 	})
diff --git a/internal/config/config_test.go b/internal/config/config_test.go
index a1a66ba5..7a2a9dc5 100644
--- a/internal/config/config_test.go
+++ b/internal/config/config_test.go
@@ -270,7 +270,7 @@ func TestParse(t *testing.T) {
 								Nodes:               map[string]bool{},
 							},
 						},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes:         map[string]bool{},
 							AllInterfaces: true,
 						}},
@@ -288,7 +288,7 @@ func TestParse(t *testing.T) {
 								Nodes:               map[string]bool{},
 							},
 						},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes:         map[string]bool{},
 							AllInterfaces: true,
 						}},
@@ -309,7 +309,7 @@ func TestParse(t *testing.T) {
 							ipnet("40.0.0.240/32"),
 							ipnet("40.0.0.250/32"),
 						},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes:         map[string]bool{},
 							AllInterfaces: true,
 						}},
@@ -318,7 +318,7 @@ func TestParse(t *testing.T) {
 					"pool4": {
 						Name: "pool4",
 						CIDR: []*net.IPNet{ipnet("2001:db8::/64")},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes:         map[string]bool{},
 							AllInterfaces: true,
 						}},
@@ -416,6 +416,70 @@ func TestParse(t *testing.T) {
 			},
 		},
 
+		{
+			desc: "ip address pool with duplicate namespace selection",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.0.0/16",
+								"10.50.0.0/24",
+							},
+							AvoidBuggyIPs: true,
+							AutoAssign:    pointer.BoolPtr(false),
+							AllocateTo: &v1beta1.ServiceAllocation{Priority: 1,
+								Namespaces: []string{"test-ns1", "test-ns1"}},
+						},
+					},
+				},
+				Namespaces: []corev1.Namespace{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "test-ns1",
+						},
+					},
+				},
+			},
+		},
+
+		{
+			desc: "ip address pool with duplicate namespace selectors",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"10.20.0.0/16",
+								"10.50.0.0/24",
+							},
+							AvoidBuggyIPs: true,
+							AutoAssign:    pointer.BoolPtr(false),
+							AllocateTo: &v1beta1.ServiceAllocation{
+								Priority: 1,
+								NamespaceSelectors: []v1.LabelSelector{{MatchLabels: map[string]string{"foo": "bar"}},
+									{MatchLabels: map[string]string{"foo": "bar"}}},
+							},
+						},
+					},
+				},
+				Namespaces: []corev1.Namespace{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name:   "test-ns1",
+							Labels: map[string]string{"foo": "bar"},
+						},
+					},
+				},
+			},
+		},
+
 		{
 			desc: "ip address pool with service selection",
 			crs: ClusterResources{
@@ -467,6 +531,32 @@ func TestParse(t *testing.T) {
 			},
 		},
 
+		{
+			desc: "ip address pool with duplicate service selection",
+			crs: ClusterResources{
+				Pools: []v1beta1.IPAddressPool{
+					{
+						ObjectMeta: v1.ObjectMeta{
+							Name: "pool1",
+						},
+						Spec: v1beta1.IPAddressPoolSpec{
+							Addresses: []string{
+								"30.0.0.0/8",
+							},
+							AllocateTo: &v1beta1.ServiceAllocation{Priority: 2,
+								ServiceSelectors: []v1.LabelSelector{{MatchExpressions: []metav1.LabelSelectorRequirement{
+									{
+										Key:      "foo",
+										Operator: metav1.LabelSelectorOpIn,
+										Values:   []string{"bar", "bar"},
+									},
+								}}}},
+						},
+					},
+				},
+			},
+		},
+
 		{
 			desc: "ip address pool with namespace and service selection",
 			crs: ClusterResources{
@@ -2731,7 +2821,7 @@ func TestParse(t *testing.T) {
 								Nodes:               map[string]bool{"second": true},
 							},
 						},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes: map[string]bool{
 								"first": true,
 							},
@@ -2982,7 +3072,7 @@ func TestParse(t *testing.T) {
 								},
 							},
 						},
-						L2Advertisements: []*L2Advertisement{&L2Advertisement{
+						L2Advertisements: []*L2Advertisement{{
 							Nodes: map[string]bool{
 								"first":  true,
 								"second": true,
@@ -3150,7 +3240,7 @@ func TestContainsAdvertisement(t *testing.T) {
 		{
 			desc: "Contain",
 			advs: []*L2Advertisement{
-				&L2Advertisement{
+				{
 					Nodes: map[string]bool{
 						"nodeA": true,
 						"nodeB": true,
@@ -3158,7 +3248,7 @@ func TestContainsAdvertisement(t *testing.T) {
 					Interfaces:    []string{"eth0", "eth1"},
 					AllInterfaces: false,
 				},
-				&L2Advertisement{
+				{
 					Nodes: map[string]bool{
 						"nodeB": true,
 					},
@@ -3179,7 +3269,7 @@ func TestContainsAdvertisement(t *testing.T) {
 		{
 			desc: "Not contain: Nodes don't equal",
 			advs: []*L2Advertisement{
-				&L2Advertisement{
+				{
 					Nodes: map[string]bool{
 						"nodeA": true,
 						"nodeB": true,
@@ -3201,7 +3291,7 @@ func TestContainsAdvertisement(t *testing.T) {
 		{
 			desc: "Not contain: Interfaces don't equal",
 			advs: []*L2Advertisement{
-				&L2Advertisement{
+				{
 					Nodes: map[string]bool{
 						"nodeA": true,
 						"nodeB": true,
@@ -3223,7 +3313,7 @@ func TestContainsAdvertisement(t *testing.T) {
 		{
 			desc: "Not contain: AllInterfaces doesn't equal",
 			advs: []*L2Advertisement{
-				&L2Advertisement{
+				{
 					Nodes: map[string]bool{
 						"nodeA": true,
 						"nodeB": true,