bundle.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. // +build linux
  2. /*
  3. Copyright The containerd Authors.
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. */
  14. package linux
  15. import (
  16. "context"
  17. "crypto/sha256"
  18. "fmt"
  19. "io/ioutil"
  20. "os"
  21. "path/filepath"
  22. "github.com/containerd/containerd/events/exchange"
  23. "github.com/containerd/containerd/runtime/linux/runctypes"
  24. "github.com/containerd/containerd/runtime/v1/shim"
  25. "github.com/containerd/containerd/runtime/v1/shim/client"
  26. "github.com/pkg/errors"
  27. )
  28. // loadBundle loads an existing bundle from disk
  29. func loadBundle(id, path, workdir string) *bundle {
  30. return &bundle{
  31. id: id,
  32. path: path,
  33. workDir: workdir,
  34. }
  35. }
  36. // newBundle creates a new bundle on disk at the provided path for the given id
  37. func newBundle(id, path, workDir string, spec []byte) (b *bundle, err error) {
  38. if err := os.MkdirAll(path, 0711); err != nil {
  39. return nil, err
  40. }
  41. path = filepath.Join(path, id)
  42. if err := os.Mkdir(path, 0711); err != nil {
  43. return nil, err
  44. }
  45. defer func() {
  46. if err != nil {
  47. os.RemoveAll(path)
  48. }
  49. }()
  50. workDir = filepath.Join(workDir, id)
  51. if err := os.MkdirAll(workDir, 0711); err != nil {
  52. return nil, err
  53. }
  54. defer func() {
  55. if err != nil {
  56. os.RemoveAll(workDir)
  57. }
  58. }()
  59. rootfs := filepath.Join(path, "rootfs")
  60. if err := os.MkdirAll(rootfs, 0711); err != nil {
  61. return nil, err
  62. }
  63. err = ioutil.WriteFile(filepath.Join(path, configFilename), spec, 0666)
  64. return &bundle{
  65. id: id,
  66. path: path,
  67. workDir: workDir,
  68. }, err
  69. }
  70. type bundle struct {
  71. id string
  72. path string
  73. workDir string
  74. }
  75. // ShimOpt specifies shim options for initialization and connection
  76. type ShimOpt func(*bundle, string, *runctypes.RuncOptions) (shim.Config, client.Opt)
  77. // ShimRemote is a ShimOpt for connecting and starting a remote shim
  78. func ShimRemote(c *Config, daemonAddress, cgroup string, exitHandler func()) ShimOpt {
  79. return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
  80. config := b.shimConfig(ns, c, ropts)
  81. return config,
  82. client.WithStart(c.Shim, b.shimAddress(ns, daemonAddress), daemonAddress, cgroup, c.ShimDebug, exitHandler)
  83. }
  84. }
  85. // ShimLocal is a ShimOpt for using an in process shim implementation
  86. func ShimLocal(c *Config, exchange *exchange.Exchange) ShimOpt {
  87. return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
  88. return b.shimConfig(ns, c, ropts), client.WithLocal(exchange)
  89. }
  90. }
  91. // ShimConnect is a ShimOpt for connecting to an existing remote shim
  92. func ShimConnect(c *Config, onClose func()) ShimOpt {
  93. return func(b *bundle, ns string, ropts *runctypes.RuncOptions) (shim.Config, client.Opt) {
  94. return b.shimConfig(ns, c, ropts), client.WithConnect(b.decideShimAddress(ns), onClose)
  95. }
  96. }
  97. // NewShimClient connects to the shim managing the bundle and tasks creating it if needed
  98. func (b *bundle) NewShimClient(ctx context.Context, namespace string, getClientOpts ShimOpt, runcOpts *runctypes.RuncOptions) (*client.Client, error) {
  99. cfg, opt := getClientOpts(b, namespace, runcOpts)
  100. return client.New(ctx, cfg, opt)
  101. }
  102. // Delete deletes the bundle from disk
  103. func (b *bundle) Delete() error {
  104. address, _ := b.loadAddress()
  105. if address != "" {
  106. // we don't care about errors here
  107. client.RemoveSocket(address)
  108. }
  109. err := atomicDelete(b.path)
  110. if err == nil {
  111. return atomicDelete(b.workDir)
  112. }
  113. // error removing the bundle path; still attempt removing work dir
  114. err2 := atomicDelete(b.workDir)
  115. if err2 == nil {
  116. return err
  117. }
  118. return errors.Wrapf(err, "Failed to remove both bundle and workdir locations: %v", err2)
  119. }
  120. func (b *bundle) legacyShimAddress(namespace string) string {
  121. return filepath.Join(string(filepath.Separator), "containerd-shim", namespace, b.id, "shim.sock")
  122. }
  123. const socketRoot = "/run/containerd"
  124. func (b *bundle) shimAddress(namespace, socketPath string) string {
  125. d := sha256.Sum256([]byte(filepath.Join(socketPath, namespace, b.id)))
  126. return fmt.Sprintf("unix://%s/%x", filepath.Join(socketRoot, "s"), d)
  127. }
  128. func (b *bundle) loadAddress() (string, error) {
  129. addressPath := filepath.Join(b.path, "address")
  130. data, err := ioutil.ReadFile(addressPath)
  131. if err != nil {
  132. return "", err
  133. }
  134. return string(data), nil
  135. }
  136. func (b *bundle) decideShimAddress(namespace string) string {
  137. address, err := b.loadAddress()
  138. if err != nil {
  139. return b.legacyShimAddress(namespace)
  140. }
  141. return address
  142. }
  143. func (b *bundle) shimConfig(namespace string, c *Config, runcOptions *runctypes.RuncOptions) shim.Config {
  144. var (
  145. criuPath string
  146. runtimeRoot = c.RuntimeRoot
  147. systemdCgroup bool
  148. )
  149. if runcOptions != nil {
  150. criuPath = runcOptions.CriuPath
  151. systemdCgroup = runcOptions.SystemdCgroup
  152. if runcOptions.RuntimeRoot != "" {
  153. runtimeRoot = runcOptions.RuntimeRoot
  154. }
  155. }
  156. return shim.Config{
  157. Path: b.path,
  158. WorkDir: b.workDir,
  159. Namespace: namespace,
  160. Criu: criuPath,
  161. RuntimeRoot: runtimeRoot,
  162. SystemdCgroup: systemdCgroup,
  163. }
  164. }
  165. // atomicDelete renames the path to a hidden file before removal
  166. func atomicDelete(path string) error {
  167. // create a hidden dir for an atomic removal
  168. atomicPath := filepath.Join(filepath.Dir(path), fmt.Sprintf(".%s", filepath.Base(path)))
  169. if err := os.Rename(path, atomicPath); err != nil {
  170. if os.IsNotExist(err) {
  171. return nil
  172. }
  173. return err
  174. }
  175. return os.RemoveAll(atomicPath)
  176. }