client_local_windows.go 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332
  1. package libcontainerd // import "github.com/docker/docker/libcontainerd"
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "os"
  8. "path"
  9. "path/filepath"
  10. "regexp"
  11. "strings"
  12. "sync"
  13. "syscall"
  14. "time"
  15. "github.com/Microsoft/hcsshim"
  16. opengcs "github.com/Microsoft/opengcs/client"
  17. "github.com/containerd/containerd"
  18. "github.com/containerd/containerd/cio"
  19. "github.com/docker/docker/pkg/sysinfo"
  20. "github.com/docker/docker/pkg/system"
  21. specs "github.com/opencontainers/runtime-spec/specs-go"
  22. "github.com/pkg/errors"
  23. "github.com/sirupsen/logrus"
  24. "golang.org/x/sys/windows"
  25. )
  26. const InitProcessName = "init"
  27. type process struct {
  28. id string
  29. pid int
  30. hcsProcess hcsshim.Process
  31. }
  32. type container struct {
  33. sync.Mutex
  34. // The ociSpec is required, as client.Create() needs a spec, but can
  35. // be called from the RestartManager context which does not otherwise
  36. // have access to the Spec
  37. ociSpec *specs.Spec
  38. isWindows bool
  39. hcsContainer hcsshim.Container
  40. id string
  41. status Status
  42. exitedAt time.Time
  43. exitCode uint32
  44. waitCh chan struct{}
  45. init *process
  46. execs map[string]*process
  47. terminateInvoked bool
  48. }
  49. // Win32 error codes that are used for various workarounds
  50. // These really should be ALL_CAPS to match golangs syscall library and standard
  51. // Win32 error conventions, but golint insists on CamelCase.
  52. const (
  53. CoEClassstring = syscall.Errno(0x800401F3) // Invalid class string
  54. ErrorNoNetwork = syscall.Errno(1222) // The network is not present or not started
  55. ErrorBadPathname = syscall.Errno(161) // The specified path is invalid
  56. ErrorInvalidObject = syscall.Errno(0x800710D8) // The object identifier does not represent a valid object
  57. )
  58. // defaultOwner is a tag passed to HCS to allow it to differentiate between
  59. // container creator management stacks. We hard code "docker" in the case
  60. // of docker.
  61. const defaultOwner = "docker"
  62. func (c *client) Version(ctx context.Context) (containerd.Version, error) {
  63. return containerd.Version{}, errors.New("not implemented on Windows")
  64. }
  65. // Create is the entrypoint to create a container from a spec.
  66. // Table below shows the fields required for HCS JSON calling parameters,
  67. // where if not populated, is omitted.
  68. // +-----------------+--------------------------------------------+---------------------------------------------------+
  69. // | | Isolation=Process | Isolation=Hyper-V |
  70. // +-----------------+--------------------------------------------+---------------------------------------------------+
  71. // | VolumePath | \\?\\Volume{GUIDa} | |
  72. // | LayerFolderPath | %root%\windowsfilter\containerID | |
  73. // | Layers[] | ID=GUIDb;Path=%root%\windowsfilter\layerID | ID=GUIDb;Path=%root%\windowsfilter\layerID |
  74. // | HvRuntime | | ImagePath=%root%\BaseLayerID\UtilityVM |
  75. // +-----------------+--------------------------------------------+---------------------------------------------------+
  76. //
  77. // Isolation=Process example:
  78. //
  79. // {
  80. // "SystemType": "Container",
  81. // "Name": "5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
  82. // "Owner": "docker",
  83. // "VolumePath": "\\\\\\\\?\\\\Volume{66d1ef4c-7a00-11e6-8948-00155ddbef9d}",
  84. // "IgnoreFlushesDuringBoot": true,
  85. // "LayerFolderPath": "C:\\\\control\\\\windowsfilter\\\\5e0055c814a6005b8e57ac59f9a522066e0af12b48b3c26a9416e23907698776",
  86. // "Layers": [{
  87. // "ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
  88. // "Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
  89. // }],
  90. // "HostName": "5e0055c814a6",
  91. // "MappedDirectories": [],
  92. // "HvPartition": false,
  93. // "EndpointList": ["eef2649d-bb17-4d53-9937-295a8efe6f2c"],
  94. //}
  95. //
  96. // Isolation=Hyper-V example:
  97. //
  98. //{
  99. // "SystemType": "Container",
  100. // "Name": "475c2c58933b72687a88a441e7e0ca4bd72d76413c5f9d5031fee83b98f6045d",
  101. // "Owner": "docker",
  102. // "IgnoreFlushesDuringBoot": true,
  103. // "Layers": [{
  104. // "ID": "18955d65-d45a-557b-bf1c-49d6dfefc526",
  105. // "Path": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c"
  106. // }],
  107. // "HostName": "475c2c58933b",
  108. // "MappedDirectories": [],
  109. // "HvPartition": true,
  110. // "EndpointList": ["e1bb1e61-d56f-405e-b75d-fd520cefa0cb"],
  111. // "DNSSearchList": "a.com,b.com,c.com",
  112. // "HvRuntime": {
  113. // "ImagePath": "C:\\\\control\\\\windowsfilter\\\\65bf96e5760a09edf1790cb229e2dfb2dbd0fcdc0bf7451bae099106bfbfea0c\\\\UtilityVM"
  114. // },
  115. //}
  116. func (c *client) Create(_ context.Context, id string, spec *specs.Spec, runtimeOptions interface{}) error {
  117. if ctr := c.getContainer(id); ctr != nil {
  118. return errors.WithStack(newConflictError("id already in use"))
  119. }
  120. // spec.Linux must be nil for Windows containers, but spec.Windows
  121. // will be filled in regardless of container platform. This is a
  122. // temporary workaround due to LCOW requiring layer folder paths,
  123. // which are stored under spec.Windows.
  124. //
  125. // TODO: @darrenstahlmsft fix this once the OCI spec is updated to
  126. // support layer folder paths for LCOW
  127. if spec.Linux == nil {
  128. return c.createWindows(id, spec, runtimeOptions)
  129. }
  130. return c.createLinux(id, spec, runtimeOptions)
  131. }
  132. func (c *client) createWindows(id string, spec *specs.Spec, runtimeOptions interface{}) error {
  133. logger := c.logger.WithField("container", id)
  134. configuration := &hcsshim.ContainerConfig{
  135. SystemType: "Container",
  136. Name: id,
  137. Owner: defaultOwner,
  138. IgnoreFlushesDuringBoot: spec.Windows.IgnoreFlushesDuringBoot,
  139. HostName: spec.Hostname,
  140. HvPartition: false,
  141. }
  142. if spec.Windows.Resources != nil {
  143. if spec.Windows.Resources.CPU != nil {
  144. if spec.Windows.Resources.CPU.Count != nil {
  145. // This check is being done here rather than in adaptContainerSettings
  146. // because we don't want to update the HostConfig in case this container
  147. // is moved to a host with more CPUs than this one.
  148. cpuCount := *spec.Windows.Resources.CPU.Count
  149. hostCPUCount := uint64(sysinfo.NumCPU())
  150. if cpuCount > hostCPUCount {
  151. c.logger.Warnf("Changing requested CPUCount of %d to current number of processors, %d", cpuCount, hostCPUCount)
  152. cpuCount = hostCPUCount
  153. }
  154. configuration.ProcessorCount = uint32(cpuCount)
  155. }
  156. if spec.Windows.Resources.CPU.Shares != nil {
  157. configuration.ProcessorWeight = uint64(*spec.Windows.Resources.CPU.Shares)
  158. }
  159. if spec.Windows.Resources.CPU.Maximum != nil {
  160. configuration.ProcessorMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
  161. }
  162. }
  163. if spec.Windows.Resources.Memory != nil {
  164. if spec.Windows.Resources.Memory.Limit != nil {
  165. configuration.MemoryMaximumInMB = int64(*spec.Windows.Resources.Memory.Limit) / 1024 / 1024
  166. }
  167. }
  168. if spec.Windows.Resources.Storage != nil {
  169. if spec.Windows.Resources.Storage.Bps != nil {
  170. configuration.StorageBandwidthMaximum = *spec.Windows.Resources.Storage.Bps
  171. }
  172. if spec.Windows.Resources.Storage.Iops != nil {
  173. configuration.StorageIOPSMaximum = *spec.Windows.Resources.Storage.Iops
  174. }
  175. }
  176. }
  177. if spec.Windows.HyperV != nil {
  178. configuration.HvPartition = true
  179. }
  180. if spec.Windows.Network != nil {
  181. configuration.EndpointList = spec.Windows.Network.EndpointList
  182. configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
  183. if spec.Windows.Network.DNSSearchList != nil {
  184. configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
  185. }
  186. configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
  187. }
  188. if cs, ok := spec.Windows.CredentialSpec.(string); ok {
  189. configuration.Credentials = cs
  190. }
  191. // We must have least two layers in the spec, the bottom one being a
  192. // base image, the top one being the RW layer.
  193. if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) < 2 {
  194. return fmt.Errorf("OCI spec is invalid - at least two LayerFolders must be supplied to the runtime")
  195. }
  196. // Strip off the top-most layer as that's passed in separately to HCS
  197. configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
  198. layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
  199. if configuration.HvPartition {
  200. // We don't currently support setting the utility VM image explicitly.
  201. // TODO @swernli/jhowardmsft circa RS5, this may be re-locatable.
  202. if spec.Windows.HyperV.UtilityVMPath != "" {
  203. return errors.New("runtime does not support an explicit utility VM path for Hyper-V containers")
  204. }
  205. // Find the upper-most utility VM image.
  206. var uvmImagePath string
  207. for _, path := range layerFolders {
  208. fullPath := filepath.Join(path, "UtilityVM")
  209. _, err := os.Stat(fullPath)
  210. if err == nil {
  211. uvmImagePath = fullPath
  212. break
  213. }
  214. if !os.IsNotExist(err) {
  215. return err
  216. }
  217. }
  218. if uvmImagePath == "" {
  219. return errors.New("utility VM image could not be found")
  220. }
  221. configuration.HvRuntime = &hcsshim.HvRuntime{ImagePath: uvmImagePath}
  222. if spec.Root.Path != "" {
  223. return errors.New("OCI spec is invalid - Root.Path must be omitted for a Hyper-V container")
  224. }
  225. } else {
  226. const volumeGUIDRegex = `^\\\\\?\\(Volume)\{{0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}\}\\$`
  227. if _, err := regexp.MatchString(volumeGUIDRegex, spec.Root.Path); err != nil {
  228. return fmt.Errorf(`OCI spec is invalid - Root.Path '%s' must be a volume GUID path in the format '\\?\Volume{GUID}\'`, spec.Root.Path)
  229. }
  230. // HCS API requires the trailing backslash to be removed
  231. configuration.VolumePath = spec.Root.Path[:len(spec.Root.Path)-1]
  232. }
  233. if spec.Root.Readonly {
  234. return errors.New(`OCI spec is invalid - Root.Readonly must not be set on Windows`)
  235. }
  236. for _, layerPath := range layerFolders {
  237. _, filename := filepath.Split(layerPath)
  238. g, err := hcsshim.NameToGuid(filename)
  239. if err != nil {
  240. return err
  241. }
  242. configuration.Layers = append(configuration.Layers, hcsshim.Layer{
  243. ID: g.ToString(),
  244. Path: layerPath,
  245. })
  246. }
  247. // Add the mounts (volumes, bind mounts etc) to the structure
  248. var mds []hcsshim.MappedDir
  249. var mps []hcsshim.MappedPipe
  250. for _, mount := range spec.Mounts {
  251. const pipePrefix = `\\.\pipe\`
  252. if mount.Type != "" {
  253. return fmt.Errorf("OCI spec is invalid - Mount.Type '%s' must not be set", mount.Type)
  254. }
  255. if strings.HasPrefix(mount.Destination, pipePrefix) {
  256. mp := hcsshim.MappedPipe{
  257. HostPath: mount.Source,
  258. ContainerPipeName: mount.Destination[len(pipePrefix):],
  259. }
  260. mps = append(mps, mp)
  261. } else {
  262. md := hcsshim.MappedDir{
  263. HostPath: mount.Source,
  264. ContainerPath: mount.Destination,
  265. ReadOnly: false,
  266. }
  267. for _, o := range mount.Options {
  268. if strings.ToLower(o) == "ro" {
  269. md.ReadOnly = true
  270. }
  271. }
  272. mds = append(mds, md)
  273. }
  274. }
  275. configuration.MappedDirectories = mds
  276. if len(mps) > 0 && system.GetOSVersion().Build < 16299 { // RS3
  277. return errors.New("named pipe mounts are not supported on this version of Windows")
  278. }
  279. configuration.MappedPipes = mps
  280. hcsContainer, err := hcsshim.CreateContainer(id, configuration)
  281. if err != nil {
  282. return err
  283. }
  284. // Construct a container object for calling start on it.
  285. ctr := &container{
  286. id: id,
  287. execs: make(map[string]*process),
  288. isWindows: true,
  289. ociSpec: spec,
  290. hcsContainer: hcsContainer,
  291. status: StatusCreated,
  292. waitCh: make(chan struct{}),
  293. }
  294. logger.Debug("starting container")
  295. if err = hcsContainer.Start(); err != nil {
  296. c.logger.WithError(err).Error("failed to start container")
  297. ctr.Lock()
  298. if err := c.terminateContainer(ctr); err != nil {
  299. c.logger.WithError(err).Error("failed to cleanup after a failed Start")
  300. } else {
  301. c.logger.Debug("cleaned up after failed Start by calling Terminate")
  302. }
  303. ctr.Unlock()
  304. return err
  305. }
  306. c.Lock()
  307. c.containers[id] = ctr
  308. c.Unlock()
  309. logger.Debug("createWindows() completed successfully")
  310. return nil
  311. }
  312. func (c *client) createLinux(id string, spec *specs.Spec, runtimeOptions interface{}) error {
  313. logrus.Debugf("libcontainerd: createLinux(): containerId %s ", id)
  314. logger := c.logger.WithField("container", id)
  315. if runtimeOptions == nil {
  316. return fmt.Errorf("lcow option must be supplied to the runtime")
  317. }
  318. lcowConfig, ok := runtimeOptions.(*opengcs.Config)
  319. if !ok {
  320. return fmt.Errorf("lcow option must be supplied to the runtime")
  321. }
  322. configuration := &hcsshim.ContainerConfig{
  323. HvPartition: true,
  324. Name: id,
  325. SystemType: "container",
  326. ContainerType: "linux",
  327. Owner: defaultOwner,
  328. TerminateOnLastHandleClosed: true,
  329. }
  330. if lcowConfig.ActualMode == opengcs.ModeActualVhdx {
  331. configuration.HvRuntime = &hcsshim.HvRuntime{
  332. ImagePath: lcowConfig.Vhdx,
  333. BootSource: "Vhd",
  334. WritableBootSource: false,
  335. }
  336. } else {
  337. configuration.HvRuntime = &hcsshim.HvRuntime{
  338. ImagePath: lcowConfig.KirdPath,
  339. LinuxKernelFile: lcowConfig.KernelFile,
  340. LinuxInitrdFile: lcowConfig.InitrdFile,
  341. LinuxBootParameters: lcowConfig.BootParameters,
  342. }
  343. }
  344. if spec.Windows == nil {
  345. return fmt.Errorf("spec.Windows must not be nil for LCOW containers")
  346. }
  347. // We must have least one layer in the spec
  348. if spec.Windows.LayerFolders == nil || len(spec.Windows.LayerFolders) == 0 {
  349. return fmt.Errorf("OCI spec is invalid - at least one LayerFolders must be supplied to the runtime")
  350. }
  351. // Strip off the top-most layer as that's passed in separately to HCS
  352. configuration.LayerFolderPath = spec.Windows.LayerFolders[len(spec.Windows.LayerFolders)-1]
  353. layerFolders := spec.Windows.LayerFolders[:len(spec.Windows.LayerFolders)-1]
  354. for _, layerPath := range layerFolders {
  355. _, filename := filepath.Split(layerPath)
  356. g, err := hcsshim.NameToGuid(filename)
  357. if err != nil {
  358. return err
  359. }
  360. configuration.Layers = append(configuration.Layers, hcsshim.Layer{
  361. ID: g.ToString(),
  362. Path: filepath.Join(layerPath, "layer.vhd"),
  363. })
  364. }
  365. if spec.Windows.Network != nil {
  366. configuration.EndpointList = spec.Windows.Network.EndpointList
  367. configuration.AllowUnqualifiedDNSQuery = spec.Windows.Network.AllowUnqualifiedDNSQuery
  368. if spec.Windows.Network.DNSSearchList != nil {
  369. configuration.DNSSearchList = strings.Join(spec.Windows.Network.DNSSearchList, ",")
  370. }
  371. configuration.NetworkSharedContainerName = spec.Windows.Network.NetworkSharedContainerName
  372. }
  373. // Add the mounts (volumes, bind mounts etc) to the structure. We have to do
  374. // some translation for both the mapped directories passed into HCS and in
  375. // the spec.
  376. //
  377. // For HCS, we only pass in the mounts from the spec which are type "bind".
  378. // Further, the "ContainerPath" field (which is a little mis-leadingly
  379. // named when it applies to the utility VM rather than the container in the
  380. // utility VM) is moved to under /tmp/gcs/<ID>/binds, where this is passed
  381. // by the caller through a 'uvmpath' option.
  382. //
  383. // We do similar translation for the mounts in the spec by stripping out
  384. // the uvmpath option, and translating the Source path to the location in the
  385. // utility VM calculated above.
  386. //
  387. // From inside the utility VM, you would see a 9p mount such as in the following
  388. // where a host folder has been mapped to /target. The line with /tmp/gcs/<ID>/binds
  389. // specifically:
  390. //
  391. // / # mount
  392. // rootfs on / type rootfs (rw,size=463736k,nr_inodes=115934)
  393. // proc on /proc type proc (rw,relatime)
  394. // sysfs on /sys type sysfs (rw,relatime)
  395. // udev on /dev type devtmpfs (rw,relatime,size=498100k,nr_inodes=124525,mode=755)
  396. // tmpfs on /run type tmpfs (rw,relatime)
  397. // cgroup on /sys/fs/cgroup type cgroup (rw,relatime,cpuset,cpu,cpuacct,blkio,memory,devices,freezer,net_cls,perf_event,net_prio,hugetlb,pids,rdma)
  398. // mqueue on /dev/mqueue type mqueue (rw,relatime)
  399. // devpts on /dev/pts type devpts (rw,relatime,mode=600,ptmxmode=000)
  400. // /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target on /binds/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/target type 9p (rw,sync,dirsync,relatime,trans=fd,rfdno=6,wfdno=6)
  401. // /dev/pmem0 on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0 type ext4 (ro,relatime,block_validity,delalloc,norecovery,barrier,dax,user_xattr,acl)
  402. // /dev/sda on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch type ext4 (rw,relatime,block_validity,delalloc,barrier,user_xattr,acl)
  403. // overlay on /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/rootfs type overlay (rw,relatime,lowerdir=/tmp/base/:/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/layer0,upperdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/upper,workdir=/tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc/scratch/work)
  404. //
  405. // /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l
  406. // total 16
  407. // drwx------ 3 0 0 60 Sep 7 18:54 binds
  408. // -rw-r--r-- 1 0 0 3345 Sep 7 18:54 config.json
  409. // drwxr-xr-x 10 0 0 4096 Sep 6 17:26 layer0
  410. // drwxr-xr-x 1 0 0 4096 Sep 7 18:54 rootfs
  411. // drwxr-xr-x 5 0 0 4096 Sep 7 18:54 scratch
  412. //
  413. // /tmp/gcs/b3ea9126d67702173647ece2744f7c11181c0150e9890fc9a431849838033edc # ls -l binds
  414. // total 0
  415. // drwxrwxrwt 2 0 0 4096 Sep 7 16:51 target
  416. mds := []hcsshim.MappedDir{}
  417. specMounts := []specs.Mount{}
  418. for _, mount := range spec.Mounts {
  419. specMount := mount
  420. if mount.Type == "bind" {
  421. // Strip out the uvmpath from the options
  422. updatedOptions := []string{}
  423. uvmPath := ""
  424. readonly := false
  425. for _, opt := range mount.Options {
  426. dropOption := false
  427. elements := strings.SplitN(opt, "=", 2)
  428. switch elements[0] {
  429. case "uvmpath":
  430. uvmPath = elements[1]
  431. dropOption = true
  432. case "rw":
  433. case "ro":
  434. readonly = true
  435. case "rbind":
  436. default:
  437. return fmt.Errorf("unsupported option %q", opt)
  438. }
  439. if !dropOption {
  440. updatedOptions = append(updatedOptions, opt)
  441. }
  442. }
  443. mount.Options = updatedOptions
  444. if uvmPath == "" {
  445. return fmt.Errorf("no uvmpath for bind mount %+v", mount)
  446. }
  447. md := hcsshim.MappedDir{
  448. HostPath: mount.Source,
  449. ContainerPath: path.Join(uvmPath, mount.Destination),
  450. CreateInUtilityVM: true,
  451. ReadOnly: readonly,
  452. }
  453. // If we are 1803/RS4+ enable LinuxMetadata support by default
  454. if system.GetOSVersion().Build >= 17134 {
  455. md.LinuxMetadata = true
  456. }
  457. mds = append(mds, md)
  458. specMount.Source = path.Join(uvmPath, mount.Destination)
  459. }
  460. specMounts = append(specMounts, specMount)
  461. }
  462. configuration.MappedDirectories = mds
  463. hcsContainer, err := hcsshim.CreateContainer(id, configuration)
  464. if err != nil {
  465. return err
  466. }
  467. spec.Mounts = specMounts
  468. // Construct a container object for calling start on it.
  469. ctr := &container{
  470. id: id,
  471. execs: make(map[string]*process),
  472. isWindows: false,
  473. ociSpec: spec,
  474. hcsContainer: hcsContainer,
  475. status: StatusCreated,
  476. waitCh: make(chan struct{}),
  477. }
  478. // Start the container.
  479. logger.Debug("starting container")
  480. if err = hcsContainer.Start(); err != nil {
  481. c.logger.WithError(err).Error("failed to start container")
  482. ctr.debugGCS()
  483. ctr.Lock()
  484. if err := c.terminateContainer(ctr); err != nil {
  485. c.logger.WithError(err).Error("failed to cleanup after a failed Start")
  486. } else {
  487. c.logger.Debug("cleaned up after failed Start by calling Terminate")
  488. }
  489. ctr.Unlock()
  490. return err
  491. }
  492. ctr.debugGCS()
  493. c.Lock()
  494. c.containers[id] = ctr
  495. c.Unlock()
  496. c.eventQ.append(id, func() {
  497. ei := EventInfo{
  498. ContainerID: id,
  499. }
  500. c.logger.WithFields(logrus.Fields{
  501. "container": ctr.id,
  502. "event": EventCreate,
  503. }).Info("sending event")
  504. err := c.backend.ProcessEvent(id, EventCreate, ei)
  505. if err != nil {
  506. c.logger.WithError(err).WithFields(logrus.Fields{
  507. "container": id,
  508. "event": EventCreate,
  509. }).Error("failed to process event")
  510. }
  511. })
  512. logger.Debug("createLinux() completed successfully")
  513. return nil
  514. }
  515. func (c *client) Start(_ context.Context, id, _ string, withStdin bool, attachStdio StdioCallback) (int, error) {
  516. ctr := c.getContainer(id)
  517. switch {
  518. case ctr == nil:
  519. return -1, errors.WithStack(newNotFoundError("no such container"))
  520. case ctr.init != nil:
  521. return -1, errors.WithStack(newConflictError("container already started"))
  522. }
  523. logger := c.logger.WithField("container", id)
  524. // Note we always tell HCS to create stdout as it's required
  525. // regardless of '-i' or '-t' options, so that docker can always grab
  526. // the output through logs. We also tell HCS to always create stdin,
  527. // even if it's not used - it will be closed shortly. Stderr is only
  528. // created if it we're not -t.
  529. var (
  530. emulateConsole bool
  531. createStdErrPipe bool
  532. )
  533. if ctr.ociSpec.Process != nil {
  534. emulateConsole = ctr.ociSpec.Process.Terminal
  535. createStdErrPipe = !ctr.ociSpec.Process.Terminal
  536. }
  537. createProcessParms := &hcsshim.ProcessConfig{
  538. EmulateConsole: emulateConsole,
  539. WorkingDirectory: ctr.ociSpec.Process.Cwd,
  540. CreateStdInPipe: true,
  541. CreateStdOutPipe: true,
  542. CreateStdErrPipe: createStdErrPipe,
  543. }
  544. if ctr.ociSpec.Process != nil && ctr.ociSpec.Process.ConsoleSize != nil {
  545. createProcessParms.ConsoleSize[0] = uint(ctr.ociSpec.Process.ConsoleSize.Height)
  546. createProcessParms.ConsoleSize[1] = uint(ctr.ociSpec.Process.ConsoleSize.Width)
  547. }
  548. // Configure the environment for the process
  549. createProcessParms.Environment = setupEnvironmentVariables(ctr.ociSpec.Process.Env)
  550. if ctr.isWindows {
  551. createProcessParms.CommandLine = strings.Join(ctr.ociSpec.Process.Args, " ")
  552. } else {
  553. createProcessParms.CommandArgs = ctr.ociSpec.Process.Args
  554. }
  555. createProcessParms.User = ctr.ociSpec.Process.User.Username
  556. // LCOW requires the raw OCI spec passed through HCS and onwards to
  557. // GCS for the utility VM.
  558. if !ctr.isWindows {
  559. ociBuf, err := json.Marshal(ctr.ociSpec)
  560. if err != nil {
  561. return -1, err
  562. }
  563. ociRaw := json.RawMessage(ociBuf)
  564. createProcessParms.OCISpecification = &ociRaw
  565. }
  566. ctr.Lock()
  567. defer ctr.Unlock()
  568. // Start the command running in the container.
  569. newProcess, err := ctr.hcsContainer.CreateProcess(createProcessParms)
  570. if err != nil {
  571. logger.WithError(err).Error("CreateProcess() failed")
  572. return -1, err
  573. }
  574. defer func() {
  575. if err != nil {
  576. if err := newProcess.Kill(); err != nil {
  577. logger.WithError(err).Error("failed to kill process")
  578. }
  579. go func() {
  580. if err := newProcess.Wait(); err != nil {
  581. logger.WithError(err).Error("failed to wait for process")
  582. }
  583. if err := newProcess.Close(); err != nil {
  584. logger.WithError(err).Error("failed to clean process resources")
  585. }
  586. }()
  587. }
  588. }()
  589. p := &process{
  590. hcsProcess: newProcess,
  591. id: InitProcessName,
  592. pid: newProcess.Pid(),
  593. }
  594. logger.WithField("pid", p.pid).Debug("init process started")
  595. dio, err := newIOFromProcess(newProcess, ctr.ociSpec.Process.Terminal)
  596. if err != nil {
  597. logger.WithError(err).Error("failed to get stdio pipes")
  598. return -1, err
  599. }
  600. _, err = attachStdio(dio)
  601. if err != nil {
  602. logger.WithError(err).Error("failed to attache stdio")
  603. return -1, err
  604. }
  605. ctr.status = StatusRunning
  606. ctr.init = p
  607. // Spin up a go routine waiting for exit to handle cleanup
  608. go c.reapProcess(ctr, p)
  609. // Generate the associated event
  610. c.eventQ.append(id, func() {
  611. ei := EventInfo{
  612. ContainerID: id,
  613. ProcessID: InitProcessName,
  614. Pid: uint32(p.pid),
  615. }
  616. c.logger.WithFields(logrus.Fields{
  617. "container": ctr.id,
  618. "event": EventStart,
  619. "event-info": ei,
  620. }).Info("sending event")
  621. err := c.backend.ProcessEvent(ei.ContainerID, EventStart, ei)
  622. if err != nil {
  623. c.logger.WithError(err).WithFields(logrus.Fields{
  624. "container": id,
  625. "event": EventStart,
  626. "event-info": ei,
  627. }).Error("failed to process event")
  628. }
  629. })
  630. logger.Debug("start() completed")
  631. return p.pid, nil
  632. }
  633. func newIOFromProcess(newProcess hcsshim.Process, terminal bool) (*cio.DirectIO, error) {
  634. stdin, stdout, stderr, err := newProcess.Stdio()
  635. if err != nil {
  636. return nil, err
  637. }
  638. dio := cio.NewDirectIO(createStdInCloser(stdin, newProcess), nil, nil, terminal)
  639. // Convert io.ReadClosers to io.Readers
  640. if stdout != nil {
  641. dio.Stdout = ioutil.NopCloser(&autoClosingReader{ReadCloser: stdout})
  642. }
  643. if stderr != nil {
  644. dio.Stderr = ioutil.NopCloser(&autoClosingReader{ReadCloser: stderr})
  645. }
  646. return dio, nil
  647. }
  648. // Exec adds a process in an running container
  649. func (c *client) Exec(ctx context.Context, containerID, processID string, spec *specs.Process, withStdin bool, attachStdio StdioCallback) (int, error) {
  650. ctr := c.getContainer(containerID)
  651. switch {
  652. case ctr == nil:
  653. return -1, errors.WithStack(newNotFoundError("no such container"))
  654. case ctr.hcsContainer == nil:
  655. return -1, errors.WithStack(newInvalidParameterError("container is not running"))
  656. case ctr.execs != nil && ctr.execs[processID] != nil:
  657. return -1, errors.WithStack(newConflictError("id already in use"))
  658. }
  659. logger := c.logger.WithFields(logrus.Fields{
  660. "container": containerID,
  661. "exec": processID,
  662. })
  663. // Note we always tell HCS to
  664. // create stdout as it's required regardless of '-i' or '-t' options, so that
  665. // docker can always grab the output through logs. We also tell HCS to always
  666. // create stdin, even if it's not used - it will be closed shortly. Stderr
  667. // is only created if it we're not -t.
  668. createProcessParms := hcsshim.ProcessConfig{
  669. CreateStdInPipe: true,
  670. CreateStdOutPipe: true,
  671. CreateStdErrPipe: !spec.Terminal,
  672. }
  673. if spec.Terminal {
  674. createProcessParms.EmulateConsole = true
  675. if spec.ConsoleSize != nil {
  676. createProcessParms.ConsoleSize[0] = uint(spec.ConsoleSize.Height)
  677. createProcessParms.ConsoleSize[1] = uint(spec.ConsoleSize.Width)
  678. }
  679. }
  680. // Take working directory from the process to add if it is defined,
  681. // otherwise take from the first process.
  682. if spec.Cwd != "" {
  683. createProcessParms.WorkingDirectory = spec.Cwd
  684. } else {
  685. createProcessParms.WorkingDirectory = ctr.ociSpec.Process.Cwd
  686. }
  687. // Configure the environment for the process
  688. createProcessParms.Environment = setupEnvironmentVariables(spec.Env)
  689. if ctr.isWindows {
  690. createProcessParms.CommandLine = strings.Join(spec.Args, " ")
  691. } else {
  692. createProcessParms.CommandArgs = spec.Args
  693. }
  694. createProcessParms.User = spec.User.Username
  695. logger.Debugf("exec commandLine: %s", createProcessParms.CommandLine)
  696. // Start the command running in the container.
  697. newProcess, err := ctr.hcsContainer.CreateProcess(&createProcessParms)
  698. if err != nil {
  699. logger.WithError(err).Errorf("exec's CreateProcess() failed")
  700. return -1, err
  701. }
  702. pid := newProcess.Pid()
  703. defer func() {
  704. if err != nil {
  705. if err := newProcess.Kill(); err != nil {
  706. logger.WithError(err).Error("failed to kill process")
  707. }
  708. go func() {
  709. if err := newProcess.Wait(); err != nil {
  710. logger.WithError(err).Error("failed to wait for process")
  711. }
  712. if err := newProcess.Close(); err != nil {
  713. logger.WithError(err).Error("failed to clean process resources")
  714. }
  715. }()
  716. }
  717. }()
  718. dio, err := newIOFromProcess(newProcess, spec.Terminal)
  719. if err != nil {
  720. logger.WithError(err).Error("failed to get stdio pipes")
  721. return -1, err
  722. }
  723. // Tell the engine to attach streams back to the client
  724. _, err = attachStdio(dio)
  725. if err != nil {
  726. return -1, err
  727. }
  728. p := &process{
  729. id: processID,
  730. pid: pid,
  731. hcsProcess: newProcess,
  732. }
  733. // Add the process to the container's list of processes
  734. ctr.Lock()
  735. ctr.execs[processID] = p
  736. ctr.Unlock()
  737. // Spin up a go routine waiting for exit to handle cleanup
  738. go c.reapProcess(ctr, p)
  739. c.eventQ.append(ctr.id, func() {
  740. ei := EventInfo{
  741. ContainerID: ctr.id,
  742. ProcessID: p.id,
  743. Pid: uint32(p.pid),
  744. }
  745. c.logger.WithFields(logrus.Fields{
  746. "container": ctr.id,
  747. "event": EventExecAdded,
  748. "event-info": ei,
  749. }).Info("sending event")
  750. err := c.backend.ProcessEvent(ctr.id, EventExecAdded, ei)
  751. if err != nil {
  752. c.logger.WithError(err).WithFields(logrus.Fields{
  753. "container": ctr.id,
  754. "event": EventExecAdded,
  755. "event-info": ei,
  756. }).Error("failed to process event")
  757. }
  758. err = c.backend.ProcessEvent(ctr.id, EventExecStarted, ei)
  759. if err != nil {
  760. c.logger.WithError(err).WithFields(logrus.Fields{
  761. "container": ctr.id,
  762. "event": EventExecStarted,
  763. "event-info": ei,
  764. }).Error("failed to process event")
  765. }
  766. })
  767. return pid, nil
  768. }
  769. // Signal handles `docker stop` on Windows. While Linux has support for
  770. // the full range of signals, signals aren't really implemented on Windows.
  771. // We fake supporting regular stop and -9 to force kill.
  772. func (c *client) SignalProcess(_ context.Context, containerID, processID string, signal int) error {
  773. ctr, p, err := c.getProcess(containerID, processID)
  774. if err != nil {
  775. return err
  776. }
  777. logger := c.logger.WithFields(logrus.Fields{
  778. "container": containerID,
  779. "process": processID,
  780. "pid": p.pid,
  781. "signal": signal,
  782. })
  783. logger.Debug("Signal()")
  784. if processID == InitProcessName {
  785. if syscall.Signal(signal) == syscall.SIGKILL {
  786. // Terminate the compute system
  787. ctr.Lock()
  788. ctr.terminateInvoked = true
  789. if err := ctr.hcsContainer.Terminate(); err != nil {
  790. if !hcsshim.IsPending(err) {
  791. logger.WithError(err).Error("failed to terminate hccshim container")
  792. }
  793. }
  794. ctr.Unlock()
  795. } else {
  796. // Shut down the container
  797. if err := ctr.hcsContainer.Shutdown(); err != nil {
  798. if !hcsshim.IsPending(err) && !hcsshim.IsAlreadyStopped(err) {
  799. // ignore errors
  800. logger.WithError(err).Error("failed to shutdown hccshim container")
  801. }
  802. }
  803. }
  804. } else {
  805. return p.hcsProcess.Kill()
  806. }
  807. return nil
  808. }
  809. // Resize handles a CLI event to resize an interactive docker run or docker
  810. // exec window.
  811. func (c *client) ResizeTerminal(_ context.Context, containerID, processID string, width, height int) error {
  812. _, p, err := c.getProcess(containerID, processID)
  813. if err != nil {
  814. return err
  815. }
  816. c.logger.WithFields(logrus.Fields{
  817. "container": containerID,
  818. "process": processID,
  819. "height": height,
  820. "width": width,
  821. "pid": p.pid,
  822. }).Debug("resizing")
  823. return p.hcsProcess.ResizeConsole(uint16(width), uint16(height))
  824. }
  825. func (c *client) CloseStdin(_ context.Context, containerID, processID string) error {
  826. _, p, err := c.getProcess(containerID, processID)
  827. if err != nil {
  828. return err
  829. }
  830. return p.hcsProcess.CloseStdin()
  831. }
  832. // Pause handles pause requests for containers
  833. func (c *client) Pause(_ context.Context, containerID string) error {
  834. ctr, _, err := c.getProcess(containerID, InitProcessName)
  835. if err != nil {
  836. return err
  837. }
  838. if ctr.ociSpec.Windows.HyperV == nil {
  839. return errors.New("cannot pause Windows Server Containers")
  840. }
  841. ctr.Lock()
  842. defer ctr.Unlock()
  843. if err = ctr.hcsContainer.Pause(); err != nil {
  844. return err
  845. }
  846. ctr.status = StatusPaused
  847. c.eventQ.append(containerID, func() {
  848. err := c.backend.ProcessEvent(containerID, EventPaused, EventInfo{
  849. ContainerID: containerID,
  850. ProcessID: InitProcessName,
  851. })
  852. c.logger.WithFields(logrus.Fields{
  853. "container": ctr.id,
  854. "event": EventPaused,
  855. }).Info("sending event")
  856. if err != nil {
  857. c.logger.WithError(err).WithFields(logrus.Fields{
  858. "container": containerID,
  859. "event": EventPaused,
  860. }).Error("failed to process event")
  861. }
  862. })
  863. return nil
  864. }
  865. // Resume handles resume requests for containers
  866. func (c *client) Resume(_ context.Context, containerID string) error {
  867. ctr, _, err := c.getProcess(containerID, InitProcessName)
  868. if err != nil {
  869. return err
  870. }
  871. if ctr.ociSpec.Windows.HyperV == nil {
  872. return errors.New("cannot resume Windows Server Containers")
  873. }
  874. ctr.Lock()
  875. defer ctr.Unlock()
  876. if err = ctr.hcsContainer.Resume(); err != nil {
  877. return err
  878. }
  879. ctr.status = StatusRunning
  880. c.eventQ.append(containerID, func() {
  881. err := c.backend.ProcessEvent(containerID, EventResumed, EventInfo{
  882. ContainerID: containerID,
  883. ProcessID: InitProcessName,
  884. })
  885. c.logger.WithFields(logrus.Fields{
  886. "container": ctr.id,
  887. "event": EventResumed,
  888. }).Info("sending event")
  889. if err != nil {
  890. c.logger.WithError(err).WithFields(logrus.Fields{
  891. "container": containerID,
  892. "event": EventResumed,
  893. }).Error("failed to process event")
  894. }
  895. })
  896. return nil
  897. }
  898. // Stats handles stats requests for containers
  899. func (c *client) Stats(_ context.Context, containerID string) (*Stats, error) {
  900. ctr, _, err := c.getProcess(containerID, InitProcessName)
  901. if err != nil {
  902. return nil, err
  903. }
  904. readAt := time.Now()
  905. s, err := ctr.hcsContainer.Statistics()
  906. if err != nil {
  907. return nil, err
  908. }
  909. return &Stats{
  910. Read: readAt,
  911. HCSStats: &s,
  912. }, nil
  913. }
  914. // Restore is the handler for restoring a container
  915. func (c *client) Restore(ctx context.Context, id string, attachStdio StdioCallback) (bool, int, error) {
  916. c.logger.WithField("container", id).Debug("restore()")
  917. // TODO Windows: On RS1, a re-attach isn't possible.
  918. // However, there is a scenario in which there is an issue.
  919. // Consider a background container. The daemon dies unexpectedly.
  920. // HCS will still have the compute service alive and running.
  921. // For consistence, we call in to shoot it regardless if HCS knows about it
  922. // We explicitly just log a warning if the terminate fails.
  923. // Then we tell the backend the container exited.
  924. if hc, err := hcsshim.OpenContainer(id); err == nil {
  925. const terminateTimeout = time.Minute * 2
  926. err := hc.Terminate()
  927. if hcsshim.IsPending(err) {
  928. err = hc.WaitTimeout(terminateTimeout)
  929. } else if hcsshim.IsAlreadyStopped(err) {
  930. err = nil
  931. }
  932. if err != nil {
  933. c.logger.WithField("container", id).WithError(err).Debug("terminate failed on restore")
  934. return false, -1, err
  935. }
  936. }
  937. return false, -1, nil
  938. }
  939. // GetPidsForContainer returns a list of process IDs running in a container.
  940. // Not used on Windows.
  941. func (c *client) ListPids(_ context.Context, _ string) ([]uint32, error) {
  942. return nil, errors.New("not implemented on Windows")
  943. }
  944. // Summary returns a summary of the processes running in a container.
  945. // This is present in Windows to support docker top. In linux, the
  946. // engine shells out to ps to get process information. On Windows, as
  947. // the containers could be Hyper-V containers, they would not be
  948. // visible on the container host. However, libcontainerd does have
  949. // that information.
  950. func (c *client) Summary(_ context.Context, containerID string) ([]Summary, error) {
  951. ctr, _, err := c.getProcess(containerID, InitProcessName)
  952. if err != nil {
  953. return nil, err
  954. }
  955. p, err := ctr.hcsContainer.ProcessList()
  956. if err != nil {
  957. return nil, err
  958. }
  959. pl := make([]Summary, len(p))
  960. for i := range p {
  961. pl[i] = Summary(p[i])
  962. }
  963. return pl, nil
  964. }
  965. func (c *client) DeleteTask(ctx context.Context, containerID string) (uint32, time.Time, error) {
  966. ec := -1
  967. ctr := c.getContainer(containerID)
  968. if ctr == nil {
  969. return uint32(ec), time.Now(), errors.WithStack(newNotFoundError("no such container"))
  970. }
  971. select {
  972. case <-ctx.Done():
  973. return uint32(ec), time.Now(), errors.WithStack(ctx.Err())
  974. case <-ctr.waitCh:
  975. default:
  976. return uint32(ec), time.Now(), errors.New("container is not stopped")
  977. }
  978. ctr.Lock()
  979. defer ctr.Unlock()
  980. return ctr.exitCode, ctr.exitedAt, nil
  981. }
  982. func (c *client) Delete(_ context.Context, containerID string) error {
  983. c.Lock()
  984. defer c.Unlock()
  985. ctr := c.containers[containerID]
  986. if ctr == nil {
  987. return errors.WithStack(newNotFoundError("no such container"))
  988. }
  989. ctr.Lock()
  990. defer ctr.Unlock()
  991. switch ctr.status {
  992. case StatusCreated:
  993. if err := c.shutdownContainer(ctr); err != nil {
  994. return err
  995. }
  996. fallthrough
  997. case StatusStopped:
  998. delete(c.containers, containerID)
  999. return nil
  1000. }
  1001. return errors.WithStack(newInvalidParameterError("container is not stopped"))
  1002. }
  1003. func (c *client) Status(ctx context.Context, containerID string) (Status, error) {
  1004. c.Lock()
  1005. defer c.Unlock()
  1006. ctr := c.containers[containerID]
  1007. if ctr == nil {
  1008. return StatusUnknown, errors.WithStack(newNotFoundError("no such container"))
  1009. }
  1010. ctr.Lock()
  1011. defer ctr.Unlock()
  1012. return ctr.status, nil
  1013. }
  1014. func (c *client) UpdateResources(ctx context.Context, containerID string, resources *Resources) error {
  1015. // Updating resource isn't supported on Windows
  1016. // but we should return nil for enabling updating container
  1017. return nil
  1018. }
  1019. func (c *client) CreateCheckpoint(ctx context.Context, containerID, checkpointDir string, exit bool) error {
  1020. return errors.New("Windows: Containers do not support checkpoints")
  1021. }
  1022. func (c *client) getContainer(id string) *container {
  1023. c.Lock()
  1024. ctr := c.containers[id]
  1025. c.Unlock()
  1026. return ctr
  1027. }
  1028. func (c *client) getProcess(containerID, processID string) (*container, *process, error) {
  1029. ctr := c.getContainer(containerID)
  1030. switch {
  1031. case ctr == nil:
  1032. return nil, nil, errors.WithStack(newNotFoundError("no such container"))
  1033. case ctr.init == nil:
  1034. return nil, nil, errors.WithStack(newNotFoundError("container is not running"))
  1035. case processID == InitProcessName:
  1036. return ctr, ctr.init, nil
  1037. default:
  1038. ctr.Lock()
  1039. defer ctr.Unlock()
  1040. if ctr.execs == nil {
  1041. return nil, nil, errors.WithStack(newNotFoundError("no execs"))
  1042. }
  1043. }
  1044. p := ctr.execs[processID]
  1045. if p == nil {
  1046. return nil, nil, errors.WithStack(newNotFoundError("no such exec"))
  1047. }
  1048. return ctr, p, nil
  1049. }
  1050. // ctr mutex must be held when calling this function.
  1051. func (c *client) shutdownContainer(ctr *container) error {
  1052. var err error
  1053. const waitTimeout = time.Minute * 5
  1054. if !ctr.terminateInvoked {
  1055. err = ctr.hcsContainer.Shutdown()
  1056. }
  1057. if hcsshim.IsPending(err) || ctr.terminateInvoked {
  1058. err = ctr.hcsContainer.WaitTimeout(waitTimeout)
  1059. } else if hcsshim.IsAlreadyStopped(err) {
  1060. err = nil
  1061. }
  1062. if err != nil {
  1063. c.logger.WithError(err).WithField("container", ctr.id).
  1064. Debug("failed to shutdown container, terminating it")
  1065. terminateErr := c.terminateContainer(ctr)
  1066. if terminateErr != nil {
  1067. c.logger.WithError(terminateErr).WithField("container", ctr.id).
  1068. Error("failed to shutdown container, and subsequent terminate also failed")
  1069. return fmt.Errorf("%s: subsequent terminate failed %s", err, terminateErr)
  1070. }
  1071. return err
  1072. }
  1073. return nil
  1074. }
  1075. // ctr mutex must be held when calling this function.
  1076. func (c *client) terminateContainer(ctr *container) error {
  1077. const terminateTimeout = time.Minute * 5
  1078. ctr.terminateInvoked = true
  1079. err := ctr.hcsContainer.Terminate()
  1080. if hcsshim.IsPending(err) {
  1081. err = ctr.hcsContainer.WaitTimeout(terminateTimeout)
  1082. } else if hcsshim.IsAlreadyStopped(err) {
  1083. err = nil
  1084. }
  1085. if err != nil {
  1086. c.logger.WithError(err).WithField("container", ctr.id).
  1087. Debug("failed to terminate container")
  1088. return err
  1089. }
  1090. return nil
  1091. }
  1092. func (c *client) reapProcess(ctr *container, p *process) int {
  1093. logger := c.logger.WithFields(logrus.Fields{
  1094. "container": ctr.id,
  1095. "process": p.id,
  1096. })
  1097. var eventErr error
  1098. // Block indefinitely for the process to exit.
  1099. if err := p.hcsProcess.Wait(); err != nil {
  1100. if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
  1101. logger.WithError(err).Warnf("Wait() failed (container may have been killed)")
  1102. }
  1103. // Fall through here, do not return. This ensures we attempt to
  1104. // continue the shutdown in HCS and tell the docker engine that the
  1105. // process/container has exited to avoid a container being dropped on
  1106. // the floor.
  1107. }
  1108. exitedAt := time.Now()
  1109. exitCode, err := p.hcsProcess.ExitCode()
  1110. if err != nil {
  1111. if herr, ok := err.(*hcsshim.ProcessError); ok && herr.Err != windows.ERROR_BROKEN_PIPE {
  1112. logger.WithError(err).Warnf("unable to get exit code for process")
  1113. }
  1114. // Since we got an error retrieving the exit code, make sure that the
  1115. // code we return doesn't incorrectly indicate success.
  1116. exitCode = -1
  1117. // Fall through here, do not return. This ensures we attempt to
  1118. // continue the shutdown in HCS and tell the docker engine that the
  1119. // process/container has exited to avoid a container being dropped on
  1120. // the floor.
  1121. }
  1122. if err := p.hcsProcess.Close(); err != nil {
  1123. logger.WithError(err).Warnf("failed to cleanup hcs process resources")
  1124. exitCode = -1
  1125. eventErr = fmt.Errorf("hcsProcess.Close() failed %s", err)
  1126. }
  1127. if p.id == InitProcessName {
  1128. // Update container status
  1129. ctr.Lock()
  1130. ctr.status = StatusStopped
  1131. ctr.exitedAt = exitedAt
  1132. ctr.exitCode = uint32(exitCode)
  1133. close(ctr.waitCh)
  1134. if err := c.shutdownContainer(ctr); err != nil {
  1135. exitCode = -1
  1136. logger.WithError(err).Warn("failed to shutdown container")
  1137. thisErr := fmt.Errorf("failed to shutdown container: %s", err)
  1138. if eventErr != nil {
  1139. eventErr = fmt.Errorf("%s: %s", eventErr, thisErr)
  1140. } else {
  1141. eventErr = thisErr
  1142. }
  1143. } else {
  1144. logger.Debug("completed container shutdown")
  1145. }
  1146. ctr.Unlock()
  1147. if err := ctr.hcsContainer.Close(); err != nil {
  1148. exitCode = -1
  1149. logger.WithError(err).Error("failed to clean hcs container resources")
  1150. thisErr := fmt.Errorf("failed to terminate container: %s", err)
  1151. if eventErr != nil {
  1152. eventErr = fmt.Errorf("%s: %s", eventErr, thisErr)
  1153. } else {
  1154. eventErr = thisErr
  1155. }
  1156. }
  1157. }
  1158. c.eventQ.append(ctr.id, func() {
  1159. ei := EventInfo{
  1160. ContainerID: ctr.id,
  1161. ProcessID: p.id,
  1162. Pid: uint32(p.pid),
  1163. ExitCode: uint32(exitCode),
  1164. ExitedAt: exitedAt,
  1165. Error: eventErr,
  1166. }
  1167. c.logger.WithFields(logrus.Fields{
  1168. "container": ctr.id,
  1169. "event": EventExit,
  1170. "event-info": ei,
  1171. }).Info("sending event")
  1172. err := c.backend.ProcessEvent(ctr.id, EventExit, ei)
  1173. if err != nil {
  1174. c.logger.WithError(err).WithFields(logrus.Fields{
  1175. "container": ctr.id,
  1176. "event": EventExit,
  1177. "event-info": ei,
  1178. }).Error("failed to process event")
  1179. }
  1180. if p.id != InitProcessName {
  1181. ctr.Lock()
  1182. delete(ctr.execs, p.id)
  1183. ctr.Unlock()
  1184. }
  1185. })
  1186. return exitCode
  1187. }