Skip to content
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
3 changes: 3 additions & 0 deletions api/v1/clusterextensionrevision_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ const (
ClusterExtensionRevisionReasonProbeFailure = "ProbeFailure"
ClusterExtensionRevisionReasonIncomplete = "Incomplete"
ClusterExtensionRevisionReasonProgressing = "Progressing"
ClusterExtensionRevisionReasonArchived = "Archived"
)

// ClusterExtensionRevisionSpec defines the desired state of ClusterExtensionRevision.
Expand Down Expand Up @@ -148,6 +149,8 @@ type ClusterExtensionRevisionStatus struct {
// +kubebuilder:subresource:status

// ClusterExtensionRevision is the Schema for the clusterextensionrevisions API
// +kubebuilder:printcolumn:name="Available",type=string,JSONPath=`.status.conditions[?(@.type=='Available')].status`
// +kubebuilder:printcolumn:name=Age,type=date,JSONPath=`.metadata.creationTimestamp`
type ClusterExtensionRevision struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Expand Down
15 changes: 7 additions & 8 deletions cmd/operator-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ type config struct {
globalPullSecret string
}

const authFilePrefix = "operator-controller-global-pull-secrets"
const (
authFilePrefix = "operator-controller-global-pull-secrets"
fieldOwnerPrefix = "olm.operatorframework.io"
)

// podNamespace checks whether the controller is running in a Pod vs.
// being run locally by inspecting the namespace file that gets mounted
Expand Down Expand Up @@ -560,6 +563,7 @@ func setupBoxcutter(
Scheme: mgr.GetScheme(),
RevisionGenerator: rg,
Preflights: preflights,
FieldOwner: fmt.Sprintf("%s/clusterextension-controller", fieldOwnerPrefix),
}
ceReconciler.RevisionStatesGetter = &controllers.BoxcutterRevisionStatesGetter{Reader: mgr.GetClient()}
ceReconciler.StorageMigrator = &applier.BoxcutterStorageMigrator{
Expand All @@ -568,11 +572,6 @@ func setupBoxcutter(
RevisionGenerator: rg,
}

// Boxcutter
const (
boxcutterSystemPrefixFieldOwner = "olm.operatorframework.io"
)

discoveryClient, err := discovery.NewDiscoveryClientForConfig(mgr.GetConfig())
if err != nil {
return fmt.Errorf("unable to create discovery client: %w", err)
Expand All @@ -599,8 +598,8 @@ func setupBoxcutter(
machinery.NewObjectEngine(
mgr.GetScheme(), trackingCache, mgr.GetClient(),
ownerhandling.NewNative(mgr.GetScheme()),
machinery.NewComparator(ownerhandling.NewNative(mgr.GetScheme()), discoveryClient, mgr.GetScheme(), boxcutterSystemPrefixFieldOwner),
boxcutterSystemPrefixFieldOwner, boxcutterSystemPrefixFieldOwner,
machinery.NewComparator(ownerhandling.NewNative(mgr.GetScheme()), discoveryClient, mgr.GetScheme(), fieldOwnerPrefix),
fieldOwnerPrefix, fieldOwnerPrefix,
),
validation.NewClusterPhaseValidator(mgr.GetRESTMapper(), mgr.GetClient()),
),
Expand Down
2 changes: 1 addition & 1 deletion commitchecker.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
expectedMergeBase: 95c5934cb8dd30f4035074cda16b2f8f6f544c03
expectedMergeBase: 9eac616c61b5da3314b273a058d13fee4b62f58e
upstreamBranch: main
upstreamOrg: operator-framework
upstreamRepo: operator-controller
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,14 @@ spec:
singular: clusterextensionrevision
scope: Cluster
versions:
- name: v1
- additionalPrinterColumns:
- jsonPath: .status.conditions[?(@.type=='Available')].status
name: Available
type: string
- jsonPath: .metadata.creationTimestamp
name: Age
type: date
name: v1
schema:
openAPIV3Schema:
description: ClusterExtensionRevision is the Schema for the clusterextensionrevisions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ metadata:
spec:
minReadySeconds: 5
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Allow temporary 2 pods (1 + 1) for zero-downtime updates
maxUnavailable: 0 # Never allow pods to be unavailable during updates
selector:
matchLabels:
control-plane: catalogd-controller-manager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ metadata:
namespace: {{ .Values.namespaces.olmv1.name }}
spec:
replicas: 1
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Allow temporary 2 pods (1 + 1) for zero-downtime updates
maxUnavailable: 0 # Never allow pods to be unavailable during updates
selector:
matchLabels:
control-plane: operator-controller-controller-manager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ spec:
type: Image
image:
pollIntervalMinutes: 10
ref: registry.redhat.io/redhat/certified-operator-index:v4.20
ref: registry.redhat.io/redhat/certified-operator-index:{{- .Values.options.openshift.catalogs.version }}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ spec:
type: Image
image:
pollIntervalMinutes: 10
ref: registry.redhat.io/redhat/community-operator-index:v4.20
ref: registry.redhat.io/redhat/community-operator-index:{{- .Values.options.openshift.catalogs.version }}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ spec:
type: Image
image:
pollIntervalMinutes: 10
ref: registry.redhat.io/redhat/redhat-marketplace-index:v4.20
ref: registry.redhat.io/redhat/redhat-marketplace-index:{{- .Values.options.openshift.catalogs.version }}
{{- end -}}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,5 @@ spec:
type: Image
image:
pollIntervalMinutes: 10
ref: registry.redhat.io/redhat/redhat-operator-index:v4.20
ref: registry.redhat.io/redhat/redhat-operator-index:{{ .Values.options.openshift.catalogs.version }}
{{- end -}}
2 changes: 2 additions & 0 deletions helm/olmv1/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ options:
enabled: false
openshift:
enabled: false
catalogs:
version: v4.20
# This can be one of: standard or experimental
featureSet: standard

Expand Down
71 changes: 42 additions & 29 deletions internal/operator-controller/applier/boxcutter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,9 @@ import (
ocv1 "github.com/operator-framework/operator-controller/api/v1"
"github.com/operator-framework/operator-controller/internal/operator-controller/controllers"
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
hashutil "github.com/operator-framework/operator-controller/internal/shared/util/hash"
)

const (
RevisionHashAnnotation = "olm.operatorframework.io/hash"
ClusterExtensionRevisionPreviousLimit = 5
)

Expand Down Expand Up @@ -200,6 +198,7 @@ type Boxcutter struct {
Scheme *runtime.Scheme
RevisionGenerator ClusterExtensionRevisionGenerator
Preflights []Preflight
FieldOwner string
}

func (bc *Boxcutter) Apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
Expand All @@ -216,34 +215,56 @@ func (bc *Boxcutter) getObjects(rev *ocv1.ClusterExtensionRevision) []client.Obj
return objs
}

func (bc *Boxcutter) createOrUpdate(ctx context.Context, obj client.Object) error {
if obj.GetObjectKind().GroupVersionKind().Empty() {
gvk, err := apiutil.GVKForObject(obj, bc.Scheme)
if err != nil {
return err
}
obj.GetObjectKind().SetGroupVersionKind(gvk)
}
return bc.Client.Patch(ctx, obj, client.Apply, client.FieldOwner(bc.FieldOwner), client.ForceOwnership)
}

func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.ClusterExtension, objectLabels, revisionAnnotations map[string]string) (bool, string, error) {
// Generate desired revision
desiredRevision, err := bc.RevisionGenerator.GenerateRevision(contentFS, ext, objectLabels, revisionAnnotations)
if err != nil {
return false, "", err
}

if err := controllerutil.SetControllerReference(ext, desiredRevision, bc.Scheme); err != nil {
return false, "", fmt.Errorf("set ownerref: %w", err)
}

// List all existing revisions
existingRevisions, err := bc.getExistingRevisions(ctx, ext.GetName())
if err != nil {
return false, "", err
}
desiredHash := hashutil.DeepHashObject(desiredRevision.Spec.Phases)

// Sort into current and previous revisions.
var (
currentRevision *ocv1.ClusterExtensionRevision
)
currentRevision := &ocv1.ClusterExtensionRevision{}
state := StateNeedsInstall
// check if we can update the current revision.
if len(existingRevisions) > 0 {
maybeCurrentRevision := existingRevisions[len(existingRevisions)-1]
annotations := maybeCurrentRevision.GetAnnotations()
if annotations != nil {
if revisionHash, ok := annotations[RevisionHashAnnotation]; ok && revisionHash == desiredHash {
currentRevision = &maybeCurrentRevision
}
// try first to update the current revision.
currentRevision = &existingRevisions[len(existingRevisions)-1]
desiredRevision.Spec.Previous = currentRevision.Spec.Previous
desiredRevision.Spec.Revision = currentRevision.Spec.Revision
desiredRevision.Name = currentRevision.Name

err := bc.createOrUpdate(ctx, desiredRevision)
switch {
case apierrors.IsInvalid(err):
// We could not update the current revision due to trying to update an immutable field.
// Therefore, we need to create a new revision.
state = StateNeedsUpgrade
case err == nil:
// inplace patch was successful, no changes in phases
state = StateUnchanged
default:
return false, "", fmt.Errorf("patching %s Revision: %w", desiredRevision.Name, err)
}
state = StateNeedsUpgrade
}

// Preflights
Expand All @@ -270,30 +291,22 @@ func (bc *Boxcutter) apply(ctx context.Context, contentFS fs.FS, ext *ocv1.Clust
}
}

if currentRevision == nil {
// all Revisions are outdated => create a new one.
if state != StateUnchanged {
// need to create new revision
prevRevisions := existingRevisions
revisionNumber := latestRevisionNumber(prevRevisions) + 1

newRevision := desiredRevision
newRevision.Name = fmt.Sprintf("%s-%d", ext.Name, revisionNumber)
if newRevision.GetAnnotations() == nil {
newRevision.Annotations = map[string]string{}
}
newRevision.Annotations[RevisionHashAnnotation] = desiredHash
newRevision.Spec.Revision = revisionNumber
desiredRevision.Name = fmt.Sprintf("%s-%d", ext.Name, revisionNumber)
desiredRevision.Spec.Revision = revisionNumber

if err = bc.setPreviousRevisions(ctx, newRevision, prevRevisions); err != nil {
if err = bc.setPreviousRevisions(ctx, desiredRevision, prevRevisions); err != nil {
return false, "", fmt.Errorf("garbage collecting old Revisions: %w", err)
}

if err := controllerutil.SetControllerReference(ext, newRevision, bc.Scheme); err != nil {
return false, "", fmt.Errorf("set ownerref: %w", err)
}
if err := bc.Client.Create(ctx, newRevision); err != nil {
if err := bc.createOrUpdate(ctx, desiredRevision); err != nil {
return false, "", fmt.Errorf("creating new Revision: %w", err)
}
currentRevision = newRevision
currentRevision = desiredRevision
}

progressingCondition := meta.FindStatusCondition(currentRevision.Status.Conditions, ocv1.TypeProgressing)
Expand Down
Loading