Skip to content

Commit f8d5537

Browse files
author
Per G. da Silva
committed
Fix renderer / applier integration
Signed-off-by: Per G. da Silva <[email protected]>
1 parent 669766b commit f8d5537

File tree

6 files changed

+147
-9
lines changed

6 files changed

+147
-9
lines changed

api/v1/clusterextension_types.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,23 @@ type ClusterExtension struct {
535535
Status ClusterExtensionStatus `json:"status,omitempty"`
536536
}
537537

538+
// ExtensionConfigBytes returns the ClusterExtension configuration input by the user
539+
// through .spec.config as a byte slice
540+
func (e *ClusterExtension) ExtensionConfigBytes() []byte {
541+
if e.Spec.Config == nil {
542+
return nil
543+
}
544+
switch e.Spec.Config.ConfigType {
545+
case ClusterExtensionConfigTypeInline:
546+
if e.Spec.Config.Inline != nil {
547+
return e.Spec.Config.Inline.Raw
548+
}
549+
return nil
550+
default:
551+
return nil
552+
}
553+
}
554+
538555
// +kubebuilder:object:root=true
539556

540557
// ClusterExtensionList contains a list of ClusterExtension

api/v1/clusterextension_types_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,11 @@ import (
99
"strings"
1010
"testing"
1111

12+
"github.com/stretchr/testify/require"
1213
"golang.org/x/exp/slices" // replace with "slices" in go 1.21
14+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
1315

16+
ocv1 "github.com/operator-framework/operator-controller/api/v1"
1417
"github.com/operator-framework/operator-controller/internal/operator-controller/conditionsets"
1518
)
1619

@@ -100,3 +103,50 @@ func parseConstants(prefix string) ([]string, error) {
100103
}
101104
return constValues, nil
102105
}
106+
107+
func Test_ExtensionConfigBytes(t *testing.T) {
108+
for _, tc := range []struct {
109+
name string
110+
config *ocv1.ClusterExtensionConfig
111+
expectedConfigBytes []byte
112+
}{
113+
{
114+
name: "nil for no config",
115+
config: nil,
116+
expectedConfigBytes: nil,
117+
},
118+
{
119+
name: "nil for unknown config type",
120+
config: &ocv1.ClusterExtensionConfig{
121+
ConfigType: "not-a-real-config-type",
122+
},
123+
expectedConfigBytes: nil,
124+
},
125+
{
126+
name: ".spec.config.inline.raw for Inline config",
127+
config: &ocv1.ClusterExtensionConfig{
128+
ConfigType: ocv1.ClusterExtensionConfigTypeInline,
129+
Inline: &apiextensionsv1.JSON{
130+
Raw: []byte(`{"key": "value"}`),
131+
},
132+
},
133+
expectedConfigBytes: []byte(`{"key": "value"}`),
134+
},
135+
{
136+
name: "nil when .spec.config.inline is nil",
137+
config: &ocv1.ClusterExtensionConfig{
138+
ConfigType: ocv1.ClusterExtensionConfigTypeInline,
139+
},
140+
expectedConfigBytes: nil,
141+
},
142+
} {
143+
t.Run(tc.name, func(t *testing.T) {
144+
ext := &ocv1.ClusterExtension{
145+
Spec: ocv1.ClusterExtensionSpec{
146+
Config: tc.config,
147+
},
148+
}
149+
require.Equal(t, tc.expectedConfigBytes, ext.ExtensionConfigBytes())
150+
})
151+
}
152+
}

internal/operator-controller/applier/provider.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ func (r *RegistryV1ManifestProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExtens
6868
render.WithCertificateProvider(r.CertificateProvider),
6969
}
7070

71-
if r.IsSingleOwnNamespaceEnabled && ext.Spec.Config != nil && ext.Spec.Config.ConfigType == ocv1.ClusterExtensionConfigTypeInline {
72-
bundleConfig, err := bundle.UnmarshallConfig(ext.Spec.Config.Inline.Raw, rv1, ext.Spec.Namespace)
71+
if r.IsSingleOwnNamespaceEnabled {
72+
bundleConfig, err := bundle.UnmarshallConfig(ext.ExtensionConfigBytes(), rv1, ext.Spec.Namespace)
7373
if err != nil {
7474
return nil, fmt.Errorf("invalid bundle configuration: %w", err)
7575
}

internal/operator-controller/applier/provider_test.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -289,7 +289,7 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) {
289289
require.Contains(t, err.Error(), "unsupported bundle")
290290
})
291291

