diff --git a/agent/agent.go b/agent/agent.go index 64df2cb8cb0cc..5718068b25da4 100644 --- a/agent/agent.go +++ b/agent/agent.go @@ -1360,7 +1360,10 @@ func newConsulConfig(runtimeCfg *config.RuntimeConfig, logger hclog.Logger) (*co cfg.PeeringEnabled = runtimeCfg.PeeringEnabled cfg.PeeringTestAllowPeerRegistrations = runtimeCfg.PeeringTestAllowPeerRegistrations + cfg.Reporting.License.Enabled = runtimeCfg.Reporting.License.Enabled + enterpriseConsulConfig(cfg, runtimeCfg) + return cfg, nil } @@ -4007,6 +4010,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 0000000000000..ceb90beb0634c --- /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 ce6e8d44ce0b1..f0fcc30ae4eae 100644 --- a/agent/config/builder_oss.go +++ b/agent/config/builder_oss.go @@ -57,6 +57,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 2fd5f50ad7c37..1fdba09e6028b 100644 --- a/agent/config/builder_oss_test.go +++ b/agent/config/builder_oss_test.go @@ -107,6 +107,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 11e09b355b4b6..faf4bcf4cd9ac 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -298,6 +298,9 @@ type Config struct { LicensePollMaxTime *string `mapstructure:"license_poll_max_time"` LicenseUpdateBaseTime *string `mapstructure:"license_update_base_time"` LicenseUpdateMaxTime *string `mapstructure:"license_update_max_time"` + + // license reporting + Reporting Reporting `mapstructure:"reporting"` } type GossipLANConfig struct { @@ -924,3 +927,11 @@ type Peering struct { // This always gets overridden in NonUserSource() TestAllowPeerRegistrations *bool `mapstructure:"test_allow_peer_registrations"` } + +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 d86952c543834..a982f41325b15 100644 --- a/agent/config/runtime.go +++ b/agent/config/runtime.go @@ -1429,9 +1429,19 @@ type RuntimeConfig struct { // AutoReloadConfigCoalesceInterval Coalesce Interval for auto reload config AutoReloadConfigCoalesceInterval 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 2179ac2df47b1..ded981601f434 100644 --- a/agent/config/runtime_oss_test.go +++ b/agent/config/runtime_oss_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/hashicorp/consul/sdk/testutil" + "github.com/stretchr/testify/require" ) var testRuntimeConfigSanitizeExpectedFilename = "TestRuntimeConfig_Sanitize.golden" @@ -27,6 +28,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 @@ -80,3 +82,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: 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: 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: 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 17c7bf18913b0..cda2963cc875a 100644 --- a/agent/config/testdata/TestRuntimeConfig_Sanitize.golden +++ b/agent/config/testdata/TestRuntimeConfig_Sanitize.golden @@ -266,6 +266,11 @@ "ReconnectTimeoutLAN": "0s", "ReconnectTimeoutWAN": "0s", "RejoinAfterLeave": false, + "Reporting": { + "License": { + "Enabled": false + } + }, "RetryJoinIntervalLAN": "0s", "RetryJoinIntervalWAN": "0s", "RetryJoinLAN": [ diff --git a/agent/config/testdata/full-config.hcl b/agent/config/testdata/full-config.hcl index f52450eeaf1ba..444fb6d50a922 100644 --- a/agent/config/testdata/full-config.hcl +++ b/agent/config/testdata/full-config.hcl @@ -344,6 +344,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 5837e25e75c85..aaf2cad0d1038 100644 --- a/agent/config/testdata/full-config.json +++ b/agent/config/testdata/full-config.json @@ -342,6 +342,11 @@ "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/consul/config.go b/agent/consul/config.go index 9cb722f8c2079..c33ea5fb0e7f8 100644 --- a/agent/consul/config.go +++ b/agent/consul/config.go @@ -408,6 +408,8 @@ type Config struct { PeeringTestAllowPeerRegistrations bool + Reporting Reporting + // Embedded Consul Enterprise specific configuration *EnterpriseConfig } @@ -630,8 +632,17 @@ type ReloadableConfig struct { RaftTrailingLogs int HeartbeatTimeout time.Duration ElectionTimeout time.Duration + Reporting Reporting } type RaftBoltDBConfig struct { NoFreelistSync bool } + +type License struct { + Enabled bool +} + +type Reporting struct { + License License +} diff --git a/agent/consul/server.go b/agent/consul/server.go index a51983ec6928c..7cb5b20124d23 100644 --- a/agent/consul/server.go +++ b/agent/consul/server.go @@ -1618,6 +1618,8 @@ func (s *Server) ReloadConfig(config ReloadableConfig) error { return err } + s.updateReportingConfig(config) + s.rpcLimiter.Store(rate.NewLimiter(config.RPCRateLimit, config.RPCMaxBurst)) s.rpcConnLimiter.SetConfig(connlimit.Config{ MaxConnsPerClientIP: config.RPCMaxConnsPerClient, diff --git a/agent/consul/server_oss.go b/agent/consul/server_oss.go index 4ae524b65c04e..67731e450195e 100644 --- a/agent/consul/server_oss.go +++ b/agent/consul/server_oss.go @@ -174,3 +174,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 0000000000000..d5a2aff082086 --- /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) +}