From 9bf4b38ab3e92a3f0bf3137e716079b97850a7e4 Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Sat, 2 Feb 2019 12:20:26 -0800 Subject: [PATCH] plugins: update hclutils test The test used old local copies of Docker structs and appeared to be testing an outdated approach to task config decoding. Updated to use real Docker structs so we can do end-to-end unit testing of real Docker task configs. --- helper/pluginutils/hclutils/util_test.go | 341 ++++++++++------------- 1 file changed, 154 insertions(+), 187 deletions(-) diff --git a/helper/pluginutils/hclutils/util_test.go b/helper/pluginutils/hclutils/util_test.go index bfbb7c0a6..c91f5aaf6 100644 --- a/helper/pluginutils/hclutils/util_test.go +++ b/helper/pluginutils/hclutils/util_test.go @@ -1,82 +1,23 @@ -package hclutils +package hclutils_test import ( "testing" "github.com/hashicorp/hcl" "github.com/hashicorp/hcl/hcl/ast" - hcl2 "github.com/hashicorp/hcl2/hcl" "github.com/hashicorp/hcl2/hcldec" - "github.com/hashicorp/nomad/helper" + "github.com/hashicorp/nomad/drivers/docker" + "github.com/hashicorp/nomad/helper/pluginutils/hclspecutils" + "github.com/hashicorp/nomad/helper/pluginutils/hclutils" "github.com/hashicorp/nomad/nomad/structs" + "github.com/hashicorp/nomad/plugins/drivers" "github.com/kr/pretty" "github.com/mitchellh/mapstructure" "github.com/stretchr/testify/require" "github.com/ugorji/go/codec" "github.com/zclconf/go-cty/cty" - "github.com/zclconf/go-cty/cty/gocty" ) -var ( - dockerSpec hcldec.Spec = hcldec.ObjectSpec(map[string]hcldec.Spec{ - "image": &hcldec.AttrSpec{ - Name: "image", - Type: cty.String, - Required: true, - }, - "args": &hcldec.AttrSpec{ - Name: "args", - Type: cty.List(cty.String), - }, - "pids_limit": &hcldec.AttrSpec{ - Name: "pids_limit", - Type: cty.Number, - }, - "port_map": &hcldec.BlockAttrsSpec{ - TypeName: "port_map", - ElementType: cty.String, - }, - - "devices": &hcldec.BlockListSpec{ - TypeName: "devices", - Nested: hcldec.ObjectSpec(map[string]hcldec.Spec{ - "host_path": &hcldec.AttrSpec{ - Name: "host_path", - Type: cty.String, - }, - "container_path": &hcldec.AttrSpec{ - Name: "container_path", - Type: cty.String, - }, - "cgroup_permissions": &hcldec.DefaultSpec{ - Primary: &hcldec.AttrSpec{ - Name: "cgroup_permissions", - Type: cty.String, - }, - Default: &hcldec.LiteralSpec{ - Value: cty.StringVal(""), - }, - }, - }), - }, - }, - ) -) - -type dockerConfig struct { - Image string `cty:"image"` - Args []string `cty:"args"` - PidsLimit *int64 `cty:"pids_limit"` - PortMap map[string]string `cty:"port_map"` - Devices []DockerDevice `cty:"devices"` -} - -type DockerDevice struct { - HostPath string `cty:"host_path"` - ContainerPath string `cty:"container_path"` - CgroupPermissions string `cty:"cgroup_permissions"` -} - func hclConfigToInterface(t *testing.T, config string) interface{} { t.Helper() @@ -121,30 +62,22 @@ func jsonConfigToInterface(t *testing.T, config string) interface{} { } func TestParseHclInterface_Hcl(t *testing.T) { - defaultCtx := &hcl2.EvalContext{ - Functions: GetStdlibFuncs(), - } - variableCtx := &hcl2.EvalContext{ - Functions: GetStdlibFuncs(), - Variables: map[string]cty.Value{ - "NOMAD_ALLOC_INDEX": cty.NumberIntVal(2), - "NOMAD_META_hello": cty.StringVal("world"), - }, - } + dockerDriver := new(docker.Driver) + dockerSpec, err := dockerDriver.TaskConfigSchema() + require.NoError(t, err) + dockerDecSpec, diags := hclspecutils.Convert(dockerSpec) + require.False(t, diags.HasErrors()) - // XXX Useful for determining what cty thinks the type is - //implied, err := gocty.ImpliedType(&dockerConfig{}) - //if err != nil { - //t.Fatalf("implied type failed: %v", err) - //} - - //t.Logf("Implied type: %v", implied.GoString()) + vars := map[string]cty.Value{ + "NOMAD_ALLOC_INDEX": cty.NumberIntVal(2), + "NOMAD_META_hello": cty.StringVal("world"), + } cases := []struct { name string config interface{} spec hcldec.Spec - ctx *hcl2.EvalContext + vars map[string]cty.Value expected interface{} expectedType interface{} }{ @@ -154,13 +87,13 @@ func TestParseHclInterface_Hcl(t *testing.T) { config { image = "redis:3.2" }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "single string attr json", @@ -170,13 +103,13 @@ func TestParseHclInterface_Hcl(t *testing.T) { "image": "redis:3.2" } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "number attr", @@ -185,14 +118,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { image = "redis:3.2" pids_limit = 2 }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PidsLimit: helper.Int64ToPtr(2), - Devices: []DockerDevice{}, + PidsLimit: 2, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "number attr json", @@ -203,14 +136,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { "pids_limit": "2" } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PidsLimit: helper.Int64ToPtr(2), - Devices: []DockerDevice{}, + PidsLimit: 2, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "number attr interpolated", @@ -219,14 +152,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { image = "redis:3.2" pids_limit = "${2 + 2}" }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PidsLimit: helper.Int64ToPtr(4), - Devices: []DockerDevice{}, + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "number attr interploated json", @@ -237,14 +170,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { "pids_limit": "${2 + 2}" } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PidsLimit: helper.Int64ToPtr(4), - Devices: []DockerDevice{}, + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "multi attr", @@ -253,14 +186,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { image = "redis:3.2" args = ["foo", "bar"] }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", Args: []string{"foo", "bar"}, - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "multi attr json", @@ -271,14 +204,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { "args": ["foo", "bar"] } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", Args: []string{"foo", "bar"}, - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "multi attr variables", @@ -288,15 +221,16 @@ func TestParseHclInterface_Hcl(t *testing.T) { args = ["${NOMAD_META_hello}", "${NOMAD_ALLOC_INDEX}"] pids_limit = "${NOMAD_ALLOC_INDEX + 2}" }`), - spec: dockerSpec, - ctx: variableCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + vars: vars, + expected: &docker.TaskConfig{ Image: "redis:3.2", Args: []string{"world", "2"}, - PidsLimit: helper.Int64ToPtr(4), - Devices: []DockerDevice{}, + PidsLimit: 4, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "multi attr variables json", @@ -307,14 +241,14 @@ func TestParseHclInterface_Hcl(t *testing.T) { "args": ["foo", "bar"] } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", Args: []string{"foo", "bar"}, - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "port_map", @@ -322,21 +256,21 @@ func TestParseHclInterface_Hcl(t *testing.T) { config { image = "redis:3.2" port_map { - foo = "db" - bar = "db2" + foo = 1234 + bar = 5678 } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PortMap: map[string]string{ - "foo": "db", - "bar": "db2", + PortMap: map[string]int{ + "foo": 1234, + "bar": 5678, }, - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "port_map json", @@ -345,22 +279,22 @@ func TestParseHclInterface_Hcl(t *testing.T) { "Config": { "image": "redis:3.2", "port_map": [{ - "foo": "db", - "bar": "db2" + "foo": 1234, + "bar": 5678 }] } }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - PortMap: map[string]string{ - "foo": "db", - "bar": "db2", + PortMap: map[string]int{ + "foo": 1234, + "bar": 5678, }, - Devices: []DockerDevice{}, + Devices: []docker.DockerDevice{}, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { name: "devices", @@ -379,11 +313,10 @@ func TestParseHclInterface_Hcl(t *testing.T) { } ] }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - Devices: []DockerDevice{ + Devices: []docker.DockerDevice{ { HostPath: "/dev/sda1", ContainerPath: "/dev/xvdc", @@ -394,33 +327,61 @@ func TestParseHclInterface_Hcl(t *testing.T) { ContainerPath: "/dev/xvdd", }, }, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, { - name: "devices json", + name: "docker_logging", + config: hclConfigToInterface(t, ` + config { + image = "redis:3.2" + network_mode = "host" + dns_servers = ["169.254.1.1"] + logging { + type = "syslog" + config { + tag = "driver-test" + } + } + }`), + spec: dockerDecSpec, + expected: &docker.TaskConfig{ + Image: "redis:3.2", + NetworkMode: "host", + DNSServers: []string{"169.254.1.1"}, + Logging: docker.DockerLogging{ + Type: "syslog", + Config: map[string]string{ + "tag": "driver-test", + }, + }, + }, + expectedType: &docker.TaskConfig{}, + }, + { + name: "docker_json", config: jsonConfigToInterface(t, ` - { - "Config": { - "image": "redis:3.2", - "devices": [ - { - "host_path": "/dev/sda1", - "container_path": "/dev/xvdc", - "cgroup_permissions": "r" - }, - { - "host_path": "/dev/sda2", - "container_path": "/dev/xvdd" - } - ] - } - }`), - spec: dockerSpec, - ctx: defaultCtx, - expected: &dockerConfig{ + { + "Config": { + "image": "redis:3.2", + "devices": [ + { + "host_path": "/dev/sda1", + "container_path": "/dev/xvdc", + "cgroup_permissions": "r" + }, + { + "host_path": "/dev/sda2", + "container_path": "/dev/xvdd" + } + ] + } + }`), + spec: dockerDecSpec, + expected: &docker.TaskConfig{ Image: "redis:3.2", - Devices: []DockerDevice{ + Devices: []docker.DockerDevice{ { HostPath: "/dev/sda1", ContainerPath: "/dev/xvdc", @@ -431,16 +392,18 @@ func TestParseHclInterface_Hcl(t *testing.T) { ContainerPath: "/dev/xvdd", }, }, + Mounts: []docker.DockerMount{}, }, - expectedType: &dockerConfig{}, + expectedType: &docker.TaskConfig{}, }, } for _, c := range cases { + c := c t.Run(c.name, func(t *testing.T) { t.Logf("Val: % #v", pretty.Formatter(c.config)) // Parse the interface - ctyValue, diag := ParseHclInterface(c.config, c.spec, c.ctx) + ctyValue, diag := hclutils.ParseHclInterface(c.config, c.spec, c.vars) if diag.HasErrors() { for _, err := range diag.Errs() { t.Error(err) @@ -448,8 +411,12 @@ func TestParseHclInterface_Hcl(t *testing.T) { t.FailNow() } - // Convert cty-value to go structs - require.NoError(t, gocty.FromCtyValue(ctyValue, c.expectedType)) + // Test encoding + taskConfig := &drivers.TaskConfig{} + require.NoError(t, taskConfig.EncodeDriverConfig(ctyValue)) + + // Test decoding + require.NoError(t, taskConfig.DecodeDriverConfig(c.expectedType)) require.EqualValues(t, c.expected, c.expectedType)