client_windows.go 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io"
  7. "io/ioutil"
  8. "os"
  9. "path/filepath"
  10. "strings"
  11. "syscall"
  12. "time"
  13. "golang.org/x/net/context"
  14. "github.com/Microsoft/hcsshim"
  15. "github.com/Sirupsen/logrus"
  16. "github.com/docker/docker/pkg/sysinfo"
  17. specs "github.com/opencontainers/runtime-spec/specs-go"
  18. )
  19. type client struct {
  20. clientCommon
  21. // Platform specific properties below here (none presently on Windows)
  22. }
  23. // Win32 error codes that are used for various workarounds
  24. // These really should be ALL_CAPS to match golangs syscall library and standard
  25. // Win32 error conventions, but golint insists on CamelCase.
  26. const (
  27. CoEClassstring = syscall.Errno(0x800401F3) // Invalid class string
  28. ErrorNoNetwork = syscall.Errno(1222) // The network is not present or not started
  29. ErrorBadPathname = syscall.Errno(161) // The specified path is invalid
  30. ErrorInvalidObject = syscall.Errno(0x800710D8) // The object identifier does not represent a valid object
  31. )
  32. // defaultOwner is a tag passed to HCS to allow it to differentiate between
  33. // container creator management stacks. We hard code "docker" in the case
  34. // of docker.
  35. const defaultOwner = "docker"
  36. // Create is the entrypoint to create a container from a spec, and if successfully
  37. // created, start it too. Table below shows the fields required for HCS JSON calling parameters,
  38. // where if not populated, is omitted.
  39. // +-----------------+--------------------------------------------+---------------------------------------------------+
  40. // | | Isolation=Process | Isolation=Hyper-V |
  41. // +-----------------+--------------------------------------------+---------------------------------------------------+
  42. // | VolumePath | \\?\\Volume{GUIDa} | |
  43. // | LayerFolderPath | %root%\windowsfilter\containerID | %root%\windowsfilter\containerID (servicing only) |
  44. // | Layers[] | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID |
  45. // | HvRuntime | | ImagePath=%root%\BaseLayerID\UtilityVM |
  46. // +-----------------+--------------------------------------------+---------------------------------------------------+
  47. //
  48. // Isolation=Process example:
  49. //
  50. // {
  51. // "SystemType": "Container",
  52. // "Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
  53. // "Owner": "docker",
  54. // "VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
  55. // "IgnoreFlushesDuringBoot": true,
  56. // "LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
  57. // "Layers": [{
  58. // "ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
  59. // "Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
  60. // }],
  61. // "HostName": "5e0055c814a6",
  62. // "MappedDirectories": [],
  63. // "HvPartition": false,
  64. // "EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
  65. // "Servicing": false
  66. //}
  67. //
  68. // Isolation=Hyper-V example:
  69. //
  70. //{
  71. // "SystemType": "Container",
  72. // "Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
  73. // "Owner": "docker",
  74. // "IgnoreFlushesDuringBoot": true,
  75. // "Layers": [{
  76. // "ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
  77. // "Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
  78. // }],
  79. // "HostName": "475c2c58933b",
  80. // "MappedDirectories": [],
  81. // "HvPartition": true,
  82. // "EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
  83. // "DNSSearchList": "a.com,b.com,c.com",
  84. // "HvRuntime": {
  85. // "ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
  86. // },
  87. // "Servicing": false
  88. //}
  89. func (clnt *client) Create(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) error {
  90. clnt.lock(containerID)
  91. defer clnt.unlock(containerID)
  92. if b, err := json.Marshal(spec); err == nil {
  93. logrus.Debugln("libcontainerd: client.Create() with spec", string(b))
  94. }
  95. osName := spec.Platform.OS
  96. if osName == "windows" {
  97. return clnt.createWindows(containerID, checkpoint, checkpointDir, spec, attachStdio, options...)
  98. }
  99. return clnt.createLinux(containerID, checkpoint, checkpointDir, spec, attachStdio, options...)
  100. }
  101. func (clnt *client) createWindows(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) error {
  102. configuration := &hcsshim.ContainerConfig{
  103. SystemType: "Container",
  104. Name: containerID,
  105. Owner: defaultOwner,
  106. IgnoreFlushesDuringBoot: false,
  107. HostName: spec.Hostname,
  108. HvPartition: false,
  109. }
  110. if spec.Windows.Resources != nil {
  111. if spec.Windows.Resources.CPU != nil {
  112. if spec.Windows.Resources.CPU.Count != nil {
  113. // This check is being done here rather than in adaptContainerSettings
  114. // because we don't want to update the HostConfig in case this container
  115. // is moved to a host with more CPUs than this one.
  116. cpuCount := *spec.Windows.Resources.CPU.Count
  117. hostCPUCount := uint64(sysinfo.NumCPU())
  118. if cpuCount > hostCPUCount {
  119. logrus.Warnf("Changing requested CPUCount of %d to current number of processors, %d", cpuCount, hostCPUCount)
  120. cpuCount = hostCPUCount
  121. }
  122. configuration.ProcessorCount = uint32(cpuCount)
  123. }
  124. if spec.Windows.Resources.CPU.Shares != nil {
  125. configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
  126. }
  127. if spec.Windows.Resources.CPU.Maximum != nil {
  128. configuration.ProcessorMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
  129. }
  130. }
  131. if spec.Windows.Resources.Memory != nil {
  132. if spec.Windows.Resources.Memory.Limit != nil {
  133. configuration.MemoryMaximumInMB = int64(*spec.Windows.Resources.Memory.Limit) / 1024 / 1024
  134. }
  135. }
  136. if spec.Windows.Resources.Storage != nil {
  137. if spec.Windows.Resources.Storage.Bps != nil {
  138. configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
  139. }
  140. if spec.Windows.Resources.Storage.Iops != nil {
  141. configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
  142. }
  143. }
  144. }
  145. var layerOpt *LayerOption
  146. for _, option := range options {
  147. if s, ok := option.(*ServicingOption); ok {
  148. configuration.Servicing = s.IsServicing
  149. continue
  150. }
  151. if f, ok := option.(*FlushOption); ok {
  152. configuration.IgnoreFlushesDuringBoot = f.IgnoreFlushesDuringBoot
  153. continue
  154. }
  155. if h, ok := option.(*HyperVIsolationOption); ok {
  156. configuration.HvPartition = h.IsHyperV
  157. continue
  158. }
  159. if l, ok := option.(*LayerOption); ok {
  160. layerOpt = l
  161. }
  162. if n, ok := option.(*NetworkEndpointsOption); ok {
  163. configuration.EndpointList = n.Endpoints
  164. configuration.AllowUnqualifiedDNSQuery = n.AllowUnqualifiedDNSQuery
  165. if n.DNSSearchList != nil {
  166. configuration.DNSSearchList = strings.Join(n.DNSSearchList, ",")
  167. }
  168. configuration.NetworkSharedContainerName = n.NetworkSharedContainerID
  169. continue
  170. }
  171. if c, ok := option.(*CredentialsOption); ok {
  172. configuration.Credentials = c.Credentials
  173. continue
  174. }
  175. }
  176. // We must have a layer option with at least one path
  177. if layerOpt == nil || layerOpt.LayerPaths == nil {
  178. return fmt.Errorf("no layer option or paths were supplied to the runtime")
  179. }
  180. if configuration.HvPartition {
  181. // Find the upper-most utility VM image, since the utility VM does not
  182. // use layering in RS1.
  183. // TODO @swernli/jhowardmsft at some point post RS1 this may be re-locatable.
  184. var uvmImagePath string
  185. for _, path := range layerOpt.LayerPaths {
  186. fullPath := filepath.Join(path, "UtilityVM")
  187. _, err := os.Stat(fullPath)
  188. if err == nil {
  189. uvmImagePath = fullPath
  190. break
  191. }
  192. if !os.IsNotExist(err) {
  193. return err
  194. }
  195. }
  196. if uvmImagePath == "" {
  197. return errors.New("utility VM image could not be found")
  198. }
  199. configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: uvmImagePath}
  200. } else {
  201. configuration.VolumePath = spec.Root.Path
  202. }
  203. configuration.LayerFolderPath = layerOpt.LayerFolderPath
  204. for _, layerPath := range layerOpt.LayerPaths {
  205. _, filename := filepath.Split(layerPath)
  206. g, err := hcsshim.NameToGuid(filename)
  207. if err != nil {
  208. return err
  209. }
  210. configuration.Layers = append(configuration.Layers, hcsshim.Layer{
  211. ID: g.ToString(),
  212. Path: layerPath,
  213. })
  214. }
  215. // Add the mounts (volumes, bind mounts etc) to the structure
  216. mds := make([]hcsshim.MappedDir, len(spec.Mounts))
  217. for i, mount := range spec.Mounts {
  218. mds[i] = hcsshim.MappedDir{
  219. HostPath: mount.Source,
  220. ContainerPath: mount.Destination,
  221. ReadOnly: false,
  222. }
  223. for _, o := range mount.Options {
  224. if strings.ToLower(o) == "ro" {
  225. mds[i].ReadOnly = true
  226. }
  227. }
  228. }
  229. configuration.MappedDirectories = mds
  230. hcsContainer, err := hcsshim.CreateContainer(containerID, configuration)
  231. if err != nil {
  232. return err
  233. }
  234. // Construct a container object for calling start on it.
  235. container := &container{
  236. containerCommon: containerCommon{
  237. process: process{
  238. processCommon: processCommon{
  239. containerID: containerID,
  240. client: clnt,
  241. friendlyName: InitFriendlyName,
  242. },
  243. },
  244. processes: make(map[string]*process),
  245. },
  246. ociSpec: spec,
  247. hcsContainer: hcsContainer,
  248. }
  249. container.options = options
  250. for _, option := range options {
  251. if err := option.Apply(container); err != nil {
  252. logrus.Errorf("libcontainerd: %v", err)
  253. }
  254. }
  255. // Call start, and if it fails, delete the container from our
  256. // internal structure, start will keep HCS in sync by deleting the
  257. // container there.
  258. logrus.Debugf("libcontainerd: createWindows() id=%s, Calling start()", containerID)
  259. if err := container.start(attachStdio); err != nil {
  260. clnt.deleteContainer(containerID)
  261. return err
  262. }
  263. logrus.Debugf("libcontainerd: createWindows() id=%s completed successfully", containerID)
  264. return nil
  265. }
  266. func (clnt *client) createLinux(containerID string, checkpoint string, checkpointDir string, spec specs.Spec, attachStdio StdioCallback, options ...CreateOption) error {
  267. logrus.Debugf("libcontainerd: createLinux(): containerId %s ", containerID)
  268. // TODO @jhowardmsft LCOW Support: This needs to be configurable, not hard-coded.
  269. // However, good-enough for the LCOW bring-up.
  270. configuration := &hcsshim.ContainerConfig{
  271. HvPartition: true,
  272. Name: containerID,
  273. SystemType: "container",
  274. ContainerType: "linux",
  275. Owner: defaultOwner,
  276. TerminateOnLastHandleClosed: true,
  277. HvRuntime: &hcsshim.HvRuntime{
  278. ImagePath: `c:\program files\lcow`,
  279. LinuxKernelFile: `bootx64.efi`,
  280. LinuxInitrdFile: `initrd.img`,
  281. },
  282. }
  283. var layerOpt *LayerOption
  284. for _, option := range options {
  285. if l, ok := option.(*LayerOption); ok {
  286. layerOpt = l
  287. }
  288. }
  289. // We must have a layer option with at least one path
  290. if layerOpt == nil || layerOpt.LayerPaths == nil {
  291. return fmt.Errorf("no layer option or paths were supplied to the runtime")
  292. }
  293. // LayerFolderPath (writeable layer) + Layers (Guid + path)
  294. configuration.LayerFolderPath = layerOpt.LayerFolderPath
  295. for _, layerPath := range layerOpt.LayerPaths {
  296. _, filename := filepath.Split(layerPath)
  297. g, err := hcsshim.NameToGuid(filename)
  298. if err != nil {
  299. return err
  300. }
  301. configuration.Layers = append(configuration.Layers, hcsshim.Layer{
  302. ID: g.ToString(),
  303. Path: filepath.Join(layerPath, "layer.vhd"),
  304. })
  305. }
  306. for _, option := range options {
  307. if n, ok := option.(*NetworkEndpointsOption); ok {
  308. configuration.EndpointList = n.Endpoints
  309. configuration.AllowUnqualifiedDNSQuery = n.AllowUnqualifiedDNSQuery
  310. if n.DNSSearchList != nil {
  311. configuration.DNSSearchList = strings.Join(n.DNSSearchList, ",")
  312. }
  313. configuration.NetworkSharedContainerName = n.NetworkSharedContainerID
  314. break
  315. }
  316. }
  317. hcsContainer, err := hcsshim.CreateContainer(containerID, configuration)
  318. if err != nil {
  319. return err
  320. }
  321. // Construct a container object for calling start on it.
  322. container := &container{
  323. containerCommon: containerCommon{
  324. process: process{
  325. processCommon: processCommon{
  326. containerID: containerID,
  327. client: clnt,
  328. friendlyName: InitFriendlyName,
  329. },
  330. },
  331. processes: make(map[string]*process),
  332. },
  333. ociSpec: spec,
  334. hcsContainer: hcsContainer,
  335. }
  336. container.options = options
  337. for _, option := range options {
  338. if err := option.Apply(container); err != nil {
  339. logrus.Errorf("libcontainerd: createLinux() %v", err)
  340. }
  341. }
  342. // Call start, and if it fails, delete the container from our
  343. // internal structure, start will keep HCS in sync by deleting the
  344. // container there.
  345. logrus.Debugf("libcontainerd: createLinux() id=%s, Calling start()", containerID)
  346. if err := container.start(attachStdio); err != nil {
  347. clnt.deleteContainer(containerID)
  348. return err
  349. }
  350. logrus.Debugf("libcontainerd: createLinux() id=%s completed successfully", containerID)
  351. return nil
  352. }
  353. // AddProcess is the handler for adding a process to an already running
  354. // container. It's called through docker exec. It returns the system pid of the
  355. // exec'd process.
  356. func (clnt *client) AddProcess(ctx context.Context, containerID, processFriendlyName string, procToAdd Process, attachStdio StdioCallback) (int, error) {
  357. clnt.lock(containerID)
  358. defer clnt.unlock(containerID)
  359. container, err := clnt.getContainer(containerID)
  360. if err != nil {
  361. return -1, err
  362. }
  363. // Note we always tell HCS to
  364. // create stdout as it's required regardless of '-i' or '-t' options, so that
  365. // docker can always grab the output through logs. We also tell HCS to always
  366. // create stdin, even if it's not used - it will be closed shortly. Stderr
  367. // is only created if it we're not -t.
  368. createProcessParms := hcsshim.ProcessConfig{
  369. CreateStdInPipe: true,
  370. CreateStdOutPipe: true,
  371. CreateStdErrPipe: !procToAdd.Terminal,
  372. }
  373. if procToAdd.Terminal {
  374. createProcessParms.EmulateConsole = true
  375. createProcessParms.ConsoleSize[0] = uint(procToAdd.ConsoleSize.Height)
  376. createProcessParms.ConsoleSize[1] = uint(procToAdd.ConsoleSize.Width)
  377. }
  378. // Take working directory from the process to add if it is defined,
  379. // otherwise take from the first process.
  380. if procToAdd.Cwd != "" {
  381. createProcessParms.WorkingDirectory = procToAdd.Cwd
  382. } else {
  383. createProcessParms.WorkingDirectory = container.ociSpec.Process.Cwd
  384. }
  385. // Configure the environment for the process
  386. createProcessParms.Environment = setupEnvironmentVariables(procToAdd.Env)
  387. if container.ociSpec.Platform.OS == "windows" {
  388. createProcessParms.CommandLine = strings.Join(procToAdd.Args, " ")
  389. } else {
  390. createProcessParms.CommandArgs = procToAdd.Args
  391. }
  392. createProcessParms.User = procToAdd.User.Username
  393. logrus.Debugf("libcontainerd: commandLine: %s", createProcessParms.CommandLine)
  394. // Start the command running in the container.
  395. var stdout, stderr io.ReadCloser
  396. var stdin io.WriteCloser
  397. newProcess, err := container.hcsContainer.CreateProcess(&createProcessParms)
  398. if err != nil {
  399. logrus.Errorf("libcontainerd: AddProcess(%s) CreateProcess() failed %s", containerID, err)
  400. return -1, err
  401. }
  402. pid := newProcess.Pid()
  403. stdin, stdout, stderr, err = newProcess.Stdio()
  404. if err != nil {
  405. logrus.Errorf("libcontainerd: %s getting std pipes failed %s", containerID, err)
  406. return -1, err
  407. }
  408. iopipe := &IOPipe{Terminal: procToAdd.Terminal}
  409. iopipe.Stdin = createStdInCloser(stdin, newProcess)
  410. // Convert io.ReadClosers to io.Readers
  411. if stdout != nil {
  412. iopipe.Stdout = ioutil.NopCloser(&autoClosingReader{ReadCloser: stdout})
  413. }
  414. if stderr != nil {
  415. iopipe.Stderr = ioutil.NopCloser(&autoClosingReader{ReadCloser: stderr})
  416. }
  417. proc := &process{
  418. processCommon: processCommon{
  419. containerID: containerID,
  420. friendlyName: processFriendlyName,
  421. client: clnt,
  422. systemPid: uint32(pid),
  423. },
  424. hcsProcess: newProcess,
  425. }
  426. // Add the process to the container's list of processes
  427. container.processes[processFriendlyName] = proc
  428. // Tell the engine to attach streams back to the client
  429. if err := attachStdio(*iopipe); err != nil {
  430. return -1, err
  431. }
  432. // Spin up a go routine waiting for exit to handle cleanup
  433. go container.waitExit(proc, false)
  434. return pid, nil
  435. }
  436. // Signal handles `docker stop` on Windows. While Linux has support for
  437. // the full range of signals, signals aren't really implemented on Windows.
  438. // We fake supporting regular stop and -9 to force kill.
  439. func (clnt *client) Signal(containerID string, sig int) error {
  440. var (
  441. cont *container
  442. err error
  443. )
  444. // Get the container as we need it to get the container handle.
  445. clnt.lock(containerID)
  446. defer clnt.unlock(containerID)
  447. if cont, err = clnt.getContainer(containerID); err != nil {
  448. return err
  449. }
  450. cont.manualStopRequested = true
  451. logrus.Debugf("libcontainerd: Signal() containerID=%s sig=%d pid=%d", containerID, sig, cont.systemPid)
  452. if syscall.Signal(sig) == syscall.SIGKILL {
  453. // Terminate the compute system
  454. if err := cont.hcsContainer.Terminate(); err != nil {
  455. if !hcsshim.IsPending(err) {
  456. logrus.Errorf("libcontainerd: failed to terminate %s - %q", containerID, err)
  457. }
  458. }
  459. } else {
  460. // Shut down the container
  461. if err := cont.hcsContainer.Shutdown(); err != nil {
  462. if !hcsshim.IsPending(err) && !hcsshim.IsAlreadyStopped(err) {
  463. // ignore errors
  464. logrus.Warnf("libcontainerd: failed to shutdown container %s: %q", containerID, err)
  465. }
  466. }
  467. }
  468. return nil
  469. }
  470. // While Linux has support for the full range of signals, signals aren't really implemented on Windows.
  471. // We try to terminate the specified process whatever signal is requested.
  472. func (clnt *client) SignalProcess(containerID string, processFriendlyName string, sig int) error {
  473. clnt.lock(containerID)
  474. defer clnt.unlock(containerID)
  475. cont, err := clnt.getContainer(containerID)
  476. if err != nil {
  477. return err
  478. }
  479. for _, p := range cont.processes {
  480. if p.friendlyName == processFriendlyName {
  481. return p.hcsProcess.Kill()
  482. }
  483. }
  484. return fmt.Errorf("SignalProcess could not find process %s in %s", processFriendlyName, containerID)
  485. }
  486. // Resize handles a CLI event to resize an interactive docker run or docker exec
  487. // window.
  488. func (clnt *client) Resize(containerID, processFriendlyName string, width, height int) error {
  489. // Get the libcontainerd container object
  490. clnt.lock(containerID)
  491. defer clnt.unlock(containerID)
  492. cont, err := clnt.getContainer(containerID)
  493. if err != nil {
  494. return err
  495. }
  496. h, w := uint16(height), uint16(width)
  497. if processFriendlyName == InitFriendlyName {
  498. logrus.Debugln("libcontainerd: resizing systemPID in", containerID, cont.process.systemPid)
  499. return cont.process.hcsProcess.ResizeConsole(w, h)
  500. }
  501. for _, p := range cont.processes {
  502. if p.friendlyName == processFriendlyName {
  503. logrus.Debugln("libcontainerd: resizing exec'd process", containerID, p.systemPid)
  504. return p.hcsProcess.ResizeConsole(w, h)
  505. }
  506. }
  507. return fmt.Errorf("Resize could not find containerID %s to resize", containerID)
  508. }
  509. // Pause handles pause requests for containers
  510. func (clnt *client) Pause(containerID string) error {
  511. unlockContainer := true
  512. // Get the libcontainerd container object
  513. clnt.lock(containerID)
  514. defer func() {
  515. if unlockContainer {
  516. clnt.unlock(containerID)
  517. }
  518. }()
  519. container, err := clnt.getContainer(containerID)
  520. if err != nil {
  521. return err
  522. }
  523. for _, option := range container.options {
  524. if h, ok := option.(*HyperVIsolationOption); ok {
  525. if !h.IsHyperV {
  526. return errors.New("cannot pause Windows Server Containers")
  527. }
  528. break
  529. }
  530. }
  531. err = container.hcsContainer.Pause()
  532. if err != nil {
  533. return err
  534. }
  535. // Unlock container before calling back into the daemon
  536. unlockContainer = false
  537. clnt.unlock(containerID)
  538. return clnt.backend.StateChanged(containerID, StateInfo{
  539. CommonStateInfo: CommonStateInfo{
  540. State: StatePause,
  541. }})
  542. }
  543. // Resume handles resume requests for containers
  544. func (clnt *client) Resume(containerID string) error {
  545. unlockContainer := true
  546. // Get the libcontainerd container object
  547. clnt.lock(containerID)
  548. defer func() {
  549. if unlockContainer {
  550. clnt.unlock(containerID)
  551. }
  552. }()
  553. container, err := clnt.getContainer(containerID)
  554. if err != nil {
  555. return err
  556. }
  557. // This should never happen, since Windows Server Containers cannot be paused
  558. for _, option := range container.options {
  559. if h, ok := option.(*HyperVIsolationOption); ok {
  560. if !h.IsHyperV {
  561. return errors.New("cannot resume Windows Server Containers")
  562. }
  563. break
  564. }
  565. }
  566. err = container.hcsContainer.Resume()
  567. if err != nil {
  568. return err
  569. }
  570. // Unlock container before calling back into the daemon
  571. unlockContainer = false
  572. clnt.unlock(containerID)
  573. return clnt.backend.StateChanged(containerID, StateInfo{
  574. CommonStateInfo: CommonStateInfo{
  575. State: StateResume,
  576. }})
  577. }
  578. // Stats handles stats requests for containers
  579. func (clnt *client) Stats(containerID string) (*Stats, error) {
  580. // Get the libcontainerd container object
  581. clnt.lock(containerID)
  582. defer clnt.unlock(containerID)
  583. container, err := clnt.getContainer(containerID)
  584. if err != nil {
  585. return nil, err
  586. }
  587. s, err := container.hcsContainer.Statistics()
  588. if err != nil {
  589. return nil, err
  590. }
  591. st := Stats(s)
  592. return &st, nil
  593. }
  594. // Restore is the handler for restoring a container
  595. func (clnt *client) Restore(containerID string, _ StdioCallback, unusedOnWindows ...CreateOption) error {
  596. logrus.Debugf("libcontainerd: Restore(%s)", containerID)
  597. // TODO Windows: On RS1, a re-attach isn't possible.
  598. // However, there is a scenario in which there is an issue.
  599. // Consider a background container. The daemon dies unexpectedly.
  600. // HCS will still have the compute service alive and running.
  601. // For consistence, we call in to shoot it regardless if HCS knows about it
  602. // We explicitly just log a warning if the terminate fails.
  603. // Then we tell the backend the container exited.
  604. if hc, err := hcsshim.OpenContainer(containerID); err == nil {
  605. const terminateTimeout = time.Minute * 2
  606. err := hc.Terminate()
  607. if hcsshim.IsPending(err) {
  608. err = hc.WaitTimeout(terminateTimeout)
  609. } else if hcsshim.IsAlreadyStopped(err) {
  610. err = nil
  611. }
  612. if err != nil {
  613. logrus.Warnf("libcontainerd: failed to terminate %s on restore - %q", containerID, err)
  614. return err
  615. }
  616. }
  617. return clnt.backend.StateChanged(containerID, StateInfo{
  618. CommonStateInfo: CommonStateInfo{
  619. State: StateExit,
  620. ExitCode: 1 << 31,
  621. }})
  622. }
  623. // GetPidsForContainer returns a list of process IDs running in a container.
  624. // Not used on Windows.
  625. func (clnt *client) GetPidsForContainer(containerID string) ([]int, error) {
  626. return nil, errors.New("not implemented on Windows")
  627. }
  628. // Summary returns a summary of the processes running in a container.
  629. // This is present in Windows to support docker top. In linux, the
  630. // engine shells out to ps to get process information. On Windows, as
  631. // the containers could be Hyper-V containers, they would not be
  632. // visible on the container host. However, libcontainerd does have
  633. // that information.
  634. func (clnt *client) Summary(containerID string) ([]Summary, error) {
  635. // Get the libcontainerd container object
  636. clnt.lock(containerID)
  637. defer clnt.unlock(containerID)
  638. container, err := clnt.getContainer(containerID)
  639. if err != nil {
  640. return nil, err
  641. }
  642. p, err := container.hcsContainer.ProcessList()
  643. if err != nil {
  644. return nil, err
  645. }
  646. pl := make([]Summary, len(p))
  647. for i := range p {
  648. pl[i] = Summary(p[i])
  649. }
  650. return pl, nil
  651. }
  652. // UpdateResources updates resources for a running container.
  653. func (clnt *client) UpdateResources(containerID string, resources Resources) error {
  654. // Updating resource isn't supported on Windows
  655. // but we should return nil for enabling updating container
  656. return nil
  657. }
  658. func (clnt *client) CreateCheckpoint(containerID string, checkpointID string, checkpointDir string, exit bool) error {
  659. return errors.New("Windows: Containers do not support checkpoints")
  660. }
  661. func (clnt *client) DeleteCheckpoint(containerID string, checkpointID string, checkpointDir string) error {
  662. return errors.New("Windows: Containers do not support checkpoints")
  663. }
  664. func (clnt *client) ListCheckpoints(containerID string, checkpointDir string) (*Checkpoints, error) {
  665. return nil, errors.New("Windows: Containers do not support checkpoints")
  666. }
  667. func (clnt *client) GetServerVersion(ctx context.Context) (*ServerVersion, error) {
  668. return &ServerVersion{}, nil
  669. }