Skip to content

OPNET-783: BGP-based VIP management: FRRConfiguration rendering, DaemonSet placement and static pod RBAC#3047

Draft
mkowalski wants to merge 3 commits into
openshift:masterfrom
mkowalski:bgp-vip-management
Draft

OPNET-783: BGP-based VIP management: FRRConfiguration rendering, DaemonSet placement and static pod RBAC#3047
mkowalski wants to merge 3 commits into
openshift:masterfrom
mkowalski:bgp-vip-management

Conversation

@mkowalski

@mkowalski mkowalski commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

What

CNO side of BGP-based VIP management (enhancement openshift/enhancements#1982, epic OPNET-595). Three commits:

  1. FRRConfiguration rendering: when BGP VIP management is active, CNO reads the installer-generated bgp-vip-config ConfigMap and renders a bgp-vip-master FRRConfiguration carrying the BGP sessions (neighbors, passwords, BFD). Advertisement deliberately stays out of the CRD surface (CRD prefixes render as unconditional network statements, defeating kube-vip's health gating — validated failure mode) and is carried in rawConfig: ip import-table 198, per-family health-gated redistribute table-direct 198 behind route-map/prefix-list filters, plus high-sequence permits appended to frr-k8s's generated per-neighbor -out route-maps (frr-k8s renders deny-any egress without toAdvertise; prefix-list deny = route-map no-match = fall-through, so egress opens exactly for the VIP prefixes).
  2. DaemonSet placement: under BGP VIP management the frr-k8s DaemonSet gets required node-affinity excluding control plane nodes by role — masters run the MCO-deployed frr-k8s static pod, workers the DaemonSet. Label-based approaches were validated broken (NodeRestriction denies node-credential label writes; DaemonSet scheduling races labeling).
  3. Static pod RBAC: the static pod authenticates with the node kubeconfig, whose identity is the openshift-machine-config-operator/node-bootstrapper ServiceAccount (verified live) — grants for FRRConfiguration/FRRK8sConfiguration reads, node state CR writes, and namespace-scoped secrets/pods reads. Per-node write scoping is a documented pre-GA follow-up (needs admission-level enforcement).

Merge safety — no openshift/api dependency

openshift/api#2923 (OPNET-780) is not merged yet, so this PR deliberately avoids the new vendored types:

  • The BGPBasedVIPManagement gate name is a local configv1.FeatureGateName constant; a KnownFeatures guard makes every new path a no-op on any cluster whose FeatureGate status does not know the gate — i.e. this code is inert everywhere until OPNET-780: Add BGPBasedVIPManagement feature gate and BGP VIP management fields api#2923 and its cluster-config plumbing ship.
  • Infrastructure.status.platformStatus.baremetal.vipManagement is read unstructured (the CRD field is equally gated api-side).

A small follow-up will switch both to typed access once the api merges and re-vendors. No vendor/, go.mod or default-behavior changes; featureSet: Default/TechPreview clusters are untouched.

Testing

Unit tests per commit (schema round-trip incl. full installer-shaped payloads and dual-stack, rawConfig content, render-var/affinity branches, bindata object counts); go test ./pkg/... fully green. Validated end to end in the enhancement reference implementation across 14 dev-scripts baremetal installs — install over a BGP-advertised API VIP, health-gated ECMP for API+Ingress VIPs, CRD handover: https://github.com/mkowalski/bgp-vip-demo.

Related: #3046 (statusmanager fix this feature exposed), openshift/api#2923, openshift/baremetal-runtimecfg#395, openshift-metal3/dev-scripts#1929, kube-vip/kube-vip#1627.

CNO subtask: OPNET-783.

@openshift-ci-robot

openshift-ci-robot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@mkowalski: This pull request references OPNET-783 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

What

CNO side of BGP-based VIP management (enhancement openshift/enhancements#1982, epic OPNET-595). Three commits:

  1. FRRConfiguration rendering: when BGP VIP management is active, CNO reads the installer-generated bgp-vip-config ConfigMap and renders a bgp-vip-master FRRConfiguration carrying the BGP sessions (neighbors, passwords, BFD). Advertisement deliberately stays out of the CRD surface (CRD prefixes render as unconditional network statements, defeating kube-vip's health gating — validated failure mode) and is carried in rawConfig: ip import-table 198, per-family health-gated redistribute table-direct 198 behind route-map/prefix-list filters, plus high-sequence permits appended to frr-k8s's generated per-neighbor -out route-maps (frr-k8s renders deny-any egress without toAdvertise; prefix-list deny = route-map no-match = fall-through, so egress opens exactly for the VIP prefixes).
  2. DaemonSet placement: under BGP VIP management the frr-k8s DaemonSet gets required node-affinity excluding control plane nodes by role — masters run the MCO-deployed frr-k8s static pod, workers the DaemonSet. Label-based approaches were validated broken (NodeRestriction denies node-credential label writes; DaemonSet scheduling races labeling).
  3. Static pod RBAC: the static pod authenticates with the node kubeconfig, whose identity is the openshift-machine-config-operator/node-bootstrapper ServiceAccount (verified live) — grants for FRRConfiguration/FRRK8sConfiguration reads, node state CR writes, and namespace-scoped secrets/pods reads. Per-node write scoping is a documented pre-GA follow-up (needs admission-level enforcement).

Merge safety — no openshift/api dependency

openshift/api#2923 (OPNET-780) is not merged yet, so this PR deliberately avoids the new vendored types:

  • The BGPBasedVIPManagement gate name is a local configv1.FeatureGateName constant; a KnownFeatures guard makes every new path a no-op on any cluster whose FeatureGate status does not know the gate — i.e. this code is inert everywhere until OPNET-780: Add BGPBasedVIPManagement feature gate and BGP VIP management fields api#2923 and its cluster-config plumbing ship.
  • Infrastructure.status.platformStatus.baremetal.vipManagement is read unstructured (the CRD field is equally gated api-side).

A small follow-up will switch both to typed access once the api merges and re-vendors. No vendor/, go.mod or default-behavior changes; featureSet: Default/TechPreview clusters are untouched.

Testing

Unit tests per commit (schema round-trip incl. full installer-shaped payloads and dual-stack, rawConfig content, render-var/affinity branches, bindata object counts); go test ./pkg/... fully green. Validated end to end in the enhancement reference implementation across 14 dev-scripts baremetal installs — install over a BGP-advertised API VIP, health-gated ECMP for API+Ingress VIPs, CRD handover: https://github.com/mkowalski/bgp-vip-demo.

Related: #3046 (statusmanager fix this feature exposed), openshift/api#2923, openshift/baremetal-runtimecfg#395, openshift-metal3/dev-scripts#1929, kube-vip/kube-vip#1627.

CNO subtask: OPNET-783.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci-robot openshift-ci-robot added the jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. label Jul 10, 2026
@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: 15f17288-2ce4-4c19-b7b1-ea198b88b000

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Adds BGP-based VIP management for BareMetal clusters, renders FRR configuration from a ConfigMap, updates FRR-K8s scheduling and rendering, adds static pod RBAC resources, and introduces coverage for single-stack, dual-stack, optional-field, and scheduling behavior.

Changes

BGP VIP FRR integration

Layer / File(s) Summary
BGP VIP configuration generation
pkg/network/bgp_vip.go, pkg/network/bgp_vip_test.go
Adds feature and platform checks, parses bgp-vip-config, builds FRRConfiguration objects and BFD profiles, generates filtered IPv4/IPv6 VIP advertisements, checks BGP session status, and tests optional and dual-stack fields.
Rendering and scheduling integration
pkg/network/render.go, pkg/network/render_test.go, bindata/network/frr-k8s/frr-k8s.yaml
Passes BGP VIP state through rendering, emits BGP VIP FRRConfiguration resources, excludes master nodes from DaemonSet placement when active, and tests active and inactive paths.
Static pod access and placement
bindata/network/frr-k8s/003-static-pod-rbac.yaml
Adds cluster- and namespace-scoped roles and bindings for the node-bootstrapper ServiceAccount, covering FRR resources, session state, nodes, secrets, and pods.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Render
  participant Infrastructure
  participant ConfigMap
  participant FRRBuilder
  participant FRRConfiguration
  Render->>Infrastructure: evaluate BGP VIP management
  Render->>ConfigMap: read bgp-vip-config
  ConfigMap-->>Render: return config.json
  Render->>FRRBuilder: build FRR configuration objects
  FRRBuilder-->>FRRConfiguration: return generated resources
  Render-->>Render: apply BGP VIP scheduling state
Loading
🚥 Pre-merge checks | ✅ 13 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Topology-Aware Scheduling Compatibility ⚠️ Warning The new frr-k8s DaemonSet adds required nodeAffinity excluding master nodes, and it’s only gated on BGP VIP status—not ControlPlaneTopology—so it can stay Pending on SNO/TNF. Gate the affinity on topology or use topology-aware scheduling: avoid excluding masters when no workers exist, or check ControlPlaneTopology before applying the constraint.
✅ Passed checks (13 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed The added/updated tests use static literal names only; no Ginkgo titles or dynamic identifiers were introduced.
Test Structure And Quality ✅ Passed The new tests are small unit tests, not Ginkgo/cluster tests; they use subtests/fake clients, no indefinite waits, and match existing repo test patterns.
Microshift Test Compatibility ✅ Passed The added tests are standard Go unit tests (Test...), not Ginkgo e2e tests, and none include MicroShift-specific tags or guards.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests were added; the new tests are plain Go unit tests and don’t assume multi-node topology.
Ote Binary Stdout Contract ✅ Passed No process-level code was changed; the PR only updates a test file and YAML, with no main/TestMain/BeforeSuite stdout writes.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed Only standard Go unit tests were added (Test* with Gomega), not Ginkgo e2e tests; no external connectivity is required, and IPv4 literals are just test fixtures.
No-Weak-Crypto ✅ Passed Changed files contain no weak ciphers, custom crypto, or secret/token comparisons; BGP peer passwords are only passed through as config data.
Container-Privileges ✅ Passed The actual PR diff only adds RBAC and a test; it does not introduce privileged:true, hostPID/IPC, allowPrivilegeEscalation:true, or SYS_ADMIN changes.
No-Sensitive-Data-In-Logs ✅ Passed No added logs print passwords, tokens, or PII; the new log messages are generic status lines, and sensitive BGP peer fields are only stored in config/CRs, not logged.
Title check ✅ Passed The title clearly summarizes the main change: BGP-based VIP management across FRR rendering, DaemonSet placement, and static pod RBAC.
Description check ✅ Passed The description is directly related to the changeset and explains the FRR rendering, DaemonSet placement, and RBAC updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@openshift-ci openshift-ci Bot requested review from arkadeepsen and bpickard22 July 10, 2026 09:27
@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: mkowalski
Once this PR has been reviewed and has the lgtm label, please assign abhat for approval. For more information see the Code Review Process.

The full list of commands accepted by this bot can be found here.

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/network/render.go (1)

132-143: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Gate BGP VIP FRRConfiguration rendering in pkg/network/render.go:138-143.
renderBGPVIPFRRConfiguration still runs whenever BGP VIP management is active, even if RoutingCapabilitiesProviderFRR isn't enabled. That can emit FRRConfiguration objects before the frr-k8s CRD/manifests exist, so this should be gated on the FRR provider too.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/network/render.go` around lines 132 - 143, Gate the
renderBGPVIPFRRConfiguration call in the rendering flow so it executes only when
BGP VIP management is active and RoutingCapabilitiesProviderFRR is enabled.
Preserve the existing error handling and object appending within that
conditional block.
🧹 Nitpick comments (1)
pkg/network/render.go (1)

859-868: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Duplicate isBGPVIPManagement check causes a redundant Infrastructure CR fetch per render.

isBGPVIPManagement is called here and again inside renderBGPVIPFRRConfiguration (bgp_vip.go), each independently issuing a dynamic-client Get on the Infrastructure CR every Render() invocation. Compute it once in Render() and pass the boolean into both functions.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/network/render.go` around lines 859 - 868, Compute the BGP VIP management
state once in Render() and pass the resulting boolean through the rendering call
chain, including renderAdditionalRoutingCapabilities and
renderBGPVIPFRRConfiguration. Remove the repeated isBGPVIPManagement call from
renderAdditionalRoutingCapabilities and renderBGPVIPFRRConfiguration, updating
their signatures and callers to use the shared value.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@bindata/network/frr-k8s/003-static-pod-rbac.yaml`:
- Around line 10-53: Restrict the FRR static-pod RBAC in the ClusterRole named
frr-k8s-static-pod: render this ClusterRole and its associated
ClusterRoleBinding only when BGP VIP management/bootstrap is active, not merely
when AdditionalRoutingCapabilities is enabled, and reduce node-state permissions
to the minimum required by the static pod, removing unnecessary destructive
verbs such as delete. Update render tests, including the bootstrapResult == nil
case, to verify the resources are omitted when BGP VIP management is inactive.

In `@pkg/network/bgp_vip.go`:
- Line 77: Replace context.TODO() in Render(), renderBGPVIPFRRConfiguration, and
the other affected client calls with a real bounded context.Context propagated
through the call chain; derive it from the reconcile/request context and apply
an appropriate timeout or cancellation before each Kubernetes API call.
- Around line 307-311: Handle the error returned by uns.NestedString in the
session status check within the BGP status function, rather than discarding it.
If extracting status or encountering a type mismatch fails, return the error to
the caller; otherwise continue checking whether status equals "Established".
- Around line 44-53: Update buildFRRConfigurationObjects and
buildBGPVIPRawConfig to propagate bgpVIPConfigData.Communities and HostOverrides
into the rendered FRRConfiguration, applying host-specific peers from
HostOverrides instead of only DefaultPeers and preserving configured route
communities. Ensure buildBGPVIPRawConfig includes both fields in its generated
raw configuration so installer and administrator settings are not discarded.

---

Outside diff comments:
In `@pkg/network/render.go`:
- Around line 132-143: Gate the renderBGPVIPFRRConfiguration call in the
rendering flow so it executes only when BGP VIP management is active and
RoutingCapabilitiesProviderFRR is enabled. Preserve the existing error handling
and object appending within that conditional block.

---

Nitpick comments:
In `@pkg/network/render.go`:
- Around line 859-868: Compute the BGP VIP management state once in Render() and
pass the resulting boolean through the rendering call chain, including
renderAdditionalRoutingCapabilities and renderBGPVIPFRRConfiguration. Remove the
repeated isBGPVIPManagement call from renderAdditionalRoutingCapabilities and
renderBGPVIPFRRConfiguration, updating their signatures and callers to use the
shared value.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Central YAML (inherited)

Review profile: CHILL

Plan: Enterprise

Run ID: af2a1a47-ab87-42a3-9874-a6d81393a5e8

📥 Commits

Reviewing files that changed from the base of the PR and between 0a9965a and f9ce13a.

📒 Files selected for processing (6)
  • bindata/network/frr-k8s/003-static-pod-rbac.yaml
  • bindata/network/frr-k8s/frr-k8s.yaml
  • pkg/network/bgp_vip.go
  • pkg/network/bgp_vip_test.go
  • pkg/network/render.go
  • pkg/network/render_test.go

Comment on lines +10 to +53
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: frr-k8s-static-pod
rules:
- apiGroups:
- frrk8s.metallb.io
resources:
- frrconfigurations
- frrk8sconfigurations
verbs:
- get
- list
- watch
- apiGroups:
- frrk8s.metallb.io
resources:
- frrnodestates
- bgpsessionstates
verbs:
- get
- list
- watch
- create
- update
- patch
- delete
- apiGroups:
- frrk8s.metallb.io
resources:
- frrnodestates/status
- bgpsessionstates/status
verbs:
- get
- update
- patch
- apiGroups:
- ""
resources:
- nodes
verbs:
- get
- list
- watch

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Broad, cluster-wide RBAC grant to a shared bootstrap ServiceAccount, applied unconditionally.

Two concerns:

  1. This ClusterRole/ClusterRoleBinding is rendered whenever the FRR AdditionalRoutingCapabilities provider is enabled — not conditioned on BGP VIP management being active (confirmed by render_test.go, where the object count is identical with bootstrapResult == nil). Clusters using FRR for unrelated BGP use cases get this grant even though the static pod it's meant for never runs.
  2. node-bootstrapper is a single ServiceAccount used across every node's bootstrap process; granting it cluster-wide create/update/patch/delete on frrnodestates/bgpsessionstates (not scoped to a single node's own objects, since plain RBAC can't express that) means a compromised node's bootstrap credential could tamper with every other node's BGP/FRR state.

As per path instructions, RBAC for Kubernetes manifests should follow "least privilege; no cluster-admin for workloads". Consider scoping the verbs down (e.g. dropping delete) and/or gating this manifest on BGP VIP management being active.

Also applies to: 55-66

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindata/network/frr-k8s/003-static-pod-rbac.yaml` around lines 10 - 53,
Restrict the FRR static-pod RBAC in the ClusterRole named frr-k8s-static-pod:
render this ClusterRole and its associated ClusterRoleBinding only when BGP VIP
management/bootstrap is active, not merely when AdditionalRoutingCapabilities is
enabled, and reduce node-state permissions to the minimum required by the static
pod, removing unnecessary destructive verbs such as delete. Update render tests,
including the bootstrapResult == nil case, to verify the resources are omitted
when BGP VIP management is inactive.

Source: Path instructions

Comment thread pkg/network/bgp_vip.go
Comment on lines +44 to +53
// bgpVIPConfigData is the parsed content of the bgp-vip-config ConfigMap's
// config.json key. The schema matches baremetal-runtimecfg's FRRPeerMapping.
type bgpVIPConfigData struct {
LocalASN int64 `json:"localASN"`
DefaultPeers []bgpVIPPeer `json:"defaultPeers"`
Communities []string `json:"communities,omitempty"`
APIVIPs []string `json:"apiVIPs"`
IngressVIPs []string `json:"ingressVIPs"`
HostOverrides map[string][]bgpVIPPeer `json:"hostOverrides,omitempty"`
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## File map\n'
ast-grep outline pkg/network/bgp_vip.go --view expanded || true

printf '\n## Search for field usages\n'
rg -n "Communities|HostOverrides|buildFRRConfigurationObjects|bgpVIPConfigData" pkg/network

printf '\n## Read relevant file sections\n'
wc -l pkg/network/bgp_vip.go pkg/network/bgp_vip_test.go
sed -n '1,240p' pkg/network/bgp_vip.go
printf '\n--- TESTS ---\n'
sed -n '1,260p' pkg/network/bgp_vip_test.go

Repository: openshift/cluster-network-operator

Length of output: 22402


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n## Read remaining bgp_vip.go section\n'
sed -n '240,340p' pkg/network/bgp_vip.go

printf '\n## Search for related terms in repo\n'
rg -n "communities|hostOverrides|defaultPeers|apiVIPs|ingressVIPs" pkg/network

Repository: openshift/cluster-network-operator

Length of output: 4736


BGP VIP config drops communities and hostOverrides
buildFRRConfigurationObjects and buildBGPVIPRawConfig only consume DefaultPeers, APIVIPs, and IngressVIPs; Communities and HostOverrides never affect the rendered FRRConfiguration. Any installer/admin config relying on per-host peer overrides or route communities is silently ignored.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/network/bgp_vip.go` around lines 44 - 53, Update
buildFRRConfigurationObjects and buildBGPVIPRawConfig to propagate
bgpVIPConfigData.Communities and HostOverrides into the rendered
FRRConfiguration, applying host-specific peers from HostOverrides instead of
only DefaultPeers and preserving configured route communities. Ensure
buildBGPVIPRawConfig includes both fields in its generated raw configuration so
installer and administrator settings are not discarded.

Comment thread pkg/network/bgp_vip.go
// vipManagement field.
infra, err := client.Default().Dynamic().Resource(schema.GroupVersionResource{
Group: "config.openshift.io", Version: "v1", Resource: "infrastructures",
}).Get(context.TODO(), "cluster", metav1.GetOptions{})

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Use a real context instead of context.TODO() for API calls.

All three client calls in this file use context.TODO(), giving no cancellation or timeout. These are external-call hot-paths executed during reconcile (Render()renderBGPVIPFRRConfiguration); an unresponsive API server would block indefinitely with no bound.

As per path instructions, **/*.go files should use "context.Context for cancellation and timeouts".

🕐 Proposed fix (threading a bounded context)
-	infra, err := client.Default().Dynamic().Resource(schema.GroupVersionResource{
-		Group: "config.openshift.io", Version: "v1", Resource: "infrastructures",
-	}).Get(context.TODO(), "cluster", metav1.GetOptions{})
+	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
+	defer cancel()
+	infra, err := client.Default().Dynamic().Resource(schema.GroupVersionResource{
+		Group: "config.openshift.io", Version: "v1", Resource: "infrastructures",
+	}).Get(ctx, "cluster", metav1.GetOptions{})

Also applies to: 105-105, 303-303

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/network/bgp_vip.go` at line 77, Replace context.TODO() in Render(),
renderBGPVIPFRRConfiguration, and the other affected client calls with a real
bounded context.Context propagated through the call chain; derive it from the
reconcile/request context and apply an appropriate timeout or cancellation
before each Kubernetes API call.

Source: Path instructions

Comment thread pkg/network/bgp_vip.go
Comment on lines +307 to +311
for _, session := range sessionList.Items {
status, _, _ := uns.NestedString(session.Object, "status", "bgpStatus")
if status != "Established" {
return false, nil
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Don't discard the error from uns.NestedString.

status, _, _ := uns.NestedString(...) silently swallows a type-mismatch error, which could mask a malformed BGPSessionState object.

As per path instructions, Go files should "Never ignore error returns".

🛡️ Proposed fix
-		status, _, _ := uns.NestedString(session.Object, "status", "bgpStatus")
+		status, _, err := uns.NestedString(session.Object, "status", "bgpStatus")
+		if err != nil {
+			return false, err
+		}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for _, session := range sessionList.Items {
status, _, _ := uns.NestedString(session.Object, "status", "bgpStatus")
if status != "Established" {
return false, nil
}
for _, session := range sessionList.Items {
status, _, err := uns.NestedString(session.Object, "status", "bgpStatus")
if err != nil {
return false, err
}
if status != "Established" {
return false, nil
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/network/bgp_vip.go` around lines 307 - 311, Handle the error returned by
uns.NestedString in the session status check within the BGP status function,
rather than discarding it. If extracting status or encountering a type mismatch
fails, return the error to the caller; otherwise continue checking whether
status equals "Established".

Source: Path instructions

@mkowalski mkowalski marked this pull request as draft July 10, 2026 10:10
@openshift-ci openshift-ci Bot added the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Jul 10, 2026
@openshift-ci

openshift-ci Bot commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

@mkowalski: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/lint f9ce13a link true /test lint

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Render a sessions-only FRRConfiguration CR named bgp-vip
(namespace openshift-frr-k8s) when BGP-based VIP management is active:
BareMetal platform, the BGPBasedVIPManagement feature gate enabled,
and the Infrastructure CR reporting vipManagement "BGP". Peer/VIP data
is read from the installer's bgp-vip-config ConfigMap (schema =
baremetal-runtimecfg's FRRPeerMapping).

The CR carries only the BGP sessions (neighbors, BFD profiles). VIP
advertisement deliberately does not use CRD prefixes/toAdvertise:
frr-k8s renders router-level prefixes as unconditional `network`
statements that would defeat kube-vip's health gating, and toAdvertise
cannot express "advertise redistributed routes" - without it frr-k8s
renders deny-any egress prefix-lists. Advertisement therefore happens
exclusively via rawConfig: `ip import-table 198` plus health-gated
table-direct redistribution of routing table 198 (populated by
kube-vip only for VIPs with healthy backends), filtered through
route-maps/prefix-lists permitting exactly the VIP prefixes, with
high-sequence per-neighbor <peer>-out permits opening egress only for
the VIP prefix-lists.

The CR carries no node selector: workers' frr-k8s DaemonSet consumes
the same sessions and gated redistribution so router-bearing workers
advertise the ingress VIP.

The feature gate is defined locally and guarded via KnownFeatures
until openshift/api#2923 ships the gate, and the Infrastructure
vipManagement field is read unstructured until it lands in the
vendored openshift/api - the feature is inert until then.

Validated end to end (github.com/mkowalski/bgp-vip-demo).
…anagement

Under BGP VIP management the masters run the frr-k8s static pod
(deployed by MCO), which owns the FRR sessions for the VIPs; the
frr-k8s DaemonSet must therefore not schedule there. Label-based
approaches fail: NodeRestriction prevents nodes from setting the
exclusion label on themselves, and any external labeler races with
the DaemonSet controller at node bring-up. A role-based required
node-affinity (node-role.kubernetes.io/master DoesNotExist) has
neither problem, so render it into the DaemonSet whenever BGP VIP
management is active.

renderAdditionalRoutingCapabilities gains the client, bootstrap
result, and feature gates needed to make that call, reusing the same
isBGPVIPManagement helper as the FRRConfiguration rendering.
The frr-k8s static pod on control plane nodes has no ServiceAccount;
its controller and frr-status containers authenticate with the node
kubeconfig (/etc/kubernetes/kubeconfig), whose identity is the MCO
node-bootstrapper ServiceAccount (verified live via oc auth whoami).

Grant that identity read access to FRRConfigurations and
FRRK8sConfigurations, write access to the node state CRs
(FRRNodeStates, BGPSessionStates and their statuses), and the
namespace-scoped secrets/pods reads that 002-rbac.yaml gives the
DaemonSet service account, so the static pod controller's informer
caches can sync.

Known limitation: these grants are not scoped per-node - any holder
of the node-bootstrapper credential can write any node's state CRs.
Admission-level enforcement is needed before GA.
@mkowalski mkowalski force-pushed the bgp-vip-management branch from f9ce13a to 70cd187 Compare July 10, 2026 10:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants