Skip to content

Commit d1a2301

Browse files
authored
Merge pull request #10703 from vincepri/add-class-key-ref
🌱 Add Cluster.GetClassKey() to retrieve a NamespacedName for classes
2 parents b677359 + 1d6b58e commit d1a2301

File tree

9 files changed

+32
-22
lines changed

9 files changed

+32
-22
lines changed

api/v1beta1/cluster_types.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
corev1 "k8s.io/api/core/v1"
2626
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
2727
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+
"k8s.io/apimachinery/pkg/types"
2829
"k8s.io/utils/ptr"
2930

3031
capierrors "sigs.k8s.io/cluster-api/errors"
@@ -509,6 +510,14 @@ type Cluster struct {
509510
Status ClusterStatus `json:"status,omitempty"`
510511
}
511512

513+
// GetClassKey returns the namespaced name for the class associated with this object.
514+
func (c *Cluster) GetClassKey() types.NamespacedName {
515+
if c.Spec.Topology == nil {
516+
return types.NamespacedName{}
517+
}
518+
return types.NamespacedName{Namespace: c.GetNamespace(), Name: c.Spec.Topology.Class}
519+
}
520+
512521
// GetConditions returns the set of conditions for this object.
513522
func (c *Cluster) GetConditions() Conditions {
514523
return c.Status.Conditions

api/v1beta1/index/cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ func ClusterByClusterClassClassName(o client.Object) []string {
5151
panic(fmt.Sprintf("Expected Cluster but got a %T", o))
5252
}
5353
if cluster.Spec.Topology != nil {
54-
return []string{cluster.Spec.Topology.Class}
54+
return []string{cluster.GetClassKey().Name}
5555
}
5656
return nil
5757
}

cmd/clusterctl/client/cluster/objectgraph.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (n *node) captureAdditionalInformation(obj *unstructured.Unstructured) erro
148148
if n.additionalInfo == nil {
149149
n.additionalInfo = map[string]interface{}{}
150150
}
151-
n.additionalInfo[clusterTopologyNameKey] = cluster.Spec.Topology.Class
151+
n.additionalInfo[clusterTopologyNameKey] = cluster.GetClassKey().Name
152152
}
153153
}
154154

cmd/clusterctl/client/cluster/topology.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -695,7 +695,8 @@ func (t *topologyClient) affectedClusters(ctx context.Context, in *TopologyPlanI
695695
// Each of the Cluster that uses the ClusterClass in the input is an affected cluster.
696696
for _, cc := range affectedClusterClasses {
697697
for i := range clusterList.Items {
698-
if clusterList.Items[i].Spec.Topology != nil && clusterList.Items[i].Spec.Topology.Class == cc.Name {
698+
cluster := clusterList.Items[i]
699+
if cluster.Spec.Topology != nil && cluster.GetClassKey().Name == cc.Name {
699700
affectedClusters[client.ObjectKeyFromObject(&clusterList.Items[i])] = true
700701
}
701702
}

cmd/clusterctl/client/clusterclass.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func clusterClassNamesFromTemplate(template Template) ([]string, error) {
8080
if cluster.Spec.Topology == nil {
8181
continue
8282
}
83-
classes = append(classes, cluster.Spec.Topology.Class)
83+
classes = append(classes, cluster.GetClassKey().Name)
8484
}
8585
return classes, nil
8686
}

internal/controllers/topology/cluster/cluster_controller.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -217,9 +217,9 @@ func (r *Reconciler) reconcile(ctx context.Context, s *scope.Scope) (ctrl.Result
217217

218218
// Get ClusterClass.
219219
clusterClass := &clusterv1.ClusterClass{}
220-
key := client.ObjectKey{Name: s.Current.Cluster.Spec.Topology.Class, Namespace: s.Current.Cluster.Namespace}
220+
key := s.Current.Cluster.GetClassKey()
221221
if err := r.Client.Get(ctx, key, clusterClass); err != nil {
222-
return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve ClusterClass %s", s.Current.Cluster.Spec.Topology.Class)
222+
return ctrl.Result{}, errors.Wrapf(err, "failed to retrieve ClusterClass %s", key)
223223
}
224224

225225
s.Blueprint.ClusterClass = clusterClass

internal/controllers/topology/cluster/cluster_controller_test.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ func TestClusterReconciler_reconcileUpdatesOnClusterClass(t *testing.T) {
303303

304304
// Get the clusterClass to update and check if clusterClass updates are being correctly reconciled.
305305
clusterClass := &clusterv1.ClusterClass{}
306-
g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: actualCluster.Spec.Topology.Class}, clusterClass)).To(Succeed())
306+
g.Expect(env.Get(ctx, actualCluster.GetClassKey(), clusterClass)).To(Succeed())
307307

308308
patchHelper, err := patch.NewHelper(clusterClass, env.Client)
309309
g.Expect(err).ToNot(HaveOccurred())
@@ -317,7 +317,7 @@ func TestClusterReconciler_reconcileUpdatesOnClusterClass(t *testing.T) {
317317
// Check that the clusterClass has been correctly updated to use the new infrastructure template.
318318
// This is necessary as sometimes the cache can take a little time to update.
319319
class := &clusterv1.ClusterClass{}
320-
g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: actualCluster.Spec.Topology.Class}, class)).To(Succeed())
320+
g.Expect(env.Get(ctx, actualCluster.GetClassKey(), class)).To(Succeed())
321321
g.Expect(class.Spec.Workers.MachineDeployments[0].Template.Infrastructure.Ref.Name).To(Equal(infrastructureMachineTemplateName2))
322322
g.Expect(class.Spec.Workers.MachinePools[0].Template.Infrastructure.Ref.Name).To(Equal(infrastructureMachinePoolTemplateName2))
323323

