Skip to content

Commit a165e9c

Browse files
Add Support to force upgrade (multiple upgrade in same version) (#25)
Signed-off-by: RokibulHasan7 <mdrokibulhasan@appscode.com>
1 parent 67bcea5 commit a165e9c

File tree

5 files changed

+56
-6
lines changed

5 files changed

+56
-6
lines changed

apis/profile/v1alpha1/managedclusterprofilebinding_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ type ManagedClusterProfileBindingStatus struct {
5858
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
5959
// +optional
6060
ObservedOpscenterFeaturesVersion string `json:"observedOpscenterFeaturesVersion,omitempty"`
61+
// +optional
62+
LastUpgradeAt metav1.Time `json:"lastUpgradeAt,omitempty"`
6163
}
6264

6365
// +genclient

apis/profile/v1alpha1/zz_generated.deepcopy.go

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crds/profile.k8s.appscode.com_managedclusterprofilebindings.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,9 @@ spec:
177177
- type
178178
type: object
179179
type: array
180+
lastUpgradeAt:
181+
format: date-time
182+
type: string
180183
observedGeneration:
181184
format: int64
182185
type: integer

pkg/common/constants.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ const (
2222

2323
ACEUpgrader = "ace.cloud.com/upgrader"
2424
ACEUpgraderVersion = "ace.cloud.com/version"
25+
UpgradeAnnotation = "ace.cloud.com/upgradeAt"
2526
)

pkg/controller/managedclusterprofilebinding_controller.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,20 @@ package controller
1919
import (
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

97132
func (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

Comments
 (0)