container.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. package runtime
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "os"
  8. "path/filepath"
  9. "syscall"
  10. "time"
  11. "github.com/Sirupsen/logrus"
  12. "github.com/docker/containerd/specs"
  13. "github.com/docker/containerd/subreaper/exec"
  14. ocs "github.com/opencontainers/runtime-spec/specs-go"
  15. )
  16. type Container interface {
  17. // ID returns the container ID
  18. ID() string
  19. // Path returns the path to the bundle
  20. Path() string
  21. // Start starts the init process of the container
  22. Start(checkpointPath string, s Stdio) (Process, error)
  23. // Exec starts another process in an existing container
  24. Exec(string, specs.ProcessSpec, Stdio) (Process, error)
  25. // Delete removes the container's state and any resources
  26. Delete() error
  27. // Processes returns all the containers processes that have been added
  28. Processes() ([]Process, error)
  29. // State returns the containers runtime state
  30. State() State
  31. // Resume resumes a paused container
  32. Resume() error
  33. // Pause pauses a running container
  34. Pause() error
  35. // RemoveProcess removes the specified process from the container
  36. RemoveProcess(string) error
  37. // Checkpoints returns all the checkpoints for a container
  38. Checkpoints(checkpointDir string) ([]Checkpoint, error)
  39. // Checkpoint creates a new checkpoint
  40. Checkpoint(checkpoint Checkpoint, checkpointDir string) error
  41. // DeleteCheckpoint deletes the checkpoint for the provided name
  42. DeleteCheckpoint(name string, checkpointDir string) error
  43. // Labels are user provided labels for the container
  44. Labels() []string
  45. // Pids returns all pids inside the container
  46. Pids() ([]int, error)
  47. // Stats returns realtime container stats and resource information
  48. Stats() (*Stat, error)
  49. // Name or path of the OCI compliant runtime used to execute the container
  50. Runtime() string
  51. // OOM signals the channel if the container received an OOM notification
  52. OOM() (OOM, error)
  53. // UpdateResource updates the containers resources to new values
  54. UpdateResources(*Resource) error
  55. // Status return the current status of the container.
  56. Status() (State, error)
  57. }
  58. type OOM interface {
  59. io.Closer
  60. FD() int
  61. ContainerID() string
  62. Flush()
  63. Removed() bool
  64. }
  65. type Stdio struct {
  66. Stdin string
  67. Stdout string
  68. Stderr string
  69. }
  70. func NewStdio(stdin, stdout, stderr string) Stdio {
  71. for _, s := range []*string{
  72. &stdin, &stdout, &stderr,
  73. } {
  74. if *s == "" {
  75. *s = "/dev/null"
  76. }
  77. }
  78. return Stdio{
  79. Stdin: stdin,
  80. Stdout: stdout,
  81. Stderr: stderr,
  82. }
  83. }
  84. type ContainerOpts struct {
  85. Root string
  86. ID string
  87. Bundle string
  88. Runtime string
  89. RuntimeArgs []string
  90. Shim string
  91. Labels []string
  92. NoPivotRoot bool
  93. Timeout time.Duration
  94. }
  95. // New returns a new container
  96. func New(opts ContainerOpts) (Container, error) {
  97. c := &container{
  98. root: opts.Root,
  99. id: opts.ID,
  100. bundle: opts.Bundle,
  101. labels: opts.Labels,
  102. processes: make(map[string]Process),
  103. runtime: opts.Runtime,
  104. runtimeArgs: opts.RuntimeArgs,
  105. shim: opts.Shim,
  106. noPivotRoot: opts.NoPivotRoot,
  107. timeout: opts.Timeout,
  108. }
  109. if err := os.Mkdir(filepath.Join(c.root, c.id), 0755); err != nil {
  110. return nil, err
  111. }
  112. f, err := os.Create(filepath.Join(c.root, c.id, StateFile))
  113. if err != nil {
  114. return nil, err
  115. }
  116. defer f.Close()
  117. if err := json.NewEncoder(f).Encode(state{
  118. Bundle: c.bundle,
  119. Labels: c.labels,
  120. Runtime: c.runtime,
  121. RuntimeArgs: c.runtimeArgs,
  122. Shim: c.shim,
  123. NoPivotRoot: opts.NoPivotRoot,
  124. }); err != nil {
  125. return nil, err
  126. }
  127. return c, nil
  128. }
  129. func Load(root, id string, timeout time.Duration) (Container, error) {
  130. var s state
  131. f, err := os.Open(filepath.Join(root, id, StateFile))
  132. if err != nil {
  133. return nil, err
  134. }
  135. defer f.Close()
  136. if err := json.NewDecoder(f).Decode(&s); err != nil {
  137. return nil, err
  138. }
  139. c := &container{
  140. root: root,
  141. id: id,
  142. bundle: s.Bundle,
  143. labels: s.Labels,
  144. runtime: s.Runtime,
  145. runtimeArgs: s.RuntimeArgs,
  146. shim: s.Shim,
  147. noPivotRoot: s.NoPivotRoot,
  148. processes: make(map[string]Process),
  149. timeout: timeout,
  150. }
  151. dirs, err := ioutil.ReadDir(filepath.Join(root, id))
  152. if err != nil {
  153. return nil, err
  154. }
  155. for _, d := range dirs {
  156. if !d.IsDir() {
  157. continue
  158. }
  159. pid := d.Name()
  160. s, err := readProcessState(filepath.Join(root, id, pid))
  161. if err != nil {
  162. return nil, err
  163. }
  164. p, err := loadProcess(filepath.Join(root, id, pid), pid, c, s)
  165. if err != nil {
  166. logrus.WithField("id", id).WithField("pid", pid).Debug("containerd: error loading process %s", err)
  167. continue
  168. }
  169. c.processes[pid] = p
  170. }
  171. return c, nil
  172. }
  173. func readProcessState(dir string) (*ProcessState, error) {
  174. f, err := os.Open(filepath.Join(dir, "process.json"))
  175. if err != nil {
  176. return nil, err
  177. }
  178. defer f.Close()
  179. var s ProcessState
  180. if err := json.NewDecoder(f).Decode(&s); err != nil {
  181. return nil, err
  182. }
  183. return &s, nil
  184. }
  185. type container struct {
  186. // path to store runtime state information
  187. root string
  188. id string
  189. bundle string
  190. runtime string
  191. runtimeArgs []string
  192. shim string
  193. processes map[string]Process
  194. labels []string
  195. oomFds []int
  196. noPivotRoot bool
  197. timeout time.Duration
  198. }
  199. func (c *container) ID() string {
  200. return c.id
  201. }
  202. func (c *container) Path() string {
  203. return c.bundle
  204. }
  205. func (c *container) Labels() []string {
  206. return c.labels
  207. }
  208. func (c *container) readSpec() (*specs.Spec, error) {
  209. var spec specs.Spec
  210. f, err := os.Open(filepath.Join(c.bundle, "config.json"))
  211. if err != nil {
  212. return nil, err
  213. }
  214. defer f.Close()
  215. if err := json.NewDecoder(f).Decode(&spec); err != nil {
  216. return nil, err
  217. }
  218. return &spec, nil
  219. }
  220. func (c *container) Delete() error {
  221. err := os.RemoveAll(filepath.Join(c.root, c.id))
  222. args := c.runtimeArgs
  223. args = append(args, "delete", c.id)
  224. if derr := exec.Command(c.runtime, args...).Run(); err == nil {
  225. err = derr
  226. }
  227. return err
  228. }
  229. func (c *container) Processes() ([]Process, error) {
  230. out := []Process{}
  231. for _, p := range c.processes {
  232. out = append(out, p)
  233. }
  234. return out, nil
  235. }
  236. func (c *container) RemoveProcess(pid string) error {
  237. delete(c.processes, pid)
  238. return os.RemoveAll(filepath.Join(c.root, c.id, pid))
  239. }
  240. func (c *container) State() State {
  241. proc := c.processes["init"]
  242. if proc == nil {
  243. return Stopped
  244. }
  245. return proc.State()
  246. }
  247. func (c *container) Runtime() string {
  248. return c.runtime
  249. }
  250. func (c *container) Pause() error {
  251. args := c.runtimeArgs
  252. args = append(args, "pause", c.id)
  253. b, err := exec.Command(c.runtime, args...).CombinedOutput()
  254. if err != nil {
  255. return fmt.Errorf("%s: %q", err.Error(), string(b))
  256. }
  257. return nil
  258. }
  259. func (c *container) Resume() error {
  260. args := c.runtimeArgs
  261. args = append(args, "resume", c.id)
  262. b, err := exec.Command(c.runtime, args...).CombinedOutput()
  263. if err != nil {
  264. return fmt.Errorf("%s: %q", err.Error(), string(b))
  265. }
  266. return nil
  267. }
  268. func (c *container) Checkpoints(checkpointDir string) ([]Checkpoint, error) {
  269. if checkpointDir == "" {
  270. checkpointDir = filepath.Join(c.bundle, "checkpoints")
  271. }
  272. dirs, err := ioutil.ReadDir(checkpointDir)
  273. if err != nil {
  274. return nil, err
  275. }
  276. var out []Checkpoint
  277. for _, d := range dirs {
  278. if !d.IsDir() {
  279. continue
  280. }
  281. path := filepath.Join(checkpointDir, d.Name(), "config.json")
  282. data, err := ioutil.ReadFile(path)
  283. if err != nil {
  284. return nil, err
  285. }
  286. var cpt Checkpoint
  287. if err := json.Unmarshal(data, &cpt); err != nil {
  288. return nil, err
  289. }
  290. out = append(out, cpt)
  291. }
  292. return out, nil
  293. }
  294. func (c *container) Checkpoint(cpt Checkpoint, checkpointDir string) error {
  295. if checkpointDir == "" {
  296. checkpointDir = filepath.Join(c.bundle, "checkpoints")
  297. }
  298. if err := os.MkdirAll(checkpointDir, 0755); err != nil {
  299. return err
  300. }
  301. path := filepath.Join(checkpointDir, cpt.Name)
  302. if err := os.Mkdir(path, 0755); err != nil {
  303. return err
  304. }
  305. f, err := os.Create(filepath.Join(path, "config.json"))
  306. if err != nil {
  307. return err
  308. }
  309. cpt.Created = time.Now()
  310. err = json.NewEncoder(f).Encode(cpt)
  311. f.Close()
  312. if err != nil {
  313. return err
  314. }
  315. args := []string{
  316. "checkpoint",
  317. "--image-path", path,
  318. }
  319. add := func(flags ...string) {
  320. args = append(args, flags...)
  321. }
  322. add(c.runtimeArgs...)
  323. if !cpt.Exit {
  324. add("--leave-running")
  325. }
  326. if cpt.Shell {
  327. add("--shell-job")
  328. }
  329. if cpt.Tcp {
  330. add("--tcp-established")
  331. }
  332. if cpt.UnixSockets {
  333. add("--ext-unix-sk")
  334. }
  335. add(c.id)
  336. out, err := exec.Command(c.runtime, args...).CombinedOutput()
  337. if err != nil {
  338. return fmt.Errorf("%s: %q", err.Error(), string(out))
  339. }
  340. return err
  341. }
  342. func (c *container) DeleteCheckpoint(name string, checkpointDir string) error {
  343. if checkpointDir == "" {
  344. checkpointDir = filepath.Join(c.bundle, "checkpoints")
  345. }
  346. return os.RemoveAll(filepath.Join(checkpointDir, name))
  347. }
  348. func (c *container) Start(checkpointPath string, s Stdio) (Process, error) {
  349. processRoot := filepath.Join(c.root, c.id, InitProcessID)
  350. if err := os.Mkdir(processRoot, 0755); err != nil {
  351. return nil, err
  352. }
  353. spec, err := c.readSpec()
  354. if err != nil {
  355. return nil, err
  356. }
  357. config := &processConfig{
  358. checkpoint: checkpointPath,
  359. root: processRoot,
  360. id: InitProcessID,
  361. c: c,
  362. stdio: s,
  363. spec: spec,
  364. processSpec: specs.ProcessSpec(spec.Process),
  365. }
  366. p, err := c.newProcess(config)
  367. if err != nil {
  368. return nil, err
  369. }
  370. if err := p.Start(); err != nil {
  371. return nil, err
  372. }
  373. c.processes[InitProcessID] = p
  374. return p, nil
  375. }
  376. func (c *container) Exec(pid string, pspec specs.ProcessSpec, s Stdio) (pp Process, err error) {
  377. processRoot := filepath.Join(c.root, c.id, pid)
  378. if err := os.Mkdir(processRoot, 0755); err != nil {
  379. return nil, err
  380. }
  381. defer func() {
  382. if err != nil {
  383. c.RemoveProcess(pid)
  384. }
  385. }()
  386. spec, err := c.readSpec()
  387. if err != nil {
  388. return nil, err
  389. }
  390. config := &processConfig{
  391. exec: true,
  392. id: pid,
  393. root: processRoot,
  394. c: c,
  395. processSpec: pspec,
  396. spec: spec,
  397. stdio: s,
  398. }
  399. p, err := c.newProcess(config)
  400. if err != nil {
  401. return nil, err
  402. }
  403. if err := p.Start(); err != nil {
  404. return nil, err
  405. }
  406. c.processes[pid] = p
  407. return p, nil
  408. }
  409. func hostIDFromMap(id uint32, mp []ocs.IDMapping) int {
  410. for _, m := range mp {
  411. if (id >= m.ContainerID) && (id <= (m.ContainerID + m.Size - 1)) {
  412. return int(m.HostID + (id - m.ContainerID))
  413. }
  414. }
  415. return 0
  416. }
  417. func (c *container) Pids() ([]int, error) {
  418. args := c.runtimeArgs
  419. args = append(args, "ps", "--format=json", c.id)
  420. out, err := exec.Command(c.runtime, args...).CombinedOutput()
  421. if err != nil {
  422. return nil, fmt.Errorf("%s: %q", err.Error(), out)
  423. }
  424. var pids []int
  425. if err := json.Unmarshal(out, &pids); err != nil {
  426. return nil, err
  427. }
  428. return pids, nil
  429. }
  430. func (c *container) Stats() (*Stat, error) {
  431. now := time.Now()
  432. args := c.runtimeArgs
  433. args = append(args, "events", "--stats", c.id)
  434. out, err := exec.Command(c.runtime, args...).CombinedOutput()
  435. if err != nil {
  436. return nil, fmt.Errorf("%s: %q", err.Error(), out)
  437. }
  438. s := struct {
  439. Data *Stat `json:"data"`
  440. }{}
  441. if err := json.Unmarshal(out, &s); err != nil {
  442. return nil, err
  443. }
  444. s.Data.Timestamp = now
  445. return s.Data, nil
  446. }
  447. // Status implements the runtime Container interface.
  448. func (c *container) Status() (State, error) {
  449. args := c.runtimeArgs
  450. args = append(args, "state", c.id)
  451. out, err := exec.Command(c.runtime, args...).CombinedOutput()
  452. if err != nil {
  453. return "", fmt.Errorf("%s: %q", err.Error(), out)
  454. }
  455. // We only require the runtime json output to have a top level Status field.
  456. var s struct {
  457. Status State `json:"status"`
  458. }
  459. if err := json.Unmarshal(out, &s); err != nil {
  460. return "", err
  461. }
  462. return s.Status, nil
  463. }
  464. func (c *container) writeEventFD(root string, cfd, efd int) error {
  465. f, err := os.OpenFile(filepath.Join(root, "cgroup.event_control"), os.O_WRONLY, 0)
  466. if err != nil {
  467. return err
  468. }
  469. defer f.Close()
  470. _, err = f.WriteString(fmt.Sprintf("%d %d", efd, cfd))
  471. return err
  472. }
  473. func (c *container) newProcess(config *processConfig) (Process, error) {
  474. if c.shim == "" {
  475. return newDirectProcess(config)
  476. }
  477. return newProcess(config)
  478. }
  479. type waitArgs struct {
  480. pid int
  481. err error
  482. }
  483. // isAlive checks if the shim that launched the container is still alive
  484. func isAlive(cmd *exec.Cmd) (bool, error) {
  485. if err := syscall.Kill(cmd.Process.Pid, 0); err != nil {
  486. if err == syscall.ESRCH {
  487. return false, nil
  488. }
  489. return false, err
  490. }
  491. return true, nil
  492. }
  493. type oom struct {
  494. id string
  495. root string
  496. control *os.File
  497. eventfd int
  498. }
  499. func (o *oom) ContainerID() string {
  500. return o.id
  501. }
  502. func (o *oom) FD() int {
  503. return o.eventfd
  504. }
  505. func (o *oom) Flush() {
  506. buf := make([]byte, 8)
  507. syscall.Read(o.eventfd, buf)
  508. }
  509. func (o *oom) Removed() bool {
  510. _, err := os.Lstat(filepath.Join(o.root, "cgroup.event_control"))
  511. return os.IsNotExist(err)
  512. }
  513. func (o *oom) Close() error {
  514. err := syscall.Close(o.eventfd)
  515. if cerr := o.control.Close(); err == nil {
  516. err = cerr
  517. }
  518. return err
  519. }
  520. type message struct {
  521. Level string `json:"level"`
  522. Msg string `json:"msg"`
  523. }
  524. func readLogMessages(path string) ([]message, error) {
  525. var out []message
  526. f, err := os.Open(path)
  527. if err != nil {
  528. return nil, err
  529. }
  530. defer f.Close()
  531. dec := json.NewDecoder(f)
  532. for {
  533. var m message
  534. if err := dec.Decode(&m); err != nil {
  535. if err == io.EOF {
  536. break
  537. }
  538. return nil, err
  539. }
  540. out = append(out, m)
  541. }
  542. return out, nil
  543. }