@@ -19,16 +19,20 @@ package controller
1919import (
2020 "context"
2121 "fmt"
22+ "time"
2223
2324 profilev1alpha1 "github.com/kluster-manager/cluster-profile/apis/profile/v1alpha1"
2425 "github.com/kluster-manager/cluster-profile/pkg/cluster_upgrade"
26+ "github.com/kluster-manager/cluster-profile/pkg/common"
2527 "github.com/kluster-manager/cluster-profile/pkg/feature_installer"
2628 "github.com/kluster-manager/cluster-profile/pkg/utils"
2729
2830 fluxhelm "github.com/fluxcd/helm-controller/api/v2"
2931 "k8s.io/apimachinery/pkg/api/errors"
32+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3033 "k8s.io/apimachinery/pkg/runtime"
3134 "k8s.io/apimachinery/pkg/types"
35+ "kmodules.xyz/resource-metadata/hub"
3236 workv1 "open-cluster-management.io/api/work/v1"
3337 ctrl "sigs.k8s.io/controller-runtime"
3438 "sigs.k8s.io/controller-runtime/pkg/client"
@@ -81,17 +85,48 @@ func (r *ManagedClusterProfileBindingReconciler) Reconcile(ctx context.Context,
8185 featureInfo [val .FeatureSet ] = append (featureInfo [val .FeatureSet ], f )
8286 }
8387
84- if profileBinding .Spec .OpscenterFeaturesVersion != "" && profileBinding .Spec .OpscenterFeaturesVersion != profileBinding .Status .ObservedOpscenterFeaturesVersion {
88+ var upgradeTime string
89+ if profileBinding .Annotations != nil {
90+ upgradeTime = profileBinding .Annotations [common .UpgradeAnnotation ]
91+ }
92+ if r .needsUpgrade (profileBinding ) {
93+ logger .Info ("Triggering cluster upgrade" )
8594 if err := cluster_upgrade .UpgradeCluster (profileBinding , profile , r .Client ); err != nil {
86- return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , err )
95+ return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , upgradeTime , err )
8796 }
88- } else if profile .Spec .Features ["opscenter-features" ].Chart .Version == profileBinding .Spec .OpscenterFeaturesVersion || profileBinding .Spec .OpscenterFeaturesVersion == "" {
97+ } else if r .shouldEnableFeatures (profileBinding , profile ) {
98+ logger .Info ("Enabling features" )
8999 if err = feature_installer .EnableFeatures (ctx , r .Client , profileBinding , featureInfo , profile ); err != nil {
90- return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , err )
100+ return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , upgradeTime , err )
91101 }
92102 }
93103
94- return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , nil )
104+ return reconcile.Result {}, r .setOpscenterFeaturesVersion (ctx , profileBinding , upgradeTime , nil )
105+ }
106+
107+ // needsUpgrade checks if a cluster upgrade is required.
108+ func (r * ManagedClusterProfileBindingReconciler ) needsUpgrade (pb * profilev1alpha1.ManagedClusterProfileBinding ) bool {
109+ // Check version mismatch
110+ if pb .Spec .OpscenterFeaturesVersion != "" && pb .Spec .OpscenterFeaturesVersion != pb .Status .ObservedOpscenterFeaturesVersion {
111+ return true
112+ }
113+ // Check upgrade annotation
114+ upgradeTime , exists := pb .Annotations [common .UpgradeAnnotation ]
115+ if ! exists || upgradeTime == "" {
116+ return false
117+ }
118+ // Validate timestamp format
119+ parsedTime , err := time .Parse (time .RFC3339 , upgradeTime )
120+ if err != nil {
121+ log .FromContext (context .Background ()).Error (err , "Invalid force-upgrade timestamp" , "value" , upgradeTime )
122+ return false
123+ }
124+ return ! pb .Status .LastUpgradeAt .Time .Equal (parsedTime )
125+ }
126+
127+ // shouldEnableFeatures checks if features need to be enabled.
128+ func (r * ManagedClusterProfileBindingReconciler ) shouldEnableFeatures (pb * profilev1alpha1.ManagedClusterProfileBinding , p * profilev1alpha1.ManagedClusterSetProfile ) bool {
129+ return p .Spec .Features [hub .ChartOpscenterFeatures ].Chart .Version == pb .Spec .OpscenterFeaturesVersion || pb .Spec .OpscenterFeaturesVersion == ""
95130}
96131
97132func (r * ManagedClusterProfileBindingReconciler ) mapClusterProfileToClusterProfileBinding (ctx context.Context , obj client.Object ) []reconcile.Request {
@@ -124,14 +159,22 @@ func (r *ManagedClusterProfileBindingReconciler) mapClusterProfileToClusterProfi
124159 return requests
125160}
126161
127- func (r * ManagedClusterProfileBindingReconciler ) setOpscenterFeaturesVersion (ctx context.Context , profileBinding * profilev1alpha1.ManagedClusterProfileBinding , err error ) error {
162+ func (r * ManagedClusterProfileBindingReconciler ) setOpscenterFeaturesVersion (ctx context.Context , profileBinding * profilev1alpha1.ManagedClusterProfileBinding , upgradeTime string , err error ) error {
128163 var pb profilev1alpha1.ManagedClusterProfileBinding
129164 // Re-fetch the latest version of the Account object
130165 if err := r .Client .Get (ctx , client .ObjectKeyFromObject (profileBinding ), & pb ); err != nil && ! errors .IsNotFound (err ) {
131166 return fmt .Errorf ("failed to get latest account object: %w" , err )
132167 }
133168
134169 pb .Status .ObservedOpscenterFeaturesVersion = setOpscenterFeaturesVersion (ctx , r .Client , pb .Namespace )
170+ if upgradeTime != "" {
171+ parsedTime , err := time .Parse (time .RFC3339 , profileBinding .Annotations [common .UpgradeAnnotation ])
172+ if err != nil {
173+ return fmt .Errorf ("invalid force-upgrade timestamp, skipping update. error: %w" , err )
174+ } else {
175+ pb .Status .LastUpgradeAt = metav1.Time {Time : parsedTime }
176+ }
177+ }
135178 if updateErr := r .Client .Status ().Update (ctx , & pb ); updateErr != nil {
136179 return fmt .Errorf ("failed to update status to Failed: %w" , updateErr )
137180 }
0 commit comments