Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
"name": "[Jira:\"Cluster Version Operator\"] cluster-version-operator should create proposals",
"labels": {
"Lifecycle:informing": {},
"OTA-1966": {},
"Serial": {}
},
"resources": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
apiVersion: v1
kind: Namespace
metadata:
name: openshift-lightspeed
annotations:
kubernetes.io/description: This manifest is only for testing purpose and will be removed when its own operator becomes available on the cluster.
include.release.openshift.io/self-managed-high-availability: "true"
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
apiVersion: v1
kind: ConfigMap
metadata:
name: ota-advisory-prompt
namespace: openshift-lightspeed
annotations:
include.release.openshift.io/self-managed-high-availability: "true"
release.openshift.io/feature-set: TechPreviewNoUpgrade
data:
prompt: |
You are an OpenShift upgrade advisor. Analyze the cluster readiness
data in the proposal request and produce an upgrade risk assessment.

The request contains a "Cluster Readiness Data" section with a JSON
block. This was collected by the Cluster Version Operator — do not
re-collect it. Parse the JSON, evaluate each check's results, and
classify findings as blockers, warnings, or informational.

Use the ota-upgrade-advisor skill for the decision framework and
blocker classification rules. When findings need deeper investigation,
use prometheus, platform-docs, redhat-support, or product-lifecycle
skills.

When the readiness data includes olm_operator_lifecycle results, use
the product-lifecycle skill to cross-reference each operator's package
name against the Red Hat Product Life Cycle API. Report support phase,
EOL dates, and OCP compatibility from PLCC alongside the OLM data.

Do not guess or assume cluster state. Do not execute upgrade commands.
6 changes: 6 additions & 0 deletions pkg/cvo/availableupdates_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ func newOperator(url string, cluster release, promqlMock clusterconditions.Condi
fake.NewClientBuilder().Build(), func(_ string) (*configv1.ClusterVersion, error) {
return &configv1.ClusterVersion{}, nil
},
func(_ context.Context, namespace, name string, _ metav1.GetOptions) (*corev1.ConfigMap, error) {
return &corev1.ConfigMap{}, nil
},
func() string {
return operator.release.Version
},
)
return availableUpdates, operator
}
Expand Down
24 changes: 17 additions & 7 deletions pkg/cvo/cvo.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,13 +324,23 @@ func New(

optr.configuration = configuration.NewClusterVersionOperatorConfiguration(operatorClient, operatorInformerFactory)

optr.proposalController = proposal.NewController(func() ([]configv1.Release, []configv1.ConditionalUpdate, error) {
availableUpdates := optr.getAvailableUpdates()
if availableUpdates == nil {
return nil, nil, nil
}
return availableUpdates.Updates, availableUpdates.ConditionalUpdates, nil
}, rtClient, cvInformer.Lister().Get)
optr.proposalController = proposal.NewController(
func() ([]configv1.Release, []configv1.ConditionalUpdate, error) {
availableUpdates := optr.getAvailableUpdates()
if availableUpdates == nil {
return nil, nil, nil
}
return availableUpdates.Updates, availableUpdates.ConditionalUpdates, nil
},
rtClient,
cvInformer.Lister().Get,
func(ctx context.Context, namespace, name string, opts metav1.GetOptions) (*corev1.ConfigMap, error) {
return kubeClient.CoreV1().ConfigMaps(namespace).Get(ctx, name, opts)
},
func() string {
return optr.release.Version
},
)

return optr, nil
}
Expand Down
4 changes: 4 additions & 0 deletions pkg/cvo/cvo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2764,6 +2764,10 @@ func TestOperator_availableUpdatesSync(t *testing.T) {
return nil, nil, nil
}, ctrlruntimefake.NewClientBuilder().Build(), func(_ string) (*configv1.ClusterVersion, error) {
return &configv1.ClusterVersion{}, nil
}, func(_ context.Context, namespace, name string, _ metav1.GetOptions) (*corev1.ConfigMap, error) {
return &corev1.ConfigMap{}, nil
}, func() string {
return optr.release.Version
})
err := optr.availableUpdatesSync(ctx, optr.queueKey())
if err != nil && tt.wantErr == nil {
Expand Down
6 changes: 6 additions & 0 deletions pkg/cvo/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,12 @@ func (optr *Operator) syncStatus(ctx context.Context, original, config *configv1
if klog.V(6).Enabled() {
klog.Infof("Apply config: %s", cmp.Diff(original, config))
}
if optr.shouldEnableProposalController() {
if original != nil && len(config.Status.History) < len(original.Status.History) {
klog.V(internal.Normal).Infof("Reconciling proposals because ClusterVersion.status.history got pruned")
optr.proposalController.Queue().Add(optr.proposalController.QueueKey())
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
updated, err := applyClusterVersionStatus(ctx, optr.client.ConfigV1(), config, original)
optr.rememberLastUpdate(updated)
return err
Expand Down
23 changes: 23 additions & 0 deletions pkg/internal/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"

"github.com/blang/semver/v4"

"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/klog/v2"

Expand Down Expand Up @@ -131,3 +133,24 @@ func IsAlertConditionReason(reason string) bool {
func AlertConditionMessage(alertName, severity, state, impact, details string) string {
return fmt.Sprintf("%s alert %s %s, %s. %s", severity, alertName, state, impact, details)
}

const (
UpdateTypeMajor = "Major"
UpdateTypeMinor = "Minor"
UpdateTypePatch = "Patch"
UpdateTypeUnknown = "Unknown"
)

// UpdateType returns the type of the update from the source to the target versions
func UpdateType(source, target semver.Version) string {
if source.Major < target.Major {
return UpdateTypeMajor
}
if source.Major == target.Major && source.Minor < target.Minor {
return UpdateTypeMinor
}
if source.LT(target) {
return UpdateTypePatch
}
return UpdateTypeUnknown
}
7 changes: 2 additions & 5 deletions pkg/payload/precondition/clusterversion/upgradeable.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,11 +150,8 @@ func majorOrMinorUpdateFrom(status configv1.ClusterVersionStatus, currentVersion
}
if cond := resourcemerge.FindOperatorStatusCondition(status.Conditions, configv1.OperatorProgressing); cond != nil &&
cond.Status == configv1.ConditionTrue {
if v.Major < currentVersion.Major {
return completedVersion, "Major"
}
if v.Major == currentVersion.Major && v.Minor < currentVersion.Minor {
return completedVersion, "Minor"
if ut := internal.UpdateType(v, currentVersion); ut == internal.UpdateTypeMajor || ut == internal.UpdateTypeMinor {
return completedVersion, ut
}
}
return "", ""
Expand Down
Loading