Skip to content

[ENG-3999] Support IPv6 filer resolvers in drive mounts - #260

Draft
SystemSculpt wants to merge 1 commit into
mainfrom
codex/eng-3999-sandbox-ipv6-filer
Draft

[ENG-3999] Support IPv6 filer resolvers in drive mounts#260
SystemSculpt wants to merge 1 commit into
mainfrom
codex/eng-3999-sandbox-ipv6-filer

Conversation

@SystemSculpt

@SystemSculpt SystemSculpt commented Jul 21, 2026

Copy link
Copy Markdown
Member

Summary

  • accept IPv4 and IPv6 nameservers when resolving the Agent Drive filer
  • preserve SeaweedFS's host:http.grpc address format for both address families
  • cover IPv4, production-shaped IPv6, zoned IPv6, malformed fallback, and directive matching

Root cause

Sandbox drive mounts treated the first resolver in /etc/resolv.conf as the filer, but validated it by requiring four dot-separated components. Current Blaxel sandboxes receive IPv6-only resolver addresses (for example 2600:1f14:c75:3900::301), so the parser rejected the valid filer address before blfs could start. The SDK integration failures were downstream symptoms of that sandbox-api defect.

The change uses netip.ParseAddr instead of an IPv4 shape check. It keeps the raw IPv6 literal in SeaweedFS's host:http.grpc representation; SeaweedFS splits on the final colon and brackets the host when it constructs HTTP and gRPC endpoints.

Verification

  • GOTOOLCHAIN=go1.25.0 SHELL=/bin/sh go test -count=1 ./...
  • GOTOOLCHAIN=go1.25.0 go test -race -count=1 ./src/handler/drive
  • GOTOOLCHAIN=go1.25.0 go vet ./...
  • GOTOOLCHAIN=go1.25.0 GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build ./...

The test matrix proves the former IPv6 input now resolves while IPv4 behavior remains unchanged. Drive listing already splits mount sources at the final colon, so IPv6 mount sources remain compatible there as well.

Scope

This changes only filer-address discovery and formatting for drive mounts. It does not change DNS injection, networking, drive authorization, retries, or error suppression.

Linear: ENG-3999


Note

Replaces IPv4-only nameserver validation with netip.ParseAddr to support both IPv4 and IPv6 filer addresses, extracts parseFilerAddress for testability, and adds a comprehensive test suite covering both address families and edge cases.

Written by Mendral for commit 26e3793.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🧪 Testing Guide

What this PR addresses

Sandbox drive mounts failed on IPv6-only environments because getFilerAddress() validated the resolver IP by checking for exactly four dot-separated components (IPv4 only). Sandboxes receiving IPv6 resolver addresses (e.g. 2600:1f14:c75:3900::301) had their filer address rejected, preventing blfs from starting and breaking drive mounts entirely.

Steps to reproduce the original issue

  1. Deploy a sandbox in an environment where /etc/resolv.conf contains only an IPv6 nameserver (e.g. nameserver 2600:1f14:c75:3900::301).
  2. Attempt to mount an Agent Drive via the sandbox API.
  3. Observe that the mount fails with "no valid nameserver found in /etc/resolv.conf" because the IPv6 address doesn't match the IPv4 shape check.

What to verify (expected behavior)

  1. Unit tests pass — run go test -race -count=1 ./src/handler/drive and confirm all TestParseFilerAddress and TestFormatFilerServerAddress cases pass, covering:
    • IPv4 nameserver (172.16.1.126)
    • IPv6 nameserver (2600:1f14:c75:3900::301)
    • IPv6 with zone ID (fe80::1%eth0)
    • Malformed entries are skipped gracefully (falls through to next valid entry)
    • Non-nameserver directives are ignored
  2. IPv4 environments remain unaffected — in a sandbox with an IPv4 resolver, drive mounts continue to work as before.
  3. IPv6 environments now work — in a sandbox with an IPv6-only resolver, drive mounts succeed and blfs receives the correct filer address in host:http.grpc format (e.g. 2600:1f14:c75:3900::301:49200.49201).
  4. Full build compiles cleanlyCGO_ENABLED=0 go build ./... succeeds with no errors.
  5. No regression in existing drive operations — listing, reading, and writing files on mounted drives still works in both IPv4 and IPv6 environments.

Note

Posted by PR Testing Guide · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

✅ Linked to Linear issue ENG-3999 — status already In Progress.

Note

Posted by Linear Issue Enforcer · Tag @mendral-app with feedback.

@mendral-app

mendral-app Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🔀 Interaction Flow

