Skip to content

Commit 4916a30

Browse files
committed
review comments 2
Signed-off-by: Harsh Rawat <harshrawat@microsoft.com>
1 parent de2dcdf commit 4916a30

7 files changed

Lines changed: 70 additions & 108 deletions

File tree

cmd/containerd-shim-runhcs-v1/pod.go

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -482,27 +482,36 @@ func (p *pod) DeleteTask(ctx context.Context, tid string) error {
482482
return nil
483483
}
484484

485+
// updateConfigForHostProcessContainer validates and adjusts a container spec
486+
// against the pod sandbox spec for HostProcess (HPC) scenarios:
487+
// - Rejects a hypervisor-isolated HPC inside a non-HPC sandbox.
488+
// - Rejects a non-HPC container inside a process-isolated HPC sandbox
489+
// (all containers in such a pod must share the host job/network).
490+
// - Propagates HPC pod-level annotations (e.g. inherit-user, rootfs
491+
// location) onto each container, since CRI only delivers them at pod
492+
// create time.
493+
// - Forces HostProcessInheritUser=false on a non-privileged container in a
494+
// privileged hypervisor-isolated sandbox so it can't inherit SYSTEM.
485495
func (p *pod) updateConfigForHostProcessContainer(s *specs.Spec) error {
486-
if !oci.IsIsolatedJobContainer(p.spec) && oci.IsIsolatedJobContainer(s) {
496+
isProcessIsolatedPrivilegedSandbox := oci.IsJobContainer(p.spec) && !oci.IsIsolated(p.spec)
497+
isHypervisorIsolatedPrivilegedSandbox := oci.IsJobContainer(p.spec) && oci.IsIsolated(p.spec)
498+
isProcessIsolatedPrivilegedContainer := oci.IsJobContainer(s) && !oci.IsIsolated(s)
499+
isHypervisorIsolatedPrivilegedContainer := oci.IsJobContainer(s) && oci.IsIsolated(s)
500+
501+
if !isHypervisorIsolatedPrivilegedSandbox && isHypervisorIsolatedPrivilegedContainer {
487502
return errors.New("cannot create a host process container inside sandbox which has missing annotation: microsoft.com/hostprocess-container")
488503
}
489504

490-
isProcessIsolatedPrivilegedSandbox := oci.IsJobContainer(p.spec)
491-
isHypervisorIsolatedPrivilegedSandbox := oci.IsIsolatedJobContainer(p.spec)
492-
isProcessIsolatedPrivilegedContainer := oci.IsJobContainer(s)
493-
isHypervisorIsolatedPrivilegedContainer := oci.IsIsolatedJobContainer(s)
494-
495505
if isProcessIsolatedPrivilegedSandbox || (isHypervisorIsolatedPrivilegedSandbox && isHypervisorIsolatedPrivilegedContainer) {
496506
if isProcessIsolatedPrivilegedSandbox && !isProcessIsolatedPrivilegedContainer {
497507
// This is a short circuit to make sure that all containers in a pod will have
498508
// the same IP address/be added to the same compartment.
499509
//
500510
// There will need to be OS work needed to support this scenario, so for now we need to block on
501511
// this.
502-
503-
// IsJobContainer returns true if there was no hypervisor isolation and request was for HPCs.
504-
// Therefore, in this request we check if the pod was a process isolated HPC pod but the
505-
// container spec is not job container spec.
512+
//
513+
// If the pod sandbox is a process-isolated HPC then the container must also be a
514+
// process-isolated HPC.
506515
return errors.New("cannot create a normal process isolated container if the pod sandbox is a job container running on host")
507516
}
508517

cmd/containerd-shim-runhcs-v1/task_hcs.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ func createContainer(
151151

152152
// For Job Container which runs on host, we create the same using shim logic.
153153
// If the request was for job container within UVM, then the request is passed along to GCS.
154-
if oci.IsJobContainer(s) {
154+
if oci.IsJobContainer(s) && !oci.IsIsolated(s) {
155155
opts := jobcontainers.CreateOptions{WCOWLayers: wcowLayers}
156156
container, resources, err = jobcontainers.Create(ctx, id, s, opts)
157157
if err != nil {
@@ -216,7 +216,7 @@ func newHcsTask(
216216

217217
// For Isolated Job containers, we need special handling wherein if the command line
218218
// is not specified, then we add 'cmd /C' by default.
219-
if oci.IsIsolatedJobContainer(s) {
219+
if oci.IsJobContainer(s) && oci.IsIsolated(s) {
220220
handleProcessArgsForIsolatedJobContainer(s, s.Process)
221221
}
222222

@@ -372,7 +372,7 @@ func (ht *hcsTask) CreateExec(ctx context.Context, req *task.ExecProcessRequest,
372372
return errors.Wrapf(errdefs.ErrFailedPrecondition, "exec: '' in task: '%s' must be running to create additional execs", ht.id)
373373
}
374374

375-
if oci.IsIsolatedJobContainer(ht.taskSpec) {
375+
if oci.IsJobContainer(ht.taskSpec) && oci.IsIsolated(ht.taskSpec) {
376376
handleProcessArgsForIsolatedJobContainer(ht.taskSpec, spec)
377377
}
378378

@@ -1027,6 +1027,23 @@ func (ht *hcsTask) requestAddContainerMount(ctx context.Context, resourcePath st
10271027
return ht.c.Modify(ctx, modification)
10281028
}
10291029

1030+
// handleProcessArgsForIsolatedJobContainer adjusts the process spec for a
1031+
// HostProcess container running inside a hypervisor-isolated UVM.
1032+
//
1033+
// Why the "cmd /c" wrap:
1034+
// - For a regular WCOW container, the guest creates the workload process
1035+
// from within the container silo, so the silo's filesystem view (including
1036+
// the bind-mounted container rootfs) is in scope for path resolution.
1037+
// - For a HostProcess container the guest creates the process from outside
1038+
// the silo and only attaches it to the silo's job object after creation.
1039+
// Executable-name resolution therefore uses the launcher's view (System32,
1040+
// Windows, PATH) and cannot see binaries that live only inside the
1041+
// container rootfs, nor resolve shell builtins like `echo`, `dir`, `set`,
1042+
// redirection, etc. Anything not present as a real .exe on the standard
1043+
// search path would fail with ERROR_FILE_NOT_FOUND.
1044+
// - cmd.exe always exists under System32 (so the launcher can find it) and,
1045+
// once joined to the silo's job, runs inside the container's filesystem
1046+
// view where it can resolve builtins and any rootfs-relative binary.
10301047
func handleProcessArgsForIsolatedJobContainer(taskSpec *specs.Spec, specs *specs.Process) {
10311048
if specs.CommandLine != "" && !strings.HasPrefix(strings.TrimSpace(strings.ToLower(specs.CommandLine)), "cmd") {
10321049
specs.CommandLine = fmt.Sprintf("cmd /c %s", specs.CommandLine)

internal/hcs/schema2/container.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,19 @@
99

1010
package hcsschema
1111

12+
// IsolationType describes the isolation mode of a container.
13+
type IsolationType string
14+
15+
const (
16+
// IsolationTypeProcess is a fully process-isolated, namespace-isolated
17+
// Windows Server container.
18+
IsolationTypeProcess IsolationType = "Process"
19+
20+
// IsolationTypeHostProcess is a privileged (Windows) HostProcess
21+
// container that shares the host namespace.
22+
IsolationTypeHostProcess IsolationType = "HostProcess"
23+
)
24+
1225
type Container struct {
1326
GuestOs *GuestOs `json:"GuestOs,omitempty"`
1427

@@ -34,5 +47,5 @@ type Container struct {
3447

3548
AdditionalDeviceNamespace *ContainerDefinitionDevice `json:"AdditionalDeviceNamespace,omitempty"`
3649

37-
IsolationType string `json:"IsolationType,omitempty"`
50+
IsolationType IsolationType `json:"IsolationType,omitempty"`
3851
}

internal/hcsoci/hcsdoc_wcow.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -501,8 +501,9 @@ func createWindowsContainerDocument(ctx context.Context, coi *createOptionsInter
501501
AddValues: registryAdd,
502502
}
503503

504-
if oci.IsIsolatedJobContainer(coi.Spec) {
505-
v2Container.IsolationType = "HostProcess"
504+
if oci.IsJobContainer(coi.Spec) && oci.IsIsolated(coi.Spec) {
505+
// Set the isolation type to host process.
506+
v2Container.IsolationType = hcsschema.IsolationTypeHostProcess
506507

507508
// If the customer specified a custom rootfs path then use that instead of default c:\hpc.
508509
if customRootFsPath, ok := coi.Spec.Annotations[annotations.HostProcessRootfsLocation]; ok {
@@ -511,6 +512,7 @@ func createWindowsContainerDocument(ctx context.Context, coi *createOptionsInter
511512
return nil, nil, err
512513
}
513514

515+
// Set the path for container root filesystem.
514516
v2Container.Storage.PrivilegedContainerRootPath = customRootFsPathSanitized
515517
}
516518
}

internal/oci/util.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,6 @@ func IsIsolated(s *specs.Spec) bool {
2121
}
2222

2323
// IsJobContainer checks if `s` is asking for a Windows job container.
24-
// This request is for a shim based process isolated HPCs.
2524
func IsJobContainer(s *specs.Spec) bool {
26-
return s.Linux == nil &&
27-
s.Windows != nil &&
28-
s.Windows.HyperV == nil &&
29-
s.Annotations[annotations.HostProcessContainer] == "true"
30-
}
31-
32-
// IsIsolatedJobContainer checks if `s` is asking for a Windows job container.
33-
// This request is for running HPC within guest.
34-
func IsIsolatedJobContainer(s *specs.Spec) bool {
35-
return s.Linux == nil &&
36-
s.Windows != nil &&
37-
s.Windows.HyperV != nil &&
38-
s.Annotations[annotations.HostProcessContainer] == "true"
25+
return IsWCOW(s) && s.Annotations[annotations.HostProcessContainer] == "true"
3926
}

internal/oci/util_test.go

Lines changed: 11 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -188,14 +188,14 @@ func Test_IsJobContainer(t *testing.T) {
188188
expected: false,
189189
},
190190
{
191-
name: "WCOW Hyper-V isolated with HostProcess=true (not a JobContainer)",
191+
name: "WCOW Hyper-V isolated with HostProcess=true",
192192
spec: &specs.Spec{
193193
Windows: &specs.Windows{
194194
HyperV: &specs.WindowsHyperV{},
195195
},
196196
Annotations: map[string]string{annotations.HostProcessContainer: "true"},
197197
},
198-
expected: false,
198+
expected: true,
199199
},
200200
{
201201
name: "LCOW without Windows (not a JobContainer)",
@@ -218,77 +218,11 @@ func Test_IsJobContainer(t *testing.T) {
218218
}
219219

220220
// -----------------------------------------------------------------------------
221-
// IsIsolatedJobContainer tests
222-
// -----------------------------------------------------------------------------
223-
224-
func Test_IsIsolatedJobContainer(t *testing.T) {
225-
tests := []struct {
226-
name string
227-
spec *specs.Spec
228-
expected bool
229-
}{
230-
{
231-
name: "WCOW Hyper-V isolated with HostProcess=true",
232-
spec: &specs.Spec{
233-
Windows: &specs.Windows{
234-
HyperV: &specs.WindowsHyperV{},
235-
},
236-
Annotations: map[string]string{annotations.HostProcessContainer: "true"},
237-
},
238-
expected: true,
239-
},
240-
{
241-
name: "WCOW Hyper-V isolated with HostProcess=false",
242-
spec: &specs.Spec{
243-
Windows: &specs.Windows{
244-
HyperV: &specs.WindowsHyperV{},
245-
},
246-
Annotations: map[string]string{annotations.HostProcessContainer: "false"},
247-
},
248-
expected: false,
249-
},
250-
{
251-
name: "WCOW Hyper-V isolated with HostProcess missing",
252-
spec: &specs.Spec{
253-
Windows: &specs.Windows{
254-
HyperV: &specs.WindowsHyperV{},
255-
},
256-
},
257-
expected: false,
258-
},
259-
{
260-
name: "WCOW process-isolated with HostProcess=true (not isolated job)",
261-
spec: &specs.Spec{
262-
Windows: &specs.Windows{},
263-
Annotations: map[string]string{annotations.HostProcessContainer: "true"},
264-
},
265-
expected: false,
266-
},
267-
{
268-
name: "LCOW without Windows (not an isolated job container)",
269-
spec: &specs.Spec{
270-
Linux: &specs.Linux{},
271-
},
272-
expected: false,
273-
},
274-
}
275-
276-
for _, tt := range tests {
277-
tt := tt
278-
t.Run(tt.name, func(t *testing.T) {
279-
actual := IsIsolatedJobContainer(tt.spec)
280-
if actual != tt.expected {
281-
t.Fatalf("IsIsolatedJobContainer() = %v, expected %v", actual, tt.expected)
282-
}
283-
})
284-
}
285-
}
286-
287-
// -----------------------------------------------------------------------------
288-
// Cross-property tests (consistency / mutual exclusivity)
221+
// Cross-property tests: IsJobContainer combined with IsIsolated to differentiate
222+
// process-isolated HPC from hypervisor-isolated HPC.
289223
// -----------------------------------------------------------------------------
290224

291-
func Test_JobContainer_And_IsolatedJobContainer_MutualExclusion(t *testing.T) {
225+
func Test_JobContainer_IsolationDifferentiation(t *testing.T) {
292226
// Process-isolated WCOW HostProcess=true
293227
processJob := &specs.Spec{
294228
Windows: &specs.Windows{},
@@ -297,8 +231,8 @@ func Test_JobContainer_And_IsolatedJobContainer_MutualExclusion(t *testing.T) {
297231
if !IsJobContainer(processJob) {
298232
t.Fatal("expected IsJobContainer to be true for process-isolated HostProcess=true")
299233
}
300-
if IsIsolatedJobContainer(processJob) {
301-
t.Fatal("expected IsIsolatedJobContainer to be false for process-isolated HostProcess=true")
234+
if IsIsolated(processJob) {
235+
t.Fatal("expected IsIsolated to be false for process-isolated HostProcess=true")
302236
}
303237

304238
// Hyper-V isolated WCOW HostProcess=true
@@ -308,10 +242,10 @@ func Test_JobContainer_And_IsolatedJobContainer_MutualExclusion(t *testing.T) {
308242
},
309243
Annotations: map[string]string{annotations.HostProcessContainer: "true"},
310244
}
311-
if IsJobContainer(hyperVJob) {
312-
t.Fatal("expected IsJobContainer to be false for Hyper-V isolated HostProcess=true")
245+
if !IsJobContainer(hyperVJob) {
246+
t.Fatal("expected IsJobContainer to be true for Hyper-V isolated HostProcess=true")
313247
}
314-
if !IsIsolatedJobContainer(hyperVJob) {
315-
t.Fatal("expected IsIsolatedJobContainer to be true for Hyper-V isolated HostProcess=true")
248+
if !IsIsolated(hyperVJob) {
249+
t.Fatal("expected IsIsolated to be true for Hyper-V isolated HostProcess=true")
316250
}
317251
}

test/internal/container/container.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func Create(
4848
tb.Fatalf("layer parsing failed: %s", err)
4949
}
5050

51-
if oci.IsJobContainer(spec) {
51+
if oci.IsJobContainer(spec) && !oci.IsIsolated(spec) {
5252
c, r, err = jobcontainers.Create(ctx, name, spec, jobcontainers.CreateOptions{WCOWLayers: wcowLayers})
5353
} else {
5454
co := &hcsoci.CreateOptions{

0 commit comments

Comments
 (0)