Skip to content

Commit 7504056

Browse files
Per G. da Silvaperdasilva
Per G. da Silva
authored andcommitted
Move install mode validation to renderer
Signed-off-by: Per G. da Silva <[email protected]>
1 parent 381af96 commit 7504056

File tree

4 files changed

+188
-166
lines changed

4 files changed

+188
-166
lines changed

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

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -191,29 +191,6 @@ func copyMetadataPropertiesToCSV(csv *v1alpha1.ClusterServiceVersion, fsys fs.FS
191191
return nil
192192
}
193193

194-
func validateTargetNamespaces(supportedInstallModes sets.Set[string], installNamespace string, targetNamespaces []string) error {
195-
set := sets.New[string](targetNamespaces...)
196-
switch {
197-
case set.Len() == 0 || (set.Len() == 1 && set.Has("")):
198-
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeAllNamespaces)) {
199-
return nil
200-
}
201-
return fmt.Errorf("supported install modes %v do not support targeting all namespaces", sets.List(supportedInstallModes))
202-
case set.Len() == 1 && !set.Has(""):
203-
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeSingleNamespace)) {
204-
return nil
205-
}
206-
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeOwnNamespace)) && targetNamespaces[0] == installNamespace {
207-
return nil
208-
}
209-
default:
210-
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeMultiNamespace)) && !set.Has("") {
211-
return nil
212-
}
213-
}
214-
return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[string](supportedInstallModes), targetNamespaces)
215-
}
216-
217194
var PlainConverter = Converter{
218195
BundleRenderer: render.BundleRenderer{
219196
BundleValidator: validators.RegistryV1BundleValidator,
@@ -255,10 +232,6 @@ func (c Converter) Convert(rv1 render.RegistryV1, installNamespace string, targe
255232
}
256233
}
257234

258-
if err := validateTargetNamespaces(supportedInstallModes, installNamespace, targetNamespaces); err != nil {
259-
return nil, err
260-
}
261-
262235
if len(rv1.CSV.Spec.APIServiceDefinitions.Owned) > 0 {
263236
return nil, fmt.Errorf("apiServiceDefintions are not supported")
264237
}

internal/operator-controller/rukpak/convert/registryv1_test.go

Lines changed: 1 addition & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -244,138 +244,6 @@ func getBaseCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) {
244244
return baseCSV, svc
245245
}
246246

