Skip to content

Commit 305f86a

Browse files
committed
wip: propagate CER conditions to CE status
1 parent d26dbc5 commit 305f86a

File tree

20 files changed

+577
-20
lines changed

20 files changed

+577
-20
lines changed

api/v1/clusterextension_types.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,14 @@ type BundleMetadata struct {
472472
Version string `json:"version"`
473473
}
474474

475+
type RevisionStatus struct {
476+
Name string `json:"name"`
477+
// +listType=map
478+
// +listMapKey=type
479+
// +optional
480+
Conditions []metav1.Condition `json:"conditions,omitempty"`
481+
}
482+
475483
// ClusterExtensionStatus defines the observed state of a ClusterExtension.
476484
type ClusterExtensionStatus struct {
477485
// The set of condition types which apply to all spec.source variations are Installed and Progressing.
@@ -504,6 +512,12 @@ type ClusterExtensionStatus struct {
504512
//
505513
// +optional
506514
Install *ClusterExtensionInstallStatus `json:"install,omitempty"`
515+
516+
// +listType=map
517+
// +listMapKey=name
518+
// +optional
519+
// <opcon:experimental>
520+
ActiveRevisions []RevisionStatus `json:"activeRevisions,omitempty"`
507521
}
508522

509523
// ClusterExtensionInstallStatus is a representation of the status of the identified bundle.
@@ -522,7 +536,7 @@ type ClusterExtensionInstallStatus struct {
522536
// +kubebuilder:subresource:status
523537
// +kubebuilder:printcolumn:name="Installed Bundle",type=string,JSONPath=`.status.install.bundle.name`
524538
// +kubebuilder:printcolumn:name=Version,type=string,JSONPath=`.status.install.bundle.version`
525-
// +kubebuilder:printcolumn:name="Installed",type=string,JSONPath=`.status.conditions[?(@.type=='Installed')].status`
539+
// +kubebuilder:printcolumn:name="Available",type=string,JSONPath=`.status.conditions[?(@.type=='Available')].status`
526540
// +kubebuilder:printcolumn:name="Progressing",type=string,JSONPath=`.status.conditions[?(@.type=='Progressing')].status`
527541
// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp`
528542

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.

cmd/operator-controller/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,7 @@ func (c *boxcutterReconcilerConfigurator) Configure(ceReconciler *controllers.Cl
628628
controllers.RetrieveRevisionStates(revisionStatesGetter),
629629
controllers.ResolveBundle(c.resolver),
630630
controllers.UnpackBundle(c.imagePuller, c.imageCache),
631-
controllers.ApplyBundle(appl),
631+
controllers.ApplyBundleWithBoxcutter(appl),
632632
}
633633

634634
baseDiscoveryClient, err := discovery.NewDiscoveryClientForConfig(c.mgr.GetConfig())

docs/api-reference/olmv1-api-reference.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ _Appears in:_
361361
| --- | --- | --- | --- |
362362
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | The set of condition types which apply to all spec.source variations are Installed and Progressing.<br />The Installed condition represents whether or not the bundle has been installed for this ClusterExtension.<br />When Installed is True and the Reason is Succeeded, the bundle has been successfully installed.<br />When Installed is False and the Reason is Failed, the bundle has failed to install.<br />The Progressing condition represents whether or not the ClusterExtension is advancing towards a new state.<br />When Progressing is True and the Reason is Succeeded, the ClusterExtension is making progress towards a new state.<br />When Progressing is True and the Reason is Retrying, the ClusterExtension has encountered an error that could be resolved on subsequent reconciliation attempts.<br />When Progressing is False and the Reason is Blocked, the ClusterExtension has encountered an error that requires manual intervention for recovery.<br /><opcon:experimental:description><br />When Progressing is True and Reason is RollingOut, the ClusterExtension has one or more ClusterExtensionRevisions in active roll out.<br /></opcon:experimental:description><br />When the ClusterExtension is sourced from a catalog, if may also communicate a deprecation condition.<br />These are indications from a package owner to guide users away from a particular package, channel, or bundle.<br />BundleDeprecated is set if the requested bundle version is marked deprecated in the catalog.<br />ChannelDeprecated is set if the requested channel is marked deprecated in the catalog.<br />PackageDeprecated is set if the requested package is marked deprecated in the catalog.<br />Deprecated is a rollup condition that is present when any of the deprecated conditions are present. | | |
363363
| `install` _[ClusterExtensionInstallStatus](#clusterextensioninstallstatus)_ | install is a representation of the current installation status for this ClusterExtension. | | |
364+
| `activeRevisions` _[RevisionStatus](#revisionstatus) array_ | <opcon:experimental> | | |
364365

365366

366367

@@ -435,6 +436,23 @@ _Appears in:_
435436
| `ref` _string_ | ref contains the resolved image digest-based reference.<br />The digest format is used so users can use other tooling to fetch the exact<br />OCI manifests that were used to extract the catalog contents. | | MaxLength: 1000 <br />Required: \{\} <br /> |
436437

437438

439+
#### RevisionStatus
440+
441+
442+
443+
444+
445+
446+
447+
_Appears in:_
448+
- [ClusterExtensionStatus](#clusterextensionstatus)
449+
450+
| Field | Description | Default | Validation |
451+
| --- | --- | --- | --- |
452+
| `name` _string_ | | | |
453+
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#condition-v1-meta) array_ | | | |
454+
455+
438456
#### ServiceAccountReference
439457

440458

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
@@ -506,6 +506,78 @@ spec:
506506
description: status is an optional field that defines the observed state
507507
of the ClusterExtension.
508508
properties:
509+
activeRevisions:
510+
items:
511+
properties:
512+
conditions:
513+
items:
514+
description: Condition contains details for one aspect of
515+
the current state of this API Resource.
516+
properties:
517+
lastTransitionTime:
518+
description: |-
519+
lastTransitionTime is the last time the condition transitioned from one status to another.
520+
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
521+
format: date-time
522+
type: string
523+
message:
524+
description: |-
525+
message is a human readable message indicating details about the transition.
526+
This may be an empty string.
527+
maxLength: 32768
528+
type: string
529+
observedGeneration:
530+
description: |-
531+
observedGeneration represents the .metadata.generation that the condition was set based upon.
532+
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
533+
with respect to the current state of the instance.
534+
format: int64
535+
minimum: 0
536+
type: integer
537+
reason:
538+
description: |-
539+
reason contains a programmatic identifier indicating the reason for the condition's last transition.
540+
Producers of specific condition types may define expected values and meanings for this field,
541+
and whether the values are considered a guaranteed API.
542+
The value should be a CamelCase string.
543+
This field may not be empty.
544+
maxLength: 1024
545+
minLength: 1
546+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
547+
type: string
548+
status:
549+
description: status of the condition, one of True, False,
550+
Unknown.
551+
enum:
552+
- "True"
553+
- "False"
554+
- Unknown
555+
type: string
556+
type:
557+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
558+
maxLength: 316
559+
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])$
560+
type: string
561+
required:
562+
- lastTransitionTime
563+
- message
564+
- reason
565+
- status
566+
- type
567+
type: object
568+
type: array
569+
x-kubernetes-list-map-keys:
570+
- type
571+
x-kubernetes-list-type: map
572+
name:
573+
type: string
574+
required:
575+
- name
576+
type: object
577+
type: array
578+
x-kubernetes-list-map-keys:
579+
- name
580+
x-kubernetes-list-type: map
509581
conditions:
510582
description: |-
511583
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
@@ -413,8 +413,6 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
413413
default:
414414
return false, progressingCondition.Message, nil
415415
}
416-
} else if availableCondition != nil && availableCondition.Status != metav1.ConditionTrue {
417-
return false, "", errors.New(availableCondition.Message)
418416
} else if succeededCondition != nil && succeededCondition.Status != metav1.ConditionTrue {
419417
return false, succeededCondition.Message, nil
420418
}

internal/operator-controller/controllers/boxcutter_reconcile_steps.go

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ import (
2323
"slices"
2424

2525
apimeta "k8s.io/apimachinery/pkg/api/meta"
26+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2627
ctrl "sigs.k8s.io/controller-runtime"
2728
"sigs.k8s.io/controller-runtime/pkg/client"
29+
"sigs.k8s.io/controller-runtime/pkg/log"
2830

2931
ocv1 "github.com/operator-framework/operator-controller/api/v1"
3032
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
@@ -58,12 +60,14 @@ func (d *BoxcutterRevisionStatesGetter) GetRevisionStates(ctx context.Context, e
5860
continue
5961
}
6062

61-
// TODO: the setting of these annotations (happens in boxcutter applier when we pass in "revisionAnnotations")
63+
// TODO: the setting of these annotations (happens in boxcutter applier when we pass in "storageLabels")
6264
// is fairly decoupled from this code where we get the annotations back out. We may want to co-locate
6365
// the set/get logic a bit better to make it more maintainable and less likely to get out of sync.
6466
rm := &RevisionMetadata{
65-
Package: rev.Annotations[labels.PackageNameKey],
66-
Image: rev.Annotations[labels.BundleReferenceKey],
67+
RevName: rev.Name,
68+
Package: rev.Labels[labels.PackageNameKey],
69+
Image: rev.Annotations[labels.BundleReferenceKey],
70+
Conditions: rev.Status.Conditions,
6771
BundleMetadata: ocv1.BundleMetadata{
6872
Name: rev.Annotations[labels.BundleNameKey],
6973
Version: rev.Annotations[labels.BundleVersionKey],
@@ -93,3 +97,61 @@ func MigrateStorage(m StorageMigrator) ReconcileStepFunc {
9397
return nil, nil
9498
}
9599
}
100+
101+
func ApplyBundleWithBoxcutter(a Applier) ReconcileStepFunc {
102+
return func(ctx context.Context, state *reconcileState, ext *ocv1.ClusterExtension) (*ctrl.Result, error) {
103+
l := log.FromContext(ctx)
104+
storeLbls := map[string]string{
105+
labels.BundleNameKey: state.resolvedRevisionMetadata.Name,
106+
labels.PackageNameKey: state.resolvedRevisionMetadata.Package,
107+
labels.BundleVersionKey: state.resolvedRevisionMetadata.Version,
108+
labels.BundleReferenceKey: state.resolvedRevisionMetadata.Image,
109+
}
110+
objLbls := map[string]string{
111+
labels.OwnerKindKey: ocv1.ClusterExtensionKind,
112+
labels.OwnerNameKey: ext.GetName(),
113+
}
114+
115+
l.Info("applying bundle contents")
116+
if _, _, err := a.Apply(ctx, state.imageFS, ext, objLbls, storeLbls); err != nil {
117+
// If there was an error applying the resolved bundle,
118+
// report the error via the Progressing condition.
119+
setStatusProgressing(ext, wrapErrorWithResolutionInfo(state.resolvedRevisionMetadata.BundleMetadata, err))
120+
return nil, err
121+
}
122+
123+
// Repopulate active revisions to avoid duplicates
124+
ext.Status.ActiveRevisions = nil
125+
126+
// Mirror Available/Progressing conditions from the installed revision
127+
if i := state.revisionStates.Installed; i != nil {
128+
for _, cndType := range []string{ocv1.ClusterExtensionRevisionTypeAvailable, ocv1.ClusterExtensionRevisionTypeProgressing} {
129+
if cnd := apimeta.FindStatusCondition(i.Conditions, cndType); cnd != nil {
130+
apimeta.SetStatusCondition(&ext.Status.Conditions, *cnd)
131+
}
132+
}
133+
ext.Status.Install = &ocv1.ClusterExtensionInstallStatus{
134+
Bundle: i.BundleMetadata,
135+
}
136+
ext.Status.ActiveRevisions = []ocv1.RevisionStatus{{Name: i.RevName}}
137+
}
138+
for idx, r := range state.revisionStates.RollingOut {
139+
rs := ocv1.RevisionStatus{Name: r.RevName}
140+
// Mirror Progressing condition from the latest active revision
141+
if idx == len(state.revisionStates.RollingOut)-1 {
142+
pcnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterExtensionRevisionTypeProgressing)
143+
if pcnd != nil {
144+
apimeta.SetStatusCondition(&ext.Status.Conditions, *pcnd)
145+
}
146+
acnd := apimeta.FindStatusCondition(r.Conditions, ocv1.ClusterExtensionRevisionTypeAvailable)
147+
if acnd != nil && pcnd != nil && pcnd.Status == metav1.ConditionFalse && acnd.Status != metav1.ConditionTrue {
148+
apimeta.SetStatusCondition(&ext.Status.Conditions, *acnd)
149+
}
150+
}
151+
ext.Status.ActiveRevisions = append(ext.Status.ActiveRevisions, rs)
152+
}
153+
154+
setInstalledStatusFromRevisionStates(ext, state.revisionStates)
155+
return nil, nil
156+
}
157+
}

internal/operator-controller/controllers/clusterextension_controller.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,11 @@ func clusterExtensionRequestsForCatalog(c client.Reader, logger logr.Logger) crh
332332
}
333333

334334
type RevisionMetadata struct {
335+
RevName string
335336
Package string
336337
Image string
337338
ocv1.BundleMetadata
339+
Conditions []metav1.Condition
338340
}
339341

340342
type RevisionStates struct {

0 commit comments

Comments
 (0)