Skip to content

Commit 1622df1

Browse files
committed
propagate conditions from CER to cluster extension
* added `.status.activeRevisions` for tracking * `Progressing` is mirrored from the counterpart of the latest revision * `Available` is mirrored from the counterpart of the latest installed revision * If the latest revision is rolled out, but not yet available, its `Available` condition is mirrored under its `activeRevision` entry.
1 parent 3ceb4c7 commit 1622df1

File tree

11 files changed

+317
-43
lines changed

11 files changed

+317
-43
lines changed

api/v1/clusterextension_types.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,14 @@ type BundleMetadata struct {
469469
Version string `json:"version"`
470470
}
471471

472+
type RevisionStatus struct {
473+
Name string `json:"name"`
474+
// +listType=map
475+
// +listMapKey=type
476+
// +optional
477+
Conditions []metav1.Condition `json:"conditions,omitempty"`
478+
}
479+
472480
// ClusterExtensionStatus defines the observed state of a ClusterExtension.
473481
type ClusterExtensionStatus struct {
474482
// The set of condition types which apply to all spec.source variations are Installed and Progressing.
@@ -498,6 +506,12 @@ type ClusterExtensionStatus struct {
498506
//
499507
// +optional
500508
Install *ClusterExtensionInstallStatus `json:"install,omitempty"`
509+
510+
// +listType=map
511+
// +listMapKey=name
512+
// +optional
513+
// <opcon:experimental>
514+
ActiveRevisions []RevisionStatus `json:"activeRevisions,omitempty"`
501515
}
502516

503517
// ClusterExtensionInstallStatus is a representation of the status of the identified bundle.
@@ -516,7 +530,7 @@ type ClusterExtensionInstallStatus struct {
516530
// +kubebuilder:subresource:status
517531
// +kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name`
518532
// +kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version`
519-
// +kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status`
533+
// +kubebuilder:printcolumn:name="Available",type=string,JSONPath=`.status.conditions[?(@.type=='Available')].status`
520534
// +kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status`
521535
// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp`
522536

api/v1/zz_generated.deepcopy.go

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

helm/olmv1/base/operator-controller/crd/experimental/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ spec:
2222
- jsonPath: .status.install.bundle.version
2323
name: Version
2424
type: string
25-
- jsonPath: .status.conditions[?(@.type=='Installed')].status
26-
name: Installed
25+
- jsonPath: .status.conditions[?(@.type=='Available')].status
26+
name: Available
2727
type: string
2828
- jsonPath: .status.conditions[?(@.type=='Progressing')].status
2929
name: Progressing
@@ -505,6 +505,78 @@ spec:
505505
description: status is an optional field that defines the observed state
506506
of the ClusterExtension.
507507
properties:
508+
activeRevisions:
509+
items:
510+
properties:
511+
conditions:
512+
items:
513+
description: Condition contains details for one aspect of
514+
the current state of this API Resource.
515+
properties:
516+
lastTransitionTime:
517+
description: |-
518+
lastTransitionTime is the last time the condition transitioned from one status to another.
519+
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
520+
format: date-time
521+
type: string
522+
message:
523+
description: |-
524+
message is a human readable message indicating details about the transition.
525+
This may be an empty string.
526+
maxLength: 32768
527+
type: string
528+
observedGeneration:
529+
description: |-
530+
observedGeneration represents the .metadata.generation that the condition was set based upon.
531+
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
532+
with respect to the current state of the instance.
533+
format: int64
534+
minimum: 0
535+
type: integer
536+
reason:
537+
description: |-
538+
reason contains a programmatic identifier indicating the reason for the condition's last transition.
539+
Producers of specific condition types may define expected values and meanings for this field,
540+
and whether the values are considered a guaranteed API.
541+
The value should be a CamelCase string.
542+
This field may not be empty.
543+
maxLength: 1024
544+
minLength: 1
545+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
546+
type: string
547+
status:
548+
description: status of the condition, one of True, False,
549+
Unknown.
550+
enum:
551+
- "True"
552+
- "False"
553+
- Unknown
554+
type: string
555+
type:
556+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
557+
maxLength: 316
558+
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])$
559+
type: string
560+
required:
561+
- lastTransitionTime
562+
- message
563+
- reason
564+
- status
565+
- type
566+
type: object
567+
type: array
568+
x-kubernetes-list-map-keys:
569+
- type
570+
x-kubernetes-list-type: map
571+
name:
572+
type: string
573+
required:
574+
- name
575+
type: object
576+
type: array
577+
x-kubernetes-list-map-keys:
578+
- name
579+
x-kubernetes-list-type: map
508580
conditions:
509581
description: |-
510582
The set of condition types which apply to all spec.source variations are Installed and Progressing.

