Skip to content

feat(agent,fmds): expose dual-stack public IP metadata - #4722

Merged
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2401
Aug 8, 2026
Merged

feat(agent,fmds): expose dual-stack public IP metadata#4722
chet merged 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2401

Conversation

@chet

@chet chet commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

FMDS was taking the first address from a physical interface and serving it as public-ipv4. Once an interface had both families, address order decided what tenants saw -- including the possibility of an IPv6 address showing up under the IPv4 name.

This keeps the lowest address from each family across every physical interface in InstanceMetadata, plumbs both through FmdsConfigUpdate, and serves public-ipv4 + public-ipv6 from typed values in both embedded and standalone FMDS. The old address field stays on the wire for IPv4 compatibility, and new FMDS still recognizes the IPv6 value an older agent may have put there.

Both fields are now listed under /meta-data/, while an unassigned family keeps the existing 200 + empty-body behavior. An older FMDS ignores address_ipv6 until it is upgraded; for an IPv6-only instance, that replaces the old wrong-family value with an empty public-ipv4 response.

Related issues

This supports #2401.

Type of Change

  • Add - New feature or capability
  • Change - Changes in existing functionality
  • Fix - Bug fixes
  • Remove - Removed features or deprecated functionality
  • Internal - Internal changes (refactoring, tests, docs, etc.)

Breaking Changes

  • This PR contains breaking changes

Testing

  • Unit tests added/updated
  • Integration tests added/updated
  • Manual testing performed
  • No testing required (docs, internal refactor, etc.)

Closes #2401

@chet
chet requested a review from a team as a code owner August 7, 2026 20:47
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Summary by CodeRabbit

  • New Features
    • Added public IPv6 address support alongside IPv4 for instance metadata and configuration.
    • Metadata listings now expose separate public-ipv4 and public-ipv6 categories.
    • Configuration updates support independent IPv4 and IPv6 public addresses.
    • Missing address families return successful empty responses.
  • Compatibility
    • Existing address configurations remain supported, including legacy IPv4 and IPv6 values.
    • Dedicated IPv6 values take precedence when provided.
  • Bug Fixes
    • Invalid address values now produce clear validation errors.

Walkthrough

The change adds dual-stack public address support. The agent selects IPv4 and IPv6 addresses independently, sends both through FMDS configuration, and exposes both through metadata endpoints.

Changes

Dual-stack public address flow

Layer / File(s) Summary
Address contracts and configuration state
crates/rpc/proto/fmds.proto, rest-api/proto/core/src/v1/fmds_nico.proto, crates/fmds/src/state.rs
The configuration contract adds address_ipv6. FmdsConfig stores optional typed IPv4 and IPv6 values. Legacy address compatibility remains documented.
Agent address selection and update
crates/agent/src/periodic_config_fetcher.rs, crates/agent/src/fmds_client.rs
The agent parses physical-interface addresses, selects the lowest address per family, preserves empty values for missing families, and sends both values in FMDS updates.
gRPC configuration ingestion
crates/fmds/src/grpc_server.rs
The gRPC server classifies legacy addresses, parses address_ipv6, applies IPv6 fallback behavior, and rejects invalid values with InvalidArgument.
Dual-stack metadata endpoints and validation
crates/agent/src/instance_metadata_endpoint.rs, crates/fmds/src/rest_server.rs, crates/agent/src/tests/full.rs
Agent and FMDS metadata endpoints expose public-ipv4 and public-ipv6. Tests cover missing values, dual-stack values, metadata listings, address ordering, invalid inputs, and gRPC-to-REST propagation.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Agent
  participant FMDS
  participant MetadataEndpoint
  Agent->>Agent: Select lowest IPv4 and IPv6 addresses
  Agent->>FMDS: Send address and address_ipv6
  FMDS->>FMDS: Store public_ipv4 and public_ipv6
  MetadataEndpoint->>FMDS: Read public address values
  FMDS-->>MetadataEndpoint: Return formatted IPv4 or IPv6 value
Loading

Possibly related issues

  • NVIDIA/infra-controller issue 2401: The change addresses dual-stack FMDS metadata objectives, including public-ipv6 exposure and order-independent address selection.

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 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.
Title check ✅ Passed The title clearly summarizes the main change: exposing dual-stack public IP metadata for the agent and FMDS.
Description check ✅ Passed The description directly explains the dual-stack metadata changes, compatibility behavior, endpoints, and testing.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@chet

chet commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full_review, thanks!

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@chet I will perform a complete review of pull request #4722.

🐇

✅ Action performed

Full review finished.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

🔐 TruffleHog Secret Scan

No secrets or credentials found!

Your code has been scanned for 700+ types of secrets and credentials. All clear! 🎉

🔗 View scan details

