local_windows.go 46 KB

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