Skip to content
This repository was archived by the owner on Mar 3, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 72 additions & 7 deletions config/crd/bases/core.catalogd.io_catalogsources.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,14 +52,79 @@ spec:
status:
description: CatalogSourceStatus defines the observed state of CatalogSource
properties:
latestImagePoll:
description: The last time the image has been polled to ensure the
image is up-to-date
format: date-time
type: string
required:
- latestImagePoll
conditions:
description: Conditions store the status conditions of the CatalogSource
instances
items:
description: "Condition contains details for one aspect of the current
state of this API Resource. --- This struct is intended for direct
use as an array at the field path .status.conditions. For example,
\n type FooStatus struct{ // Represents the observations of a
foo's current state. // Known .status.conditions.type are: \"Available\",
\"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
// +listType=map // +listMapKey=type Conditions []metav1.Condition
`json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
properties:
lastTransitionTime:
description: lastTransitionTime is the last time the condition
transitioned from one status to another. This should be when
the underlying condition changed. If that is not known, then
using the time when the API field changed is acceptable.
format: date-time
type: string
message:
description: message is a human readable message indicating
details about the transition. This may be an empty string.
maxLength: 32768
type: string
observedGeneration:
description: observedGeneration represents the .metadata.generation
that the condition was set based upon. For instance, if .metadata.generation
is currently 12, but the .status.conditions[x].observedGeneration
is 9, the condition is out of date with respect to the current
state of the instance.
format: int64
minimum: 0
type: integer
reason:
description: reason contains a programmatic identifier indicating
the reason for the condition's last transition. Producers
of specific condition types may define expected values and
meanings for this field, and whether the values are considered
a guaranteed API. The value should be a CamelCase string.
This field may not be empty.
maxLength: 1024
minLength: 1
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
type: string
status:
description: status of the condition, one of True, False, Unknown.
enum:
- "True"
- "False"
- Unknown
type: string
type:
description: type of condition in CamelCase or in foo.example.com/CamelCase.
--- Many .condition.type values are consistent across resources
like Available, but because arbitrary conditions can be useful
(see .node.status.conditions), the ability to deconflict is
important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
maxLength: 316
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
type: string
required:
- lastTransitionTime
- message
- reason
- status
- type
type: object
type: array
type: object
type: object
served: true
storage: true
subresources:
status: {}
13 changes: 10 additions & 3 deletions pkg/apis/core/v1beta1/catalogsource_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,20 @@ import (
"sigs.k8s.io/apiserver-runtime/pkg/builder/resource/resourcestrategy"
)

const (
TypeReady = "Ready"

ReasonContentsAvailable = "ContentsAvailable"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do in a follow up too. But having all the conditions and reasons added to an array eventually would help in testing condition invariants as in here in future.

ReasonUnpackError = "UnpackError"
)

// +genclient
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object

// CatalogSource
// +k8s:openapi-gen=true
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:subresource:status
type CatalogSource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down Expand Up @@ -108,9 +116,8 @@ func (in *CatalogSourceList) GetListMeta() *metav1.ListMeta {

// CatalogSourceStatus defines the observed state of CatalogSource
type CatalogSourceStatus struct {

// The last time the image has been polled to ensure the image is up-to-date
LatestImagePoll *metav1.Time `json:"latestImagePoll"`
// Conditions store the status conditions of the CatalogSource instances
Conditions []metav1.Condition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type" protobuf:"bytes,1,rep,name=conditions"`
}

