Skip to content

Commit f85c707

Browse files
(fix): Corrected an unclear validation error when configuring watchNamespace on an operator restricted to AllNamespaces mode.
1 parent 03eb884 commit f85c707

3 files changed

Lines changed: 44 additions & 17 deletions

File tree

internal/operator-controller/config/config.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ const (
4545
// FormatSingleNamespaceInstallMode defines the format check to ensure that
4646
// the watchNamespace must differ from install namespace
4747
FormatSingleNamespaceInstallMode = "singleNamespaceInstallMode"
48+
// FormatAllNamespacesOnlyInstallMode defines the format check to reject
49+
// watchNamespace when only AllNamespaces mode is supported (registry+v1 specific)
50+
FormatAllNamespacesOnlyInstallMode = "allNamespacesOnlyInstallMode"
4851
)
4952

5053
// SchemaProvider lets each package format type describe what configuration it accepts.
@@ -192,6 +195,14 @@ func validateConfigWithSchema(configBytes []byte, schema map[string]any, install
192195
return nil
193196
},
194197
})
198+
compiler.RegisterFormat(&jsonschema.Format{
199+
Name: FormatAllNamespacesOnlyInstallMode,
200+
Validate: func(value interface{}) error {
201+
// Always reject - this format is used when AllNamespaces is the only supported mode
202+
// and watchNamespace configuration doesn't make sense
203+
return fmt.Errorf("watchNamespace configuration is not supported when the content only supports AllNamespaces install mode")
204+
},
205+
})
195206

196207
if err := compiler.AddResource(configSchemaID, schema); err != nil {
197208
return fmt.Errorf("failed to load schema: %w", err)

internal/operator-controller/config/config_test.go

Lines changed: 2 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -80,22 +80,10 @@ func Test_UnmarshalConfig(t *testing.T) {
8080
expectedErrMessage: `got object, want string`,
8181
},
8282
{
83-
name: "rejects with unknown field when install modes {AllNamespaces}",
83+
name: "rejects with descriptive message when install modes {AllNamespaces}",
8484
supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces},
8585
rawConfig: []byte(`{"watchNamespace": "some-namespace"}`),
86-
expectedErrMessage: `unknown field "watchNamespace"`,
87-
},
88-
{
89-
name: "rejects with unknown field when install modes {MultiNamespace}",
90-
supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeMultiNamespace},
91-
rawConfig: []byte(`{"watchNamespace": "some-namespace"}`),
92-
expectedErrMessage: `unknown field "watchNamespace"`,
93-
},
94-
{
95-
name: "reject with unknown field when install modes {AllNamespaces, MultiNamespace}",
96-
supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeMultiNamespace},
97-
rawConfig: []byte(`{"watchNamespace": "some-namespace"}`),
98-
expectedErrMessage: `unknown field "watchNamespace"`,
86+
expectedErrMessage: `watchNamespace configuration is not supported when the content only supports AllNamespaces install mode`,
9987
},
10088
{
10189
name: "reject with required field when install modes {OwnNamespace} and watchNamespace is null",

internal/operator-controller/rukpak/bundle/registryv1.go

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func (rv1 *RegistryV1) GetConfigSchema() (map[string]any, error) {
3434
// buildBundleConfigSchema creates validation rules based on what the operator supports.
3535
//
3636
// Examples of how install modes affect validation:
37-
// - AllNamespaces only: user can't set watchNamespace (operator watches everything)
37+
// - AllNamespaces only: watchNamespace is explicitly rejected with helpful error
3838
// - OwnNamespace only: user must set watchNamespace to the install namespace
3939
// - SingleNamespace only: user must set watchNamespace to a different namespace
4040
// - AllNamespaces + OwnNamespace: user can optionally set watchNamespace
@@ -48,8 +48,12 @@ func buildBundleConfigSchema(installModes sets.Set[v1alpha1.InstallMode]) (map[s
4848
properties := map[string]any{}
4949
var required []any
5050

51-
// Add watchNamespace property if the bundle supports it
52-
if isWatchNamespaceConfigurable(installModes) {
51+
// Special case: if ONLY AllNamespaces is supported, explicitly reject watchNamespace
52+
// with a helpful error message (instead of generic "unknown field")
53+
if isAllNamespacesOnly(installModes) {
54+
properties["watchNamespace"] = buildRejectedWatchNamespaceProperty()
55+
} else if isWatchNamespaceConfigurable(installModes) {
56+
// Add watchNamespace property if the bundle supports it
5357
watchNSProperty, isRequired := buildWatchNamespaceProperty(installModes)
5458
properties["watchNamespace"] = watchNSProperty
5559
if isRequired {
@@ -151,3 +155,27 @@ func isWatchNamespaceConfigRequired(installModes sets.Set[v1alpha1.InstallMode])
151155
return isWatchNamespaceConfigurable(installModes) &&
152156
!installModes.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true})
153157
}
158+
159+
// isAllNamespacesOnly checks if only AllNamespaces install mode is supported.
160+
//
161+
// Returns true when:
162+
// - Only AllNamespaces is supported (no OwnNamespace, no SingleNamespace)
163+
//
164+
// Returns false when:
165+
// - OwnNamespace or SingleNamespace is also supported
166+
func isAllNamespacesOnly(installModes sets.Set[v1alpha1.InstallMode]) bool {
167+
hasAllNamespaces := installModes.Has(v1alpha1.InstallMode{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true})
168+
hasConfigurable := isWatchNamespaceConfigurable(installModes)
169+
170+
return hasAllNamespaces && !hasConfigurable
171+
}
172+
173+
// buildRejectedWatchNamespaceProperty creates a schema property that always rejects
174+
// watchNamespace with a descriptive error message for AllNamespaces-only operators.
175+
func buildRejectedWatchNamespaceProperty() map[string]any {
176+
return map[string]any{
177+
"type": "string",
178+
"format": config.FormatAllNamespacesOnlyInstallMode,
179+
"description": "This field is not supported for this operator's install mode configuration",
180+
}
181+
}

0 commit comments

Comments
 (0)