Skip to content

Commit 0031546

Browse files
authored
Merge pull request #69 from exdx/feat/custom-categories
feat: add support for custom categories
2 parents d5d5493 + c880f35 commit 0031546

7 files changed

+482
-3
lines changed

pkg/validation/internal/operatorhub.go

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package internal
22

33
import (
4+
"encoding/json"
45
"fmt"
6+
"io/ioutil"
57
"net/mail"
68
"net/url"
9+
"os"
10+
"path/filepath"
711
"strings"
812

913
"github.com/blang/semver"
@@ -148,12 +152,57 @@ func validateHubCSVSpec(csv v1alpha1.ClusterServiceVersion) []error {
148152
if categories, ok := csv.ObjectMeta.Annotations["categories"]; ok {
149153
categorySlice := strings.Split(categories, ",")
150154

151-
for _, category := range categorySlice {
152-
if _, ok := validCategories[category]; !ok {
153-
errs = append(errs, fmt.Errorf("csv.Metadata.Annotations.Categories %s is not a valid category", category))
155+
// use custom categories for validation if provided
156+
customCategoriesPath := os.Getenv("OPERATOR_BUNDLE_CATEGORIES")
157+
if customCategoriesPath != "" {
158+
customCategories, err := extractCategories(customCategoriesPath)
159+
if err != nil {
160+
errs = append(errs, fmt.Errorf("could not extract custom categories from categories %#v: %s", customCategories, err))
161+
return errs
162+
}
163+
for _, category := range categorySlice {
164+
if _, ok := customCategories[category]; !ok {
165+
errs = append(errs, fmt.Errorf("csv.Metadata.Annotations.Categories %s is not a valid custom category", category))
166+
}
167+
}
168+
} else {
169+
// use default categories
170+
for _, category := range categorySlice {
171+
if _, ok := validCategories[category]; !ok {
172+
errs = append(errs, fmt.Errorf("csv.Metadata.Annotations.Categories %s is not a valid category", category))
173+
}
154174
}
155175
}
156176
}
157177

158178
return errs
159179
}
180+
181+
type categories struct {
182+
Contents []string `json:"categories"`
183+
}
184+
185+
// extractCategories reads a custom categories file and returns the contents in a map[string]struct{}
186+
func extractCategories(path string) (map[string]struct{}, error) {
187+
path, err := filepath.Abs(path)
188+
if err != nil {
189+
return nil, fmt.Errorf("finding category file: %w", err)
190+
}
191+
192+
data, err := ioutil.ReadFile(path)
193+
if err != nil {
194+
return nil, fmt.Errorf("reading category file: %w", err)
195+
}
196+
197+
cat := categories{}
198+
err = json.Unmarshal(data, &cat)
199+
if err != nil {
200+
return nil, fmt.Errorf("unmarshaling category file: %w", err)
201+
}
202+
203+
customCategories := make(map[string]struct{})
204+
for _, c := range cat.Contents {
205+
customCategories[c] = struct{}{}
206+
}
207+
return customCategories, nil
208+
}

pkg/validation/internal/operatorhub_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package internal
22

33
import (
4+
"os"
45
"testing"
56

67
"github.com/operator-framework/api/pkg/manifests"
@@ -58,3 +59,73 @@ func TestValidateBundleOperatorHub(t *testing.T) {
5859
}
5960
}
6061
}
62+
63+
func TestCustomCategories(t *testing.T) {
64+
var table = []struct {
65+
description string
66+
directory string
67+
hasError bool
68+
errStrings []string
69+
custom bool
70+
}{
71+
{
72+
description: "valid bundle custom categories",
73+
directory: "./testdata/valid_bundle_custom_categories",
74+
hasError: false,
75+
custom: true,
76+
},
77+
{
78+
description: "valid bundle standard categories",
79+
directory: "./testdata/valid_bundle",
80+
hasError: false,
81+
custom: false,
82+
},
83+
}
84+
85+
for _, tt := range table {
86+
t.Logf("%s", tt.description)
87+
if tt.custom {
88+
os.Setenv("OPERATOR_BUNDLE_CATEGORIES", "./testdata/categories.json")
89+
} else {
90+
os.Setenv("OPERATOR_BUNDLE_CATEGORIES", "")
91+
}
92+
93+
// Validate the bundle object
94+
bundle, err := manifests.GetBundleFromDir(tt.directory)
95+
require.NoError(t, err)
96+
97+
results := OperatorHubValidator.Validate(bundle)
98+
99+
if len(results) > 0 {
100+
require.Equal(t, results[0].HasError(), tt.hasError)
101+
if results[0].HasError() {
102+
require.Equal(t, len(tt.errStrings), len(results[0].Errors))
103+
for _, err := range results[0].Errors {
104+
errString := err.Error()
105+
require.Contains(t, tt.errStrings, errString)
106+
}
107+
}
108+
}
109+
}
110+
}
111+
112+
func TestExtractCategories(t *testing.T) {
113+
path := "./testdata/categories.json"
114+
categories, err := extractCategories(path)
115+
if err != nil {
116+
t.Fatalf("extracting categories.json: %s", err)
117+
}
118+
119+
expected := map[string]struct{}{
120+
"Cloud Pak": {},
121+
"Registry": {},
122+
"MyCoolThing": {},
123+
"This/Or & That": {},
124+
}
125+
126+
for key := range categories {
127+
if _, ok := expected[key]; !ok {
128+
t.Fatalf("did not find key %s", key)
129+
}
130+
}
131+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"categories":[
3+
"Cloud Pak",
4+
"Registry",
5+
"MyCoolThing",
6+
"This/Or & That"
7+
]
8+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
apiVersion: apiextensions.k8s.io/v1beta1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: etcdbackups.etcd.database.coreos.com
5+
spec:
6+
group: etcd.database.coreos.com
7+
names:
8+
kind: EtcdBackup
9+
listKind: EtcdBackupList
10+
plural: etcdbackups
11+
singular: etcdbackup
12+
scope: Namespaced
13+
version: v1beta2
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
apiVersion: apiextensions.k8s.io/v1beta1
2+
kind: CustomResourceDefinition
3+
metadata:
4+
name: etcdclusters.etcd.database.coreos.com
5+
spec:
6+
group: etcd.database.coreos.com
7+
names:
8+
kind: EtcdCluster
9+
listKind: EtcdClusterList
10+
plural: etcdclusters
11+
shortNames:
12+
- etcdclus
13+
- etcd
14+
singular: etcdcluster
15+
scope: Namespaced
16+
version: v1beta2

0 commit comments

Comments
 (0)