client_linux.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. package libcontainerd
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "sync"
  9. "syscall"
  10. "github.com/Sirupsen/logrus"
  11. containerd "github.com/docker/containerd/api/grpc/types"
  12. "github.com/docker/docker/pkg/idtools"
  13. "github.com/docker/docker/pkg/mount"
  14. "github.com/opencontainers/specs/specs-go"
  15. "golang.org/x/net/context"
  16. )
  17. type client struct {
  18. clientCommon
  19. // Platform specific properties below here.
  20. remote *remote
  21. q queue
  22. exitNotifiers map[string]*exitNotifier
  23. }
  24. func (clnt *client) AddProcess(containerID, processFriendlyName string, specp Process) error {
  25. clnt.lock(containerID)
  26. defer clnt.unlock(containerID)
  27. container, err := clnt.getContainer(containerID)
  28. if err != nil {
  29. return err
  30. }
  31. spec, err := container.spec()
  32. if err != nil {
  33. return err
  34. }
  35. sp := spec.Process
  36. sp.Args = specp.Args
  37. sp.Terminal = specp.Terminal
  38. if specp.Env != nil {
  39. sp.Env = specp.Env
  40. }
  41. if specp.Cwd != nil {
  42. sp.Cwd = *specp.Cwd
  43. }
  44. if specp.User != nil {
  45. sp.User = specs.User{
  46. UID: specp.User.UID,
  47. GID: specp.User.GID,
  48. AdditionalGids: specp.User.AdditionalGids,
  49. }
  50. }
  51. if specp.Capabilities != nil {
  52. sp.Capabilities = specp.Capabilities
  53. }
  54. p := container.newProcess(processFriendlyName)
  55. r := &containerd.AddProcessRequest{
  56. Args: sp.Args,
  57. Cwd: sp.Cwd,
  58. Terminal: sp.Terminal,
  59. Id: containerID,
  60. Env: sp.Env,
  61. User: &containerd.User{
  62. Uid: sp.User.UID,
  63. Gid: sp.User.GID,
  64. AdditionalGids: sp.User.AdditionalGids,
  65. },
  66. Pid: processFriendlyName,
  67. Stdin: p.fifo(syscall.Stdin),
  68. Stdout: p.fifo(syscall.Stdout),
  69. Stderr: p.fifo(syscall.Stderr),
  70. Capabilities: sp.Capabilities,
  71. ApparmorProfile: sp.ApparmorProfile,
  72. SelinuxLabel: sp.SelinuxLabel,
  73. NoNewPrivileges: sp.NoNewPrivileges,
  74. Rlimits: convertRlimits(sp.Rlimits),
  75. }
  76. iopipe, err := p.openFifos(sp.Terminal)
  77. if err != nil {
  78. return err
  79. }
  80. if _, err := clnt.remote.apiClient.AddProcess(context.Background(), r); err != nil {
  81. p.closeFifos(iopipe)
  82. return err
  83. }
  84. container.processes[processFriendlyName] = p
  85. clnt.unlock(containerID)
  86. if err := clnt.backend.AttachStreams(processFriendlyName, *iopipe); err != nil {
  87. return err
  88. }
  89. clnt.lock(containerID)
  90. return nil
  91. }
  92. func (clnt *client) prepareBundleDir(uid, gid int) (string, error) {
  93. root, err := filepath.Abs(clnt.remote.stateDir)
  94. if err != nil {
  95. return "", err
  96. }
  97. if uid == 0 && gid == 0 {
  98. return root, nil
  99. }
  100. p := string(filepath.Separator)
  101. for _, d := range strings.Split(root, string(filepath.Separator))[1:] {
  102. p = filepath.Join(p, d)
  103. fi, err := os.Stat(p)
  104. if err != nil && !os.IsNotExist(err) {
  105. return "", err
  106. }
  107. if os.IsNotExist(err) || fi.Mode()&1 == 0 {
  108. p = fmt.Sprintf("%s.%d.%d", p, uid, gid)
  109. if err := idtools.MkdirAs(p, 0700, uid, gid); err != nil && !os.IsExist(err) {
  110. return "", err
  111. }
  112. }
  113. }
  114. return p, nil
  115. }
  116. func (clnt *client) Create(containerID string, spec Spec, options ...CreateOption) (err error) {
  117. clnt.lock(containerID)
  118. defer clnt.unlock(containerID)
  119. if ctr, err := clnt.getContainer(containerID); err == nil {
  120. if ctr.restarting { // docker doesn't actually call start if restart is on atm, but probably should in the future
  121. ctr.restartManager.Cancel()
  122. ctr.clean()
  123. } else {
  124. return fmt.Errorf("Container %s is already active", containerID)
  125. }
  126. }
  127. uid, gid, err := getRootIDs(specs.Spec(spec))
  128. if err != nil {
  129. return err
  130. }
  131. dir, err := clnt.prepareBundleDir(uid, gid)
  132. if err != nil {
  133. return err
  134. }
  135. container := clnt.newContainer(filepath.Join(dir, containerID), options...)
  136. if err := container.clean(); err != nil {
  137. return err
  138. }
  139. defer func() {
  140. if err != nil {
  141. container.clean()
  142. clnt.deleteContainer(containerID)
  143. }
  144. }()
  145. // uid/gid
  146. rootfsDir := filepath.Join(container.dir, "rootfs")
  147. if err := idtools.MkdirAllAs(rootfsDir, 0700, uid, gid); err != nil && !os.IsExist(err) {
  148. return err
  149. }
  150. if err := syscall.Mount(spec.Root.Path, rootfsDir, "bind", syscall.MS_REC|syscall.MS_BIND, ""); err != nil {
  151. return err
  152. }
  153. spec.Root.Path = "rootfs"
  154. f, err := os.Create(filepath.Join(container.dir, configFilename))
  155. if err != nil {
  156. return err
  157. }
  158. defer f.Close()
  159. if err := json.NewEncoder(f).Encode(spec); err != nil {
  160. return err
  161. }
  162. return container.start()
  163. }
  164. func (clnt *client) Signal(containerID string, sig int) error {
  165. clnt.lock(containerID)
  166. defer clnt.unlock(containerID)
  167. _, err := clnt.remote.apiClient.Signal(context.Background(), &containerd.SignalRequest{
  168. Id: containerID,
  169. Pid: InitFriendlyName,
  170. Signal: uint32(sig),
  171. })
  172. return err
  173. }
  174. func (clnt *client) Resize(containerID, processFriendlyName string, width, height int) error {
  175. clnt.lock(containerID)
  176. defer clnt.unlock(containerID)
  177. if _, err := clnt.getContainer(containerID); err != nil {
  178. return err
  179. }
  180. _, err := clnt.remote.apiClient.UpdateProcess(context.Background(), &containerd.UpdateProcessRequest{
  181. Id: containerID,
  182. Pid: processFriendlyName,
  183. Width: uint32(width),
  184. Height: uint32(height),
  185. })
  186. return err
  187. }
  188. func (clnt *client) Pause(containerID string) error {
  189. return clnt.setState(containerID, StatePause)
  190. }
  191. func (clnt *client) setState(containerID, state string) error {
  192. clnt.lock(containerID)
  193. container, err := clnt.getContainer(containerID)
  194. if err != nil {
  195. clnt.unlock(containerID)
  196. return err
  197. }
  198. if container.systemPid == 0 {
  199. clnt.unlock(containerID)
  200. return fmt.Errorf("No active process for container %s", containerID)
  201. }
  202. st := "running"
  203. if state == StatePause {
  204. st = "paused"
  205. }
  206. chstate := make(chan struct{})
  207. _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{
  208. Id: containerID,
  209. Pid: InitFriendlyName,
  210. Status: st,
  211. })
  212. if err != nil {
  213. clnt.unlock(containerID)
  214. return err
  215. }
  216. container.pauseMonitor.append(state, chstate)
  217. clnt.unlock(containerID)
  218. <-chstate
  219. return nil
  220. }
  221. func (clnt *client) Resume(containerID string) error {
  222. return clnt.setState(containerID, StateResume)
  223. }
  224. func (clnt *client) Stats(containerID string) (*Stats, error) {
  225. resp, err := clnt.remote.apiClient.Stats(context.Background(), &containerd.StatsRequest{containerID})
  226. if err != nil {
  227. return nil, err
  228. }
  229. return (*Stats)(resp), nil
  230. }
  231. func (clnt *client) setExited(containerID string) error {
  232. clnt.lock(containerID)
  233. defer clnt.unlock(containerID)
  234. var exitCode uint32
  235. if event, ok := clnt.remote.pastEvents[containerID]; ok {
  236. exitCode = event.Status
  237. delete(clnt.remote.pastEvents, containerID)
  238. }
  239. err := clnt.backend.StateChanged(containerID, StateInfo{
  240. CommonStateInfo: CommonStateInfo{
  241. State: StateExit,
  242. ExitCode: exitCode,
  243. }})
  244. // Unmount and delete the bundle folder
  245. if mts, err := mount.GetMounts(); err == nil {
  246. for _, mts := range mts {
  247. if strings.HasSuffix(mts.Mountpoint, containerID+"/rootfs") {
  248. if err := syscall.Unmount(mts.Mountpoint, syscall.MNT_DETACH); err == nil {
  249. os.RemoveAll(strings.TrimSuffix(mts.Mountpoint, "/rootfs"))
  250. }
  251. break
  252. }
  253. }
  254. }
  255. return err
  256. }
  257. func (clnt *client) GetPidsForContainer(containerID string) ([]int, error) {
  258. cont, err := clnt.getContainerdContainer(containerID)
  259. if err != nil {
  260. return nil, err
  261. }
  262. pids := make([]int, len(cont.Pids))
  263. for i, p := range cont.Pids {
  264. pids[i] = int(p)
  265. }
  266. return pids, nil
  267. }
  268. // Summary returns a summary of the processes running in a container.
  269. // This is a no-op on Linux.
  270. func (clnt *client) Summary(containerID string) ([]Summary, error) {
  271. return nil, nil
  272. }
  273. func (clnt *client) getContainerdContainer(containerID string) (*containerd.Container, error) {
  274. resp, err := clnt.remote.apiClient.State(context.Background(), &containerd.StateRequest{Id: containerID})
  275. if err != nil {
  276. return nil, err
  277. }
  278. for _, cont := range resp.Containers {
  279. if cont.Id == containerID {
  280. return cont, nil
  281. }
  282. }
  283. return nil, fmt.Errorf("invalid state response")
  284. }
  285. func (clnt *client) newContainer(dir string, options ...CreateOption) *container {
  286. container := &container{
  287. containerCommon: containerCommon{
  288. process: process{
  289. dir: dir,
  290. processCommon: processCommon{
  291. containerID: filepath.Base(dir),
  292. client: clnt,
  293. friendlyName: InitFriendlyName,
  294. },
  295. },
  296. processes: make(map[string]*process),
  297. },
  298. }
  299. for _, option := range options {
  300. if err := option.Apply(container); err != nil {
  301. logrus.Error(err)
  302. }
  303. }
  304. return container
  305. }
  306. func (clnt *client) UpdateResources(containerID string, resources Resources) error {
  307. clnt.lock(containerID)
  308. defer clnt.unlock(containerID)
  309. container, err := clnt.getContainer(containerID)
  310. if err != nil {
  311. return err
  312. }
  313. if container.systemPid == 0 {
  314. return fmt.Errorf("No active process for container %s", containerID)
  315. }
  316. _, err = clnt.remote.apiClient.UpdateContainer(context.Background(), &containerd.UpdateContainerRequest{
  317. Id: containerID,
  318. Pid: InitFriendlyName,
  319. Resources: (*containerd.UpdateResource)(&resources),
  320. })
  321. if err != nil {
  322. return err
  323. }
  324. return nil
  325. }
  326. func (clnt *client) getExitNotifier(containerID string) *exitNotifier {
  327. clnt.mapMutex.RLock()
  328. defer clnt.mapMutex.RUnlock()
  329. return clnt.exitNotifiers[containerID]
  330. }
  331. func (clnt *client) getOrCreateExitNotifier(containerID string) *exitNotifier {
  332. clnt.mapMutex.Lock()
  333. w, ok := clnt.exitNotifiers[containerID]
  334. defer clnt.mapMutex.Unlock()
  335. if !ok {
  336. w = &exitNotifier{c: make(chan struct{}), client: clnt}
  337. clnt.exitNotifiers[containerID] = w
  338. }
  339. return w
  340. }
  341. type exitNotifier struct {
  342. id string
  343. client *client
  344. c chan struct{}
  345. once sync.Once
  346. }
  347. func (en *exitNotifier) close() {
  348. en.once.Do(func() {
  349. close(en.c)
  350. en.client.mapMutex.Lock()
  351. if en == en.client.exitNotifiers[en.id] {
  352. delete(en.client.exitNotifiers, en.id)
  353. }
  354. en.client.mapMutex.Unlock()
  355. })
  356. }
  357. func (en *exitNotifier) wait() <-chan struct{} {
  358. return en.c
  359. }