Skip to content

feat(dns): serve PTR queries from carbide-dns#2720

Draft
chet wants to merge 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2643
Draft

feat(dns): serve PTR queries from carbide-dns#2720
chet wants to merge 1 commit into
NVIDIA:mainfrom
chet:gh-issue-2643

Conversation

@chet

@chet chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Summary

carbide-dns forwards each DNS query to carbide-api's LookupRecord RPC and builds RData from the reply. Its qtype gate allowed only A and AAAA (NotImp for the rest), so reverse-DNS (PTR) queries never reached the resolver. This adds PTR to the gate and builds an RData::PTR from the reply.

  • PTR joins the handle_request qtype gate; retrieve_records builds RData::PTR from the reply's content — a Name, unlike the address-valued A/AAAA records.
  • The per-type content → RData mapping moves into a small content_to_rdata function, so it is unit-testable without a mock upstream.
  • Table test covers the A/AAAA/PTR mappings plus the drop-on-unparseable and unsupported-type paths.

With this, a PTR query reaches the resolver handler (#2641) end to end: carbide-dns forwards it, the handler resolves the address to a hostname, and carbide-dns returns it as a PTR record.

Implements #2643. Part of the #2630 epic (reverse DNS / PTR records).

Draft pending review.

@copy-pr-bot

copy-pr-bot Bot commented Jun 21, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 8aebeb96-63d4-471e-aef5-270dbd4d16e6

📥 Commits

Reviewing files that changed from the base of the PR and between 611e96b and 128a4f5.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • crates/dns/Cargo.toml
  • crates/dns/src/lib.rs

Summary by CodeRabbit

  • New Features

    • Added support for PTR DNS record type resolution
  • Bug Fixes

    • Improved DNS record parsing with enhanced error handling and validation to prevent failures during record construction

Walkthrough

A new private helper content_to_rdata centralizes DNS record-content parsing into Hickory RData for A, AAAA, and PTR query types, with warning-logged None on failure. handle_request is extended to dispatch PTR queries, and retrieve_records is refactored to call the helper rather than inline per-type logic. A unit test validates all cases.

Changes

PTR Support and RData Centralization

Layer / File(s) Summary
content_to_rdata helper and PTR dispatch gate
crates/dns/Cargo.toml, crates/dns/src/lib.rs
Adds carbide-test-support as a dev dependency, imports PTR from Hickory, introduces content_to_rdata(qtype, content) -> Option<RData> for A, AAAA, and PTR with warning-logged None on parse failure, and extends the handle_request qtype match gate to include DnsResourceRecordType::PTR.
retrieve_records refactor and unit tests
crates/dns/src/lib.rs
Replaces the inline per-qtype RData construction match in retrieve_records with a delegation to content_to_rdata, and adds a unit test covering successful A/AAAA/PTR parsing, invalid content graceful handling, and None return for unsupported qtypes such as SOA.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

  • carbide-dns: unlock PTR in handle_request + RData::PTR arm #2643: Implements PTR record support via the same functions (handle_request dispatch gate and retrieve_records RData construction), directly fulfilling the centralized content_to_rdata helper approach with comprehensive unit test coverage.
  • Support Reverse DNS (PTR Records) #2630: Addresses the "unlocking PTR in carbide-dns" requirement by adding PTR to the supported record types gate in handle_request and introducing the content_to_rdata helper for PTR record parsing.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: adding PTR query support to carbide-dns, which is the primary objective of this changeset.
Description check ✅ Passed The description is comprehensive and directly aligned with the changeset, detailing the motivation, implementation approach, testing strategy, and context within the larger epic.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

@chet

chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

🧹 Nitpick comments (1)
crates/dns/src/lib.rs (1)

534-558: ⚡ Quick win

Refactor this test to table-driven scenarios for consistency with project standards.

This test maps multiple input variants to expected outcomes and should be expressed as table-driven cases to match repo testing conventions and improve extensibility.

Suggested refactor
 #[test]
 fn content_to_rdata_builds_supported_types_and_drops_unparseable() {
-    // Supported types build the matching RData from valid content.
-    assert!(matches!(
-        content_to_rdata(DnsResourceRecordType::A, "192.0.2.1"),
-        Some(RData::A(_))
-    ));
-    assert!(matches!(
-        content_to_rdata(DnsResourceRecordType::AAAA, "fd00::1"),
-        Some(RData::AAAA(_))
-    ));
-
-    // A PTR's content is the target FQDN, which round-trips into the RData.
-    match content_to_rdata(DnsResourceRecordType::PTR, "host.example.com.") {
-        Some(RData::PTR(PTR(name))) => assert_eq!(name.to_string(), "host.example.com."),
-        other => panic!("expected PTR rdata, got {other:?}"),
-    }
-
-    // Unparseable content for a supported type is dropped rather than panicked on.
-    assert!(content_to_rdata(DnsResourceRecordType::A, "not-an-ip").is_none());
-    assert!(content_to_rdata(DnsResourceRecordType::AAAA, "192.0.2.1").is_none());
-
-    // A type the gate never dispatches here produces no RData.
-    assert!(content_to_rdata(DnsResourceRecordType::SOA, "unused").is_none());
+    let cases = [
+        (DnsResourceRecordType::A, "192.0.2.1", true),
+        (DnsResourceRecordType::AAAA, "fd00::1", true),
+        (DnsResourceRecordType::A, "not-an-ip", false),
+        (DnsResourceRecordType::AAAA, "192.0.2.1", false),
+        (DnsResourceRecordType::SOA, "unused", false),
+    ];
+
+    for (qtype, content, expect_some) in cases {
+        assert_eq!(content_to_rdata(qtype, content).is_some(), expect_some);
+    }
+
+    match content_to_rdata(DnsResourceRecordType::PTR, "host.example.com.") {
+        Some(RData::PTR(PTR(name))) => assert_eq!(name.to_string(), "host.example.com."),
+        other => panic!("expected PTR rdata, got {other:?}"),
+    }
 }

As per coding guidelines, “Prefer table-driven tests for any function that maps inputs to outputs, errors, or other observable results,” and “Reach for a table whenever two or more tests call the same operation with different inputs.”

🤖 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/dns/src/lib.rs` around lines 534 - 558, The test function
content_to_rdata_builds_supported_types_and_drops_unparseable uses individual
assert statements to test multiple input-output scenarios for the
content_to_rdata function. Refactor this to a table-driven test pattern by
defining a slice or vector of test cases where each case contains the input
parameters (DnsResourceRecordType and content string), the expected output
(matching RData variant or None), and a test description. Then iterate through
the table executing the same validation logic for each case, consolidating the
repetitive assertions into a single parameterized test loop.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@crates/dns/src/lib.rs`:
- Around line 534-558: The test function
content_to_rdata_builds_supported_types_and_drops_unparseable uses individual
assert statements to test multiple input-output scenarios for the
content_to_rdata function. Refactor this to a table-driven test pattern by
defining a slice or vector of test cases where each case contains the input
parameters (DnsResourceRecordType and content string), the expected output
(matching RData variant or None), and a test description. Then iterate through
the table executing the same validation logic for each case, consolidating the
repetitive assertions into a single parameterized test loop.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 48e1f939-2f39-4f51-b483-128390d250f8

📥 Commits

Reviewing files that changed from the base of the PR and between 611e96b and 14a9996.

📒 Files selected for processing (1)
  • crates/dns/src/lib.rs

@chet

chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Thanks! Done -- content_to_rdata's test is now a case table. Used a named-struct (Case) with an Expect enum for the per-type outcome rather than a positional tuple (so each row reads for itself, and the PTR FQDN round-trip folds into the table too), matching this repo's table-test convention. PTAL.

@chet

chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh pr view 2720 --repo NVIDIA/infra-controller --json files,title,body | jq '.title, .body'
gh pr diff 2720 --repo NVIDIA/infra-controller

Length of output: 8829



@chet: The refactor looks clean and production-grade. A few observations:

content_to_rdata — well-structured; each parse path logs at warn! and returns None rather than propagating errors, which is the right contract for a filter-map context. The doc comment accurately describes the function's invariants.

Gate extension — the multi-line Ok(t @ (A | AAAA | PTR)) pattern is idiomatic and makes adding a fourth type mechanical.

retrieve_records callsite — collapsing the prior 16-line inline match to content_to_rdata(qtype, &r.content)? is a net improvement in readability and removes the now-stale // Unreachable comment correctly.

Test — the Case/Expect design is the right call. Each row is fully self-describing, the Ptr variant carries the expected FQDN so the round-trip is explicitly asserted, and the description field surfaces immediately in any panic message. Two minor observations, neither blocking:

  1. The Expect::A and Expect::Aaaa arms match on _ for the inner value, so they verify shape but not content (e.g., "192.0.2.1" actually parsing to 192.0.2.1). This is defensible — you're testing dispatch, not std's IPv4 parser — but if you ever want stronger coverage, storing the expected IP in the variant (e.g., A(Ipv4Addr)) and asserting ip == expected_ip would make the test self-validating end-to-end.

  2. Expect::Aaaa — both Aaaa (PascalCase) and AAAA (acronym) are idiomatic Rust; Aaaa is consistent with PascalCase convention so there's no issue here.

Nothing else to flag. The implementation is solid. LGTM.

🐇 ✨

@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.

🧹 Nitpick comments (1)
crates/dns/src/lib.rs (1)

534-604: ⚡ Quick win

Prefer value_scenarios!/check_values for this total-operation table test.

This test is well-structured, but for consistency with repo conventions, migrate the case table to carbide_test_support::value_scenarios! (or check_values if clearer with multi-field expectations).

As per coding guidelines, “Use carbide_test_support::value_scenarios! macro for total operations (those returning a plain value, Option, or bool) in Rust tests.”

🤖 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/dns/src/lib.rs` around lines 534 - 604, The test
`content_to_rdata_builds_supported_types_and_drops_unparseable` should be
migrated from a manual table-driven test pattern to use the
`carbide_test_support::value_scenarios!` macro for consistency with repository
conventions. Replace the current manual `Expect` enum, `Case` struct, and the
for loop that iterates over the cases array with the `value_scenarios!` macro
pattern. The macro will handle the test case iteration and validation, making
the test more idiomatic for this codebase which uses value scenarios for total
operations like the `content_to_rdata` function.

Source: Coding guidelines

🤖 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.

Nitpick comments:
In `@crates/dns/src/lib.rs`:
- Around line 534-604: The test
`content_to_rdata_builds_supported_types_and_drops_unparseable` should be
migrated from a manual table-driven test pattern to use the
`carbide_test_support::value_scenarios!` macro for consistency with repository
conventions. Replace the current manual `Expect` enum, `Case` struct, and the
for loop that iterates over the cases array with the `value_scenarios!` macro
pattern. The macro will handle the test case iteration and validation, making
the test more idiomatic for this codebase which uses value scenarios for total
operations like the `content_to_rdata` function.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 72625392-5383-4191-b285-3eb77cd3f6f9

📥 Commits

Reviewing files that changed from the base of the PR and between 611e96b and 6c95e35.

📒 Files selected for processing (1)
  • crates/dns/src/lib.rs

carbide-dns answers DNS by forwarding each query to carbide-api's `LookupRecord` RPC and building `RData` from the reply. Its qtype gate allowed only A and AAAA, returning NotImp for anything else, so reverse-DNS (PTR) queries never reached the resolver. This adds PTR to the gate and builds an `RData::PTR` from the reply, so a PTR query now flows through to the handler that resolves the address to a hostname (NVIDIA#2641) and returns that name.

The reply's content is parsed by record type -- an address for A/AAAA, a `Name` for PTR, since a PTR answer is a hostname rather than an address. That mapping moves into a small `content_to_rdata` function so it can be tested without a mock upstream; a record whose content does not parse is logged and dropped, unchanged from before.

A table test covers the A, AAAA, and PTR mappings, plus the drop-on-unparseable and unsupported-type paths.

This supports NVIDIA#2643.

Signed-off-by: Chet Nichols III <chetn@nvidia.com>
@chet

chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai Thanks! Migrated the content_to_rdata test to value_scenarios! (added carbide-test-support as a dev-dep) -- the total-operation macro convention, and now consistent with the arpa-parse test in #2637. PTAL.

@chet

chet commented Jun 21, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh pr diff 2720 --repo NVIDIA/infra-controller

Length of output: 6927



@chet: The migration to value_scenarios! is a clear improvement over the Case/Expect approach, and it directly resolves the one concern raised in the prior pass.

What changed and why it's better:

  • Exact-value assertions for A and AAAA — the A arm now asserts Ipv4Addr::new(192, 0, 2, 1).into() and the AAAA arm round-trips through the parsed Ipv6Addr, rather than matching on _. This fully validates the parse path end-to-end, not just the discriminant shape.
  • Named scenario groups — grouping into "supported types…", "unparseable content…", and "a type the gate never dispatches…" maps failure messages directly to test intent, which is strictly better than a flat table with a description field.
  • Consistency with #2637 — aligning with the arpa-parse test's convention keeps the test corpus uniform, reducing future cognitive load.

Cargo.tomlcarbide-test-support is correctly placed under [dev-dependencies], so it carries no production binary weight.

No further issues. The implementation is production-grade throughout. LGTM.

🐇 ✨

[approve]

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.

1 participant