Skip to content

Conversation

@lstocchi
Copy link
Collaborator

@lstocchi lstocchi commented Nov 5, 2025

This needs cfergeau/podman#38 and fixes #280

Summary by CodeRabbit

  • New Features

    • Expanded cloud-init support: multiple file writes, commands, mounts, and embedded resources.
    • Improved Windows Hyper-V networking with integrated gvforwarder support for user-mode networking.
  • Bug Fixes

    • Avoids IPv6 localhost resolution timeouts in WSL by defaulting to IPv4 localhost.
  • Chores

    • Updated vendored Podman module and added Hyper-V utility enhancements.

@coderabbitai
Copy link

coderabbitai bot commented Nov 5, 2025

Walkthrough

Defaults SSH address resolution to IPv4 (127.0.0.1) via mc.GetAddress(), expands cloud-init data model and generation to support embedded resources and files, adds platform-specific secure symlink opening, factors Hyper‑V networking utilities into hutil, and updates vendored Podman pseudo-version and module entries.

Changes

Cohort / File(s) Summary
SSH Address Resolution
cmd/macadam/ssh.go, vendor/github.com/containers/podman/v5/pkg/machine/vmconfigs/machine.go
Replaces manual localhost/mc.IPAddress handling with a single mc.GetAddress() call; changes GetAddress default from "localhost" to "127.0.0.1".
Cloud‑Init Core Infrastructure
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go
Introduces WriteFile and EmbeddedResource types; expands UserData to include WriteFiles, RunCmd, and Mounts; adds GenerateUserDataFile and embeds extra resources into ISO creation.
Platform Cloud‑Init Generators
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go, vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_windows.go
Adds GenerateUserData and GetEmbeddedResources implementations for Unix and Windows; Windows implementation fetches/injects gvforwarder and prepares Hyper‑V networking artifacts.
Secure Symlink Handling
vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_linux.go, vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_unsupported.go
Adds platform-specific openSymlinkPath implementations: Linux uses securejoin.OpenatInRoot and reopen; non-Linux returns unsupported error.
Hyper‑V Networking Utilities
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/hutil/hutil.go
Adds exported HyperVVsockNMConnection constant and CreateNetworkUnit(netPort uint64) (string, error) for generating NetworkManager/systemd unit content.
Hyper‑V Stub Refactor
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/stubber.go
Replaces in-file network unit and constant with hutil counterparts, adjusts early networking logic, and moves cloud‑init ISO generation into a unified path.
Module & Vendoring Updates
go.mod, vendor/modules.txt
Updates github.com/containers/podman/v5 pseudo-version and adds .../pkg/machine/hyperv/hutil to vendored modules.

Sequence Diagram(s)

sequenceDiagram
    participant CLI as macadam CLI
    participant MC as MachineConfig (mc)
    participant CloudInit as cloudinit package
    participant ISO as ISO generator
    participant HV as Hyper-V (hutil)
    CLI->>MC: request SSH / start
    CLI->>MC: mc.GetAddress()
    note right of MC `#E6F0FF`: Address default now 127.0.0.1
    MC-->>CLI: return address

    CLI->>CloudInit: GenerateUserData(mc)
    CloudInit->>CloudInit: build UserData (users, writefiles, runCmd, mounts)
    CloudInit-->>ISO: user-data + embedded resources
    ISO->>ISO: create ISO (user-data, meta-data, network-config, embedded)
    ISO-->>HV: attach ISO / set DVDDiskPath

    CLI->>HV: request network unit
    HV->>HV: CreateNetworkUnit(netPort)
    HV-->>CLI: return unit content
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Review areas requiring extra attention:
    • Windows cloud-init: gvforwarder download, HTTP/GitHub API parsing, error handling.
    • Hyper-V hutil: verify CreateNetworkUnit signature vs. returned values and usage.
    • Changes to cloud-init data model and ISO embedding: ensure resource wiring and file encodings are correct.
    • SSH address default change: confirm no regressions where IPv6 localhost was expected.

Possibly related PRs

Suggested reviewers

  • cfergeau

Poem

🐇 I hopped through clouds and tiny bits of code,
Swapped localhost for a safer road.
I stitched files, fetched a tiny forwarder friend,
Wired networks, ISOs to the very end.
Nibble the carrot — this rabbit's patch is done! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes are in-scope updates to Podman vendored dependencies. However, vendored dependency updates appear to exceed the scope of fixing the localhost/127.0.0.1 issue in macadam. Clarify whether the extensive Podman vendor updates (new functions, cloud-init changes, hyperv utilities) are necessary for the macadam fix or should be separated into a dependency update PR.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title directly describes the main change: replacing manual address resolution with a machineConfig function call.
Linked Issues check ✅ Passed The PR successfully addresses issue #280 by using GetAddress() to default to 127.0.0.1 instead of localhost, fixing WSL mirrored networking IPv6 resolution issues.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@lstocchi lstocchi marked this pull request as ready for review November 5, 2025 16:28
@lstocchi lstocchi requested a review from cfergeau November 5, 2025 16:28
update podman to version containining IPv4 fix cfergeau/podman#38

