Skip to content

guest: use OCIBundlePath as sandbox root source of truth#2653

Open
shreyanshjain7174 wants to merge 1 commit intomicrosoft:mainfrom
shreyanshjain7174:sandbox-path-refactor
Open

guest: use OCIBundlePath as sandbox root source of truth#2653
shreyanshjain7174 wants to merge 1 commit intomicrosoft:mainfrom
shreyanshjain7174:sandbox-path-refactor

Conversation

@shreyanshjain7174
Copy link
Copy Markdown
Contributor

@shreyanshjain7174 shreyanshjain7174 commented Mar 31, 2026

Guest-side GCS currently derives sandbox paths by combining a hard-coded prefix (/run/gcs/c) with the sandbox ID, ignoring the OCIBundlePath the host already sends. This breaks with Shim v2 path layouts and multi-pod UVMs.

This PR stores settings.OCIBundlePath as the canonical sandbox root on the Host struct (sandboxRoots map), and threads the resolved root through all setup and cleanup functions instead of re-deriving it.

For virtual pods, the sandbox root is derived from OCIBundlePath's parent directory (preserving the virtual-pods/ layout). For workload containers, it's looked up from the mapping. A legacy fallback ensures backwards compatibility with shim v1.

When the old shim sends OCIBundlePath in the legacy format, all paths are identical to before — verified by parity tests and a full CRI lifecycle (crictl runp/create/start/stop/rm) on a Hyper-V UVM.

Depends on: #2655 (bug fix for leaked LCOW resources) — both PRs modify uvm.go cleanup paths. Will rebase after #2655 merges.

@shreyanshjain7174 shreyanshjain7174 requested a review from a team as a code owner March 31, 2026 12:55
@shreyanshjain7174 shreyanshjain7174 force-pushed the sandbox-path-refactor branch 2 times, most recently from bbc4d4d to 7deb168 Compare March 31, 2026 13:05
@rawahars rawahars requested a review from helsaawy March 31, 2026 20:28
Copy link
Copy Markdown
Contributor

@rawahars rawahars left a comment

Choose a reason for hiding this comment

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

Overall love the simplification of the virtual pod vs normal pod differentiation.

Major comments are regarding keeping existing utility methods, while removing virtual pod specific ones and then changing these methods to return paths/values which you are creating on the fly. That would make it easier to review too.

@rawahars
Copy link
Copy Markdown
Contributor

@anmaxvl Can you please take a look too to see if any invariant of Security Policy gets impacted with this change. As I see it that should not change but please do confirm that.

@anmaxvl
Copy link
Copy Markdown
Contributor

anmaxvl commented Apr 1, 2026

@micromaomao as well.

@micromaomao
Copy link
Copy Markdown
Member

@anmaxvl:

@micromaomao as well.

I will have a closer look tomorrow, but I think this is probably good because #2581 added validation to settings.OCIBundlePath (not in this PR's branch yet but in main) to force it to be the expected value.

@shreyanshjain7174 shreyanshjain7174 force-pushed the sandbox-path-refactor branch 2 times, most recently from c7fcebd to e1f8a6f Compare April 1, 2026 06:41
@shreyanshjain7174
Copy link
Copy Markdown
Contributor Author

@rawahars Addressed all 8 review comments. Also added scripts/validate-sandbox-paths.ps1 — a portable E2E validation script that exercises real CRI pod lifecycles via crictl and validates that /etc/hostname, /etc/hosts, and /etc/resolv.conf are correctly mounted from the sandbox root. It tests single-pod, sandbox+workload, and sequential multi-pod scenarios. All 13 checks pass locally.

Do you think this script is worth keeping in the repo for manual validation, or should it go in a separate PR / be dropped from this one?

Copy link
Copy Markdown
Contributor

@rawahars rawahars left a comment

Choose a reason for hiding this comment

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

Major driving comment are that for the current change, try to align as close to original code structure as possible.

@shreyanshjain7174
Copy link
Copy Markdown
Contributor Author

Thanks for the detailed round 2 review. Pushed an update addressing all the comments:

  • Restored section comments in Container.Delete (remove user mounts, tmpfs mounts, hugepages)
  • Added utility methods back: getSandboxHostnamePath/HostsPath/ResolvPath in sandbox_container.go, getStandaloneHostnamePath/HostsPath/ResolvPath in standalone_container.go
  • Restored original setup function names: setupSandboxMountsPath, setupSandboxTmpfsMountsPath, etc.
  • Moved the root-based path helpers (SandboxMountsDirFromRoot, SandboxTmpfsMountsDirFromRoot, etc.) into spec.go alongside the existing ID-based methods
  • Moved GenerateWorkloadContainerNetworkMountsFromRoot into spec.go as well, removed the local inline version
  • Added whitespace and comments in registerSandboxRoot for readability
  • Kept the VirtualPod* methods in spec.go for now since GenerateWorkloadContainerNetworkMounts (used by securitypolicy) still depends on VirtualPodAwareSandboxRootDir. Happy to remove them in a follow-up once we wire that caller through the root-based path too.