helm/olmv1/base/operator-controller/crd/standard/olm.operatorframework.io_clusterextensions.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ spec:
2222
- jsonPath: .status.install.bundle.version
2323
name: Version
2424
type: string
25-
- jsonPath: .status.conditions[?(@.type=='Installed')].status
26-
name: Installed
25+
- jsonPath: .status.conditions[?(@.type=='Available')].status
26+
name: Available
2727
type: string
2828
- jsonPath: .status.conditions[?(@.type=='Progressing')].status
2929
name: Progressing

internal/operator-controller/applier/boxcutter.go

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,6 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
317317
return false, "New revision created", nil
318318
} else if progressingCondition != nil && progressingCondition.Status == metav1.ConditionTrue {
319319
return false, progressingCondition.Message, nil
320-
} else if availableCondition != nil && availableCondition.Status != metav1.ConditionTrue {
321-
return false, "", errors.New(availableCondition.Message)
322320
} else if succeededCondition != nil && succeededCondition.Status != metav1.ConditionTrue {
323321
return false, succeededCondition.Message, nil
324322
}

internal/operator-controller/controllers/clusterextension_controller.go

Lines changed: 40 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -297,32 +297,43 @@ func (r *ClusterExtensionReconciler) reconcile(ctx context.Context, ext *ocv1.Cl
297297
// to ensure exponential backoff can occur:
298298
// - Permission errors (it is not possible to watch changes to permissions.
299299
// The only way to eventually recover from permission errors is to keep retrying).
300-
rolloutSucceeded, rolloutStatus, err := r.Applier.Apply(ctx, imageFS, ext, objLbls, storeLbls)
301-
302-
// Set installed status
303-
if rolloutSucceeded {
304-
revisionStates = &RevisionStates{Installed: resolvedRevisionMetadata}
305-
} else if err == nil && revisionStates.Installed == nil && len(revisionStates.RollingOut) == 0 {
306-
revisionStates = &RevisionStates{RollingOut: []*RevisionMetadata{resolvedRevisionMetadata}}
307-
}
308-
setInstalledStatusFromRevisionStates(ext, revisionStates)
309-
310-
// If there was an error applying the resolved bundle,
311-
// report the error via the Progressing condition.
312-
if err != nil {
300+
if _, _, err := r.Applier.Apply(ctx, imageFS, ext, objLbls, storeLbls); err != nil {
301+
// If there was an error applying the resolved bundle,
302+
// report the error via the Progressing condition.
313303
setStatusProgressing(ext, wrapErrorWithResolutionInfo(resolvedRevisionMetadata.BundleMetadata, err))
314304
return ctrl.Result{}, err
315-
} else if !rolloutSucceeded {
316-
apimeta.SetStatusCondition(&ext.Status.Conditions, metav1.Condition{
317-
Type: ocv1.TypeProgressing,
318-
Status: metav1.ConditionTrue,
319-
Reason: ocv1.ReasonRolloutInProgress,
320-
Message: rolloutStatus,
321-
ObservedGeneration: ext.GetGeneration(),
322-
})
323-
} else {
324-
setStatusProgressing(ext, nil)
325305
}
306+
307+
// Mirror Available/Progressing conditions from the installed revision
308+
if i := revisionStates.Installed; i != nil {
309+
for _, cndType := range []string{ocv1.ClusterExtensionRevisionTypeAvailable, ocv1.ClusterExtensionRevisionTypeProgressing} {
310+
cnd := *apimeta.FindStatusCondition(i.Conditions, cndType)
311+
apimeta.SetStatusCondition(&ext.Status.Conditions, cnd)
312+
}
313+
ext.Status.Install = &ocv1.ClusterExtensionInstallStatus{
314+
Bundle: i.BundleMetadata,
315+
}
316+
ext.Status.ActiveRevisions = []ocv1.RevisionStatus{{Name: i.RevName}}
317+
}
318+
for idx, r := range revisionStates.RollingOut {
319+
rs := ocv1.RevisionStatus{Name: r.RevName}
320+
// Mirror Progressing condition from the latest active revision
321+
if idx == len(revisionStates.RollingOut)-1 {
322+
pcnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
323+
if pcnd != nil {
324+
apimeta.SetStatusCondition(&ext.Status.Conditions, *pcnd)
325+
}
326+
if acnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable); pcnd.Status == metav1.ConditionFalse && acnd != nil && acnd.Status != metav1.ConditionTrue {
327+
apimeta.SetStatusCondition(&rs.Conditions, *acnd)
328+
}
329+
}
330+
if len(ext.Status.ActiveRevisions) == 0 {
331+
ext.Status.ActiveRevisions = []ocv1.RevisionStatus{rs}
332+
} else {
333+
ext.Status.ActiveRevisions = append(ext.Status.ActiveRevisions, rs)
334+
}
335+
}
336+
326337
return ctrl.Result{}, nil
327338
}
328339

