Skip to content

Commit 61c9e21

Browse files
authored
Merge pull request #5762 from Jont828/tree-hacks
✨Add `clusterctl` options to show templates and cluster resource sets
2 parents 70c9771 + a86e886 commit 61c9e21

File tree

13 files changed

+840
-76
lines changed

13 files changed

+840
-76
lines changed

cmd/clusterctl/client/describe.go

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ type DescribeClusterOptions struct {
4141
// ShowMachineSets instructs the discovery process to include machine sets in the ObjectTree.
4242
ShowMachineSets bool
4343

44+
// ShowClusterResourceSets instructs the discovery process to include cluster resource sets in the ObjectTree.
45+
ShowClusterResourceSets bool
46+
47+
// ShowTemplates instructs the discovery process to include infrastructure and bootstrap config templates in the ObjectTree.
48+
ShowTemplates bool
49+
50+
// AddTemplateVirtualNode instructs the discovery process to group template under a virtual node.
51+
AddTemplateVirtualNode bool
52+
4453
// Echo displays MachineInfrastructure or BootstrapConfig objects if the object's ready condition is true
4554
// or it has the same Status, Severity and Reason of the parent's object ready condition (it is an echo)
4655
Echo bool
@@ -80,9 +89,12 @@ func (c *clusterctlClient) DescribeCluster(options DescribeClusterOptions) (*tre
8089

8190
// Gets the object tree representing the status of a Cluster API cluster.
8291
return tree.Discovery(context.TODO(), client, options.Namespace, options.ClusterName, tree.DiscoverOptions{
83-
ShowOtherConditions: options.ShowOtherConditions,
84-
ShowMachineSets: options.ShowMachineSets,
85-
Echo: options.Echo,
86-
Grouping: options.Grouping,
92+
ShowOtherConditions: options.ShowOtherConditions,
93+
ShowMachineSets: options.ShowMachineSets,
94+
ShowClusterResourceSets: options.ShowClusterResourceSets,
95+
ShowTemplates: options.ShowTemplates,
96+
AddTemplateVirtualNode: options.AddTemplateVirtualNode,
97+
Echo: options.Echo,
98+
Grouping: options.Grouping,
8799
})
88100
}

cmd/clusterctl/client/tree/annotations.go

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ const (
4949

5050
// GroupItemsSeparator is the separator used in the GroupItemsAnnotation.
5151
GroupItemsSeparator = ", "
52+
53+
// ObjectZOrderAnnotation contains an integer that defines the sorting of child objects when the object tree is printed.
54+
// Objects are sorted by their z-order from highest to lowest, and then by their name in alphaebetical order if the
55+
// z-order is the same. Objects with no z-order set are assumed to have a default z-order of 0.
56+
ObjectZOrderAnnotation = "tree.cluster.x-k8s.io.io/z-order"
5257
)
5358

5459
// GetMetaName returns the object meta name that should be used for the object in the presentation layer, if defined.
@@ -69,7 +74,7 @@ func IsGroupingObject(obj client.Object) bool {
6974
return false
7075
}
7176

72-
// IsGroupObject return true if the object is the result of a grouping operation, and
77+
// IsGroupObject returns true if the object is the result of a grouping operation, and
7378
// thus the object is representing group of sibling object, e.g. a group of machines.
7479
func IsGroupObject(obj client.Object) bool {
7580
if val, ok := getBoolAnnotation(obj, GroupObjectAnnotation); ok {
@@ -78,15 +83,25 @@ func IsGroupObject(obj client.Object) bool {
7883
return false
7984
}
8085

81-
// GetGroupItems return the list of names for the objects included in a group object.
86+
// GetGroupItems returns the list of names for the objects included in a group object.
8287
func GetGroupItems(obj client.Object) string {
8388
if val, ok := getAnnotation(obj, GroupItemsAnnotation); ok {
8489
return val
8590
}
8691
return ""
8792
}
8893

89-
// IsVirtualObject return true if the object does not correspond to any real object, but instead it is
94+
// GetZOrder return the zOrder of the object. Objects with no zOrder have a default zOrder of 0.
95+
func GetZOrder(obj client.Object) int {
96+
if val, ok := getAnnotation(obj, ObjectZOrderAnnotation); ok {
97+
if zOrder, err := strconv.ParseInt(val, 10, 0); err == nil {
98+
return int(zOrder)
99+
}
100+
}
101+
return 0
102+
}
103+
104+
// IsVirtualObject returns true if the object does not correspond to any real object, but instead it is
90105
// a virtual object introduced to provide a better representation of the cluster status.
91106
func IsVirtualObject(obj client.Object) bool {
92107
if val, ok := getBoolAnnotation(obj, VirtualObjectAnnotation); ok {

cmd/clusterctl/client/tree/discovery.go

Lines changed: 175 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,15 @@ package tree
1919
import (
2020
"context"
2121

22+
corev1 "k8s.io/api/core/v1"
2223
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24+
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
2325
"sigs.k8s.io/controller-runtime/pkg/client"
2426

2527
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2628
"sigs.k8s.io/cluster-api/controllers/external"
29+
addonsv1 "sigs.k8s.io/cluster-api/exp/addons/api/v1beta1"
30+
expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
2731
"sigs.k8s.io/cluster-api/util"
2832
)
2933

@@ -36,8 +40,16 @@ type DiscoverOptions struct {
3640
// ShowMachineSets instructs the discovery process to include machine sets in the ObjectTree.
3741
ShowMachineSets bool
3842

43+
// ShowClusterResourceSets instructs the discovery process to include cluster resource sets in the ObjectTree.
44+
ShowClusterResourceSets bool
45+
46+
// ShowTemplates instructs the discovery process to include infrastructure and bootstrap config templates in the ObjectTree.
47+
ShowTemplates bool
48+
49+
// AddTemplateVirtualNode instructs the discovery process to group template under a virtual node.
50+
AddTemplateVirtualNode bool
51+
3952
// Echo displays MachineInfrastructure or BootstrapConfig objects if the object's ready condition is true
40-
// or it has the same Status, Severity and Reason of the parent's object ready condition (it is an echo)
4153
Echo bool
4254

4355
// Grouping groups machine objects in case the ready conditions
@@ -75,10 +87,14 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
7587
tree.Add(cluster, clusterInfra, ObjectMetaName("ClusterInfrastructure"))
7688
}
7789

90+
if options.ShowClusterResourceSets {
91+
addClusterResourceSetsToObjectTree(ctx, c, cluster, tree)
92+
}
93+
7894
// Adds control plane
79-
controlPLane, err := external.Get(ctx, c, cluster.Spec.ControlPlaneRef, cluster.Namespace)
95+
controlPlane, err := external.Get(ctx, c, cluster.Spec.ControlPlaneRef, cluster.Namespace)
8096
if err == nil {
81-
tree.Add(cluster, controlPLane, ObjectMetaName("ControlPlane"), GroupingObject(true))
97+
addControlPlane(cluster, controlPlane, tree, options)
8298
}
8399

84100
// Adds control plane machines.
@@ -105,25 +121,103 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
105121
controlPlaneMachines := selectControlPlaneMachines(machinesList)
106122
for i := range controlPlaneMachines {
107123
cp := controlPlaneMachines[i]
108-
addMachineFunc(controlPLane, cp)
124+
addMachineFunc(controlPlane, cp)
109125
}
110126

111-
if len(machinesList.Items) == len(controlPlaneMachines) {
112-
return tree, nil
127+
machinePoolList, err := getMachinePoolsInCluster(ctx, c, cluster.Namespace, cluster.Name)
128+
if err != nil {
129+
return nil, err
113130
}
114131

115132
workers := VirtualObject(cluster.Namespace, "WorkerGroup", "Workers")
116-
tree.Add(cluster, workers)
133+
// Add WorkerGroup if there are MachineDeployments or MachinePools
134+
if len(machinesList.Items) != len(controlPlaneMachines) || len(machinePoolList.Items) > 0 {
135+
tree.Add(cluster, workers)
136+
}
137+
138+
if len(machinesList.Items) != len(controlPlaneMachines) { // Add MachineDeployment objects
139+
tree.Add(cluster, workers)
140+
err = addMachineDeploymentToObjectTree(ctx, c, cluster, workers, machinesList, tree, options, addMachineFunc)
141+
if err != nil {
142+
return nil, err
143+
}
144+
145+
// Handles orphan machines.
146+
if len(machineMap) < len(machinesList.Items) {
147+
other := VirtualObject(cluster.Namespace, "OtherGroup", "Other")
148+
tree.Add(workers, other)
149+
150+
for i := range machinesList.Items {
151+
m := &machinesList.Items[i]
152+
if _, ok := machineMap[m.Name]; ok {
153+
continue
154+
}
155+
addMachineFunc(other, m)
156+
}
157+
}
158+
}
159+
160+
if len(machinePoolList.Items) > 0 { // Add MachinePool objects
161+
tree.Add(cluster, workers)
162+
addMachinePoolsToObjectTree(ctx, c, cluster.Namespace, workers, machinePoolList, tree)
163+
}
164+
165+
return tree, nil
166+
}
117167

168+
func addClusterResourceSetsToObjectTree(ctx context.Context, c client.Client, cluster *clusterv1.Cluster, tree *ObjectTree) {
169+
if resourceSetBinding, err := getResourceSetBindingInCluster(ctx, c, cluster.Namespace, cluster.Name); err == nil {
170+
resourceSetGroup := VirtualObject(cluster.Namespace, "ClusterResourceSetGroup", "ClusterResourceSets")
171+
tree.Add(cluster, resourceSetGroup)
172+
173+
for _, binding := range resourceSetBinding.Spec.Bindings {
174+
resourceSetRefObject := ObjectReferenceObject(&corev1.ObjectReference{
175+
Kind: "ClusterResourceSet",
176+
Namespace: cluster.Namespace,
177+
Name: binding.ClusterResourceSetName,
178+
APIVersion: addonsv1.GroupVersion.String(),
179+
})
180+
tree.Add(resourceSetGroup, resourceSetRefObject)
181+
}
182+
}
183+
}
184+
185+
func addControlPlane(cluster *clusterv1.Cluster, controlPlane *unstructured.Unstructured, tree *ObjectTree, options DiscoverOptions) {
186+
tree.Add(cluster, controlPlane, ObjectMetaName("ControlPlane"), GroupingObject(true))
187+
188+
if options.ShowTemplates {
189+
// Add control plane infrastructure ref using spec fields guaranteed in contract
190+
infrastructureRef, found, err := unstructured.NestedMap(controlPlane.UnstructuredContent(), "spec", "machineTemplate", "infrastructureRef")
191+
if err == nil && found {
192+
infrastructureObjectRef := &corev1.ObjectReference{
193+
Kind: infrastructureRef["kind"].(string),
194+
Namespace: infrastructureRef["namespace"].(string),
195+
Name: infrastructureRef["name"].(string),
196+
APIVersion: infrastructureRef["apiVersion"].(string),
197+
}
198+
199+
machineTemplateRefObject := ObjectReferenceObject(infrastructureObjectRef)
200+
var templateParent client.Object
201+
if options.AddTemplateVirtualNode {
202+
templateParent = addTemplateVirtualNode(tree, controlPlane, cluster.Namespace)
203+
} else {
204+
templateParent = controlPlane
205+
}
206+
tree.Add(templateParent, machineTemplateRefObject, ObjectMetaName("MachineInfrastructureTemplate"))
207+
}
208+
}
209+
}
210+
211+
func addMachineDeploymentToObjectTree(ctx context.Context, c client.Client, cluster *clusterv1.Cluster, workers *unstructured.Unstructured, machinesList *clusterv1.MachineList, tree *ObjectTree, options DiscoverOptions, addMachineFunc func(parent client.Object, m *clusterv1.Machine)) error {
118212
// Adds worker machines.
119213
machinesDeploymentList, err := getMachineDeploymentsInCluster(ctx, c, cluster.Namespace, cluster.Name)
120214
if err != nil {
121-
return nil, err
215+
return err
122216
}
123217

124218
machineSetList, err := getMachineSetsInCluster(ctx, c, cluster.Namespace, cluster.Name)
125219
if err != nil {
126-
return nil, err
220+
return err
127221
}
128222

129223
for i := range machinesDeploymentList.Items {
@@ -134,6 +228,21 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
134228
}
135229
tree.Add(workers, md, addOpts...)
136230

231+
if options.ShowTemplates {
232+
var templateParent client.Object
233+
if options.AddTemplateVirtualNode {
234+
templateParent = addTemplateVirtualNode(tree, md, cluster.Namespace)
235+
} else {
236+
templateParent = md
237+
}
238+
239+
bootstrapTemplateRefObject := ObjectReferenceObject(md.Spec.Template.Spec.Bootstrap.ConfigRef)
240+
tree.Add(templateParent, bootstrapTemplateRefObject, ObjectMetaName("BootstrapConfigTemplate"))
241+
242+
machineTemplateRefObject := ObjectReferenceObject(&md.Spec.Template.Spec.InfrastructureRef)
243+
tree.Add(templateParent, machineTemplateRefObject, ObjectMetaName("MachineInfrastructureTemplate"))
244+
}
245+
137246
machineSets := selectMachinesSetsControlledBy(machineSetList, md)
138247
for i := range machineSets {
139248
ms := machineSets[i]
@@ -151,21 +260,42 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
151260
}
152261
}
153262

154-
// Handles orphan machines.
155-
if len(machineMap) < len(machinesList.Items) {
156-
other := VirtualObject(cluster.Namespace, "OtherGroup", "Other")
157-
tree.Add(workers, other)
263+
return nil
264+
}
265+
266+
func addMachinePoolsToObjectTree(ctx context.Context, c client.Client, namespace string, workers *unstructured.Unstructured, machinePoolList *expv1.MachinePoolList, tree *ObjectTree) {
267+
for i := range machinePoolList.Items {
268+
mp := &machinePoolList.Items[i]
269+
_, visible := tree.Add(workers, mp)
158270

159-
for i := range machinesList.Items {
160-
m := &machinesList.Items[i]
161-
if _, ok := machineMap[m.Name]; ok {
162-
continue
271+
if visible {
272+
if machinePoolBootstrap, err := external.Get(ctx, c, mp.Spec.Template.Spec.Bootstrap.ConfigRef, namespace); err == nil {
273+
tree.Add(mp, machinePoolBootstrap, ObjectMetaName("BootstrapConfig"), NoEcho(true))
274+
}
275+
276+
if machinePoolInfra, err := external.Get(ctx, c, &mp.Spec.Template.Spec.InfrastructureRef, namespace); err == nil {
277+
tree.Add(mp, machinePoolInfra, ObjectMetaName("MachineInfrastructure"), NoEcho(true))
163278
}
164-
addMachineFunc(other, m)
165279
}
166280
}
281+
}
167282

168-
return tree, nil
283+
func getResourceSetBindingInCluster(ctx context.Context, c client.Client, namespace string, name string) (*addonsv1.ClusterResourceSetBinding, error) {
284+
if name == "" {
285+
return nil, nil
286+
}
287+
288+
resourceSetBinding := &addonsv1.ClusterResourceSetBinding{}
289+
resourceSetBindingKey := client.ObjectKey{Namespace: namespace, Name: name}
290+
if err := c.Get(ctx, resourceSetBindingKey, resourceSetBinding); err != nil {
291+
return nil, err
292+
}
293+
resourceSetBinding.TypeMeta = metav1.TypeMeta{
294+
Kind: "ClusterResourceSetBinding",
295+
APIVersion: addonsv1.GroupVersion.String(),
296+
}
297+
298+
return resourceSetBinding, nil
169299
}
170300

171301
func getMachinesInCluster(ctx context.Context, c client.Client, namespace, name string) (*clusterv1.MachineList, error) {
@@ -213,6 +343,21 @@ func getMachineSetsInCluster(ctx context.Context, c client.Client, namespace, na
213343
return machineSetList, nil
214344
}
215345

346+
func getMachinePoolsInCluster(ctx context.Context, c client.Client, namespace, name string) (*expv1.MachinePoolList, error) {
347+
if name == "" {
348+
return nil, nil
349+
}
350+
351+
machinePoolList := &expv1.MachinePoolList{}
352+
labels := map[string]string{clusterv1.ClusterLabelName: name}
353+
354+
if err := c.List(ctx, machinePoolList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil {
355+
return nil, err
356+
}
357+
358+
return machinePoolList, nil
359+
}
360+
216361
func selectControlPlaneMachines(machineList *clusterv1.MachineList) []*clusterv1.Machine {
217362
machines := []*clusterv1.Machine{}
218363
for i := range machineList.Items {
@@ -245,3 +390,14 @@ func selectMachinesControlledBy(machineList *clusterv1.MachineList, controller c
245390
}
246391
return machines
247392
}
393+
394+
func addTemplateVirtualNode(tree *ObjectTree, parent client.Object, namespace string) client.Object {
395+
templateNode := VirtualObject(namespace, "TemplateGroup", parent.GetName())
396+
addOpts := []AddObjectOption{
397+
ZOrder(1),
398+
ObjectMetaName("Templates"),
399+
}
400+
tree.Add(parent, templateNode, addOpts...)
401+
402+
return templateNode
403+
}

0 commit comments

Comments
 (0)