From dc73f58b68bca90e77c8588f84d7dd665100b1ff Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Tue, 2 Sep 2025 10:37:30 +0200 Subject: [PATCH 1/3] Compute target namespace defaults Signed-off-by: Per Goncalves da Silva --- .../rukpak/render/render.go | 55 ++++--- .../rukpak/render/render_test.go | 139 ++++++++++++++++-- 2 files changed, 164 insertions(+), 30 deletions(-) diff --git a/internal/operator-controller/rukpak/render/render.go b/internal/operator-controller/rukpak/render/render.go index 70063f1d4..5cf889fa7 100644 --- a/internal/operator-controller/rukpak/render/render.go +++ b/internal/operator-controller/rukpak/render/render.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" "sigs.k8s.io/controller-runtime/pkg/client" @@ -74,9 +74,6 @@ func (o *Options) apply(opts ...Option) *Options { func (o *Options) validate(rv1 *bundle.RegistryV1) (*Options, []error) { var errs []error - if len(o.TargetNamespaces) == 0 { - errs = append(errs, errors.New("at least one target namespace must be specified")) - } if o.UniqueNameGenerator == nil { errs = append(errs, errors.New("unique name generator must be specified")) } @@ -121,7 +118,7 @@ func (r BundleRenderer) Render(rv1 bundle.RegistryV1, installNamespace string, o genOpts, errs := (&Options{ // default options InstallNamespace: installNamespace, - TargetNamespaces: []string{metav1.NamespaceAll}, + TargetNamespaces: defaultTargetNamespacesForBundle(&rv1, installNamespace), UniqueNameGenerator: DefaultUniqueNameGenerator, CertificateProvider: nil, }).apply(opts...).validate(&rv1) @@ -147,31 +144,55 @@ func DefaultUniqueNameGenerator(base string, o interface{}) (string, error) { } func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, targetNamespaces []string) error { - supportedInstallModes := sets.New[string]() - for _, im := range rv1.CSV.Spec.InstallModes { - if im.Supported { - supportedInstallModes.Insert(string(im.Type)) - } - } + supportedInstallModes := supportedBundleInstallModes(rv1) set := sets.New[string](targetNamespaces...) switch { - case set.Len() == 0 || (set.Len() == 1 && set.Has("")): - if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeAllNamespaces)) { + case set.Len() == 0: + if supportedInstallModes.Len() == 1 && supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { + return errors.New("exactly one target namespace must be specified") + } + return errors.New("at least one target namespace must be specified") + case set.Len() == 1 && set.Has(""): + if supportedInstallModes.Has(v1alpha1.InstallModeTypeAllNamespaces) { return nil } return fmt.Errorf("supported install modes %v do not support targeting all namespaces", sets.List(supportedInstallModes)) case set.Len() == 1 && !set.Has(""): - if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeSingleNamespace)) { + if supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { return nil } - if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeOwnNamespace)) && targetNamespaces[0] == installNamespace { + if supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) && targetNamespaces[0] == installNamespace { return nil } default: - if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeMultiNamespace)) && !set.Has("") { + if supportedInstallModes.Has(v1alpha1.InstallModeTypeMultiNamespace) && !set.Has("") { return nil } } - return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[string](supportedInstallModes), targetNamespaces) + return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[v1alpha1.InstallModeType](supportedInstallModes), targetNamespaces) +} + +func defaultTargetNamespacesForBundle(rv1 *bundle.RegistryV1, installNamespace string) []string { + supportedInstallModes := supportedBundleInstallModes(rv1) + + if supportedInstallModes.Has(v1alpha1.InstallModeTypeAllNamespaces) { + return []string{corev1.NamespaceAll} + } + + if supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) { + return []string{installNamespace} + } + + return nil +} + +func supportedBundleInstallModes(rv1 *bundle.RegistryV1) sets.Set[v1alpha1.InstallModeType] { + supportedInstallModes := sets.New[v1alpha1.InstallModeType]() + for _, im := range rv1.CSV.Spec.InstallModes { + if im.Supported { + supportedInstallModes.Insert(im.Type) + } + } + return supportedInstallModes } diff --git a/internal/operator-controller/rukpak/render/render_test.go b/internal/operator-controller/rukpak/render/render_test.go index 0760c4fa1..9ab50b588 100644 --- a/internal/operator-controller/rukpak/render/render_test.go +++ b/internal/operator-controller/rukpak/render/render_test.go @@ -42,23 +42,136 @@ func Test_BundleRenderer_ValidatesBundle(t *testing.T) { require.Contains(t, err.Error(), "this bundle is invalid") } -func Test_BundleRenderer_CreatesCorrectDefaultOptions(t *testing.T) { - expectedInstallNamespace := "install-namespace" - expectedTargetNamespaces := []string{""} - expectedUniqueNameGenerator := render.DefaultUniqueNameGenerator +func Test_BundleRenderer_CreatesCorrectRenderOptions_WithDefaults(t *testing.T) { + const ( + expectedInstallNamespace = "install-namespace" + ) - renderer := render.BundleRenderer{ - ResourceGenerators: []render.ResourceGenerator{ - func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { + for _, tc := range []struct { + name string + csv v1alpha1.ClusterServiceVersion + validate func(t *testing.T, opts render.Options) + expectedErrMsgFragment string + }{ + { + name: "sets install-namespace correctly", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), + validate: func(t *testing.T, opts render.Options) { require.Equal(t, expectedInstallNamespace, opts.InstallNamespace) - require.Equal(t, expectedTargetNamespaces, opts.TargetNamespaces) - require.Equal(t, reflect.ValueOf(expectedUniqueNameGenerator).Pointer(), reflect.ValueOf(render.DefaultUniqueNameGenerator).Pointer(), "options has unexpected default unique name generator") - return nil, nil }, + }, { + name: "uses DefaultUniqueNameGenerator by default", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, reflect.ValueOf(render.DefaultUniqueNameGenerator).Pointer(), reflect.ValueOf(opts.UniqueNameGenerator).Pointer(), "options has unexpected default unique name generator") + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace, SingleNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace, OwnNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, SingleNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) + }, + }, { + name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, SingleNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + validate: func(t *testing.T, opts render.Options) { + require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) + }, + }, { + name: "returns error if target namespaces is not set bundle supports install modes [SingleNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace)), + expectedErrMsgFragment: "exactly one target namespace must be specified", + }, { + name: "returns error if target namespaces is not set bundle supports install modes [MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeMultiNamespace)), + expectedErrMsgFragment: "at least one target namespace must be specified", + }, { + name: "returns error if target namespaces is not set bundle supports install modes [SingleNamespace, MultiNamespace]", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), + expectedErrMsgFragment: "at least one target namespace must be specified", }, - } + } { + t.Run(tc.name, func(t *testing.T) { + renderer := render.BundleRenderer{ + ResourceGenerators: []render.ResourceGenerator{ + func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { + tc.validate(t, opts) + return nil, nil + }, + }, + } - _, _ = renderer.Render(bundle.RegistryV1{}, expectedInstallNamespace) + _, err := renderer.Render(bundle.RegistryV1{ + CSV: tc.csv, + }, expectedInstallNamespace) + if tc.expectedErrMsgFragment == "" { + require.NoError(t, err) + } else { + require.Error(t, err) + require.Contains(t, err.Error(), tc.expectedErrMsgFragment) + } + }) + } } func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { @@ -76,7 +189,7 @@ func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { opts: []render.Option{ render.WithTargetNamespaces(), }, - err: errors.New("invalid option(s): at least one target namespace must be specified"), + err: errors.New("invalid option(s): invalid target namespaces []: at least one target namespace must be specified"), }, { name: "rejects nil unique name generator", installNamespace: "install-namespace", From 4fc183ad5b2e4fd84ad02ab08475264258b00582 Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Wed, 3 Sep 2025 15:48:20 +0200 Subject: [PATCH 2/3] Update target namespace validation to reject own namespace installation on single namespace mode Signed-off-by: Per Goncalves da Silva --- internal/operator-controller/rukpak/render/render.go | 7 +++++-- .../operator-controller/rukpak/render/render_test.go | 10 +++++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/operator-controller/rukpak/render/render.go b/internal/operator-controller/rukpak/render/render.go index 5cf889fa7..d77793162 100644 --- a/internal/operator-controller/rukpak/render/render.go +++ b/internal/operator-controller/rukpak/render/render.go @@ -159,10 +159,13 @@ func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, t } return fmt.Errorf("supported install modes %v do not support targeting all namespaces", sets.List(supportedInstallModes)) case set.Len() == 1 && !set.Has(""): - if supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { + if targetNamespaces[0] == installNamespace { + if !supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) { + return fmt.Errorf("supported install modes %v do not support targeting own namespace", sets.List(supportedInstallModes)) + } return nil } - if supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) && targetNamespaces[0] == installNamespace { + if supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { return nil } default: diff --git a/internal/operator-controller/rukpak/render/render_test.go b/internal/operator-controller/rukpak/render/render_test.go index 9ab50b588..30b4e716b 100644 --- a/internal/operator-controller/rukpak/render/render_test.go +++ b/internal/operator-controller/rukpak/render/render_test.go @@ -213,7 +213,7 @@ func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { opts: []render.Option{ render.WithTargetNamespaces("install-namespace"), }, - err: errors.New("invalid option(s): invalid target namespaces [install-namespace]: supported install modes [AllNamespaces] do not support target namespaces [install-namespace]"), + err: errors.New("invalid option(s): invalid target namespaces [install-namespace]: supported install modes [AllNamespaces] do not support targeting own namespace"), }, { name: "rejects install out of own namespace if only OwnNamespace install mode is supported", installNamespace: "install-namespace", @@ -266,6 +266,14 @@ func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { opts: []render.Option{ render.WithTargetNamespaces("some-namespace"), }, + }, { + name: "rejects single namespace render if OwnNamespace install mode is unsupported and targets own namespace", + installNamespace: "install-namespace", + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace)), + opts: []render.Option{ + render.WithTargetNamespaces("install-namespace"), + }, + err: errors.New("invalid option(s): invalid target namespaces [install-namespace]: supported install modes [SingleNamespace] do not support targeting own namespace"), }, { name: "accepts multi namespace render if MultiNamespace install mode is supported", installNamespace: "install-namespace", From 01b28a803ef6edd7dc5753ef0331f345b230a09d Mon Sep 17 00:00:00 2001 From: Per Goncalves da Silva Date: Thu, 4 Sep 2025 18:11:07 +0200 Subject: [PATCH 3/3] Update WithTargetNamespace behavior and MultiNamespace validation Signed-off-by: Per Goncalves da Silva --- .../rukpak/render/render.go | 18 ++- .../rukpak/render/render_test.go | 152 +++--------------- 2 files changed, 36 insertions(+), 134 deletions(-) diff --git a/internal/operator-controller/rukpak/render/render.go b/internal/operator-controller/rukpak/render/render.go index d77793162..384b16796 100644 --- a/internal/operator-controller/rukpak/render/render.go +++ b/internal/operator-controller/rukpak/render/render.go @@ -85,9 +85,14 @@ func (o *Options) validate(rv1 *bundle.RegistryV1) (*Options, []error) { type Option func(*Options) +// WithTargetNamespaces sets the target namespaces to be used when rendering the bundle +// The value will only be used if len(namespaces) > 0. Otherwise, the default value for the bundle +// derived from its install mode support will be used (if such a value can be defined). func WithTargetNamespaces(namespaces ...string) Option { return func(o *Options) { - o.TargetNamespaces = namespaces + if len(namespaces) > 0 { + o.TargetNamespaces = namespaces + } } } @@ -149,6 +154,14 @@ func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, t set := sets.New[string](targetNamespaces...) switch { case set.Len() == 0: + // Note: this function generally expects targetNamespace to contain at least one value set by default + // in case the user does not specify the value. The option to set the targetNamespace is a no-op if it is empty. + // The only case for which a default targetNamespace is undefined is in the case of a bundle that only + // supports SingleNamespace install mode. The if statement here is added to provide a more friendly error + // message than just the generic (at least one target namespace must be specified) which would occur + // in case only the MultiNamespace install mode is supported by the bundle. + // If AllNamespaces mode is supported, the default will be [""] -> watch all namespaces + // If only OwnNamespace is supported, the default will be [install-namespace] -> only watch the install/own namespace if supportedInstallModes.Len() == 1 && supportedInstallModes.Has(v1alpha1.InstallModeTypeSingleNamespace) { return errors.New("exactly one target namespace must be specified") } @@ -169,6 +182,9 @@ func validateTargetNamespaces(rv1 *bundle.RegistryV1, installNamespace string, t return nil } default: + if !supportedInstallModes.Has(v1alpha1.InstallModeTypeOwnNamespace) && set.Has(installNamespace) { + return fmt.Errorf("supported install modes %v do not support targeting own namespace", sets.List(supportedInstallModes)) + } if supportedInstallModes.Has(v1alpha1.InstallModeTypeMultiNamespace) && !set.Has("") { return nil } diff --git a/internal/operator-controller/rukpak/render/render_test.go b/internal/operator-controller/rukpak/render/render_test.go index 30b4e716b..0461ea3be 100644 --- a/internal/operator-controller/rukpak/render/render_test.go +++ b/internal/operator-controller/rukpak/render/render_test.go @@ -42,136 +42,23 @@ func Test_BundleRenderer_ValidatesBundle(t *testing.T) { require.Contains(t, err.Error(), "this bundle is invalid") } -func Test_BundleRenderer_CreatesCorrectRenderOptions_WithDefaults(t *testing.T) { - const ( - expectedInstallNamespace = "install-namespace" - ) +func Test_BundleRenderer_CreatesCorrectDefaultOptions(t *testing.T) { + expectedInstallNamespace := "install-namespace" + expectedTargetNamespaces := []string{""} + expectedUniqueNameGenerator := render.DefaultUniqueNameGenerator - for _, tc := range []struct { - name string - csv v1alpha1.ClusterServiceVersion - validate func(t *testing.T, opts render.Options) - expectedErrMsgFragment string - }{ - { - name: "sets install-namespace correctly", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), - validate: func(t *testing.T, opts render.Options) { + renderer := render.BundleRenderer{ + ResourceGenerators: []render.ResourceGenerator{ + func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { require.Equal(t, expectedInstallNamespace, opts.InstallNamespace) + require.Equal(t, expectedTargetNamespaces, opts.TargetNamespaces) + require.Equal(t, reflect.ValueOf(expectedUniqueNameGenerator).Pointer(), reflect.ValueOf(render.DefaultUniqueNameGenerator).Pointer(), "options has unexpected default unique name generator") + return nil, nil }, - }, { - name: "uses DefaultUniqueNameGenerator by default", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, reflect.ValueOf(render.DefaultUniqueNameGenerator).Pointer(), reflect.ValueOf(opts.UniqueNameGenerator).Pointer(), "options has unexpected default unique name generator") - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace, SingleNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, OwnNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [corev1.NamespaceAll] by default if bundle supports install modes [AllNamespaces, SingleNamespace, OwnNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{corev1.NamespaceAll}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, SingleNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) - }, - }, { - name: "sets target namespaces to [install-namespace] by default if bundle supports install modes [OwnNamespace, SingleNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeOwnNamespace, v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - validate: func(t *testing.T, opts render.Options) { - require.Equal(t, []string{expectedInstallNamespace}, opts.TargetNamespaces) - }, - }, { - name: "returns error if target namespaces is not set bundle supports install modes [SingleNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace)), - expectedErrMsgFragment: "exactly one target namespace must be specified", - }, { - name: "returns error if target namespaces is not set bundle supports install modes [MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeMultiNamespace)), - expectedErrMsgFragment: "at least one target namespace must be specified", - }, { - name: "returns error if target namespaces is not set bundle supports install modes [SingleNamespace, MultiNamespace]", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace, v1alpha1.InstallModeTypeMultiNamespace)), - expectedErrMsgFragment: "at least one target namespace must be specified", }, - } { - t.Run(tc.name, func(t *testing.T) { - renderer := render.BundleRenderer{ - ResourceGenerators: []render.ResourceGenerator{ - func(rv1 *bundle.RegistryV1, opts render.Options) ([]client.Object, error) { - tc.validate(t, opts) - return nil, nil - }, - }, - } - - _, err := renderer.Render(bundle.RegistryV1{ - CSV: tc.csv, - }, expectedInstallNamespace) - if tc.expectedErrMsgFragment == "" { - require.NoError(t, err) - } else { - require.Error(t, err) - require.Contains(t, err.Error(), tc.expectedErrMsgFragment) - } - }) } + + _, _ = renderer.Render(bundle.RegistryV1{}, expectedInstallNamespace) } func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { @@ -183,13 +70,12 @@ func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { err error }{ { - name: "rejects empty targetNamespaces", + name: "accepts empty targetNamespaces (because it is ignored)", installNamespace: "install-namespace", csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeAllNamespaces)), opts: []render.Option{ render.WithTargetNamespaces(), }, - err: errors.New("invalid option(s): invalid target namespaces []: at least one target namespace must be specified"), }, { name: "rejects nil unique name generator", installNamespace: "install-namespace", @@ -267,20 +153,20 @@ func Test_BundleRenderer_ValidatesRenderOptions(t *testing.T) { render.WithTargetNamespaces("some-namespace"), }, }, { - name: "rejects single namespace render if OwnNamespace install mode is unsupported and targets own namespace", + name: "accepts multi namespace render if MultiNamespace install mode is supported", installNamespace: "install-namespace", - csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeSingleNamespace)), + csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeMultiNamespace)), opts: []render.Option{ - render.WithTargetNamespaces("install-namespace"), + render.WithTargetNamespaces("n1", "n2", "n3"), }, - err: errors.New("invalid option(s): invalid target namespaces [install-namespace]: supported install modes [SingleNamespace] do not support targeting own namespace"), }, { - name: "accepts multi namespace render if MultiNamespace install mode is supported", + name: "reject multi namespace render if OwnNamespace install mode is not supported and target namespaces include install namespace", installNamespace: "install-namespace", csv: MakeCSV(WithInstallModeSupportFor(v1alpha1.InstallModeTypeMultiNamespace)), opts: []render.Option{ - render.WithTargetNamespaces("n1", "n2", "n3"), + render.WithTargetNamespaces("n1", "n2", "n3", "install-namespace"), }, + err: errors.New("invalid option(s): invalid target namespaces [n1 n2 n3 install-namespace]: supported install modes [MultiNamespace] do not support targeting own namespace"), }, } { t.Run(tc.name, func(t *testing.T) {