@@ -474,9 +485,11 @@ func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crh
474485
}
475486

476487
type RevisionMetadata struct {
488+
RevName string
477489
Package string
478490
Image string
479491
ocv1.BundleMetadata
492+
Conditions []metav1.Condition
480493
}
481494

482495
type RevisionStates struct {
@@ -553,8 +566,10 @@ func (d *BoxcutterRevisionStatesGetter) GetRevisionStates(ctx context.Context, e
553566
// is fairly decoupled from this code where we get the annotations back out. We may want to co-locate
554567
// the set/get logic a bit better to make it more maintainable and less likely to get out of sync.
555568
rm := &RevisionMetadata{
556-
Package: rev.Labels[labels.PackageNameKey],
557-
Image: rev.Annotations[labels.BundleReferenceKey],
569+
RevName: rev.Name,
570+
Package: rev.Labels[labels.PackageNameKey],
571+
Image: rev.Annotations[labels.BundleReferenceKey],
572+
Conditions: rev.Status.Conditions,
558573
BundleMetadata: ocv1.BundleMetadata{
559574
Name: rev.Annotations[labels.BundleNameKey],
560575
Version: rev.Annotations[labels.BundleVersionKey],

internal/operator-controller/controllers/clusterextensionrevision_controller.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"strings"
1111
"time"
1212

13+
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
1314
appsv1 "k8s.io/api/apps/v1"
1415
corev1 "k8s.io/api/core/v1"
1516
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
@@ -122,6 +123,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
122123
return c.teardown(ctx, rev, revision)
123124
}
124125

126+
revVersion := rev.GetAnnotations()[labels.BundleVersionKey]
125127
//
126128
// Reconcile
127129
//
@@ -138,7 +140,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
138140
rev.MarkAsProgressing(ocv1.ClusterExtensionRevisionReasonReconcileFailure, werr.Error())
139141
return ctrl.Result{}, werr
140142
}
141-
rev.MarkAsProgressing(ocv1.ClusterExtensionRevisionReasonRolloutInProgress, "Revision is being rolled out.")
143+
rev.MarkAsProgressing(ocv1.ClusterExtensionRevisionReasonRolloutInProgress, fmt.Sprintf("Revision %s is being rolled out.", revVersion))
142144
}
143145

144146
rres, err := c.RevisionEngine.Reconcile(ctx, *revision, opts...)
@@ -225,7 +227,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
225227

226228
if !rres.InTransistion() {
227229
// we have rolled out all objects in all phases, not interested in probes here
228-
rev.MarkAsNotProgressing(ocv1.ClusterExtensionRevisionReasonRolledOut, "Revision is rolled out.")
230+
rev.MarkAsNotProgressing(ocv1.ClusterExtensionRevisionReasonRolledOut, fmt.Sprintf("Revision %s is rolled out.", revVersion))
229231
}
230232

231233
//nolint:nestif
@@ -284,7 +286,7 @@ func (c *ClusterExtensionRevisionReconciler) reconcile(ctx context.Context, rev
284286
if len(probeFailureMsgs) > 0 {
285287
rev.MarkAsUnavailable(ocv1.ClusterExtensionRevisionReasonProbeFailure, strings.Join(probeFailureMsgs, "\n"))
286288
} else {
287-
rev.MarkAsUnavailable(ocv1.ClusterExtensionRevisionReasonIncomplete, "Revision has not been rolled out completely.")
289+
rev.MarkAsUnavailable(ocv1.ClusterExtensionRevisionReasonIncomplete, fmt.Sprintf("Revision %s has not been rolled out completely.", revVersion))
288290
}
289291
}
290292

0 commit comments

Comments
 (0)