@ -1,86 +1,56 @@
package hcs
import (
"context"
"encoding/json"
"os"
"strconv "
"err or s"
"strings "
"sync"
"syscall"
"time"
"github.com/Microsoft/hcsshim/internal/interop"
"github.com/Microsoft/hcsshim/internal/logfields"
"github.com/Microsoft/hcsshim/internal/cow"
"github.com/Microsoft/hcsshim/internal/log"
"github.com/Microsoft/hcsshim/internal/oc"
"github.com/Microsoft/hcsshim/internal/schema1"
hcsschema "github.com/Microsoft/hcsshim/internal/schema2"
"github.com/Microsoft/hcsshim/internal/timeout"
"github.com/sirupsen/logrus"
"github.com/Microsoft/hcsshim/internal/vmcompute"
"go.opencensus.io/trace"
)
// currentContainerStarts is used to limit the number of concurrent container
// starts.
var currentContainerStarts containerStarts
type containerStarts struct {
maxParallel int
inProgress int
sync . Mutex
}
func init ( ) {
mpsS := os . Getenv ( "HCSSHIM_MAX_PARALLEL_START" )
if len ( mpsS ) > 0 {
mpsI , err := strconv . Atoi ( mpsS )
if err != nil || mpsI < 0 {
return
}
currentContainerStarts . maxParallel = mpsI
}
}
type System struct {
handleLock sync . RWMutex
handle h csSystem
handle vmcompute . HcsSystem
id string
callbackNumber uintptr
logctx logrus . Fields
closedWaitOnce sync . Once
waitBlock chan struct { }
waitError error
exitError error
os , typ string
}
func newSystem ( id string ) * System {
return & System {
id : id ,
logctx : logrus . Fields {
logfields . ContainerID : id ,
} ,
}
}
func ( computeSystem * System ) logOperationBegin ( operation string ) {
logOperationBegin (
computeSystem . logctx ,
operation + " - Begin Operation" )
}
func ( computeSystem * System ) logOperationEnd ( operation string , err error ) {
var result string
if err == nil {
result = "Success"
} else {
result = "Error"
waitBlock : make ( chan struct { } ) ,
}
logOperationEnd (
computeSystem . logctx ,
operation + " - End Operation - " + result ,
err )
}
// CreateComputeSystem creates a new compute system with the given configuration but does not start it.
func CreateComputeSystem ( id string , hcsDocumentInterface interface { } ) ( _ * System , err error ) {
func CreateComputeSystem ( ctx context . Context , id string , hcsDocumentInterface interface { } ) ( _ * System , err error ) {
operation := "hcsshim::CreateComputeSystem"
// hcsCreateComputeSystemContext is an async operation. Start the outer span
// here to measure the full create time.
ctx , span := trace . StartSpan ( ctx , operation )
defer span . End ( )
defer func ( ) { oc . SetSpanStatus ( span , err ) } ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , id ) )
computeSystem := newSystem ( id )
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
hcsDocumentB , err := json . Marshal ( hcsDocumentInterface )
if err != nil {
@ -89,126 +59,114 @@ func CreateComputeSystem(id string, hcsDocumentInterface interface{}) (_ *System
hcsDocument := string ( hcsDocumentB )
logrus . WithFields ( computeSystem . logctx ) .
WithField ( logfields . JSON , hcsDocument ) .
Debug ( "HCS ComputeSystem Document" )
var (
resultp * uint16
identity syscall . Handle
resultJSON string
createError error
)
syscallWatcher ( computeSystem . logctx , func ( ) {
createError = hcsCreateComputeSystem ( id , hcsDocument , identity , & computeSystem . handle , & resultp )
} )
computeSystem . handle , resultJSON , createError = vmcompute . HcsCreateComputeSystem ( ctx , id , hcsDocument , identity )
if createError == nil || IsPending ( createError ) {
if err = computeSystem . registerCallback ( ) ; err != nil {
defer func ( ) {
if err != nil {
computeSystem . Close ( )
}
} ( )
if err = computeSystem . registerCallback ( ctx ) ; err != nil {
// Terminate the compute system if it still exists. We're okay to
// ignore a failure here.
computeSystem . Terminate ( )
computeSystem . Terminate ( ctx )
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
}
events , err := processAsyncHcsResult ( createError , resultp , computeSystem . callbackNumber , hcsNotificationSystemCreateCompleted , & timeout . SystemCreate )
events , err := processAsyncHcsResult ( ctx , c reateError , resultJSON , computeSystem . callbackNumber , hcsNotificationSystemCreateCompleted , & timeout . SystemCreate )
if err != nil {
if err == ErrTimeout {
// Terminate the compute system if it still exists. We're okay to
// ignore a failure here.
computeSystem . Terminate ( )
computeSystem . Terminate ( ctx )
}
return nil , makeSystemError ( computeSystem , operation , hcsDocument , err , events )
}
go computeSystem . waitBackground ( )
if err = computeSystem . getCachedProperties ( ctx ) ; err != nil {
return nil , err
}
return computeSystem , nil
}
// OpenComputeSystem opens an existing compute system by ID.
func OpenComputeSystem ( id string ) ( _ * System , err error ) {
func OpenComputeSystem ( ctx context . Context , id string ) ( * System , error ) {
operation := "hcsshim::OpenComputeSystem"
computeSystem := newSystem ( id )
computeSystem . logOperationBegin ( operation )
handle , resultJSON , err := vmcompute . HcsOpenComputeSystem ( ctx , id )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , events )
}
computeSystem . handle = handle
defer func ( ) {
if IsNotExist ( err ) {
computeSystem . logOperationEnd ( operation , nil )
} else {
computeSystem . logOperationEnd ( operation , err )
if err != nil {
computeSystem . Close ( )
}
} ( )
if err = computeSystem . registerCallback ( ctx ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
go computeSystem . waitBackground ( )
if err = computeSystem . getCachedProperties ( ctx ) ; err != nil {
return nil , err
}
return computeSystem , nil
}
var (
handle hcsSystem
resultp * uint16
)
err = hcsOpenComputeSystem ( id , & handle , & resultp )
events := processHcsResult ( resultp )
func ( computeSystem * System ) getCachedProperties ( ctx context . Context ) error {
props , err := computeSystem . Properties ( ctx )
if err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , events )
return err
}
computeSystem . handle = handle
if err = computeSystem . registerCallback ( ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
computeSystem . typ = strings . ToLower ( props . SystemType )
computeSystem . os = strings . ToLower ( props . RuntimeOSType )
if computeSystem . os == "" && computeSystem . typ == "container" {
// Pre-RS5 HCS did not return the OS, but it only supported containers
// that ran Windows.
computeSystem . os = "windows"
}
return nil
}
return computeSystem , nil
// OS returns the operating system of the compute system, "linux" or "windows".
func ( computeSystem * System ) OS ( ) string {
return computeSystem . os
}
// IsOCI returns whether processes in the compute system should be created via
// OCI.
func ( computeSystem * System ) IsOCI ( ) bool {
return computeSystem . os == "linux" && computeSystem . typ == "container"
}
// GetComputeSystems gets a list of the compute systems on the system that match the query
func GetComputeSystems ( q schema1 . ComputeSystemQuery ) ( _ [ ] schema1 . ContainerProperties , err error ) {
func GetComputeSystems ( ctx context . Context , q schema1 . ComputeSystemQuery ) ( [ ] schema1 . ContainerProperties , error ) {
operation := "hcsshim::GetComputeSystems"
fields := logrus . Fields { }
logOperationBegin (
fields ,
operation + " - Begin Operation" )
defer func ( ) {
var result string
if err == nil {
result = "Success"
} else {
result = "Error"
}
logOperationEnd (
fields ,
operation + " - End Operation - " + result ,
err )
} ( )
queryb , err := json . Marshal ( q )
if err != nil {
return nil , err
}
query := string ( queryb )
logrus . WithFields ( fields ) .
WithField ( logfields . JSON , query ) .
Debug ( "HCS ComputeSystem Query" )
var (
resultp * uint16
computeSystemsp * uint16
)
syscallWatcher ( fields , func ( ) {
err = hcsEnumerateComputeSystems ( query , & computeSystemsp , & resultp )
} )
events := processHcsResult ( resultp )
computeSystemsJSON , resultJSON , err := vmcompute . HcsEnumerateComputeSystems ( ctx , string ( queryb ) )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , & HcsError { Op : operation , Err : err , Events : events }
}
if computeSystemsp == nil {
if computeSystemsJSON == "" {
return nil , ErrUnexpectedValue
}
computeSystemsRaw := interop . ConvertAndFreeCoTaskMemBytes ( computeSystemsp )
computeSystems := [ ] schema1 . ContainerProperties { }
if err = json . Unmarshal ( computeSystemsRaw , & computeSystems ) ; err != nil {
if err = json . Unmarshal ( [ ] byte ( computeSystemsJSON ) , & computeSystems ) ; err != nil {
return nil , err
}
@ -216,51 +174,27 @@ func GetComputeSystems(q schema1.ComputeSystemQuery) (_ []schema1.ContainerPrope
}
// Start synchronously starts the computeSystem.
func ( computeSystem * System ) Start ( ) ( err error ) {
func ( computeSystem * System ) Start ( ctx context . Context ) ( err error ) {
operation := "hcsshim::System::Start"
// hcsStartComputeSystemContext is an async operation. Start the outer span
// here to measure the full start time.
ctx , span := trace . StartSpan ( ctx , operation )
defer span . End ( )
defer func ( ) { oc . SetSpanStatus ( span , err ) } ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , computeSystem . id ) )
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Start"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Start" , "" , ErrAlreadyClosed , nil )
}
// This is a very simple backoff-retry loop to limit the number
// of parallel container starts if environment variable
// HCSSHIM_MAX_PARALLEL_START is set to a positive integer.
// It should generally only be used as a workaround to various
// platform issues that exist between RS1 and RS4 as of Aug 2018
if currentContainerStarts . maxParallel > 0 {
for {
currentContainerStarts . Lock ( )
if currentContainerStarts . inProgress < currentContainerStarts . maxParallel {
currentContainerStarts . inProgress ++
currentContainerStarts . Unlock ( )
break
}
if currentContainerStarts . inProgress == currentContainerStarts . maxParallel {
currentContainerStarts . Unlock ( )
time . Sleep ( 100 * time . Millisecond )
}
}
// Make sure we decrement the count when we are done.
defer func ( ) {
currentContainerStarts . Lock ( )
currentContainerStarts . inProgress --
currentContainerStarts . Unlock ( )
} ( )
return makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsStartComputeSystem ( computeSystem . handle , "" , & resultp )
} )
events , err := processAsyncHcsResult ( err , resultp , computeSystem . callbackNumber , hcsNotificationSystemStartCompleted , & timeout . SystemStart )
resultJSON , err := vmcompute . HcsStartComputeSystem ( ctx , computeSystem . handle , "" )
events , err := processAsyncHcsResult ( ctx , err , resultJSON , computeSystem . callbackNumber , hcsNotificationSystemStartCompleted , & timeout . SystemStart )
if err != nil {
return makeSystemError ( computeSystem , "Start" , "" , err , events )
return makeSystemError ( computeSystem , operation , "" , err , events )
}
return nil
@ -271,360 +205,357 @@ func (computeSystem *System) ID() string {
return computeSystem . id
}
// Shutdown requests a compute system shutdown, if IsPending() on the error returned is true,
// it may not actually be shut down until Wait() succeeds.
func ( computeSystem * System ) Shutdown ( ) ( err error ) {
// Shutdown requests a compute system shutdown.
func ( computeSystem * System ) Shutdown ( ctx context . Context ) error {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Shutdown"
computeSystem . logOperationBegin ( operation )
defer func ( ) {
if IsAlreadyStopped ( err ) {
computeSystem . logOperationEnd ( operation , nil )
} else {
computeSystem . logOperationEnd ( operation , err )
}
} ( )
operation := "hcsshim::System::Shutdown"
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Shutdown" , "" , ErrAlreadyClosed , nil )
return nil
}
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsShutdownComputeSystem ( computeSystem . handle , "" , & resultp )
} )
events := processHcsResult ( resultp )
if err != nil {
return makeSystemError ( computeSystem , "Shutdown" , "" , err , events )
resultJSON , err := vmcompute . HcsShutdownComputeSystem ( ctx , computeSystem . handle , "" )
events := processHcsResult ( ctx , resultJSON )
switch err {
case nil , ErrVmcomputeAlreadyStopped , ErrComputeSystemDoesNotExist , ErrVmcomputeOperationPending :
default :
return makeSystemError ( computeSystem , operation , "" , err , events )
}
return nil
}
// Terminate requests a compute system terminate, if IsPending() on the error returned is true,
// it may not actually be shut down until Wait() succeeds.
func ( computeSystem * System ) Terminate ( ) ( err error ) {
// Terminate requests a compute system terminate.
func ( computeSystem * System ) Terminate ( ctx context . Context ) error {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Terminate"
computeSystem . logOperationBegin ( operation )
defer func ( ) {
if IsPending ( err ) {
computeSystem . logOperationEnd ( operation , nil )
} else {
computeSystem . logOperationEnd ( operation , err )
}
} ( )
operation := "hcsshim::System::Terminate"
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Terminate" , "" , ErrAlreadyClosed , nil )
return nil
}
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsTerminateComputeSystem ( computeSystem . handle , "" , & resultp )
} )
events := processHcsResult ( resultp )
if err != nil && err != ErrVmcomputeAlreadyStopped {
return makeSystemError ( computeSystem , "Terminate" , "" , err , events )
resultJSON , err := vmcompute . HcsTerminateComputeSystem ( ctx , computeSystem . handle , "" )
events := processHcsResult ( ctx , resultJSON )
switch err {
case nil , ErrVmcomputeAlreadyStopped , ErrComputeSystemDoesNotExist , ErrVmcomputeOperationPending :
default :
return makeSystemError ( computeSystem , operation , "" , err , events )
}
return nil
}
// Wait synchronously waits for the compute system to shutdown or terminate.
func ( computeSystem * System ) Wait ( ) ( err error ) {
operation := "hcsshim::ComputeSystem::Wait"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
err = waitForNotification ( computeSystem . callbackNumber , hcsNotificationSystemExited , nil )
if err != nil {
return makeSystemError ( computeSystem , "Wait" , "" , err , nil )
}
return nil
// waitBackground waits for the compute system exit notification. Once received
// sets `computeSystem.waitError` (if any) and unblocks all `Wait` calls.
//
// This MUST be called exactly once per `computeSystem.handle` but `Wait` is
// safe to call multiple times.
func ( computeSystem * System ) waitBackground ( ) {
operation := "hcsshim::System::waitBackground"
ctx , span := trace . StartSpan ( context . Background ( ) , operation )
defer span . End ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , computeSystem . id ) )
err := waitForNotification ( ctx , computeSystem . callbackNumber , hcsNotificationSystemExited , nil )
switch err {
case nil :
log . G ( ctx ) . Debug ( "system exited" )
case ErrVmcomputeUnexpectedExit :
log . G ( ctx ) . Debug ( "unexpected system exit" )
computeSystem . exitError = makeSystemError ( computeSystem , operation , "" , err , nil )
err = nil
default :
err = makeSystemError ( computeSystem , operation , "" , err , nil )
}
computeSystem . closedWaitOnce . Do ( func ( ) {
computeSystem . waitError = err
close ( computeSystem . waitBlock )
} )
oc . SetSpanStatus ( span , err )
}
// WaitExpectedError synchronously waits for the compute system to shutdown or
// terminate, and ignores the passed error if it occurs.
func ( computeSystem * System ) WaitExpectedError ( expected error ) ( err error ) {
operation := "hcsshim::ComputeSystem::WaitExpectedError"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
// Wait synchronously waits for the compute system to shutdown or terminate. If
// the compute system has already exited returns the previous error (if any) .
func ( computeSystem * System ) Wait ( ) error {
<- computeSystem . waitBlock
return computeSystem . waitError
}
err = waitForNotification ( computeSystem . callbackNumber , hcsNotificationSystemExited , nil )
if err != nil && getInnerError ( err ) != expected {
return makeSystemError ( computeSystem , "WaitExpectedError" , "" , err , nil )
// ExitError returns an error describing the reason the compute system terminated.
func ( computeSystem * System ) ExitError ( ) error {
select {
case <- computeSystem . waitBlock :
if computeSystem . waitError != nil {
return computeSystem . waitError
}
return computeSystem . exitError
default :
return errors . New ( "container not exited" )
}
return nil
}
// WaitTimeout synchronously waits for the compute system to terminate or the duration to elapse.
// If the timeout expires, IsTimeout(err) == true
func ( computeSystem * System ) WaitTimeout ( timeout time . Duration ) ( err error ) {
operation := "hcsshim::ComputeSystem::WaitTimeout"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
// Properties returns the requested container properties targeting a V1 schema container .
func ( computeSystem * System ) Properties ( ctx context . Context , types ... schema1 . PropertyType ) ( * schema1 . ContainerProperties , error ) {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::System::Properties"
err = waitForNotification ( computeSystem . callbackNumber , hcsNotificationSystemExited , & timeout )
queryBytes , err := json . Marshal ( schema1 . PropertyQuery { PropertyTypes : types } )
if err != nil {
return makeSystemError ( computeSystem , "WaitTimeout" , "" , err , nil )
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
return nil
propertiesJSON , resultJSON , err := vmcompute . HcsGetComputeSystemProperties ( ctx , computeSystem . handle , string ( queryBytes ) )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , events )
}
if propertiesJSON == "" {
return nil , ErrUnexpectedValue
}
properties := & schema1 . ContainerProperties { }
if err := json . Unmarshal ( [ ] byte ( propertiesJSON ) , properties ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
return properties , nil
}
func ( computeSystem * System ) Properties ( types ... schema1 . PropertyType ) ( _ * schema1 . ContainerProperties , err error ) {
// PropertiesV2 returns the requested container properties targeting a V2 schema container.
func ( computeSystem * System ) PropertiesV2 ( ctx context . Context , types ... hcsschema . PropertyType ) ( * hcsschema . Properties , error ) {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Properties"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
operation := "hcsshim::System::PropertiesV2"
queryj , err := json . Marshal ( schema1 . PropertyQuery { types } )
queryBytes , err := json . Marshal ( hcsschema . PropertyQuery { PropertyTypes : types } )
if err != nil {
return nil , makeSystemError ( computeSystem , "Properties" , "" , err , nil )
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
logrus . WithFields ( computeSystem . logctx ) .
WithField ( logfields . JSON , queryj ) .
Debug ( "HCS ComputeSystem Properties Query" )
var resultp , propertiesp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsGetComputeSystemProperties ( computeSystem . handle , string ( queryj ) , & propertiesp , & resultp )
} )
events := processHcsResult ( resultp )
propertiesJSON , resultJSON , err := vmcompute . HcsGetComputeSystemProperties ( ctx , computeSystem . handle , string ( queryBytes ) )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , makeSystemError ( computeSystem , "Properties" , "" , err , events )
return nil , makeSystemError ( computeSystem , operation , "" , err , events )
}
if propertiesp == nil {
if propertiesJSON == "" {
return nil , ErrUnexpectedValue
}
propertiesRaw := interop . ConvertAndFreeCoTaskMemBytes ( propertiesp )
properties := & schema1 . ContainerProperties { }
if err := json . Unmarshal ( propertiesRaw , properties ) ; err != nil {
return nil , makeSystemError ( computeSystem , "Properties" , "" , err , nil )
properties := & hcsschema . Properties { }
if err := json . Unmarshal ( [ ] byte ( propertiesJSON ) , properties ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
return properties , nil
}
// Pause pauses the execution of the computeSystem. This feature is not enabled in TP5.
func ( computeSystem * System ) Pause ( ) ( err error ) {
func ( computeSystem * System ) Pause ( ctx context . Context ) ( err error ) {
operation := "hcsshim::System::Pause"
// hcsPauseComputeSystemContext is an async peration. Start the outer span
// here to measure the full pause time.
ctx , span := trace . StartSpan ( ctx , operation )
defer span . End ( )
defer func ( ) { oc . SetSpanStatus ( span , err ) } ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , computeSystem . id ) )
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Pause"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Pause" , "" , ErrAlreadyClosed , nil )
return makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsPauseComputeSystem ( computeSystem . handle , "" , & resultp )
} )
events , err := processAsyncHcsResult ( err , resultp , computeSystem . callbackNumber , hcsNotificationSystemPauseCompleted , & timeout . SystemPause )
resultJSON , err := vmcompute . HcsPauseComputeSystem ( ctx , computeSystem . handle , "" )
events , err := processAsyncHcsResult ( ctx , err , resultJSON , computeSystem . callbackNumber , hcsNotificationSystemPauseCompleted , & timeout . SystemPause )
if err != nil {
return makeSystemError ( computeSystem , "Pause" , "" , err , events )
return makeSystemError ( computeSystem , operation , "" , err , events )
}
return nil
}
// Resume resumes the execution of the computeSystem. This feature is not enabled in TP5.
func ( computeSystem * System ) Resume ( ) ( err error ) {
func ( computeSystem * System ) Resume ( ctx context . Context ) ( err error ) {
operation := "hcsshim::System::Resume"
// hcsResumeComputeSystemContext is an async operation. Start the outer span
// here to measure the full restore time.
ctx , span := trace . StartSpan ( ctx , operation )
defer span . End ( )
defer func ( ) { oc . SetSpanStatus ( span , err ) } ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , computeSystem . id ) )
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Resume"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Resume" , "" , ErrAlreadyClosed , nil )
return makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsResumeComputeSystem ( computeSystem . handle , "" , & resultp )
} )
events , err := processAsyncHcsResult ( err , resultp , computeSystem . callbackNumber , hcsNotificationSystemResumeCompleted , & timeout . SystemResume )
resultJSON , err := vmcompute . HcsResumeComputeSystem ( ctx , computeSystem . handle , "" )
events , err := processAsyncHcsResult ( ctx , err , resultJSON , computeSystem . callbackNumber , hcsNotificationSystemResumeCompleted , & timeout . SystemResume )
if err != nil {
return makeSystemError ( computeSystem , "Resume" , "" , err , events )
return makeSystemError ( computeSystem , operation , "" , err , events )
}
return nil
}
// CreateProcess launches a new process within the computeSystem.
func ( computeSystem * System ) CreateProcess ( c interface { } ) ( _ * Process , err error ) {
func ( computeSystem * System ) createProcess ( ctx context . Context , operation string , c interface { } ) ( * Process , * vmcompute . HcsProcessInformation , error ) {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::CreateProcess"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
var (
processInfo hcsProcessInformation
processHandle hcsProcess
resultp * uint16
)
if computeSystem . handle == 0 {
return nil , makeSystemError ( computeSystem , "CreateProcess" , "" , ErrAlreadyClosed , nil )
return nil , nil , makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
configurationb , err := json . Marshal ( c )
if err != nil {
return nil , makeSystemError ( computeSystem , "CreateProcess" , "" , err , nil )
return nil , nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
configuration := string ( configurationb )
processInfo , processHandle , resultJSON , err := vmcompute . HcsCreateProcess ( ctx , computeSystem . handle , configuration )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , nil , makeSystemError ( computeSystem , operation , configuration , err , events )
}
logrus . WithFields ( computeSystem . logctx ) .
WithField ( logfields . JSON , configuration ) .
Debug ( "HCS ComputeSystem Process Document" )
log . G ( ctx ) . WithField ( "pid" , processInfo . ProcessId ) . Debug ( "created process pid" )
return newProcess ( processHandle , int ( processInfo . ProcessId ) , computeSystem ) , & processInfo , nil
}
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsCreateProcess ( computeSystem . handle , configuration , & processInfo , & processHandle , & resultp )
} )
events := processHcsResult ( resultp )
// CreateProcess launches a new process within the computeSystem.
func ( computeSystem * System ) CreateProcess ( ctx context . Context , c interface { } ) ( cow . Process , error ) {
operation := "hcsshim::System::CreateProcess"
process , processInfo , err := computeSystem . createProcess ( ctx , operation , c )
if err != nil {
return nil , makeSystemError ( computeSystem , "CreateProcess" , configuration , err , events )
return nil , err
}
defer func ( ) {
if err != nil {
process . Close ( )
}
} ( )
logrus . WithFields ( computeSystem . logctx ) .
WithField ( logfields . ProcessID , processInfo . ProcessId ) .
Debug ( "HCS ComputeSystem CreateProcess PID" )
process := newProcess ( processHandle , int ( processInfo . ProcessId ) , computeSystem )
process . cachedPipes = & cachedPipes {
stdIn : processInfo . StdInput ,
stdOut : processInfo . StdOutput ,
stdErr : processInfo . StdError ,
pipes , err := makeOpenFiles ( [ ] syscall . Handle { processInfo . StdInput , processInfo . StdOutput , processInfo . StdError } )
if err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
process . stdin = pipes [ 0 ]
process . stdout = pipes [ 1 ]
process . stderr = pipes [ 2 ]
process . hasCachedStdio = true
if err = process . registerCallback ( ) ; err != nil {
return nil , makeSystemError ( computeSystem , "CreateProcess" , "" , err , nil )
if err = process . registerCallback ( ctx ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
go process . waitBackground ( )
return process , nil
}
// OpenProcess gets an interface to an existing process within the computeSystem.
func ( computeSystem * System ) OpenProcess ( pid int ) ( _ * Process , err error ) {
func ( computeSystem * System ) OpenProcess ( ctx context . Context , pid int ) ( * Process , error ) {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
// Add PID for the context of this operation
computeSystem . logctx [ logfields . ProcessID ] = pid
defer delete ( computeSystem . logctx , logfields . ProcessID )
operation := "hcsshim::ComputeSystem::OpenProcess"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
var (
processHandle hcsProcess
resultp * uint16
)
operation := "hcsshim::System::OpenProcess"
if computeSystem . handle == 0 {
return nil , makeSystemError ( computeSystem , "OpenProcess" , "" , ErrAlreadyClosed , nil )
return nil , makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsOpenProcess ( computeSystem . handle , uint32 ( pid ) , & processHandle , & resultp )
} )
events := processHcsResult ( resultp )
processHandle , resultJSON , err := vmcompute . HcsOpenProcess ( ctx , computeSystem . handle , uint32 ( pid ) )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return nil , makeSystemError ( computeSystem , "OpenProcess" , "" , err , events )
return nil , makeSystemError ( computeSystem , operation , "" , err , events )
}
process := newProcess ( processHandle , pid , computeSystem )
if err = process . registerCallback ( ) ; err != nil {
return nil , makeSystemError ( computeSystem , "OpenProcess" , "" , err , nil )
if err = process . registerCallback ( ctx ) ; err != nil {
return nil , makeSystemError ( computeSystem , operation , "" , err , nil )
}
go process . waitBackground ( )
return process , nil
}
// Close cleans up any state associated with the compute system but does not terminate or wait for it.
func ( computeSystem * System ) Close ( ) ( err error ) {
operation := "hcsshim::System::Close"
ctx , span := trace . StartSpan ( context . Background ( ) , operation )
defer span . End ( )
defer func ( ) { oc . SetSpanStatus ( span , err ) } ( )
span . AddAttributes ( trace . StringAttribute ( "cid" , computeSystem . id ) )
computeSystem . handleLock . Lock ( )
defer computeSystem . handleLock . Unlock ( )
operation := "hcsshim::ComputeSystem::Close"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
// Don't double free this
if computeSystem . handle == 0 {
return nil
}
if err = computeSystem . unregisterCallback ( ) ; err != nil {
return makeSystemError ( computeSystem , "Close" , "" , err , nil )
if err = computeSystem . unregisterCallback ( ctx ) ; err != nil {
return makeSystemError ( computeSystem , operation , "" , err , nil )
}
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsCloseComputeSystem ( computeSystem . handle )
} )
err = vmcompute . HcsCloseComputeSystem ( ctx , computeSystem . handle )
if err != nil {
return makeSystemError ( computeSystem , "Close" , "" , err , nil )
return makeSystemError ( computeSystem , operation , "" , err , nil )
}
computeSystem . handle = 0
computeSystem . closedWaitOnce . Do ( func ( ) {
computeSystem . waitError = ErrAlreadyClosed
close ( computeSystem . waitBlock )
} )
return nil
}
func ( computeSystem * System ) registerCallback ( ) error {
context := & notifcationWatcherContext {
channels : newChannels ( ) ,
func ( computeSystem * System ) registerCallback ( ctx context . Context ) error {
callbackContext := & notifcationWatcherContext {
channels : newSystemChannels ( ) ,
systemID : computeSystem . id ,
}
callbackMapLock . Lock ( )
callbackNumber := nextCallback
nextCallback ++
callbackMap [ callbackNumber ] = context
callbackMap [ callbackNumber ] = callbackC ontext
callbackMapLock . Unlock ( )
var callbackHandle hcsCallback
err := hcsRegisterComputeSystemCallback ( computeSystem . handle , notificationWatcherCallback , callbackNumber , & callbackHandle )
callbackHandle , err := vmcompute . HcsRegisterComputeSystemCallback ( ctx , computeSystem . handle , notificationWatcherCallback , callbackNumber )
if err != nil {
return err
}
context . handle = callbackHandle
callbackC ontext . handle = callbackHandle
computeSystem . callbackNumber = callbackNumber
return nil
}
func ( computeSystem * System ) unregisterCallback ( ) error {
func ( computeSystem * System ) unregisterCallback ( ctx context . Context ) error {
callbackNumber := computeSystem . callbackNumber
callbackMapLock . RLock ( )
context := callbackMap [ callbackNumber ]
callbackC ontext := callbackMap [ callbackNumber ]
callbackMapLock . RUnlock ( )
if context == nil {
if callbackC ontext == nil {
return nil
}
handle := context . handle
handle := callbackC ontext . handle
if handle == 0 {
return nil
@ -632,15 +563,15 @@ func (computeSystem *System) unregisterCallback() error {
// hcsUnregisterComputeSystemCallback has its own syncronization
// to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
err := h csUnregisterComputeSystemCallback( handle )
err := vmcompute . H csUnregisterComputeSystemCallback( ctx , handle )
if err != nil {
return err
}
closeChannels ( context . channels )
closeChannels ( callbackC ontext . channels )
callbackMapLock . Lock ( )
callbackMap [ callbackNumber ] = nil
delete ( callbackMap , callbackNumber )
callbackMapLock . Unlock ( )
handle = 0
@ -649,36 +580,26 @@ func (computeSystem *System) unregisterCallback() error {
}
// Modify the System by sending a request to HCS
func ( computeSystem * System ) Modify ( config interface { } ) ( err error ) {
func ( computeSystem * System ) Modify ( ctx context . Context , config interface { } ) error {
computeSystem . handleLock . RLock ( )
defer computeSystem . handleLock . RUnlock ( )
operation := "hcsshim::ComputeSystem::Modify"
computeSystem . logOperationBegin ( operation )
defer func ( ) { computeSystem . logOperationEnd ( operation , err ) } ( )
operation := "hcsshim::System::Modify"
if computeSystem . handle == 0 {
return makeSystemError ( computeSystem , "Modify" , "" , ErrAlreadyClosed , nil )
return makeSystemError ( computeSystem , operation , "" , ErrAlreadyClosed , nil )
}
requestJSON , err := json . Marshal ( config )
requestBytes , err := json . Marshal ( config )
if err != nil {
return err
}
requestString := string ( requestJSON )
logrus . WithFields ( computeSystem . logctx ) .
WithField ( logfields . JSON , requestString ) .
Debug ( "HCS ComputeSystem Modify Document" )
var resultp * uint16
syscallWatcher ( computeSystem . logctx , func ( ) {
err = hcsModifyComputeSystem ( computeSystem . handle , requestString , & resultp )
} )
events := processHcsResult ( resultp )
requestJSON := string ( requestBytes )
resultJSON , err := vmcompute . HcsModifyComputeSystem ( ctx , computeSystem . handle , requestJSON )
events := processHcsResult ( ctx , resultJSON )
if err != nil {
return makeSystemError ( computeSystem , "Modify" , requestString , err , events )
return makeSystemError ( computeSystem , operation , requestJSON , err , events )
}
return nil