All tests pass locally (unit + E2E via crictl).

@shreyanshjain7174 shreyanshjain7174 force-pushed the sandbox-path-refactor branch 2 times, most recently from 08c0b9e to 7c4f900 Compare April 1, 2026 16:49
@shreyanshjain7174
Copy link
Copy Markdown
Contributor Author

Note: this PR has a merge conflict with #2655 (bug fix for leaked LCOW resources) in uvm.go — specifically around RemoveContainer and the virtual pod cleanup paths. Once #2655 merges, I'll rebase on top of it.

Copy link
Copy Markdown
Contributor

@helsaawy helsaawy left a comment

Choose a reason for hiding this comment

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

Can we switch entirely to the *FromRoot functions and delete the [VirtualPodAware]*MountsDir/Source functions in internal/guest/spec?

}

// remove user mounts in sandbox container - use virtual pod aware paths
if err := storage.UnmountAllInPath(ctx, specGuest.VirtualPodAwareSandboxMountsDir(c.id, virtualSandboxID), true); err != nil {
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.

Are we planning on removing VirtualPodAwareSandboxMountsDir and other utility methods in next PR?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, the follow-up PR (guest-pod-unification branch) removes VirtualPodAwareSandboxMountsDir and the other VirtualPod* methods as part of the unified pod model.

// Set cgroup path
virtualSandboxID := spec.Annotations[annotations.VirtualPodID]
if virtualSandboxID != "" {
// Standalone container in virtual pod goes under /containers/virtual-pods/{virtualSandboxID}/{containerID}
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.

nit: Consider adding this comment in next PR when you do the refactoring.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Will add in the follow-up.

// sandboxRoots maps sandboxID to the resolved sandbox root directory.
// Populated via RegisterSandboxRoot during sandbox creation using
// the host-provided OCIBundlePath as source of truth.
sandboxRootsMutex sync.RWMutex
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.

nit: Why not just use the containersMutex? If sandboxRoots is always used under containersMutex then you can skip sandboxRootsMutex.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point — sandboxRoots is only accessed within CreateContainer and RemoveContainer which both hold containersMutex. I'll consolidate to use containersMutex only in the follow-up to keep this PR minimal.

Copy link
Copy Markdown
Contributor

@helsaawy helsaawy left a comment

Choose a reason for hiding this comment

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

Unsure about the container root path change, since it will break functional tests (e.g., TestLCOW_TeeLogPath), and probably several of our CRI integration tests too

// LCOWRootPrefixInUVM is the path inside UVM where LCOW container's root
// file system will be mounted
LCOWRootPrefixInUVM = "/run/gcs/c"
LCOWRootPrefixInUVM = "/run/gcs/pods"
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.

this will probably break a lot of our functional and cri tests that hardcode the /run/gcs/c as a container's bundle prefix.

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.

+1

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good catch — reverted. LCOWRootPrefixInUVM stays as /run/gcs/c and hcsoci/create.go is unchanged from main. This PR now only touches the guest side: the GCS uses whatever OCIBundlePath the shim sends, making it path-agnostic. No shim-side changes needed.

Comment on lines 75 to 77
func getLogDir(bundlePath string) string {
return bundlePath
}
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.

can we just delete this? its more confusing to keep it since its basically the identity function

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done — deleted getLogDir entirely. makeLogDir and getLogPath now use bundlePath directly.

Refactor the guest runtime to derive all sandbox paths from the
host-provided OCIBundlePath rather than reconstructing them from
hardcoded prefixes. This makes the GCS path-agnostic: it works with
any OCIBundlePath the shim sends, regardless of prefix.

Key changes:
- runc.NewRuntime() no longer takes a base log path; runc.log is
  co-located with each container's bundlePath
- Host.sandboxRoots map tracks the resolved sandbox root per container
- CreateContainer registers sandbox roots; RemoveContainer cleans up
- Sandbox, standalone, and workload container setup functions accept
  a resolved sandboxRoot parameter
- New *FromRoot helpers in spec.go for sandbox subdirectory paths
- runtime.Runtime.CreateContainer now takes sandboxID parameter

Signed-off-by: Shreyansh Jain <shreyanshjain7174@gmail.com>
Signed-off-by: Shreyansh Sancheti <shsancheti@microsoft.com>
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.

6 participants