Merge pull request #8500 from hashicorp/dnephin/auto-config-loader
auto-config: reduce awareness of config
This commit is contained in:
commit
7d4201a09c
|
@ -433,7 +433,7 @@ func New(options ...AgentOption) (*Agent, error) {
|
|||
}
|
||||
|
||||
// parse the configuration and handle the error/warnings
|
||||
config, warnings, err := autoconf.LoadConfig(flat.builderOpts, nil, flat.overrides...)
|
||||
cfg, warnings, err := config.Load(flat.builderOpts, nil, flat.overrides...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -449,7 +449,7 @@ func New(options ...AgentOption) (*Agent, error) {
|
|||
|
||||
// set the config in the agent, this is just the preliminary configuration as we haven't
|
||||
// loaded any auto-config sources yet.
|
||||
a.config = config
|
||||
a.config = cfg
|
||||
|
||||
// create the cache using the rate limiting settings from the config. Note that this means
|
||||
// that these limits are not reloadable.
|
||||
|
@ -457,15 +457,15 @@ func New(options ...AgentOption) (*Agent, error) {
|
|||
|
||||
if flat.logger == nil {
|
||||
logConf := &logging.Config{
|
||||
LogLevel: config.LogLevel,
|
||||
LogJSON: config.LogJSON,
|
||||
LogLevel: cfg.LogLevel,
|
||||
LogJSON: cfg.LogJSON,
|
||||
Name: logging.Agent,
|
||||
EnableSyslog: config.EnableSyslog,
|
||||
SyslogFacility: config.SyslogFacility,
|
||||
LogFilePath: config.LogFile,
|
||||
LogRotateDuration: config.LogRotateDuration,
|
||||
LogRotateBytes: config.LogRotateBytes,
|
||||
LogRotateMaxFiles: config.LogRotateMaxFiles,
|
||||
EnableSyslog: cfg.EnableSyslog,
|
||||
SyslogFacility: cfg.SyslogFacility,
|
||||
LogFilePath: cfg.LogFile,
|
||||
LogRotateDuration: cfg.LogRotateDuration,
|
||||
LogRotateBytes: cfg.LogRotateBytes,
|
||||
LogRotateMaxFiles: cfg.LogRotateMaxFiles,
|
||||
}
|
||||
|
||||
a.logger, err = logging.Setup(logConf, flat.writers)
|
||||
|
@ -477,7 +477,7 @@ func New(options ...AgentOption) (*Agent, error) {
|
|||
}
|
||||
|
||||
if flat.initTelemetry {
|
||||
memSink, err := lib.InitTelemetry(config.Telemetry)
|
||||
memSink, err := lib.InitTelemetry(cfg.Telemetry)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to initialize telemetry: %w", err)
|
||||
}
|
||||
|
@ -539,12 +539,14 @@ func New(options ...AgentOption) (*Agent, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
acConf := new(autoconf.Config).
|
||||
WithDirectRPC(a.connPool).
|
||||
WithBuilderOpts(flat.builderOpts).
|
||||
WithLogger(a.logger).
|
||||
WithOverrides(flat.overrides...).
|
||||
WithCertMonitor(acCertMon)
|
||||
acConf := autoconf.Config{
|
||||
DirectRPC: a.connPool,
|
||||
Logger: a.logger,
|
||||
CertMonitor: acCertMon,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
return config.Load(flat.builderOpts, source, flat.overrides...)
|
||||
},
|
||||
}
|
||||
ac, err := autoconf.New(acConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -54,26 +54,20 @@ var (
|
|||
// then we will need to add some locking here. I am deferring that for now
|
||||
// to help ease the review of this already large PR.
|
||||
type AutoConfig struct {
|
||||
builderOpts config.BuilderOpts
|
||||
acConfig Config
|
||||
logger hclog.Logger
|
||||
directRPC DirectRPC
|
||||
waiter *lib.RetryWaiter
|
||||
overrides []config.Source
|
||||
certMonitor CertMonitor
|
||||
config *config.RuntimeConfig
|
||||
autoConfigResponse *pbautoconf.AutoConfigResponse
|
||||
autoConfigSource config.Source
|
||||
cancel context.CancelFunc
|
||||
}
|
||||
|
||||
// New creates a new AutoConfig object for providing automatic
|
||||
// Consul configuration.
|
||||
func New(config *Config) (*AutoConfig, error) {
|
||||
if config == nil {
|
||||
return nil, fmt.Errorf("must provide a config struct")
|
||||
}
|
||||
|
||||
if config.DirectRPC == nil {
|
||||
// New creates a new AutoConfig object for providing automatic Consul configuration.
|
||||
func New(config Config) (*AutoConfig, error) {
|
||||
switch {
|
||||
case config.Loader == nil:
|
||||
return nil, fmt.Errorf("must provide a config loader")
|
||||
case config.DirectRPC == nil:
|
||||
return nil, fmt.Errorf("must provide a direct RPC delegate")
|
||||
}
|
||||
|
||||
|
@ -84,27 +78,21 @@ func New(config *Config) (*AutoConfig, error) {
|
|||
logger = logger.Named(logging.AutoConfig)
|
||||
}
|
||||
|
||||
waiter := config.Waiter
|
||||
if waiter == nil {
|
||||
waiter = lib.NewRetryWaiter(1, 0, 10*time.Minute, lib.NewJitterRandomStagger(25))
|
||||
if config.Waiter == nil {
|
||||
config.Waiter = lib.NewRetryWaiter(1, 0, 10*time.Minute, lib.NewJitterRandomStagger(25))
|
||||
}
|
||||
|
||||
ac := &AutoConfig{
|
||||
builderOpts: config.BuilderOpts,
|
||||
return &AutoConfig{
|
||||
acConfig: config,
|
||||
logger: logger,
|
||||
directRPC: config.DirectRPC,
|
||||
waiter: waiter,
|
||||
overrides: config.Overrides,
|
||||
certMonitor: config.CertMonitor,
|
||||
}
|
||||
|
||||
return ac, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadConfig will parse the current configuration and inject any
|
||||
// auto-config sources if present into the correct place in the parsing chain.
|
||||
func (ac *AutoConfig) ReadConfig() (*config.RuntimeConfig, error) {
|
||||
cfg, warnings, err := LoadConfig(ac.builderOpts, ac.autoConfigSource, ac.overrides...)
|
||||
cfg, warnings, err := ac.acConfig.Loader(ac.autoConfigSource)
|
||||
if err != nil {
|
||||
return cfg, err
|
||||
}
|
||||
|
@ -377,7 +365,7 @@ func (ac *AutoConfig) getInitialConfigurationOnce(ctx context.Context, csr strin
|
|||
}
|
||||
|
||||
ac.logger.Debug("making AutoConfig.InitialConfiguration RPC", "addr", addr.String())
|
||||
if err = ac.directRPC.RPC(ac.config.Datacenter, ac.config.NodeName, &addr, "AutoConfig.InitialConfiguration", &request, &resp); err != nil {
|
||||
if err = ac.acConfig.DirectRPC.RPC(ac.config.Datacenter, ac.config.NodeName, &addr, "AutoConfig.InitialConfiguration", &request, &resp); err != nil {
|
||||
ac.logger.Error("AutoConfig.InitialConfiguration RPC failed", "addr", addr.String(), "error", err)
|
||||
continue
|
||||
}
|
||||
|
@ -405,7 +393,7 @@ func (ac *AutoConfig) getInitialConfiguration(ctx context.Context) error {
|
|||
}
|
||||
|
||||
// this resets the failures so that we will perform immediate request
|
||||
wait := ac.waiter.Success()
|
||||
wait := ac.acConfig.Waiter.Success()
|
||||
for {
|
||||
select {
|
||||
case <-wait:
|
||||
|
@ -417,7 +405,7 @@ func (ac *AutoConfig) getInitialConfiguration(ctx context.Context) error {
|
|||
} else {
|
||||
ac.logger.Error("No error returned when fetching the initial auto-configuration but no response was either")
|
||||
}
|
||||
wait = ac.waiter.Failed()
|
||||
wait = ac.acConfig.Waiter.Failed()
|
||||
case <-ctx.Done():
|
||||
ac.logger.Info("interrupted during initial auto configuration", "err", ctx.Err())
|
||||
return ctx.Err()
|
||||
|
|
|
@ -87,11 +87,23 @@ func TestNew(t *testing.T) {
|
|||
|
||||
cases := map[string]testCase{
|
||||
"no-direct-rpc": {
|
||||
config: Config{
|
||||
Loader: func(source config.Source) (cfg *config.RuntimeConfig, warnings []string, err error) {
|
||||
return nil, nil, nil
|
||||
},
|
||||
},
|
||||
err: "must provide a direct RPC delegate",
|
||||
},
|
||||
|
||||
"no-config-loader": {
|
||||
err: "must provide a config loader",
|
||||
},
|
||||
"ok": {
|
||||
config: Config{
|
||||
DirectRPC: &mockDirectRPC{},
|
||||
Loader: func(source config.Source) (cfg *config.RuntimeConfig, warnings []string, err error) {
|
||||
return nil, nil, nil
|
||||
},
|
||||
},
|
||||
validate: func(t *testing.T, ac *AutoConfig) {
|
||||
t.Helper()
|
||||
|
@ -102,7 +114,7 @@ func TestNew(t *testing.T) {
|
|||
|
||||
for name, tcase := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ac, err := New(&tcase.config)
|
||||
ac, err := New(tcase.config)
|
||||
if tcase.err != "" {
|
||||
testutil.RequireErrorContains(t, err, tcase.err)
|
||||
} else {
|
||||
|
@ -116,99 +128,64 @@ func TestNew(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestLoadConfig(t *testing.T) {
|
||||
// Basically just testing that injection of the extra
|
||||
// source works.
|
||||
devMode := true
|
||||
builderOpts := config.BuilderOpts{
|
||||
// putting this in dev mode so that the config validates
|
||||
// without having to specify a data directory
|
||||
DevMode: &devMode,
|
||||
}
|
||||
|
||||
cfg, warnings, err := LoadConfig(builderOpts, config.FileSource{
|
||||
Name: "test",
|
||||
Format: "hcl",
|
||||
Data: `node_name = "hobbiton"`,
|
||||
},
|
||||
config.FileSource{
|
||||
Name: "overrides",
|
||||
Format: "json",
|
||||
Data: `{"check_reap_interval": "1ms"}`,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, warnings)
|
||||
require.NotNil(t, cfg)
|
||||
require.Equal(t, "hobbiton", cfg.NodeName)
|
||||
require.Equal(t, 1*time.Millisecond, cfg.CheckReapInterval)
|
||||
}
|
||||
|
||||
func TestReadConfig(t *testing.T) {
|
||||
// just testing that some auto config source gets injected
|
||||
devMode := true
|
||||
ac := AutoConfig{
|
||||
autoConfigSource: config.LiteralSource{
|
||||
Name: autoConfigFileName,
|
||||
Config: config.Config{NodeName: stringPointer("hobbiton")},
|
||||
},
|
||||
builderOpts: config.BuilderOpts{
|
||||
// putting this in dev mode so that the config validates
|
||||
// without having to specify a data directory
|
||||
DevMode: &devMode,
|
||||
},
|
||||
logger: testutil.Logger(t),
|
||||
acConfig: Config{
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
cfg, _, err := source.Parse()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
return &config.RuntimeConfig{
|
||||
DevMode: true,
|
||||
NodeName: *cfg.NodeName,
|
||||
}, nil, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cfg, err := ac.ReadConfig()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
require.Equal(t, "hobbiton", cfg.NodeName)
|
||||
require.True(t, cfg.DevMode)
|
||||
require.Same(t, ac.config, cfg)
|
||||
}
|
||||
|
||||
func testSetupAutoConf(t *testing.T) (string, string, config.BuilderOpts) {
|
||||
func setupRuntimeConfig(t *testing.T) *config.RuntimeConfig {
|
||||
t.Helper()
|
||||
|
||||
// create top level directory to hold both config and data
|
||||
tld := testutil.TempDir(t, "auto-config")
|
||||
t.Cleanup(func() { os.RemoveAll(tld) })
|
||||
dataDir := testutil.TempDir(t, "auto-config")
|
||||
t.Cleanup(func() { os.RemoveAll(dataDir) })
|
||||
|
||||
// create the data directory
|
||||
dataDir := filepath.Join(tld, "data")
|
||||
require.NoError(t, os.Mkdir(dataDir, 0700))
|
||||
|
||||
// create the config directory
|
||||
configDir := filepath.Join(tld, "config")
|
||||
require.NoError(t, os.Mkdir(configDir, 0700))
|
||||
|
||||
builderOpts := config.BuilderOpts{
|
||||
HCL: []string{
|
||||
`data_dir = "` + dataDir + `"`,
|
||||
`datacenter = "dc1"`,
|
||||
`node_name = "autoconf"`,
|
||||
`bind_addr = "127.0.0.1"`,
|
||||
},
|
||||
rtConfig := &config.RuntimeConfig{
|
||||
DataDir: dataDir,
|
||||
Datacenter: "dc1",
|
||||
NodeName: "autoconf",
|
||||
BindAddr: &net.IPAddr{IP: net.ParseIP("127.0.0.1")},
|
||||
}
|
||||
|
||||
return dataDir, configDir, builderOpts
|
||||
return rtConfig
|
||||
}
|
||||
|
||||
func TestInitialConfiguration_disabled(t *testing.T) {
|
||||
dataDir, configDir, builderOpts := testSetupAutoConf(t)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"primary_datacenter": "primary",
|
||||
"auto_config": {"enabled": false}
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
|
||||
directRPC := mockDirectRPC{}
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC)
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
rtConfig.PrimaryDatacenter = "primary"
|
||||
rtConfig.AutoConfig.Enabled = false
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -217,26 +194,17 @@ func TestInitialConfiguration_disabled(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NotNil(t, cfg)
|
||||
require.Equal(t, "primary", cfg.PrimaryDatacenter)
|
||||
require.NoFileExists(t, filepath.Join(dataDir, autoConfigFileName))
|
||||
require.NoFileExists(t, filepath.Join(rtConfig.DataDir, autoConfigFileName))
|
||||
|
||||
// ensure no RPC was made
|
||||
directRPC.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func TestInitialConfiguration_cancelled(t *testing.T) {
|
||||
_, configDir, builderOpts := testSetupAutoConf(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"primary_datacenter": "primary",
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["127.0.0.1:8300"]},
|
||||
"verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
|
||||
directRPC := mockDirectRPC{}
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
expectedRequest := pbautoconf.AutoConfigRequest{
|
||||
Datacenter: "dc1",
|
||||
Node: "autoconf",
|
||||
|
@ -244,9 +212,19 @@ func TestInitialConfiguration_cancelled(t *testing.T) {
|
|||
}
|
||||
|
||||
directRPC.On("RPC", "dc1", "autoconf", &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 8300}, "AutoConfig.InitialConfiguration", &expectedRequest, mock.Anything).Return(fmt.Errorf("injected error")).Times(0)
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
rtConfig.PrimaryDatacenter = "primary"
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{"127.0.0.1:8300"},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -263,17 +241,10 @@ func TestInitialConfiguration_cancelled(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInitialConfiguration_restored(t *testing.T) {
|
||||
dataDir, configDir, builderOpts := testSetupAutoConf(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["127.0.0.1:8300"]}, "verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
// persist an auto config response to the data dir where it is expected
|
||||
persistedFile := filepath.Join(dataDir, autoConfigFileName)
|
||||
persistedFile := filepath.Join(rtConfig.DataDir, autoConfigFileName)
|
||||
response := &pbautoconf.AutoConfigResponse{
|
||||
Config: &pbconfig.Config{
|
||||
PrimaryDatacenter: "primary",
|
||||
|
@ -312,11 +283,13 @@ func TestInitialConfiguration_restored(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
require.NoError(t, ioutil.WriteFile(persistedFile, []byte(data), 0600))
|
||||
|
||||
directRPC := mockDirectRPC{}
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
|
||||
// setup the mock certificate monitor to ensure that the initial state gets
|
||||
// updated appropriately during config restoration.
|
||||
certMon := mockCertMonitor{}
|
||||
certMon := new(mockCertMonitor)
|
||||
certMon.Test(t)
|
||||
certMon.On("Update", &structs.SignedResponse{
|
||||
IssuedCert: structs.IssuedCert{
|
||||
SerialNumber: "1234",
|
||||
|
@ -349,10 +322,22 @@ func TestInitialConfiguration_restored(t *testing.T) {
|
|||
VerifyServerHostname: true,
|
||||
}).Return(nil).Once()
|
||||
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC).
|
||||
WithCertMonitor(&certMon)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
if err := setPrimaryDatacenterFromSource(rtConfig, source); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{"127.0.0.1:8300"},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
CertMonitor: certMon,
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -367,18 +352,22 @@ func TestInitialConfiguration_restored(t *testing.T) {
|
|||
certMon.AssertExpectations(t)
|
||||
}
|
||||
|
||||
func setPrimaryDatacenterFromSource(rtConfig *config.RuntimeConfig, source config.Source) error {
|
||||
if source != nil {
|
||||
cfg, _, err := source.Parse()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rtConfig.PrimaryDatacenter = *cfg.PrimaryDatacenter
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestInitialConfiguration_success(t *testing.T) {
|
||||
dataDir, configDir, builderOpts := testSetupAutoConf(t)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["127.0.0.1:8300"]}, "verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
|
||||
persistedFile := filepath.Join(dataDir, autoConfigFileName)
|
||||
directRPC := mockDirectRPC{}
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
|
||||
populateResponse := func(val interface{}) {
|
||||
resp, ok := val.(*pbautoconf.AutoConfigResponse)
|
||||
|
@ -434,7 +423,8 @@ func TestInitialConfiguration_success(t *testing.T) {
|
|||
|
||||
// setup the mock certificate monitor to ensure that the initial state gets
|
||||
// updated appropriately during config restoration.
|
||||
certMon := mockCertMonitor{}
|
||||
certMon := new(mockCertMonitor)
|
||||
certMon.Test(t)
|
||||
certMon.On("Update", &structs.SignedResponse{
|
||||
IssuedCert: structs.IssuedCert{
|
||||
SerialNumber: "1234",
|
||||
|
@ -465,10 +455,22 @@ func TestInitialConfiguration_success(t *testing.T) {
|
|||
VerifyServerHostname: true,
|
||||
}).Return(nil).Once()
|
||||
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC).
|
||||
WithCertMonitor(&certMon)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
if err := setPrimaryDatacenterFromSource(rtConfig, source); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{"127.0.0.1:8300"},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
CertMonitor: certMon,
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -479,6 +481,7 @@ func TestInitialConfiguration_success(t *testing.T) {
|
|||
require.Equal(t, "primary", cfg.PrimaryDatacenter)
|
||||
|
||||
// the file was written to.
|
||||
persistedFile := filepath.Join(rtConfig.DataDir, autoConfigFileName)
|
||||
require.FileExists(t, persistedFile)
|
||||
|
||||
// ensure no RPC was made
|
||||
|
@ -487,17 +490,10 @@ func TestInitialConfiguration_success(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestInitialConfiguration_retries(t *testing.T) {
|
||||
dataDir, configDir, builderOpts := testSetupAutoConf(t)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["198.18.0.1", "198.18.0.2:8398", "198.18.0.3:8399", "127.0.0.1:1234"]}, "verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
|
||||
persistedFile := filepath.Join(dataDir, autoConfigFileName)
|
||||
directRPC := mockDirectRPC{}
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
|
||||
populateResponse := func(val interface{}) {
|
||||
resp, ok := val.(*pbautoconf.AutoConfigResponse)
|
||||
|
@ -557,11 +553,27 @@ func TestInitialConfiguration_retries(t *testing.T) {
|
|||
&expectedRequest,
|
||||
&pbautoconf.AutoConfigResponse{}).Return(populateResponse)
|
||||
|
||||
waiter := lib.NewRetryWaiter(2, 0, 1*time.Millisecond, nil)
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC).
|
||||
WithRetryWaiter(waiter)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
if err := setPrimaryDatacenterFromSource(rtConfig, source); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{
|
||||
"198.18.0.1:8300",
|
||||
"198.18.0.2:8398",
|
||||
"198.18.0.3:8399",
|
||||
"127.0.0.1:1234",
|
||||
},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
Waiter: lib.NewRetryWaiter(2, 0, 1*time.Millisecond, nil),
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -572,6 +584,7 @@ func TestInitialConfiguration_retries(t *testing.T) {
|
|||
require.Equal(t, "primary", cfg.PrimaryDatacenter)
|
||||
|
||||
// the file was written to.
|
||||
persistedFile := filepath.Join(rtConfig.DataDir, autoConfigFileName)
|
||||
require.FileExists(t, persistedFile)
|
||||
|
||||
// ensure no RPC was made
|
||||
|
@ -584,25 +597,34 @@ func TestAutoConfig_StartStop(t *testing.T) {
|
|||
// stopped and not that anything with regards to running the cert monitor
|
||||
// actually work. Those are tested in the cert-monitor package.
|
||||
|
||||
_, configDir, builderOpts := testSetupAutoConf(t)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["198.18.0.1", "198.18.0.2:8398", "198.18.0.3:8399", "127.0.0.1:1234"]}, "verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
directRPC := &mockDirectRPC{}
|
||||
directRPC.Test(t)
|
||||
certMon := &mockCertMonitor{}
|
||||
certMon.Test(t)
|
||||
|
||||
certMon.On("Start").Return((<-chan struct{})(nil), nil).Once()
|
||||
certMon.On("Stop").Return(true).Once()
|
||||
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(directRPC).
|
||||
WithCertMonitor(certMon)
|
||||
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{
|
||||
"198.18.0.1",
|
||||
"198.18.0.2:8398",
|
||||
"198.18.0.3:8399",
|
||||
"127.0.0.1:1234",
|
||||
},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
CertMonitor: certMon,
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
@ -618,16 +640,10 @@ func TestAutoConfig_StartStop(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFallBackTLS(t *testing.T) {
|
||||
_, configDir, builderOpts := testSetupAutoConf(t)
|
||||
rtConfig := setupRuntimeConfig(t)
|
||||
|
||||
cfgFile := filepath.Join(configDir, "test.json")
|
||||
require.NoError(t, ioutil.WriteFile(cfgFile, []byte(`{
|
||||
"auto_config": {"enabled": true, "intro_token": "blarg", "server_addresses": ["127.0.0.1:8300"]}, "verify_outgoing": true
|
||||
}`), 0600))
|
||||
|
||||
builderOpts.ConfigFiles = append(builderOpts.ConfigFiles, cfgFile)
|
||||
|
||||
directRPC := mockDirectRPC{}
|
||||
directRPC := new(mockDirectRPC)
|
||||
directRPC.Test(t)
|
||||
|
||||
populateResponse := func(val interface{}) {
|
||||
resp, ok := val.(*pbautoconf.AutoConfigResponse)
|
||||
|
@ -684,12 +700,21 @@ func TestFallBackTLS(t *testing.T) {
|
|||
// setup the mock certificate monitor we don't expect it to be used
|
||||
// as the FallbackTLS method is mainly used by the certificate monitor
|
||||
// if for some reason it fails to renew the TLS certificate in time.
|
||||
certMon := mockCertMonitor{}
|
||||
certMon := new(mockCertMonitor)
|
||||
|
||||
conf := new(Config).
|
||||
WithBuilderOpts(builderOpts).
|
||||
WithDirectRPC(&directRPC).
|
||||
WithCertMonitor(&certMon)
|
||||
conf := Config{
|
||||
DirectRPC: directRPC,
|
||||
Loader: func(source config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
rtConfig.AutoConfig = config.AutoConfig{
|
||||
Enabled: true,
|
||||
IntroToken: "blarg",
|
||||
ServerAddresses: []string{"127.0.0.1:8300"},
|
||||
}
|
||||
rtConfig.VerifyOutgoing = true
|
||||
return rtConfig, nil, nil
|
||||
},
|
||||
CertMonitor: certMon,
|
||||
}
|
||||
ac, err := New(conf)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, ac)
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package autoconf
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/consul/agent/config"
|
||||
)
|
||||
|
||||
// LoadConfig will build the configuration including the extraHead source injected
|
||||
// after all other defaults but before any user supplied configuration and the overrides
|
||||
// source injected as the final source in the configuration parsing chain.
|
||||
func LoadConfig(builderOpts config.BuilderOpts, extraHead config.Source, overrides ...config.Source) (*config.RuntimeConfig, []string, error) {
|
||||
b, err := config.NewBuilder(builderOpts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if extraHead != nil {
|
||||
b.Head = append(b.Head, extraHead)
|
||||
}
|
||||
|
||||
if len(overrides) != 0 {
|
||||
b.Tail = append(b.Tail, overrides...)
|
||||
}
|
||||
|
||||
cfg, err := b.BuildAndValidate()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &cfg, b.Warnings, nil
|
||||
}
|
|
@ -37,17 +37,6 @@ type Config struct {
|
|||
// configuration. Setting this field is required.
|
||||
DirectRPC DirectRPC
|
||||
|
||||
// BuilderOpts are any configuration building options that should be
|
||||
// used when loading the Consul configuration. This is mostly a pass
|
||||
// through from what the CLI will generate. While this option is
|
||||
// not strictly required, not setting it will prevent AutoConfig
|
||||
// from doing anything useful. Enabling AutoConfig requires a
|
||||
// CLI flag or a config file (also specified by the CLI) flag.
|
||||
// So without providing the opts its equivalent to using the
|
||||
// configuration of not specifying anything to the consul agent
|
||||
// cli.
|
||||
BuilderOpts config.BuilderOpts
|
||||
|
||||
// Waiter is a RetryWaiter to be used during retrieval of the
|
||||
// initial configuration. When a round of requests fails we will
|
||||
// wait and eventually make another round of requests (1 round
|
||||
|
@ -60,56 +49,14 @@ type Config struct {
|
|||
// having the test take minutes/hours to complete.
|
||||
Waiter *lib.RetryWaiter
|
||||
|
||||
// Overrides are a list of configuration sources to append to the tail of
|
||||
// the config builder. This field is optional and mainly useful for testing
|
||||
// to override values that would be otherwise not user-settable.
|
||||
Overrides []config.Source
|
||||
|
||||
// CertMonitor is the Connect TLS Certificate Monitor to be used for ongoing
|
||||
// certificate renewals and connect CA roots updates. This field is not
|
||||
// strictly required but if not provided the TLS certificates retrieved
|
||||
// through by the AutoConfig.InitialConfiguration RPC will not be used
|
||||
// or renewed.
|
||||
CertMonitor CertMonitor
|
||||
}
|
||||
|
||||
// WithLogger will cause the created AutoConfig type to use the provided logger
|
||||
func (c *Config) WithLogger(logger hclog.Logger) *Config {
|
||||
c.Logger = logger
|
||||
return c
|
||||
}
|
||||
|
||||
// WithConnectionPool will cause the created AutoConfig type to use the provided connection pool
|
||||
func (c *Config) WithDirectRPC(directRPC DirectRPC) *Config {
|
||||
c.DirectRPC = directRPC
|
||||
return c
|
||||
}
|
||||
|
||||
// WithBuilderOpts will cause the created AutoConfig type to use the provided CLI builderOpts
|
||||
func (c *Config) WithBuilderOpts(builderOpts config.BuilderOpts) *Config {
|
||||
c.BuilderOpts = builderOpts
|
||||
return c
|
||||
}
|
||||
|
||||
// WithRetryWaiter will cause the created AutoConfig type to use the provided retry waiter
|
||||
func (c *Config) WithRetryWaiter(waiter *lib.RetryWaiter) *Config {
|
||||
c.Waiter = waiter
|
||||
return c
|
||||
}
|
||||
|
||||
// WithOverrides is used to provide a config source to append to the tail sources
|
||||
// during config building. It is really only useful for testing to tune non-user
|
||||
// configurable tunables to make various tests converge more quickly than they
|
||||
// could otherwise.
|
||||
func (c *Config) WithOverrides(overrides ...config.Source) *Config {
|
||||
c.Overrides = overrides
|
||||
return c
|
||||
}
|
||||
|
||||
// WithCertMonitor is used to provide a certificate monitor to the auto-config.
|
||||
// This monitor is responsible for renewing the agents TLS certificate and keeping
|
||||
// the connect CA roots up to date.
|
||||
func (c *Config) WithCertMonitor(certMonitor CertMonitor) *Config {
|
||||
c.CertMonitor = certMonitor
|
||||
return c
|
||||
// Loader merges source with the existing FileSources and returns the complete
|
||||
// RuntimeConfig.
|
||||
Loader func(source config.Source) (cfg *config.RuntimeConfig, warnings []string, err error)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,31 @@ import (
|
|||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
// LoadConfig will build the configuration including the extraHead source injected
|
||||
// after all other defaults but before any user supplied configuration and the overrides
|
||||
// source injected as the final source in the configuration parsing chain.
|
||||
func Load(builderOpts BuilderOpts, extraHead Source, overrides ...Source) (*RuntimeConfig, []string, error) {
|
||||
b, err := NewBuilder(builderOpts)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if extraHead != nil {
|
||||
b.Head = append(b.Head, extraHead)
|
||||
}
|
||||
|
||||
if len(overrides) != 0 {
|
||||
b.Tail = append(b.Tail, overrides...)
|
||||
}
|
||||
|
||||
cfg, err := b.BuildAndValidate()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return &cfg, b.Warnings, nil
|
||||
}
|
||||
|
||||
// Builder constructs a valid runtime configuration from multiple
|
||||
// configuration sources.
|
||||
//
|
||||
|
|
|
@ -6,10 +6,39 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestLoad(t *testing.T) {
|
||||
// Basically just testing that injection of the extra
|
||||
// source works.
|
||||
devMode := true
|
||||
builderOpts := BuilderOpts{
|
||||
// putting this in dev mode so that the config validates
|
||||
// without having to specify a data directory
|
||||
DevMode: &devMode,
|
||||
}
|
||||
|
||||
cfg, warnings, err := Load(builderOpts, FileSource{
|
||||
Name: "test",
|
||||
Format: "hcl",
|
||||
Data: `node_name = "hobbiton"`,
|
||||
},
|
||||
FileSource{
|
||||
Name: "overrides",
|
||||
Format: "json",
|
||||
Data: `{"check_reap_interval": "1ms"}`,
|
||||
})
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, warnings)
|
||||
require.NotNil(t, cfg)
|
||||
require.Equal(t, "hobbiton", cfg.NodeName)
|
||||
require.Equal(t, 1*time.Millisecond, cfg.CheckReapInterval)
|
||||
}
|
||||
|
||||
func TestShouldParseFile(t *testing.T) {
|
||||
var testcases = []struct {
|
||||
filename string
|
||||
|
|
Loading…
Reference in New Issue