Skip to content

Commit 3b5eef5

Browse files
committed
🌱 Refactor HelmChartStatus
Refactor HelmChartStatus so that it is more user-friendly and always shows the appropriate phase we are in. Signed-off-by: janiskemper <[email protected]>
1 parent d74e76a commit 3b5eef5

File tree

6 files changed

+118
-45
lines changed

6 files changed

+118
-45
lines changed

api/v1alpha1/clusteraddon_types.go

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ limitations under the License.
1717
package v1alpha1
1818

1919
import (
20+
"github.com/SovereignCloudStack/cluster-stack-operator/pkg/clusteraddon"
2021
corev1 "k8s.io/api/core/v1"
2122
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2223
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
@@ -27,17 +28,33 @@ const (
2728
ClusterAddonFinalizer = "clusteraddon.clusterstack.x-k8s.io"
2829
)
2930

30-
// HelmChartStatusConditions defines the status of helm chart in the cluster addon.
31-
type HelmChartStatusConditions string
31+
// StagePhase defines the status of helm chart in the cluster addon.
32+
type StagePhase string
3233

3334
var (
34-
None = HelmChartStatusConditions("")
35-
WaitingForPreCondition = HelmChartStatusConditions("waitingForPreCondition")
36-
ApplyingOrDeleting = HelmChartStatusConditions("applyingOrDeleting")
37-
WaitingForPostCondition = HelmChartStatusConditions("waitingForPostCondition")
38-
Done = HelmChartStatusConditions("done")
35+
None = StagePhase("")
36+
Pending = StagePhase("Pending")
37+
WaitingForPreCondition = StagePhase("waitingForPreCondition")
38+
ApplyingOrDeleting = StagePhase("applyingOrDeleting")
39+
WaitingForPostCondition = StagePhase("waitingForPostCondition")
40+
Done = StagePhase("done")
3941
)
4042

43+
// StageStatus represents the helm charts of the hook and it's phases.
44+
type StageStatus struct {
45+
// Name represent name of the helm chart
46+
// +optional
47+
Name string `json:"helmChartName"`
48+
49+
// Action is the action of the helm chart. e.g. - apply and delete.
50+
// +optional
51+
Action clusteraddon.Action `json:"action,omitempty"`
52+
53+
// Phase is the current phase of the helm chart.
54+
// +optional
55+
Phase StagePhase `json:"phase"`
56+
}
57+
4158
// ClusterAddonSpec defines the desired state of a ClusterAddon object.
4259
type ClusterAddonSpec struct {
4360
// ClusterStack is the full string <provider>-<name>-<Kubernetes version>-<version> that will be filled with the cluster stack that
@@ -64,9 +81,9 @@ type ClusterAddonStatus struct {
6481
// +optional
6582
Resources []*Resource `json:"resources,omitempty"`
6683

67-
// HelmChartStatus defines the status of helm chart in the cluster addon.
84+
// Stages shows the state of all stages in the current running hook.
6885
// +optional
69-
HelmChartStatus map[string]HelmChartStatusConditions `json:"helmChartStatus,omitempty"`
86+
Stages []StageStatus `json:"stages,omitempty"`
7087

7188
// +optional
7289
// +kubebuilder:default:=false
@@ -96,6 +113,27 @@ type ClusterAddon struct {
96113
Status ClusterAddonStatus `json:"status,omitempty"`
97114
}
98115

116+
// GetStagePhase returns helm chart status for the helm chart.
117+
func (r *ClusterAddon) GetStagePhase(helmChartName string, action clusteraddon.Action) StagePhase {
118+
for _, stage := range r.Status.Stages {
119+
if stage.Name == helmChartName && stage.Action == action {
120+
return stage.Phase
121+
}
122+
}
123+
124+
// This cannot occur as we populate phase value with "pending".
125+
return None
126+
}
127+
128+
// SetStagePhase sets the helm chart status phase.
129+
func (r *ClusterAddon) SetStagePhase(helmChartName string, action clusteraddon.Action, phase StagePhase) {
130+
for i, _ := range r.Status.Stages {
131+
if r.Status.Stages[i].Name == helmChartName && r.Status.Stages[i].Action == action {
132+
r.Status.Stages[i].Phase = phase
133+
}
134+
}
135+
}
136+
99137
// GetConditions returns the observations of the operational state of the ClusterAddon resource.
100138
func (r *ClusterAddon) GetConditions() clusterv1.Conditions {
101139
return r.Status.Conditions

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 19 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/clusterstack.x-k8s.io_clusteraddons.yaml

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,6 @@ spec:
172172
- type
173173
type: object
174174
type: array
175-
helmChartStatus:
176-
additionalProperties:
177-
description: HelmChartStatusConditions defines the status of helm
178-
chart in the cluster addon.
179-
type: string
180-
description: HelmChartStatus defines the status of helm chart in the
181-
cluster addon.
182-
type: object
183175
ready:
184176
default: false
185177
type: boolean
@@ -214,6 +206,24 @@ spec:
214206
type: string
215207
type: object
216208
type: array
209+
stages:
210+
description: Stages shows the state of all stages in the current running
211+
hook.
212+
items:
213+
description: StageStatus represents the helm charts of the hook
214+
and it's phases.
215+
properties:
216+
action:
217+
description: Action is the action of the helm chart. e.g. -
218+
apply and delete.
219+
helmChartName:
220+
description: Name represent name of the helm chart
221+
type: string
222+
phase:
223+
description: Phase is the current phase of the helm chart.
224+
type: string
225+
type: object
226+
type: array
217227
type: object
218228
type: object
219229
served: true

internal/controller/clusteraddon_controller.go

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,12 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
280280
return reconcile.Result{}, fmt.Errorf("failed to get addon stages input: %w", err)
281281
}
282282

283+
// clusteraddon.yaml in the release.
284+
clusterAddonConfig, err := clusteraddon.ParseClusterAddonConfig(in.clusterAddonConfigPath)
285+
if err != nil {
286+
return reconcile.Result{}, fmt.Errorf("failed to parse clusteraddon.yaml config: %w", err)
287+
}
288+
283289
var (
284290
oldRelease release.Release
285291
requeue bool
@@ -307,11 +313,13 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
307313
// if a hook is set, it is expected that HelmChartAppliedCondition is removed
308314
if clusterAddon.Spec.Hook != "" {
309315
// if the clusterAddon was ready before, it means this hook is fresh and we have to reset the status
310-
if clusterAddon.Status.Ready {
311-
clusterAddon.Status.HelmChartStatus = make(map[string]csov1alpha1.HelmChartStatusConditions)
312-
}
313-
if clusterAddon.Status.HelmChartStatus == nil {
314-
clusterAddon.Status.HelmChartStatus = make(map[string]csov1alpha1.HelmChartStatusConditions)
316+
if clusterAddon.Status.Ready || len(clusterAddon.Status.Stages) == 0 {
317+
clusterAddon.Status.Stages = make([]csov1alpha1.StageStatus, len(clusterAddonConfig.AddonStages[clusterAddon.Spec.Hook]))
318+
for i, stage := range clusterAddonConfig.AddonStages[clusterAddon.Spec.Hook] {
319+
clusterAddon.Status.Stages[i].Name = stage.HelmChartName
320+
clusterAddon.Status.Stages[i].Action = stage.Action
321+
clusterAddon.Status.Stages[i].Phase = csov1alpha1.Pending
322+
}
315323
}
316324
clusterAddon.Status.Ready = false
317325
}
@@ -320,7 +328,14 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
320328
// Therefore, we have to check whether the ClusterStack is upgraded and if that is the case, the ClusterAddons have to be upgraded as well.
321329
if clusterAddon.Spec.ClusterStack != cluster.Spec.Topology.Class && oldRelease.Meta.Versions.Kubernetes == releaseAsset.Meta.Versions.Kubernetes {
322330
if clusterAddon.Spec.Version != releaseAsset.Meta.Versions.Components.ClusterAddon {
323-
clusterAddon.Status.HelmChartStatus = make(map[string]csov1alpha1.HelmChartStatusConditions)
331+
if clusterAddon.Status.Ready || len(clusterAddon.Status.Stages) == 0 {
332+
clusterAddon.Status.Stages = make([]csov1alpha1.StageStatus, len(clusterAddonConfig.AddonStages["BeforeClusterUpgrade"]))
333+
for i, stage := range clusterAddonConfig.AddonStages["BeforeClusterUpgrade"] {
334+
clusterAddon.Status.Stages[i].Name = stage.HelmChartName
335+
clusterAddon.Status.Stages[i].Action = stage.Action
336+
clusterAddon.Status.Stages[i].Phase = csov1alpha1.Pending
337+
}
338+
}
324339
clusterAddon.Status.Ready = false
325340
conditions.Delete(clusterAddon, csov1alpha1.HelmChartAppliedCondition)
326341
} else {
@@ -336,12 +351,6 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
336351
return reconcile.Result{}, nil
337352
}
338353

339-
// clusteraddon.yaml in the release.
340-
clusterAddonConfig, err := clusteraddon.ParseClusterAddonConfig(in.clusterAddonConfigPath)
341-
if err != nil {
342-
return reconcile.Result{}, fmt.Errorf("failed to parse clusteraddon.yaml config: %w", err)
343-
}
344-
345354
// In case the Kubernetes version stayed the same during an upgrade, the hook server does not trigger and
346355
// we just take the Helm charts that are supposed to be installed in the BeforeClusterUpgrade hook and apply them.
347356
if oldRelease.Meta.Versions.Kubernetes == releaseAsset.Meta.Versions.Kubernetes {
@@ -363,7 +372,7 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
363372
clusterAddon.Status.Resources = make([]*csov1alpha1.Resource, 0)
364373

365374
// remove the helm chart status from the status.
366-
clusterAddon.Status.HelmChartStatus = make(map[string]csov1alpha1.HelmChartStatusConditions)
375+
clusterAddon.Status.Stages = make([]csov1alpha1.StageStatus, 0)
367376

368377
// update the latest cluster class
369378
clusterAddon.Spec.ClusterStack = cluster.Spec.Topology.Class
@@ -397,7 +406,7 @@ func (r *ClusterAddonReconciler) Reconcile(ctx context.Context, req reconcile.Re
397406
conditions.MarkTrue(clusterAddon, csov1alpha1.HelmChartAppliedCondition)
398407

399408
// remove the helm chart status from the status.
400-
clusterAddon.Status.HelmChartStatus = make(map[string]csov1alpha1.HelmChartStatusConditions)
409+
clusterAddon.Status.Stages = make([]csov1alpha1.StageStatus, 0)
401410

402411
// remove the status resource if hook is finished
403412
clusterAddon.Status.Resources = make([]*csov1alpha1.Resource, 0)
@@ -534,8 +543,8 @@ func (r *ClusterAddonReconciler) executeStage(ctx context.Context, stage cluster
534543
}
535544

536545
check:
537-
switch in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] {
538-
case csov1alpha1.None, csov1alpha1.WaitingForPreCondition:
546+
switch in.clusterAddon.GetStagePhase(stage.HelmChartName, stage.Action) {
547+
case csov1alpha1.Pending, csov1alpha1.WaitingForPreCondition:
539548
// If WaitForPreCondition is mentioned.
540549
if !reflect.DeepEqual(stage.WaitForPreCondition, clusteraddon.WaitForCondition{}) {
541550
// Evaluate the condition.
@@ -550,15 +559,16 @@ check:
550559
"failed to successfully evaluate pre condition: %q: %s", stage.HelmChartName, err.Error(),
551560
)
552561

553-
in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] = csov1alpha1.WaitingForPreCondition
562+
in.clusterAddon.SetStagePhase(stage.HelmChartName, stage.Action, csov1alpha1.WaitingForPreCondition)
554563

555564
return true, nil
556565
}
557566
return false, fmt.Errorf("failed to get dynamic resource and evaluate cel expression for pre condition: %w", err)
558567
}
559568
logger.V(1).Info("finished evaluating pre condition", "clusterStack", in.clusterAddon.Spec.ClusterStack, "helm chart", stage.HelmChartName, "hook", in.clusterAddon.Spec.Hook)
560569
}
561-
in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] = csov1alpha1.ApplyingOrDeleting
570+
571+
in.clusterAddon.SetStagePhase(stage.HelmChartName, stage.Action, csov1alpha1.ApplyingOrDeleting)
562572
goto check
563573

564574
case csov1alpha1.ApplyingOrDeleting:
@@ -584,7 +594,7 @@ check:
584594
// remove status resource if applied successfully
585595
in.clusterAddon.Status.Resources = make([]*csov1alpha1.Resource, 0)
586596

587-
in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] = csov1alpha1.WaitingForPostCondition
597+
in.clusterAddon.SetStagePhase(stage.HelmChartName, stage.Action, csov1alpha1.WaitingForPostCondition)
588598
goto check
589599

590600
} else {
@@ -610,7 +620,7 @@ check:
610620
// remove status resource if deleted successfully
611621
in.clusterAddon.Status.Resources = make([]*csov1alpha1.Resource, 0)
612622

613-
in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] = csov1alpha1.WaitingForPostCondition
623+
in.clusterAddon.SetStagePhase(stage.HelmChartName, stage.Action, csov1alpha1.WaitingForPostCondition)
614624
goto check
615625
}
616626

@@ -636,7 +646,7 @@ check:
636646
logger.V(1).Info("finished evaluating post condition", "clusterStack", in.clusterAddon.Spec.ClusterStack, "helm chart", stage.HelmChartName, "hook", in.clusterAddon.Spec.Hook)
637647
}
638648

639-
in.clusterAddon.Status.HelmChartStatus[stage.HelmChartName] = csov1alpha1.Done
649+
in.clusterAddon.SetStagePhase(stage.HelmChartName, stage.Action, csov1alpha1.Done)
640650
}
641651

642652
return false, nil

test/cluster-stacks/2/docker/ferrol/1-27/clusteraddon.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ addonStages:
99
BeforeClusterUpgrade:
1010
- helmChartName: metrics-server
1111
action: apply
12+
- helmChartName: metrics-server
13+
action: delete
1214
- helmChartName: ngnix
1315
action: apply
1416
waitForPostCondition:

test/cluster-stacks/2/docker/ferrol/1-27/csmctl.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
apiVersion: csmctl.clusterstack.x-k8s.io/v1alpha1
22
config:
3-
kubernetesVersion: v1.27.7
3+
kubernetesVersion: v1.27.6
44
clusterStackName: ferrol
55
provider:
66
type: docker

0 commit comments

Comments
 (0)