container.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  1. package hcsshim
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "sync"
  7. "syscall"
  8. "time"
  9. "github.com/Sirupsen/logrus"
  10. )
  11. var (
  12. defaultTimeout = time.Minute * 4
  13. )
  14. const (
  15. pendingUpdatesQuery = `{ "PropertyTypes" : ["PendingUpdates"]}`
  16. statisticsQuery = `{ "PropertyTypes" : ["Statistics"]}`
  17. processListQuery = `{ "PropertyTypes" : ["ProcessList"]}`
  18. )
  19. type container struct {
  20. handleLock sync.RWMutex
  21. handle hcsSystem
  22. id string
  23. callbackNumber uintptr
  24. }
  25. // ContainerProperties holds the properties for a container and the processes running in that container
  26. type ContainerProperties struct {
  27. ID string `json:"Id"`
  28. Name string
  29. SystemType string
  30. Owner string
  31. SiloGUID string `json:"SiloGuid,omitempty"`
  32. IsDummy bool `json:",omitempty"`
  33. RuntimeID string `json:"RuntimeId,omitempty"`
  34. IsRuntimeTemplate bool `json:",omitempty"`
  35. RuntimeImagePath string `json:",omitempty"`
  36. Stopped bool `json:",omitempty"`
  37. ExitType string `json:",omitempty"`
  38. AreUpdatesPending bool `json:",omitempty"`
  39. ObRoot string `json:",omitempty"`
  40. Statistics Statistics `json:",omitempty"`
  41. ProcessList []ProcessListItem `json:",omitempty"`
  42. }
  43. // MemoryStats holds the memory statistics for a container
  44. type MemoryStats struct {
  45. UsageCommitBytes uint64 `json:"MemoryUsageCommitBytes,omitempty"`
  46. UsageCommitPeakBytes uint64 `json:"MemoryUsageCommitPeakBytes,omitempty"`
  47. UsagePrivateWorkingSetBytes uint64 `json:"MemoryUsagePrivateWorkingSetBytes,omitempty"`
  48. }
  49. // ProcessorStats holds the processor statistics for a container
  50. type ProcessorStats struct {
  51. TotalRuntime100ns uint64 `json:",omitempty"`
  52. RuntimeUser100ns uint64 `json:",omitempty"`
  53. RuntimeKernel100ns uint64 `json:",omitempty"`
  54. }
  55. // StorageStats holds the storage statistics for a container
  56. type StorageStats struct {
  57. ReadCountNormalized uint64 `json:",omitempty"`
  58. ReadSizeBytes uint64 `json:",omitempty"`
  59. WriteCountNormalized uint64 `json:",omitempty"`
  60. WriteSizeBytes uint64 `json:",omitempty"`
  61. }
  62. // NetworkStats holds the network statistics for a container
  63. type NetworkStats struct {
  64. BytesReceived uint64 `json:",omitempty"`
  65. BytesSent uint64 `json:",omitempty"`
  66. PacketsReceived uint64 `json:",omitempty"`
  67. PacketsSent uint64 `json:",omitempty"`
  68. DroppedPacketsIncoming uint64 `json:",omitempty"`
  69. DroppedPacketsOutgoing uint64 `json:",omitempty"`
  70. EndpointId string `json:",omitempty"`
  71. InstanceId string `json:",omitempty"`
  72. }
  73. // Statistics is the structure returned by a statistics call on a container
  74. type Statistics struct {
  75. Timestamp time.Time `json:",omitempty"`
  76. ContainerStartTime time.Time `json:",omitempty"`
  77. Uptime100ns uint64 `json:",omitempty"`
  78. Memory MemoryStats `json:",omitempty"`
  79. Processor ProcessorStats `json:",omitempty"`
  80. Storage StorageStats `json:",omitempty"`
  81. Network []NetworkStats `json:",omitempty"`
  82. }
  83. // ProcessList is the structure of an item returned by a ProcessList call on a container
  84. type ProcessListItem struct {
  85. CreateTimestamp time.Time `json:",omitempty"`
  86. ImageName string `json:",omitempty"`
  87. KernelTime100ns uint64 `json:",omitempty"`
  88. MemoryCommitBytes uint64 `json:",omitempty"`
  89. MemoryWorkingSetPrivateBytes uint64 `json:",omitempty"`
  90. MemoryWorkingSetSharedBytes uint64 `json:",omitempty"`
  91. ProcessId uint32 `json:",omitempty"`
  92. UserTime100ns uint64 `json:",omitempty"`
  93. }
  94. // Type of Request Support in ModifySystem
  95. type RequestType string
  96. // Type of Resource Support in ModifySystem
  97. type ResourceType string
  98. // RequestType const
  99. const (
  100. Add RequestType = "Add"
  101. Remove RequestType = "Remove"
  102. Network ResourceType = "Network"
  103. )
  104. // ResourceModificationRequestResponse is the structure used to send request to the container to modify the system
  105. // Supported resource types are Network and Request Types are Add/Remove
  106. type ResourceModificationRequestResponse struct {
  107. Resource ResourceType `json:"ResourceType"`
  108. Data string `json:"Settings"`
  109. Request RequestType `json:"RequestType,omitempty"`
  110. }
  111. // createContainerAdditionalJSON is read from the environment at initialisation
  112. // time. It allows an environment variable to define additional JSON which
  113. // is merged in the CreateContainer call to HCS.
  114. var createContainerAdditionalJSON string
  115. func init() {
  116. createContainerAdditionalJSON = os.Getenv("HCSSHIM_CREATECONTAINER_ADDITIONALJSON")
  117. }
  118. // CreateContainer creates a new container with the given configuration but does not start it.
  119. func CreateContainer(id string, c *ContainerConfig) (Container, error) {
  120. return createContainerWithJSON(id, c, "")
  121. }
  122. // CreateContainerWithJSON creates a new container with the given configuration but does not start it.
  123. // It is identical to CreateContainer except that optional additional JSON can be merged before passing to HCS.
  124. func CreateContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
  125. return createContainerWithJSON(id, c, additionalJSON)
  126. }
  127. func createContainerWithJSON(id string, c *ContainerConfig, additionalJSON string) (Container, error) {
  128. operation := "CreateContainer"
  129. title := "HCSShim::" + operation
  130. container := &container{
  131. id: id,
  132. }
  133. configurationb, err := json.Marshal(c)
  134. if err != nil {
  135. return nil, err
  136. }
  137. configuration := string(configurationb)
  138. logrus.Debugf(title+" id=%s config=%s", id, configuration)
  139. // Merge any additional JSON. Priority is given to what is passed in explicitly,
  140. // falling back to what's set in the environment.
  141. if additionalJSON == "" && createContainerAdditionalJSON != "" {
  142. additionalJSON = createContainerAdditionalJSON
  143. }
  144. if additionalJSON != "" {
  145. configurationMap := map[string]interface{}{}
  146. if err := json.Unmarshal([]byte(configuration), &configurationMap); err != nil {
  147. return nil, fmt.Errorf("failed to unmarshal %s: %s", configuration, err)
  148. }
  149. additionalMap := map[string]interface{}{}
  150. if err := json.Unmarshal([]byte(additionalJSON), &additionalMap); err != nil {
  151. return nil, fmt.Errorf("failed to unmarshal %s: %s", additionalJSON, err)
  152. }
  153. mergedMap := mergeMaps(additionalMap, configurationMap)
  154. mergedJSON, err := json.Marshal(mergedMap)
  155. if err != nil {
  156. return nil, fmt.Errorf("failed to marshal merged configuration map %+v: %s", mergedMap, err)
  157. }
  158. configuration = string(mergedJSON)
  159. logrus.Debugf(title+" id=%s merged config=%s", id, configuration)
  160. }
  161. var (
  162. resultp *uint16
  163. identity syscall.Handle
  164. )
  165. createError := hcsCreateComputeSystem(id, configuration, identity, &container.handle, &resultp)
  166. if createError == nil || IsPending(createError) {
  167. if err := container.registerCallback(); err != nil {
  168. return nil, makeContainerError(container, operation, "", err)
  169. }
  170. }
  171. err = processAsyncHcsResult(createError, resultp, container.callbackNumber, hcsNotificationSystemCreateCompleted, &defaultTimeout)
  172. if err != nil {
  173. return nil, makeContainerError(container, operation, configuration, err)
  174. }
  175. logrus.Debugf(title+" succeeded id=%s handle=%d", id, container.handle)
  176. return container, nil
  177. }
  178. // mergeMaps recursively merges map `fromMap` into map `ToMap`. Any pre-existing values
  179. // in ToMap are overwritten. Values in fromMap are added to ToMap.
  180. // From http://stackoverflow.com/questions/40491438/merging-two-json-strings-in-golang
  181. func mergeMaps(fromMap, ToMap interface{}) interface{} {
  182. switch fromMap := fromMap.(type) {
  183. case map[string]interface{}:
  184. ToMap, ok := ToMap.(map[string]interface{})
  185. if !ok {
  186. return fromMap
  187. }
  188. for keyToMap, valueToMap := range ToMap {
  189. if valueFromMap, ok := fromMap[keyToMap]; ok {
  190. fromMap[keyToMap] = mergeMaps(valueFromMap, valueToMap)
  191. } else {
  192. fromMap[keyToMap] = valueToMap
  193. }
  194. }
  195. case nil:
  196. // merge(nil, map[string]interface{...}) -> map[string]interface{...}
  197. ToMap, ok := ToMap.(map[string]interface{})
  198. if ok {
  199. return ToMap
  200. }
  201. }
  202. return fromMap
  203. }
  204. // OpenContainer opens an existing container by ID.
  205. func OpenContainer(id string) (Container, error) {
  206. operation := "OpenContainer"
  207. title := "HCSShim::" + operation
  208. logrus.Debugf(title+" id=%s", id)
  209. container := &container{
  210. id: id,
  211. }
  212. var (
  213. handle hcsSystem
  214. resultp *uint16
  215. )
  216. err := hcsOpenComputeSystem(id, &handle, &resultp)
  217. err = processHcsResult(err, resultp)
  218. if err != nil {
  219. return nil, makeContainerError(container, operation, "", err)
  220. }
  221. container.handle = handle
  222. if err := container.registerCallback(); err != nil {
  223. return nil, makeContainerError(container, operation, "", err)
  224. }
  225. logrus.Debugf(title+" succeeded id=%s handle=%d", id, handle)
  226. return container, nil
  227. }
  228. // GetContainers gets a list of the containers on the system that match the query
  229. func GetContainers(q ComputeSystemQuery) ([]ContainerProperties, error) {
  230. operation := "GetContainers"
  231. title := "HCSShim::" + operation
  232. queryb, err := json.Marshal(q)
  233. if err != nil {
  234. return nil, err
  235. }
  236. query := string(queryb)
  237. logrus.Debugf(title+" query=%s", query)
  238. var (
  239. resultp *uint16
  240. computeSystemsp *uint16
  241. )
  242. err = hcsEnumerateComputeSystems(query, &computeSystemsp, &resultp)
  243. err = processHcsResult(err, resultp)
  244. if err != nil {
  245. return nil, err
  246. }
  247. if computeSystemsp == nil {
  248. return nil, ErrUnexpectedValue
  249. }
  250. computeSystemsRaw := convertAndFreeCoTaskMemBytes(computeSystemsp)
  251. computeSystems := []ContainerProperties{}
  252. if err := json.Unmarshal(computeSystemsRaw, &computeSystems); err != nil {
  253. return nil, err
  254. }
  255. logrus.Debugf(title + " succeeded")
  256. return computeSystems, nil
  257. }
  258. // Start synchronously starts the container.
  259. func (container *container) Start() error {
  260. container.handleLock.RLock()
  261. defer container.handleLock.RUnlock()
  262. operation := "Start"
  263. title := "HCSShim::Container::" + operation
  264. logrus.Debugf(title+" id=%s", container.id)
  265. if container.handle == 0 {
  266. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  267. }
  268. var resultp *uint16
  269. err := hcsStartComputeSystem(container.handle, "", &resultp)
  270. err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemStartCompleted, &defaultTimeout)
  271. if err != nil {
  272. return makeContainerError(container, operation, "", err)
  273. }
  274. logrus.Debugf(title+" succeeded id=%s", container.id)
  275. return nil
  276. }
  277. // Shutdown requests a container shutdown, if IsPending() on the error returned is true,
  278. // it may not actually be shut down until Wait() succeeds.
  279. func (container *container) Shutdown() error {
  280. container.handleLock.RLock()
  281. defer container.handleLock.RUnlock()
  282. operation := "Shutdown"
  283. title := "HCSShim::Container::" + operation
  284. logrus.Debugf(title+" id=%s", container.id)
  285. if container.handle == 0 {
  286. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  287. }
  288. var resultp *uint16
  289. err := hcsShutdownComputeSystem(container.handle, "", &resultp)
  290. err = processHcsResult(err, resultp)
  291. if err != nil {
  292. return makeContainerError(container, operation, "", err)
  293. }
  294. logrus.Debugf(title+" succeeded id=%s", container.id)
  295. return nil
  296. }
  297. // Terminate requests a container terminate, if IsPending() on the error returned is true,
  298. // it may not actually be shut down until Wait() succeeds.
  299. func (container *container) Terminate() error {
  300. container.handleLock.RLock()
  301. defer container.handleLock.RUnlock()
  302. operation := "Terminate"
  303. title := "HCSShim::Container::" + operation
  304. logrus.Debugf(title+" id=%s", container.id)
  305. if container.handle == 0 {
  306. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  307. }
  308. var resultp *uint16
  309. err := hcsTerminateComputeSystem(container.handle, "", &resultp)
  310. err = processHcsResult(err, resultp)
  311. if err != nil {
  312. return makeContainerError(container, operation, "", err)
  313. }
  314. logrus.Debugf(title+" succeeded id=%s", container.id)
  315. return nil
  316. }
  317. // Wait synchronously waits for the container to shutdown or terminate.
  318. func (container *container) Wait() error {
  319. operation := "Wait"
  320. title := "HCSShim::Container::" + operation
  321. logrus.Debugf(title+" id=%s", container.id)
  322. err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, nil)
  323. if err != nil {
  324. return makeContainerError(container, operation, "", err)
  325. }
  326. logrus.Debugf(title+" succeeded id=%s", container.id)
  327. return nil
  328. }
  329. // WaitTimeout synchronously waits for the container to terminate or the duration to elapse.
  330. // If the timeout expires, IsTimeout(err) == true
  331. func (container *container) WaitTimeout(timeout time.Duration) error {
  332. operation := "WaitTimeout"
  333. title := "HCSShim::Container::" + operation
  334. logrus.Debugf(title+" id=%s", container.id)
  335. err := waitForNotification(container.callbackNumber, hcsNotificationSystemExited, &timeout)
  336. if err != nil {
  337. return makeContainerError(container, operation, "", err)
  338. }
  339. logrus.Debugf(title+" succeeded id=%s", container.id)
  340. return nil
  341. }
  342. func (container *container) properties(query string) (*ContainerProperties, error) {
  343. var (
  344. resultp *uint16
  345. propertiesp *uint16
  346. )
  347. err := hcsGetComputeSystemProperties(container.handle, query, &propertiesp, &resultp)
  348. err = processHcsResult(err, resultp)
  349. if err != nil {
  350. return nil, err
  351. }
  352. if propertiesp == nil {
  353. return nil, ErrUnexpectedValue
  354. }
  355. propertiesRaw := convertAndFreeCoTaskMemBytes(propertiesp)
  356. properties := &ContainerProperties{}
  357. if err := json.Unmarshal(propertiesRaw, properties); err != nil {
  358. return nil, err
  359. }
  360. return properties, nil
  361. }
  362. // HasPendingUpdates returns true if the container has updates pending to install
  363. func (container *container) HasPendingUpdates() (bool, error) {
  364. container.handleLock.RLock()
  365. defer container.handleLock.RUnlock()
  366. operation := "HasPendingUpdates"
  367. title := "HCSShim::Container::" + operation
  368. logrus.Debugf(title+" id=%s", container.id)
  369. if container.handle == 0 {
  370. return false, makeContainerError(container, operation, "", ErrAlreadyClosed)
  371. }
  372. properties, err := container.properties(pendingUpdatesQuery)
  373. if err != nil {
  374. return false, makeContainerError(container, operation, "", err)
  375. }
  376. logrus.Debugf(title+" succeeded id=%s", container.id)
  377. return properties.AreUpdatesPending, nil
  378. }
  379. // Statistics returns statistics for the container
  380. func (container *container) Statistics() (Statistics, error) {
  381. container.handleLock.RLock()
  382. defer container.handleLock.RUnlock()
  383. operation := "Statistics"
  384. title := "HCSShim::Container::" + operation
  385. logrus.Debugf(title+" id=%s", container.id)
  386. if container.handle == 0 {
  387. return Statistics{}, makeContainerError(container, operation, "", ErrAlreadyClosed)
  388. }
  389. properties, err := container.properties(statisticsQuery)
  390. if err != nil {
  391. return Statistics{}, makeContainerError(container, operation, "", err)
  392. }
  393. logrus.Debugf(title+" succeeded id=%s", container.id)
  394. return properties.Statistics, nil
  395. }
  396. // ProcessList returns an array of ProcessListItems for the container
  397. func (container *container) ProcessList() ([]ProcessListItem, error) {
  398. container.handleLock.RLock()
  399. defer container.handleLock.RUnlock()
  400. operation := "ProcessList"
  401. title := "HCSShim::Container::" + operation
  402. logrus.Debugf(title+" id=%s", container.id)
  403. if container.handle == 0 {
  404. return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
  405. }
  406. properties, err := container.properties(processListQuery)
  407. if err != nil {
  408. return nil, makeContainerError(container, operation, "", err)
  409. }
  410. logrus.Debugf(title+" succeeded id=%s", container.id)
  411. return properties.ProcessList, nil
  412. }
  413. // Pause pauses the execution of the container. This feature is not enabled in TP5.
  414. func (container *container) Pause() error {
  415. container.handleLock.RLock()
  416. defer container.handleLock.RUnlock()
  417. operation := "Pause"
  418. title := "HCSShim::Container::" + operation
  419. logrus.Debugf(title+" id=%s", container.id)
  420. if container.handle == 0 {
  421. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  422. }
  423. var resultp *uint16
  424. err := hcsPauseComputeSystem(container.handle, "", &resultp)
  425. err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemPauseCompleted, &defaultTimeout)
  426. if err != nil {
  427. return makeContainerError(container, operation, "", err)
  428. }
  429. logrus.Debugf(title+" succeeded id=%s", container.id)
  430. return nil
  431. }
  432. // Resume resumes the execution of the container. This feature is not enabled in TP5.
  433. func (container *container) Resume() error {
  434. container.handleLock.RLock()
  435. defer container.handleLock.RUnlock()
  436. operation := "Resume"
  437. title := "HCSShim::Container::" + operation
  438. logrus.Debugf(title+" id=%s", container.id)
  439. if container.handle == 0 {
  440. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  441. }
  442. var resultp *uint16
  443. err := hcsResumeComputeSystem(container.handle, "", &resultp)
  444. err = processAsyncHcsResult(err, resultp, container.callbackNumber, hcsNotificationSystemResumeCompleted, &defaultTimeout)
  445. if err != nil {
  446. return makeContainerError(container, operation, "", err)
  447. }
  448. logrus.Debugf(title+" succeeded id=%s", container.id)
  449. return nil
  450. }
  451. // CreateProcess launches a new process within the container.
  452. func (container *container) CreateProcess(c *ProcessConfig) (Process, error) {
  453. container.handleLock.RLock()
  454. defer container.handleLock.RUnlock()
  455. operation := "CreateProcess"
  456. title := "HCSShim::Container::" + operation
  457. var (
  458. processInfo hcsProcessInformation
  459. processHandle hcsProcess
  460. resultp *uint16
  461. )
  462. if container.handle == 0 {
  463. return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
  464. }
  465. // If we are not emulating a console, ignore any console size passed to us
  466. if !c.EmulateConsole {
  467. c.ConsoleSize[0] = 0
  468. c.ConsoleSize[1] = 0
  469. }
  470. configurationb, err := json.Marshal(c)
  471. if err != nil {
  472. return nil, makeContainerError(container, operation, "", err)
  473. }
  474. configuration := string(configurationb)
  475. logrus.Debugf(title+" id=%s config=%s", container.id, configuration)
  476. err = hcsCreateProcess(container.handle, configuration, &processInfo, &processHandle, &resultp)
  477. err = processHcsResult(err, resultp)
  478. if err != nil {
  479. return nil, makeContainerError(container, operation, configuration, err)
  480. }
  481. process := &process{
  482. handle: processHandle,
  483. processID: int(processInfo.ProcessId),
  484. container: container,
  485. cachedPipes: &cachedPipes{
  486. stdIn: processInfo.StdInput,
  487. stdOut: processInfo.StdOutput,
  488. stdErr: processInfo.StdError,
  489. },
  490. }
  491. if err := process.registerCallback(); err != nil {
  492. return nil, makeContainerError(container, operation, "", err)
  493. }
  494. logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
  495. return process, nil
  496. }
  497. // OpenProcess gets an interface to an existing process within the container.
  498. func (container *container) OpenProcess(pid int) (Process, error) {
  499. container.handleLock.RLock()
  500. defer container.handleLock.RUnlock()
  501. operation := "OpenProcess"
  502. title := "HCSShim::Container::" + operation
  503. logrus.Debugf(title+" id=%s, processid=%d", container.id, pid)
  504. var (
  505. processHandle hcsProcess
  506. resultp *uint16
  507. )
  508. if container.handle == 0 {
  509. return nil, makeContainerError(container, operation, "", ErrAlreadyClosed)
  510. }
  511. err := hcsOpenProcess(container.handle, uint32(pid), &processHandle, &resultp)
  512. err = processHcsResult(err, resultp)
  513. if err != nil {
  514. return nil, makeContainerError(container, operation, "", err)
  515. }
  516. process := &process{
  517. handle: processHandle,
  518. processID: pid,
  519. container: container,
  520. }
  521. if err := process.registerCallback(); err != nil {
  522. return nil, makeContainerError(container, operation, "", err)
  523. }
  524. logrus.Debugf(title+" succeeded id=%s processid=%s", container.id, process.processID)
  525. return process, nil
  526. }
  527. // Close cleans up any state associated with the container but does not terminate or wait for it.
  528. func (container *container) Close() error {
  529. container.handleLock.Lock()
  530. defer container.handleLock.Unlock()
  531. operation := "Close"
  532. title := "HCSShim::Container::" + operation
  533. logrus.Debugf(title+" id=%s", container.id)
  534. // Don't double free this
  535. if container.handle == 0 {
  536. return nil
  537. }
  538. if err := container.unregisterCallback(); err != nil {
  539. return makeContainerError(container, operation, "", err)
  540. }
  541. if err := hcsCloseComputeSystem(container.handle); err != nil {
  542. return makeContainerError(container, operation, "", err)
  543. }
  544. container.handle = 0
  545. logrus.Debugf(title+" succeeded id=%s", container.id)
  546. return nil
  547. }
  548. func (container *container) registerCallback() error {
  549. context := &notifcationWatcherContext{
  550. channels: newChannels(),
  551. }
  552. callbackMapLock.Lock()
  553. callbackNumber := nextCallback
  554. nextCallback++
  555. callbackMap[callbackNumber] = context
  556. callbackMapLock.Unlock()
  557. var callbackHandle hcsCallback
  558. err := hcsRegisterComputeSystemCallback(container.handle, notificationWatcherCallback, callbackNumber, &callbackHandle)
  559. if err != nil {
  560. return err
  561. }
  562. context.handle = callbackHandle
  563. container.callbackNumber = callbackNumber
  564. return nil
  565. }
  566. func (container *container) unregisterCallback() error {
  567. callbackNumber := container.callbackNumber
  568. callbackMapLock.RLock()
  569. context := callbackMap[callbackNumber]
  570. callbackMapLock.RUnlock()
  571. if context == nil {
  572. return nil
  573. }
  574. handle := context.handle
  575. if handle == 0 {
  576. return nil
  577. }
  578. // hcsUnregisterComputeSystemCallback has its own syncronization
  579. // to wait for all callbacks to complete. We must NOT hold the callbackMapLock.
  580. err := hcsUnregisterComputeSystemCallback(handle)
  581. if err != nil {
  582. return err
  583. }
  584. closeChannels(context.channels)
  585. callbackMapLock.Lock()
  586. callbackMap[callbackNumber] = nil
  587. callbackMapLock.Unlock()
  588. handle = 0
  589. return nil
  590. }
  591. // Modifies the System by sending a request to HCS
  592. func (container *container) Modify(config *ResourceModificationRequestResponse) error {
  593. container.handleLock.RLock()
  594. defer container.handleLock.RUnlock()
  595. operation := "Modify"
  596. title := "HCSShim::Container::" + operation
  597. if container.handle == 0 {
  598. return makeContainerError(container, operation, "", ErrAlreadyClosed)
  599. }
  600. requestJSON, err := json.Marshal(config)
  601. if err != nil {
  602. return err
  603. }
  604. requestString := string(requestJSON)
  605. logrus.Debugf(title+" id=%s request=%s", container.id, requestString)
  606. var resultp *uint16
  607. err = hcsModifyComputeSystem(container.handle, requestString, &resultp)
  608. err = processHcsResult(err, resultp)
  609. if err != nil {
  610. return makeContainerError(container, operation, "", err)
  611. }
  612. logrus.Debugf(title+" succeeded id=%s", container.id)
  613. return nil
  614. }