mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 02:15:43 +03:00
tests: add hcl task driver config parsing tests (#5314)
* drivers: add config parsing tests Add basic tests for parsing and encoding task config. * drivers/docker: fix some config declarations * refactor and document config parse helpers
This commit is contained in:
@@ -235,7 +235,7 @@ var (
|
||||
"command": hclspec.NewAttr("command", "string", false),
|
||||
"cpu_hard_limit": hclspec.NewAttr("cpu_hard_limit", "bool", false),
|
||||
"cpu_cfs_period": hclspec.NewAttr("cpu_cfs_period", "number", false),
|
||||
"devices": hclspec.NewBlockSet("devices", hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"devices": hclspec.NewBlockList("devices", hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"host_path": hclspec.NewAttr("host_path", "string", false),
|
||||
"container_path": hclspec.NewAttr("container_path", "string", false),
|
||||
"cgroup_permissions": hclspec.NewAttr("cgroup_permissions", "string", false),
|
||||
@@ -258,7 +258,7 @@ var (
|
||||
"config": hclspec.NewBlockAttrs("config", "string", false),
|
||||
})),
|
||||
"mac_address": hclspec.NewAttr("mac_address", "string", false),
|
||||
"mounts": hclspec.NewBlockSet("mounts", hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"mounts": hclspec.NewBlockList("mounts", hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"type": hclspec.NewDefault(
|
||||
hclspec.NewAttr("type", "string", false),
|
||||
hclspec.NewLiteral("\"volume\""),
|
||||
@@ -276,7 +276,7 @@ var (
|
||||
"volume_options": hclspec.NewBlock("volume_options", false, hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"no_copy": hclspec.NewAttr("no_copy", "bool", false),
|
||||
"labels": hclspec.NewBlockAttrs("labels", "string", false),
|
||||
"driver_config": hclspec.NewBlockSet("driver_config", hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"driver_config": hclspec.NewBlock("driver_config", false, hclspec.NewObject(map[string]*hclspec.Spec{
|
||||
"name": hclspec.NewAttr("name", "string", false),
|
||||
"options": hclspec.NewBlockAttrs("options", "string", false),
|
||||
})),
|
||||
|
||||
296
drivers/docker/config_test.go
Normal file
296
drivers/docker/config_test.go
Normal file
@@ -0,0 +1,296 @@
|
||||
package docker
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfig_ParseHCL(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
|
||||
input string
|
||||
expected *TaskConfig
|
||||
}{
|
||||
{
|
||||
"basic image",
|
||||
`config {
|
||||
image = "redis:3.2"
|
||||
}`,
|
||||
&TaskConfig{
|
||||
Image: "redis:3.2",
|
||||
Devices: []DockerDevice{},
|
||||
Mounts: []DockerMount{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
parser := hclutils.NewConfigParser(taskConfigSpec)
|
||||
for _, c := range cases {
|
||||
c := c
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
var tc *TaskConfig
|
||||
|
||||
parser.ParseHCL(t, c.input, &tc)
|
||||
|
||||
require.EqualValues(t, c.expected, tc)
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
advertise_ipv6_address = true
|
||||
args = ["command_arg1", "command_arg2"]
|
||||
auth {
|
||||
username = "myusername"
|
||||
password = "mypassword"
|
||||
email = "myemail@example.com"
|
||||
server_address = "https://example.com"
|
||||
}
|
||||
|
||||
auth_soft_fail = true
|
||||
cap_add = ["CAP_SYS_NICE"]
|
||||
cap_drop = ["CAP_SYS_ADMIN", "CAP_SYS_TIME"]
|
||||
command = "/bin/bash"
|
||||
cpu_hard_limit = true
|
||||
cpu_cfs_period = 20
|
||||
devices = [
|
||||
{"host_path"="/dev/null", "container_path"="/tmp/container-null", cgroup_permissions="rwm"},
|
||||
{"host_path"="/dev/random", "container_path"="/tmp/container-random"},
|
||||
]
|
||||
dns_search_domains = ["sub.example.com", "sub2.example.com"]
|
||||
dns_options = ["debug", "attempts:10"]
|
||||
dns_servers = ["8.8.8.8", "1.1.1.1"]
|
||||
entrypoint = ["/bin/bash", "-c"]
|
||||
extra_hosts = ["127.0.0.1 localhost.example.com"]
|
||||
force_pull = true
|
||||
hostname = "self.example.com"
|
||||
interactive = true
|
||||
ipc_mode = "host"
|
||||
ipv4_address = "10.0.2.1"
|
||||
ipv6_address = "2601:184:407f:b37c:d834:412e:1f86:7699"
|
||||
labels {
|
||||
owner = "hashicorp-nomad"
|
||||
key = "val"
|
||||
}
|
||||
load = "/tmp/image.tar.gz"
|
||||
logging {
|
||||
type = "json-file"
|
||||
config {
|
||||
"max-file" = "3"
|
||||
"max-size" = "10m"
|
||||
}
|
||||
}
|
||||
mac_address = "02:42:ac:11:00:02"
|
||||
mounts = [
|
||||
{
|
||||
type = "bind"
|
||||
target = "/bind-target",
|
||||
source = "/bind-source"
|
||||
readonly = true
|
||||
bind_options {
|
||||
propagation = "rshared"
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "tmpfs"
|
||||
target = "/tmpfs-target",
|
||||
readonly = true
|
||||
tmpfs_options {
|
||||
size = 30000
|
||||
mode = 0777
|
||||
}
|
||||
},
|
||||
{
|
||||
type = "volume"
|
||||
target = "/volume-target"
|
||||
source = "/volume-source"
|
||||
readonly = true
|
||||
volume_options {
|
||||
no_copy = true
|
||||
labels {
|
||||
label_key = "label_value"
|
||||
}
|
||||
driver_config {
|
||||
name = "nfs"
|
||||
options {
|
||||
option_key = "option_value"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
network_aliases = ["redis"]
|
||||
network_mode = "host"
|
||||
pids_limit = 2000
|
||||
pid_mode = "host"
|
||||
port_map {
|
||||
http = 80
|
||||
redis = 6379
|
||||
}
|
||||
privileged = true
|
||||
readonly_rootfs = true
|
||||
security_opt = [
|
||||
"credentialspec=file://gmsaUser.json"
|
||||
],
|
||||
shm_size = 30000
|
||||
storage_opt {
|
||||
dm.thinpooldev = "dev/mapper/thin-pool"
|
||||
dm.use_deferred_deletion = "true"
|
||||
dm.use_deferred_removal = "true"
|
||||
|
||||
}
|
||||
sysctl {
|
||||
net.core.somaxconn = "16384"
|
||||
}
|
||||
tty = true
|
||||
ulimit {
|
||||
nproc = "4242"
|
||||
nofile = "2048:4096"
|
||||
}
|
||||
uts_mode = "host"
|
||||
userns_mode = "host"
|
||||
volumes = [
|
||||
"/host-path:/container-path:rw",
|
||||
]
|
||||
volume_driver = "host"
|
||||
work_dir = "/tmp/workdir"
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
Image: "redis:3.2",
|
||||
AdvertiseIPv6Addr: true,
|
||||
Args: []string{"command_arg1", "command_arg2"},
|
||||
Auth: DockerAuth{
|
||||
Username: "myusername",
|
||||
Password: "mypassword",
|
||||
Email: "myemail@example.com",
|
||||
ServerAddr: "https://example.com",
|
||||
},
|
||||
AuthSoftFail: true,
|
||||
CapAdd: []string{"CAP_SYS_NICE"},
|
||||
CapDrop: []string{"CAP_SYS_ADMIN", "CAP_SYS_TIME"},
|
||||
Command: "/bin/bash",
|
||||
CPUHardLimit: true,
|
||||
CPUCFSPeriod: 20,
|
||||
Devices: []DockerDevice{
|
||||
{
|
||||
HostPath: "/dev/null",
|
||||
ContainerPath: "/tmp/container-null",
|
||||
CgroupPermissions: "rwm",
|
||||
},
|
||||
{
|
||||
HostPath: "/dev/random",
|
||||
ContainerPath: "/tmp/container-random",
|
||||
CgroupPermissions: "",
|
||||
},
|
||||
},
|
||||
DNSSearchDomains: []string{"sub.example.com", "sub2.example.com"},
|
||||
DNSOptions: []string{"debug", "attempts:10"},
|
||||
DNSServers: []string{"8.8.8.8", "1.1.1.1"},
|
||||
Entrypoint: []string{"/bin/bash", "-c"},
|
||||
ExtraHosts: []string{"127.0.0.1 localhost.example.com"},
|
||||
ForcePull: true,
|
||||
Hostname: "self.example.com",
|
||||
Interactive: true,
|
||||
IPCMode: "host",
|
||||
IPv4Address: "10.0.2.1",
|
||||
IPv6Address: "2601:184:407f:b37c:d834:412e:1f86:7699",
|
||||
Labels: map[string]string{
|
||||
"owner": "hashicorp-nomad",
|
||||
"key": "val",
|
||||
},
|
||||
LoadImage: "/tmp/image.tar.gz",
|
||||
Logging: DockerLogging{
|
||||
Type: "json-file",
|
||||
Config: map[string]string{
|
||||
"max-file": "3",
|
||||
"max-size": "10m",
|
||||
}},
|
||||
MacAddress: "02:42:ac:11:00:02",
|
||||
Mounts: []DockerMount{
|
||||
{
|
||||
Type: "bind",
|
||||
Target: "/bind-target",
|
||||
Source: "/bind-source",
|
||||
ReadOnly: true,
|
||||
BindOptions: DockerBindOptions{
|
||||
Propagation: "rshared",
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "tmpfs",
|
||||
Target: "/tmpfs-target",
|
||||
Source: "",
|
||||
ReadOnly: true,
|
||||
TmpfsOptions: DockerTmpfsOptions{
|
||||
SizeBytes: 30000,
|
||||
Mode: 511,
|
||||
},
|
||||
},
|
||||
{
|
||||
Type: "volume",
|
||||
Target: "/volume-target",
|
||||
Source: "/volume-source",
|
||||
ReadOnly: true,
|
||||
VolumeOptions: DockerVolumeOptions{
|
||||
NoCopy: true,
|
||||
Labels: map[string]string{
|
||||
"label_key": "label_value",
|
||||
},
|
||||
DriverConfig: DockerVolumeDriverConfig{
|
||||
Name: "nfs",
|
||||
Options: map[string]string{
|
||||
"option_key": "option_value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
NetworkAliases: []string{"redis"},
|
||||
NetworkMode: "host",
|
||||
PidsLimit: 2000,
|
||||
PidMode: "host",
|
||||
PortMap: map[string]int{
|
||||
"http": 80,
|
||||
"redis": 6379,
|
||||
},
|
||||
Privileged: true,
|
||||
ReadonlyRootfs: true,
|
||||
SecurityOpt: []string{
|
||||
"credentialspec=file://gmsaUser.json",
|
||||
},
|
||||
ShmSize: 30000,
|
||||
StorageOpt: map[string]string{
|
||||
"dm.thinpooldev": "dev/mapper/thin-pool",
|
||||
"dm.use_deferred_deletion": "true",
|
||||
"dm.use_deferred_removal": "true",
|
||||
},
|
||||
Sysctl: map[string]string{
|
||||
"net.core.somaxconn": "16384",
|
||||
},
|
||||
TTY: true,
|
||||
Ulimit: map[string]string{
|
||||
"nofile": "2048:4096",
|
||||
"nproc": "4242",
|
||||
},
|
||||
UTSMode: "host",
|
||||
UsernsMode: "host",
|
||||
Volumes: []string{
|
||||
"/host-path:/container-path:rw",
|
||||
},
|
||||
VolumeDriver: "host",
|
||||
WorkDir: "/tmp/workdir",
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctestutils "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/testtask"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
@@ -548,3 +549,21 @@ touch: cannot touch '/tmp/task-path-ro/testfile-from-ro': Read-only file system`
|
||||
require.NoError(err)
|
||||
require.Equal("from-exec", strings.TrimSpace(string(fromRWContent)))
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
command = "/bin/bash"
|
||||
args = ["-c", "echo hello"]
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
Command: "/bin/bash",
|
||||
Args: []string{"-c", "echo hello"},
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctestutil "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
@@ -291,3 +292,27 @@ func copyFile(src, dst string, t *testing.T) {
|
||||
t.Fatalf("copying %v -> %v failed: %v", src, dst, err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
class = "java.main"
|
||||
class_path = "/tmp/cp"
|
||||
jar_path = "/tmp/jar.jar"
|
||||
jvm_options = ["-Xmx600"]
|
||||
args = ["arg1", "arg2"]
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
Class: "java.main",
|
||||
ClassPath: "/tmp/cp",
|
||||
JarPath: "/tmp/jar.jar",
|
||||
JvmOpts: []string{"-Xmx600"},
|
||||
Args: []string{"arg1", "arg2"},
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctestutil "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
@@ -375,3 +376,33 @@ func TestQemuDriver_Fingerprint(t *testing.T) {
|
||||
require.Fail("timeout receiving fingerprint")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
image_path = "/tmp/image_path"
|
||||
accelerator = "kvm"
|
||||
args = ["arg1", "arg2"]
|
||||
port_map {
|
||||
http = 80
|
||||
https = 443
|
||||
}
|
||||
graceful_shutdown = true
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
ImagePath: "/tmp/image_path",
|
||||
Accelerator: "kvm",
|
||||
Args: []string{"arg1", "arg2"},
|
||||
PortMap: map[string]int{
|
||||
"http": 80,
|
||||
"https": 443,
|
||||
},
|
||||
GracefulShutdown: true,
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctestutil "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/testtask"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
@@ -452,3 +453,21 @@ func TestRawExecDriver_Exec(t *testing.T) {
|
||||
|
||||
require.NoError(harness.DestroyTask(task.ID, true))
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
command = "/bin/bash"
|
||||
args = ["-c", "echo hello"]
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
Command: "/bin/bash",
|
||||
Args: []string{"-c", "echo hello"},
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"time"
|
||||
|
||||
ctestutil "github.com/hashicorp/nomad/client/testutil"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclutils"
|
||||
"github.com/hashicorp/nomad/helper/testlog"
|
||||
"github.com/hashicorp/nomad/helper/testtask"
|
||||
"github.com/hashicorp/nomad/helper/uuid"
|
||||
@@ -862,3 +863,53 @@ func TestRktDriver_Stats(t *testing.T) {
|
||||
require.NoError(harness.DestroyTask(task.ID, true))
|
||||
|
||||
}
|
||||
|
||||
func TestConfig_ParseAllHCL(t *testing.T) {
|
||||
cfgStr := `
|
||||
config {
|
||||
image = "docker://redis:3.2"
|
||||
command = "/bin/bash"
|
||||
args = ["-c", "echo hi"]
|
||||
trust_prefix = "coreos.com/etcd"
|
||||
dns_servers = ["8.8.8.8"]
|
||||
dns_search_domains = ["example.com"]
|
||||
net = ["network1"]
|
||||
port_map {
|
||||
http = "80-tcp"
|
||||
https = "443-tcp"
|
||||
}
|
||||
volumes = [
|
||||
"/host-path:/container-path",
|
||||
]
|
||||
insecure_options = ["image", "tls", "ondisk"]
|
||||
no_overlay = true
|
||||
debug = true
|
||||
group = "mygroup"
|
||||
}`
|
||||
|
||||
expected := &TaskConfig{
|
||||
ImageName: "docker://redis:3.2",
|
||||
Command: "/bin/bash",
|
||||
Args: []string{"-c", "echo hi"},
|
||||
TrustPrefix: "coreos.com/etcd",
|
||||
DNSServers: []string{"8.8.8.8"},
|
||||
DNSSearchDomains: []string{"example.com"},
|
||||
Net: []string{"network1"},
|
||||
PortMap: map[string]string{
|
||||
"http": "80-tcp",
|
||||
"https": "443-tcp",
|
||||
},
|
||||
Volumes: []string{
|
||||
"/host-path:/container-path",
|
||||
},
|
||||
InsecureOptions: []string{"image", "tls", "ondisk"},
|
||||
NoOverlay: true,
|
||||
Debug: true,
|
||||
Group: "mygroup",
|
||||
}
|
||||
|
||||
var tc *TaskConfig
|
||||
hclutils.NewConfigParser(taskConfigSpec).ParseHCL(t, cfgStr, &tc)
|
||||
|
||||
require.EqualValues(t, expected, tc)
|
||||
}
|
||||
|
||||
125
helper/pluginutils/hclutils/testing.go
Normal file
125
helper/pluginutils/hclutils/testing.go
Normal file
@@ -0,0 +1,125 @@
|
||||
package hclutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
"github.com/hashicorp/nomad/helper/pluginutils/hclspecutils"
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
"github.com/hashicorp/nomad/plugins/drivers"
|
||||
"github.com/hashicorp/nomad/plugins/shared/hclspec"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/ugorji/go/codec"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
type HCLParser struct {
|
||||
spec *hclspec.Spec
|
||||
vars map[string]cty.Value
|
||||
}
|
||||
|
||||
// NewConfigParser return a helper for parsing drivers TaskConfig
|
||||
// Parser is an immutable object can be used in multiple tests
|
||||
func NewConfigParser(spec *hclspec.Spec) *HCLParser {
|
||||
return &HCLParser{
|
||||
spec: spec,
|
||||
}
|
||||
}
|
||||
|
||||
// WithVars returns a new parser that uses passed vars when interpolated strings in config
|
||||
func (b *HCLParser) WithVars(vars map[string]cty.Value) *HCLParser {
|
||||
return &HCLParser{
|
||||
spec: b.spec,
|
||||
vars: vars,
|
||||
}
|
||||
}
|
||||
|
||||
// ParseJson parses the json config string and decode it into the `out` parameter.
|
||||
// out parameter should be a golang reference to a driver specific TaskConfig reference.
|
||||
// The function terminates and reports errors if any is found during conversion.
|
||||
//
|
||||
// Sample invocation would be
|
||||
//
|
||||
// ```
|
||||
// var tc *TaskConfig
|
||||
// hclutils.NewConfigParser(spec).ParseJson(t, configString, &tc)
|
||||
// ```
|
||||
func (b *HCLParser) ParseJson(t *testing.T, configStr string, out interface{}) {
|
||||
config := JsonConfigToInterface(t, configStr)
|
||||
b.parse(t, config, out)
|
||||
}
|
||||
|
||||
// ParseHCL parses the hcl config string and decode it into the `out` parameter.
|
||||
// out parameter should be a golang reference to a driver specific TaskConfig reference.
|
||||
// The function terminates and reports errors if any is found during conversion.
|
||||
//
|
||||
// Sample invocation would be
|
||||
//
|
||||
// ```
|
||||
// var tc *TaskConfig
|
||||
// hclutils.NewConfigParser(spec).ParseHCL(t, configString, &tc)
|
||||
// ```
|
||||
func (b *HCLParser) ParseHCL(t *testing.T, configStr string, out interface{}) {
|
||||
config := HclConfigToInterface(t, configStr)
|
||||
b.parse(t, config, out)
|
||||
}
|
||||
|
||||
func (b *HCLParser) parse(t *testing.T, config, out interface{}) {
|
||||
decSpec, diags := hclspecutils.Convert(b.spec)
|
||||
require.Empty(t, diags)
|
||||
|
||||
ctyValue, diag := ParseHclInterface(config, decSpec, b.vars)
|
||||
require.Empty(t, diag)
|
||||
|
||||
// encode
|
||||
dtc := &drivers.TaskConfig{}
|
||||
require.NoError(t, dtc.EncodeDriverConfig(ctyValue))
|
||||
|
||||
// decode
|
||||
require.NoError(t, dtc.DecodeDriverConfig(out))
|
||||
}
|
||||
|
||||
func HclConfigToInterface(t *testing.T, config string) interface{} {
|
||||
t.Helper()
|
||||
|
||||
// Parse as we do in the jobspec parser
|
||||
root, err := hcl.Parse(config)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hcl parse the config: %v", err)
|
||||
}
|
||||
|
||||
// Top-level item should be a list
|
||||
list, ok := root.Node.(*ast.ObjectList)
|
||||
if !ok {
|
||||
t.Fatalf("root should be an object")
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := hcl.DecodeObject(&m, list.Items[0]); err != nil {
|
||||
t.Fatalf("failed to decode object: %v", err)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
if err := mapstructure.WeakDecode(m, &m2); err != nil {
|
||||
t.Fatalf("failed to weak decode object: %v", err)
|
||||
}
|
||||
|
||||
return m2["config"]
|
||||
}
|
||||
|
||||
func JsonConfigToInterface(t *testing.T, config string) interface{} {
|
||||
t.Helper()
|
||||
|
||||
// Decode from json
|
||||
dec := codec.NewDecoderBytes([]byte(config), structs.JsonHandle)
|
||||
|
||||
var m map[string]interface{}
|
||||
err := dec.Decode(&m)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode: %v", err)
|
||||
}
|
||||
|
||||
return m["Config"]
|
||||
}
|
||||
@@ -3,64 +3,16 @@ package hclutils_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/hcl"
|
||||
"github.com/hashicorp/hcl/hcl/ast"
|
||||
"github.com/hashicorp/hcl2/hcldec"
|
||||
"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"
|
||||
)
|
||||
|
||||
func hclConfigToInterface(t *testing.T, config string) interface{} {
|
||||
t.Helper()
|
||||
|
||||
// Parse as we do in the jobspec parser
|
||||
root, err := hcl.Parse(config)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to hcl parse the config: %v", err)
|
||||
}
|
||||
|
||||
// Top-level item should be a list
|
||||
list, ok := root.Node.(*ast.ObjectList)
|
||||
if !ok {
|
||||
t.Fatalf("root should be an object")
|
||||
}
|
||||
|
||||
var m map[string]interface{}
|
||||
if err := hcl.DecodeObject(&m, list.Items[0]); err != nil {
|
||||
t.Fatalf("failed to decode object: %v", err)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
if err := mapstructure.WeakDecode(m, &m2); err != nil {
|
||||
t.Fatalf("failed to weak decode object: %v", err)
|
||||
}
|
||||
|
||||
return m2["config"]
|
||||
}
|
||||
|
||||
func jsonConfigToInterface(t *testing.T, config string) interface{} {
|
||||
t.Helper()
|
||||
|
||||
// Decode from json
|
||||
dec := codec.NewDecoderBytes([]byte(config), structs.JsonHandle)
|
||||
|
||||
var m map[string]interface{}
|
||||
err := dec.Decode(&m)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to decode: %v", err)
|
||||
}
|
||||
|
||||
return m["Config"]
|
||||
}
|
||||
|
||||
func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
dockerDriver := new(docker.Driver)
|
||||
dockerSpec, err := dockerDriver.TaskConfigSchema()
|
||||
@@ -83,7 +35,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "single string attr",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
}`),
|
||||
@@ -97,7 +49,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "single string attr json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2"
|
||||
@@ -113,7 +65,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "number attr",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
pids_limit = 2
|
||||
@@ -129,7 +81,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "number attr json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
@@ -147,7 +99,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "number attr interpolated",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
pids_limit = "${2 + 2}"
|
||||
@@ -163,7 +115,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "number attr interploated json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
@@ -181,7 +133,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multi attr",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
args = ["foo", "bar"]
|
||||
@@ -197,7 +149,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multi attr json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
@@ -215,7 +167,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multi attr variables",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
args = ["${NOMAD_META_hello}", "${NOMAD_ALLOC_INDEX}"]
|
||||
@@ -234,7 +186,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "multi attr variables json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
@@ -252,7 +204,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "port_map",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
port_map {
|
||||
@@ -274,7 +226,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "port_map json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
@@ -298,7 +250,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "devices",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
devices = [
|
||||
@@ -333,7 +285,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "docker_logging",
|
||||
config: hclConfigToInterface(t, `
|
||||
config: hclutils.HclConfigToInterface(t, `
|
||||
config {
|
||||
image = "redis:3.2"
|
||||
network_mode = "host"
|
||||
@@ -363,7 +315,7 @@ func TestParseHclInterface_Hcl(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "docker_json",
|
||||
config: jsonConfigToInterface(t, `
|
||||
config: hclutils.JsonConfigToInterface(t, `
|
||||
{
|
||||
"Config": {
|
||||
"image": "redis:3.2",
|
||||
|
||||
Reference in New Issue
Block a user