🕐 Last updated: 2026-08-07 21:03:31 UTC | Commit: 4371e01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@crates/rpc/proto/fmds.proto`:
- Around line 61-63: Update the public_ipv6 field documentation in
crates/rpc/proto/fmds.proto (lines 61-63) and
rest-api/proto/core/src/v1/fmds_nico.proto (lines 49-51) to state that an empty
or omitted value falls back to address, while a non-empty public_ipv6 value
takes precedence; document this create/update omission and fallback behavior
consistently in both protocol definitions.
🪄 Autofix

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 64dd1294-16fc-4175-8cae-8662a568508d

📥 Commits

Reviewing files that changed from the base of the PR and between 355618d and 1d5c212.

⛔ Files ignored due to path filters (1)
  • rest-api/proto/core/gen/v1/fmds_nico.pb.go is excluded by !**/*.pb.go, !**/gen/**, !rest-api/**/*.pb.go
📒 Files selected for processing (9)
  • crates/agent/src/fmds_client.rs
  • crates/agent/src/instance_metadata_endpoint.rs
  • crates/agent/src/periodic_config_fetcher.rs
  • crates/agent/src/tests/full.rs
  • crates/fmds/src/grpc_server.rs
  • crates/fmds/src/rest_server.rs
  • crates/fmds/src/state.rs
  • crates/rpc/proto/fmds.proto
  • rest-api/proto/core/src/v1/fmds_nico.proto

Comment thread crates/rpc/proto/fmds.proto Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
crates/agent/src/periodic_config_fetcher.rs (1)

337-348: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Collect addresses from every physical interface.

Line 345 uses .find(...), so interfaces after the first physical interface cannot contribute an address. If IPv4 and IPv6 addresses are on different physical interfaces, FMDS serves one family as unavailable.

Filter all physical interfaces, flatten their addresses, and pass the combined values to select_public_addresses. Add a case with addresses split across two physical interfaces.

Proposed fix
-    let public_addresses = instance
-        .status
-        .as_ref()
-        .and_then(|status| status.network.as_ref())
-        .and_then(|network| {
-            network
-                .interfaces
-                .iter()
-                .find(|interface| interface.virtual_function_id.is_none())
-                .map(|interface| select_public_addresses(&interface.addresses))
-        })
+    let public_addresses = instance
+        .status
+        .as_ref()
+        .and_then(|status| status.network.as_ref())
+        .map(|network| {
+            let addresses = network
+                .interfaces
+                .iter()
+                .filter(|interface| interface.virtual_function_id.is_none())
+                .flat_map(|interface| interface.addresses.iter().cloned())
+                .collect::<Vec<_>>();
+            select_public_addresses(&addresses)
+        })
         .transpose()?
         .unwrap_or_default();
🤖 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 `@crates/agent/src/periodic_config_fetcher.rs` around lines 337 - 348, Update
the public_addresses collection to process every physical interface instead of
stopping at the first match: filter interfaces with no virtual_function_id,
flatten their addresses into one combined collection, and pass it to
select_public_addresses while preserving the existing optional-status behavior.
Add a test covering IPv4 and IPv6 addresses split across two physical
interfaces.
🤖 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.

Outside diff comments:
In `@crates/agent/src/periodic_config_fetcher.rs`:
- Around line 337-348: Update the public_addresses collection to process every
physical interface instead of stopping at the first match: filter interfaces
with no virtual_function_id, flatten their addresses into one combined
collection, and pass it to select_public_addresses while preserving the existing
optional-status behavior. Add a test covering IPv4 and IPv6 addresses split
across two physical interfaces.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 64b76b96-50a8-49a9-b1dc-f4d6e0d21710

📥 Commits

Reviewing files that changed from the base of the PR and between 355618d and 1d5c212.

⛔ Files ignored due to path filters (1)
  • rest-api/proto/core/gen/v1/fmds_nico.pb.go is excluded by !**/*.pb.go, !**/gen/**, !rest-api/**/*.pb.go
📒 Files selected for processing (9)
  • crates/agent/src/fmds_client.rs
  • crates/agent/src/instance_metadata_endpoint.rs
  • crates/agent/src/periodic_config_fetcher.rs
  • crates/agent/src/tests/full.rs
  • crates/fmds/src/grpc_server.rs
  • crates/fmds/src/rest_server.rs
  • crates/fmds/src/state.rs
  • crates/rpc/proto/fmds.proto
  • rest-api/proto/core/src/v1/fmds_nico.proto

FMDS was taking the first address from a physical interface and serving it as `public-ipv4`. Once an interface had both families, address order decided what tenants saw -- including the possibility of an IPv6 address showing up under the IPv4 name.

So, keep the lowest address from each family in `InstanceMetadata`, plumb both through `FmdsConfigUpdate`, and serve `public-ipv4` + `public-ipv6` from typed values in both embedded and standalone FMDS. The old `address` field stays on the wire for IPv4 compatibility, and new FMDS still recognizes the IPv6 value an older agent may have put there.

Both fields are now listed under `/meta-data/`, while an unassigned family keeps the existing `200` + empty-body behavior. An older FMDS ignores `address_ipv6` until it is upgraded; for an IPv6-only instance, that replaces the old wrong-family value with an empty `public-ipv4` response.

This does NOT add an IPv6 FMDS listener or the per-interface EC2 metadata tree -- it makes the metadata we already serve properly dual-stack.

Tested with the focused agent metadata tests, the standalone FMDS suite, the end-to-end agent FMDS test, Clippy, and Carbide lints.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet
chet merged commit e2f5a78 into NVIDIA:main Aug 8, 2026
122 checks passed
@chet
chet deleted the gh-issue-2401 branch August 8, 2026 00:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Overlay/10 — FMDS dual-stack metadata

2 participants