diff --git a/agent/config/config.go b/agent/config/config.go index f1d39b3df..df5bca3c7 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/decode" "github.com/hashicorp/go-multierror" "github.com/hashicorp/hcl" "github.com/mitchellh/mapstructure" @@ -108,32 +109,11 @@ func Parse(data string, format string) (c Config, keys []string, err error) { "config_entries.bootstrap", // completely ignore this tree (fixed elsewhere) }) - // There is a difference of representation of some fields depending on - // where they are used. The HTTP API uses CamelCase whereas the config - // files use snake_case and between the two there is no automatic mapping. - // While the JSON and HCL parsers match keys without case (both `id` and - // `ID` are mapped to an ID field) the same thing does not happen between - // CamelCase and snake_case. Since changing either format would break - // existing setups we have to support both and slowly transition to one of - // the formats. Also, there is at least one case where we use the "wrong" - // key and want to map that to the new key to support deprecation - - // see [GH-3179]. TranslateKeys maps potentially CamelCased values to the - // snake_case that is used in the config file parser. If both the CamelCase - // and snake_case values are set the snake_case value is used and the other - // value is discarded. - lib.TranslateKeys(m, map[string]string{ - "deregistercriticalserviceafter": "deregister_critical_service_after", - "dockercontainerid": "docker_container_id", - "scriptargs": "args", - "serviceid": "service_id", - "tlsskipverify": "tls_skip_verify", - "config_entries.bootstrap": "", - }) - var md mapstructure.Metadata d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ - Metadata: &md, - Result: &c, + DecodeHook: decode.HookTranslateKeys, + Metadata: &md, + Result: &c, }) if err != nil { return Config{}, nil, err diff --git a/agent/discovery_chain_endpoint.go b/agent/discovery_chain_endpoint.go index 6765fed57..01bdf81eb 100644 --- a/agent/discovery_chain_endpoint.go +++ b/agent/discovery_chain_endpoint.go @@ -9,6 +9,7 @@ import ( cachetype "github.com/hashicorp/consul/agent/cache-types" "github.com/hashicorp/consul/agent/structs" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/decode" "github.com/mitchellh/mapstructure" ) @@ -105,15 +106,12 @@ func decodeDiscoveryChainReadRequest(raw map[string]interface{}) (*discoveryChai // to do this part first. raw = lib.PatchSliceOfMaps(raw, nil, nil) - lib.TranslateKeys(raw, map[string]string{ - "override_mesh_gateway": "overridemeshgateway", - "override_protocol": "overrideprotocol", - "override_connect_timeout": "overrideconnecttimeout", - }) - var apiReq discoveryChainReadRequest decodeConf := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + decode.HookTranslateKeys, + mapstructure.StringToTimeDurationHookFunc(), + ), Result: &apiReq, WeaklyTypedInput: true, } diff --git a/agent/structs/config_entry.go b/agent/structs/config_entry.go index f57517dbf..0cacafef3 100644 --- a/agent/structs/config_entry.go +++ b/agent/structs/config_entry.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/consul/acl" "github.com/hashicorp/consul/agent/cache" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/decode" "github.com/hashicorp/go-msgpack/codec" "github.com/hashicorp/go-multierror" "github.com/mitchellh/hashstructure" @@ -283,7 +284,7 @@ func DecodeConfigEntry(raw map[string]interface{}) (ConfigEntry, error) { return nil, fmt.Errorf("Kind value in payload is not a string") } - skipWhenPatching, translateKeysDict, err := ConfigEntryDecodeRulesForKind(entry.GetKind()) + skipWhenPatching, err := ConfigEntryDecodeRulesForKind(entry.GetKind()) if err != nil { return nil, err } @@ -292,11 +293,12 @@ func DecodeConfigEntry(raw map[string]interface{}) (ConfigEntry, error) { // to do this part first. raw = lib.PatchSliceOfMaps(raw, skipWhenPatching, nil) - lib.TranslateKeys(raw, translateKeysDict) - var md mapstructure.Metadata decodeConf := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + decode.HookTranslateKeys, + mapstructure.StringToTimeDurationHookFunc(), + ), Metadata: &md, Result: &entry, WeaklyTypedInput: true, @@ -327,80 +329,48 @@ func DecodeConfigEntry(raw map[string]interface{}) (ConfigEntry, error) { // ConfigEntryDecodeRulesForKind returns rules for 'fixing' config entry key // formats by kind. This is shared between the 'structs' and 'api' variations // of config entries. -func ConfigEntryDecodeRulesForKind(kind string) (skipWhenPatching []string, translateKeysDict map[string]string, err error) { +func ConfigEntryDecodeRulesForKind(kind string) (skipWhenPatching []string, err error) { switch kind { case ProxyDefaults: return []string{ - "expose.paths", - "Expose.Paths", - }, map[string]string{ - "local_path_port": "localpathport", - "listener_port": "listenerport", - "mesh_gateway": "meshgateway", - "config": "", - }, nil + "expose.paths", + "Expose.Paths", + }, nil case ServiceDefaults: return []string{ - "expose.paths", - "Expose.Paths", - }, map[string]string{ - "local_path_port": "localpathport", - "listener_port": "listenerport", - "mesh_gateway": "meshgateway", - "external_sni": "externalsni", - }, nil + "expose.paths", + "Expose.Paths", + }, nil case ServiceRouter: return []string{ - "routes", - "Routes", - "routes.match.http.header", - "Routes.Match.HTTP.Header", - "routes.match.http.query_param", - "Routes.Match.HTTP.QueryParam", - }, map[string]string{ - "num_retries": "numretries", - "path_exact": "pathexact", - "path_prefix": "pathprefix", - "path_regex": "pathregex", - "prefix_rewrite": "prefixrewrite", - "query_param": "queryparam", - "request_timeout": "requesttimeout", - "retry_on_connect_failure": "retryonconnectfailure", - "retry_on_status_codes": "retryonstatuscodes", - "service_subset": "servicesubset", - }, nil + "routes", + "Routes", + "routes.match.http.header", + "Routes.Match.HTTP.Header", + "routes.match.http.query_param", + "Routes.Match.HTTP.QueryParam", + }, nil case ServiceSplitter: return []string{ - "splits", - "Splits", - }, map[string]string{ - "service_subset": "servicesubset", - }, nil - case ServiceResolver: - return nil, map[string]string{ - "connect_timeout": "connecttimeout", - "default_subset": "defaultsubset", - "only_passing": "onlypassing", - "service_subset": "servicesubset", + "splits", + "Splits", }, nil + case ServiceResolver: + return nil, nil case IngressGateway: return []string{ "listeners", "Listeners", "listeners.services", "Listeners.Services", - }, nil, nil + }, nil case TerminatingGateway: return []string{ - "services", - "Services", - }, map[string]string{ - "ca_file": "cafile", - "cert_file": "certfile", - "key_file": "keyfile", - }, nil + "services", + "Services", + }, nil default: - return nil, nil, fmt.Errorf("kind %q should be explicitly handled here", kind) + return nil, fmt.Errorf("kind %q should be explicitly handled here", kind) } } diff --git a/agent/xds/config.go b/agent/xds/config.go index 2e2c236c9..d2a54e443 100644 --- a/agent/xds/config.go +++ b/agent/xds/config.go @@ -7,7 +7,7 @@ import ( envoycluster "github.com/envoyproxy/go-control-plane/envoy/api/v2/cluster" "github.com/gogo/protobuf/types" "github.com/hashicorp/consul/agent/structs" - "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/decode" "github.com/mitchellh/mapstructure" ) @@ -97,15 +97,18 @@ type GatewayConfig struct { // error occurs during parsing, it is returned along with the default config. This // allows the caller to choose whether and how to report the error func ParseGatewayConfig(m map[string]interface{}) (GatewayConfig, error) { - // Fixup for deprecated mesh gateway names - lib.TranslateKeys(m, map[string]string{ - "envoy_mesh_gateway_bind_tagged_addresses": "envoy_gateway_bind_tagged_addresses", - "envoy_mesh_gateway_bind_addresses": "envoy_gateway_bind_addresses", - "envoy_mesh_gateway_no_default_bind": "envoy_gateway_no_default_bind", - }) - var cfg GatewayConfig - err := mapstructure.WeakDecode(m, &cfg) + d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{ + DecodeHook: decode.HookTranslateKeys, + Result: &cfg, + WeaklyTypedInput: true, + }) + if err != nil { + return cfg, err + } + if err := d.Decode(m); err != nil { + return cfg, err + } if cfg.ConnectTimeoutMs < 1 { cfg.ConnectTimeoutMs = 5000 diff --git a/command/config/write/config_write.go b/command/config/write/config_write.go index 3cc3de3f6..81deeb89e 100644 --- a/command/config/write/config_write.go +++ b/command/config/write/config_write.go @@ -10,6 +10,7 @@ import ( "github.com/hashicorp/consul/command/flags" "github.com/hashicorp/consul/command/helpers" "github.com/hashicorp/consul/lib" + "github.com/hashicorp/consul/lib/decode" "github.com/hashicorp/go-multierror" "github.com/mitchellh/cli" "github.com/mitchellh/mapstructure" @@ -132,7 +133,7 @@ func newDecodeConfigEntry(raw map[string]interface{}) (api.ConfigEntry, error) { return nil, fmt.Errorf("Kind value in payload is not a string") } - skipWhenPatching, translateKeysDict, err := structs.ConfigEntryDecodeRulesForKind(entry.GetKind()) + skipWhenPatching, err := structs.ConfigEntryDecodeRulesForKind(entry.GetKind()) if err != nil { return nil, err } @@ -141,14 +142,12 @@ func newDecodeConfigEntry(raw map[string]interface{}) (api.ConfigEntry, error) { // to do this part first. raw = lib.PatchSliceOfMaps(raw, skipWhenPatching, nil) - // CamelCase is the canonical form for these, since this translation - // happens in the `consul config write` command and the JSON form is sent - // off to the server. - lib.TranslateKeys(raw, translateKeysDict) - var md mapstructure.Metadata decodeConf := &mapstructure.DecoderConfig{ - DecodeHook: mapstructure.StringToTimeDurationHookFunc(), + DecodeHook: mapstructure.ComposeDecodeHookFunc( + decode.HookTranslateKeys, + mapstructure.StringToTimeDurationHookFunc(), + ), Metadata: &md, Result: &entry, WeaklyTypedInput: true, diff --git a/lib/translate.go b/lib/translate.go index 6bb934cc6..815dd73a4 100644 --- a/lib/translate.go +++ b/lib/translate.go @@ -34,6 +34,8 @@ import ( // // item's config field // "widgets.config": "", // }) +// +// Deprecated: Use lib/decode.HookTranslateKeys instead. func TranslateKeys(v map[string]interface{}, dict map[string]string) { // Convert all dict keys for exclusions to lower. so we can match against them // unambiguously with a single lookup.