From 206b599e78271255a5b19a98ff43675ef5982f9e Mon Sep 17 00:00:00 2001 From: Federico Paolinelli Date: Fri, 11 Sep 2026 08:44:16 +0000 Subject: [PATCH 1/2] Bump MetalLB to latest main Regenerate helm charts, CRDs, RBAC, webhook configuration and bundle manifests from the upstream MetalLB main branch. Picks up the new speaker.gratuitousARPInterval helm value along with controller-gen version and other upstream changes. Assisted-by: claude-opus-4.7 Signed-off-by: Federico Paolinelli --- .github/workflows/publish.yaml | 2 +- Dockerfile | 2 +- api/v1beta1/zz_generated.deepcopy.go | 2 +- bin/metallb-operator.yaml | 18 ++++++------- bindata/deployment/helm/metallb/README.md | 8 +++++- .../helm/metallb/templates/controller.yaml | 1 + .../metallb/templates/servicemonitor.yaml | 16 +++++++++--- .../helm/metallb/templates/speaker.yaml | 21 +++++++-------- .../helm/metallb/values.schema.json | 13 +++++++++- bindata/deployment/helm/metallb/values.yaml | 26 +++++++++++++++++-- ...etallb-operator.clusterserviceversion.yaml | 2 +- bundle/manifests/metallb.io_bfdprofiles.yaml | 2 +- .../metallb.io_bgpadvertisements.yaml | 2 +- bundle/manifests/metallb.io_bgppeers.yaml | 2 +- bundle/manifests/metallb.io_communities.yaml | 2 +- .../metallb.io_configurationstates.yaml | 2 +- .../manifests/metallb.io_ipaddresspools.yaml | 2 +- .../metallb.io_l2advertisements.yaml | 2 +- .../metallb.io_servicebgpstatuses.yaml | 2 +- .../metallb.io_servicel2statuses.yaml | 2 +- config/crd/bases/metallb.io_bfdprofiles.yaml | 2 +- .../bases/metallb.io_bgpadvertisements.yaml | 2 +- config/crd/bases/metallb.io_bgppeers.yaml | 2 +- config/crd/bases/metallb.io_communities.yaml | 2 +- .../bases/metallb.io_configurationstates.yaml | 2 +- .../crd/bases/metallb.io_ipaddresspools.yaml | 2 +- .../bases/metallb.io_l2advertisements.yaml | 2 +- .../bases/metallb.io_servicebgpstatuses.yaml | 2 +- .../bases/metallb.io_servicel2statuses.yaml | 2 +- go.mod | 4 +-- hack/metallb_ref.txt | 2 +- .../testdata/ocp-metrics-controller.golden | 1 + pkg/helm/testdata/ocp-metrics-speaker.golden | 7 ++--- .../testdata/vanilla-metrics-speaker.golden | 7 ++--- 34 files changed, 109 insertions(+), 59 deletions(-) diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 435225206..0103c5d9a 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-24.04 strategy: matrix: - go: ["1.25"] + go: ["1.26"] name: Go ${{ matrix.go }} steps: - name: Checkout Metal LB Operator diff --git a/Dockerfile b/Dockerfile index d5dfab102..60a56ceac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.2 -FROM --platform=$BUILDPLATFORM docker.io/golang:1.25.7 AS builder +FROM --platform=$BUILDPLATFORM docker.io/golang:1.26.8 AS builder ARG GIT_COMMIT=dev ARG GIT_BRANCH=dev diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index eeb86fda7..220efb2cc 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -23,7 +23,7 @@ package v1beta1 import ( "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" + runtime "k8s.io/apimachinery/pkg/runtime" ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. diff --git a/bin/metallb-operator.yaml b/bin/metallb-operator.yaml index 1a312d8fa..241f0c69e 100644 --- a/bin/metallb-operator.yaml +++ b/bin/metallb-operator.yaml @@ -9,7 +9,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bfdprofiles.metallb.io spec: group: metallb.io @@ -130,7 +130,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bgpadvertisements.metallb.io spec: group: metallb.io @@ -411,7 +411,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bgppeers.metallb.io spec: conversion: @@ -874,7 +874,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: communities.metallb.io spec: group: metallb.io @@ -939,7 +939,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: configurationstates.metallb.io spec: group: metallb.io @@ -1703,7 +1703,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: ipaddresspools.metallb.io spec: group: metallb.io @@ -1941,7 +1941,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: l2advertisements.metallb.io spec: group: metallb.io @@ -4517,7 +4517,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: servicebgpstatuses.metallb.io spec: group: metallb.io @@ -4603,7 +4603,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: servicel2statuses.metallb.io spec: group: metallb.io diff --git a/bindata/deployment/helm/metallb/README.md b/bindata/deployment/helm/metallb/README.md index 15985b089..0aabac02d 100644 --- a/bindata/deployment/helm/metallb/README.md +++ b/bindata/deployment/helm/metallb/README.md @@ -49,6 +49,7 @@ Kubernetes: `>= 1.19.0-0` | controller.readinessProbe.successThreshold | int | `1` | | | controller.readinessProbe.timeoutSeconds | int | `1` | | | controller.resources | object | `{}` | | +| controller.revisionHistoryLimit | int | `10` | | | controller.runtimeClassName | string | `""` | | | controller.securityContext.fsGroup | int | `65534` | | | controller.securityContext.runAsNonRoot | bool | `true` | | @@ -124,13 +125,17 @@ Kubernetes: `>= 1.19.0-0` | speaker.enabled | bool | `true` | | | speaker.excludeInterfaces.enabled | bool | `true` | | | speaker.extraContainers | list | `[]` | | +| speaker.frr.dockerStartPath | string | `"/usr/lib/frr/docker-start"` | Path to the docker-start script inside the FRR container. Override this when using an FRR image (e.g. Docker Hardened Images) that places docker-start at a different location. | | speaker.frr.enabled | bool | `false` | | | speaker.frr.image.pullPolicy | string | `nil` | | | speaker.frr.image.repository | string | `"quay.io/frrouting/frr"` | | | speaker.frr.image.tag | string | `"10.5.3"` | | | speaker.frr.metricsPort | int | `9121` | | | speaker.frr.resources | object | `{}` | | +| speaker.frr.securityContext | object | `{"allowPrivilegeEscalation":false,"capabilities":{"add":["NET_ADMIN","NET_RAW","SYS_ADMIN","NET_BIND_SERVICE"]},"readOnlyRootFilesystem":true}` | Security context for the FRR container. | +| speaker.frr.tiniPath | string | `"/sbin/tini"` | Path to the tini binary inside the FRR container. Override this when using an FRR image (e.g. Docker Hardened Images) that places tini at a different location. | | speaker.frrMetrics.resources | object | `{}` | | +| speaker.gratuitousARPInterval | string | `nil` | Interval in seconds for periodic gratuitous ARP/NDP announcements. Unset or 0 (default) disables periodic announcements. | | speaker.ignoreExcludeLB | bool | `false` | | | speaker.image.pullPolicy | string | `nil` | | | speaker.image.repository | string | `"quay.io/metallb/speaker"` | | @@ -163,6 +168,7 @@ Kubernetes: `>= 1.19.0-0` | speaker.readinessProbe.timeoutSeconds | int | `1` | | | speaker.reloader.resources | object | `{}` | | | speaker.resources | object | `{}` | | +| speaker.revisionHistoryLimit | int | `10` | | | speaker.runtimeClassName | string | `""` | | | speaker.securityContext | object | `{}` | | | speaker.serviceAccount.annotations | object | `{}` | | @@ -181,4 +187,4 @@ Kubernetes: `>= 1.19.0-0` | tls.speakerMetricsTLSSecret | string | `""` | The name of the secret to be mounted in the speaker pod to provide TLS certificates for metrics endpoints. If not present, a self-signed certificate is auto-generated. | ---------------------------------------------- -Autogenerated from chart metadata using [helm-docs v1.10.0](https://github.com/norwoodj/helm-docs/releases/v1.10.0) +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/bindata/deployment/helm/metallb/templates/controller.yaml b/bindata/deployment/helm/metallb/templates/controller.yaml index 10053c777..fada472b4 100644 --- a/bindata/deployment/helm/metallb/templates/controller.yaml +++ b/bindata/deployment/helm/metallb/templates/controller.yaml @@ -14,6 +14,7 @@ spec: {{- if .Values.controller.strategy }} strategy: {{- toYaml .Values.controller.strategy | nindent 4 }} {{- end }} + revisionHistoryLimit: {{ .Values.controller.revisionHistoryLimit }} selector: matchLabels: {{- include "metallb.selectorLabels" . | nindent 6 }} diff --git a/bindata/deployment/helm/metallb/templates/servicemonitor.yaml b/bindata/deployment/helm/metallb/templates/servicemonitor.yaml index 60be10e76..9fdab0b25 100644 --- a/bindata/deployment/helm/metallb/templates/servicemonitor.yaml +++ b/bindata/deployment/helm/metallb/templates/servicemonitor.yaml @@ -64,11 +64,15 @@ spec: apiVersion: v1 kind: Service metadata: + {{- if or .Values.prometheus.scrapeAnnotations .Values.prometheus.serviceMonitor.speaker.annotations }} annotations: + {{- if .Values.prometheus.scrapeAnnotations }} prometheus.io/scrape: "true" prometheus.io/scheme: "https" - {{- if .Values.prometheus.serviceMonitor.speaker.annotations }} -{{ toYaml .Values.prometheus.serviceMonitor.speaker.annotations | indent 4 }} + {{- end }} + {{- if .Values.prometheus.serviceMonitor.speaker.annotations }} + {{- toYaml .Values.prometheus.serviceMonitor.speaker.annotations | nindent 4 }} + {{- end }} {{- end }} labels: name: speaker-monitor-service @@ -139,11 +143,15 @@ spec: apiVersion: v1 kind: Service metadata: + {{- if or .Values.prometheus.scrapeAnnotations .Values.prometheus.serviceMonitor.controller.annotations }} annotations: + {{- if .Values.prometheus.scrapeAnnotations }} prometheus.io/scrape: "true" prometheus.io/scheme: "https" - {{- if .Values.prometheus.serviceMonitor.controller.annotations }} -{{ toYaml .Values.prometheus.serviceMonitor.controller.annotations | indent 4 }} + {{- end }} + {{- if .Values.prometheus.serviceMonitor.controller.annotations }} + {{- toYaml .Values.prometheus.serviceMonitor.controller.annotations | nindent 4 }} + {{- end }} {{- end }} labels: name: controller-monitor-service diff --git a/bindata/deployment/helm/metallb/templates/speaker.yaml b/bindata/deployment/helm/metallb/templates/speaker.yaml index f0df774d4..b1b95fe50 100644 --- a/bindata/deployment/helm/metallb/templates/speaker.yaml +++ b/bindata/deployment/helm/metallb/templates/speaker.yaml @@ -138,6 +138,7 @@ spec: {{- if .Values.speaker.updateStrategy }} updateStrategy: {{- toYaml .Values.speaker.updateStrategy | nindent 4 }} {{- end }} + revisionHistoryLimit: {{ .Values.speaker.revisionHistoryLimit }} selector: matchLabels: {{- include "metallb.selectorLabels" . | nindent 6 }} @@ -287,6 +288,9 @@ spec: {{- if .Values.speaker.bgpDebounceTimeout }} - --bgp-debounce-timeout={{ .Values.speaker.bgpDebounceTimeout }} {{- end }} + {{- if .Values.speaker.gratuitousARPInterval }} + - --gratuitous-arp-interval={{ .Values.speaker.gratuitousARPInterval }} + {{- end }} {{- if .Values.frrk8s.external }} - --frrk8s-namespace={{ required "namespace is required when frrk8s is external" .Values.frrk8s.namespace }} {{- if .Values.frrk8s.secretPassthrough }} @@ -419,15 +423,10 @@ spec: {{- end }} {{- if .Values.speaker.frr.enabled }} - name: frr + {{- if .Values.speaker.frr.securityContext }} securityContext: - readOnlyRootFilesystem: true - allowPrivilegeEscalation: false - capabilities: - add: - - NET_ADMIN - - NET_RAW - - SYS_ADMIN - - NET_BIND_SERVICE + {{- toYaml .Values.speaker.frr.securityContext | nindent 10 }} + {{- end }} image: {{ .Values.speaker.frr.image.repository }}:{{ .Values.speaker.frr.image.tag | default .Chart.AppVersion }} {{- if .Values.speaker.frr.image.pullPolicy }} imagePullPolicy: {{ .Values.speaker.frr.image.pullPolicy }} @@ -445,9 +444,9 @@ spec: - name: frr-lib mountPath: /var/lib/frr command: - - /bin/sh - - -c - - /sbin/tini -- /usr/lib/frr/docker-start + - {{ .Values.speaker.frr.tiniPath | quote }} + - -- + - {{ .Values.speaker.frr.dockerStartPath | quote }} {{- with .Values.speaker.frr.resources }} resources: {{- toYaml . | nindent 12 }} diff --git a/bindata/deployment/helm/metallb/values.schema.json b/bindata/deployment/helm/metallb/values.schema.json index 617045732..743212cba 100644 --- a/bindata/deployment/helm/metallb/values.schema.json +++ b/bindata/deployment/helm/metallb/values.schema.json @@ -307,6 +307,10 @@ }, "required": [ "type" ] }, + "revisionHistoryLimit": { + "type": "integer", + "minimum": 0 + }, "command" : { "type": "string" }, @@ -375,6 +379,10 @@ }, "required": [ "type" ] }, + "revisionHistoryLimit": { + "type": "integer", + "minimum": 0 + }, "runtimeClassName": { "type": "string" }, @@ -394,7 +402,10 @@ "image": { "$ref": "#/definitions/component/properties/image" }, "metricsPort": { "type": "integer" }, "secureMetricsPort": { "type": "integer" }, - "resources:": { "type": "object" } + "resources:": { "type": "object" }, + "tiniPath": { "type": "string" }, + "dockerStartPath": { "type": "string" }, + "securityContext": { "type": "object" } }, "required": [ "enabled" ] }, diff --git a/bindata/deployment/helm/metallb/values.yaml b/bindata/deployment/helm/metallb/values.yaml index 00d8c7002..d62c3d66a 100644 --- a/bindata/deployment/helm/metallb/values.yaml +++ b/bindata/deployment/helm/metallb/values.yaml @@ -80,7 +80,7 @@ prometheus: # - sourceLabels: [__meta_kubernetes_pod_node_name] # separator: ; # regex: ^(.*)$ - # target_label: nodename + # targetLabel: nodename # replacement: $1 # action: replace @@ -131,7 +131,7 @@ prometheus: # - sourceLabels: [__meta_kubernetes_pod_node_name] # separator: ; # regex: ^(.*)$ - # target_label: nodename + # targetLabel: nodename # replacement: $1 # action: replace @@ -210,6 +210,7 @@ controller: ## maxSurge: 25% ## maxUnavailable: 25% ## + revisionHistoryLimit: 10 strategy: type: RollingUpdate serviceAccount: @@ -274,6 +275,8 @@ speaker: ignoreExcludeLB: false # -- BGP debounce timeout for FRR configuration reloads, in milliseconds. Only applies when BGP type is frr. Default (when unset) is 3000 ms. This feature is experimental bgpDebounceTimeout: null + # -- Interval in seconds for periodic gratuitous ARP/NDP announcements. Unset or 0 (default) disables periodic announcements. + gratuitousARPInterval: null image: repository: quay.io/metallb/speaker @@ -282,6 +285,7 @@ speaker: ## @param speaker.updateStrategy.type Speaker daemonset strategy type ## ref: https://kubernetes.io/docs/tasks/manage-daemon/update-daemon-set/ ## + revisionHistoryLimit: 10 updateStrategy: ## StrategyType ## Can be set to RollingUpdate or OnDelete @@ -344,6 +348,24 @@ speaker: pullPolicy: metricsPort: 9121 resources: {} + # -- Path to the tini binary inside the FRR container. Override this + # when using an FRR image (e.g. Docker Hardened Images) that places + # tini at a different location. + tiniPath: /sbin/tini + # -- Path to the docker-start script inside the FRR container. Override + # this when using an FRR image (e.g. Docker Hardened Images) that places + # docker-start at a different location. + dockerStartPath: /usr/lib/frr/docker-start + # -- Security context for the FRR container. + securityContext: + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + capabilities: + add: + - NET_ADMIN + - NET_RAW + - SYS_ADMIN + - NET_BIND_SERVICE reloader: resources: {} diff --git a/bundle/manifests/metallb-operator.clusterserviceversion.yaml b/bundle/manifests/metallb-operator.clusterserviceversion.yaml index 28e6f41ce..8c7e560dc 100644 --- a/bundle/manifests/metallb-operator.clusterserviceversion.yaml +++ b/bundle/manifests/metallb-operator.clusterserviceversion.yaml @@ -432,7 +432,7 @@ metadata: categories: Networking certified: "false" containerImage: quay.io/metallb/metallb-operator - createdAt: "2026-07-28T06:35:36Z" + createdAt: "2026-09-11T08:44:08Z" description: An operator for deploying MetalLB on a kubernetes cluster. operators.operatorframework.io/builder: operator-sdk-v1.40.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/manifests/metallb.io_bfdprofiles.yaml b/bundle/manifests/metallb.io_bfdprofiles.yaml index 57d20e62c..88d96be40 100644 --- a/bundle/manifests/metallb.io_bfdprofiles.yaml +++ b/bundle/manifests/metallb.io_bfdprofiles.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: bfdprofiles.metallb.io spec: diff --git a/bundle/manifests/metallb.io_bgpadvertisements.yaml b/bundle/manifests/metallb.io_bgpadvertisements.yaml index 4ca4036e8..0b242a8eb 100644 --- a/bundle/manifests/metallb.io_bgpadvertisements.yaml +++ b/bundle/manifests/metallb.io_bgpadvertisements.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: bgpadvertisements.metallb.io spec: diff --git a/bundle/manifests/metallb.io_bgppeers.yaml b/bundle/manifests/metallb.io_bgppeers.yaml index 7d02a240d..d9d6acf6f 100644 --- a/bundle/manifests/metallb.io_bgppeers.yaml +++ b/bundle/manifests/metallb.io_bgppeers.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: bgppeers.metallb.io spec: diff --git a/bundle/manifests/metallb.io_communities.yaml b/bundle/manifests/metallb.io_communities.yaml index a797d80af..b8821fe5a 100644 --- a/bundle/manifests/metallb.io_communities.yaml +++ b/bundle/manifests/metallb.io_communities.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: communities.metallb.io spec: diff --git a/bundle/manifests/metallb.io_configurationstates.yaml b/bundle/manifests/metallb.io_configurationstates.yaml index 3d2edbe9e..97665bf17 100644 --- a/bundle/manifests/metallb.io_configurationstates.yaml +++ b/bundle/manifests/metallb.io_configurationstates.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: configurationstates.metallb.io spec: diff --git a/bundle/manifests/metallb.io_ipaddresspools.yaml b/bundle/manifests/metallb.io_ipaddresspools.yaml index 286673a31..8b0ddfdc1 100644 --- a/bundle/manifests/metallb.io_ipaddresspools.yaml +++ b/bundle/manifests/metallb.io_ipaddresspools.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: ipaddresspools.metallb.io spec: diff --git a/bundle/manifests/metallb.io_l2advertisements.yaml b/bundle/manifests/metallb.io_l2advertisements.yaml index 2fb9181b8..6c3765bb3 100644 --- a/bundle/manifests/metallb.io_l2advertisements.yaml +++ b/bundle/manifests/metallb.io_l2advertisements.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: l2advertisements.metallb.io spec: diff --git a/bundle/manifests/metallb.io_servicebgpstatuses.yaml b/bundle/manifests/metallb.io_servicebgpstatuses.yaml index af1618763..b200cdf0c 100644 --- a/bundle/manifests/metallb.io_servicebgpstatuses.yaml +++ b/bundle/manifests/metallb.io_servicebgpstatuses.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: servicebgpstatuses.metallb.io spec: diff --git a/bundle/manifests/metallb.io_servicel2statuses.yaml b/bundle/manifests/metallb.io_servicel2statuses.yaml index bebacbcea..f64e6e10b 100644 --- a/bundle/manifests/metallb.io_servicel2statuses.yaml +++ b/bundle/manifests/metallb.io_servicel2statuses.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 creationTimestamp: null name: servicel2statuses.metallb.io spec: diff --git a/config/crd/bases/metallb.io_bfdprofiles.yaml b/config/crd/bases/metallb.io_bfdprofiles.yaml index 4e5945125..7565018ba 100644 --- a/config/crd/bases/metallb.io_bfdprofiles.yaml +++ b/config/crd/bases/metallb.io_bfdprofiles.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bfdprofiles.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_bgpadvertisements.yaml b/config/crd/bases/metallb.io_bgpadvertisements.yaml index 62cb8595a..5286bce70 100644 --- a/config/crd/bases/metallb.io_bgpadvertisements.yaml +++ b/config/crd/bases/metallb.io_bgpadvertisements.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bgpadvertisements.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_bgppeers.yaml b/config/crd/bases/metallb.io_bgppeers.yaml index 9fb7ed353..e2553d493 100644 --- a/config/crd/bases/metallb.io_bgppeers.yaml +++ b/config/crd/bases/metallb.io_bgppeers.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: bgppeers.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_communities.yaml b/config/crd/bases/metallb.io_communities.yaml index 00d7df11e..af1509e55 100644 --- a/config/crd/bases/metallb.io_communities.yaml +++ b/config/crd/bases/metallb.io_communities.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: communities.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_configurationstates.yaml b/config/crd/bases/metallb.io_configurationstates.yaml index d60e86dc1..62f06b6d8 100644 --- a/config/crd/bases/metallb.io_configurationstates.yaml +++ b/config/crd/bases/metallb.io_configurationstates.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: configurationstates.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_ipaddresspools.yaml b/config/crd/bases/metallb.io_ipaddresspools.yaml index 5182f8ccd..7cf9be19b 100644 --- a/config/crd/bases/metallb.io_ipaddresspools.yaml +++ b/config/crd/bases/metallb.io_ipaddresspools.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: ipaddresspools.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_l2advertisements.yaml b/config/crd/bases/metallb.io_l2advertisements.yaml index e8ba5b12c..8f66fa406 100644 --- a/config/crd/bases/metallb.io_l2advertisements.yaml +++ b/config/crd/bases/metallb.io_l2advertisements.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: l2advertisements.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_servicebgpstatuses.yaml b/config/crd/bases/metallb.io_servicebgpstatuses.yaml index 8f20dcbb1..6b2b4d870 100644 --- a/config/crd/bases/metallb.io_servicebgpstatuses.yaml +++ b/config/crd/bases/metallb.io_servicebgpstatuses.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: servicebgpstatuses.metallb.io spec: group: metallb.io diff --git a/config/crd/bases/metallb.io_servicel2statuses.yaml b/config/crd/bases/metallb.io_servicel2statuses.yaml index 4def283e0..2ba2c1d89 100644 --- a/config/crd/bases/metallb.io_servicel2statuses.yaml +++ b/config/crd/bases/metallb.io_servicel2statuses.yaml @@ -2,7 +2,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.19.0 + controller-gen.kubebuilder.io/version: v0.22.0 name: servicel2statuses.metallb.io spec: group: metallb.io diff --git a/go.mod b/go.mod index 5ca850860..b01c98661 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,8 @@ module github.com/metallb/metallb-operator -go 1.25.0 +go 1.26.0 -toolchain go1.25.7 +toolchain go1.26.8 require ( github.com/Masterminds/semver v1.5.0 diff --git a/hack/metallb_ref.txt b/hack/metallb_ref.txt index 7a0b099a1..0aade7ec9 100644 --- a/hack/metallb_ref.txt +++ b/hack/metallb_ref.txt @@ -1 +1 @@ -44ee0904c4ed2ea6a7637efdafec598797e2ae78 +7dff5b4c1094fbe3802a964256bf23df6d102d96 diff --git a/pkg/helm/testdata/ocp-metrics-controller.golden b/pkg/helm/testdata/ocp-metrics-controller.golden index 7bb721b37..878b4cd49 100644 --- a/pkg/helm/testdata/ocp-metrics-controller.golden +++ b/pkg/helm/testdata/ocp-metrics-controller.golden @@ -13,6 +13,7 @@ "namespace": "metallb-test-namespace" }, "spec": { + "revisionHistoryLimit": 10, "selector": { "matchLabels": { "app": "metallb", diff --git a/pkg/helm/testdata/ocp-metrics-speaker.golden b/pkg/helm/testdata/ocp-metrics-speaker.golden index 66e25a7eb..afcd84caa 100644 --- a/pkg/helm/testdata/ocp-metrics-speaker.golden +++ b/pkg/helm/testdata/ocp-metrics-speaker.golden @@ -13,6 +13,7 @@ "namespace": "metallb-test-namespace" }, "spec": { + "revisionHistoryLimit": 10, "selector": { "matchLabels": { "app": "metallb", @@ -180,9 +181,9 @@ }, { "command": [ - "/bin/sh", - "-c", - "/sbin/tini -- /usr/lib/frr/docker-start" + "/sbin/tini", + "--", + "/usr/lib/frr/docker-start" ], "env": [ { diff --git a/pkg/helm/testdata/vanilla-metrics-speaker.golden b/pkg/helm/testdata/vanilla-metrics-speaker.golden index bf1d846b6..073fce017 100644 --- a/pkg/helm/testdata/vanilla-metrics-speaker.golden +++ b/pkg/helm/testdata/vanilla-metrics-speaker.golden @@ -13,6 +13,7 @@ "namespace": "metallb-test-namespace" }, "spec": { + "revisionHistoryLimit": 10, "selector": { "matchLabels": { "app": "metallb", @@ -171,9 +172,9 @@ }, { "command": [ - "/bin/sh", - "-c", - "/sbin/tini -- /usr/lib/frr/docker-start" + "/sbin/tini", + "--", + "/usr/lib/frr/docker-start" ], "env": [ { From 24afdd5162179cc719409619524ae6ada5bfcdd9 Mon Sep 17 00:00:00 2001 From: Federico Paolinelli Date: Fri, 11 Sep 2026 08:44:36 +0000 Subject: [PATCH 2/2] Expose speaker gratuitousARPInterval in MetalLB CR Add a new optional SpeakerGratuitousARPInterval field of type *metav1.Duration on the MetalLB spec. When set to a non-zero value it is passed to the speaker as the gratuitousARPInterval helm chart value, enabling periodic gratuitous ARP / unsolicited NDP announcements for L2-advertised services. When unset or zero the chart default (disabled) is preserved. Assisted-by: claude-opus-4.7 Signed-off-by: Federico Paolinelli --- api/v1beta1/metallb_types.go | 11 ++++ api/v1beta1/zz_generated.deepcopy.go | 5 ++ bin/metallb-operator.yaml | 13 +++++ ...etallb-operator.clusterserviceversion.yaml | 2 +- bundle/manifests/metallb.io_metallbs.yaml | 13 +++++ config/crd/bases/metallb.io_metallbs.yaml | 13 +++++ pkg/helm/metallb.go | 5 ++ pkg/helm/metallb_test.go | 54 +++++++++++++++++++ 8 files changed, 115 insertions(+), 1 deletion(-) diff --git a/api/v1beta1/metallb_types.go b/api/v1beta1/metallb_types.go index a67867f61..0de8466aa 100644 --- a/api/v1beta1/metallb_types.go +++ b/api/v1beta1/metallb_types.go @@ -100,6 +100,17 @@ type MetalLBSpec struct { // The specific frr-k8s configuration FRRK8SConfig *FRRK8SConfig `json:"frrk8sConfig,omitempty"` + + // SpeakerGratuitousARPInterval is the interval at which the MetalLB speaker + // sends periodic gratuitous ARP / unsolicited NDP announcements for + // L2-advertised services. When unset or set to 0, periodic announcements + // are disabled and only the announcements triggered by service events are + // sent. The value must be a valid Go duration string (e.g. "30s", "1m") + // and, when non-zero, must be at least 1s to avoid flooding the network + // with announcements. + // +optional + // +kubebuilder:validation:XValidation:rule="duration(self) == duration('0s') || duration(self) >= duration('1s')",message="speakerGratuitousARPInterval must be 0 or at least 1s" + SpeakerGratuitousARPInterval *metav1.Duration `json:"speakerGratuitousARPInterval,omitempty"` } type FRRK8SConfig struct { diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index 220efb2cc..b104072b9 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -183,6 +183,11 @@ func (in *MetalLBSpec) DeepCopyInto(out *MetalLBSpec) { *out = new(FRRK8SConfig) (*in).DeepCopyInto(*out) } + if in.SpeakerGratuitousARPInterval != nil { + in, out := &in.SpeakerGratuitousARPInterval, &out.SpeakerGratuitousARPInterval + *out = new(metav1.Duration) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MetalLBSpec. diff --git a/bin/metallb-operator.yaml b/bin/metallb-operator.yaml index 241f0c69e..a11883644 100644 --- a/bin/metallb-operator.yaml +++ b/bin/metallb-operator.yaml @@ -4403,6 +4403,19 @@ spec: description: Define container runtime configuration class type: string type: object + speakerGratuitousARPInterval: + description: |- + SpeakerGratuitousARPInterval is the interval at which the MetalLB speaker + sends periodic gratuitous ARP / unsolicited NDP announcements for + L2-advertised services. When unset or set to 0, periodic announcements + are disabled and only the announcements triggered by service events are + sent. The value must be a valid Go duration string (e.g. "30s", "1m") + and, when non-zero, must be at least 1s to avoid flooding the network + with announcements. + type: string + x-kubernetes-validations: + - message: speakerGratuitousARPInterval must be 0 or at least 1s + rule: duration(self) == duration('0s') || duration(self) >= duration('1s') speakerTolerations: description: |- tolerations is a list of tolerations applied to MetalLB speaker diff --git a/bundle/manifests/metallb-operator.clusterserviceversion.yaml b/bundle/manifests/metallb-operator.clusterserviceversion.yaml index 8c7e560dc..aad1189b1 100644 --- a/bundle/manifests/metallb-operator.clusterserviceversion.yaml +++ b/bundle/manifests/metallb-operator.clusterserviceversion.yaml @@ -432,7 +432,7 @@ metadata: categories: Networking certified: "false" containerImage: quay.io/metallb/metallb-operator - createdAt: "2026-09-11T08:44:08Z" + createdAt: "2026-09-11T08:44:27Z" description: An operator for deploying MetalLB on a kubernetes cluster. operators.operatorframework.io/builder: operator-sdk-v1.40.0 operators.operatorframework.io/project_layout: go.kubebuilder.io/v4 diff --git a/bundle/manifests/metallb.io_metallbs.yaml b/bundle/manifests/metallb.io_metallbs.yaml index 2d0183542..b58d02d5c 100644 --- a/bundle/manifests/metallb.io_metallbs.yaml +++ b/bundle/manifests/metallb.io_metallbs.yaml @@ -2146,6 +2146,19 @@ spec: description: Define container runtime configuration class type: string type: object + speakerGratuitousARPInterval: + description: |- + SpeakerGratuitousARPInterval is the interval at which the MetalLB speaker + sends periodic gratuitous ARP / unsolicited NDP announcements for + L2-advertised services. When unset or set to 0, periodic announcements + are disabled and only the announcements triggered by service events are + sent. The value must be a valid Go duration string (e.g. "30s", "1m") + and, when non-zero, must be at least 1s to avoid flooding the network + with announcements. + type: string + x-kubernetes-validations: + - message: speakerGratuitousARPInterval must be 0 or at least 1s + rule: duration(self) == duration('0s') || duration(self) >= duration('1s') speakerTolerations: description: |- tolerations is a list of tolerations applied to MetalLB speaker diff --git a/config/crd/bases/metallb.io_metallbs.yaml b/config/crd/bases/metallb.io_metallbs.yaml index b6c7146c7..5d9be3871 100644 --- a/config/crd/bases/metallb.io_metallbs.yaml +++ b/config/crd/bases/metallb.io_metallbs.yaml @@ -2146,6 +2146,19 @@ spec: description: Define container runtime configuration class type: string type: object + speakerGratuitousARPInterval: + description: |- + SpeakerGratuitousARPInterval is the interval at which the MetalLB speaker + sends periodic gratuitous ARP / unsolicited NDP announcements for + L2-advertised services. When unset or set to 0, periodic announcements + are disabled and only the announcements triggered by service events are + sent. The value must be a valid Go duration string (e.g. "30s", "1m") + and, when non-zero, must be at least 1s to avoid flooding the network + with announcements. + type: string + x-kubernetes-validations: + - message: speakerGratuitousARPInterval must be 0 or at least 1s + rule: duration(self) == duration('0s') || duration(self) >= duration('1s') speakerTolerations: description: |- tolerations is a list of tolerations applied to MetalLB speaker diff --git a/pkg/helm/metallb.go b/pkg/helm/metallb.go index 2435eee9e..eb0de7ecc 100644 --- a/pkg/helm/metallb.go +++ b/pkg/helm/metallb.go @@ -17,6 +17,8 @@ limitations under the License. package helm import ( + "time" + metallbv1beta1 "github.com/metallb/metallb-operator/api/v1beta1" "github.com/metallb/metallb-operator/pkg/openshift" "github.com/metallb/metallb-operator/pkg/params" @@ -362,6 +364,9 @@ func speakerValues(envConfig params.EnvConfig, crdConfig *metallbv1beta1.MetalLB speakerValueMap["podAnnotations"] = toInterfaceMap(otherConfigs.Annotations) } } + if crdConfig.Spec.SpeakerGratuitousARPInterval != nil && crdConfig.Spec.SpeakerGratuitousARPInterval.Duration >= time.Second { + speakerValueMap["gratuitousARPInterval"] = crdConfig.Spec.SpeakerGratuitousARPInterval.Duration.String() + } return speakerValueMap } diff --git a/pkg/helm/metallb_test.go b/pkg/helm/metallb_test.go index 1a8a51752..b08c96dd4 100644 --- a/pkg/helm/metallb_test.go +++ b/pkg/helm/metallb_test.go @@ -23,6 +23,7 @@ import ( "os" "path/filepath" "testing" + "time" "github.com/google/go-cmp/cmp" metallbv1beta1 "github.com/metallb/metallb-operator/api/v1beta1" @@ -280,6 +281,59 @@ func TestSecretPassthrough(t *testing.T) { } } +func TestSpeakerGratuitousARPInterval(t *testing.T) { + tests := []struct { + name string + interval *metav1.Duration + expectArg string + expectSet bool + }{ + {"unset", nil, "", false}, + {"set", &metav1.Duration{Duration: 5 * time.Second}, "--gratuitous-arp-interval=5s", true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + g := NewGomegaWithT(t) + chart, err := NewMetalLBChart(metalLBChartPath, metalLBChartName, MetalLBTestNameSpace, nil) + g.Expect(err).To(BeNil()) + + metallb := &metallbv1beta1.MetalLB{ + ObjectMeta: metav1.ObjectMeta{ + Name: "metallb", + Namespace: MetalLBTestNameSpace, + }, + Spec: metallbv1beta1.MetalLBSpec{ + SpeakerGratuitousARPInterval: tt.interval, + }, + } + + objs, err := chart.Objects(defaultEnvConfig, metallb) + g.Expect(err).To(BeNil()) + var speakerFound bool + for _, obj := range objs { + if obj.GetKind() == "DaemonSet" && obj.GetName() == speakerDaemonSet { + speaker := appsv1.DaemonSet{} + err = runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &speaker) + g.Expect(err).To(BeNil()) + for _, container := range speaker.Spec.Template.Spec.Containers { + if container.Name == "speaker" { + if tt.expectSet { + g.Expect(container.Args).To(ContainElement(tt.expectArg)) + } else { + for _, a := range container.Args { + g.Expect(a).NotTo(HavePrefix("--gratuitous-arp-interval")) + } + } + speakerFound = true + } + } + } + } + g.Expect(speakerFound).To(BeTrue()) + }) + } +} + func TestParseOCPSecureMetrics(t *testing.T) { g := NewGomegaWithT(t)