Here's a sequence diagram showing how the drive mount components interact after this change:

sequenceDiagram
    participant Caller as Mount Handler
    participant GFA as getFilerAddress()
    participant PFA as parseFilerAddress()
    participant RC as /etc/resolv.conf
    participant NIP as net/netip.ParseAddr()
    participant FMT as formatFilerServerAddress()
    participant SW as SeaweedFS (blfs)

    Caller->>GFA: getFilerAddress()
    GFA->>PFA: parseFilerAddress(resolvConfPath)
    PFA->>RC: Read resolv.conf lines
    RC-->>PFA: nameserver entries + directives
    loop Each resolv.conf line
        PFA->>PFA: Match "nameserver" directive
        PFA->>NIP: ParseAddr(candidate)
        NIP-->>PFA: valid IPv4/IPv6 or error
        Note over PFA,NIP: Supports IPv4, IPv6, and zoned IPv6
    end
    PFA-->>GFA: filer IP address (string)
    GFA-->>Caller: filer address
    Caller->>FMT: formatFilerServerAddress(addr)
    FMT-->>Caller: "addr:49200.49201"
    Note over FMT: SeaweedFS host:http.grpc format<br/>(IPv6 left unbracketed—SW adds brackets)
    Caller->>SW: exec blfs mount -filer=addr:49200.49201
Loading

Summary

The PR refactors the filer address resolution into two focused functions:

Function Responsibility
parseFilerAddress() Reads resolv.conf, validates entries via net/netip.ParseAddr() (handles IPv4, IPv6, zoned IPv6)
formatFilerServerAddress() Wraps the address in SeaweedFS's host:http.grpc port format

Previously, validation relied on splitting by . and checking for 4 components—rejecting valid IPv6 resolvers. Now the standard library handles all address families correctly.

Note

Posted by PR Sequence Diagram · Tag @mendral-app with feedback.

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

LGTM

The change is correct and well-tested. The critical assumption—that SeaweedFS splits on the last colon in its host:http.grpc format—makes the unbracketed IPv6 representation safe (e.g., 2600:1f14:c75:3900::301:49200.49201 splits cleanly because the port segment 49200.49201 contains no colon). The netip.ParseAddr canonicalization is a strict improvement over the old dot-count heuristic. Test coverage is thorough.

Tag @mendral-app with feedback or questions. View session

@SystemSculpt
SystemSculpt marked this pull request as draft July 27, 2026 20:08
@SystemSculpt

Copy link
Copy Markdown
Member Author

Parking this pending owner review — @cploujoux, this is in your lane (ENG-3999), so handing it over rather than merging it myself.

What is verified:

  • merges cleanly with current main; go build ./..., go vet ./... and go test ./src/handler/drive all pass
  • the full sandbox-api suite shows only mkdir /var/log/sandbox-api: permission denied failures in src/handler/process, and those reproduce identically on origin/main (local non-root environment, unrelated to this change)
  • the IPv4 path is unchanged: the format string is still %s:49200.49201 and netip.ParseAddr(...).String() returns the same literal for IPv4. The old four-dot check also accepted invalid input such as 1.2.3.junk, so this is strictly tighter
  • no overlap with cploujoux/sandbox-api-ipv6-autoforward, which adds src/lib/networking/dualstack.go on the listener side and does not touch getFilerAddress

What is NOT verified, and why I stopped:

  • that blfs/SeaweedFS actually mounts with an unbracketed IPv6 filer. The split-on-final-colon reasoning is sound but it is an argument, not an execution — and that is the entire point of the change
  • I tried to verify on dev first and could not: three sandboxes in a dev workspace (blaxel/base-image:latest x2, blaxel/node:latest x1, eu-dub-1) all reached FAILED. Cause is scheduling, not this PR:
reason: FailedScheduling
0/9 nodes are available: 2 node(s) didn't match Pod's node affinity/selector,
7 node(s) had untolerated taint(s). no new claims to deallocate,
preemption: 0/9 nodes are available: 9 Preemption is not helpful for scheduling.

Pod sbx-eng3999-probe3-gokind, namespace dev-onboarder, cluster executionplanev3-dev-eu-west-1, label blaxel-node-type-override: kraft. All probe sandboxes were deleted.

One thing to weigh before merging: a push to main sets BL_ENV=prod in build.yaml, builds mk3 images into the prod image bucket, and runs publish-sandbox.sh with TAG: latest, which controlplane picks up automatically. Worst case if the IPv6 assumption is wrong is that IPv6 mounts keep failing; IPv4 behaviour is provably unchanged.

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