247-
func TestRegistryV1SuiteGenerateAllNamespace(t *testing.T) {
248-
t.Log("RegistryV1 Suite Convert")
249-
t.Log("It should generate objects successfully based on target namespaces")
250-
251-
t.Log("It should convert into plain manifests successfully with AllNamespaces")
252-
baseCSV, svc := getBaseCsvAndService()
253-
csv := baseCSV.DeepCopy()
254-
csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeAllNamespaces, Supported: true}}
255-
256-
t.Log("By creating a registry v1 bundle")
257-
watchNamespaces := []string{""}
258-
unstructuredSvc := convertToUnstructured(t, svc)
259-
registryv1Bundle := render.RegistryV1{
260-
PackageName: "testPkg",
261-
CSV: *csv,
262-
Others: []unstructured.Unstructured{unstructuredSvc},
263-
}
264-
265-
t.Log("By converting to plain")
266-
plainBundle, err := convert.PlainConverter.Convert(registryv1Bundle, installNamespace, watchNamespaces)
267-
require.NoError(t, err)
268-
269-
t.Log("By verifying if plain bundle has required objects")
270-
require.NotNil(t, plainBundle)
271-
require.Len(t, plainBundle.Objects, 5)
272-
273-
t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template")
274-
dep := findObjectByName("test-deployment", plainBundle.Objects)
275-
require.NotNil(t, dep)
276-
require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces)
277-
require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces])
278-
}
279-
280-
func TestRegistryV1SuiteGenerateMultiNamespace(t *testing.T) {
281-
t.Log("RegistryV1 Suite Convert")
282-
t.Log("It should generate objects successfully based on target namespaces")
283-
284-
t.Log("It should convert into plain manifests successfully with MultiNamespace")
285-
baseCSV, svc := getBaseCsvAndService()
286-
csv := baseCSV.DeepCopy()
287-
csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeMultiNamespace, Supported: true}}
288-
289-
t.Log("By creating a registry v1 bundle")
290-
watchNamespaces := []string{"testWatchNs1", "testWatchNs2"}
291-
unstructuredSvc := convertToUnstructured(t, svc)
292-
registryv1Bundle := render.RegistryV1{
293-
PackageName: "testPkg",
294-
CSV: *csv,
295-
Others: []unstructured.Unstructured{unstructuredSvc},
296-
}
297-
298-
t.Log("By converting to plain")
299-
plainBundle, err := convert.PlainConverter.Convert(registryv1Bundle, installNamespace, watchNamespaces)
300-
require.NoError(t, err)
301-
302-
t.Log("By verifying if plain bundle has required objects")
303-
require.NotNil(t, plainBundle)
304-
require.Len(t, plainBundle.Objects, 7)
305-
306-
t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template")
307-
dep := findObjectByName("test-deployment", plainBundle.Objects)
308-
require.NotNil(t, dep)
309-
require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces)
310-
require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces])
311-
}
312-
313-
func TestRegistryV1SuiteGenerateSingleNamespace(t *testing.T) {
314-
t.Log("RegistryV1 Suite Convert")
315-
t.Log("It should generate objects successfully based on target namespaces")
316-
317-
t.Log("It should convert into plain manifests successfully with SingleNamespace")
318-
baseCSV, svc := getBaseCsvAndService()
319-
csv := baseCSV.DeepCopy()
320-
csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeSingleNamespace, Supported: true}}
321-
322-
t.Log("By creating a registry v1 bundle")
323-
watchNamespaces := []string{"testWatchNs1"}
324-
unstructuredSvc := convertToUnstructured(t, svc)
325-
registryv1Bundle := render.RegistryV1{
326-
PackageName: "testPkg",
327-
CSV: *csv,
328-
Others: []unstructured.Unstructured{unstructuredSvc},
329-
}
330-
331-
t.Log("By converting to plain")
332-
plainBundle, err := convert.PlainConverter.Convert(registryv1Bundle, installNamespace, watchNamespaces)
333-
require.NoError(t, err)
334-
335-
t.Log("By verifying if plain bundle has required objects")
336-
require.NotNil(t, plainBundle)
337-
require.Len(t, plainBundle.Objects, 5)
338-
339-
t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template")
340-
dep := findObjectByName("test-deployment", plainBundle.Objects)
341-
require.NotNil(t, dep)
342-
require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces)
343-
require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces])
344-
}
345-
346-
func TestRegistryV1SuiteGenerateOwnNamespace(t *testing.T) {
347-
t.Log("RegistryV1 Suite Convert")
348-
t.Log("It should generate objects successfully based on target namespaces")
349-
350-
t.Log("It should convert into plain manifests successfully with own namespace")
351-
baseCSV, svc := getBaseCsvAndService()
352-
csv := baseCSV.DeepCopy()
353-
csv.Spec.InstallModes = []v1alpha1.InstallMode{{Type: v1alpha1.InstallModeTypeOwnNamespace, Supported: true}}
354-
355-
t.Log("By creating a registry v1 bundle")
356-
watchNamespaces := []string{installNamespace}
357-
unstructuredSvc := convertToUnstructured(t, svc)
358-
registryv1Bundle := render.RegistryV1{
359-
PackageName: "testPkg",
360-
CSV: *csv,
361-
Others: []unstructured.Unstructured{unstructuredSvc},
362-
}
363-
364-
t.Log("By converting to plain")
365-
plainBundle, err := convert.PlainConverter.Convert(registryv1Bundle, installNamespace, watchNamespaces)
366-
require.NoError(t, err)
367-
368-
t.Log("By verifying if plain bundle has required objects")
369-
require.NotNil(t, plainBundle)
370-
require.Len(t, plainBundle.Objects, 5)
371-
372-
t.Log("By verifying olm.targetNamespaces annotation in the deployment's pod template")
373-
dep := findObjectByName("test-deployment", plainBundle.Objects)
374-
require.NotNil(t, dep)
375-
require.Contains(t, dep.(*appsv1.Deployment).Spec.Template.Annotations, olmNamespaces)
376-
require.Equal(t, strings.Join(watchNamespaces, ","), dep.(*appsv1.Deployment).Spec.Template.Annotations[olmNamespaces])
377-
}
378-
379247
func TestConvertInstallModeValidation(t *testing.T) {
380248
for _, tc := range []struct {
381249
description string
@@ -609,7 +477,7 @@ func TestRegistryV1SuiteGenerateWebhooks_WebhookSupportFGEnabled(t *testing.T) {
609477
require.NotNil(t, plainBundle)
610478
}
611479

612-
func TestRegistryV1SuiteGenerateNoAPISerciceDefinitions(t *testing.T) {
480+
func TestRegistryV1SuiteGenerateNoAPIServiceDefinitions(t *testing.T) {
613481
t.Log("RegistryV1 Suite Convert")
614482
t.Log("It should generate objects successfully based on target namespaces")
615483

internal/operator-controller/rukpak/render/render.go

Lines changed: 55 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ package render
22

33
import (
44
"errors"
5+
"fmt"
56

67
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
78
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
89
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
10+
"k8s.io/apimachinery/pkg/util/sets"
911
"sigs.k8s.io/controller-runtime/pkg/client"
1012

1113
"github.com/operator-framework/api/pkg/operators/v1alpha1"
@@ -78,6 +80,20 @@ func (o *Options) apply(opts ...Option) *Options {
7880
return o
7981
}
8082

83+
func (o *Options) validate(rv1 *RegistryV1) (*Options, []error) {
84+
var errs []error
85+
if len(o.TargetNamespaces) == 0 {
86+
errs = append(errs, errors.New("at least one target namespace must be specified"))
87+
}
88+
if o.UniqueNameGenerator == nil {
89+
errs = append(errs, errors.New("unique name generator must be specified"))
90+
}
91+
if err := validateTargetNamespaces(rv1, o.InstallNamespace, o.TargetNamespaces); err != nil {
92+
errs = append(errs, fmt.Errorf("invalid target namespaces %v: %w", o.TargetNamespaces, err))
93+
}
94+
return o, errs
95+
}
96+
8197
type Option func(*Options)
8298

8399
func WithTargetNamespaces(namespaces ...string) Option {
@@ -109,13 +125,19 @@ func (r BundleRenderer) Render(rv1 RegistryV1, installNamespace string, opts ...
109125
return nil, err
110126
}
111127

112-
genOpts := (&Options{
128+
// generate bundle objects
129+
genOpts, errs := (&Options{
130+
// default options
113131
InstallNamespace: installNamespace,
114132
TargetNamespaces: []string{metav1.NamespaceAll},
115133
UniqueNameGenerator: DefaultUniqueNameGenerator,
116-
}).apply(opts...)
134+
CertificateProvider: nil,
135+
}).apply(opts...).validate(&rv1)
136+
137+
if len(errs) > 0 {
138+
return nil, fmt.Errorf("invalid option(s): %w", errors.Join(errs...))
139+
}
117140

118-
// generate bundle objects
119141
objs, err := ResourceGenerators(r.ResourceGenerators).GenerateResources(&rv1, *genOpts)
120142
if err != nil {
121143
return nil, err
@@ -131,3 +153,33 @@ func DefaultUniqueNameGenerator(base string, o interface{}) (string, error) {
131153
}
132154
return util.ObjectNameForBaseAndSuffix(base, hashStr), nil
133155
}
156+
157+
func validateTargetNamespaces(rv1 *RegistryV1, installNamespace string, targetNamespaces []string) error {
158+
supportedInstallModes := sets.New[string]()
159+
for _, im := range rv1.CSV.Spec.InstallModes {
160+
if im.Supported {
161+
supportedInstallModes.Insert(string(im.Type))
162+
}
163+
}
164+
165+
set := sets.New[string](targetNamespaces...)
166+
switch {
167+
case set.Len() == 0 || (set.Len() == 1 && set.Has("")):
168+
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeAllNamespaces)) {
169+
return nil
170+
}
171+
return fmt.Errorf("supported install modes %v do not support targeting all namespaces", sets.List(supportedInstallModes))
172+
case set.Len() == 1 && !set.Has(""):
173+
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeSingleNamespace)) {
174+
return nil
175+
}
176+
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeOwnNamespace)) && targetNamespaces[0] == installNamespace {
177+
return nil
178+
}
179+
default:
180+
if supportedInstallModes.Has(string(v1alpha1.InstallModeTypeMultiNamespace)) && !set.Has("") {
181+
return nil
182+
}
183+
}
184+
return fmt.Errorf("supported install modes %v do not support target namespaces %v", sets.List[string](supportedInstallModes), targetNamespaces)
185+
}

0 commit comments

Comments
 (0)