diff --git a/pkg/config/config.go b/pkg/config/config.go index 5fe8e165..5bd00ff3 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -19,7 +19,7 @@ const ( type StaticConfig struct { DeniedResources []GroupVersionKind `toml:"denied_resources"` - LogLevel int `toml:"log_level,omitempty"` + LogLevel int `toml:"log_level,omitzero"` Port string `toml:"port,omitempty"` SSEBaseURL string `toml:"sse_base_url,omitempty"` KubeConfig string `toml:"kubeconfig,omitempty"` @@ -70,13 +70,6 @@ type StaticConfig struct { parsedClusterProviderConfigs map[string]ProviderConfig } -func Default() *StaticConfig { - return &StaticConfig{ - ListOutput: "table", - Toolsets: []string{"core", "config", "helm"}, - } -} - type GroupVersionKind struct { Group string `toml:"group"` Version string `toml:"version"` diff --git a/pkg/config/config_default.go b/pkg/config/config_default.go new file mode 100644 index 00000000..febea70c --- /dev/null +++ b/pkg/config/config_default.go @@ -0,0 +1,43 @@ +package config + +import ( + "bytes" + + "github.com/BurntSushi/toml" +) + +func Default() *StaticConfig { + defaultConfig := StaticConfig{ + ListOutput: "table", + Toolsets: []string{"core", "config", "helm"}, + } + overrides := defaultOverrides() + mergedConfig := mergeConfig(defaultConfig, overrides) + return &mergedConfig +} + +// HasDefaultOverrides indicates whether the internal defaultOverrides function +// provides any overrides or an empty StaticConfig. +func HasDefaultOverrides() bool { + overrides := defaultOverrides() + var buf bytes.Buffer + if err := toml.NewEncoder(&buf).Encode(overrides); err != nil { + // If marshaling fails, assume no overrides + return false + } + return len(bytes.TrimSpace(buf.Bytes())) > 0 +} + +// mergeConfig applies non-zero values from override to base using TOML serialization +// and returns the merged StaticConfig. +// In case of any error during marshalling or unmarshalling, it returns the base config unchanged. +func mergeConfig(base, override StaticConfig) StaticConfig { + var overrideBuffer bytes.Buffer + if err := toml.NewEncoder(&overrideBuffer).Encode(override); err != nil { + // If marshaling fails, return base unchanged + return base + } + + _, _ = toml.NewDecoder(&overrideBuffer).Decode(&base) + return base +} diff --git a/pkg/config/config_default_overrides.go b/pkg/config/config_default_overrides.go new file mode 100644 index 00000000..70d065bc --- /dev/null +++ b/pkg/config/config_default_overrides.go @@ -0,0 +1,8 @@ +package config + +func defaultOverrides() StaticConfig { + return StaticConfig{ + // IMPORTANT: this file is used to override default config values in downstream builds. + // This is intentionally left blank. + } +} diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index d0e87726..afdde191 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -174,6 +174,49 @@ func (s *ConfigSuite) TestReadConfigValidPreservesDefaultsForMissingFields() { }) } +func (s *ConfigSuite) TestMergeConfig() { + base := StaticConfig{ + ListOutput: "table", + Toolsets: []string{"core", "config", "helm"}, + Port: "8080", + } + s.Run("merges override values on top of base", func() { + override := StaticConfig{ + ListOutput: "json", + Port: "9090", + } + + result := mergeConfig(base, override) + + s.Equal("json", result.ListOutput, "ListOutput should be overridden") + s.Equal("9090", result.Port, "Port should be overridden") + }) + + s.Run("preserves base values when override is empty", func() { + override := StaticConfig{} + + result := mergeConfig(base, override) + + s.Equal("table", result.ListOutput, "ListOutput should be preserved from base") + s.Equal([]string{"core", "config", "helm"}, result.Toolsets, "Toolsets should be preserved from base") + s.Equal("8080", result.Port, "Port should be preserved from base") + }) + + s.Run("handles partial overrides", func() { + override := StaticConfig{ + Toolsets: []string{"custom"}, + ReadOnly: true, + } + + result := mergeConfig(base, override) + + s.Equal("table", result.ListOutput, "ListOutput should be preserved from base") + s.Equal([]string{"custom"}, result.Toolsets, "Toolsets should be overridden") + s.Equal("8080", result.Port, "Port should be preserved from base since override doesn't specify it") + s.True(result.ReadOnly, "ReadOnly should be overridden to true") + }) +} + func TestConfig(t *testing.T) { suite.Run(t, new(ConfigSuite)) } diff --git a/pkg/mcp/toolsets_test.go b/pkg/mcp/toolsets_test.go index 527b1e22..d81392a5 100644 --- a/pkg/mcp/toolsets_test.go +++ b/pkg/mcp/toolsets_test.go @@ -65,6 +65,9 @@ func (s *ToolsetsSuite) TestNoToolsets() { } func (s *ToolsetsSuite) TestDefaultToolsetsTools() { + if configuration.HasDefaultOverrides() { + s.T().Skip("Skipping test because default configuration overrides are present (this is a downstream fork)") + } s.Run("Default configuration toolsets", func() { s.InitMcpClient() tools, err := s.ListTools(s.T().Context(), mcp.ListToolsRequest{}) @@ -82,6 +85,9 @@ func (s *ToolsetsSuite) TestDefaultToolsetsTools() { } func (s *ToolsetsSuite) TestDefaultToolsetsToolsInOpenShift() { + if configuration.HasDefaultOverrides() { + s.T().Skip("Skipping test because default configuration overrides are present (this is a downstream fork)") + } s.Run("Default configuration toolsets in OpenShift", func() { s.Handle(&test.InOpenShiftHandler{}) s.InitMcpClient() @@ -100,6 +106,9 @@ func (s *ToolsetsSuite) TestDefaultToolsetsToolsInOpenShift() { } func (s *ToolsetsSuite) TestDefaultToolsetsToolsInMultiCluster() { + if configuration.HasDefaultOverrides() { + s.T().Skip("Skipping test because default configuration overrides are present (this is a downstream fork)") + } s.Run("Default configuration toolsets in multi-cluster (with 11 clusters)", func() { kubeconfig := s.Kubeconfig() for i := 0; i < 10; i++ { @@ -123,6 +132,9 @@ func (s *ToolsetsSuite) TestDefaultToolsetsToolsInMultiCluster() { } func (s *ToolsetsSuite) TestDefaultToolsetsToolsInMultiClusterEnum() { + if configuration.HasDefaultOverrides() { + s.T().Skip("Skipping test because default configuration overrides are present (this is a downstream fork)") + } s.Run("Default configuration toolsets in multi-cluster (with 2 clusters)", func() { kubeconfig := s.Kubeconfig() // Add additional cluster to force multi-cluster behavior with enum parameter