Skip to content

Commit cc88cbb

Browse files
Add clusterClass generation check to Cluster reconciler
1 parent ad074ef commit cc88cbb

File tree

3 files changed

+43
-17
lines changed

3 files changed

+43
-17
lines changed

api/v1beta1/condition_consts.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,10 @@ const (
276276
// TopologyReconciledHookBlockingReason (Severity=Info) documents reconciliation of a Cluster topology
277277
// not yet completed because at least one of the lifecycle hooks is blocking.
278278
TopologyReconciledHookBlockingReason = "LifecycleHookBlocking"
279+
280+
// TopologyReconciledClusterClassOutdatedReason (Severity=Info) documents reconciliation of a Cluster topology not
281+
// yet completed because the ClusterClass `status.observedGeneration` is not the same as its `metadata.generation`.
282+
TopologyReconciledClusterClassOutdatedReason = "LifecycleHookBlocking"
279283
)
280284

281285
// Conditions and condition reasons for ClusterClass.

internal/controllers/topology/cluster/cluster_controller.go

Lines changed: 23 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -175,26 +175,9 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
175175
return ctrl.Result{}, err
176176
}
177177

178-
// Get ClusterClass.
179-
clusterClass := &clusterv1.ClusterClass{}
180-
key := client.ObjectKey{Name: cluster.Spec.Topology.Class, Namespace: cluster.Namespace}
181-
if err := r.Client.Get(ctx, key, clusterClass); err != nil {
182-
return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve ClusterClass %s", cluster.Spec.Topology.Class)
183-
}
184-
// Default and Validate the Cluster based on information from the ClusterClass.
185-
// This step is needed as if the ClusterClass does not exist at Cluster creation some fields may not be defaulted or
186-
// validated in the webhook.
187-
if errs := webhooks.DefaultVariables(cluster, clusterClass); len(errs) > 0 {
188-
return ctrl.Result{}, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), cluster.Name, errs)
189-
}
190-
if errs := webhooks.ValidateClusterForClusterClass(cluster, clusterClass, field.NewPath("spec", "topology")); len(errs) > 0 {
191-
return ctrl.Result{}, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), cluster.Name, errs)
192-
}
193-
194178
// Create a scope initialized with only the cluster; during reconcile
195179
// additional information will be added about the Cluster blueprint, current state and desired state.
196180
s := scope.New(cluster)
197-
s.Blueprint.ClusterClass = clusterClass
198181

199182
defer func() {
200183
if err := r.reconcileConditions(s, cluster, reterr); err != nil {
@@ -221,6 +204,29 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (_ ctrl.Re
221204
func (r *Reconciler) reconcile(ctx context.Context, s *scope.Scope) (ctrl.Result, error) {
222205
var err error
223206

207+
// Get ClusterClass.
208+
clusterClass := &clusterv1.ClusterClass{}
209+
key := client.ObjectKey{Name: s.Current.Cluster.Spec.Topology.Class, Namespace: s.Current.Cluster.Namespace}
210+
if err := r.Client.Get(ctx, key, clusterClass); err != nil {
211+
return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve ClusterClass %s", s.Current.Cluster.Spec.Topology.Class)
212+
}
213+
214+
s.Blueprint.ClusterClass = clusterClass
215+
// If the ClusterClass `metadata.Generation` doesn't match the `status.ObservedGeneration` requeue as the ClusterClass
216+
// is not up to date.
217+
if clusterClass.GetGeneration() != clusterClass.Status.ObservedGeneration {
218+
return ctrl.Result{}, nil
219+
}
220+
221+
// Default and Validate the Cluster based on information from the ClusterClass.
222+
// This step is needed as if the ClusterClass does not exist at Cluster creation some fields may not be defaulted or
223+
// validated in the webhook.
224+
if errs := webhooks.DefaultVariables(s.Current.Cluster, clusterClass); len(errs) > 0 {
225+
return ctrl.Result{}, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), s.Current.Cluster.Name, errs)
226+
}
227+
if errs := webhooks.ValidateClusterForClusterClass(s.Current.Cluster, clusterClass, field.NewPath("spec", "topology")); len(errs) > 0 {
228+
return ctrl.Result{}, apierrors.NewInvalid(clusterv1.GroupVersion.WithKind("Cluster").GroupKind(), s.Current.Cluster.Name, errs)
229+
}
224230
// Gets the blueprint with the ClusterClass and the referenced templates
225231
// and store it in the request scope.
226232
s.Blueprint, err = r.getBlueprint(ctx, s.Current.Cluster, s.Blueprint.ClusterClass)

internal/controllers/topology/cluster/conditions.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ func (r *Reconciler) reconcileTopologyReconciledCondition(s *scope.Scope, cluste
5858
return nil
5959
}
6060

61+
// If the ClusterClass `metadata.Generation` doesn't match the `status.ObservedGeneration` requeue as the ClusterClass
62+
// is not up to date.
63+
if s.Blueprint != nil && s.Blueprint.ClusterClass != nil &&
64+
s.Blueprint.ClusterClass.GetGeneration() != s.Blueprint.ClusterClass.Status.ObservedGeneration {
65+
conditions.Set(
66+
cluster,
67+
conditions.FalseCondition(
68+
clusterv1.TopologyReconciledCondition,
69+
clusterv1.ClusterClassOutdatedRefVersionsReason,
70+
clusterv1.ConditionSeverityInfo,
71+
"ClusterClass is outdated. If this condition persists please check ClusterClass status.",
72+
),
73+
)
74+
return nil
75+
}
76+
6177
// If any of the lifecycle hooks are blocking any part of the reconciliation then topology
6278
// is not considered as fully reconciled.
6379
if s.HookResponseTracker.AggregateRetryAfter() != 0 {

0 commit comments

Comments
 (0)