From 8255cc97f5894bd8ff16e2c23efeb3b78f367ec6 Mon Sep 17 00:00:00 2001 From: Poonam Jadhav Date: Tue, 11 Apr 2023 15:04:02 -0400 Subject: [PATCH] feat: add reporting config with reload (#16890) --- agent/agent.go | 8 ++ agent/agent_oss_test.go | 46 +++++++++++ agent/config/builder_oss.go | 4 + agent/config/builder_oss_test.go | 13 +++ agent/config/config.go | 11 +++ agent/config/runtime.go | 10 +++ agent/config/runtime_oss_test.go | 81 +++++++++++++++++++ .../TestRuntimeConfig_Sanitize.golden | 5 ++ agent/config/testdata/full-config.hcl | 5 ++ agent/config/testdata/full-config.json | 5 ++ agent/consul/config.go | 11 +++ agent/consul/server.go | 2 + agent/consul/server_oss.go | 4 + agent/consul/server_oss_test.go | 43 ++++++++++ 14 files changed, 248 insertions(+) create mode 100644 agent/agent_oss_test.go create mode 100644 agent/consul/server_oss_test.go diff --git a/agent/agent.go b/agent/agent.go index d796315285..554facb3a2 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1499,7 +1499,10 @@ func newConsulConfig(runtimeCfg *config.RuntimeConfig, logger hclog.Logger) (*co cfg.RequestLimitsWriteRate = runtimeCfg.RequestLimitsWriteRate cfg.Locality = runtimeCfg.StructLocality() + cfg.Reporting.License.Enabled = runtimeCfg.Reporting.License.Enabled + enterpriseConsulConfig(cfg, runtimeCfg) + return cfg, nil } @@ -4187,6 +4190,11 @@ func (a *Agent) reloadConfigInternal(newCfg *config.RuntimeConfig) error { HeartbeatTimeout: newCfg.ConsulRaftHeartbeatTimeout, ElectionTimeout: newCfg.ConsulRaftElectionTimeout, RaftTrailingLogs: newCfg.RaftTrailingLogs, + Reporting: consul.Reporting{ + License: consul.License{ + Enabled: newCfg.Reporting.License.Enabled, + }, + }, } if err := a.delegate.ReloadConfig(cc); err != nil { return err diff --git a/agent/agent_oss_test.go b/agent/agent_oss_test.go new file mode 100644 index 0000000000..ceb90beb06 --- /dev/null +++ b/agent/agent_oss_test.go @@ -0,0 +1,46 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +//go:build !consulent +// +build !consulent + +package agent + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestAgent_consulConfig_Reporting(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + hcl := ` + reporting { + license { + enabled = true + } + } + ` + a := NewTestAgent(t, hcl) + defer a.Shutdown() + require.Equal(t, false, a.consulConfig().Reporting.License.Enabled) +} + +func TestAgent_consulConfig_Reporting_Default(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + + t.Parallel() + hcl := ` + reporting { + } + ` + a := NewTestAgent(t, hcl) + defer a.Shutdown() + require.Equal(t, false, a.consulConfig().Reporting.License.Enabled) +} diff --git a/agent/config/builder_oss.go b/agent/config/builder_oss.go index f8d56df082..fa715a38a3 100644 --- a/agent/config/builder_oss.go +++ b/agent/config/builder_oss.go @@ -60,6 +60,10 @@ func validateEnterpriseConfigKeys(config *Config) []error { add("license_path") config.LicensePath = nil } + if config.Reporting.License.Enabled != nil { + add("reporting.license.enabled") + config.Reporting.License.Enabled = nil + } return result } diff --git a/agent/config/builder_oss_test.go b/agent/config/builder_oss_test.go index 15d706a120..100f905859 100644 --- a/agent/config/builder_oss_test.go +++ b/agent/config/builder_oss_test.go @@ -110,6 +110,19 @@ func TestValidateEnterpriseConfigKeys(t *testing.T) { require.Empty(t, c.LicensePath) }, }, + "reporting.license.enabled": { + config: Config{ + Reporting: Reporting{ + License: License{ + Enabled: &boolVal, + }, + }, + }, + badKeys: []string{"reporting.license.enabled"}, + check: func(t *testing.T, c *Config) { + require.Nil(t, c.Reporting.License.Enabled) + }, + }, "multi": { config: Config{ ReadReplica: &boolVal, diff --git a/agent/config/config.go b/agent/config/config.go index 95d1b36777..4d41815550 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -295,6 +295,9 @@ type Config struct { LicensePollMaxTime *string `mapstructure:"license_poll_max_time" json:"-"` LicenseUpdateBaseTime *string `mapstructure:"license_update_base_time" json:"-"` LicenseUpdateMaxTime *string `mapstructure:"license_update_max_time" json:"-"` + + // license reporting + Reporting Reporting `mapstructure:"reporting" json:"-"` } type GossipLANConfig struct { @@ -956,3 +959,11 @@ type RaftBoltDBConfigRaw struct { type RaftWALConfigRaw struct { SegmentSizeMB *int `mapstructure:"segment_size_mb" json:"segment_size_mb,omitempty"` } + +type License struct { + Enabled *bool `mapstructure:"enabled"` +} + +type Reporting struct { + License License `mapstructure:"license"` +} diff --git a/agent/config/runtime.go b/agent/config/runtime.go index 9abcb612f5..790eec425a 100644 --- a/agent/config/runtime.go +++ b/agent/config/runtime.go @@ -1484,9 +1484,19 @@ type RuntimeConfig struct { // here so that tests can use a smaller value. LocalProxyConfigResyncInterval time.Duration + Reporting ReportingConfig + EnterpriseRuntimeConfig } +type LicenseConfig struct { + Enabled bool +} + +type ReportingConfig struct { + License LicenseConfig +} + type AutoConfig struct { Enabled bool IntroToken string diff --git a/agent/config/runtime_oss_test.go b/agent/config/runtime_oss_test.go index 39c1ba38c2..06801ac835 100644 --- a/agent/config/runtime_oss_test.go +++ b/agent/config/runtime_oss_test.go @@ -13,6 +13,7 @@ import ( "testing" "github.com/hashicorp/consul/sdk/testutil" + "github.com/stretchr/testify/require" ) var testRuntimeConfigSanitizeExpectedFilename = "TestRuntimeConfig_Sanitize.golden" @@ -31,6 +32,7 @@ var enterpriseConfigKeyWarnings = []string{ enterpriseConfigKeyError{key: "acl.msp_disable_bootstrap"}.Error(), enterpriseConfigKeyError{key: "acl.tokens.managed_service_provider"}.Error(), enterpriseConfigKeyError{key: "audit"}.Error(), + enterpriseConfigKeyError{key: "reporting.license.enabled"}.Error(), } // OSS-only equivalent of TestConfigFlagsAndEdgecases @@ -87,3 +89,82 @@ func TestLoad_IntegrationWithFlags_OSS(t *testing.T) { } } } + +func TestLoad_ReportingConfig(t *testing.T) { + dir := testutil.TempDir(t, t.Name()) + + t.Run("load from JSON defaults to false", func(t *testing.T) { + content := `{ + "reporting": {} + }` + + opts := LoadOpts{ + FlagValues: FlagValuesTarget{Config: Config{ + DataDir: &dir, + }}, + Overrides: []Source{ + FileSource{ + Name: "reporting.json", + Format: "json", + Data: content, + }, + }, + } + patchLoadOptsShims(&opts) + result, err := Load(opts) + require.NoError(t, err) + require.Len(t, result.Warnings, 0) + require.Equal(t, false, result.RuntimeConfig.Reporting.License.Enabled) + }) + + t.Run("load from HCL defaults to false", func(t *testing.T) { + content := ` + reporting {} + ` + + opts := LoadOpts{ + FlagValues: FlagValuesTarget{Config: Config{ + DataDir: &dir, + }}, + Overrides: []Source{ + FileSource{ + Name: "reporting.hcl", + Format: "hcl", + Data: content, + }, + }, + } + patchLoadOptsShims(&opts) + result, err := Load(opts) + require.NoError(t, err) + require.Len(t, result.Warnings, 0) + require.Equal(t, false, result.RuntimeConfig.Reporting.License.Enabled) + }) + + t.Run("with value set returns warning and defaults to false", func(t *testing.T) { + content := `reporting { + license { + enabled = true + } + }` + + opts := LoadOpts{ + FlagValues: FlagValuesTarget{Config: Config{ + DataDir: &dir, + }}, + Overrides: []Source{ + FileSource{ + Name: "reporting.hcl", + Format: "hcl", + Data: content, + }, + }, + } + patchLoadOptsShims(&opts) + result, err := Load(opts) + require.NoError(t, err) + require.Len(t, result.Warnings, 1) + require.Contains(t, result.Warnings[0], "\"reporting.license.enabled\" is a Consul Enterprise configuration and will have no effect") + require.Equal(t, false, result.RuntimeConfig.Reporting.License.Enabled) + }) +} diff --git a/agent/config/testdata/TestRuntimeConfig_Sanitize.golden b/agent/config/testdata/TestRuntimeConfig_Sanitize.golden index 48d760c706..37c3e692fe 100644 --- a/agent/config/testdata/TestRuntimeConfig_Sanitize.golden +++ b/agent/config/testdata/TestRuntimeConfig_Sanitize.golden @@ -295,6 +295,11 @@ "ReconnectTimeoutLAN": "0s", "ReconnectTimeoutWAN": "0s", "RejoinAfterLeave": false, + "Reporting": { + "License": { + "Enabled": false + } + }, "RequestLimitsMode": 0, "RequestLimitsReadRate": 0, "RequestLimitsWriteRate": 0, diff --git a/agent/config/testdata/full-config.hcl b/agent/config/testdata/full-config.hcl index 6d46c4fd6e..718c879b29 100644 --- a/agent/config/testdata/full-config.hcl +++ b/agent/config/testdata/full-config.hcl @@ -375,6 +375,11 @@ reconnect_timeout = "23739s" reconnect_timeout_wan = "26694s" recursors = [ "63.38.39.58", "92.49.18.18" ] rejoin_after_leave = true +reporting = { + license = { + enabled = false + } +} retry_interval = "8067s" retry_interval_wan = "28866s" retry_join = [ "pbsSFY7U", "l0qLtWij" ] diff --git a/agent/config/testdata/full-config.json b/agent/config/testdata/full-config.json index 7c36981561..f98bfe4dab 100644 --- a/agent/config/testdata/full-config.json +++ b/agent/config/testdata/full-config.json @@ -428,6 +428,11 @@ "92.49.18.18" ], "rejoin_after_leave": true, + "reporting": { + "license": { + "enabled": false + } + }, "retry_interval": "8067s", "retry_interval_wan": "28866s", "retry_join": [ diff --git a/agent/consul/config.go b/agent/consul/config.go index 6fa6375046..b6f0dae873 100644 --- a/agent/consul/config.go +++ b/agent/consul/config.go @@ -441,6 +441,8 @@ type Config struct { Locality *structs.Locality + Reporting Reporting + // Embedded Consul Enterprise specific configuration *EnterpriseConfig } @@ -676,6 +678,7 @@ type ReloadableConfig struct { RaftTrailingLogs int HeartbeatTimeout time.Duration ElectionTimeout time.Duration + Reporting Reporting } type RaftLogStoreConfig struct { @@ -698,3 +701,11 @@ type RaftBoltDBConfig struct { type WALConfig struct { SegmentSize int } + +type License struct { + Enabled bool +} + +type Reporting struct { + License License +} diff --git a/agent/consul/server.go b/agent/consul/server.go index 19f9738336..5347b0a788 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -1803,6 +1803,8 @@ func (s *Server) ReloadConfig(config ReloadableConfig) error { return err } + s.updateReportingConfig(config) + s.rpcLimiter.Store(rate.NewLimiter(config.RPCRateLimit, config.RPCMaxBurst)) if config.RequestLimits != nil { diff --git a/agent/consul/server_oss.go b/agent/consul/server_oss.go index 868dd6ec96..44ffebf5dc 100644 --- a/agent/consul/server_oss.go +++ b/agent/consul/server_oss.go @@ -182,3 +182,7 @@ func addSerfMetricsLabels(conf *serf.Config, wan bool, segment string, partition conf.MetricLabels = append(conf.MetricLabels, networkMetric) } + +func (s *Server) updateReportingConfig(config ReloadableConfig) { + // no-op +} diff --git a/agent/consul/server_oss_test.go b/agent/consul/server_oss_test.go new file mode 100644 index 0000000000..d5a2aff082 --- /dev/null +++ b/agent/consul/server_oss_test.go @@ -0,0 +1,43 @@ +//go:build !consulent +// +build !consulent + +package consul + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/hashicorp/consul/testrpc" +) + +func TestAgent_ReloadConfig_Reporting(t *testing.T) { + if testing.Short() { + t.Skip("too slow for testing.Short") + } + t.Parallel() + + dir1, s := testServerWithConfig(t, func(c *Config) { + c.Reporting.License.Enabled = false + }) + defer os.RemoveAll(dir1) + defer s.Shutdown() + + testrpc.WaitForTestAgent(t, s.RPC, "dc1") + + require.Equal(t, false, s.config.Reporting.License.Enabled) + + rc := ReloadableConfig{ + Reporting: Reporting{ + License: License{ + Enabled: true, + }, + }, + } + + require.NoError(t, s.ReloadConfig(rc)) + + // Check config reload is no-op + require.Equal(t, false, s.config.Reporting.License.Enabled) +}