@@ -412,7 +412,7 @@ func TestClusterReconciler_reconcileClusterClassRebase(t *testing.T) {
412412
return err
413413
}
414414
// Check to ensure the spec.topology.class has been successfully updated in the API server and cache.
415-
g.Expect(updatedCluster.Spec.Topology.Class).To(Equal(clusterClassName2))
415+
g.Expect(updatedCluster.GetClassKey().Name).To(Equal(clusterClassName2))
416416
// Check if Cluster has relevant Infrastructure and ControlPlane and labels and annotations.
417417
g.Expect(assertClusterReconcile(updatedCluster)).Should(Succeed())
418418

@@ -633,7 +633,7 @@ func TestClusterReconciler_deleteClusterClass(t *testing.T) {
633633

634634
// Ensure the clusterClass is available in the API server .
635635
clusterClass := &clusterv1.ClusterClass{}
636-
g.Expect(env.Get(ctx, client.ObjectKey{Namespace: ns.Name, Name: actualCluster.Spec.Topology.Class}, clusterClass)).To(Succeed())
636+
g.Expect(env.Get(ctx, actualCluster.GetClassKey(), clusterClass)).To(Succeed())
637637

638638
// Attempt to delete the ClusterClass. Expect an error here as the ClusterClass deletion is blocked by the webhook.
639639
g.Expect(env.Delete(ctx, clusterClass)).NotTo(Succeed())
@@ -968,7 +968,7 @@ func assertControlPlaneReconcile(cluster *clusterv1.Cluster) error {
968968
}
969969
}
970970
clusterClass := &clusterv1.ClusterClass{}
971-
if err := env.Get(ctx, client.ObjectKey{Namespace: cluster.Namespace, Name: cluster.Spec.Topology.Class}, clusterClass); err != nil {
971+
if err := env.Get(ctx, cluster.GetClassKey(), clusterClass); err != nil {
972972
return err
973973
}
974974
// Check for the ControlPlaneInfrastructure if it's referenced in the clusterClass.

internal/controllers/topology/cluster/patches/variables/variables.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ func Global(clusterTopology *clusterv1.Topology, cluster *clusterv1.Cluster, def
6363
Namespace: cluster.Namespace,
6464
Topology: &runtimehooksv1.ClusterTopologyBuiltins{
6565
Version: cluster.Spec.Topology.Version,
66-
Class: cluster.Spec.Topology.Class,
66+
Class: cluster.GetClassKey().Name,
6767
},
6868
},
6969
}

internal/webhooks/cluster.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (webhook *Cluster) Default(ctx context.Context, obj runtime.Object) error {
102102
cluster.Spec.Topology.Version = "v" + cluster.Spec.Topology.Version
103103
}
104104

