runtime_unix_test.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. //go:build !windows
  2. package daemon
  3. import (
  4. "os"
  5. "path/filepath"
  6. "testing"
  7. "github.com/containerd/containerd/plugin"
  8. v2runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
  9. "gotest.tools/v3/assert"
  10. is "gotest.tools/v3/assert/cmp"
  11. "github.com/docker/docker/api/types"
  12. "github.com/docker/docker/daemon/config"
  13. "github.com/docker/docker/errdefs"
  14. )
  15. func TestInitRuntimes_InvalidConfigs(t *testing.T) {
  16. cases := []struct {
  17. name string
  18. runtime types.Runtime
  19. expectErr string
  20. }{
  21. {
  22. name: "Empty",
  23. expectErr: "either a runtimeType or a path must be configured",
  24. },
  25. {
  26. name: "ArgsOnly",
  27. runtime: types.Runtime{Args: []string{"foo", "bar"}},
  28. expectErr: "either a runtimeType or a path must be configured",
  29. },
  30. {
  31. name: "OptionsOnly",
  32. runtime: types.Runtime{Options: map[string]interface{}{"hello": "world"}},
  33. expectErr: "either a runtimeType or a path must be configured",
  34. },
  35. {
  36. name: "PathAndType",
  37. runtime: types.Runtime{Path: "/bin/true", Type: "io.containerd.runsc.v1"},
  38. expectErr: "cannot configure both",
  39. },
  40. {
  41. name: "PathAndOptions",
  42. runtime: types.Runtime{Path: "/bin/true", Options: map[string]interface{}{"a": "b"}},
  43. expectErr: "options cannot be used with a path runtime",
  44. },
  45. {
  46. name: "TypeAndArgs",
  47. runtime: types.Runtime{Type: "io.containerd.runsc.v1", Args: []string{"--version"}},
  48. expectErr: "args cannot be used with a runtimeType runtime",
  49. },
  50. {
  51. name: "PathArgsOptions",
  52. runtime: types.Runtime{
  53. Path: "/bin/true",
  54. Args: []string{"--version"},
  55. Options: map[string]interface{}{"hmm": 3},
  56. },
  57. expectErr: "options cannot be used with a path runtime",
  58. },
  59. {
  60. name: "TypeOptionsArgs",
  61. runtime: types.Runtime{
  62. Type: "io.containerd.kata.v2",
  63. Options: map[string]interface{}{"a": "b"},
  64. Args: []string{"--help"},
  65. },
  66. expectErr: "args cannot be used with a runtimeType runtime",
  67. },
  68. {
  69. name: "PathArgsTypeOptions",
  70. runtime: types.Runtime{
  71. Path: "/bin/true",
  72. Args: []string{"foo"},
  73. Type: "io.containerd.runsc.v1",
  74. Options: map[string]interface{}{"a": "b"},
  75. },
  76. expectErr: "cannot configure both",
  77. },
  78. }
  79. for _, tt := range cases {
  80. t.Run(tt.name, func(t *testing.T) {
  81. cfg, err := config.New()
  82. assert.NilError(t, err)
  83. cfg.Root = t.TempDir()
  84. cfg.Runtimes["myruntime"] = tt.runtime
  85. d := &Daemon{}
  86. d.configStore.Store(cfg)
  87. assert.Assert(t, os.Mkdir(filepath.Join(d.config().Root, "runtimes"), 0700))
  88. err = d.initRuntimes(d.config())
  89. assert.Check(t, is.ErrorContains(err, tt.expectErr))
  90. })
  91. }
  92. }
  93. func TestGetRuntime(t *testing.T) {
  94. // Configured runtimes can have any arbitrary name, including names
  95. // which would not be allowed as implicit runtime names. Explicit takes
  96. // precedence over implicit.
  97. const configuredRtName = "my/custom.runtime.v1"
  98. configuredRuntime := types.Runtime{Path: "/bin/true"}
  99. const rtWithArgsName = "withargs"
  100. rtWithArgs := types.Runtime{
  101. Path: "/bin/false",
  102. Args: []string{"--version"},
  103. }
  104. const shimWithOptsName = "shimwithopts"
  105. shimWithOpts := types.Runtime{
  106. Type: plugin.RuntimeRuncV2,
  107. Options: map[string]interface{}{"IoUid": 42},
  108. }
  109. const shimAliasName = "wasmedge"
  110. shimAlias := types.Runtime{Type: "io.containerd.wasmedge.v1"}
  111. const configuredShimByPathName = "shimwithpath"
  112. configuredShimByPath := types.Runtime{Type: "/path/to/my/shim"}
  113. cfg, err := config.New()
  114. assert.NilError(t, err)
  115. cfg.Root = t.TempDir()
  116. assert.Assert(t, os.Mkdir(filepath.Join(cfg.Root, "runtimes"), 0700))
  117. cfg.Runtimes = map[string]types.Runtime{
  118. configuredRtName: configuredRuntime,
  119. rtWithArgsName: rtWithArgs,
  120. shimWithOptsName: shimWithOpts,
  121. shimAliasName: shimAlias,
  122. configuredShimByPathName: configuredShimByPath,
  123. }
  124. configureRuntimes(cfg)
  125. d := &Daemon{}
  126. d.configStore.Store(cfg)
  127. assert.Assert(t, d.loadRuntimes())
  128. stockRuntime, ok := cfg.Runtimes[config.StockRuntimeName]
  129. assert.Assert(t, ok, "stock runtime could not be found (test needs to be updated)")
  130. configdOpts := *stockRuntime.ShimConfig.Opts.(*v2runcoptions.Options)
  131. configdOpts.BinaryName = configuredRuntime.Path
  132. for _, tt := range []struct {
  133. name, runtime string
  134. wantShim string
  135. wantOpts interface{}
  136. }{
  137. {
  138. name: "StockRuntime",
  139. runtime: config.StockRuntimeName,
  140. wantShim: stockRuntime.ShimConfig.Binary,
  141. wantOpts: stockRuntime.ShimConfig.Opts,
  142. },
  143. {
  144. name: "ShimName",
  145. runtime: "io.containerd.my-shim.v42",
  146. wantShim: "io.containerd.my-shim.v42",
  147. },
  148. {
  149. // containerd is pretty loose about the format of runtime names. Perhaps too
  150. // loose. The only requirements are that the name contain a dot and (depending
  151. // on the containerd version) not start with a dot. It does not enforce any
  152. // particular format of the dot-delimited components of the name.
  153. name: "VersionlessShimName",
  154. runtime: "io.containerd.my-shim",
  155. wantShim: "io.containerd.my-shim",
  156. },
  157. {
  158. name: "IllformedShimName",
  159. runtime: "myshim",
  160. },
  161. {
  162. name: "EmptyString",
  163. runtime: "",
  164. },
  165. {
  166. name: "PathToShim",
  167. runtime: "/path/to/runc",
  168. },
  169. {
  170. name: "PathToShimName",
  171. runtime: "/path/to/io.containerd.runc.v2",
  172. },
  173. {
  174. name: "RelPathToShim",
  175. runtime: "my/io.containerd.runc.v2",
  176. },
  177. {
  178. name: "ConfiguredRuntime",
  179. runtime: configuredRtName,
  180. wantShim: stockRuntime.ShimConfig.Binary,
  181. wantOpts: &configdOpts,
  182. },
  183. {
  184. name: "RuntimeWithArgs",
  185. runtime: rtWithArgsName,
  186. wantShim: stockRuntime.ShimConfig.Binary,
  187. wantOpts: defaultV2ShimConfig(
  188. d.config(),
  189. d.rewriteRuntimePath(
  190. d.config(),
  191. rtWithArgsName,
  192. rtWithArgs.Path,
  193. rtWithArgs.Args)).Opts,
  194. },
  195. {
  196. name: "ShimWithOpts",
  197. runtime: shimWithOptsName,
  198. wantShim: shimWithOpts.Type,
  199. wantOpts: &v2runcoptions.Options{IoUid: 42},
  200. },
  201. {
  202. name: "ShimAlias",
  203. runtime: shimAliasName,
  204. wantShim: shimAlias.Type,
  205. },
  206. {
  207. name: "ConfiguredShimByPath",
  208. runtime: configuredShimByPathName,
  209. wantShim: configuredShimByPath.Type,
  210. },
  211. } {
  212. tt := tt
  213. t.Run(tt.name, func(t *testing.T) {
  214. gotShim, gotOpts, err := d.getRuntime(cfg, tt.runtime)
  215. assert.Check(t, is.Equal(gotShim, tt.wantShim))
  216. assert.Check(t, is.DeepEqual(gotOpts, tt.wantOpts))
  217. if tt.wantShim != "" {
  218. assert.Check(t, err)
  219. } else {
  220. assert.Check(t, errdefs.IsInvalidParameter(err))
  221. }
  222. })
  223. }
  224. }