Skip to content

Commit e36a985

Browse files
author
Per Goncalves da Silva
committed
Add ParseFS failure unit tests
Signed-off-by: Per Goncalves da Silva <[email protected]>
1 parent bd1634c commit e36a985

File tree

2 files changed

+114
-15
lines changed

2 files changed

+114
-15
lines changed

internal/rukpak/convert/registryv1.go

+14
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ func RegistryV1ToHelmChart(ctx context.Context, rv1 fs.FS, installNamespace stri
6969
return chrt, nil
7070
}
7171

72+
// ParseFS converts the rv1 filesystem into a RegistryV1.
73+
// ParseFS expects the filesystem to conform to the registry+v1 format:
74+
// metadata/annotations.yaml
75+
// manifests/
76+
// - csv.yaml
77+
// - ...
78+
//
79+
// manifests directory does not contain subdirectories
7280
func ParseFS(ctx context.Context, rv1 fs.FS) (RegistryV1, error) {
7381
l := log.FromContext(ctx)
7482

@@ -84,6 +92,7 @@ func ParseFS(ctx context.Context, rv1 fs.FS) (RegistryV1, error) {
8492
reg.PackageName = annotationsFile.Annotations.PackageName
8593

8694
const manifestsDir = "manifests"
95+
foundCSV := false
8796
if err := fs.WalkDir(rv1, manifestsDir, func(path string, e fs.DirEntry, err error) error {
8897
if err != nil {
8998
return err
@@ -119,6 +128,7 @@ func ParseFS(ctx context.Context, rv1 fs.FS) (RegistryV1, error) {
119128
return err
120129
}
121130
reg.CSV = csv
131+
foundCSV = true
122132
default:
123133
reg.Others = append(reg.Others, *info.Object.(*unstructured.Unstructured))
124134
}
@@ -131,6 +141,10 @@ func ParseFS(ctx context.Context, rv1 fs.FS) (RegistryV1, error) {
131141
return reg, err
132142
}
133143

144+
if !foundCSV {
145+
return reg, fmt.Errorf("no ClusterServiceVersion found in %q", manifestsDir)
146+
}
147+
134148
if err := copyMetadataPropertiesToCSV(&reg.CSV, rv1); err != nil {
135149
return reg, err
136150
}

internal/rukpak/convert/registryv1_test.go

+100-15
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ package convert_test
33
import (
44
"context"
55
"fmt"
6-
"github.com/operator-framework/operator-controller/internal/rukpak/convert"
6+
"io/fs"
77
"os"
88
"strings"
99
"testing"
10+
"testing/fstest"
1011

1112
"github.com/stretchr/testify/require"
1213
appsv1 "k8s.io/api/apps/v1"
@@ -21,12 +22,17 @@ import (
2122

2223
"github.com/operator-framework/api/pkg/operators/v1alpha1"
2324
"github.com/operator-framework/operator-registry/alpha/property"
25+
26+
"github.com/operator-framework/operator-controller/internal/rukpak/convert"
2427
)
2528

2629
const (
2730
olmNamespaces = "olm.targetNamespaces"
2831
olmProperties = "olm.properties"
2932
installNamespace = "testInstallNamespace"
33+
34+
bundlePathAnnotations = "metadata/annotations.yaml"
35+
bundlePathCSV = "manifests/csv.yaml"
3036
)
3137

3238
func getCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) {
@@ -50,7 +56,7 @@ func getCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) {
5056
func TestRegistryV1SuiteNamespaceNotAvailable(t *testing.T) {
5157
var targetNamespaces []string
5258

53-
t.Log("convert.RegistryV1 Suite Convert")
59+
t.Log("RegistryV1 Suite Convert")
5460
t.Log("It should set the namespaces of the object correctly")
5561
t.Log("It should set the namespace to installnamespace if not available")
5662

@@ -81,7 +87,7 @@ func TestRegistryV1SuiteNamespaceNotAvailable(t *testing.T) {
8187
func TestRegistryV1SuiteNamespaceAvailable(t *testing.T) {
8288
var targetNamespaces []string
8389

84-
t.Log("convert.RegistryV1 Suite Convert")
90+
t.Log("RegistryV1 Suite Convert")
8591
t.Log("It should set the namespaces of the object correctly")
8692
t.Log("It should override namespace if already available")
8793

@@ -115,7 +121,7 @@ func TestRegistryV1SuiteNamespaceAvailable(t *testing.T) {
115121
func TestRegistryV1SuiteNamespaceUnsupportedKind(t *testing.T) {
116122
var targetNamespaces []string
117123

118-
t.Log("convert.RegistryV1 Suite Convert")
124+
t.Log("RegistryV1 Suite Convert")
119125
t.Log("It should set the namespaces of the object correctly")
120126
t.Log("It should error when object is not supported")
121127
t.Log("It should error when unsupported GVK is passed")
@@ -149,7 +155,7 @@ func TestRegistryV1SuiteNamespaceUnsupportedKind(t *testing.T) {
149155
func TestRegistryV1SuiteNamespaceClusterScoped(t *testing.T) {
150156
var targetNamespaces []string
151157

152-
t.Log("convert.RegistryV1 Suite Convert")
158+
t.Log("RegistryV1 Suite Convert")
153159
t.Log("It should set the namespaces of the object correctly")
154160
t.Log("It should not set ns cluster scoped object is passed")
155161
t.Log("It should not error when cluster scoped obj is passed and not set its namespace")
@@ -243,7 +249,7 @@ func getBaseCsvAndService() (v1alpha1.ClusterServiceVersion, corev1.Service) {
243249
}
244250

245251
func TestRegistryV1SuiteGenerateAllNamespace(t *testing.T) {
246-
t.Log("convert.RegistryV1 Suite Convert")
252+
t.Log("RegistryV1 Suite Convert")
247253
t.Log("It should generate objects successfully based on target namespaces")
248254

249255
t.Log("It should convert into plain manifests successfully with AllNamespaces")
@@ -276,7 +282,7 @@ func TestRegistryV1SuiteGenerateAllNamespace(t *testing.T) {
276282
}
277283

278284
func TestRegistryV1SuiteGenerateMultiNamespace(t *testing.T) {
279-
t.Log("convert.RegistryV1 Suite Convert")
285+
t.Log("RegistryV1 Suite Convert")
280286
t.Log("It should generate objects successfully based on target namespaces")
281287

282288
t.Log("It should convert into plain manifests successfully with MultiNamespace")
@@ -309,7 +315,7 @@ func TestRegistryV1SuiteGenerateMultiNamespace(t *testing.T) {
309315
}
310316

311317
func TestRegistryV1SuiteGenerateSingleNamespace(t *testing.T) {
312-
t.Log("convert.RegistryV1 Suite Convert")
318+
t.Log("RegistryV1 Suite Convert")
313319
t.Log("It should generate objects successfully based on target namespaces")
314320

315321
t.Log("It should convert into plain manifests successfully with SingleNamespace")
@@ -342,7 +348,7 @@ func TestRegistryV1SuiteGenerateSingleNamespace(t *testing.T) {
342348
}
343349

344350
func TestRegistryV1SuiteGenerateOwnNamespace(t *testing.T) {
345-
t.Log("convert.RegistryV1 Suite Convert")
351+
t.Log("RegistryV1 Suite Convert")
346352
t.Log("It should generate objects successfully based on target namespaces")
347353

348354
t.Log("It should convert into plain manifests successfully with own namespace")
@@ -375,7 +381,7 @@ func TestRegistryV1SuiteGenerateOwnNamespace(t *testing.T) {
375381
}
376382

377383
func TestRegistryV1SuiteGenerateErrorMultiNamespaceEmpty(t *testing.T) {
378-
t.Log("convert.RegistryV1 Suite Convert")
384+
t.Log("RegistryV1 Suite Convert")
379385
t.Log("It should generate objects successfully based on target namespaces")
380386

381387
t.Log("It should error when multinamespace mode is supported with an empty string in target namespaces")
@@ -399,7 +405,7 @@ func TestRegistryV1SuiteGenerateErrorMultiNamespaceEmpty(t *testing.T) {
399405
}
400406

401407
func TestRegistryV1SuiteGenerateErrorSingleNamespaceDisabled(t *testing.T) {
402-
t.Log("convert.RegistryV1 Suite Convert")
408+
t.Log("RegistryV1 Suite Convert")
403409
t.Log("It should generate objects successfully based on target namespaces")
404410

405411
t.Log("It should error when single namespace mode is disabled with more than one target namespaces")
@@ -423,7 +429,7 @@ func TestRegistryV1SuiteGenerateErrorSingleNamespaceDisabled(t *testing.T) {
423429
}
424430

425431
func TestRegistryV1SuiteGenerateErrorAllNamespaceDisabled(t *testing.T) {
426-
t.Log("convert.RegistryV1 Suite Convert")
432+
t.Log("RegistryV1 Suite Convert")
427433
t.Log("It should generate objects successfully based on target namespaces")
428434

429435
t.Log("It should error when all namespace mode is disabled with target namespace containing an empty string")
@@ -452,7 +458,7 @@ func TestRegistryV1SuiteGenerateErrorAllNamespaceDisabled(t *testing.T) {
452458
}
453459

454460
func TestRegistryV1SuiteReadBundleFileSystem(t *testing.T) {
455-
t.Log("convert.RegistryV1 Suite Convert")
461+
t.Log("RegistryV1 Suite Convert")
456462
t.Log("It should generate objects successfully based on target namespaces")
457463

458464
t.Log("It should read the registry+v1 bundle filesystem correctly")
@@ -466,8 +472,50 @@ func TestRegistryV1SuiteReadBundleFileSystem(t *testing.T) {
466472
require.JSONEq(t, `[{"type":"from-csv-annotations-key","value":"from-csv-annotations-value"},{"type":"from-file-key","value":"from-file-value"}]`, chrt.Metadata.Annotations[olmProperties])
467473
}
468474

475+
func TestParseFSFails(t *testing.T) {
476+
for _, tt := range []struct {
477+
name string
478+
FS fs.FS
479+
}{
480+
{
481+
name: "bundle missing ClusterServiceVersion manifest",
482+
FS: removePaths(newBundleFS(), bundlePathCSV),
483+
}, {
484+
name: "bundle missing metadata/annotations.yaml",
485+
FS: removePaths(newBundleFS(), bundlePathAnnotations),
486+
}, {
487+
name: "bundle missing metadata/ directory",
488+
FS: removePaths(newBundleFS(), "metadata/"),
489+
}, {
490+
name: "bundle missing manifests/ directory",
491+
FS: removePaths(newBundleFS(), "manifests/"),
492+
},
493+
} {
494+
t.Run(tt.name, func(t *testing.T) {
495+
_, err := convert.ParseFS(context.Background(), tt.FS)
496+
require.Error(t, err)
497+
})
498+
}
499+
}
500+
501+
func TestRegistryV1SuiteReadBundleFileSystemFailsOnNoCSV(t *testing.T) {
502+
t.Log("RegistryV1 Suite Convert")
503+
t.Log("It should generate objects successfully based on target namespaces")
504+
505+
t.Log("It should read the registry+v1 bundle filesystem correctly")
506+
t.Log("It should include metadata/properties.yaml and csv.metadata.annotations['olm.properties'] in chart metadata")
507+
fsys := os.DirFS("testdata/combine-properties-bundle")
508+
509+
chrt, err := convert.RegistryV1ToHelmChart(context.Background(), fsys, "", nil)
510+
require.NoError(t, err)
511+
require.NotNil(t, chrt)
512+
require.NotNil(t, chrt.Metadata)
513+
require.Contains(t, chrt.Metadata.Annotations, olmProperties)
514+
require.JSONEq(t, `[{"type":"from-csv-annotations-key","value":"from-csv-annotations-value"},{"type":"from-file-key","value":"from-file-value"}]`, chrt.Metadata.Annotations[olmProperties])
515+
}
516+
469517
func TestRegistryV1SuiteGenerateNoWebhooks(t *testing.T) {
470-
t.Log("convert.RegistryV1 Suite Convert")
518+
t.Log("RegistryV1 Suite Convert")
471519
t.Log("It should generate objects successfully based on target namespaces")
472520

473521
t.Log("It should enforce limitations")
@@ -496,7 +544,7 @@ func TestRegistryV1SuiteGenerateNoWebhooks(t *testing.T) {
496544
}
497545

498546
func TestRegistryV1SuiteGenerateNoAPISerciceDefinitions(t *testing.T) {
499-
t.Log("convert.RegistryV1 Suite Convert")
547+
t.Log("RegistryV1 Suite Convert")
500548
t.Log("It should generate objects successfully based on target namespaces")
501549

502550
t.Log("It should enforce limitations")
@@ -543,3 +591,40 @@ func findObjectByName(name string, result []client.Object) client.Object {
543591
}
544592
return nil
545593
}
594+
595+
func newBundleFS() fstest.MapFS {
596+
annotationsYml := `
597+
annotations:
598+
operators.operatorframework.io.bundle.mediatype.v1: registry+v1
599+
operators.operatorframework.io.bundle.package.v1: test
600+
`
601+
602+
csvYml := `
603+
apiVersion: operators.operatorframework.io/v1alpha1
604+
kind: ClusterServiceVersion
605+
metadata:
606+
name: test.v1.0.0
607+
annotations:
608+
olm.properties: '[{"type":"from-csv-annotations-key", "value":"from-csv-annotations-value"}]'
609+
spec:
610+
installModes:
611+
- type: AllNamespaces
612+
supported: true
613+
`
614+
615+
return fstest.MapFS{
616+
bundlePathAnnotations: &fstest.MapFile{Data: []byte(strings.Trim(annotationsYml, "\n"))},
617+
bundlePathCSV: &fstest.MapFile{Data: []byte(strings.Trim(csvYml, "\n"))},
618+
}
619+
}
620+
621+
func removePaths(mapFs fstest.MapFS, paths ...string) fstest.MapFS {
622+
for k := range mapFs {
623+
for _, path := range paths {
624+
if strings.HasPrefix(k, path) {
625+
delete(mapFs, k)
626+
}
627+
}
628+
}
629+
return mapFs
630+
}

0 commit comments

Comments
 (0)