Skip to content

Commit 490e96e

Browse files
author
Shreyansh Sancheti
committed
hcs: add function variables for compute API calls and unit tests
Replace direct vmcompute.HcsXxx() calls in system.go and process.go with package-level function variables (hcsXxx) defined in zsyscall_compute.go. The variables default to the vmcompute functions. Tests swap individual variables to intercept specific operations. When upstream migrated System.Start to the operation-based computecore API (HcsCreateOperation + HcsStartComputeSystem + HcsWaitForOperationResult + HcsCloseOperation), the same wrapper pattern is extended to those four calls so the Start path remains testable without standing up real HCS. Tests are in package hcs (internal) so they access the function vars directly - no wrapper layer needed. 12 tests covering: Start (operation API): success, already-closed handle, CreateOperation failure, StartComputeSystem failure, wait returns ErrUnexpectedContainerExit (VM crash at boot), wait returns ErrUnexpectedProcessAbort (HCS service disconnect), wait returns ERROR_TIMEOUT. Pause: system exit during pending Pause. waitBackground: normal vs unexpected exit classification. Wait: multi-goroutine fan-out. Callback: late notification after unregistration is a no-op. Signed-off-by: Shreyansh Sancheti <shsancheti@microsoft.com>
1 parent b1fb76e commit 490e96e

5 files changed

Lines changed: 450 additions & 31 deletions

File tree

internal/hcs/export_test.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//go:build windows
2+
3+
package hcs
4+
5+
import (
6+
"context"
7+
8+
"github.com/Microsoft/hcsshim/internal/vmcompute"
9+
)
10+
11+
// fireNotificationForTest simulates an HCS notification callback for a given
12+
// callback number. Used to drive async completion paths in tests.
13+
func fireNotificationForTest(callbackNumber uintptr, notification hcsNotification, result error) {
14+
callbackMapLock.RLock()
15+
ctx := callbackMap[callbackNumber]
16+
callbackMapLock.RUnlock()
17+
if ctx == nil {
18+
return
19+
}
20+
if ch, ok := ctx.channels[notification]; ok {
21+
ch <- result
22+
}
23+
}
24+
25+
func newTestSystemWithHandle(id string, handle uintptr) *System {
26+
s := newSystem(id)
27+
s.handle = vmcompute.HcsSystem(handle)
28+
return s
29+
}
30+
31+
func registerCallbackForTest(s *System) error {
32+
return s.registerCallback(context.Background())
33+
}
34+
35+
func startWaitBackgroundForTest(s *System) {
36+
go s.waitBackground()
37+
}

internal/hcs/process.go

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func (process *Process) Signal(ctx context.Context, options interface{}) (bool,
106106
return false, err
107107
}
108108

