Skip to content

Commit 53116f6

Browse files
committed
(feat): allow filtering with list of channels
Signed-off-by: everettraven <[email protected]>
1 parent 75bb03d commit 53116f6

9 files changed

+291
-146
lines changed

api/v1alpha1/clusterextension_types.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ type CatalogSource struct {
263263
//+optional
264264
Version string `json:"version,omitempty"`
265265

266-
// channel is an optional reference to a channel belonging to
266+
// channels is an optional reference to a set of channels belonging to
267267
// the package specified in the packageName field.
268268
//
269269
// A "channel" is a package author defined stream of updates for an extension.
@@ -304,10 +304,10 @@ type CatalogSource struct {
304304
//
305305
// [RFC 1123]: https://tools.ietf.org/html/rfc1123
306306
//
307-
//+kubebuilder:validation:MaxLength:=253
308-
//+kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
307+
//+kubebuilder:validation:items:MaxLength:=253
308+
//+kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
309309
//+optional
310-
Channel string `json:"channel,omitempty"`
310+
Channels []string `json:"channels,omitempty"`
311311

312312
// selector is an optional field that can be used
313313
// to filter the set of ClusterCatalogs used in the bundle

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/base/crd/bases/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,9 @@ spec:
199199
catalog is used to configure how information is sourced from a catalog. This field must be defined when sourceType is set to "Catalog",
200200
and must be the only field defined for this sourceType.
201201
properties:
202-
channel:
202+
channels:
203203
description: |-
204-
channel is an optional reference to a channel belonging to
204+
channels is an optional reference to a set of channels belonging to
205205
the package specified in the packageName field.
206206
207207
A "channel" is a package author defined stream of updates for an extension.
@@ -241,9 +241,11 @@ spec:
241241
- --default-channel
242242
243243
[RFC 1123]: https://tools.ietf.org/html/rfc1123
244-
maxLength: 253
245-
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
246-
type: string
244+
items:
245+
maxLength: 253
246+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
247+
type: string
248+
type: array
247249
packageName:
248250
description: |-
249251
packageName is a reference to the name of the package to be installed

internal/controllers/clusterextension_admission_test.go

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -235,29 +235,29 @@ func TestClusterExtensionAdmissionVersion(t *testing.T) {
235235
}
236236

237237
func TestClusterExtensionAdmissionChannel(t *testing.T) {
238-
tooLongError := "spec.source.catalog.channel: Too long: may not be longer than 253"
239-
regexMismatchError := "spec.source.catalog.channel in body should match"
238+
tooLongError := "spec.source.catalog.channels[0]: Too long: may not be longer than 253"
239+
regexMismatchError := "spec.source.catalog.channels[0] in body should match"
240240

241241
testCases := []struct {
242-
name string
243-
channelName string
244-
errMsg string
242+
name string
243+
channels []string
244+
errMsg string
245245
}{
246-
{"no channel name", "", ""},
247-
{"hypen-separated", "hyphenated-name", ""},
248-
{"dot-separated", "dotted.name", ""},
249-
{"includes version", "channel-has-version-1.0.1", ""},
250-
{"long channel name", strings.Repeat("x", 254), tooLongError},
251-
{"spaces", "spaces spaces", regexMismatchError},
252-
{"capitalized", "Capitalized", regexMismatchError},
253-
{"camel case", "camelCase", regexMismatchError},
254-
{"invalid characters", "many/invalid$characters+in_name", regexMismatchError},
255-
{"starts with hyphen", "-start-with-hyphen", regexMismatchError},
256-
{"ends with hyphen", "end-with-hyphen-", regexMismatchError},
257-
{"starts with period", ".start-with-period", regexMismatchError},
258-
{"ends with period", "end-with-period.", regexMismatchError},
259-
{"contains underscore", "some_thing", regexMismatchError},
260-
{"multiple sequential separators", "a.-b", regexMismatchError},
246+
{"no channel name", []string{""}, regexMismatchError},
247+
{"hypen-separated", []string{"hyphenated-name"}, ""},
248+
{"dot-separated", []string{"dotted.name"}, ""},
249+
{"includes version", []string{"channel-has-version-1.0.1"}, ""},
250+
{"long channel name", []string{strings.Repeat("x", 254)}, tooLongError},
251+
{"spaces", []string{"spaces spaces"}, regexMismatchError},
252+
{"capitalized", []string{"Capitalized"}, regexMismatchError},
253+
{"camel case", []string{"camelCase"}, regexMismatchError},
254+
{"invalid characters", []string{"many/invalid$characters+in_name"}, regexMismatchError},
255+
{"starts with hyphen", []string{"-start-with-hyphen"}, regexMismatchError},
256+
{"ends with hyphen", []string{"end-with-hyphen-"}, regexMismatchError},
257+
{"starts with period", []string{".start-with-period"}, regexMismatchError},
258+
{"ends with period", []string{"end-with-period."}, regexMismatchError},
259+
{"contains underscore", []string{"some_thing"}, regexMismatchError},
260+
{"multiple sequential separators", []string{"a.-b"}, regexMismatchError},
261261
}
262262

263263
t.Parallel()
@@ -271,7 +271,7 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) {
271271
SourceType: "Catalog",
272272
Catalog: &ocv1alpha1.CatalogSource{
273273
PackageName: "package",
274-
Channel: tc.channelName,
274+
Channels: tc.channels,
275275
},
276276
},
277277
Install: ocv1alpha1.ClusterExtensionInstallConfig{
@@ -282,7 +282,7 @@ func TestClusterExtensionAdmissionChannel(t *testing.T) {
282282
},
283283
}))
284284
if tc.errMsg == "" {
285-
require.NoError(t, err, "unexpected error for channel %q: %w", tc.channelName, err)
285+
require.NoError(t, err, "unexpected error for channel %q: %w", tc.channels, err)
286286
} else {
287287
require.Error(t, err)
288288
require.Contains(t, err.Error(), tc.errMsg)

internal/controllers/clusterextension_controller.go

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
apimeta "k8s.io/apimachinery/pkg/api/meta"
3131
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3232
"k8s.io/apimachinery/pkg/types"
33+
"k8s.io/apimachinery/pkg/util/sets"
3334
ctrl "sigs.k8s.io/controller-runtime"
3435
"sigs.k8s.io/controller-runtime/pkg/builder"
3536
"sigs.k8s.io/controller-runtime/pkg/cache"
@@ -342,22 +343,27 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1alp
342343
// SetDeprecationStatus will set the appropriate deprecation statuses for a ClusterExtension
343344
// based on the provided bundle
344345
func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundleName string, deprecation *declcfg.Deprecation) {
345-
deprecations := map[string]declcfg.DeprecationEntry{}
346+
deprecations := map[string][]declcfg.DeprecationEntry{}
347+
channelSet := sets.New[string]()
348+
if ext.Spec.Source.Catalog != nil {
349+
for _, channel := range ext.Spec.Source.Catalog.Channels {
350+
channelSet.Insert(channel)
351+
}
352+
}
346353
if deprecation != nil {
347354
for _, entry := range deprecation.Entries {
348355
switch entry.Reference.Schema {
349356
case declcfg.SchemaPackage:
350-
deprecations[ocv1alpha1.TypePackageDeprecated] = entry
357+
deprecations[ocv1alpha1.TypePackageDeprecated] = []declcfg.DeprecationEntry{entry}
351358
case declcfg.SchemaChannel:
352-
if ext.Spec.Source.Catalog.Channel != entry.Reference.Name {
353-
continue
359+
if channelSet.Has(entry.Reference.Name) {
360+
deprecations[ocv1alpha1.TypeChannelDeprecated] = append(deprecations[ocv1alpha1.TypeChannelDeprecated], entry)
354361
}
355-
deprecations[ocv1alpha1.TypeChannelDeprecated] = entry
356362
case declcfg.SchemaBundle:
357363
if bundleName != entry.Reference.Name {
358364
continue
359365
}
360-
deprecations[ocv1alpha1.TypeBundleDeprecated] = entry
366+
deprecations[ocv1alpha1.TypeBundleDeprecated] = []declcfg.DeprecationEntry{entry}
361367
}
362368
}
363369
}
@@ -369,8 +375,10 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundleName string, d
369375
ocv1alpha1.TypeChannelDeprecated,
370376
ocv1alpha1.TypeBundleDeprecated,
371377
} {
372-
if entry, ok := deprecations[conditionType]; ok {
373-
deprecationMessages = append(deprecationMessages, entry.Message)
378+
if entries, ok := deprecations[conditionType]; ok {
379+
for _, entry := range entries {
380+
deprecationMessages = append(deprecationMessages, entry.Message)
381+
}
374382
}
375383
}
376384

@@ -393,11 +401,13 @@ func SetDeprecationStatus(ext *ocv1alpha1.ClusterExtension, bundleName string, d
393401
ocv1alpha1.TypeChannelDeprecated,
394402
ocv1alpha1.TypeBundleDeprecated,
395403
} {
396-
entry, ok := deprecations[conditionType]
404+
entries, ok := deprecations[conditionType]
397405
status, reason, message := metav1.ConditionFalse, ocv1alpha1.ReasonDeprecated, ""
398406
if ok {
399-
status, reason, message = metav1.ConditionTrue, ocv1alpha1.ReasonDeprecated, entry.Message
400-
deprecationMessages = append(deprecationMessages, message)
407+
status, reason = metav1.ConditionTrue, ocv1alpha1.ReasonDeprecated
408+
for _, entry := range entries {
409+
message = fmt.Sprintf("%s\n%s", message, entry.Message)
410+
}
401411
}
402412
apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{
403413
Type: conditionType,

0 commit comments

Comments
 (0)