Commit 08864d5e authored by Michael Schurter's avatar Michael Schurter
Browse files

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.
parent 1d17fbc6
Showing with 154 additions and 187 deletions
+154 -187
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"),
},
}
// 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)
//}
dockerDriver := new(docker.Driver)
dockerSpec, err := dockerDriver.TaskConfigSchema()
require.NoError(t, err)
dockerDecSpec, diags := hclspecutils.Convert(dockerSpec)
require.False(t, diags.HasErrors())
//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: &docker.TaskConfig{},
},
{
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: &dockerConfig{},
expectedType: &docker.TaskConfig{},
},
{
name: "devices json",
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)
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment