Skip to content

Commit d320249

Browse files
✨ Adding Availability enum to catalog spec (#421)
* [WIP] Adding Availability enum to catalog spec Signed-off-by: Lalatendu Mohanty <[email protected]> * Removing the finalizer after the catalog is disabled Signed-off-by: Lalatendu Mohanty <[email protected]> * Adding unit tests Signed-off-by: Lalatendu Mohanty <[email protected]> --------- Signed-off-by: Lalatendu Mohanty <[email protected]>
1 parent 1df66b0 commit d320249

File tree

5 files changed

+234
-12
lines changed

5 files changed

+234
-12
lines changed

api/core/v1alpha1/clustercatalog_types.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,17 @@ const (
3333
// Serving reasons
3434
ReasonAvailable = "Available"
3535
ReasonUnavailable = "Unavailable"
36+
ReasonDisabled = "Disabled"
3637

3738
// Progressing reasons
3839
ReasonSucceeded = "Succeeded"
3940
ReasonRetrying = "Retrying"
4041
ReasonBlocked = "Blocked"
4142

4243
MetadataNameLabel = "olm.operatorframework.io/metadata.name"
44+
45+
AvailabilityEnabled = "Enabled"
46+
AvailabilityDisabled = "Disabled"
4347
)
4448

4549
//+kubebuilder:object:root=true
@@ -92,6 +96,20 @@ type ClusterCatalogSpec struct {
9296
// +kubebuilder:default:=0
9397
// +optional
9498
Priority int32 `json:"priority"`
99+
100+
// Availability is an optional field that allows users to define whether the ClusterCatalog is utilized by the operator-controller.
101+
//
102+
// Allowed values are : ["Enabled", "Disabled"].
103+
// If set to "Enabled", the catalog will be used for updates, serving contents, and package installations.
104+
//
105+
// If set to "Disabled", catalogd will stop serving the catalog and the cached data will be removed.
106+
//
107+
// If unspecified, the default value is "Enabled"
108+
//
109+
// +kubebuilder:validation:Enum="Disabled";"Enabled"
110+
// +kubebuilder:default="Enabled"
111+
// +optional
112+
Availability string `json:"availability,omitempty"`
95113
}
96114

97115
// ClusterCatalogStatus defines the observed state of ClusterCatalog

cmd/manager/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,7 @@ func main() {
260260

261261
localStorage = storage.LocalDir{RootDir: storeDir, BaseURL: baseStorageURL}
262262

263+
// Config for the the catalogd web server
263264
catalogServerConfig := serverutil.CatalogServerConfig{
264265
ExternalAddr: externalAddr,
265266
CatalogAddr: catalogServerAddr,

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,21 @@ spec:
5151
spec:
5252
description: ClusterCatalogSpec defines the desired state of ClusterCatalog
5353
properties:
54+
availability:
55+
default: Enabled
56+
description: |-
57+
Availability is an optional field that allows users to define whether the ClusterCatalog is utilized by the operator-controller.
58+
59+
Allowed values are : ["Enabled", "Disabled"].
60+
If set to "Enabled", the catalog will be used for updates, serving contents, and package installations.
61+
62+
If set to "Disabled", catalogd will stop serving the catalog and the cached data will be removed.
63+
64+
If unspecified, the default value is "Enabled"
65+
enum:
66+
- Disabled
67+
- Enabled
68+
type: string
5469
priority:
5570
default: 0
5671
description: |-

internal/controllers/core/clustercatalog_controller.go

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import (
3131
"k8s.io/apimachinery/pkg/util/wait"
3232
ctrl "sigs.k8s.io/controller-runtime"
3333
"sigs.k8s.io/controller-runtime/pkg/client"
34+
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3435
crfinalizer "sigs.k8s.io/controller-runtime/pkg/finalizer"
3536
"sigs.k8s.io/controller-runtime/pkg/log"
3637
"sigs.k8s.io/controller-runtime/pkg/reconcile"
@@ -88,6 +89,7 @@ func (r *ClusterCatalogReconciler) Reconcile(ctx context.Context, req ctrl.Reque
8889
if err := r.Client.Get(ctx, req.NamespacedName, &existingCatsrc); err != nil {
8990
return ctrl.Result{}, client.IgnoreNotFound(err)
9091
}
92+
9193
reconciledCatsrc := existingCatsrc.DeepCopy()
9294
res, reconcileErr := r.reconcile(ctx, reconciledCatsrc)
9395

@@ -156,6 +158,25 @@ func (r *ClusterCatalogReconciler) SetupWithManager(mgr ctrl.Manager) error {
156158
// nolint:unparam
157159
func (r *ClusterCatalogReconciler) reconcile(ctx context.Context, catalog *v1alpha1.ClusterCatalog) (ctrl.Result, error) {
158160
l := log.FromContext(ctx)
161+
// Check if the catalog availability is set to disabled, if true then
162+
// unset content URL, delete it from the cache and set appropriate status
163+
if catalog.Spec.Availability == v1alpha1.AvailabilityDisabled {
164+
// Delete the catalog from local cache
165+
err := r.deleteCatalogCache(ctx, catalog)
166+
if err != nil {
167+
return ctrl.Result{}, err
168+
}
169+
170+
// Set status.conditions[type=Progressing] to False as we are done with
171+
// all that needs to be done with the catalog
172+
updateStatusCatalogDisabled(&catalog.Status, catalog.GetGeneration())
173+
174+
// Remove the fbcDeletionFinalizer as we do not want a finalizer attached to the catalog
175+
// when it is disabled. Because the finalizer serves no purpose now.
176+
controllerutil.RemoveFinalizer(catalog, fbcDeletionFinalizer)
177+
178+
return ctrl.Result{}, nil
179+
}
159180

160181
finalizeResult, err := r.finalizers.Finalize(ctx, catalog)
161182
if err != nil {
@@ -315,6 +336,17 @@ func updateStatusServing(status *v1alpha1.ClusterCatalogStatus, result source.Re
315336
})
316337
}
317338

339+
func updateStatusCatalogDisabled(status *v1alpha1.ClusterCatalogStatus, generation int64) {
340+
progressingCond := metav1.Condition{
341+
Type: v1alpha1.TypeProgressing,
342+
Status: metav1.ConditionFalse,
343+
Reason: v1alpha1.ReasonDisabled,
344+
Message: "Catalog availability is set to Disabled",
345+
ObservedGeneration: generation,
346+
}
347+
meta.SetStatusCondition(&status.Conditions, progressingCond)
348+
}
349+
318350
func updateStatusNotServing(status *v1alpha1.ClusterCatalogStatus, generation int64) {
319351
status.ResolvedSource = nil
320352
status.ContentURL = ""
@@ -358,18 +390,8 @@ func (r *ClusterCatalogReconciler) setupFinalizers() error {
358390
if !ok {
359391
panic("could not convert object to clusterCatalog")
360392
}
361-
if err := r.Storage.Delete(catalog.Name); err != nil {
362-
updateStatusProgressing(&catalog.Status, catalog.GetGeneration(), err)
363-
return crfinalizer.Result{StatusUpdated: true}, err
364-
}
365-
updateStatusNotServing(&catalog.Status, catalog.GetGeneration())
366-
if err := r.Unpacker.Cleanup(ctx, catalog); err != nil {
367-
updateStatusProgressing(&catalog.Status, catalog.GetGeneration(), err)
368-
return crfinalizer.Result{StatusUpdated: true}, err
369-
}
370-
371-
r.deleteStoredCatalog(catalog.Name)
372-
return crfinalizer.Result{StatusUpdated: true}, nil
393+
err := r.deleteCatalogCache(ctx, catalog)
394+
return crfinalizer.Result{StatusUpdated: true}, err
373395
}))
374396
if err != nil {
375397
return err
@@ -383,3 +405,17 @@ func (r *ClusterCatalogReconciler) deleteStoredCatalog(catalogName string) {
383405
defer r.storedCatalogsMu.Unlock()
384406
delete(r.storedCatalogs, catalogName)
385407
}
408+
409+
func (r *ClusterCatalogReconciler) deleteCatalogCache(ctx context.Context, catalog *v1alpha1.ClusterCatalog) error {
410+
if err := r.Storage.Delete(catalog.Name); err != nil {
411+
updateStatusProgressing(&catalog.Status, catalog.GetGeneration(), err)
412+
return err
413+
}
414+
updateStatusNotServing(&catalog.Status, catalog.GetGeneration())
415+
if err := r.Unpacker.Cleanup(ctx, catalog); err != nil {
416+
updateStatusProgressing(&catalog.Status, catalog.GetGeneration(), err)
417+
return err
418+
}
419+
r.deleteStoredCatalog(catalog.Name)
420+
return nil
421+
}

internal/controllers/core/clustercatalog_controller_test.go

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,158 @@ func TestCatalogdControllerReconcile(t *testing.T) {
589589
},
590590
},
591591
},
592+
{
593+
name: "catalog availability set to disabled, ContentURL should get unset",
594+
source: &MockSource{
595+
result: &source.Result{
596+
State: source.StateUnpacked,
597+
FS: &fstest.MapFS{},
598+
},
599+
},
600+
store: &MockStore{},
601+
catalog: &catalogdv1alpha1.ClusterCatalog{
602+
ObjectMeta: metav1.ObjectMeta{
603+
Name: "catalog",
604+
},
605+
Spec: catalogdv1alpha1.ClusterCatalogSpec{
606+
Source: catalogdv1alpha1.CatalogSource{
607+
Type: catalogdv1alpha1.SourceTypeImage,
608+
Image: &catalogdv1alpha1.ImageSource{
609+
Ref: "my.org/someimage:latest",
610+
},
611+
},
612+
Availability: "Disabled",
613+
},
614+
Status: catalogdv1alpha1.ClusterCatalogStatus{
615+
ContentURL: "URL",
616+
LastUnpacked: metav1.Time{},
617+
ResolvedSource: &catalogdv1alpha1.ResolvedCatalogSource{
618+
Type: catalogdv1alpha1.SourceTypeImage,
619+
Image: &catalogdv1alpha1.ResolvedImageSource{
620+
Ref: "",
621+
},
622+
},
623+
Conditions: []metav1.Condition{
624+
{
625+
Type: catalogdv1alpha1.TypeServing,
626+
Status: metav1.ConditionTrue,
627+
Reason: catalogdv1alpha1.ReasonAvailable,
628+
},
629+
{
630+
Type: catalogdv1alpha1.TypeProgressing,
631+
Status: metav1.ConditionFalse,
632+
Reason: catalogdv1alpha1.ReasonSucceeded,
633+
},
634+
},
635+
},
636+
},
637+
expectedCatalog: &catalogdv1alpha1.ClusterCatalog{
638+
ObjectMeta: metav1.ObjectMeta{
639+
Name: "catalog",
640+
},
641+
Spec: catalogdv1alpha1.ClusterCatalogSpec{
642+
Source: catalogdv1alpha1.CatalogSource{
643+
Type: catalogdv1alpha1.SourceTypeImage,
644+
Image: &catalogdv1alpha1.ImageSource{
645+
Ref: "my.org/someimage:latest",
646+
},
647+
},
648+
Availability: "Disabled",
649+
},
650+
Status: catalogdv1alpha1.ClusterCatalogStatus{
651+
ContentURL: "",
652+
Conditions: []metav1.Condition{
653+
{
654+
Type: catalogdv1alpha1.TypeServing,
655+
Status: metav1.ConditionFalse,
656+
Reason: catalogdv1alpha1.ReasonUnavailable,
657+
},
658+
{
659+
Type: catalogdv1alpha1.TypeProgressing,
660+
Status: metav1.ConditionFalse,
661+
Reason: catalogdv1alpha1.ReasonDisabled,
662+
},
663+
},
664+
},
665+
},
666+
},
667+
{
668+
name: "catalog availability set to disabled, finalizer should get removed",
669+
source: &MockSource{
670+
result: &source.Result{
671+
State: source.StateUnpacked,
672+
FS: &fstest.MapFS{},
673+
},
674+
},
675+
store: &MockStore{},
676+
catalog: &catalogdv1alpha1.ClusterCatalog{
677+
ObjectMeta: metav1.ObjectMeta{
678+
Name: "catalog",
679+
Finalizers: []string{fbcDeletionFinalizer},
680+
},
681+
Spec: catalogdv1alpha1.ClusterCatalogSpec{
682+
Source: catalogdv1alpha1.CatalogSource{
683+
Type: catalogdv1alpha1.SourceTypeImage,
684+
Image: &catalogdv1alpha1.ImageSource{
685+
Ref: "my.org/someimage:latest",
686+
},
687+
},
688+
Availability: "Disabled",
689+
},
690+
Status: catalogdv1alpha1.ClusterCatalogStatus{
691+
ContentURL: "URL",
692+
LastUnpacked: metav1.Time{},
693+
ResolvedSource: &catalogdv1alpha1.ResolvedCatalogSource{
694+
Type: catalogdv1alpha1.SourceTypeImage,
695+
Image: &catalogdv1alpha1.ResolvedImageSource{
696+
Ref: "",
697+
},
698+
},
699+
Conditions: []metav1.Condition{
700+
{
701+
Type: catalogdv1alpha1.TypeServing,
702+
Status: metav1.ConditionTrue,
703+
Reason: catalogdv1alpha1.ReasonAvailable,
704+
},
705+
{
706+
Type: catalogdv1alpha1.TypeProgressing,
707+
Status: metav1.ConditionFalse,
708+
Reason: catalogdv1alpha1.ReasonSucceeded,
709+
},
710+
},
711+
},
712+
},
713+
expectedCatalog: &catalogdv1alpha1.ClusterCatalog{
714+
ObjectMeta: metav1.ObjectMeta{
715+
Name: "catalog",
716+
Finalizers: []string{},
717+
},
718+
Spec: catalogdv1alpha1.ClusterCatalogSpec{
719+
Source: catalogdv1alpha1.CatalogSource{
720+
Type: catalogdv1alpha1.SourceTypeImage,
721+
Image: &catalogdv1alpha1.ImageSource{
722+
Ref: "my.org/someimage:latest",
723+
},
724+
},
725+
Availability: "Disabled",
726+
},
727+
Status: catalogdv1alpha1.ClusterCatalogStatus{
728+
ContentURL: "",
729+
Conditions: []metav1.Condition{
730+
{
731+
Type: catalogdv1alpha1.TypeServing,
732+
Status: metav1.ConditionFalse,
733+
Reason: catalogdv1alpha1.ReasonUnavailable,
734+
},
735+
{
736+
Type: catalogdv1alpha1.TypeProgressing,
737+
Status: metav1.ConditionFalse,
738+
Reason: catalogdv1alpha1.ReasonDisabled,
739+
},
740+
},
741+
},
742+
},
743+
},
592744
} {
593745
t.Run(tt.name, func(t *testing.T) {
594746
reconciler := &ClusterCatalogReconciler{

0 commit comments

Comments
 (0)