Skip to content

Commit 05fd6db

Browse files
joelanfordclaude
andcommitted
🌱 Return *DeploymentConfig directly from GetDeploymentConfig()
Change GetDeploymentConfig() to return (*DeploymentConfig, error) instead of map[string]any, eliminating the intermediate convertToDeploymentConfig() function in provider.go. The caller was immediately converting the map to a DeploymentConfig anyway, so this simplifies the API and removes unnecessary indirection. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 71a91dc commit 05fd6db

3 files changed

Lines changed: 23 additions & 109 deletions

File tree

internal/operator-controller/applier/provider.go

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -112,13 +112,13 @@ func (r *RegistryV1ManifestProvider) extractBundleConfigOptions(rv1 *bundle.Regi
112112
opts = append(opts, render.WithTargetNamespaces(*watchNS))
113113
}
114114

115-
// Extract and convert deploymentConfig if present and the feature gate is enabled.
115+
// Extract deploymentConfig if present and the feature gate is enabled.
116116
if r.IsDeploymentConfigEnabled {
117-
if deploymentConfigMap := bundleConfig.GetDeploymentConfig(); deploymentConfigMap != nil {
118-
deploymentConfig, err := convertToDeploymentConfig(deploymentConfigMap)
119-
if err != nil {
120-
return nil, errorutil.NewTerminalError(ocv1.ReasonInvalidConfiguration, fmt.Errorf("invalid deploymentConfig: %w", err))
121-
}
117+
deploymentConfig, err := bundleConfig.GetDeploymentConfig()
118+
if err != nil {
119+
return nil, errorutil.NewTerminalError(ocv1.ReasonInvalidConfiguration, fmt.Errorf("invalid deploymentConfig: %w", err))
120+
}
121+
if deploymentConfig != nil {
122122
opts = append(opts, render.WithDeploymentConfig(deploymentConfig))
123123
}
124124
}
@@ -187,29 +187,6 @@ func extensionConfigBytes(ext *ocv1.ClusterExtension) []byte {
187187
return nil
188188
}
189189

190-
// convertToDeploymentConfig converts a map[string]any (from validated bundle config)
191-
// to a *config.DeploymentConfig struct that can be passed to the renderer.
192-
// Returns nil if the map is empty.
193-
func convertToDeploymentConfig(deploymentConfigMap map[string]any) (*config.DeploymentConfig, error) {
194-
if len(deploymentConfigMap) == 0 {
195-
return nil, nil
196-
}
197-
198-
// Marshal the map to JSON
199-
data, err := json.Marshal(deploymentConfigMap)
200-
if err != nil {
201-
return nil, fmt.Errorf("failed to marshal deploymentConfig: %w", err)
202-
}
203-
204-
// Unmarshal into the DeploymentConfig struct
205-
var deploymentConfig config.DeploymentConfig
206-
if err := json.Unmarshal(data, &deploymentConfig); err != nil {
207-
return nil, fmt.Errorf("failed to unmarshal deploymentConfig: %w", err)
208-
}
209-
210-
return &deploymentConfig, nil
211-
}
212-
213190
func getBundleAnnotations(bundleFS fs.FS) (map[string]string, error) {
214191
// The need to get the underlying bundle in order to extract its annotations
215192
// will go away once we have a bundle interface that can surface the annotations independently of the

internal/operator-controller/config/config.go

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -107,43 +107,33 @@ func (c *Config) GetWatchNamespace() *string {
107107

108108
// GetDeploymentConfig returns the deploymentConfig value if present in the configuration.
109109
// Returns nil if deploymentConfig is not set or is explicitly set to null.
110-
// The returned value is a generic map[string]any that can be marshaled to JSON
111-
// for validation or conversion to specific types (like v1alpha1.SubscriptionConfig).
112-
//
113-
// Returns a defensive deep copy so callers can't mutate the internal Config state.
114-
func (c *Config) GetDeploymentConfig() map[string]any {
110+
func (c *Config) GetDeploymentConfig() (*DeploymentConfig, error) {
115111
if c == nil || *c == nil {
116-
return nil
112+
return nil, nil
117113
}
118114
val, exists := (*c)["deploymentConfig"]
119115
if !exists {
120-
return nil
116+
return nil, nil
121117
}
122118
// User set deploymentConfig: null - treat as "not configured"
123119
if val == nil {
124-
return nil
120+
return nil, nil
125121
}
126122
// Schema validation ensures this is an object (map)
127123
dcMap, ok := val.(map[string]any)
128124
if !ok {
129-
return nil
125+
return nil, nil
130126
}
131127

132-
// Return a defensive deep copy so callers can't mutate the internal Config state.
133-
// We use JSON marshal/unmarshal because the data is already JSON-compatible and
134-
// this handles nested structures correctly.
135128
data, err := json.Marshal(dcMap)
136129
if err != nil {
137-
// This should never happen since the map came from validated JSON/YAML,
138-
// but return nil as a safe fallback
139-
return nil
130+
return nil, fmt.Errorf("failed to marshal deploymentConfig: %w", err)
140131
}
141-
var copied map[string]any
142-
if err := json.Unmarshal(data, &copied); err != nil {
143-
// This should never happen for valid JSON
144-
return nil
132+
var dc DeploymentConfig
133+
if err := json.Unmarshal(data, &dc); err != nil {
134+
return nil, fmt.Errorf("failed to unmarshal deploymentConfig: %w", err)
145135
}
146-
return copied
136+
return &dc, nil
147137
}
148138

149139
// UnmarshalConfig takes user configuration, validates it, and creates a Config object.

internal/operator-controller/config/config_test.go

Lines changed: 7 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ func Test_GetDeploymentConfig(t *testing.T) {
596596
tests := []struct {
597597
name string
598598
rawConfig []byte
599-
expectedDeploymentConfig map[string]any
599+
expectedDeploymentConfig *config.DeploymentConfig
600600
expectedDeploymentConfigNil bool
601601
}{
602602
{
@@ -620,23 +620,13 @@ func Test_GetDeploymentConfig(t *testing.T) {
620620
"deploymentConfig": {
621621
"nodeSelector": {
622622
"kubernetes.io/os": "linux"
623-
},
624-
"resources": {
625-
"requests": {
626-
"memory": "128Mi"
627-
}
628623
}
629624
}
630625
}`),
631-
expectedDeploymentConfig: map[string]any{
632-
"nodeSelector": map[string]any{
626+
expectedDeploymentConfig: &config.DeploymentConfig{
627+
NodeSelector: map[string]string{
633628
"kubernetes.io/os": "linux",
634629
},
635-
"resources": map[string]any{
636-
"requests": map[string]any{
637-
"memory": "128Mi",
638-
},
639-
},
640630
},
641631
expectedDeploymentConfigNil: false,
642632
},
@@ -650,7 +640,8 @@ func Test_GetDeploymentConfig(t *testing.T) {
650640
cfg, err := config.UnmarshalConfig(tt.rawConfig, schema, "")
651641
require.NoError(t, err)
652642

653-
result := cfg.GetDeploymentConfig()
643+
result, err := cfg.GetDeploymentConfig()
644+
require.NoError(t, err)
654645
if tt.expectedDeploymentConfigNil {
655646
require.Nil(t, result)
656647
} else {
@@ -663,52 +654,8 @@ func Test_GetDeploymentConfig(t *testing.T) {
663654
// Test nil config separately
664655
t.Run("nil config returns nil", func(t *testing.T) {
665656
var cfg *config.Config
666-
result := cfg.GetDeploymentConfig()
667-
require.Nil(t, result)
668-
})
669-
670-
// Test that returned map is a defensive copy (mutations don't affect original)
671-
t.Run("returned map is defensive copy - mutations don't affect original", func(t *testing.T) {
672-
rawConfig := []byte(`{
673-
"deploymentConfig": {
674-
"nodeSelector": {
675-
"kubernetes.io/os": "linux"
676-
}
677-
}
678-
}`)
679-
680-
schema, err := bundle.GetConfigSchema()
657+
result, err := cfg.GetDeploymentConfig()
681658
require.NoError(t, err)
682-
683-
cfg, err := config.UnmarshalConfig(rawConfig, schema, "")
684-
require.NoError(t, err)
685-
686-
// Get the deploymentConfig
687-
result1 := cfg.GetDeploymentConfig()
688-
require.NotNil(t, result1)
689-
690-
// Mutate the returned map
691-
result1["nodeSelector"] = map[string]any{
692-
"mutated": "value",
693-
}
694-
result1["newField"] = "added"
695-
696-
// Get deploymentConfig again - should be unaffected by mutations
697-
result2 := cfg.GetDeploymentConfig()
698-
require.NotNil(t, result2)
699-
700-
// Original values should be intact
701-
require.Equal(t, map[string]any{
702-
"nodeSelector": map[string]any{
703-
"kubernetes.io/os": "linux",
704-
},
705-
}, result2)
706-
707-
// New field should not exist
708-
_, exists := result2["newField"]
709-
require.False(t, exists)
710-
711-
// result1 should have the mutations
712-
require.Equal(t, "added", result1["newField"])
659+
require.Nil(t, result)
713660
})
714661
}

0 commit comments

Comments
 (0)