Skip to content

Commit 60e4ff4

Browse files
committedJan 27, 2025
add Ready condition to Management resources
Signed-off-by: Satyam Bhardwaj <sbhardwaj@mirantis.com>
1 parent 7fbf627 commit 60e4ff4

File tree

7 files changed

+146
-4
lines changed

7 files changed

+146
-4
lines changed
 

‎api/v1alpha1/clusterdeployment_types.go

-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ const (
4646
HelmChartReadyCondition = "HelmChartReady"
4747
// HelmReleaseReadyCondition indicates the corresponding HelmRelease is ready and fully reconciled.
4848
HelmReleaseReadyCondition = "HelmReleaseReady"
49-
// ReadyCondition indicates the ClusterDeployment is ready and fully reconciled.
50-
ReadyCondition string = "Ready"
5149
)
5250

5351
// ClusterDeploymentSpec defines the desired state of ClusterDeployment

‎api/v1alpha1/common.go

+3
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ const (
2828
ProgressingReason string = "Progressing"
2929
)
3030

31+
// ReadyCondition indicates a resource is ready and fully reconciled.
32+
const ReadyCondition string = "Ready"
33+
3134
type (
3235
// Holds different types of CAPI providers.
3336
Providers []string

‎api/v1alpha1/management_types.go

+16-1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,13 @@ type ManagementSpec struct {
4545
Providers []Provider `json:"providers,omitempty"`
4646
}
4747

48+
const (
49+
// AllComponentsHealthyReason surfaces overall readiness of Management's components.
50+
AllComponentsHealthyReason = "AllComponentsHealthy"
51+
// NotAllComponentsHealthyReason documents a condition not in Status=True because one or more components are failing.
52+
NotAllComponentsHealthyReason = "NotAllComponentsHealthy"
53+
)
54+
4855
// Core represents a structure describing core Management components.
4956
type Core struct {
5057
// KCM represents the core KCM component and references the KCM template.
@@ -111,6 +118,11 @@ type ManagementStatus struct {
111118
CAPIContracts map[string]CompatibilityContracts `json:"capiContracts,omitempty"`
112119
// Components indicates the status of installed KCM components and CAPI providers.
113120
Components map[string]ComponentStatus `json:"components,omitempty"`
121+
// Conditions represents the observations of a Management's current state.
122+
// +listType=map
123+
// +listMapKey=type
124+
// +kubebuilder:validation:MaxItems=32
125+
Conditions []metav1.Condition `json:"conditions,omitempty"`
114126
// BackupName is a name of the management cluster scheduled backup.
115127
BackupName string `json:"backupName,omitempty"`
116128
// Release indicates the current Release object.
@@ -132,8 +144,11 @@ type ComponentStatus struct {
132144
}
133145

134146
// +kubebuilder:object:root=true
135-
// +kubebuilder:subresource:status
136147
// +kubebuilder:resource:shortName=kcm-mgmt;mgmt,scope=Cluster
148+
// +kubebuilder:subresource:status
149+
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status",description="Overall readiness of the Management resource"
150+
// +kubebuilder:printcolumn:name="Release",type="string",JSONPath=".status.release",description="Current release version"
151+
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="Time duration since creation of Management"
137152

138153
// Management is the Schema for the managements API
139154
type Management struct {

‎api/v1alpha1/zz_generated.deepcopy.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎internal/controller/management_controller.go

+31
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"errors"
2121
"fmt"
2222
"slices"
23+
"sort"
2324
"strings"
2425
"time"
2526

@@ -31,6 +32,7 @@ import (
3132
appsv1 "k8s.io/api/apps/v1"
3233
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
3334
apierrors "k8s.io/apimachinery/pkg/api/errors"
35+
"k8s.io/apimachinery/pkg/api/meta"
3436
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3537
"k8s.io/apimachinery/pkg/labels"
3638
"k8s.io/apimachinery/pkg/runtime"
@@ -222,6 +224,8 @@ func (r *ManagementReconciler) Update(ctx context.Context, management *kcm.Manag
222224
requeue = true
223225
}
224226

227+
setReadyCondition(management)
228+
225229
if err := r.Client.Status().Update(ctx, management); err != nil {
226230
errs = errors.Join(errs, fmt.Errorf("failed to update status for Management %s: %w", management.Name, err))
227231
}
@@ -772,6 +776,33 @@ func updateComponentsStatus(
772776
}
773777
}
774778

779+
// setReadyCondition updates the Management resource's "Ready" condition based on whether
780+
// all components are healthy.
781+
func setReadyCondition(management *kcm.Management) {
782+
var failing []string
783+
for name, comp := range management.Status.Components {
784+
if !comp.Success {
785+
failing = append(failing, name)
786+
}
787+
}
788+
789+
readyCond := metav1.Condition{
790+
Type: kcm.ReadyCondition,
791+
ObservedGeneration: management.Generation,
792+
Status: metav1.ConditionTrue,
793+
Reason: kcm.AllComponentsHealthyReason,
794+
Message: "All components are successfully installed.",
795+
}
796+
sort.Strings(failing)
797+
if len(failing) > 0 {
798+
readyCond.Status = metav1.ConditionFalse
799+
readyCond.Reason = kcm.NotAllComponentsHealthyReason
800+
readyCond.Message = fmt.Sprintf("Components not ready: %v", failing)
801+
}
802+
803+
meta.SetStatusCondition(&management.Status.Conditions, readyCond)
804+
}
805+
775806
// SetupWithManager sets up the controller with the Manager.
776807
func (r *ManagementReconciler) SetupWithManager(mgr ctrl.Manager) error {
777808
dc, err := dynamic.NewForConfig(mgr.GetConfig())

‎internal/controller/management_controller_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
. "github.com/onsi/gomega"
2727
corev1 "k8s.io/api/core/v1"
2828
apierrors "k8s.io/apimachinery/pkg/api/errors"
29+
"k8s.io/apimachinery/pkg/api/meta"
2930
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
3031
"k8s.io/apimachinery/pkg/types"
3132
capioperator "sigs.k8s.io/cluster-api-operator/api/v1alpha2"
@@ -355,6 +356,12 @@ var _ = Describe("Management Controller", func() {
355356
},
356357
}))
357358

359+
By("Expecting condition Ready=False Management status")
360+
cond := meta.FindStatusCondition(mgmt.Status.Conditions, kcmv1.ReadyCondition)
361+
Expect(cond).NotTo(BeNil(), "Expected Ready condition to exist after reconcile")
362+
Expect(cond.Status).To(Equal(metav1.ConditionFalse), "Expected Ready to be False")
363+
Expect(cond.Reason).To(Equal(kcmv1.NotAllComponentsHealthyReason))
364+
358365
By("Updating capi HelmRelease with Ready condition")
359366
helmRelease = &helmcontrollerv2.HelmRelease{}
360367
Expect(k8sClient.Get(ctx, types.NamespacedName{
@@ -422,6 +429,12 @@ var _ = Describe("Management Controller", func() {
422429
kcmv1.CoreCAPIName: {Success: true, Template: providerTemplateRequiredComponent},
423430
}))
424431

432+
By("Expecting condition Ready=True Management status")
433+
cond = meta.FindStatusCondition(mgmt.Status.Conditions, kcmv1.ReadyCondition)
434+
Expect(cond).NotTo(BeNil(), "Expected Ready condition to exist")
435+
Expect(cond.Status).To(Equal(metav1.ConditionTrue), "Expected Ready to be True")
436+
Expect(cond.Reason).To(Equal(kcmv1.AllComponentsHealthyReason))
437+
425438
By("Removing the leftover objects")
426439
mgmt.Finalizers = nil
427440
Expect(k8sClient.Update(ctx, mgmt)).To(Succeed())

‎templates/provider/kcm/templates/crds/k0rdent.mirantis.com_managements.yaml

+76-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,20 @@ spec:
1717
singular: management
1818
scope: Cluster
1919
versions:
20-
- name: v1alpha1
20+
- additionalPrinterColumns:
21+
- description: Overall readiness of the Management resource
22+
jsonPath: .status.conditions[?(@.type=='Ready')].status
23+
name: Ready
24+
type: string
25+
- description: Current release version
26+
jsonPath: .status.release
27+
name: Release
28+
type: string
29+
- description: Time duration since creation of Management
30+
jsonPath: .metadata.creationTimestamp
31+
name: Age
32+
type: date
33+
name: v1alpha1
2134
schema:
2235
openAPIV3Schema:
2336
description: Management is the Schema for the managements API
@@ -163,6 +176,68 @@ spec:
163176
description: Components indicates the status of installed KCM components
164177
and CAPI providers.
165178
type: object
179+
conditions:
180+
description: Conditions represents the observations of a Management's
181+
current state.
182+
items:
183+
description: Condition contains details for one aspect of the current
184+
state of this API Resource.
185+
properties:
186+
lastTransitionTime:
187+
description: |-
188+
lastTransitionTime is the last time the condition transitioned from one status to another.
189+
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
190+
format: date-time
191+
type: string
192+
message:
193+
description: |-
194+
message is a human readable message indicating details about the transition.
195+
This may be an empty string.
196+
maxLength: 32768
197+
type: string
198+
observedGeneration:
199+
description: |-
200+
observedGeneration represents the .metadata.generation that the condition was set based upon.
201+
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
202+
with respect to the current state of the instance.
203+
format: int64
204+
minimum: 0
205+
type: integer
206+
reason:
207+
description: |-
208+
reason contains a programmatic identifier indicating the reason for the condition's last transition.
209+
Producers of specific condition types may define expected values and meanings for this field,
210+
and whether the values are considered a guaranteed API.
211+
The value should be a CamelCase string.
212+
This field may not be empty.
213+
maxLength: 1024
214+
minLength: 1
215+
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
216+
type: string
217+
status:
218+
description: status of the condition, one of True, False, Unknown.
219+
enum:
220+
- "True"
221+
- "False"
222+
- Unknown
223+
type: string
224+
type:
225+
description: type of condition in CamelCase or in foo.example.com/CamelCase.
226+
maxLength: 316
227+
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
228+
type: string
229+
required:
230+
- lastTransitionTime
231+
- message
232+
- reason
233+
- status
234+
- type
235+
type: object
236+
maxItems: 32
237+
type: array
238+
x-kubernetes-list-map-keys:
239+
- type
240+
x-kubernetes-list-type: map
166241
observedGeneration:
167242
description: ObservedGeneration is the last observed generation.
168243
format: int64

0 commit comments

Comments
 (0)