2016-03-12 02:24:58 +00:00
|
|
|
package agent
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2016-06-16 20:30:29 +00:00
|
|
|
"time"
|
2016-03-12 02:24:58 +00:00
|
|
|
|
2017-11-15 01:53:23 +00:00
|
|
|
multierror "github.com/hashicorp/go-multierror"
|
2019-01-11 19:36:26 +00:00
|
|
|
version "github.com/hashicorp/go-version"
|
2016-03-12 02:24:58 +00:00
|
|
|
"github.com/hashicorp/hcl"
|
|
|
|
"github.com/hashicorp/hcl/hcl/ast"
|
2017-10-13 21:36:02 +00:00
|
|
|
"github.com/hashicorp/nomad/helper"
|
2018-05-08 20:32:07 +00:00
|
|
|
"github.com/hashicorp/nomad/helper/tlsutil"
|
2016-05-22 03:15:58 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs/config"
|
2016-03-12 02:24:58 +00:00
|
|
|
"github.com/mitchellh/mapstructure"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ParseConfigFile parses the given path as a config file.
|
|
|
|
func ParseConfigFile(path string) (*Config, error) {
|
|
|
|
path, err := filepath.Abs(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
config, err := ParseConfig(f)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseConfig parses the config from the given io.Reader.
|
|
|
|
//
|
|
|
|
// Due to current internal limitations, the entire contents of the
|
|
|
|
// io.Reader will be copied into memory first before parsing.
|
|
|
|
func ParseConfig(r io.Reader) (*Config, error) {
|
|
|
|
// Copy the reader into an in-memory buffer first since HCL requires it.
|
|
|
|
var buf bytes.Buffer
|
|
|
|
if _, err := io.Copy(&buf, r); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the buffer
|
|
|
|
root, err := hcl.Parse(buf.String())
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("error parsing: %s", err)
|
|
|
|
}
|
|
|
|
buf.Reset()
|
|
|
|
|
|
|
|
// Top-level item should be a list
|
|
|
|
list, ok := root.Node.(*ast.ObjectList)
|
|
|
|
if !ok {
|
|
|
|
return nil, fmt.Errorf("error parsing: root should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
var config Config
|
|
|
|
if err := parseConfig(&config, list); err != nil {
|
|
|
|
return nil, fmt.Errorf("error parsing 'config': %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &config, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseConfig(result *Config, list *ast.ObjectList) error {
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"region",
|
|
|
|
"datacenter",
|
|
|
|
"name",
|
|
|
|
"data_dir",
|
2018-08-30 20:43:09 +00:00
|
|
|
"plugin_dir",
|
2016-03-12 02:24:58 +00:00
|
|
|
"log_level",
|
2019-01-11 19:36:26 +00:00
|
|
|
"log_json",
|
2016-03-12 02:24:58 +00:00
|
|
|
"bind_addr",
|
|
|
|
"enable_debug",
|
|
|
|
"ports",
|
|
|
|
"addresses",
|
2016-03-19 05:05:57 +00:00
|
|
|
"interfaces",
|
2016-03-12 02:24:58 +00:00
|
|
|
"advertise",
|
|
|
|
"client",
|
|
|
|
"server",
|
|
|
|
"telemetry",
|
|
|
|
"leave_on_interrupt",
|
|
|
|
"leave_on_terminate",
|
|
|
|
"enable_syslog",
|
|
|
|
"syslog_facility",
|
|
|
|
"disable_update_check",
|
|
|
|
"disable_anonymous_signature",
|
2016-03-31 06:20:24 +00:00
|
|
|
"consul",
|
2016-08-06 01:13:06 +00:00
|
|
|
"vault",
|
2016-10-24 20:46:22 +00:00
|
|
|
"tls",
|
2016-03-12 02:24:58 +00:00
|
|
|
"http_api_response_headers",
|
2017-08-13 20:46:47 +00:00
|
|
|
"acl",
|
2017-09-19 14:47:10 +00:00
|
|
|
"sentinel",
|
2017-12-18 21:16:23 +00:00
|
|
|
"autopilot",
|
2018-08-29 23:26:44 +00:00
|
|
|
"plugin",
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(list, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return multierror.Prefix(err, "config:")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Decode the full thing into a map[string]interface for ease
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, list); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
delete(m, "ports")
|
|
|
|
delete(m, "addresses")
|
2016-03-19 05:05:57 +00:00
|
|
|
delete(m, "interfaces")
|
2016-03-12 02:24:58 +00:00
|
|
|
delete(m, "advertise")
|
|
|
|
delete(m, "client")
|
|
|
|
delete(m, "server")
|
|
|
|
delete(m, "telemetry")
|
2016-03-31 06:20:24 +00:00
|
|
|
delete(m, "consul")
|
2016-08-06 01:13:06 +00:00
|
|
|
delete(m, "vault")
|
2016-10-24 20:46:22 +00:00
|
|
|
delete(m, "tls")
|
2016-03-12 02:24:58 +00:00
|
|
|
delete(m, "http_api_response_headers")
|
2017-08-13 20:46:47 +00:00
|
|
|
delete(m, "acl")
|
2017-09-19 14:47:10 +00:00
|
|
|
delete(m, "sentinel")
|
2017-12-18 21:16:23 +00:00
|
|
|
delete(m, "autopilot")
|
2018-08-29 23:26:44 +00:00
|
|
|
delete(m, "plugin")
|
2016-03-12 02:24:58 +00:00
|
|
|
|
|
|
|
// Decode the rest
|
|
|
|
if err := mapstructure.WeakDecode(m, result); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse ports
|
|
|
|
if o := list.Filter("ports"); len(o.Items) > 0 {
|
|
|
|
if err := parsePorts(&result.Ports, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "ports ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse addresses
|
|
|
|
if o := list.Filter("addresses"); len(o.Items) > 0 {
|
|
|
|
if err := parseAddresses(&result.Addresses, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "addresses ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse advertise
|
|
|
|
if o := list.Filter("advertise"); len(o.Items) > 0 {
|
|
|
|
if err := parseAdvertise(&result.AdvertiseAddrs, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "advertise ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse client config
|
|
|
|
if o := list.Filter("client"); len(o.Items) > 0 {
|
|
|
|
if err := parseClient(&result.Client, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "client ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse server config
|
|
|
|
if o := list.Filter("server"); len(o.Items) > 0 {
|
|
|
|
if err := parseServer(&result.Server, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "server ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-13 20:46:47 +00:00
|
|
|
// Parse ACL config
|
|
|
|
if o := list.Filter("acl"); len(o.Items) > 0 {
|
|
|
|
if err := parseACL(&result.ACL, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "acl ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
// Parse telemetry config
|
|
|
|
if o := list.Filter("telemetry"); len(o.Items) > 0 {
|
|
|
|
if err := parseTelemetry(&result.Telemetry, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "telemetry ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-31 06:20:24 +00:00
|
|
|
// Parse the consul config
|
|
|
|
if o := list.Filter("consul"); len(o.Items) > 0 {
|
2016-05-22 00:39:45 +00:00
|
|
|
if err := parseConsulConfig(&result.Consul, o); err != nil {
|
2016-03-31 06:20:24 +00:00
|
|
|
return multierror.Prefix(err, "consul ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-06 01:13:06 +00:00
|
|
|
// Parse the vault config
|
|
|
|
if o := list.Filter("vault"); len(o.Items) > 0 {
|
|
|
|
if err := parseVaultConfig(&result.Vault, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "vault ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-24 20:46:22 +00:00
|
|
|
// Parse the TLS config
|
|
|
|
if o := list.Filter("tls"); len(o.Items) > 0 {
|
|
|
|
if err := parseTLSConfig(&result.TLSConfig, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "tls ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-19 14:47:10 +00:00
|
|
|
// Parse Sentinel config
|
|
|
|
if o := list.Filter("sentinel"); len(o.Items) > 0 {
|
|
|
|
if err := parseSentinel(&result.Sentinel, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "sentinel->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-18 21:16:23 +00:00
|
|
|
// Parse Autopilot config
|
|
|
|
if o := list.Filter("autopilot"); len(o.Items) > 0 {
|
|
|
|
if err := parseAutopilot(&result.Autopilot, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "autopilot->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-29 23:26:44 +00:00
|
|
|
// Parse Plugin configs
|
|
|
|
if o := list.Filter("plugin"); len(o.Items) > 0 {
|
|
|
|
if err := parsePlugins(&result.Plugins, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "plugin->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
// Parse out http_api_response_headers fields. These are in HCL as a list so
|
|
|
|
// we need to iterate over them and merge them.
|
|
|
|
if headersO := list.Filter("http_api_response_headers"); len(headersO.Items) > 0 {
|
|
|
|
for _, o := range headersO.Elem().Items {
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, o.Val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(m, &result.HTTPAPIResponseHeaders); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parsePorts(result **Ports, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'ports' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our ports object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"http",
|
|
|
|
"rpc",
|
|
|
|
"serf",
|
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var ports Ports
|
|
|
|
if err := mapstructure.WeakDecode(m, &ports); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*result = &ports
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseAddresses(result **Addresses, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'addresses' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our addresses object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"http",
|
|
|
|
"rpc",
|
|
|
|
"serf",
|
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var addresses Addresses
|
|
|
|
if err := mapstructure.WeakDecode(m, &addresses); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*result = &addresses
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseAdvertise(result **AdvertiseAddrs, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'advertise' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our advertise object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"http",
|
|
|
|
"rpc",
|
|
|
|
"serf",
|
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var advertise AdvertiseAddrs
|
|
|
|
if err := mapstructure.WeakDecode(m, &advertise); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*result = &advertise
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseClient(result **ClientConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'client' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our client object
|
|
|
|
obj := list.Items[0]
|
|
|
|
|
|
|
|
// Value should be an object
|
|
|
|
var listVal *ast.ObjectList
|
|
|
|
if ot, ok := obj.Val.(*ast.ObjectType); ok {
|
|
|
|
listVal = ot.List
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("client value: should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"enabled",
|
|
|
|
"state_dir",
|
|
|
|
"alloc_dir",
|
|
|
|
"servers",
|
|
|
|
"node_class",
|
|
|
|
"options",
|
|
|
|
"meta",
|
2016-08-02 02:58:32 +00:00
|
|
|
"chroot_env",
|
2016-03-12 02:24:58 +00:00
|
|
|
"network_interface",
|
|
|
|
"network_speed",
|
2018-01-22 20:28:29 +00:00
|
|
|
"memory_total_mb",
|
2017-03-14 21:15:49 +00:00
|
|
|
"cpu_total_compute",
|
2016-03-12 02:24:58 +00:00
|
|
|
"max_kill_timeout",
|
|
|
|
"client_max_port",
|
|
|
|
"client_min_port",
|
|
|
|
"reserved",
|
2016-05-25 05:30:10 +00:00
|
|
|
"stats",
|
2017-01-31 23:32:20 +00:00
|
|
|
"gc_interval",
|
|
|
|
"gc_disk_usage_threshold",
|
|
|
|
"gc_inode_usage_threshold",
|
2017-03-11 00:27:00 +00:00
|
|
|
"gc_parallel_destroys",
|
2017-05-11 00:39:45 +00:00
|
|
|
"gc_max_allocs",
|
2017-02-27 21:42:37 +00:00
|
|
|
"no_host_uuid",
|
2018-05-21 22:41:28 +00:00
|
|
|
"server_join",
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(m, "options")
|
|
|
|
delete(m, "meta")
|
2016-08-02 02:58:32 +00:00
|
|
|
delete(m, "chroot_env")
|
2016-03-12 02:24:58 +00:00
|
|
|
delete(m, "reserved")
|
2016-05-25 05:30:10 +00:00
|
|
|
delete(m, "stats")
|
2018-05-21 22:41:28 +00:00
|
|
|
delete(m, "server_join")
|
2016-03-12 02:24:58 +00:00
|
|
|
|
|
|
|
var config ClientConfig
|
2017-01-31 23:32:20 +00:00
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &config,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse out options fields. These are in HCL as a list so we need to
|
|
|
|
// iterate over them and merge them.
|
|
|
|
if optionsO := listVal.Filter("options"); len(optionsO.Items) > 0 {
|
|
|
|
for _, o := range optionsO.Elem().Items {
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, o.Val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(m, &config.Options); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse out options meta. These are in HCL as a list so we need to
|
|
|
|
// iterate over them and merge them.
|
|
|
|
if metaO := listVal.Filter("meta"); len(metaO.Items) > 0 {
|
|
|
|
for _, o := range metaO.Elem().Items {
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, o.Val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(m, &config.Meta); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-02 02:58:32 +00:00
|
|
|
// Parse out chroot_env fields. These are in HCL as a list so we need to
|
|
|
|
// iterate over them and merge them.
|
|
|
|
if chrootEnvO := listVal.Filter("chroot_env"); len(chrootEnvO.Items) > 0 {
|
|
|
|
for _, o := range chrootEnvO.Elem().Items {
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, o.Val); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := mapstructure.WeakDecode(m, &config.ChrootEnv); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
// Parse reserved config
|
|
|
|
if o := listVal.Filter("reserved"); len(o.Items) > 0 {
|
|
|
|
if err := parseReserved(&config.Reserved, o); err != nil {
|
|
|
|
return multierror.Prefix(err, "reserved ->")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 19:52:05 +00:00
|
|
|
// Parse ServerJoin config
|
2018-05-21 22:41:28 +00:00
|
|
|
if o := listVal.Filter("server_join"); len(o.Items) > 0 {
|
2018-05-11 19:52:05 +00:00
|
|
|
if err := parseServerJoin(&config.ServerJoin, o); err != nil {
|
2018-05-21 22:41:28 +00:00
|
|
|
return multierror.Prefix(err, "server_join->")
|
2018-05-11 19:52:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
*result = &config
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseReserved(result **Resources, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'reserved' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our reserved object
|
|
|
|
obj := list.Items[0]
|
|
|
|
|
|
|
|
// Value should be an object
|
|
|
|
var listVal *ast.ObjectList
|
|
|
|
if ot, ok := obj.Val.(*ast.ObjectType); ok {
|
|
|
|
listVal = ot.List
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("client value: should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"cpu",
|
|
|
|
"memory",
|
|
|
|
"disk",
|
2016-03-12 03:02:44 +00:00
|
|
|
"reserved_ports",
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var reserved Resources
|
|
|
|
if err := mapstructure.WeakDecode(m, &reserved); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-10-02 20:36:04 +00:00
|
|
|
if err := reserved.CanParseReserved(); err != nil {
|
2016-03-12 03:02:44 +00:00
|
|
|
return err
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*result = &reserved
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func parseServer(result **ServerConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'server' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our server object
|
|
|
|
obj := list.Items[0]
|
|
|
|
|
|
|
|
// Value should be an object
|
|
|
|
var listVal *ast.ObjectList
|
|
|
|
if ot, ok := obj.Val.(*ast.ObjectType); ok {
|
|
|
|
listVal = ot.List
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("client value: should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"enabled",
|
|
|
|
"bootstrap_expect",
|
|
|
|
"data_dir",
|
|
|
|
"protocol_version",
|
2017-12-18 21:16:23 +00:00
|
|
|
"raft_protocol",
|
2016-03-12 02:24:58 +00:00
|
|
|
"num_schedulers",
|
|
|
|
"enabled_schedulers",
|
|
|
|
"node_gc_threshold",
|
2017-02-27 19:58:10 +00:00
|
|
|
"eval_gc_threshold",
|
|
|
|
"job_gc_threshold",
|
2017-06-29 18:29:44 +00:00
|
|
|
"deployment_gc_threshold",
|
2016-03-12 02:24:58 +00:00
|
|
|
"heartbeat_grace",
|
2017-07-19 16:38:35 +00:00
|
|
|
"min_heartbeat_ttl",
|
|
|
|
"max_heartbeats_per_second",
|
2016-03-12 02:24:58 +00:00
|
|
|
"rejoin_after_leave",
|
2016-10-17 17:48:04 +00:00
|
|
|
"encrypt",
|
2017-08-13 20:46:47 +00:00
|
|
|
"authoritative_region",
|
2017-12-18 21:16:23 +00:00
|
|
|
"non_voting_server",
|
2018-01-30 03:53:34 +00:00
|
|
|
"redundancy_zone",
|
|
|
|
"upgrade_version",
|
2018-05-11 19:52:05 +00:00
|
|
|
|
2018-05-21 22:41:28 +00:00
|
|
|
"server_join",
|
2018-05-11 19:52:05 +00:00
|
|
|
|
|
|
|
// For backwards compatibility
|
|
|
|
"start_join",
|
|
|
|
"retry_join",
|
|
|
|
"retry_max",
|
|
|
|
"retry_interval",
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-21 22:41:28 +00:00
|
|
|
delete(m, "server_join")
|
2018-05-11 19:52:05 +00:00
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
var config ServerConfig
|
2017-07-19 16:38:35 +00:00
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &config,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-01-30 03:53:34 +00:00
|
|
|
if config.UpgradeVersion != "" {
|
|
|
|
if _, err := version.NewVersion(config.UpgradeVersion); err != nil {
|
|
|
|
return fmt.Errorf("error parsing upgrade_version: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-11 19:52:05 +00:00
|
|
|
// Parse ServerJoin config
|
2018-05-21 22:41:28 +00:00
|
|
|
if o := listVal.Filter("server_join"); len(o.Items) > 0 {
|
2018-05-11 19:52:05 +00:00
|
|
|
if err := parseServerJoin(&config.ServerJoin, o); err != nil {
|
2018-05-21 22:41:28 +00:00
|
|
|
return multierror.Prefix(err, "server_join->")
|
2018-05-11 19:52:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
*result = &config
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-05-11 19:52:05 +00:00
|
|
|
func parseServerJoin(result **ServerJoin, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
2018-05-21 22:41:28 +00:00
|
|
|
return fmt.Errorf("only one 'server_join' block allowed")
|
2018-05-11 19:52:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Get our object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"start_join",
|
|
|
|
"retry_join",
|
|
|
|
"retry_max",
|
|
|
|
"retry_interval",
|
|
|
|
}
|
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var serverJoinInfo ServerJoin
|
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &serverJoinInfo,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = &serverJoinInfo
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-08-13 20:46:47 +00:00
|
|
|
func parseACL(result **ACLConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'acl' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our server object
|
|
|
|
obj := list.Items[0]
|
|
|
|
|
|
|
|
// Value should be an object
|
|
|
|
var listVal *ast.ObjectList
|
|
|
|
if ot, ok := obj.Val.(*ast.ObjectType); ok {
|
|
|
|
listVal = ot.List
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("acl value: should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"enabled",
|
|
|
|
"token_ttl",
|
|
|
|
"policy_ttl",
|
2017-08-21 03:51:30 +00:00
|
|
|
"replication_token",
|
2017-08-13 20:46:47 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2017-08-13 20:46:47 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var config ACLConfig
|
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &config,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = &config
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-03-12 02:24:58 +00:00
|
|
|
func parseTelemetry(result **Telemetry, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'telemetry' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our telemetry object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"statsite_address",
|
|
|
|
"statsd_address",
|
|
|
|
"disable_hostname",
|
2016-12-13 21:10:39 +00:00
|
|
|
"use_node_name",
|
2016-06-16 20:30:29 +00:00
|
|
|
"collection_interval",
|
2016-08-02 02:49:01 +00:00
|
|
|
"publish_allocation_metrics",
|
|
|
|
"publish_node_metrics",
|
2016-10-26 20:28:28 +00:00
|
|
|
"datadog_address",
|
2018-02-06 16:21:15 +00:00
|
|
|
"datadog_tags",
|
2017-09-11 13:47:44 +00:00
|
|
|
"prometheus_metrics",
|
2016-07-22 16:33:10 +00:00
|
|
|
"circonus_api_token",
|
|
|
|
"circonus_api_app",
|
|
|
|
"circonus_api_url",
|
|
|
|
"circonus_submission_interval",
|
|
|
|
"circonus_submission_url",
|
|
|
|
"circonus_check_id",
|
|
|
|
"circonus_check_force_metric_activation",
|
|
|
|
"circonus_check_instance_id",
|
|
|
|
"circonus_check_search_tag",
|
2016-11-09 20:12:30 +00:00
|
|
|
"circonus_check_display_name",
|
|
|
|
"circonus_check_tags",
|
2016-07-22 16:33:10 +00:00
|
|
|
"circonus_broker_id",
|
2016-07-22 19:16:14 +00:00
|
|
|
"circonus_broker_select_tag",
|
2017-09-05 21:58:35 +00:00
|
|
|
"disable_tagged_metrics",
|
|
|
|
"backwards_compatible_metrics",
|
2018-11-14 16:13:52 +00:00
|
|
|
"prefix_filter",
|
2018-11-14 20:02:49 +00:00
|
|
|
"filter_default",
|
2018-11-14 16:13:52 +00:00
|
|
|
"disable_dispatched_job_summary_metrics",
|
2016-03-12 02:24:58 +00:00
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-12 02:24:58 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var telemetry Telemetry
|
|
|
|
if err := mapstructure.WeakDecode(m, &telemetry); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2016-06-16 20:30:29 +00:00
|
|
|
if telemetry.CollectionInterval != "" {
|
|
|
|
if dur, err := time.ParseDuration(telemetry.CollectionInterval); err != nil {
|
|
|
|
return fmt.Errorf("error parsing value of %q: %v", "collection_interval", err)
|
|
|
|
} else {
|
|
|
|
telemetry.collectionInterval = dur
|
|
|
|
}
|
|
|
|
}
|
2016-03-12 02:24:58 +00:00
|
|
|
*result = &telemetry
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-22 03:15:58 +00:00
|
|
|
func parseConsulConfig(result **config.ConsulConfig, list *ast.ObjectList) error {
|
2016-03-31 06:20:24 +00:00
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'consul' block allowed")
|
|
|
|
}
|
|
|
|
|
2016-06-17 03:41:05 +00:00
|
|
|
// Get our Consul object
|
2016-03-31 06:20:24 +00:00
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
2016-05-26 20:26:08 +00:00
|
|
|
"address",
|
2016-03-31 06:20:24 +00:00
|
|
|
"auth",
|
2016-06-14 19:11:38 +00:00
|
|
|
"auto_advertise",
|
2016-03-31 06:20:24 +00:00
|
|
|
"ca_file",
|
|
|
|
"cert_file",
|
2016-10-27 18:29:12 +00:00
|
|
|
"checks_use_advertise",
|
2016-05-11 22:22:00 +00:00
|
|
|
"client_auto_join",
|
2016-05-24 04:36:23 +00:00
|
|
|
"client_service_name",
|
2018-03-20 15:03:58 +00:00
|
|
|
"client_http_check_name",
|
2016-05-24 04:36:23 +00:00
|
|
|
"key_file",
|
2016-05-11 22:22:00 +00:00
|
|
|
"server_auto_join",
|
2016-05-24 04:36:23 +00:00
|
|
|
"server_service_name",
|
2018-03-20 15:03:58 +00:00
|
|
|
"server_http_check_name",
|
|
|
|
"server_serf_check_name",
|
|
|
|
"server_rpc_check_name",
|
2016-05-24 04:36:23 +00:00
|
|
|
"ssl",
|
2016-05-26 20:31:32 +00:00
|
|
|
"timeout",
|
2016-05-24 04:36:23 +00:00
|
|
|
"token",
|
|
|
|
"verify_ssl",
|
2016-03-31 06:20:24 +00:00
|
|
|
}
|
|
|
|
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-03-31 06:20:24 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-06-17 03:41:05 +00:00
|
|
|
consulConfig := config.DefaultConsulConfig()
|
2016-05-27 18:47:52 +00:00
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &consulConfig,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
2016-03-31 06:20:24 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-06-17 03:41:05 +00:00
|
|
|
*result = consulConfig
|
2016-03-31 06:20:24 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-10-25 22:57:38 +00:00
|
|
|
func parseTLSConfig(result **config.TLSConfig, list *ast.ObjectList) error {
|
2016-10-24 20:46:22 +00:00
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'tls' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the TLS object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
valid := []string{
|
2016-10-25 00:07:44 +00:00
|
|
|
"http",
|
|
|
|
"rpc",
|
2016-10-24 20:46:22 +00:00
|
|
|
"verify_server_hostname",
|
2017-10-25 17:59:08 +00:00
|
|
|
"rpc_upgrade_mode",
|
2016-10-24 20:46:22 +00:00
|
|
|
"ca_file",
|
|
|
|
"cert_file",
|
|
|
|
"key_file",
|
2017-04-28 09:45:09 +00:00
|
|
|
"verify_https_client",
|
2018-05-08 20:32:07 +00:00
|
|
|
"tls_cipher_suites",
|
2018-05-09 20:30:02 +00:00
|
|
|
"tls_min_version",
|
2018-05-23 17:34:53 +00:00
|
|
|
"tls_prefer_server_cipher_suites",
|
2016-10-24 20:46:22 +00:00
|
|
|
}
|
|
|
|
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-10-24 20:46:22 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-10-25 22:57:38 +00:00
|
|
|
var tlsConfig config.TLSConfig
|
2016-10-24 20:46:22 +00:00
|
|
|
if err := mapstructure.WeakDecode(m, &tlsConfig); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-15 01:53:23 +00:00
|
|
|
|
2018-08-06 17:54:57 +00:00
|
|
|
if _, err := tlsutil.ParseCiphers(&tlsConfig); err != nil {
|
2018-05-08 20:32:07 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-05-09 20:30:02 +00:00
|
|
|
if _, err := tlsutil.ParseMinVersion(tlsConfig.TLSMinVersion); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2016-10-24 20:46:22 +00:00
|
|
|
*result = &tlsConfig
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-08-06 01:13:06 +00:00
|
|
|
func parseVaultConfig(result **config.VaultConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'vault' block allowed")
|
|
|
|
}
|
|
|
|
|
2016-08-08 22:16:40 +00:00
|
|
|
// Get our Vault object
|
2016-08-06 01:13:06 +00:00
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"address",
|
|
|
|
"allow_unauthenticated",
|
2016-08-09 21:52:20 +00:00
|
|
|
"enabled",
|
2016-08-13 04:59:31 +00:00
|
|
|
"task_token_ttl",
|
2016-11-03 21:24:39 +00:00
|
|
|
"ca_file",
|
|
|
|
"ca_path",
|
|
|
|
"cert_file",
|
2017-01-19 21:40:32 +00:00
|
|
|
"create_from_role",
|
2016-11-03 21:24:39 +00:00
|
|
|
"key_file",
|
2016-08-06 01:13:06 +00:00
|
|
|
"tls_server_name",
|
2016-08-08 22:16:40 +00:00
|
|
|
"tls_skip_verify",
|
2016-08-13 04:59:31 +00:00
|
|
|
"token",
|
2019-04-03 11:58:34 +00:00
|
|
|
"namespace",
|
2016-08-06 01:13:06 +00:00
|
|
|
}
|
|
|
|
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2016-08-06 01:13:06 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
vaultConfig := config.DefaultVaultConfig()
|
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &vaultConfig,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = vaultConfig
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-09-19 14:47:10 +00:00
|
|
|
func parseSentinel(result **config.SentinelConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'sentinel' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our sentinel object
|
|
|
|
obj := list.Items[0]
|
|
|
|
|
|
|
|
// Value should be an object
|
|
|
|
var listVal *ast.ObjectList
|
|
|
|
if ot, ok := obj.Val.(*ast.ObjectType); ok {
|
|
|
|
listVal = ot.List
|
|
|
|
} else {
|
|
|
|
return fmt.Errorf("sentinel value: should be an object")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"import",
|
|
|
|
}
|
2017-10-13 21:36:02 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
2017-09-19 14:47:10 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var config config.SentinelConfig
|
|
|
|
if err := hcl.DecodeObject(&config, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = &config
|
|
|
|
return nil
|
|
|
|
}
|
2017-12-18 21:16:23 +00:00
|
|
|
|
|
|
|
func parseAutopilot(result **config.AutopilotConfig, list *ast.ObjectList) error {
|
|
|
|
list = list.Elem()
|
|
|
|
if len(list.Items) > 1 {
|
|
|
|
return fmt.Errorf("only one 'autopilot' block allowed")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get our Autopilot object
|
|
|
|
listVal := list.Items[0].Val
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"cleanup_dead_servers",
|
|
|
|
"server_stabilization_time",
|
|
|
|
"last_contact_threshold",
|
|
|
|
"max_trailing_logs",
|
2018-01-30 03:53:34 +00:00
|
|
|
"enable_redundancy_zones",
|
2017-12-18 21:16:23 +00:00
|
|
|
"disable_upgrade_migration",
|
2018-01-30 03:53:34 +00:00
|
|
|
"enable_custom_upgrades",
|
2017-12-18 21:16:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := helper.CheckHCLKeys(listVal, valid); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
var m map[string]interface{}
|
|
|
|
if err := hcl.DecodeObject(&m, listVal); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
autopilotConfig := config.DefaultAutopilotConfig()
|
|
|
|
dec, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
|
|
|
DecodeHook: mapstructure.StringToTimeDurationHookFunc(),
|
|
|
|
WeaklyTypedInput: true,
|
|
|
|
Result: &autopilotConfig,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := dec.Decode(m); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = autopilotConfig
|
|
|
|
return nil
|
|
|
|
}
|
2018-08-29 23:26:44 +00:00
|
|
|
|
|
|
|
func parsePlugins(result *[]*config.PluginConfig, list *ast.ObjectList) error {
|
|
|
|
listLen := len(list.Items)
|
|
|
|
plugins := make([]*config.PluginConfig, listLen)
|
|
|
|
|
|
|
|
// Check for invalid keys
|
|
|
|
valid := []string{
|
|
|
|
"args",
|
|
|
|
"config",
|
|
|
|
}
|
|
|
|
|
|
|
|
for i := 0; i < listLen; i++ {
|
|
|
|
// Get the current plugin object
|
|
|
|
listVal := list.Items[i]
|
|
|
|
|
2019-03-28 21:30:38 +00:00
|
|
|
// Deal with json->hcl AST parsing incorrectness when directly nested
|
2019-03-29 13:56:25 +00:00
|
|
|
// items show up as additional keys.
|
|
|
|
// TODO(preetha): Add additional tests and fix other places that have the same issue
|
2019-03-28 21:30:38 +00:00
|
|
|
unwrapLegacyHCLObjectKeysFromJSON(listVal, 1)
|
2018-08-29 23:26:44 +00:00
|
|
|
if err := helper.CheckHCLKeys(listVal.Val, valid); err != nil {
|
|
|
|
return fmt.Errorf("invalid keys in plugin config %d: %v", i+1, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure there is a key
|
|
|
|
if len(listVal.Keys) != 1 {
|
|
|
|
return fmt.Errorf("plugin config %d doesn't incude a name key", i+1)
|
|
|
|
}
|
|
|
|
|
|
|
|
var plugin config.PluginConfig
|
|
|
|
if err := hcl.DecodeObject(&plugin, listVal); err != nil {
|
|
|
|
return fmt.Errorf("error decoding plugin config %d: %v", i+1, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
plugins[i] = &plugin
|
|
|
|
}
|
|
|
|
|
|
|
|
*result = plugins
|
|
|
|
return nil
|
|
|
|
}
|
2019-03-28 21:30:38 +00:00
|
|
|
|
|
|
|
// unwrapLegacyHCLObjectKeysFromJSON cleans up an edge case that can occur when
|
|
|
|
// parsing JSON as input: if we're parsing JSON then directly nested
|
|
|
|
// items will show up as additional "keys".
|
|
|
|
//
|
|
|
|
// For objects that expect a fixed number of keys, this breaks the
|
|
|
|
// decoding process. This function unwraps the object into what it would've
|
|
|
|
// looked like if it came directly from HCL by specifying the number of keys
|
|
|
|
// you expect.
|
|
|
|
//
|
|
|
|
// Example:
|
|
|
|
//
|
|
|
|
// { "foo": { "baz": {} } }
|
|
|
|
//
|
|
|
|
// Will show up with Keys being: []string{"foo", "baz"}
|
|
|
|
// when we really just want the first two. This function will fix this.
|
|
|
|
func unwrapLegacyHCLObjectKeysFromJSON(item *ast.ObjectItem, depth int) {
|
|
|
|
if len(item.Keys) > depth && item.Keys[0].Token.JSON {
|
|
|
|
for len(item.Keys) > depth {
|
|
|
|
// Pop off the last key
|
|
|
|
n := len(item.Keys)
|
|
|
|
key := item.Keys[n-1]
|
|
|
|
item.Keys[n-1] = nil
|
|
|
|
item.Keys = item.Keys[:n-1]
|
|
|
|
|
|
|
|
// Wrap our value in a list
|
|
|
|
item.Val = &ast.ObjectType{
|
|
|
|
List: &ast.ObjectList{
|
|
|
|
Items: []*ast.ObjectItem{
|
2019-03-28 23:01:40 +00:00
|
|
|
{
|
2019-03-28 21:30:38 +00:00
|
|
|
Keys: []*ast.ObjectKey{key},
|
|
|
|
Val: item.Val,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|