105-
if cluster.Spec.Topology.Class == "" {
105+
if cluster.GetClassKey().Name == "" {
106106
allErrs = append(
107107
allErrs,
108108
field.Required(
@@ -119,7 +119,7 @@ func (webhook *Cluster) Default(ctx context.Context, obj runtime.Object) error {
119119
if apierrors.IsNotFound(err) || errors.Is(err, errClusterClassNotReconciled) {
120120
return nil
121121
}
122-
return apierrors.NewInternalError(errors.Wrapf(err, "Cluster %s can't be defaulted. ClusterClass %s can not be retrieved", cluster.Name, cluster.Spec.Topology.Class))
122+
return apierrors.NewInternalError(errors.Wrapf(err, "Cluster %s can't be defaulted. ClusterClass %s can not be retrieved", cluster.Name, cluster.GetClassKey().Name))
123123
}
124124

125125
// Doing both defaulting and validating here prevents a race condition where the ClusterClass could be
@@ -254,7 +254,7 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
254254
var allErrs field.ErrorList
255255

256256
// class should be defined.
257-
if newCluster.Spec.Topology.Class == "" {
257+
if newCluster.GetClassKey().Name == "" {
258258
allErrs = append(
259259
allErrs,
260260
field.Required(
@@ -334,7 +334,7 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
334334
}
335335

336336
// Topology or Class can not be added on update unless ClusterTopologyUnsafeUpdateClassNameAnnotation is set.
337-
if oldCluster.Spec.Topology == nil || oldCluster.Spec.Topology.Class == "" {
337+
if oldCluster.Spec.Topology == nil || oldCluster.GetClassKey().Name == "" {
338338
if _, ok := newCluster.Annotations[clusterv1.ClusterTopologyUnsafeUpdateClassNameAnnotation]; ok {
339339
return allWarnings, allErrs
340340
}
@@ -386,15 +386,15 @@ func (webhook *Cluster) validateTopology(ctx context.Context, oldCluster, newClu
386386
}
387387

388388
// If the ClusterClass referenced in the Topology has changed compatibility checks are needed.
389-
if oldCluster.Spec.Topology.Class != newCluster.Spec.Topology.Class {
389+
if oldCluster.GetClassKey() != newCluster.GetClassKey() {
390390
// Check to see if the ClusterClass referenced in the old version of the Cluster exists.
391391
oldClusterClass, err := webhook.pollClusterClassForCluster(ctx, oldCluster)
392392
if err != nil {
393393
allErrs = append(
394394
allErrs, field.Forbidden(
395395
fldPath.Child("class"),
396396
fmt.Sprintf("valid ClusterClass with name %q could not be retrieved, change from class %[1]q to class %q cannot be validated. Error: %s",
397-
oldCluster.Spec.Topology.Class, newCluster.Spec.Topology.Class, err.Error())))
397+
oldCluster.GetClassKey(), newCluster.GetClassKey(), err.Error())))
398398

399399
// Return early with errors if the ClusterClass can't be retrieved.
400400
return allWarnings, allErrs
@@ -854,20 +854,20 @@ func (webhook *Cluster) validateClusterClassExistsAndIsReconciled(ctx context.Co
854854
fmt.Sprintf(
855855
"Cluster refers to ClusterClass %s in the topology but it does not exist. "+
856856
"Cluster topology has not been fully validated. "+
857-
"The ClusterClass must be created to reconcile the Cluster", newCluster.Spec.Topology.Class),
857+
"The ClusterClass must be created to reconcile the Cluster", newCluster.GetClassKey()),
858858
)
859859
case errors.Is(clusterClassPollErr, errClusterClassNotReconciled):
860860
allWarnings = append(allWarnings,
861861
fmt.Sprintf(
862862
"Cluster refers to ClusterClass %s but this object which hasn't yet been reconciled. "+
863-
"Cluster topology has not been fully validated. ", newCluster.Spec.Topology.Class),
863+
"Cluster topology has not been fully validated. ", newCluster.GetClassKey()),
864864
)
865865
// If there's any other error return a generic warning with the error message.
866866
default:
867867
allWarnings = append(allWarnings,
868868
fmt.Sprintf(
869869
"Cluster refers to ClusterClass %s in the topology but it could not be retrieved. "+
870-
"Cluster topology has not been fully validated: %s", newCluster.Spec.Topology.Class, clusterClassPollErr.Error()),
870+
"Cluster topology has not been fully validated: %s", newCluster.GetClassKey(), clusterClassPollErr.Error()),
871871
)
872872
}
873873
}
@@ -879,7 +879,7 @@ func (webhook *Cluster) pollClusterClassForCluster(ctx context.Context, cluster
879879
clusterClass := &clusterv1.ClusterClass{}
880880
var clusterClassPollErr error
881881
_ = wait.PollUntilContextTimeout(ctx, 200*time.Millisecond, 2*time.Second, true, func(ctx context.Context) (bool, error) {
882-
if clusterClassPollErr = webhook.Client.Get(ctx, client.ObjectKey{Namespace: cluster.Namespace, Name: cluster.Spec.Topology.Class}, clusterClass); clusterClassPollErr != nil {
882+
if clusterClassPollErr = webhook.Client.Get(ctx, cluster.GetClassKey(), clusterClass); clusterClassPollErr != nil {
883883
return false, nil //nolint:nilerr
884884
}
885885

0 commit comments

Comments
 (0)