func (in CatalogSourceStatus) SubResourceName() string {
Expand Down
9 changes: 6 additions & 3 deletions pkg/apis/core/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

57 changes: 56 additions & 1 deletion pkg/controllers/core/catalogsource_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,17 @@ import (
batchv1 "k8s.io/api/batch/v1"
v1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlutil "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"

corev1beta1 "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
)
Expand Down Expand Up @@ -72,11 +75,19 @@ func (r *CatalogSourceReconciler) Reconcile(ctx context.Context, req ctrl.Reques
if err != nil {
if errors.IsNotFound(err) {
if err = r.createUnpackJob(ctx, catalogSource); err != nil {
updateStatusError(&catalogSource, err)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
return ctrl.Result{}, err
}
// after creating the job requeue
return ctrl.Result{Requeue: true}, nil
}
updateStatusError(&catalogSource, err)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
return ctrl.Result{}, err
}

Expand All @@ -86,24 +97,68 @@ func (r *CatalogSourceReconciler) Reconcile(ctx context.Context, req ctrl.Reques
if corev1beta1.IsUnpackPhaseError(err) {
return ctrl.Result{RequeueAfter: 10 * time.Second}, nil
}
updateStatusError(&catalogSource, err)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
return ctrl.Result{}, err
}

// TODO: Can we create these resources in parallel using goroutines?
if err := r.buildPackages(ctx, declCfg, catalogSource); err != nil {
updateStatusError(&catalogSource, err)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
return ctrl.Result{}, err
}

if err := r.buildBundleMetadata(ctx, declCfg, catalogSource); err != nil {
Comment thread
everettraven marked this conversation as resolved.
updateStatusError(&catalogSource, err)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
return ctrl.Result{}, err
}

// update CatalogSource status as "Ready" since at this point
// all catalog content should be available on cluster
updateStatusReady(&catalogSource)
if err := r.Client.Status().Update(ctx, &catalogSource); err != nil {
return ctrl.Result{}, fmt.Errorf("updating catalogsource status: %v", err)
}
Comment on lines +127 to +129
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For follow-up: we should check to see if the status changed to avoid unecessary update calls. I think this may be covered by #5

return ctrl.Result{}, nil
}

func updateStatusReady(catalogSource *corev1beta1.CatalogSource) {
meta.SetStatusCondition(&catalogSource.Status.Conditions, metav1.Condition{
Type: corev1beta1.TypeReady,
Reason: corev1beta1.ReasonContentsAvailable,
Status: metav1.ConditionTrue,
Message: "catalog contents have been unpacked and are available on cluster",
})
}
Comment on lines +133 to +140
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In all of the other scenarios when we return an error from Reconcile, we need to set this condition somehow. For now, I think it's fine to always say Status: false with the error message.

But eventually we'll need to consider what happens when an old version of the catalog is available but we had a problem unpacking/syncing the new version.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is partially the goal of #6 but if we want it loosely implemented in this PR I am happy to update it

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'd propose we at least do the big picture invariant of "all conditions are updated somehow in every Reconcile pass"

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be resolved as of bf10f4f


func updateStatusError(catalogSource *corev1beta1.CatalogSource, err error) {
meta.SetStatusCondition(&catalogSource.Status.Conditions, metav1.Condition{
Type: corev1beta1.TypeReady,
Status: metav1.ConditionFalse,
Reason: corev1beta1.ReasonUnpackError,
Message: err.Error(),
})
}

// SetupWithManager sets up the controller with the Manager.
func (r *CatalogSourceReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&corev1beta1.CatalogSource{}).
// TODO: Due to us not having proper error handling,
// not having this results in the controller getting into
// an error state because once we update the status it requeues
// and then errors out when trying to create all the Packages again
// even though they already exist. This should be resolved by the fix
// for https://github.com/operator-framework/catalogd/issues/6. The fix for
// #6 should also remove the usage of `builder.WithPredicates(predicate.GenerationChangedPredicate{})`
For(&corev1beta1.CatalogSource{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't do this. If someone/something changes the status out from under us, we should go back and re-reconcile.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently since we don't have good error handling, not having this results in the controller getting into an error state because once we update the status it requeues and then errors out when trying to create all the Packages again even though they already exist.

Would you be okay with this going in for now and fixing it along with the changes needed for #6 ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, can you add a TODO comment, summarizing that in the code?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And maybe it makes sense to have a separate issue about just that particular problem?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be resolved by 80fbb30

Complete(r)
}

Expand Down