292-
t.Run("accepts bundles without AllNamespaces install mode and with SingleNamespace support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) {
292+
t.Run("accepts bundles with install modes {SingleNamespace} when the appropriate configuration is given", func(t *testing.T) {
293293
expectedWatchNamespace := "some-namespace"
294294
provider := applier.RegistryV1ManifestProvider{
295295
BundleRenderer: render.BundleRenderer{
@@ -321,7 +321,34 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) {
321321
require.NoError(t, err)
322322
})
323323

324-
t.Run("accepts bundles without AllNamespaces install mode and with OwnNamespace support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) {
324+
t.Run("rejects bundles with {SingleNamespace} install modes when no configuration is given", func(t *testing.T) {
325+
expectedWatchNamespace := "some-namespace"
326+
provider := applier.RegistryV1ManifestProvider{
327+
BundleRenderer: render.BundleRenderer{
328+
ResourceGenerators: []render.ResourceGenerator{
329+
func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) {
330+
t.Log("ensure watch namespace is appropriately configured")
331+
require.Equal(t, []string{expectedWatchNamespace}, opts.TargetNamespaces)
332+
return nil, nil
333+
},
334+
},
335+
},
336+
IsSingleOwnNamespaceEnabled: true,
337+
}
338+
339+
bundleFS := bundlefs.Builder().WithPackageName("test").
340+
WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace).Build()).Build()
341+
342+
_, err := provider.Get(bundleFS, &ocv1.ClusterExtension{
343+
Spec: ocv1.ClusterExtensionSpec{
344+
Namespace: "install-namespace",
345+
},
346+
})
347+
require.Error(t, err)
348+
require.Contains(t, err.Error(), "required field \"watchNamespace\" is missing")
349+
})
350+
351+
t.Run("accepts bundles with {OwnNamespace} install modes when the appropriate configuration is given", func(t *testing.T) {
325352
provider := applier.RegistryV1ManifestProvider{
326353
IsSingleOwnNamespaceEnabled: true,
327354
}
@@ -330,11 +357,53 @@ func Test_RegistryV1ManifestProvider_SingleOwnNamespaceSupport(t *testing.T) {
330357
_, err := provider.Get(bundleFS, &ocv1.ClusterExtension{
331358
Spec: ocv1.ClusterExtensionSpec{
332359
Namespace: "install-namespace",
360+
Config: &ocv1.ClusterExtensionConfig{
361+
ConfigType: ocv1.ClusterExtensionConfigTypeInline,
362+
Inline: &apiextensionsv1.JSON{
363+
Raw: []byte(`{"watchNamespace": "install-namespace"}`),
364+
},
365+
},
333366
},
334367
})
335368
require.NoError(t, err)
336369
})
337370

371+
t.Run("rejects bundles with {OwnNamespace} install modes when no configuration is given", func(t *testing.T) {
372+
provider := applier.RegistryV1ManifestProvider{
373+
IsSingleOwnNamespaceEnabled: true,
374+
}
375+
bundleFS := bundlefs.Builder().WithPackageName("test").
376+
WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build()
377+
_, err := provider.Get(bundleFS, &ocv1.ClusterExtension{
378+
Spec: ocv1.ClusterExtensionSpec{
379+
Namespace: "install-namespace",
380+
},
381+
})
382+
require.Error(t, err)
383+
require.Contains(t, err.Error(), "required field \"watchNamespace\" is missing")
384+
})
385+
386+
t.Run("rejects bundles with {OwnNamespace} install modes when watchNamespace is not install namespace", func(t *testing.T) {
387+
provider := applier.RegistryV1ManifestProvider{
388+
IsSingleOwnNamespaceEnabled: true,
389+
}
390+
bundleFS := bundlefs.Builder().WithPackageName("test").
391+
WithCSV(clusterserviceversion.Builder().WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace).Build()).Build()
392+
_, err := provider.Get(bundleFS, &ocv1.ClusterExtension{
393+
Spec: ocv1.ClusterExtensionSpec{
394+
Namespace: "install-namespace",
395+
Config: &ocv1.ClusterExtensionConfig{
396+
ConfigType: ocv1.ClusterExtensionConfigTypeInline,
397+
Inline: &apiextensionsv1.JSON{
398+
Raw: []byte(`{"watchNamespace": "not-install-namespace"}`),
399+
},
400+
},
401+
},
402+
})
403+
require.Error(t, err)
404+
require.Contains(t, err.Error(), "invalid 'watchNamespace' \"not-install-namespace\": must be install namespace (install-namespace)")
405+
})
406+
338407
t.Run("rejects bundles without AllNamespaces, SingleNamespace, or OwnNamespace install mode support when Single/OwnNamespace install mode support is enabled", func(t *testing.T) {
339408
provider := applier.RegistryV1ManifestProvider{
340409
IsSingleOwnNamespaceEnabled: true,

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@ type Config struct {
2222
// - rv is nil
2323
// - bytes is not a valid YAML/JSON object
2424
// - bytes is a valid YAML/JSON object but does not follow the registry+v1 schema
25-
// if bytes is nil a nil bundle.Config is returned
25+
// if bytes is nil it will be treated as an empty json object ({})
2626
func UnmarshallConfig(bytes []byte, rv1 RegistryV1, installNamespace string) (*Config, error) {
2727
if bytes == nil {
28-
return nil, nil
28+
bytes = []byte("{}")
2929
}
3030

3131
bundleConfig := &Config{}

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

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ func Test_UnmarshallConfig(t *testing.T) {
2222
expectedConfig *bundle.Config
2323
}{
2424
{
25-
name: "accepts nil raw config",
26-
rawConfig: nil,
27-
expectedConfig: nil,
25+
name: "treats nil config as {}",
26+
rawConfig: nil,
27+
supportedInstallModes: []v1alpha1.InstallModeType{v1alpha1.InstallModeTypeSingleNamespace},
28+
expectedConfig: nil,
29+
expectedErrMessage: "required field \"watchNamespace\" is missing",
2830
},
2931
{
3032
name: "accepts json config",

0 commit comments

Comments
 (0)