@@ -27,6 +27,7 @@ import (
2727 "time"
2828
2929 cmv1 "github.com/openshift-online/ocm-sdk-go/clustersmgmt/v1"
30+ corev1 "k8s.io/api/core/v1"
3031 apierrors "k8s.io/apimachinery/pkg/api/errors"
3132 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3233 "k8s.io/apimachinery/pkg/types"
@@ -188,6 +189,15 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
188189 }
189190 defer rosaClient .Close ()
190191
192+ isValid , err := validateControlPlaneSpec (rosaClient , rosaScope )
193+ if err != nil {
194+ return ctrl.Result {}, fmt .Errorf ("failed to validate ROSAControlPlane.spec: %w" , err )
195+ }
196+ if ! isValid {
197+ // dont' requeue because input is invalid and manual intervention is needed.
198+ return ctrl.Result {}, nil
199+ }
200+
191201 cluster , err := rosaClient .GetCluster ()
192202 if err != nil {
193203 return ctrl.Result {}, err
@@ -213,6 +223,9 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
213223 if err := r .reconcileKubeconfig (ctx , rosaScope , rosaClient , cluster ); err != nil {
214224 return ctrl.Result {}, fmt .Errorf ("failed to reconcile kubeconfig: %w" , err )
215225 }
226+ if err := r .reconcileClusterVersion (rosaScope , rosaClient , cluster ); err != nil {
227+ return ctrl.Result {}, err
228+ }
216229 return ctrl.Result {}, nil
217230 case cmv1 .ClusterStateError :
218231 errorMessage := cluster .Status ().ProvisionErrorMessage ()
@@ -255,7 +268,7 @@ func (r *ROSAControlPlaneReconciler) reconcileNormal(ctx context.Context, rosaSc
255268 DisableUserWorkloadMonitoring (true ).
256269 Version (
257270 cmv1 .NewVersion ().
258- ID (* rosaScope .ControlPlane .Spec .Version ).
271+ ID (fmt . Sprintf ( "openshift-v%s" , rosaScope .ControlPlane .Spec .Version ) ).
259272 ChannelGroup ("stable" ),
260273 ).
261274 ExpirationTimestamp (time .Now ().Add (1 * time .Hour )).
@@ -394,6 +407,41 @@ func (r *ROSAControlPlaneReconciler) reconcileDelete(ctx context.Context, rosaSc
394407 return ctrl.Result {RequeueAfter : time .Second * 60 }, nil
395408}
396409
410+ func (r * ROSAControlPlaneReconciler ) reconcileClusterVersion (rosaScope * scope.ROSAControlPlaneScope , rosaClient * rosa.RosaClient , cluster * cmv1.Cluster ) error {
411+ version := rosaScope .ControlPlane .Spec .Version
412+ if version == cluster .Version ().RawID () {
413+ conditions .MarkFalse (rosaScope .ControlPlane , rosacontrolplanev1 .ROSAControlPlaneUpgradingCondition , "upgraded" , clusterv1 .ConditionSeverityInfo , "" )
414+ return nil
415+ }
416+
417+ scheduledUpgrade , err := rosaClient .CheckExistingScheduledUpgrade (cluster )
418+ if err != nil {
419+ return fmt .Errorf ("failed to get existing scheduled upgrades: %w" , err )
420+ }
421+
422+ if scheduledUpgrade == nil {
423+ scheduledUpgrade , err = rosaClient .ScheduleControlPlaneUpgrade (cluster , version , time .Now ())
424+ if err != nil {
425+ return fmt .Errorf ("failed to schedule control plane upgrade to version %s: %w" , version , err )
426+ }
427+ }
428+
429+ condition := & clusterv1.Condition {
430+ Type : rosacontrolplanev1 .ROSAControlPlaneUpgradingCondition ,
431+ Status : corev1 .ConditionTrue ,
432+ Reason : string (scheduledUpgrade .State ().Value ()),
433+ Message : fmt .Sprintf ("Upgrading to version %s" , scheduledUpgrade .Version ()),
434+ }
435+ conditions .Set (rosaScope .ControlPlane , condition )
436+
437+ // if cluster is already upgrading to another version we need to wait until the current upgrade is finished, return an error to requeue and try later.
438+ if scheduledUpgrade .Version () != version {
439+ return fmt .Errorf ("there is already a %s upgrade to version %s" , scheduledUpgrade .State ().Value (), scheduledUpgrade .Version ())
440+ }
441+
442+ return nil
443+ }
444+
397445func (r * ROSAControlPlaneReconciler ) reconcileKubeconfig (ctx context.Context , rosaScope * scope.ROSAControlPlaneScope , rosaClient * rosa.RosaClient , cluster * cmv1.Cluster ) error {
398446 rosaScope .Debug ("Reconciling ROSA kubeconfig for cluster" , "cluster-name" , rosaScope .RosaClusterName ())
399447
@@ -510,6 +558,26 @@ func (r *ROSAControlPlaneReconciler) reconcileClusterAdminPassword(ctx context.C
510558 return password , nil
511559}
512560
561+ func validateControlPlaneSpec (rosaClient * rosa.RosaClient , rosaScope * scope.ROSAControlPlaneScope ) (bool , error ) {
562+ // reset previous message.
563+ rosaScope .ControlPlane .Status .FailureMessage = nil
564+
565+ version := rosaScope .ControlPlane .Spec .Version
566+ isSupported , err := rosaClient .IsVersionSupported (version )
567+ if err != nil {
568+ return false , err
569+ }
570+
571+ if ! isSupported {
572+ message := fmt .Sprintf ("version %s is not supported" , version )
573+ rosaScope .ControlPlane .Status .FailureMessage = & message
574+ return false , nil
575+ }
576+
577+ // TODO: add more input validations
578+ return true , nil
579+ }
580+
513581func (r * ROSAControlPlaneReconciler ) rosaClusterToROSAControlPlane (log * logger.Logger ) handler.MapFunc {
514582 return func (ctx context.Context , o client.Object ) []ctrl.Request {
515583 rosaCluster , ok := o .(* expinfrav1.ROSACluster )
0 commit comments