github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/drivers/docker/config_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/nomad/ci"
     7  	"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
     8  	"github.com/hashicorp/nomad/plugins/drivers"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestConfig_ParseHCL(t *testing.T) {
    13  	ci.Parallel(t)
    14  
    15  	cases := []struct {
    16  		name string
    17  
    18  		input    string
    19  		expected *TaskConfig
    20  	}{
    21  		{
    22  			"basic image",
    23  			`config {
    24  				image = "redis:7"
    25  			}`,
    26  			&TaskConfig{
    27  				Image:            "redis:7",
    28  				Devices:          []DockerDevice{},
    29  				Mounts:           []DockerMount{},
    30  				MountsList:       []DockerMount{},
    31  				CPUCFSPeriod:     100000,
    32  				ImagePullTimeout: "5m",
    33  			},
    34  		},
    35  	}
    36  
    37  	parser := hclutils.NewConfigParser(taskConfigSpec)
    38  	for _, c := range cases {
    39  		c := c
    40  		t.Run(c.name, func(t *testing.T) {
    41  			var tc *TaskConfig
    42  
    43  			parser.ParseHCL(t, c.input, &tc)
    44  
    45  			require.EqualValues(t, c.expected, tc)
    46  
    47  		})
    48  	}
    49  }
    50  
    51  func TestConfig_ParseJSON(t *testing.T) {
    52  	ci.Parallel(t)
    53  
    54  	cases := []struct {
    55  		name     string
    56  		input    string
    57  		expected TaskConfig
    58  	}{
    59  		{
    60  			name:  "nil values for blocks are safe",
    61  			input: `{"Config": {"image": "bash:3", "mounts": null}}`,
    62  			expected: TaskConfig{
    63  				Image:            "bash:3",
    64  				Mounts:           []DockerMount{},
    65  				MountsList:       []DockerMount{},
    66  				Devices:          []DockerDevice{},
    67  				CPUCFSPeriod:     100000,
    68  				ImagePullTimeout: "5m",
    69  			},
    70  		},
    71  		{
    72  			name:  "nil values for 'volumes' field are safe",
    73  			input: `{"Config": {"image": "bash:3", "volumes": null}}`,
    74  			expected: TaskConfig{
    75  				Image:            "bash:3",
    76  				Mounts:           []DockerMount{},
    77  				MountsList:       []DockerMount{},
    78  				Devices:          []DockerDevice{},
    79  				CPUCFSPeriod:     100000,
    80  				ImagePullTimeout: "5m",
    81  			},
    82  		},
    83  		{
    84  			name:  "nil values for 'args' field are safe",
    85  			input: `{"Config": {"image": "bash:3", "args": null}}`,
    86  			expected: TaskConfig{
    87  				Image:            "bash:3",
    88  				Mounts:           []DockerMount{},
    89  				MountsList:       []DockerMount{},
    90  				Devices:          []DockerDevice{},
    91  				CPUCFSPeriod:     100000,
    92  				ImagePullTimeout: "5m",
    93  			},
    94  		},
    95  		{
    96  			name:  "nil values for string fields are safe",
    97  			input: `{"Config": {"image": "bash:3", "command": null}}`,
    98  			expected: TaskConfig{
    99  				Image:            "bash:3",
   100  				Mounts:           []DockerMount{},
   101  				MountsList:       []DockerMount{},
   102  				Devices:          []DockerDevice{},
   103  				CPUCFSPeriod:     100000,
   104  				ImagePullTimeout: "5m",
   105  			},
   106  		},
   107  	}
   108  
   109  	for _, c := range cases {
   110  		c := c
   111  		t.Run(c.name, func(t *testing.T) {
   112  			var tc TaskConfig
   113  			hclutils.NewConfigParser(taskConfigSpec).ParseJson(t, c.input, &tc)
   114  
   115  			require.Equal(t, c.expected, tc)
   116  		})
   117  	}
   118  }
   119  
   120  func TestConfig_PortMap_Deserialization(t *testing.T) {
   121  	ci.Parallel(t)
   122  
   123  	parser := hclutils.NewConfigParser(taskConfigSpec)
   124  
   125  	expectedMap := map[string]int{
   126  		"ssh":   25,
   127  		"http":  80,
   128  		"https": 443,
   129  	}
   130  
   131  	t.Run("parsing hcl block case", func(t *testing.T) {
   132  		validHCL := `
   133  config {
   134    image = "redis"
   135    port_map {
   136      ssh   = 25
   137      http  = 80
   138      https = 443
   139    }
   140  }`
   141  
   142  		var tc *TaskConfig
   143  		parser.ParseHCL(t, validHCL, &tc)
   144  
   145  		require.EqualValues(t, expectedMap, tc.PortMap)
   146  	})
   147  
   148  	t.Run("parsing hcl assignment case", func(t *testing.T) {
   149  		validHCL := `
   150  config {
   151    image = "redis"
   152    port_map = {
   153      ssh   = 25
   154      http  = 80
   155      https = 443
   156    }
   157  }`
   158  
   159  		var tc *TaskConfig
   160  		parser.ParseHCL(t, validHCL, &tc)
   161  
   162  		require.EqualValues(t, expectedMap, tc.PortMap)
   163  	})
   164  
   165  	validJsons := []struct {
   166  		name string
   167  		json string
   168  	}{
   169  		{
   170  			"single map in an array",
   171  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25, "http": 80, "https": 443}]}}`,
   172  		},
   173  		{
   174  			"array of single map entries",
   175  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25}, {"http": 80}, {"https": 443}]}}`,
   176  		},
   177  		{
   178  			"array of maps",
   179  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25, "http": 80}, {"https": 443}]}}`,
   180  		},
   181  	}
   182  
   183  	for _, c := range validJsons {
   184  		t.Run("json:"+c.name, func(t *testing.T) {
   185  			var tc *TaskConfig
   186  			parser.ParseJson(t, c.json, &tc)
   187  
   188  			require.EqualValues(t, expectedMap, tc.PortMap)
   189  		})
   190  	}
   191  
   192  }
   193  
   194  func TestConfig_ParseAllHCL(t *testing.T) {
   195  	ci.Parallel(t)
   196  
   197  	cfgStr := `
   198  config {
   199    image = "redis:7"
   200    image_pull_timeout = "15m"
   201    advertise_ipv6_address = true
   202    args = ["command_arg1", "command_arg2"]
   203    auth {
   204      username = "myusername"
   205      password = "mypassword"
   206      email = "myemail@example.com"
   207      server_address = "https://example.com"
   208    }
   209  
   210    auth_soft_fail = true
   211    cap_add = ["CAP_SYS_NICE"]
   212    cap_drop = ["CAP_SYS_ADMIN", "CAP_SYS_TIME"]
   213    command = "/bin/bash"
   214    cpu_hard_limit = true
   215    cpu_cfs_period = 20
   216    devices = [
   217      {"host_path"="/dev/null", "container_path"="/tmp/container-null", cgroup_permissions="rwm"},
   218      {"host_path"="/dev/random", "container_path"="/tmp/container-random"},
   219    ]
   220    dns_search_domains = ["sub.example.com", "sub2.example.com"]
   221    dns_options = ["debug", "attempts:10"]
   222    dns_servers = ["8.8.8.8", "1.1.1.1"]
   223    entrypoint = ["/bin/bash", "-c"]
   224    extra_hosts = ["127.0.0.1  localhost.example.com"]
   225    force_pull = true
   226    healthchecks {
   227      disable = true
   228    }
   229    hostname = "self.example.com"
   230    interactive = true
   231    ipc_mode = "host"
   232    ipv4_address = "10.0.2.1"
   233    ipv6_address = "2601:184:407f:b37c:d834:412e:1f86:7699"
   234    labels {
   235      owner = "hashicorp-nomad"
   236      key = "val"
   237    }
   238    load = "/tmp/image.tar.gz"
   239    logging {
   240      driver = "json-file-driver"
   241      type   = "json-file"
   242      config {
   243        "max-file" = "3"
   244        "max-size" = "10m"
   245      }
   246    }
   247    mac_address = "02:42:ac:11:00:02"
   248    memory_hard_limit = 512
   249  
   250    mount {
   251      type = "bind"
   252      target ="/mount-bind-target"
   253      source = "/bind-source-mount"
   254      readonly = true
   255      bind_options {
   256        propagation = "rshared"
   257      }
   258    }
   259  
   260    mount {
   261      type = "tmpfs"
   262      target ="/mount-tmpfs-target"
   263      readonly = true
   264      tmpfs_options {
   265        size = 30000
   266        mode = 0777
   267      }
   268    }
   269  
   270    mounts = [
   271      {
   272        type = "bind"
   273        target = "/bind-target",
   274        source = "/bind-source"
   275        readonly = true
   276        bind_options {
   277          propagation = "rshared"
   278        }
   279      },
   280      {
   281        type = "tmpfs"
   282        target = "/tmpfs-target",
   283        readonly = true
   284        tmpfs_options {
   285          size = 30000
   286          mode = 0777
   287        }
   288      },
   289      {
   290        type = "volume"
   291        target = "/volume-target"
   292        source = "/volume-source"
   293        readonly = true
   294        volume_options {
   295          no_copy = true
   296          labels {
   297            label_key = "label_value"
   298  	}
   299          driver_config {
   300            name = "nfs"
   301            options {
   302              option_key = "option_value"
   303            }
   304          }
   305        }
   306      },
   307    ]
   308    network_aliases = ["redis"]
   309    network_mode = "host"
   310    pids_limit = 2000
   311  	pid_mode = "host"
   312  	ports = ["http", "https"]
   313    port_map {
   314      http = 80
   315      redis = 6379
   316    }
   317    privileged = true
   318    readonly_rootfs = true
   319    runtime = "runc"
   320    security_opt = [
   321      "credentialspec=file://gmsaUser.json"
   322    ],
   323    shm_size = 30000
   324    storage_opt {
   325      dm.thinpooldev = "dev/mapper/thin-pool"
   326      dm.use_deferred_deletion = "true"
   327      dm.use_deferred_removal = "true"
   328  
   329    }
   330    sysctl {
   331      net.core.somaxconn = "16384"
   332    }
   333    tty = true
   334    ulimit {
   335      nproc = "4242"
   336      nofile = "2048:4096"
   337    }
   338    uts_mode = "host"
   339    userns_mode = "host"
   340    volumes = [
   341      "/host-path:/container-path:rw",
   342    ]
   343    volume_driver = "host"
   344    work_dir = "/tmp/workdir"
   345  }`
   346  
   347  	expected := &TaskConfig{
   348  		Image:             "redis:7",
   349  		ImagePullTimeout:  "15m",
   350  		AdvertiseIPv6Addr: true,
   351  		Args:              []string{"command_arg1", "command_arg2"},
   352  		Auth: DockerAuth{
   353  			Username:   "myusername",
   354  			Password:   "mypassword",
   355  			Email:      "myemail@example.com",
   356  			ServerAddr: "https://example.com",
   357  		},
   358  		AuthSoftFail: true,
   359  		CapAdd:       []string{"CAP_SYS_NICE"},
   360  		CapDrop:      []string{"CAP_SYS_ADMIN", "CAP_SYS_TIME"},
   361  		Command:      "/bin/bash",
   362  		CPUHardLimit: true,
   363  		CPUCFSPeriod: 20,
   364  		Devices: []DockerDevice{
   365  			{
   366  				HostPath:          "/dev/null",
   367  				ContainerPath:     "/tmp/container-null",
   368  				CgroupPermissions: "rwm",
   369  			},
   370  			{
   371  				HostPath:          "/dev/random",
   372  				ContainerPath:     "/tmp/container-random",
   373  				CgroupPermissions: "",
   374  			},
   375  		},
   376  		DNSSearchDomains: []string{"sub.example.com", "sub2.example.com"},
   377  		DNSOptions:       []string{"debug", "attempts:10"},
   378  		DNSServers:       []string{"8.8.8.8", "1.1.1.1"},
   379  		Entrypoint:       []string{"/bin/bash", "-c"},
   380  		ExtraHosts:       []string{"127.0.0.1  localhost.example.com"},
   381  		ForcePull:        true,
   382  		Healthchecks:     DockerHealthchecks{Disable: true},
   383  		Hostname:         "self.example.com",
   384  		Interactive:      true,
   385  		IPCMode:          "host",
   386  		IPv4Address:      "10.0.2.1",
   387  		IPv6Address:      "2601:184:407f:b37c:d834:412e:1f86:7699",
   388  		Labels: map[string]string{
   389  			"owner": "hashicorp-nomad",
   390  			"key":   "val",
   391  		},
   392  		LoadImage: "/tmp/image.tar.gz",
   393  		Logging: DockerLogging{
   394  			Driver: "json-file-driver",
   395  			Type:   "json-file",
   396  			Config: map[string]string{
   397  				"max-file": "3",
   398  				"max-size": "10m",
   399  			}},
   400  		MacAddress:      "02:42:ac:11:00:02",
   401  		MemoryHardLimit: 512,
   402  		Mounts: []DockerMount{
   403  			{
   404  				Type:     "bind",
   405  				Target:   "/mount-bind-target",
   406  				Source:   "/bind-source-mount",
   407  				ReadOnly: true,
   408  				BindOptions: DockerBindOptions{
   409  					Propagation: "rshared",
   410  				},
   411  			},
   412  			{
   413  				Type:     "tmpfs",
   414  				Target:   "/mount-tmpfs-target",
   415  				Source:   "",
   416  				ReadOnly: true,
   417  				TmpfsOptions: DockerTmpfsOptions{
   418  					SizeBytes: 30000,
   419  					Mode:      511,
   420  				},
   421  			},
   422  		},
   423  		MountsList: []DockerMount{
   424  			{
   425  				Type:     "bind",
   426  				Target:   "/bind-target",
   427  				Source:   "/bind-source",
   428  				ReadOnly: true,
   429  				BindOptions: DockerBindOptions{
   430  					Propagation: "rshared",
   431  				},
   432  			},
   433  			{
   434  				Type:     "tmpfs",
   435  				Target:   "/tmpfs-target",
   436  				Source:   "",
   437  				ReadOnly: true,
   438  				TmpfsOptions: DockerTmpfsOptions{
   439  					SizeBytes: 30000,
   440  					Mode:      511,
   441  				},
   442  			},
   443  			{
   444  				Type:     "volume",
   445  				Target:   "/volume-target",
   446  				Source:   "/volume-source",
   447  				ReadOnly: true,
   448  				VolumeOptions: DockerVolumeOptions{
   449  					NoCopy: true,
   450  					Labels: map[string]string{
   451  						"label_key": "label_value",
   452  					},
   453  					DriverConfig: DockerVolumeDriverConfig{
   454  						Name: "nfs",
   455  						Options: map[string]string{
   456  							"option_key": "option_value",
   457  						},
   458  					},
   459  				},
   460  			},
   461  		},
   462  		NetworkAliases: []string{"redis"},
   463  		NetworkMode:    "host",
   464  		PidsLimit:      2000,
   465  		PidMode:        "host",
   466  		Ports:          []string{"http", "https"},
   467  		PortMap: map[string]int{
   468  			"http":  80,
   469  			"redis": 6379,
   470  		},
   471  		Privileged:     true,
   472  		ReadonlyRootfs: true,
   473  		Runtime:        "runc",
   474  		SecurityOpt: []string{
   475  			"credentialspec=file://gmsaUser.json",
   476  		},
   477  		ShmSize: 30000,
   478  		StorageOpt: map[string]string{
   479  			"dm.thinpooldev":           "dev/mapper/thin-pool",
   480  			"dm.use_deferred_deletion": "true",
   481  			"dm.use_deferred_removal":  "true",
   482  		},
   483  		Sysctl: map[string]string{
   484  			"net.core.somaxconn": "16384",
   485  		},
   486  		TTY: true,
   487  		Ulimit: map[string]string{
   488  			"nofile": "2048:4096",
   489  			"nproc":  "4242",
   490  		},
   491  		UTSMode:    "host",
   492  		UsernsMode: "host",
   493  		Volumes: []string{
   494  			"/host-path:/container-path:rw",
   495  		},
   496  		VolumeDriver: "host",
   497  		WorkDir:      "/tmp/workdir",
   498  	}
   499  
   500  	var tc *TaskConfig
   501  	hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
   502  
   503  	require.EqualValues(t, expected, tc)
   504  }
   505  
   506  // TestConfig_DriverConfig_GC asserts that gc is parsed
   507  // and populated with defaults as expected
   508  func TestConfig_DriverConfig_GC(t *testing.T) {
   509  	ci.Parallel(t)
   510  
   511  	cases := []struct {
   512  		name     string
   513  		config   string
   514  		expected GCConfig
   515  	}{
   516  		{
   517  			name:   "pure default",
   518  			config: `{}`,
   519  			expected: GCConfig{
   520  				Image: true, ImageDelay: "3m", Container: true,
   521  				DanglingContainers: ContainerGCConfig{
   522  					Enabled: true, PeriodStr: "5m", CreationGraceStr: "5m"},
   523  			},
   524  		},
   525  		{
   526  			name:   "partial gc",
   527  			config: `{ gc { } }`,
   528  			expected: GCConfig{
   529  				Image: true, ImageDelay: "3m", Container: true,
   530  				DanglingContainers: ContainerGCConfig{
   531  					Enabled: true, PeriodStr: "5m", CreationGraceStr: "5m"},
   532  			},
   533  		},
   534  		{
   535  			name:   "partial gc",
   536  			config: `{ gc { dangling_containers { } } }`,
   537  			expected: GCConfig{
   538  				Image: true, ImageDelay: "3m", Container: true,
   539  				DanglingContainers: ContainerGCConfig{
   540  					Enabled: true, PeriodStr: "5m", CreationGraceStr: "5m"},
   541  			},
   542  		},
   543  		{
   544  			name:   "partial image",
   545  			config: `{ gc { image = false } }`,
   546  			expected: GCConfig{
   547  				Image: false, ImageDelay: "3m", Container: true,
   548  				DanglingContainers: ContainerGCConfig{
   549  					Enabled: true, PeriodStr: "5m", CreationGraceStr: "5m"},
   550  			},
   551  		},
   552  		{
   553  			name:   "partial image_delay",
   554  			config: `{ gc { image_delay = "1d"} }`,
   555  			expected: GCConfig{
   556  				Image: true, ImageDelay: "1d", Container: true,
   557  				DanglingContainers: ContainerGCConfig{
   558  					Enabled: true, PeriodStr: "5m", CreationGraceStr: "5m"},
   559  			},
   560  		},
   561  		{
   562  			name:   "partial dangling_containers",
   563  			config: `{ gc { dangling_containers { enabled = false } } }`,
   564  			expected: GCConfig{
   565  				Image: true, ImageDelay: "3m", Container: true,
   566  				DanglingContainers: ContainerGCConfig{
   567  					Enabled: false, PeriodStr: "5m", CreationGraceStr: "5m"},
   568  			},
   569  		},
   570  		{
   571  			name:   "incomplete dangling_containers 2",
   572  			config: `{ gc { dangling_containers { period = "10m" } } }`,
   573  			expected: GCConfig{
   574  				Image: true, ImageDelay: "3m", Container: true,
   575  				DanglingContainers: ContainerGCConfig{
   576  					Enabled: true, PeriodStr: "10m", CreationGraceStr: "5m"},
   577  			},
   578  		},
   579  		{
   580  			name: "full default",
   581  			config: `{ gc {
   582  			image = false
   583  			image_delay = "5m"
   584  			container = false
   585  			dangling_containers {
   586  			     enabled = false
   587  			     dry_run = true
   588  			     period = "10m"
   589  			     creation_grace = "20m"
   590  			}}}`,
   591  			expected: GCConfig{
   592  				Image:      false,
   593  				ImageDelay: "5m",
   594  				Container:  false,
   595  				DanglingContainers: ContainerGCConfig{
   596  					Enabled:          false,
   597  					DryRun:           true,
   598  					PeriodStr:        "10m",
   599  					CreationGraceStr: "20m",
   600  				},
   601  			},
   602  		},
   603  	}
   604  
   605  	for _, c := range cases {
   606  		t.Run(c.name, func(t *testing.T) {
   607  			var tc DriverConfig
   608  			hclutils.NewConfigParser(configSpec).ParseHCL(t, "config "+c.config, &tc)
   609  			require.EqualValues(t, c.expected, tc.GC)
   610  
   611  		})
   612  	}
   613  }
   614  
   615  func TestConfig_InternalCapabilities(t *testing.T) {
   616  	ci.Parallel(t)
   617  
   618  	cases := []struct {
   619  		name     string
   620  		config   string
   621  		expected drivers.InternalCapabilities
   622  	}{
   623  		{
   624  			name:     "pure default",
   625  			config:   `{}`,
   626  			expected: drivers.InternalCapabilities{},
   627  		},
   628  		{
   629  			name:     "disabled",
   630  			config:   `{ disable_log_collection = true }`,
   631  			expected: drivers.InternalCapabilities{DisableLogCollection: true},
   632  		},
   633  		{
   634  			name:     "enabled explicitly",
   635  			config:   `{ disable_log_collection = false }`,
   636  			expected: drivers.InternalCapabilities{},
   637  		},
   638  	}
   639  
   640  	for _, c := range cases {
   641  		t.Run(c.name, func(t *testing.T) {
   642  			var tc DriverConfig
   643  			hclutils.NewConfigParser(configSpec).ParseHCL(t, "config "+c.config, &tc)
   644  
   645  			d := &Driver{config: &tc}
   646  			require.Equal(t, c.expected, d.InternalCapabilities())
   647  		})
   648  	}
   649  }
   650  
   651  func TestConfig_DriverConfig_InfraImagePullTimeout(t *testing.T) {
   652  	ci.Parallel(t)
   653  
   654  	cases := []struct {
   655  		name     string
   656  		config   string
   657  		expected string
   658  	}{
   659  		{
   660  			name:     "default",
   661  			config:   `{}`,
   662  			expected: "5m",
   663  		},
   664  		{
   665  			name:     "set explicitly",
   666  			config:   `{ infra_image_pull_timeout = "1m" }`,
   667  			expected: "1m",
   668  		},
   669  	}
   670  
   671  	for _, c := range cases {
   672  		t.Run(c.name, func(t *testing.T) {
   673  			var tc DriverConfig
   674  			hclutils.NewConfigParser(configSpec).ParseHCL(t, "config "+c.config, &tc)
   675  			require.Equal(t, c.expected, tc.InfraImagePullTimeout)
   676  		})
   677  	}
   678  }
   679  
   680  func TestConfig_DriverConfig_PullActivityTimeout(t *testing.T) {
   681  	ci.Parallel(t)
   682  
   683  	cases := []struct {
   684  		name     string
   685  		config   string
   686  		expected string
   687  	}{
   688  		{
   689  			name:     "default",
   690  			config:   `{}`,
   691  			expected: "2m",
   692  		},
   693  		{
   694  			name:     "set explicitly",
   695  			config:   `{ pull_activity_timeout = "5m" }`,
   696  			expected: "5m",
   697  		},
   698  	}
   699  
   700  	for _, c := range cases {
   701  		t.Run(c.name, func(t *testing.T) {
   702  			var tc DriverConfig
   703  			hclutils.NewConfigParser(configSpec).ParseHCL(t, "config "+c.config, &tc)
   704  			require.Equal(t, c.expected, tc.PullActivityTimeout)
   705  		})
   706  	}
   707  }
   708  
   709  func TestConfig_DriverConfig_AllowRuntimes(t *testing.T) {
   710  	ci.Parallel(t)
   711  
   712  	cases := []struct {
   713  		name     string
   714  		config   string
   715  		expected map[string]struct{}
   716  	}{
   717  		{
   718  			name:     "pure default",
   719  			config:   `{}`,
   720  			expected: map[string]struct{}{"runc": {}, "nvidia": {}},
   721  		},
   722  		{
   723  			name:     "custom",
   724  			config:   `{ allow_runtimes = ["runc", "firecracker"]}`,
   725  			expected: map[string]struct{}{"runc": {}, "firecracker": {}},
   726  		},
   727  	}
   728  
   729  	for _, c := range cases {
   730  		t.Run(c.name, func(t *testing.T) {
   731  			var tc map[string]interface{}
   732  			hclutils.NewConfigParser(configSpec).ParseHCL(t, "config "+c.config, &tc)
   733  
   734  			dh := dockerDriverHarness(t, tc)
   735  			d := dh.Impl().(*Driver)
   736  			require.Equal(t, c.expected, d.config.allowRuntimes)
   737  		})
   738  	}
   739  }