Signed-off-by: lstocchi <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 30dedee and 8c79d9d.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (11)
  • cmd/macadam/ssh.go (1 hunks)
  • go.mod (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_linux.go (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_unsupported.go (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go (2 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_windows.go (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/hyperv/hutil/hutil.go (1 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/hyperv/stubber.go (5 hunks)
  • vendor/github.com/containers/podman/v5/pkg/machine/vmconfigs/machine.go (1 hunks)
  • vendor/modules.txt (3 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go (4)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_windows.go (2)
  • GenerateUserData (18-78)
  • GetEmbeddedResources (144-161)
vendor/github.com/containers/podman/v5/pkg/machine/vmconfigs/config.go (1)
  • MachineConfig (15-63)
vendor/github.com/containers/podman/v5/pkg/machine/keys.go (1)
  • GetSSHKeys (40-50)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go (3)
  • UserData (31-36)
  • User (15-21)
  • EmbeddedResource (38-41)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_windows.go (5)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go (1)
  • GenerateUserData (12-40)
vendor/github.com/containers/podman/v5/pkg/machine/vmconfigs/config.go (1)
  • MachineConfig (15-63)
vendor/github.com/containers/podman/v5/pkg/machine/keys.go (1)
  • GetSSHKeys (40-50)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go (4)
  • UserData (31-36)
  • User (15-21)
  • WriteFile (23-29)
  • EmbeddedResource (38-41)
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/hutil/hutil.go (2)
  • CreateNetworkUnit (27-35)
  • HyperVVsockNMConnection (9-25)
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/stubber.go (1)
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/hutil/hutil.go (2)
  • CreateNetworkUnit (27-35)
  • HyperVVsockNMConnection (9-25)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go (2)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go (1)
  • GetEmbeddedResources (42-44)
vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_windows.go (1)
  • GetEmbeddedResources (144-161)
cmd/macadam/ssh.go (1)
vendor/github.com/containers/podman/v5/pkg/machine/ssh.go (1)
  • LocalhostSSHShellWithAddress (24-29)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: test (windows-latest)
🔇 Additional comments (16)
vendor/github.com/containers/podman/v5/pkg/machine/vmconfigs/machine.go (1)

315-326: LGTM! IPv4 localhost default addresses the WSL mirrored networking issue.

The change to default to "127.0.0.1" instead of "localhost" directly resolves the issue where WSL mirrored networking prefers IPv6 ::1 resolution, which it doesn't support. The detailed comment explains the rationale clearly.

go.mod (1)

194-194: LGTM! Vendored dependency updated to match PR requirements.

The pseudo-version update aligns with the PR description noting dependency on cfergeau/podman#38.

cmd/macadam/ssh.go (1)

119-119: LGTM! Centralized address resolution improves maintainability.

The refactoring to use mc.GetAddress() creates a single source of truth for address resolution, ensuring consistent behavior across the codebase.

vendor/modules.txt (1)

320-320: LGTM! Vendor manifest updated correctly.

The module resolution and new hutil package import path are properly recorded in the vendor manifest.

Also applies to: 399-399

vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit_unix.go (2)

12-40: LGTM! Unix cloud-init generation is well-structured.

The function correctly:

  • Retrieves SSH keys with proper error handling
  • Constructs user-data with appropriate permissions (passwordless sudo)
  • Marshals to YAML with error logging
  • Prepends the required "#cloud-config" header

42-44: LGTM! Unix platform requires no embedded resources.

Correctly returns nil as Unix platforms don't require embedded resources like the Windows gvforwarder.

vendor/github.com/containers/podman/v5/pkg/machine/hyperv/hutil/hutil.go (2)

9-25: LGTM! NetworkManager configuration is well-defined.

The vsock0 connection configuration is appropriate for Hyper-V vsock networking with TUN mode and IPv4 auto configuration.


27-35: LGTM! Unit file generation is clean and correct.

The function properly constructs a systemd unit with:

  • Dependency on NetworkManager.service
  • gvforwarder execution with the provided port
  • NetworkManager connection activation via ExecStartPost
  • Proper installation target
vendor/github.com/containers/podman/v5/pkg/machine/hyperv/stubber.go (3)

80-82: Verify the UserModeNetworking logic change.

The logic changed from always disabling UserModeNetworking when CloudInit is present to a conditional: !mc.CloudInit || opts.UserModeNetworking. This means:

  • When CloudInit is false (ignition mode): UserModeNetworking is true (default)
  • When CloudInit is true: UserModeNetworking follows opts.UserModeNetworking

Ensure this behavior aligns with expectations for both ignition and cloud-init workflows, especially regarding backward compatibility.


96-103: LGTM! Cloud-init ISO generation properly integrated.

The ISO generation is correctly positioned after networking setup and properly assigns the DVDDiskPath. Error handling is appropriate.


27-27: LGTM! Refactoring to hutil package improves code organization.

Moving the network unit creation and vsock connection configuration to the centralized hutil package eliminates duplication and improves maintainability.

Also applies to: 123-123, 141-141

vendor/github.com/containers/podman/v5/pkg/machine/cloudinit/cloudinit.go (4)

23-29: LGTM! WriteFile struct aligns with cloud-init standards.

The structure properly represents cloud-init's write_files directive with appropriate field tags.


31-36: LGTM! UserData expansion enables richer VM customization.

The expanded UserData structure now supports:

  • File writing (write_files)
  • Command execution (runcmd)
  • Filesystem mounts (mounts)

This aligns with cloud-init's standard schema and enables more sophisticated VM initialization.


38-41: LGTM! EmbeddedResource supports binary resource inclusion.

The structure appropriately represents resources (like binaries) that need to be embedded into the cloud-init ISO.


125-132: LGTM! Resource embedding properly integrated into ISO generation.

The code correctly retrieves and embeds platform-specific resources (like gvforwarder for Windows) into the ISO alongside the cloud-init configuration files.

vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_unsupported.go (1)

1-13: LGTM! Appropriate platform stub implementation.

The stub correctly returns an error for unsupported platforms. The build tags ensure this implementation is only used on non-Linux, non-remote builds where secure symlink opening is not supported.

Comment on lines +11 to +18
// openSymlinkPath opens the path under root using securejoin.OpenatInRoot().
func openSymlinkPath(root *os.File, unsafePath string, flags int) (*os.File, error) {
file, err := securejoin.OpenatInRoot(root, unsafePath)
if err != nil {
return nil, err
}
return securejoin.Reopen(file, flags)
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify resource handling in securejoin.Reopen.

The implementation opens a file with OpenatInRoot and then reopens it with different flags using Reopen. If Reopen fails, verify that the intermediate file descriptor from OpenatInRoot is properly closed to avoid a resource leak.

Check the securejoin library documentation to confirm whether Reopen closes the input file on error:


🌐 Web query:

Does securejoin.Reopen close the input file descriptor on error in Go?

💡 Result:

No. securejoin.Reopen does not close the input *os.File on error — it opens a new descriptor (via /proc/self/fd) and returns it or an error; the original handle is not closed by Reopen, so the caller must close it if desired. [1][2]

Sources:
[1] package docs for github.com/cyphar/filepath-securejoin (Reopen description).
[2] securejoin open_linux.go implementation showing Reopen uses handle.Fd()/Name() and returns a new *os.File (does not close the input).


Close the original file descriptor after Reopen to prevent resource leak.

The function has a resource leak: OpenatInRoot opens a file, then Reopen creates a new descriptor from it. Since Reopen does not close the original file, the descriptor from OpenatInRoot is never closed—whether Reopen succeeds or fails.

Fix by explicitly closing the original file after Reopen completes. If Reopen succeeds, close before returning the new descriptor. If it fails, close before returning the error.

🤖 Prompt for AI Agents
In vendor/github.com/containers/podman/v5/pkg/domain/infra/abi/play_linux.go
around lines 11 to 18, the file descriptor returned by securejoin.OpenatInRoot
is never closed after calling securejoin.Reopen, leaking the original
descriptor; modify the function so that after calling securejoin.Reopen you
always close the original file: if Reopen returns an error, close the original
file before returning the error; if Reopen succeeds, close the original file
prior to returning the new file descriptor (ensuring errors from Close are
handled/logged as appropriate).

Comment on lines +57 to +66
userData.RunCmd = []string{
"install -o root -g root -m 0755 /mnt/gvforwarder /usr/local/bin/gvforwarder",
"nmcli connection reload",
"systemctl daemon-reload",
"systemctl enable --now vsock-network.service",
}

userData.Mounts = [][]string{
{"/dev/sr0", "/mnt", "iso9660", "defaults,ro", "0", "0"},
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Do not schedule install /mnt/gvforwarder when the binary is missing.

GetEmbeddedResources() logs the download failure and returns an empty slice, which means the ISO is created without gvforwarder. However, RunCmd here still unconditionally runs install -o root -g root -m 0755 /mnt/gvforwarder .... On any network hiccup, offline environment, or GitHub API outage, /mnt/gvforwarder will not exist and this command will fail, aborting the cloud-init sequence and leaving user-mode networking broken. We need to either propagate the download error so VM creation aborts earlier, or gate the RunCmd setup on the resource being present (and skip the vsock setup if it isn't). As written this is a deterministic failure path in common environments without outbound GitHub access.

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.

VM readiness check fails on WSL mirrored networking

1 participant