config: Introduce DeprecatedConfig

This struct allows us to move all the deprecated config options off of
the main config struct, and keeps all the deprecation logic in a single
place, instead of spread across 3+ places.
This commit is contained in:
Daniel Nephin 2020-12-22 14:19:34 -05:00
parent d2274df53f
commit 8ed14296ea
5 changed files with 77 additions and 27 deletions

View File

@ -346,10 +346,12 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
for _, err := range validateEnterpriseConfigKeys(&c2) {
b.warn("%s", err)
}
b.Warnings = append(b.Warnings, md.Warnings...)
// if we have a single 'check' or 'service' we need to add them to the
// list of checks and services first since we cannot merge them
// generically and later values would clobber earlier ones.
// TODO: move to applyDeprecatedConfig
if c2.Check != nil {
c2.Checks = append(c2.Checks, *c2.Check)
c2.Check = nil
@ -733,16 +735,6 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
aclsEnabled := false
primaryDatacenter := strings.ToLower(stringVal(c.PrimaryDatacenter))
if c.ACLDatacenter != nil {
b.warn("The 'acl_datacenter' field is deprecated. Use the 'primary_datacenter' field instead.")
if primaryDatacenter == "" {
primaryDatacenter = strings.ToLower(stringVal(c.ACLDatacenter))
}
// when the acl_datacenter config is used it implicitly enables acls
aclsEnabled = true
}
if c.ACL.Enabled != nil {
aclsEnabled = boolVal(c.ACL.Enabled)
@ -887,7 +879,7 @@ func (b *builder) build() (rt RuntimeConfig, err error) {
EnablePersistence: boolValWithDefault(c.ACL.EnableTokenPersistence, false),
ACLDefaultToken: stringValWithDefault(c.ACL.Tokens.Default, stringVal(c.ACLToken)),
ACLAgentToken: stringValWithDefault(c.ACL.Tokens.Agent, stringVal(c.ACLAgentToken)),
ACLAgentMasterToken: stringValWithDefault(c.ACL.Tokens.AgentMaster, stringVal(c.ACLAgentMasterToken)),
ACLAgentMasterToken: stringVal(c.ACL.Tokens.AgentMaster),
ACLReplicationToken: stringValWithDefault(c.ACL.Tokens.Replication, stringVal(c.ACLReplicationToken)),
},

View File

@ -15,7 +15,7 @@ type Source interface {
// Source returns an identifier for the Source that can be used in error message
Source() string
// Parse a configuration and return the result.
Parse() (Config, mapstructure.Metadata, error)
Parse() (Config, Metadata, error)
}
// ErrNoData indicates to Builder.Build that the source contained no data, and
@ -34,9 +34,10 @@ func (f FileSource) Source() string {
}
// Parse a config file in either JSON or HCL format.
func (f FileSource) Parse() (Config, mapstructure.Metadata, error) {
func (f FileSource) Parse() (Config, Metadata, error) {
m := Metadata{}
if f.Name == "" || f.Data == "" {
return Config{}, mapstructure.Metadata{}, ErrNoData
return Config{}, m, ErrNoData
}
var raw map[string]interface{}
@ -51,10 +52,10 @@ func (f FileSource) Parse() (Config, mapstructure.Metadata, error) {
err = fmt.Errorf("invalid format: %s", f.Format)
}
if err != nil {
return Config{}, md, err
return Config{}, m, err
}
var c Config
var target decodeTarget
d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
DecodeHook: mapstructure.ComposeDecodeHookFunc(
// decode.HookWeakDecodeFromSlice is only necessary when reading from
@ -66,16 +67,30 @@ func (f FileSource) Parse() (Config, mapstructure.Metadata, error) {
decode.HookTranslateKeys,
),
Metadata: &md,
Result: &c,
Result: &target,
})
if err != nil {
return Config{}, md, err
return Config{}, m, err
}
if err := d.Decode(raw); err != nil {
return Config{}, md, err
return Config{}, m, err
}
return c, md, nil
c, warns := applyDeprecatedConfig(&target)
m.Unused = md.Unused
m.Keys = md.Keys
m.Warnings = warns
return c, m, nil
}
// Metadata created by Source.Parse
type Metadata struct {
// Keys used in the config file.
Keys []string
// Unused keys that did not match any struct fields.
Unused []string
// Warnings caused by deprecated fields
Warnings []string
}
// LiteralSource implements Source and returns an existing Config struct.
@ -88,8 +103,13 @@ func (l LiteralSource) Source() string {
return l.Name
}
func (l LiteralSource) Parse() (Config, mapstructure.Metadata, error) {
return l.Config, mapstructure.Metadata{}, nil
func (l LiteralSource) Parse() (Config, Metadata, error) {
return l.Config, Metadata{}, nil
}
type decodeTarget struct {
DeprecatedConfig `mapstructure:",squash"`
Config `mapstructure:",squash"`
}
// Cache configuration for the agent/cache.
@ -110,12 +130,8 @@ type Cache struct {
// configuration it should be treated as an external API which cannot be
// changed and refactored at will since this will break existing setups.
type Config struct {
// DEPRECATED (ACL-Legacy-Compat) - moved into the "acl.tokens" stanza
ACLAgentMasterToken *string `mapstructure:"acl_agent_master_token"`
// DEPRECATED (ACL-Legacy-Compat) - moved into the "acl.tokens" stanza
ACLAgentToken *string `mapstructure:"acl_agent_token"`
// DEPRECATED (ACL-Legacy-Compat) - moved to "primary_datacenter"
ACLDatacenter *string `mapstructure:"acl_datacenter"`
// DEPRECATED (ACL-Legacy-Compat) - moved into the "acl" stanza
ACLDefaultPolicy *string `mapstructure:"acl_default_policy"`
// DEPRECATED (ACL-Legacy-Compat) - moved into the "acl" stanza

View File

@ -0,0 +1,42 @@
package config
import "fmt"
type DeprecatedConfig struct {
// DEPRECATED (ACL-Legacy-Compat) - moved into the "acl.tokens" stanza
ACLAgentMasterToken *string `mapstructure:"acl_agent_master_token"`
// DEPRECATED (ACL-Legacy-Compat) - moved to "primary_datacenter"
ACLDatacenter *string `mapstructure:"acl_datacenter"`
}
func applyDeprecatedConfig(d *decodeTarget) (Config, []string) {
dep := d.DeprecatedConfig
var warns []string
if dep.ACLAgentMasterToken != nil {
if d.Config.ACL.Tokens.AgentMaster == nil {
d.Config.ACL.Tokens.AgentMaster = dep.ACLAgentMasterToken
}
warns = append(warns, deprecationWarning("acl_agent_master_token", "acl.tokens.agent_master"))
}
if dep.ACLDatacenter != nil {
if d.Config.PrimaryDatacenter == nil {
d.Config.PrimaryDatacenter = dep.ACLDatacenter
}
// when the acl_datacenter config is used it implicitly enables acls
d.Config.ACL.Enabled = pBool(true)
warns = append(warns, deprecationWarning("acl_datacenter", "primary_datacenter"))
}
return d.Config, warns
}
func deprecationWarning(old, new string) string {
return fmt.Sprintf("The '%v' field is deprecated. Use the '%v' field instead.", old, new)
}
func pBool(v bool) *bool {
return &v
}

View File

@ -52,7 +52,6 @@ func TestMerge(t *testing.T) {
}
}
func pBool(v bool) *bool { return &v }
func pInt(v int) *int { return &v }
func pString(v string) *string { return &v }
func pDuration(v time.Duration) *string { s := v.String(); return &s }

View File

@ -5903,6 +5903,7 @@ func TestLoad_FullConfig(t *testing.T) {
expectedWarns := []string{
`The 'acl_datacenter' field is deprecated. Use the 'primary_datacenter' field instead.`,
`The 'acl_agent_master_token' field is deprecated. Use the 'acl.tokens.agent_master' field instead.`,
`bootstrap_expect > 0: expecting 53 servers`,
}
expectedWarns = append(expectedWarns, enterpriseConfigKeyWarnings...)