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 }