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.
This commit is contained in:
parent
6c0cc65b2e
commit
9bf4b38ab3
|
@ -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{
|
||||
dockerDriver := new(docker.Driver)
|
||||
dockerSpec, err := dockerDriver.TaskConfigSchema()
|
||||
require.NoError(t, err)
|
||||
dockerDecSpec, diags := hclspecutils.Convert(dockerSpec)
|
||||
require.False(t, diags.HasErrors())
|
||||
|
||||
vars := map[string]cty.Value{
|
||||
"NOMAD_ALLOC_INDEX": cty.NumberIntVal(2),
|
||||
"NOMAD_META_hello": cty.StringVal("world"),
|
||||
},
|
||||
}
|
||||
|
||||
// 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())
|
||||
|
||||
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,11 +327,40 @@ 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": {
|
||||
|
@ -416,11 +378,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",
|
||||
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue