github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/libcontainer/configs/validate/validator_test.go (about)

     1  package validate
     2  
     3  import (
     4  	"os"
     5  	"path/filepath"
     6  	"testing"
     7  
     8  	"github.com/opencontainers/runc/libcontainer/configs"
     9  	"github.com/opencontainers/runtime-spec/specs-go"
    10  	"golang.org/x/sys/unix"
    11  )
    12  
    13  func TestValidate(t *testing.T) {
    14  	config := &configs.Config{
    15  		Rootfs: "/var",
    16  	}
    17  
    18  	err := Validate(config)
    19  	if err != nil {
    20  		t.Errorf("Expected error to not occur: %+v", err)
    21  	}
    22  }
    23  
    24  func TestValidateWithInvalidRootfs(t *testing.T) {
    25  	dir := "rootfs"
    26  	if err := os.Symlink("/var", dir); err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	defer os.Remove(dir)
    30  
    31  	config := &configs.Config{
    32  		Rootfs: dir,
    33  	}
    34  
    35  	err := Validate(config)
    36  	if err == nil {
    37  		t.Error("Expected error to occur but it was nil")
    38  	}
    39  }
    40  
    41  func TestValidateNetworkWithoutNETNamespace(t *testing.T) {
    42  	network := &configs.Network{Type: "loopback"}
    43  	config := &configs.Config{
    44  		Rootfs:     "/var",
    45  		Namespaces: []configs.Namespace{},
    46  		Networks:   []*configs.Network{network},
    47  	}
    48  
    49  	err := Validate(config)
    50  	if err == nil {
    51  		t.Error("Expected error to occur but it was nil")
    52  	}
    53  }
    54  
    55  func TestValidateNetworkRoutesWithoutNETNamespace(t *testing.T) {
    56  	route := &configs.Route{Gateway: "255.255.255.0"}
    57  	config := &configs.Config{
    58  		Rootfs:     "/var",
    59  		Namespaces: []configs.Namespace{},
    60  		Routes:     []*configs.Route{route},
    61  	}
    62  
    63  	err := Validate(config)
    64  	if err == nil {
    65  		t.Error("Expected error to occur but it was nil")
    66  	}
    67  }
    68  
    69  func TestValidateHostname(t *testing.T) {
    70  	config := &configs.Config{
    71  		Rootfs:   "/var",
    72  		Hostname: "runc",
    73  		Namespaces: configs.Namespaces(
    74  			[]configs.Namespace{
    75  				{Type: configs.NEWUTS},
    76  			},
    77  		),
    78  	}
    79  
    80  	err := Validate(config)
    81  	if err != nil {
    82  		t.Errorf("Expected error to not occur: %+v", err)
    83  	}
    84  }
    85  
    86  func TestValidateUTS(t *testing.T) {
    87  	config := &configs.Config{
    88  		Rootfs:     "/var",
    89  		Domainname: "runc",
    90  		Hostname:   "runc",
    91  		Namespaces: configs.Namespaces(
    92  			[]configs.Namespace{
    93  				{Type: configs.NEWUTS},
    94  			},
    95  		),
    96  	}
    97  
    98  	err := Validate(config)
    99  	if err != nil {
   100  		t.Errorf("Expected error to not occur: %+v", err)
   101  	}
   102  }
   103  
   104  func TestValidateUTSWithoutUTSNamespace(t *testing.T) {
   105  	config := &configs.Config{
   106  		Rootfs:   "/var",
   107  		Hostname: "runc",
   108  	}
   109  
   110  	err := Validate(config)
   111  	if err == nil {
   112  		t.Error("Expected error to occur but it was nil")
   113  	}
   114  
   115  	config = &configs.Config{
   116  		Rootfs:     "/var",
   117  		Domainname: "runc",
   118  	}
   119  
   120  	err = Validate(config)
   121  	if err == nil {
   122  		t.Error("Expected error to occur but it was nil")
   123  	}
   124  }
   125  
   126  func TestValidateSecurityWithMaskPaths(t *testing.T) {
   127  	config := &configs.Config{
   128  		Rootfs:    "/var",
   129  		MaskPaths: []string{"/proc/kcore"},
   130  		Namespaces: configs.Namespaces(
   131  			[]configs.Namespace{
   132  				{Type: configs.NEWNS},
   133  			},
   134  		),
   135  	}
   136  
   137  	err := Validate(config)
   138  	if err != nil {
   139  		t.Errorf("Expected error to not occur: %+v", err)
   140  	}
   141  }
   142  
   143  func TestValidateSecurityWithROPaths(t *testing.T) {
   144  	config := &configs.Config{
   145  		Rootfs:        "/var",
   146  		ReadonlyPaths: []string{"/proc/sys"},
   147  		Namespaces: configs.Namespaces(
   148  			[]configs.Namespace{
   149  				{Type: configs.NEWNS},
   150  			},
   151  		),
   152  	}
   153  
   154  	err := Validate(config)
   155  	if err != nil {
   156  		t.Errorf("Expected error to not occur: %+v", err)
   157  	}
   158  }
   159  
   160  func TestValidateSecurityWithoutNEWNS(t *testing.T) {
   161  	config := &configs.Config{
   162  		Rootfs:        "/var",
   163  		MaskPaths:     []string{"/proc/kcore"},
   164  		ReadonlyPaths: []string{"/proc/sys"},
   165  	}
   166  
   167  	err := Validate(config)
   168  	if err == nil {
   169  		t.Error("Expected error to occur but it was nil")
   170  	}
   171  }
   172  
   173  func TestValidateUserNamespace(t *testing.T) {
   174  	if _, err := os.Stat("/proc/self/ns/user"); os.IsNotExist(err) {
   175  		t.Skip("Test requires userns.")
   176  	}
   177  	config := &configs.Config{
   178  		Rootfs: "/var",
   179  		Namespaces: configs.Namespaces(
   180  			[]configs.Namespace{
   181  				{Type: configs.NEWUSER},
   182  			},
   183  		),
   184  		UIDMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   185  		GIDMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   186  	}
   187  
   188  	err := Validate(config)
   189  	if err != nil {
   190  		t.Errorf("expected error to not occur %+v", err)
   191  	}
   192  }
   193  
   194  func TestValidateUsernsMappingWithoutNamespace(t *testing.T) {
   195  	config := &configs.Config{
   196  		Rootfs:      "/var",
   197  		UIDMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   198  		GIDMappings: []configs.IDMap{{HostID: 0, ContainerID: 123, Size: 100}},
   199  	}
   200  
   201  	err := Validate(config)
   202  	if err == nil {
   203  		t.Error("Expected error to occur but it was nil")
   204  	}
   205  }
   206  
   207  func TestValidateTimeNamespace(t *testing.T) {
   208  	if _, err := os.Stat("/proc/self/ns/time"); os.IsNotExist(err) {
   209  		t.Skip("Test requires timens.")
   210  	}
   211  	config := &configs.Config{
   212  		Rootfs: "/var",
   213  		Namespaces: configs.Namespaces(
   214  			[]configs.Namespace{
   215  				{Type: configs.NEWTIME},
   216  			},
   217  		),
   218  	}
   219  
   220  	err := Validate(config)
   221  	if err != nil {
   222  		t.Errorf("expected error to not occur %+v", err)
   223  	}
   224  }
   225  
   226  func TestValidateTimeNamespaceWithBothPathAndTimeOffset(t *testing.T) {
   227  	if _, err := os.Stat("/proc/self/ns/time"); os.IsNotExist(err) {
   228  		t.Skip("Test requires timens.")
   229  	}
   230  	config := &configs.Config{
   231  		Rootfs: "/var",
   232  		Namespaces: configs.Namespaces(
   233  			[]configs.Namespace{
   234  				{Type: configs.NEWTIME, Path: "/proc/1/ns/time"},
   235  			},
   236  		),
   237  		TimeOffsets: map[string]specs.LinuxTimeOffset{
   238  			"boottime":  {Secs: 150, Nanosecs: 314159},
   239  			"monotonic": {Secs: 512, Nanosecs: 271818},
   240  		},
   241  	}
   242  
   243  	err := Validate(config)
   244  	if err == nil {
   245  		t.Error("Expected error to occur but it was nil")
   246  	}
   247  }
   248  
   249  func TestValidateTimeOffsetsWithoutTimeNamespace(t *testing.T) {
   250  	config := &configs.Config{
   251  		Rootfs: "/var",
   252  		TimeOffsets: map[string]specs.LinuxTimeOffset{
   253  			"boottime":  {Secs: 150, Nanosecs: 314159},
   254  			"monotonic": {Secs: 512, Nanosecs: 271818},
   255  		},
   256  	}
   257  
   258  	err := Validate(config)
   259  	if err == nil {
   260  		t.Error("Expected error to occur but it was nil")
   261  	}
   262  }
   263  
   264  // TestConvertSysctlVariableToDotsSeparator tests whether the sysctl variable
   265  // can be correctly converted to a dot as a separator.
   266  func TestConvertSysctlVariableToDotsSeparator(t *testing.T) {
   267  	type testCase struct {
   268  		in  string
   269  		out string
   270  	}
   271  	valid := []testCase{
   272  		{in: "kernel.shm_rmid_forced", out: "kernel.shm_rmid_forced"},
   273  		{in: "kernel/shm_rmid_forced", out: "kernel.shm_rmid_forced"},
   274  		{in: "net.ipv4.conf.eno2/100.rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
   275  		{in: "net/ipv4/conf/eno2.100/rp_filter", out: "net.ipv4.conf.eno2/100.rp_filter"},
   276  		{in: "net/ipv4/ip_local_port_range", out: "net.ipv4.ip_local_port_range"},
   277  		{in: "kernel/msgmax", out: "kernel.msgmax"},
   278  		{in: "kernel/sem", out: "kernel.sem"},
   279  	}
   280  
   281  	for _, test := range valid {
   282  		convertSysctlVal := convertSysctlVariableToDotsSeparator(test.in)
   283  		if convertSysctlVal != test.out {
   284  			t.Errorf("The sysctl variable was not converted correctly. got: %s, want: %s", convertSysctlVal, test.out)
   285  		}
   286  	}
   287  }
   288  
   289  func TestValidateSysctl(t *testing.T) {
   290  	sysctl := map[string]string{
   291  		"fs.mqueue.ctl":                    "ctl",
   292  		"fs/mqueue/ctl":                    "ctl",
   293  		"net.ctl":                          "ctl",
   294  		"net/ctl":                          "ctl",
   295  		"net.ipv4.conf.eno2/100.rp_filter": "ctl",
   296  		"kernel.ctl":                       "ctl",
   297  		"kernel/ctl":                       "ctl",
   298  	}
   299  
   300  	for k, v := range sysctl {
   301  		config := &configs.Config{
   302  			Rootfs: "/var",
   303  			Sysctl: map[string]string{k: v},
   304  		}
   305  
   306  		err := Validate(config)
   307  		if err == nil {
   308  			t.Error("Expected error to occur but it was nil")
   309  		}
   310  	}
   311  }
   312  
   313  func TestValidateValidSysctl(t *testing.T) {
   314  	sysctl := map[string]string{
   315  		"fs.mqueue.ctl":                    "ctl",
   316  		"fs/mqueue/ctl":                    "ctl",
   317  		"net.ctl":                          "ctl",
   318  		"net/ctl":                          "ctl",
   319  		"net.ipv4.conf.eno2/100.rp_filter": "ctl",
   320  		"kernel.msgmax":                    "ctl",
   321  		"kernel/msgmax":                    "ctl",
   322  	}
   323  
   324  	for k, v := range sysctl {
   325  		config := &configs.Config{
   326  			Rootfs: "/var",
   327  			Sysctl: map[string]string{k: v},
   328  			Namespaces: []configs.Namespace{
   329  				{
   330  					Type: configs.NEWNET,
   331  				},
   332  				{
   333  					Type: configs.NEWIPC,
   334  				},
   335  			},
   336  		}
   337  
   338  		err := Validate(config)
   339  		if err != nil {
   340  			t.Errorf("Expected error to not occur with {%s=%s} but got: %q", k, v, err)
   341  		}
   342  	}
   343  }
   344  
   345  func TestValidateSysctlWithSameNs(t *testing.T) {
   346  	config := &configs.Config{
   347  		Rootfs: "/var",
   348  		Sysctl: map[string]string{"net.ctl": "ctl"},
   349  		Namespaces: configs.Namespaces(
   350  			[]configs.Namespace{
   351  				{
   352  					Type: configs.NEWNET,
   353  					Path: "/proc/self/ns/net",
   354  				},
   355  			},
   356  		),
   357  	}
   358  
   359  	err := Validate(config)
   360  	if err == nil {
   361  		t.Error("Expected error to occur but it was nil")
   362  	}
   363  }
   364  
   365  func TestValidateSysctlWithBindHostNetNS(t *testing.T) {
   366  	if os.Getuid() != 0 {
   367  		t.Skip("requires root")
   368  	}
   369  
   370  	const selfnet = "/proc/self/ns/net"
   371  
   372  	file := filepath.Join(t.TempDir(), "default")
   373  	fd, err := os.Create(file)
   374  	if err != nil {
   375  		t.Fatal(err)
   376  	}
   377  	defer os.Remove(file)
   378  	fd.Close()
   379  
   380  	if err := unix.Mount(selfnet, file, "bind", unix.MS_BIND, ""); err != nil {
   381  		t.Fatalf("can't bind-mount %s to %s: %s", selfnet, file, err)
   382  	}
   383  	defer func() {
   384  		_ = unix.Unmount(file, unix.MNT_DETACH)
   385  	}()
   386  
   387  	config := &configs.Config{
   388  		Rootfs: "/var",
   389  		Sysctl: map[string]string{"net.ctl": "ctl", "net.foo": "bar"},
   390  		Namespaces: configs.Namespaces(
   391  			[]configs.Namespace{
   392  				{
   393  					Type: configs.NEWNET,
   394  					Path: file,
   395  				},
   396  			},
   397  		),
   398  	}
   399  
   400  	if err := Validate(config); err == nil {
   401  		t.Error("Expected error to occur but it was nil")
   402  	}
   403  }
   404  
   405  func TestValidateSysctlWithoutNETNamespace(t *testing.T) {
   406  	config := &configs.Config{
   407  		Rootfs:     "/var",
   408  		Sysctl:     map[string]string{"net.ctl": "ctl"},
   409  		Namespaces: []configs.Namespace{},
   410  	}
   411  
   412  	err := Validate(config)
   413  	if err == nil {
   414  		t.Error("Expected error to occur but it was nil")
   415  	}
   416  }
   417  
   418  func TestValidateMounts(t *testing.T) {
   419  	testCases := []struct {
   420  		isErr bool
   421  		dest  string
   422  	}{
   423  		{isErr: false, dest: "not/an/abs/path"},
   424  		{isErr: false, dest: "./rel/path"},
   425  		{isErr: false, dest: "./rel/path"},
   426  		{isErr: false, dest: "../../path"},
   427  		{isErr: false, dest: "/abs/path"},
   428  		{isErr: false, dest: "/abs/but/../unclean"},
   429  	}
   430  
   431  	for _, tc := range testCases {
   432  		config := &configs.Config{
   433  			Rootfs: "/var",
   434  			Mounts: []*configs.Mount{
   435  				{Destination: tc.dest},
   436  			},
   437  		}
   438  
   439  		err := Validate(config)
   440  		if tc.isErr && err == nil {
   441  			t.Errorf("mount dest: %s, expected error, got nil", tc.dest)
   442  		}
   443  		if !tc.isErr && err != nil {
   444  			t.Errorf("mount dest: %s, expected nil, got error %v", tc.dest, err)
   445  		}
   446  	}
   447  }
   448  
   449  func TestValidateBindMounts(t *testing.T) {
   450  	testCases := []struct {
   451  		isErr bool
   452  		flags int
   453  		data  string
   454  	}{
   455  		{isErr: false, flags: 0, data: ""},
   456  		{isErr: false, flags: unix.MS_RDONLY | unix.MS_NOSYMFOLLOW, data: ""},
   457  
   458  		{isErr: true, flags: 0, data: "idmap"},
   459  		{isErr: true, flags: unix.MS_RDONLY, data: "custom_ext4_flag"},
   460  		{isErr: true, flags: unix.MS_NOATIME, data: "rw=foobar"},
   461  	}
   462  
   463  	for _, tc := range testCases {
   464  		for _, bind := range []string{"bind", "rbind"} {
   465  			bindFlag := map[string]int{
   466  				"bind":  unix.MS_BIND,
   467  				"rbind": unix.MS_BIND | unix.MS_REC,
   468  			}[bind]
   469  
   470  			config := &configs.Config{
   471  				Rootfs: "/var",
   472  				Mounts: []*configs.Mount{
   473  					{
   474  						Destination: "/",
   475  						Flags:       tc.flags | bindFlag,
   476  						Data:        tc.data,
   477  					},
   478  				},
   479  			}
   480  
   481  			err := Validate(config)
   482  			if tc.isErr && err == nil {
   483  				t.Errorf("%s mount flags:0x%x data:%v, expected error, got nil", bind, tc.flags, tc.data)
   484  			}
   485  			if !tc.isErr && err != nil {
   486  				t.Errorf("%s mount flags:0x%x data:%v, expected nil, got error %v", bind, tc.flags, tc.data, err)
   487  			}
   488  		}
   489  	}
   490  }
   491  
   492  func TestValidateIDMapMounts(t *testing.T) {
   493  	mapping := []configs.IDMap{
   494  		{
   495  			ContainerID: 0,
   496  			HostID:      10000,
   497  			Size:        1,
   498  		},
   499  	}
   500  
   501  	testCases := []struct {
   502  		name   string
   503  		isErr  bool
   504  		config *configs.Config
   505  	}{
   506  		{
   507  			name:  "idmap non-bind mount",
   508  			isErr: true,
   509  			config: &configs.Config{
   510  				UIDMappings: mapping,
   511  				GIDMappings: mapping,
   512  				Mounts: []*configs.Mount{
   513  					{
   514  						Source:      "/dev/sda1",
   515  						Destination: "/abs/path/",
   516  						Device:      "ext4",
   517  						IDMapping: &configs.MountIDMapping{
   518  							UIDMappings: mapping,
   519  							GIDMappings: mapping,
   520  						},
   521  					},
   522  				},
   523  			},
   524  		},
   525  		{
   526  			name:  "idmap option non-bind mount",
   527  			isErr: true,
   528  			config: &configs.Config{
   529  				Mounts: []*configs.Mount{
   530  					{
   531  						Source:      "/dev/sda1",
   532  						Destination: "/abs/path/",
   533  						Device:      "ext4",
   534  						IDMapping:   &configs.MountIDMapping{},
   535  					},
   536  				},
   537  			},
   538  		},
   539  		{
   540  			name:  "ridmap option non-bind mount",
   541  			isErr: true,
   542  			config: &configs.Config{
   543  				Mounts: []*configs.Mount{
   544  					{
   545  						Source:      "/dev/sda1",
   546  						Destination: "/abs/path/",
   547  						Device:      "ext4",
   548  						IDMapping: &configs.MountIDMapping{
   549  							Recursive: true,
   550  						},
   551  					},
   552  				},
   553  			},
   554  		},
   555  		{
   556  			name:  "idmap mount no uid mapping",
   557  			isErr: true,
   558  			config: &configs.Config{
   559  				Mounts: []*configs.Mount{
   560  					{
   561  						Source:      "/abs/path/",
   562  						Destination: "/abs/path/",
   563  						Flags:       unix.MS_BIND,
   564  						IDMapping: &configs.MountIDMapping{
   565  							GIDMappings: mapping,
   566  						},
   567  					},
   568  				},
   569  			},
   570  		},
   571  		{
   572  			name:  "idmap mount no gid mapping",
   573  			isErr: true,
   574  			config: &configs.Config{
   575  				Mounts: []*configs.Mount{
   576  					{
   577  						Source:      "/abs/path/",
   578  						Destination: "/abs/path/",
   579  						Flags:       unix.MS_BIND,
   580  						IDMapping: &configs.MountIDMapping{
   581  							UIDMappings: mapping,
   582  						},
   583  					},
   584  				},
   585  			},
   586  		},
   587  		{
   588  			name:  "rootless idmap mount",
   589  			isErr: true,
   590  			config: &configs.Config{
   591  				RootlessEUID: true,
   592  				UIDMappings:  mapping,
   593  				GIDMappings:  mapping,
   594  				Mounts: []*configs.Mount{
   595  					{
   596  						Source:      "/abs/path/",
   597  						Destination: "/abs/path/",
   598  						Flags:       unix.MS_BIND,
   599  						IDMapping: &configs.MountIDMapping{
   600  							UIDMappings: mapping,
   601  							GIDMappings: mapping,
   602  						},
   603  					},
   604  				},
   605  			},
   606  		},
   607  		{
   608  			name: "idmap mounts without abs source path",
   609  			config: &configs.Config{
   610  				UIDMappings: mapping,
   611  				GIDMappings: mapping,
   612  				Mounts: []*configs.Mount{
   613  					{
   614  						Source:      "./rel/path/",
   615  						Destination: "/abs/path/",
   616  						Flags:       unix.MS_BIND,
   617  						IDMapping: &configs.MountIDMapping{
   618  							UIDMappings: mapping,
   619  							GIDMappings: mapping,
   620  						},
   621  					},
   622  				},
   623  			},
   624  		},
   625  		{
   626  			name: "idmap mounts without abs dest path",
   627  			config: &configs.Config{
   628  				UIDMappings: mapping,
   629  				GIDMappings: mapping,
   630  				Mounts: []*configs.Mount{
   631  					{
   632  						Source:      "/abs/path/",
   633  						Destination: "./rel/path/",
   634  						Flags:       unix.MS_BIND,
   635  						IDMapping: &configs.MountIDMapping{
   636  							UIDMappings: mapping,
   637  							GIDMappings: mapping,
   638  						},
   639  					},
   640  				},
   641  			},
   642  		},
   643  		{
   644  			name: "simple idmap mount",
   645  			config: &configs.Config{
   646  				UIDMappings: mapping,
   647  				GIDMappings: mapping,
   648  				Mounts: []*configs.Mount{
   649  					{
   650  						Source:      "/another-abs/path/",
   651  						Destination: "/abs/path/",
   652  						Flags:       unix.MS_BIND,
   653  						IDMapping: &configs.MountIDMapping{
   654  							UIDMappings: mapping,
   655  							GIDMappings: mapping,
   656  						},
   657  					},
   658  				},
   659  			},
   660  		},
   661  		{
   662  			name: "idmap mount with more flags",
   663  			config: &configs.Config{
   664  				UIDMappings: mapping,
   665  				GIDMappings: mapping,
   666  				Mounts: []*configs.Mount{
   667  					{
   668  						Source:      "/another-abs/path/",
   669  						Destination: "/abs/path/",
   670  						Flags:       unix.MS_BIND | unix.MS_RDONLY,
   671  						IDMapping: &configs.MountIDMapping{
   672  							UIDMappings: mapping,
   673  							GIDMappings: mapping,
   674  						},
   675  					},
   676  				},
   677  			},
   678  		},
   679  		{
   680  			name: "idmap mount without userns mappings",
   681  			config: &configs.Config{
   682  				Mounts: []*configs.Mount{
   683  					{
   684  						Source:      "/abs/path/",
   685  						Destination: "/abs/path/",
   686  						Flags:       unix.MS_BIND,
   687  						IDMapping: &configs.MountIDMapping{
   688  							UIDMappings: mapping,
   689  							GIDMappings: mapping,
   690  						},
   691  					},
   692  				},
   693  			},
   694  		},
   695  		{
   696  			name: "idmap mounts with different userns and mount mappings",
   697  			config: &configs.Config{
   698  				UIDMappings: mapping,
   699  				GIDMappings: mapping,
   700  				Mounts: []*configs.Mount{
   701  					{
   702  						Source:      "/abs/path/",
   703  						Destination: "/abs/path/",
   704  						Flags:       unix.MS_BIND,
   705  						IDMapping: &configs.MountIDMapping{
   706  							UIDMappings: []configs.IDMap{
   707  								{
   708  									ContainerID: 10,
   709  									HostID:      10,
   710  									Size:        1,
   711  								},
   712  							},
   713  							GIDMappings: mapping,
   714  						},
   715  					},
   716  				},
   717  			},
   718  		},
   719  		{
   720  			name: "idmap mounts with different userns and mount mappings",
   721  			config: &configs.Config{
   722  				UIDMappings: mapping,
   723  				GIDMappings: mapping,
   724  				Mounts: []*configs.Mount{
   725  					{
   726  						Source:      "/abs/path/",
   727  						Destination: "/abs/path/",
   728  						Flags:       unix.MS_BIND,
   729  						IDMapping: &configs.MountIDMapping{
   730  							UIDMappings: mapping,
   731  							GIDMappings: []configs.IDMap{
   732  								{
   733  									ContainerID: 10,
   734  									HostID:      10,
   735  									Size:        1,
   736  								},
   737  							},
   738  						},
   739  					},
   740  				},
   741  			},
   742  		},
   743  		{
   744  			name:  "mount with 'idmap' option but no mappings",
   745  			isErr: true,
   746  			config: &configs.Config{
   747  				Mounts: []*configs.Mount{
   748  					{
   749  						Source:      "/abs/path/",
   750  						Destination: "/abs/path/",
   751  						Flags:       unix.MS_BIND,
   752  						IDMapping:   &configs.MountIDMapping{},
   753  					},
   754  				},
   755  			},
   756  		},
   757  		{
   758  			name:  "mount with 'ridmap' option but no mappings",
   759  			isErr: true,
   760  			config: &configs.Config{
   761  				Mounts: []*configs.Mount{
   762  					{
   763  						Source:      "/abs/path/",
   764  						Destination: "/abs/path/",
   765  						Flags:       unix.MS_BIND,
   766  						IDMapping: &configs.MountIDMapping{
   767  							Recursive: true,
   768  						},
   769  					},
   770  				},
   771  			},
   772  		},
   773  	}
   774  
   775  	for _, tc := range testCases {
   776  		tc := tc
   777  		t.Run(tc.name, func(t *testing.T) {
   778  			config := tc.config
   779  			config.Rootfs = "/var"
   780  
   781  			err := mountsStrict(config)
   782  			if tc.isErr && err == nil {
   783  				t.Error("expected error, got nil")
   784  			}
   785  
   786  			if !tc.isErr && err != nil {
   787  				t.Error(err)
   788  			}
   789  		})
   790  	}
   791  }
   792  
   793  func TestValidateScheduler(t *testing.T) {
   794  	testCases := []struct {
   795  		isErr     bool
   796  		policy    string
   797  		niceValue int32
   798  		priority  int32
   799  		runtime   uint64
   800  		deadline  uint64
   801  		period    uint64
   802  	}{
   803  		{isErr: true, niceValue: 0},
   804  		{isErr: false, policy: "SCHED_OTHER", niceValue: 19},
   805  		{isErr: false, policy: "SCHED_OTHER", niceValue: -20},
   806  		{isErr: true, policy: "SCHED_OTHER", niceValue: 20},
   807  		{isErr: true, policy: "SCHED_OTHER", niceValue: -21},
   808  		{isErr: true, policy: "SCHED_OTHER", priority: 100},
   809  		{isErr: false, policy: "SCHED_FIFO", priority: 100},
   810  		{isErr: true, policy: "SCHED_FIFO", runtime: 20},
   811  		{isErr: true, policy: "SCHED_BATCH", deadline: 30},
   812  		{isErr: true, policy: "SCHED_IDLE", period: 40},
   813  		{isErr: true, policy: "SCHED_DEADLINE", priority: 100},
   814  		{isErr: false, policy: "SCHED_DEADLINE", runtime: 200},
   815  		{isErr: false, policy: "SCHED_DEADLINE", deadline: 300},
   816  		{isErr: false, policy: "SCHED_DEADLINE", period: 400},
   817  		{isErr: true, policy: "SCHED_OTHER", niceValue: 20},
   818  		{isErr: true, policy: "SCHED_OTHER", niceValue: -21},
   819  		{isErr: false, policy: "SCHED_FIFO", priority: 100, niceValue: 100},
   820  	}
   821  
   822  	for _, tc := range testCases {
   823  		scheduler := configs.Scheduler{
   824  			Policy:   specs.LinuxSchedulerPolicy(tc.policy),
   825  			Nice:     tc.niceValue,
   826  			Priority: tc.priority,
   827  			Runtime:  tc.runtime,
   828  			Deadline: tc.deadline,
   829  			Period:   tc.period,
   830  		}
   831  		config := &configs.Config{
   832  			Rootfs:    "/var",
   833  			Scheduler: &scheduler,
   834  		}
   835  
   836  		err := Validate(config)
   837  		if tc.isErr && err == nil {
   838  			t.Errorf("scheduler: %d, expected error, got nil", tc.niceValue)
   839  		}
   840  		if !tc.isErr && err != nil {
   841  			t.Errorf("scheduler: %d, expected nil, got error %v", tc.niceValue, err)
   842  		}
   843  	}
   844  }
   845  
   846  func TestValidateIOPriority(t *testing.T) {
   847  	testCases := []struct {
   848  		isErr    bool
   849  		priority int
   850  	}{
   851  		{isErr: false, priority: 0},
   852  		{isErr: false, priority: 7},
   853  		{isErr: true, priority: -1},
   854  	}
   855  
   856  	for _, tc := range testCases {
   857  		ioPriroty := configs.IOPriority{
   858  			Priority: tc.priority,
   859  		}
   860  		config := &configs.Config{
   861  			Rootfs:     "/var",
   862  			IOPriority: &ioPriroty,
   863  		}
   864  
   865  		err := Validate(config)
   866  		if tc.isErr && err == nil {
   867  			t.Errorf("iopriority: %d, expected error, got nil", tc.priority)
   868  		}
   869  		if !tc.isErr && err != nil {
   870  			t.Errorf("iopriority: %d, expected nil, got error %v", tc.priority, err)
   871  		}
   872  	}
   873  }