runtime_unix_test.go 6.4 KB

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