github.com/rawahars/moby@v24.0.4+incompatible/daemon/runtime_unix_test.go (about)

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