123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739 |
- package hcsshim
- import (
- "encoding/json"
- "fmt"
- "os"
- "sync"
- "syscall"
- "time"
- "github.com/Sirupsen/logrus"
- )
- var (
- defaultTimeout = time.Minute * 4
- )
- const (
- pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}`
- statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}`
- processListQuery = `{ "PropertyTypes" : ["ProcessList"]}`
- )
- type container struct {
- handleLock sync.RWMutex
- handle hcsSystem
- id string
- callbackNumber uintptr
- }
- // ContainerProperties holds the properties for a container and the processes running in that container
- type ContainerProperties struct {
- ID string `json:"Id"`
- Name string
- SystemType string
- Owner string
- SiloGUID string `json:"SiloGuid,omitempty"`
- IsDummy bool `json:",omitempty"`
- RuntimeID string `json:"RuntimeId,omitempty"`
- IsRuntimeTemplate bool `json:",omitempty"`
- RuntimeImagePath string `json:",omitempty"`
- Stopped bool `json:",omitempty"`
- ExitType string `json:",omitempty"`
- AreUpdatesPending bool `json:",omitempty"`
- ObRoot string `json:",omitempty"`
- Statistics Statistics `json:",omitempty"`
- ProcessList []ProcessListItem `json:",omitempty"`
- }
- // MemoryStats holds the memory statistics for a container
- type MemoryStats struct {
- UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
- UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
- UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
- }
- // ProcessorStats holds the processor statistics for a container
- type ProcessorStats struct {
- TotalRuntime100ns uint64 `json:",omitempty"`
- RuntimeUser100ns uint64 `json:",omitempty"`
- RuntimeKernel100ns uint64 `json:",omitempty"`
- }
- // StorageStats holds the storage statistics for a container
- type StorageStats struct {
- ReadCountNormalized uint64 `json:",omitempty"`
- ReadSizeBytes uint64 `json:",omitempty"`
- WriteCountNormalized uint64 `json:",omitempty"`
- WriteSizeBytes uint64 `json:",omitempty"`
- }
- // NetworkStats holds the network statistics for a container
- type NetworkStats struct {
- BytesReceived uint64 `json:",omitempty"`
- BytesSent uint64 `json:",omitempty"`
- PacketsReceived uint64 `json:",omitempty"`
- PacketsSent uint64 `json:",omitempty"`
- DroppedPacketsIncoming uint64 `json:",omitempty"`
- DroppedPacketsOutgoing uint64 `json:",omitempty"`
- EndpointId string `json:",omitempty"`
- InstanceId string `json:",omitempty"`
- }
- // Statistics is the structure returned by a statistics call on a container
- type Statistics struct {
- Timestamp time.Time `json:",omitempty"`
- ContainerStartTime time.Time `json:",omitempty"`
- Uptime100ns uint64 `json:",omitempty"`
- Memory MemoryStats `json:",omitempty"`
- Processor ProcessorStats `json:",omitempty"`
- Storage StorageStats `json:",omitempty"`
- Network []NetworkStats `json:",omitempty"`
- }
- // ProcessList is the structure of an item returned by a ProcessList call on a container
- type ProcessListItem struct {
- CreateTimestamp time.Time `json:",omitempty"`
- ImageName string `json:",omitempty"`
- KernelTime100ns uint64 `json:",omitempty"`
- MemoryCommitBytes uint64 `json:",omitempty"`
- MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"`
- MemoryWorkingSetSharedBytes uint64 `json:",omitempty"`
- ProcessId uint32 `json:",omitempty"`
- UserTime100ns uint64 `json:",omitempty"`
- }
- // Type of Request Support in ModifySystem
- type RequestType string
- // Type of Resource Support in ModifySystem
- type ResourceType string
- // RequestType const
- const (
- Add RequestType = "Add"
- Remove RequestType = "Remove"
- Network ResourceType = "Network"
- )
- // ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
- // Supported resource types are Network and Request Types are Add/Remove
- type ResourceModificationRequestResponse struct {
- Resource ResourceType `json:"ResourceType"`
- Data string `json:"Settings"`
- Request RequestType `json:"RequestType,omitempty"`
- }
- // createContainerAdditionalJSON is read from the environment at initialisation
- // time. It allows an environment variable to define additional JSON which
- // is merged in the CreateContainer call to HCS.
- var createContainerAdditionalJSON string
- func init() {
- createContainerAdditionalJSON = os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")
- }
- // CreateContainer creates a new container with the given configuration but does not start it.
- func CreateContainer(id string, c *ContainerConfig) (Container, error) {
- return createContainerWithJSON(id, c, "")
- }
- // CreateContainerWithJSON creates a new container with the given configuration but does not start it.
- // It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS.
- func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
- return createContainerWithJSON(id, c, additionalJSON)
- }
- func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
- operation := "CreateContainer"
- title := "HCSShim::" + operation
- container := &container{
- id: id,
- }
- configurationb, err := json.Marshal(c)
- if err != nil {
- return nil, err
- }
- configuration := string(configurationb)
- logrus.Debugf(title+" id=%s config=%s", id, configuration)
- // Merge any additional JSON. Priority is given to what is passed in explicitly,
- // falling back to what's set in the environment.
- if additionalJSON == "" && createContainerAdditionalJSON != "" {
- additionalJSON = createContainerAdditionalJSON
- }
- if additionalJSON != "" {
- configurationMap := map[string]interface{}{}
- if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil {
- return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err)
- }
- additionalMap := map[string]interface{}{}
- if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil {
- return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err)
- }
- mergedMap := mergeMaps(additionalMap, configurationMap)
- mergedJSON, err := json.Marshal(mergedMap)
- if err != nil {
- return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err)
- }
- configuration = string(mergedJSON)
- logrus.Debugf(title+" id=%s merged config=%s", id, configuration)
- }
- var (
- resultp *uint16
- identity syscall.Handle
- )
- createError := hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp)
- if createError == nil || IsPending(createError) {
- if err := container.registerCallback(); err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- }
- err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout)
- if err != nil {
- return nil, makeContainerError(container, operation, configuration, err)
- }
- logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle)
- return container, nil
- }
- // mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values
- // in ToMap are overwritten. Values in fromMap are added to ToMap.
- // From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang
- func mergeMaps(fromMap, ToMap interface{}) interface{} {
- switch fromMap := fromMap.(type) {
- case map[string]interface{}:
- ToMap, ok := ToMap.(map[string]interface{})
- if !ok {
- return fromMap
- }
- for keyToMap, valueToMap := range ToMap {
- if valueFromMap, ok := fromMap[keyToMap]; ok {
- fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap)
- } else {
- fromMap[keyToMap] = valueToMap
- }
- }
- case nil:
- // merge(nil, map[string]interface{...}) -> map[string]interface{...}
- ToMap, ok := ToMap.(map[string]interface{})
- if ok {
- return ToMap
- }
- }
- return fromMap
- }
- // OpenContainer opens an existing container by ID.
- func OpenContainer(id string) (Container, error) {
- operation := "OpenContainer"
- title := "HCSShim::" + operation
- logrus.Debugf(title+" id=%s", id)
- container := &container{
- id: id,
- }
- var (
- handle hcsSystem
- resultp *uint16
- )
- err := hcsOpenComputeSystem(id, &handle, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- container.handle = handle
- if err := container.registerCallback(); err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
- return container, nil
- }
- // GetContainers gets a list of the containers on the system that match the query
- func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
- operation := "GetContainers"
- title := "HCSShim::" + operation
- queryb, err := json.Marshal(q)
- if err != nil {
- return nil, err
- }
- query := string(queryb)
- logrus.Debugf(title+" query=%s", query)
- var (
- resultp *uint16
- computeSystemsp *uint16
- )
- err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return nil, err
- }
- if computeSystemsp == nil {
- return nil, ErrUnexpectedValue
- }
- computeSystemsRaw := convertAndFreeCoTaskMemBytes(computeSystemsp)
- computeSystems := []ContainerProperties{}
- if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil {
- return nil, err
- }
- logrus.Debugf(title + " succeeded")
- return computeSystems, nil
- }
- // Start synchronously starts the container.
- func (container *container) Start() error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Start"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- var resultp *uint16
- err := hcsStartComputeSystem(container.handle, "", &resultp)
- err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemStartCompleted, &defaultTimeout)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // Shutdown requests a container shutdown, if IsPending() on the error returned is true,
- // it may not actually be shut down until Wait() succeeds.
- func (container *container) Shutdown() error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Shutdown"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- var resultp *uint16
- err := hcsShutdownComputeSystem(container.handle, "", &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // Terminate requests a container terminate, if IsPending() on the error returned is true,
- // it may not actually be shut down until Wait() succeeds.
- func (container *container) Terminate() error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Terminate"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- var resultp *uint16
- err := hcsTerminateComputeSystem(container.handle, "", &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // Wait synchronously waits for the container to shutdown or terminate.
- func (container *container) Wait() error {
- operation := "Wait"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, nil)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // WaitTimeout synchronously waits for the container to terminate or the duration to elapse.
- // If the timeout expires, IsTimeout(err) == true
- func (container *container) WaitTimeout(timeout time.Duration) error {
- operation := "WaitTimeout"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, &timeout)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- func (container *container) properties(query string) (*ContainerProperties, error) {
- var (
- resultp *uint16
- propertiesp *uint16
- )
- err := hcsGetComputeSystemProperties(container.handle, query, &propertiesp, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return nil, err
- }
- if propertiesp == nil {
- return nil, ErrUnexpectedValue
- }
- propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
- properties := &ContainerProperties{}
- if err := json.Unmarshal(propertiesRaw, properties); err != nil {
- return nil, err
- }
- return properties, nil
- }
- // HasPendingUpdates returns true if the container has updates pending to install
- func (container *container) HasPendingUpdates() (bool, error) {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "HasPendingUpdates"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return false, makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- properties, err := container.properties(pendingUpdatesQuery)
- if err != nil {
- return false, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return properties.AreUpdatesPending, nil
- }
- // Statistics returns statistics for the container
- func (container *container) Statistics() (Statistics, error) {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Statistics"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return Statistics{}, makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- properties, err := container.properties(statisticsQuery)
- if err != nil {
- return Statistics{}, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return properties.Statistics, nil
- }
- // ProcessList returns an array of ProcessListItems for the container
- func (container *container) ProcessList() ([]ProcessListItem, error) {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "ProcessList"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- properties, err := container.properties(processListQuery)
- if err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return properties.ProcessList, nil
- }
- // Pause pauses the execution of the container. This feature is not enabled in TP5.
- func (container *container) Pause() error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Pause"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- var resultp *uint16
- err := hcsPauseComputeSystem(container.handle, "", &resultp)
- err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemPauseCompleted, &defaultTimeout)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // Resume resumes the execution of the container. This feature is not enabled in TP5.
- func (container *container) Resume() error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Resume"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- var resultp *uint16
- err := hcsResumeComputeSystem(container.handle, "", &resultp)
- err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemResumeCompleted, &defaultTimeout)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- // CreateProcess launches a new process within the container.
- func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "CreateProcess"
- title := "HCSShim::Container::" + operation
- var (
- processInfo hcsProcessInformation
- processHandle hcsProcess
- resultp *uint16
- )
- if container.handle == 0 {
- return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- // If we are not emulating a console, ignore any console size passed to us
- if !c.EmulateConsole {
- c.ConsoleSize[0] = 0
- c.ConsoleSize[1] = 0
- }
- configurationb, err := json.Marshal(c)
- if err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- configuration := string(configurationb)
- logrus.Debugf(title+" id=%s config=%s", container.id, configuration)
- err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return nil, makeContainerError(container, operation, configuration, err)
- }
- process := &process{
- handle: processHandle,
- processID: int(processInfo.ProcessId),
- container: container,
- cachedPipes: &cachedPipes{
- stdIn: processInfo.StdInput,
- stdOut: processInfo.StdOutput,
- stdErr: processInfo.StdError,
- },
- }
- if err := process.registerCallback(); err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
- return process, nil
- }
- // OpenProcess gets an interface to an existing process within the container.
- func (container *container) OpenProcess(pid int) (Process, error) {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "OpenProcess"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s, processid=%d", container.id, pid)
- var (
- processHandle hcsProcess
- resultp *uint16
- )
- if container.handle == 0 {
- return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- process := &process{
- handle: processHandle,
- processID: pid,
- container: container,
- }
- if err := process.registerCallback(); err != nil {
- return nil, makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
- return process, nil
- }
- // Close cleans up any state associated with the container but does not terminate or wait for it.
- func (container *container) Close() error {
- container.handleLock.Lock()
- defer container.handleLock.Unlock()
- operation := "Close"
- title := "HCSShim::Container::" + operation
- logrus.Debugf(title+" id=%s", container.id)
- // Don't double free this
- if container.handle == 0 {
- return nil
- }
- if err := container.unregisterCallback(); err != nil {
- return makeContainerError(container, operation, "", err)
- }
- if err := hcsCloseComputeSystem(container.handle); err != nil {
- return makeContainerError(container, operation, "", err)
- }
- container.handle = 0
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
- func (container *container) registerCallback() error {
- context := ¬ifcationWatcherContext{
- channels: newChannels(),
- }
- callbackMapLock.Lock()
- callbackNumber := nextCallback
- nextCallback++
- callbackMap[callbackNumber] = context
- callbackMapLock.Unlock()
- var callbackHandle hcsCallback
- err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
- if err != nil {
- return err
- }
- context.handle = callbackHandle
- container.callbackNumber = callbackNumber
- return nil
- }
- func (container *container) unregisterCallback() error {
- callbackNumber := container.callbackNumber
- callbackMapLock.RLock()
- context := callbackMap[callbackNumber]
- callbackMapLock.RUnlock()
- if context == nil {
- return nil
- }
- handle := context.handle
- if handle == 0 {
- return nil
- }
- // hcsUnregisterComputeSystemCallback has its own syncronization
- // to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
- err := hcsUnregisterComputeSystemCallback(handle)
- if err != nil {
- return err
- }
- closeChannels(context.channels)
- callbackMapLock.Lock()
- callbackMap[callbackNumber] = nil
- callbackMapLock.Unlock()
- handle = 0
- return nil
- }
- // Modifies the System by sending a request to HCS
- func (container *container) Modify(config *ResourceModificationRequestResponse) error {
- container.handleLock.RLock()
- defer container.handleLock.RUnlock()
- operation := "Modify"
- title := "HCSShim::Container::" + operation
- if container.handle == 0 {
- return makeContainerError(container, operation, "", ErrAlreadyClosed)
- }
- requestJSON, err := json.Marshal(config)
- if err != nil {
- return err
- }
- requestString := string(requestJSON)
- logrus.Debugf(title+" id=%s request=%s", container.id, requestString)
- var resultp *uint16
- err = hcsModifyComputeSystem(container.handle, requestString, &resultp)
- err = processHcsResult(err, resultp)
- if err != nil {
- return makeContainerError(container, operation, "", err)
- }
- logrus.Debugf(title+" succeeded id=%s", container.id)
- return nil
- }
|