109-
resultJSON, err := vmcompute.HcsSignalProcess(ctx, process.handle, string(optionsb))
109+
resultJSON, err := hcsSignalProcess(ctx, process.handle, string(optionsb))
110110
events := processHcsResult(ctx, resultJSON)
111111
delivered, err := process.processSignalResult(ctx, err)
112112
if err != nil {
@@ -171,7 +171,7 @@ func (process *Process) Kill(ctx context.Context) (bool, error) {
171171
}
172172
defer newProcessHandle.Close()
173173

174-
resultJSON, err := vmcompute.HcsTerminateProcess(ctx, newProcessHandle.handle)
174+
resultJSON, err := hcsTerminateProcess(ctx, newProcessHandle.handle)
175175
if err != nil {
176176
// We still need to check these two cases, as processes may still be killed by an
177177
// external actor (human operator, OOM, random script etc).
@@ -234,7 +234,7 @@ func (process *Process) waitBackground() {
234234

235235
// Make sure we didn't race with Close() here
236236
if process.handle != 0 {
237-
propertiesJSON, resultJSON, err = vmcompute.HcsGetProcessProperties(ctx, process.handle)
237+
propertiesJSON, resultJSON, err = hcsGetProcessProperties(ctx, process.handle)
238238
events := processHcsResult(ctx, resultJSON)
239239
if err != nil {
240240
err = makeProcessError(process, operation, err, events)
@@ -303,7 +303,7 @@ func (process *Process) ResizeConsole(ctx context.Context, width, height uint16)
303303
return err
304304
}
305305

306-
resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb))
306+
resultJSON, err := hcsModifyProcess(ctx, process.handle, string(modifyRequestb))
307307
events := processHcsResult(ctx, resultJSON)
308308
if err != nil {
309309
return makeProcessError(process, operation, err, events)
@@ -352,7 +352,7 @@ func (process *Process) StdioLegacy() (_ io.WriteCloser, _ io.ReadCloser, _ io.R
352352
return stdin, stdout, stderr, nil
353353
}
354354

355-
processInfo, resultJSON, err := vmcompute.HcsGetProcessInfo(ctx, process.handle)
355+
processInfo, resultJSON, err := hcsGetProcessInfo(ctx, process.handle)
356356
events := processHcsResult(ctx, resultJSON)
357357
if err != nil {
358358
return nil, nil, nil, makeProcessError(process, operation, err, events)
@@ -406,7 +406,7 @@ func (process *Process) CloseStdin(ctx context.Context) (err error) {
406406
return err
407407
}
408408

409-
resultJSON, err := vmcompute.HcsModifyProcess(ctx, process.handle, string(modifyRequestb))
409+
resultJSON, err := hcsModifyProcess(ctx, process.handle, string(modifyRequestb))
410410
events := processHcsResult(ctx, resultJSON)
411411
if err != nil {
412412
return makeProcessError(process, operation, err, events)
@@ -509,7 +509,7 @@ func (process *Process) Close() (err error) {
509509
return makeProcessError(process, operation, err, nil)
510510
}
511511

512-
if err = vmcompute.HcsCloseProcess(ctx, process.handle); err != nil {
512+
if err = hcsCloseProcess(ctx, process.handle); err != nil {
513513
return makeProcessError(process, operation, err, nil)
514514
}
515515

@@ -536,7 +536,7 @@ func (process *Process) registerCallback(ctx context.Context) error {
536536
callbackMap[callbackNumber] = callbackContext
537537
callbackMapLock.Unlock()
538538

539-
callbackHandle, err := vmcompute.HcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber)
539+
callbackHandle, err := hcsRegisterProcessCallback(ctx, process.handle, notificationWatcherCallback, callbackNumber)
540540
if err != nil {
541541
return err
542542
}
@@ -563,9 +563,10 @@ func (process *Process) unregisterCallback(ctx context.Context) error {
563563
return nil
564564
}
565565

566-
// vmcompute.HcsUnregisterProcessCallback has its own synchronization to
567-
// wait for all callbacks to complete. We must NOT hold the callbackMapLock.
568-
err := vmcompute.HcsUnregisterProcessCallback(ctx, handle)
566+
// The underlying HCS API (HcsUnregisterProcessCallback) has its own
567+
// synchronization to wait for all in-flight callbacks to complete.
568+
// We must NOT hold the callbackMapLock during this call.
569+
err := hcsUnregisterProcessCallback(ctx, handle)
569570
if err != nil {
570571
return err
571572
}

internal/hcs/system.go

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ func CreateComputeSystem(ctx context.Context, id string, hcsDocumentInterface in
9191
resultJSON string
9292
createError error
9393
)
94-
computeSystem.handle, resultJSON, createError = vmcompute.HcsCreateComputeSystem(ctx, id, hcsDocument, identity)
94+
computeSystem.handle, resultJSON, createError = hcsCreateComputeSystem(ctx, id, hcsDocument, identity)
9595
if createError == nil || IsPending(createError) {
9696
defer func() {
9797
if err != nil {
@@ -128,7 +128,7 @@ func OpenComputeSystem(ctx context.Context, id string) (*System, error) {
128128
operation := "hcs::OpenComputeSystem"
129129

130130
computeSystem := newSystem(id)
131-
handle, resultJSON, err := vmcompute.HcsOpenComputeSystem(ctx, id)
131+
handle, resultJSON, err := hcsOpenComputeSystem(ctx, id)
132132
events := processHcsResult(ctx, resultJSON)
133133
if err != nil {
134134
return nil, makeSystemError(computeSystem, operation, err, events)
@@ -185,7 +185,7 @@ func GetComputeSystems(ctx context.Context, q schema1.ComputeSystemQuery) ([]sch
185185
return nil, err
186186
}
187187

188-
computeSystemsJSON, resultJSON, err := vmcompute.HcsEnumerateComputeSystems(ctx, string(queryb))
188+
computeSystemsJSON, resultJSON, err := hcsEnumerateComputeSystems(ctx, string(queryb))
189189
events := processHcsResult(ctx, resultJSON)
190190
if err != nil {
191191
return nil, &HcsError{Op: operation, Err: err, Events: events}
@@ -211,11 +211,11 @@ func (computeSystem *System) Start(ctx context.Context) error {
211211
return makeSystemError(computeSystem, "hcs::System::Start", ErrAlreadyClosed, nil)
212212
}
213213

214-
op, err := computecore.HcsCreateOperation(ctx, 0, 0)
214+
op, err := hcsCreateOperation(ctx, 0, 0)
215215
if err != nil {
216216
return makeSystemError(computeSystem, "hcs::System::Start", err, nil)
217217
}
218-
defer computecore.HcsCloseOperation(ctx, op)
218+
defer hcsCloseOperation(ctx, op)
219219

220220
return computeSystem.start(ctx, op, "")
221221
}
@@ -236,7 +236,7 @@ func (computeSystem *System) start(ctx context.Context, op computecore.HcsOperat
236236
defer func() { oc.SetSpanStatus(span, err) }()
237237
span.AddAttributes(trace.StringAttribute("cid", computeSystem.id))
238238

239-
if err := computecore.HcsStartComputeSystem(
239+
if err := hcsStartComputeSystem(
240240
ctx,
241241
computecore.HcsSystem(computeSystem.handle),
242242
op,
@@ -245,7 +245,7 @@ func (computeSystem *System) start(ctx context.Context, op computecore.HcsOperat
245245
return makeSystemError(computeSystem, operation, err, nil)
246246
}
247247

248-
if _, err := computecore.HcsWaitForOperationResult(ctx, op, 0xFFFFFFFF); err != nil {
248+
if _, err := hcsWaitForOperationResult(ctx, op, 0xFFFFFFFF); err != nil {
249249
return makeSystemError(computeSystem, operation, err, nil)
250250
}
251251

@@ -269,7 +269,7 @@ func (computeSystem *System) Shutdown(ctx context.Context) error {
269269
return nil
270270
}
271271

272-
resultJSON, err := vmcompute.HcsShutdownComputeSystem(ctx, computeSystem.handle, "")
272+
resultJSON, err := hcsShutdownComputeSystem(ctx, computeSystem.handle, "")
273273
events := processHcsResult(ctx, resultJSON)
274274
if err != nil &&
275275
!errors.Is(err, ErrVmcomputeAlreadyStopped) &&
@@ -291,7 +291,7 @@ func (computeSystem *System) Terminate(ctx context.Context) error {
291291
return nil
292292
}
293293

294-
resultJSON, err := vmcompute.HcsTerminateComputeSystem(ctx, computeSystem.handle, "")
294+
resultJSON, err := hcsTerminateComputeSystem(ctx, computeSystem.handle, "")
295295
events := processHcsResult(ctx, resultJSON)
296296
if err != nil &&
297297
!errors.Is(err, ErrVmcomputeAlreadyStopped) &&
@@ -394,7 +394,7 @@ func (computeSystem *System) Properties(ctx context.Context, types ...schema1.Pr
394394
return nil, makeSystemError(computeSystem, operation, err, nil)
395395
}
396396

397-
propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
397+
propertiesJSON, resultJSON, err := hcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
398398
events := processHcsResult(ctx, resultJSON)
399399
if err != nil {
400400
return nil, makeSystemError(computeSystem, operation, err, events)
@@ -535,7 +535,7 @@ func (computeSystem *System) hcsPropertiesV2Query(ctx context.Context, types []h
535535
return nil, makeSystemError(computeSystem, operation, err, nil)
536536
}
537537

538-
propertiesJSON, resultJSON, err := vmcompute.HcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
538+
propertiesJSON, resultJSON, err := hcsGetComputeSystemProperties(ctx, computeSystem.handle, string(queryBytes))
539539
events := processHcsResult(ctx, resultJSON)
540540
if err != nil {
541541
return nil, makeSystemError(computeSystem, operation, err, events)
@@ -672,7 +672,7 @@ func (computeSystem *System) Pause(ctx context.Context) (err error) {
672672
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil)
673673
}
674674

675-
resultJSON, err := vmcompute.HcsPauseComputeSystem(ctx, computeSystem.handle, "")
675+
resultJSON, err := hcsPauseComputeSystem(ctx, computeSystem.handle, "")
676676
events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber,
677677
hcsNotificationSystemPauseCompleted, &timeout.SystemPause)
678678
if err != nil {
@@ -700,7 +700,7 @@ func (computeSystem *System) Resume(ctx context.Context) (err error) {
700700
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil)
701701
}
702702

703-
resultJSON, err := vmcompute.HcsResumeComputeSystem(ctx, computeSystem.handle, "")
703+
resultJSON, err := hcsResumeComputeSystem(ctx, computeSystem.handle, "")
704704
events, err := processAsyncHcsResult(ctx, err, resultJSON, computeSystem.callbackNumber,
705705
hcsNotificationSystemResumeCompleted, &timeout.SystemResume)
706706
if err != nil {
@@ -733,7 +733,7 @@ func (computeSystem *System) Save(ctx context.Context, options interface{}) (err
733733
return makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil)
734734
}
735735

736-
result, err := vmcompute.HcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions))
736+
result, err := hcsSaveComputeSystem(ctx, computeSystem.handle, string(saveOptions))
737737
events, err := processAsyncHcsResult(ctx, err, result, computeSystem.callbackNumber,
738738
hcsNotificationSystemSaveCompleted, &timeout.SystemSave)
739739
if err != nil {
@@ -757,7 +757,7 @@ func (computeSystem *System) createProcess(ctx context.Context, operation string
757757
}
758758

759759
configuration := string(configurationb)
760-
processInfo, processHandle, resultJSON, err := vmcompute.HcsCreateProcess(ctx, computeSystem.handle, configuration)
760+
processInfo, processHandle, resultJSON, err := hcsCreateProcess(ctx, computeSystem.handle, configuration)
761761
events := processHcsResult(ctx, resultJSON)
762762
if err != nil {
763763
if v2, ok := c.(*hcsschema.ProcessParameters); ok {
@@ -813,7 +813,7 @@ func (computeSystem *System) OpenProcess(ctx context.Context, pid int) (*Process
813813
return nil, makeSystemError(computeSystem, operation, ErrAlreadyClosed, nil)
814814
}
815815

816-
processHandle, resultJSON, err := vmcompute.HcsOpenProcess(ctx, computeSystem.handle, uint32(pid))
816+
processHandle, resultJSON, err := hcsOpenProcess(ctx, computeSystem.handle, uint32(pid))
817817
events := processHcsResult(ctx, resultJSON)
818818
if err != nil {
819819
return nil, makeSystemError(computeSystem, operation, err, events)
@@ -856,7 +856,7 @@ func (computeSystem *System) CloseCtx(ctx context.Context) (err error) {
856856
return makeSystemError(computeSystem, operation, err, nil)
857857
}
858858

859-
err = vmcompute.HcsCloseComputeSystem(ctx, computeSystem.handle)
859+
err = hcsCloseComputeSystem(ctx, computeSystem.handle)
860860
if err != nil {
861861
return makeSystemError(computeSystem, operation, err, nil)
862862
}
@@ -885,7 +885,7 @@ func (computeSystem *System) registerCallback(ctx context.Context) error {
885885
callbackMap[callbackNumber] = callbackContext
886886
callbackMapLock.Unlock()
887887

888-
callbackHandle, err := vmcompute.HcsRegisterComputeSystemCallback(ctx, computeSystem.handle,
888+
callbackHandle, err := hcsRegisterComputeSystemCallback(ctx, computeSystem.handle,
889889
notificationWatcherCallback, callbackNumber)
890890
if err != nil {
891891
return err
@@ -915,7 +915,7 @@ func (computeSystem *System) unregisterCallback(ctx context.Context) error {
915915

916916
// hcsUnregisterComputeSystemCallback has its own synchronization
917917
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
918-
err := vmcompute.HcsUnregisterComputeSystemCallback(ctx, handle)
918+
err := hcsUnregisterComputeSystemCallback(ctx, handle)
919919
if err != nil {
920920
return err
921921
}
@@ -948,7 +948,7 @@ func (computeSystem *System) Modify(ctx context.Context, config interface{}) err
948948
}
949949

950950
requestJSON := string(requestBytes)
951-
resultJSON, err := vmcompute.HcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON)
951+
resultJSON, err := hcsModifyComputeSystem(ctx, computeSystem.handle, requestJSON)
952952
events := processHcsResult(ctx, resultJSON)
953953
if err != nil {
954954
return makeSystemError(computeSystem, operation, err, events)

0 commit comments

Comments
 (0)