container.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package hcsshim
  2. import (
  3. "context"
  4. "fmt"
  5. "os"
  6. "sync"
  7. "time"
  8. "github.com/Microsoft/hcsshim/internal/hcs"
  9. "github.com/Microsoft/hcsshim/internal/mergemaps"
  10. "github.com/Microsoft/hcsshim/internal/schema1"
  11. )
  12. // ContainerProperties holds the properties for a container and the processes running in that container
  13. type ContainerProperties = schema1.ContainerProperties
  14. // MemoryStats holds the memory statistics for a container
  15. type MemoryStats = schema1.MemoryStats
  16. // ProcessorStats holds the processor statistics for a container
  17. type ProcessorStats = schema1.ProcessorStats
  18. // StorageStats holds the storage statistics for a container
  19. type StorageStats = schema1.StorageStats
  20. // NetworkStats holds the network statistics for a container
  21. type NetworkStats = schema1.NetworkStats
  22. // Statistics is the structure returned by a statistics call on a container
  23. type Statistics = schema1.Statistics
  24. // ProcessList is the structure of an item returned by a ProcessList call on a container
  25. type ProcessListItem = schema1.ProcessListItem
  26. // MappedVirtualDiskController is the structure of an item returned by a MappedVirtualDiskList call on a container
  27. type MappedVirtualDiskController = schema1.MappedVirtualDiskController
  28. // Type of Request Support in ModifySystem
  29. type RequestType = schema1.RequestType
  30. // Type of Resource Support in ModifySystem
  31. type ResourceType = schema1.ResourceType
  32. // RequestType const
  33. const (
  34. Add = schema1.Add
  35. Remove = schema1.Remove
  36. Network = schema1.Network
  37. )
  38. // ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
  39. // Supported resource types are Network and Request Types are Add/Remove
  40. type ResourceModificationRequestResponse = schema1.ResourceModificationRequestResponse
  41. type container struct {
  42. system *hcs.System
  43. waitOnce sync.Once
  44. waitErr error
  45. waitCh chan struct{}
  46. }
  47. // createComputeSystemAdditionalJSON is read from the environment at initialisation
  48. // time. It allows an environment variable to define additional JSON which
  49. // is merged in the CreateComputeSystem call to HCS.
  50. var createContainerAdditionalJSON []byte
  51. func init() {
  52. createContainerAdditionalJSON = ([]byte)(os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON"))
  53. }
  54. // CreateContainer creates a new container with the given configuration but does not start it.
  55. func CreateContainer(id string, c *ContainerConfig) (Container, error) {
  56. fullConfig, err := mergemaps.MergeJSON(c, createContainerAdditionalJSON)
  57. if err != nil {
  58. return nil, fmt.Errorf("failed to merge additional JSON '%s': %s", createContainerAdditionalJSON, err)
  59. }
  60. system, err := hcs.CreateComputeSystem(context.Background(), id, fullConfig)
  61. if err != nil {
  62. return nil, err
  63. }
  64. return &container{system: system}, err
  65. }
  66. // OpenContainer opens an existing container by ID.
  67. func OpenContainer(id string) (Container, error) {
  68. system, err := hcs.OpenComputeSystem(context.Background(), id)
  69. if err != nil {
  70. return nil, err
  71. }
  72. return &container{system: system}, err
  73. }
  74. // GetContainers gets a list of the containers on the system that match the query
  75. func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
  76. return hcs.GetComputeSystems(context.Background(), q)
  77. }
  78. // Start synchronously starts the container.
  79. func (container *container) Start() error {
  80. return convertSystemError(container.system.Start(context.Background()), container)
  81. }
  82. // Shutdown requests a container shutdown, but it may not actually be shutdown until Wait() succeeds.
  83. func (container *container) Shutdown() error {
  84. err := container.system.Shutdown(context.Background())
  85. if err != nil {
  86. return convertSystemError(err, container)
  87. }
  88. return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Shutdown"}
  89. }
  90. // Terminate requests a container terminate, but it may not actually be terminated until Wait() succeeds.
  91. func (container *container) Terminate() error {
  92. err := container.system.Terminate(context.Background())
  93. if err != nil {
  94. return convertSystemError(err, container)
  95. }
  96. return &ContainerError{Container: container, Err: ErrVmcomputeOperationPending, Operation: "hcsshim::ComputeSystem::Terminate"}
  97. }
  98. // Waits synchronously waits for the container to shutdown or terminate.
  99. func (container *container) Wait() error {
  100. err := container.system.Wait()
  101. if err == nil {
  102. err = container.system.ExitError()
  103. }
  104. return convertSystemError(err, container)
  105. }
  106. // WaitTimeout synchronously waits for the container to terminate or the duration to elapse. It
  107. // returns false if timeout occurs.
  108. func (container *container) WaitTimeout(timeout time.Duration) error {
  109. container.waitOnce.Do(func() {
  110. container.waitCh = make(chan struct{})
  111. go func() {
  112. container.waitErr = container.Wait()
  113. close(container.waitCh)
  114. }()
  115. })
  116. t := time.NewTimer(timeout)
  117. defer t.Stop()
  118. select {
  119. case <-t.C:
  120. return &ContainerError{Container: container, Err: ErrTimeout, Operation: "hcsshim::ComputeSystem::Wait"}
  121. case <-container.waitCh:
  122. return container.waitErr
  123. }
  124. }
  125. // Pause pauses the execution of a container.
  126. func (container *container) Pause() error {
  127. return convertSystemError(container.system.Pause(context.Background()), container)
  128. }
  129. // Resume resumes the execution of a container.
  130. func (container *container) Resume() error {
  131. return convertSystemError(container.system.Resume(context.Background()), container)
  132. }
  133. // HasPendingUpdates returns true if the container has updates pending to install
  134. func (container *container) HasPendingUpdates() (bool, error) {
  135. return false, nil
  136. }
  137. // Statistics returns statistics for the container. This is a legacy v1 call
  138. func (container *container) Statistics() (Statistics, error) {
  139. properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeStatistics)
  140. if err != nil {
  141. return Statistics{}, convertSystemError(err, container)
  142. }
  143. return properties.Statistics, nil
  144. }
  145. // ProcessList returns an array of ProcessListItems for the container. This is a legacy v1 call
  146. func (container *container) ProcessList() ([]ProcessListItem, error) {
  147. properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeProcessList)
  148. if err != nil {
  149. return nil, convertSystemError(err, container)
  150. }
  151. return properties.ProcessList, nil
  152. }
  153. // This is a legacy v1 call
  154. func (container *container) MappedVirtualDisks() (map[int]MappedVirtualDiskController, error) {
  155. properties, err := container.system.Properties(context.Background(), schema1.PropertyTypeMappedVirtualDisk)
  156. if err != nil {
  157. return nil, convertSystemError(err, container)
  158. }
  159. return properties.MappedVirtualDiskControllers, nil
  160. }
  161. // CreateProcess launches a new process within the container.
  162. func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
  163. p, err := container.system.CreateProcess(context.Background(), c)
  164. if err != nil {
  165. return nil, convertSystemError(err, container)
  166. }
  167. return &process{p: p.(*hcs.Process)}, nil
  168. }
  169. // OpenProcess gets an interface to an existing process within the container.
  170. func (container *container) OpenProcess(pid int) (Process, error) {
  171. p, err := container.system.OpenProcess(context.Background(), pid)
  172. if err != nil {
  173. return nil, convertSystemError(err, container)
  174. }
  175. return &process{p: p}, nil
  176. }
  177. // Close cleans up any state associated with the container but does not terminate or wait for it.
  178. func (container *container) Close() error {
  179. return convertSystemError(container.system.Close(), container)
  180. }
  181. // Modify the System
  182. func (container *container) Modify(config *ResourceModificationRequestResponse) error {
  183. return convertSystemError(container.system.Modify(context.Background(), config), container)
  184. }