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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ require (
github.com/openshift/api v0.0.0-20260416105050-3c6b218b8a80
github.com/openshift/client-go v0.0.0-20260416131737-a19e91702ab5
github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260507160300-84a97541c3e0
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a
github.com/operator-framework/api v0.17.1
github.com/operator-framework/operator-lifecycle-manager v0.22.0
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,8 @@ github.com/openshift/client-go v0.0.0-20260416131737-a19e91702ab5 h1:R5gdIA+R7MO
github.com/openshift/client-go v0.0.0-20260416131737-a19e91702ab5/go.mod h1:u56GmXEMF6bvws8ipkT1ZRNJH52RF5sZ/yRP+6PwkH4=
github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1 h1:NdVGxmPGwWoMlhSmTxMMgp2SszLtAH3nJ6AMGBpXclY=
github.com/openshift/library-go v0.0.0-20260413093329-d2db42c961e1/go.mod h1:3bi4pLpYRdVd1aEhsHfRTJkwxwPLfRZ+ZePn3RmJd2k=
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260507160300-84a97541c3e0 h1:ORAZYemGOmlB+2ulDHqxuUg69FaoCe8cUdablBX7LNo=
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260507160300-84a97541c3e0/go.mod h1:tZlKXEZJ4/qxPPh6mamWrkwK+EQjudHt8AypudHttVs=
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a h1:bUTmfSz1JIB8U8JiHsJ+8xWJH9jvrRBzSUWo0wbB9aU=
github.com/openshift/lightspeed-agentic-operator/api v0.0.0-20260521135452-44bd61b9b92a/go.mod h1:tZlKXEZJ4/qxPPh6mamWrkwK+EQjudHt8AypudHttVs=
github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12 h1:AKx/w1qpS8We43bsRgf8Nll3CGlDHpr/WAXvuedTNZI=
github.com/openshift/onsi-ginkgo/v2 v2.6.1-0.20241205171354-8006f302fd12/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo=
github.com/operator-framework/api v0.17.1 h1:J/6+Xj4IEV8C7hcirqUFwOiZAU3PbnJhWvB0/bB51c4=
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
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"
workload.openshift.io/allowed: "management"
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 @@ -345,13 +345,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 @@ -2758,6 +2758,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 @@ -179,6 +179,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