github.com/bigcommerce/nomad@v0.9.3-bc/drivers/docker/config_test.go (about)

     1  package docker
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
     7  	"github.com/stretchr/testify/require"
     8  )
     9  
    10  func TestConfig_ParseHCL(t *testing.T) {
    11  	cases := []struct {
    12  		name string
    13  
    14  		input    string
    15  		expected *TaskConfig
    16  	}{
    17  		{
    18  			"basic image",
    19  			`config {
    20  				image = "redis:3.2"
    21  			}`,
    22  			&TaskConfig{
    23  				Image:   "redis:3.2",
    24  				Devices: []DockerDevice{},
    25  				Mounts:  []DockerMount{},
    26  			},
    27  		},
    28  	}
    29  
    30  	parser := hclutils.NewConfigParser(taskConfigSpec)
    31  	for _, c := range cases {
    32  		c := c
    33  		t.Run(c.name, func(t *testing.T) {
    34  			var tc *TaskConfig
    35  
    36  			parser.ParseHCL(t, c.input, &tc)
    37  
    38  			require.EqualValues(t, c.expected, tc)
    39  
    40  		})
    41  	}
    42  }
    43  
    44  func TestConfig_ParseJSON(t *testing.T) {
    45  	cases := []struct {
    46  		name     string
    47  		input    string
    48  		expected TaskConfig
    49  	}{
    50  		{
    51  			name:  "nil values for blocks are safe",
    52  			input: `{"Config": {"image": "bash:3", "mounts": null}}`,
    53  			expected: TaskConfig{
    54  				Image:   "bash:3",
    55  				Mounts:  []DockerMount{},
    56  				Devices: []DockerDevice{},
    57  			},
    58  		},
    59  		{
    60  			name:  "nil values for 'volumes' field are safe",
    61  			input: `{"Config": {"image": "bash:3", "volumes": null}}`,
    62  			expected: TaskConfig{
    63  				Image:   "bash:3",
    64  				Mounts:  []DockerMount{},
    65  				Devices: []DockerDevice{},
    66  			},
    67  		},
    68  		{
    69  			name:  "nil values for 'args' field are safe",
    70  			input: `{"Config": {"image": "bash:3", "args": null}}`,
    71  			expected: TaskConfig{
    72  				Image:   "bash:3",
    73  				Mounts:  []DockerMount{},
    74  				Devices: []DockerDevice{},
    75  			},
    76  		},
    77  		{
    78  			name:  "nil values for string fields are safe",
    79  			input: `{"Config": {"image": "bash:3", "command": null}}`,
    80  			expected: TaskConfig{
    81  				Image:   "bash:3",
    82  				Mounts:  []DockerMount{},
    83  				Devices: []DockerDevice{},
    84  			},
    85  		},
    86  	}
    87  
    88  	for _, c := range cases {
    89  		c := c
    90  		t.Run(c.name, func(t *testing.T) {
    91  			var tc TaskConfig
    92  			hclutils.NewConfigParser(taskConfigSpec).ParseJson(t, c.input, &tc)
    93  
    94  			require.Equal(t, c.expected, tc)
    95  		})
    96  	}
    97  }
    98  
    99  func TestConfig_PortMap_Deserialization(t *testing.T) {
   100  	parser := hclutils.NewConfigParser(taskConfigSpec)
   101  
   102  	expectedMap := map[string]int{
   103  		"ssh":   25,
   104  		"http":  80,
   105  		"https": 443,
   106  	}
   107  
   108  	t.Run("parsing hcl block case", func(t *testing.T) {
   109  		validHCL := `
   110  config {
   111    image = "redis"
   112    port_map {
   113      ssh   = 25
   114      http  = 80
   115      https = 443
   116    }
   117  }`
   118  
   119  		var tc *TaskConfig
   120  		parser.ParseHCL(t, validHCL, &tc)
   121  
   122  		require.EqualValues(t, expectedMap, tc.PortMap)
   123  	})
   124  
   125  	t.Run("parsing hcl assignment case", func(t *testing.T) {
   126  		validHCL := `
   127  config {
   128    image = "redis"
   129    port_map = {
   130      ssh   = 25
   131      http  = 80
   132      https = 443
   133    }
   134  }`
   135  
   136  		var tc *TaskConfig
   137  		parser.ParseHCL(t, validHCL, &tc)
   138  
   139  		require.EqualValues(t, expectedMap, tc.PortMap)
   140  	})
   141  
   142  	validJsons := []struct {
   143  		name string
   144  		json string
   145  	}{
   146  		{
   147  			"single map in an array",
   148  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25, "http": 80, "https": 443}]}}`,
   149  		},
   150  		{
   151  			"array of single map entries",
   152  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25}, {"http": 80}, {"https": 443}]}}`,
   153  		},
   154  		{
   155  			"array of maps",
   156  			`{"Config": {"image": "redis", "port_map": [{"ssh": 25, "http": 80}, {"https": 443}]}}`,
   157  		},
   158  	}
   159  
   160  	for _, c := range validJsons {
   161  		t.Run("json:"+c.name, func(t *testing.T) {
   162  			var tc *TaskConfig
   163  			parser.ParseJson(t, c.json, &tc)
   164  
   165  			require.EqualValues(t, expectedMap, tc.PortMap)
   166  		})
   167  	}
   168  
   169  }
   170  
   171  func TestConfig_ParseAllHCL(t *testing.T) {
   172  	cfgStr := `
   173  config {
   174    image = "redis:3.2"
   175    advertise_ipv6_address = true
   176    args = ["command_arg1", "command_arg2"]
   177    auth {
   178      username = "myusername"
   179      password = "mypassword"
   180      email = "myemail@example.com"
   181      server_address = "https://example.com"
   182    }
   183  
   184    auth_soft_fail = true
   185    cap_add = ["CAP_SYS_NICE"]
   186    cap_drop = ["CAP_SYS_ADMIN", "CAP_SYS_TIME"]
   187    command = "/bin/bash"
   188    cpu_hard_limit = true
   189    cpu_cfs_period = 20
   190    devices = [
   191      {"host_path"="/dev/null", "container_path"="/tmp/container-null", cgroup_permissions="rwm"},
   192      {"host_path"="/dev/random", "container_path"="/tmp/container-random"},
   193    ]
   194    dns_search_domains = ["sub.example.com", "sub2.example.com"]
   195    dns_options = ["debug", "attempts:10"]
   196    dns_servers = ["8.8.8.8", "1.1.1.1"]
   197    entrypoint = ["/bin/bash", "-c"]
   198    extra_hosts = ["127.0.0.1  localhost.example.com"]
   199    force_pull = true
   200    hostname = "self.example.com"
   201    interactive = true
   202    ipc_mode = "host"
   203    ipv4_address = "10.0.2.1"
   204    ipv6_address = "2601:184:407f:b37c:d834:412e:1f86:7699"
   205    labels {
   206      owner = "hashicorp-nomad"
   207      key = "val"
   208    }
   209    load = "/tmp/image.tar.gz"
   210    logging {
   211      driver = "json-file-driver"
   212      type   = "json-file"
   213      config {
   214        "max-file" = "3"
   215        "max-size" = "10m"
   216      }
   217    }
   218    mac_address = "02:42:ac:11:00:02"
   219    mounts = [
   220      {
   221        type = "bind"
   222        target = "/bind-target",
   223        source = "/bind-source"
   224        readonly = true
   225        bind_options {
   226          propagation = "rshared"
   227        }
   228      },
   229      {
   230        type = "tmpfs"
   231        target = "/tmpfs-target",
   232        readonly = true
   233        tmpfs_options {
   234          size = 30000
   235          mode = 0777
   236        }
   237      },
   238      {
   239        type = "volume"
   240        target = "/volume-target"
   241        source = "/volume-source"
   242        readonly = true
   243        volume_options {
   244          no_copy = true
   245          labels {
   246            label_key = "label_value"
   247  	}
   248          driver_config {
   249            name = "nfs"
   250            options {
   251              option_key = "option_value"
   252            }
   253          }
   254        }
   255      },
   256    ]
   257    network_aliases = ["redis"]
   258    network_mode = "host"
   259    pids_limit = 2000
   260    pid_mode = "host"
   261    port_map {
   262      http = 80
   263      redis = 6379
   264    }
   265    privileged = true
   266    readonly_rootfs = true
   267    security_opt = [
   268      "credentialspec=file://gmsaUser.json"
   269    ],
   270    shm_size = 30000
   271    storage_opt {
   272      dm.thinpooldev = "dev/mapper/thin-pool"
   273      dm.use_deferred_deletion = "true"
   274      dm.use_deferred_removal = "true"
   275  
   276    }
   277    sysctl {
   278      net.core.somaxconn = "16384"
   279    }
   280    tty = true
   281    ulimit {
   282      nproc = "4242"
   283      nofile = "2048:4096"
   284    }
   285    uts_mode = "host"
   286    userns_mode = "host"
   287    volumes = [
   288      "/host-path:/container-path:rw",
   289    ]
   290    volume_driver = "host"
   291    work_dir = "/tmp/workdir"
   292  }`
   293  
   294  	expected := &TaskConfig{
   295  		Image:             "redis:3.2",
   296  		AdvertiseIPv6Addr: true,
   297  		Args:              []string{"command_arg1", "command_arg2"},
   298  		Auth: DockerAuth{
   299  			Username:   "myusername",
   300  			Password:   "mypassword",
   301  			Email:      "myemail@example.com",
   302  			ServerAddr: "https://example.com",
   303  		},
   304  		AuthSoftFail: true,
   305  		CapAdd:       []string{"CAP_SYS_NICE"},
   306  		CapDrop:      []string{"CAP_SYS_ADMIN", "CAP_SYS_TIME"},
   307  		Command:      "/bin/bash",
   308  		CPUHardLimit: true,
   309  		CPUCFSPeriod: 20,
   310  		Devices: []DockerDevice{
   311  			{
   312  				HostPath:          "/dev/null",
   313  				ContainerPath:     "/tmp/container-null",
   314  				CgroupPermissions: "rwm",
   315  			},
   316  			{
   317  				HostPath:          "/dev/random",
   318  				ContainerPath:     "/tmp/container-random",
   319  				CgroupPermissions: "",
   320  			},
   321  		},
   322  		DNSSearchDomains: []string{"sub.example.com", "sub2.example.com"},
   323  		DNSOptions:       []string{"debug", "attempts:10"},
   324  		DNSServers:       []string{"8.8.8.8", "1.1.1.1"},
   325  		Entrypoint:       []string{"/bin/bash", "-c"},
   326  		ExtraHosts:       []string{"127.0.0.1  localhost.example.com"},
   327  		ForcePull:        true,
   328  		Hostname:         "self.example.com",
   329  		Interactive:      true,
   330  		IPCMode:          "host",
   331  		IPv4Address:      "10.0.2.1",
   332  		IPv6Address:      "2601:184:407f:b37c:d834:412e:1f86:7699",
   333  		Labels: map[string]string{
   334  			"owner": "hashicorp-nomad",
   335  			"key":   "val",
   336  		},
   337  		LoadImage: "/tmp/image.tar.gz",
   338  		Logging: DockerLogging{
   339  			Driver: "json-file-driver",
   340  			Type:   "json-file",
   341  			Config: map[string]string{
   342  				"max-file": "3",
   343  				"max-size": "10m",
   344  			}},
   345  		MacAddress: "02:42:ac:11:00:02",
   346  		Mounts: []DockerMount{
   347  			{
   348  				Type:     "bind",
   349  				Target:   "/bind-target",
   350  				Source:   "/bind-source",
   351  				ReadOnly: true,
   352  				BindOptions: DockerBindOptions{
   353  					Propagation: "rshared",
   354  				},
   355  			},
   356  			{
   357  				Type:     "tmpfs",
   358  				Target:   "/tmpfs-target",
   359  				Source:   "",
   360  				ReadOnly: true,
   361  				TmpfsOptions: DockerTmpfsOptions{
   362  					SizeBytes: 30000,
   363  					Mode:      511,
   364  				},
   365  			},
   366  			{
   367  				Type:     "volume",
   368  				Target:   "/volume-target",
   369  				Source:   "/volume-source",
   370  				ReadOnly: true,
   371  				VolumeOptions: DockerVolumeOptions{
   372  					NoCopy: true,
   373  					Labels: map[string]string{
   374  						"label_key": "label_value",
   375  					},
   376  					DriverConfig: DockerVolumeDriverConfig{
   377  						Name: "nfs",
   378  						Options: map[string]string{
   379  							"option_key": "option_value",
   380  						},
   381  					},
   382  				},
   383  			},
   384  		},
   385  		NetworkAliases: []string{"redis"},
   386  		NetworkMode:    "host",
   387  		PidsLimit:      2000,
   388  		PidMode:        "host",
   389  		PortMap: map[string]int{
   390  			"http":  80,
   391  			"redis": 6379,
   392  		},
   393  		Privileged:     true,
   394  		ReadonlyRootfs: true,
   395  		SecurityOpt: []string{
   396  			"credentialspec=file://gmsaUser.json",
   397  		},
   398  		ShmSize: 30000,
   399  		StorageOpt: map[string]string{
   400  			"dm.thinpooldev":           "dev/mapper/thin-pool",
   401  			"dm.use_deferred_deletion": "true",
   402  			"dm.use_deferred_removal":  "true",
   403  		},
   404  		Sysctl: map[string]string{
   405  			"net.core.somaxconn": "16384",
   406  		},
   407  		TTY: true,
   408  		Ulimit: map[string]string{
   409  			"nofile": "2048:4096",
   410  			"nproc":  "4242",
   411  		},
   412  		UTSMode:    "host",
   413  		UsernsMode: "host",
   414  		Volumes: []string{
   415  			"/host-path:/container-path:rw",
   416  		},
   417  		VolumeDriver: "host",
   418  		WorkDir:      "/tmp/workdir",
   419  	}
   420  
   421  	var tc *TaskConfig
   422  	hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
   423  
   424  	require.EqualValues(t, expected, tc)
   425  }