Skip to content

Commit

Permalink
Add support for v1beta2 conditions to clusterctl describe
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziopandini committed Nov 22, 2024
1 parent 6865cec commit 19f02b6
Show file tree
Hide file tree
Showing 8 changed files with 1,714 additions and 150 deletions.
4 changes: 4 additions & 0 deletions cmd/clusterctl/client/describe.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type DescribeClusterOptions struct {
// Grouping groups machines objects in case the ready conditions
// have the same Status, Severity and Reason.
Grouping bool

// V1Beta2 instructs tree to use V1Beta2 conditions.
V1Beta2 bool
}

// DescribeCluster returns the object tree representing the status of a Cluster API cluster.
Expand Down Expand Up @@ -96,5 +99,6 @@ func (c *clusterctlClient) DescribeCluster(ctx context.Context, options Describe
AddTemplateVirtualNode: options.AddTemplateVirtualNode,
Echo: options.Echo,
Grouping: options.Grouping,
V1Beta2: options.V1Beta2,
})
}
58 changes: 58 additions & 0 deletions cmd/clusterctl/client/tree/annotations.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@ const (
// GroupItemsAnnotation contains the list of names for the objects included in a group object.
GroupItemsAnnotation = "tree.cluster.x-k8s.io.io/group-items"

// GroupItemsAvailableCounter contains the number of available objects in the group, e.g. available Machines.
GroupItemsAvailableCounter = "tree.cluster.x-k8s.io.io/group-items-available-count"

// GroupItemsReadyCounter contains the number of ready objects in the group, e.g. ready Machines.
GroupItemsReadyCounter = "tree.cluster.x-k8s.io.io/group-items-ready-count"

// GroupItemsUpToDateCounter contains the number of up-to-date objects in the group, e.g. up-to-date Machines.
GroupItemsUpToDateCounter = "tree.cluster.x-k8s.io.io/group-items-up-to-date-count"

// ObjectContractAnnotation is added to unstructured objects to track which Cluster API contract those objects abide to.
// Note: Currently this annotation is applied only to control plane objects.
ObjectContractAnnotation = "tree.cluster.x-k8s.io.io/object-contract"

// GroupItemsSeparator is the separator used in the GroupItemsAnnotation.
GroupItemsSeparator = ", "

Expand Down Expand Up @@ -91,6 +104,51 @@ func GetGroupItems(obj client.Object) string {
return ""
}

// GetGroupItemsAvailableCounter returns the number of available objects in the group, e.g. available Machines.
func GetGroupItemsAvailableCounter(obj client.Object) int {
val, ok := getAnnotation(obj, GroupItemsAvailableCounter)
if !ok {
return 0
}
if v, err := strconv.Atoi(val); err == nil {
return v
}
return 0
}

// GetGroupItemsReadyCounter returns the number of ready objects in the group, e.g. ready Machines.
func GetGroupItemsReadyCounter(obj client.Object) int {
val, ok := getAnnotation(obj, GroupItemsReadyCounter)
if !ok {
return 0
}
if v, err := strconv.Atoi(val); err == nil {
return v
}
return 0
}

// GetGroupItemsUpToDateCounter returns the number of up-to-date objects in the group, e.g. up-to-date Machines.
func GetGroupItemsUpToDateCounter(obj client.Object) int {
val, ok := getAnnotation(obj, GroupItemsUpToDateCounter)
if !ok {
return 0
}
if v, err := strconv.Atoi(val); err == nil {
return v
}
return 0
}

// GetObjectContract returns which Cluster API contract an unstructured object abides to.
// Note: Currently this annotation is applied only to control plane objects.
func GetObjectContract(obj client.Object) string {
if val, ok := getAnnotation(obj, ObjectContractAnnotation); ok {
return val
}
return ""
}

// GetZOrder return the zOrder of the object. Objects with no zOrder have a default zOrder of 0.
func GetZOrder(obj client.Object) int {
if val, ok := getAnnotation(obj, ObjectZOrderAnnotation); ok {
Expand Down
13 changes: 10 additions & 3 deletions cmd/clusterctl/client/tree/discovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import (
"github.com/pkg/errors"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"sigs.k8s.io/controller-runtime/pkg/client"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
Expand Down Expand Up @@ -56,6 +56,9 @@ type DiscoverOptions struct {
// Grouping groups machine objects in case the ready conditions
// have the same Status, Severity and Reason.
Grouping bool

// V1Beta2 instructs tree to use V1Beta2 conditions.
V1Beta2 bool
}

func (d DiscoverOptions) toObjectTreeOptions() ObjectTreeOptions {
Expand Down Expand Up @@ -97,6 +100,10 @@ func Discovery(ctx context.Context, c client.Client, namespace, name string, opt
// Adds control plane
controlPlane, err := external.Get(ctx, c, cluster.Spec.ControlPlaneRef)
if err == nil {
// Keep track that this objects abides to the Cluster API control plane contract,
// so the consumers of the ObjectTree will know which info are available on this unstructured object
// and how to extract them.
addAnnotation(controlPlane, ObjectContractAnnotation, "ControlPlane")
addControlPlane(cluster, controlPlane, tree, options)
}

Expand Down Expand Up @@ -217,7 +224,7 @@ func addControlPlane(cluster *clusterv1.Cluster, controlPlane *unstructured.Unst
}
}

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 {
func addMachineDeploymentToObjectTree(ctx context.Context, c client.Client, cluster *clusterv1.Cluster, workers *NodeObject, machinesList *clusterv1.MachineList, tree *ObjectTree, options DiscoverOptions, addMachineFunc func(parent client.Object, m *clusterv1.Machine)) error {
// Adds worker machines.
machinesDeploymentList, err := getMachineDeploymentsInCluster(ctx, c, cluster.Namespace, cluster.Name)
if err != nil {
Expand Down Expand Up @@ -275,7 +282,7 @@ func addMachineDeploymentToObjectTree(ctx context.Context, c client.Client, clus
return nil
}

func addMachinePoolsToObjectTree(ctx context.Context, c client.Client, workers *unstructured.Unstructured, machinePoolList *expv1.MachinePoolList, machinesList *clusterv1.MachineList, tree *ObjectTree, addMachineFunc func(parent client.Object, m *clusterv1.Machine)) {
func addMachinePoolsToObjectTree(ctx context.Context, c client.Client, workers *NodeObject, machinePoolList *expv1.MachinePoolList, machinesList *clusterv1.MachineList, tree *ObjectTree, addMachineFunc func(parent client.Object, m *clusterv1.Machine)) {
for i := range machinePoolList.Items {
mp := &machinePoolList.Items[i]
_, visible := tree.Add(workers, mp, GroupingObject(true))
Expand Down
127 changes: 127 additions & 0 deletions cmd/clusterctl/client/tree/node_object.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
Copyright 2024 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package tree

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"

clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
)

// NodeObject represent a node in the tree which doesn't correspond to Cluster, MachineDeployment, Machine etc.
// An example of NodeObject are GroupNodes, which are used e.g. to represent a set of Machines.
// Note: NodeObject implements condition getter and setter interfaces as well as the minimal set of methods
// usually existing on Kubernetes objects.
type NodeObject struct {
metav1.TypeMeta
metav1.ObjectMeta
Status NodeStatus
}

// NodeStatus is the status of a node object.
type NodeStatus struct {
Conditions clusterv1.Conditions
V1Beta2 *NodeObjectV1Beta2Status
}

// NodeObjectV1Beta2Status is the v1Beta2 status of a node object.
type NodeObjectV1Beta2Status struct {
Conditions []metav1.Condition
}

// GetConditions returns the set of conditions for this object.
func (o *NodeObject) GetConditions() clusterv1.Conditions {
return o.Status.Conditions
}

// SetConditions sets the conditions on this object.
func (o *NodeObject) SetConditions(conditions clusterv1.Conditions) {
o.Status.Conditions = conditions
}

// GetV1Beta2Conditions returns the set of conditions for this object.
func (o *NodeObject) GetV1Beta2Conditions() []metav1.Condition {
if o.Status.V1Beta2 == nil {
return nil
}
return o.Status.V1Beta2.Conditions
}

// SetV1Beta2Conditions sets conditions for an API object.
func (o *NodeObject) SetV1Beta2Conditions(conditions []metav1.Condition) {
if o.Status.V1Beta2 == nil && conditions != nil {
o.Status.V1Beta2 = &NodeObjectV1Beta2Status{}
}
o.Status.V1Beta2.Conditions = conditions
}

// GetUID returns object's UID.
func (o *NodeObject) GetUID() types.UID {
return o.UID
}

// SetUID sets object's UID.
func (o *NodeObject) SetUID(uid types.UID) {
o.UID = uid
}

// GetCreationTimestamp returns object's CreationTimestamp.
func (o *NodeObject) GetCreationTimestamp() metav1.Time {
return o.CreationTimestamp
}

// SetCreationTimestamp sets object's CreationTimestamp.
func (o *NodeObject) SetCreationTimestamp(timestamp metav1.Time) {
o.CreationTimestamp = timestamp
}

// GetDeletionTimestamp returns object's DeletionTimestamp.
func (o *NodeObject) GetDeletionTimestamp() *metav1.Time {
return o.DeletionTimestamp
}

// SetDeletionTimestamp sets object's DeletionTimestamp.
func (o *NodeObject) SetDeletionTimestamp(timestamp *metav1.Time) {
o.DeletionTimestamp = timestamp
}

// GetOwnerReferences returns object's OwnerReferences.
func (o *NodeObject) GetOwnerReferences() []metav1.OwnerReference {
return o.OwnerReferences
}

// SetOwnerReferences sets object's OwnerReferences.
func (o *NodeObject) SetOwnerReferences(references []metav1.OwnerReference) {
o.OwnerReferences = references
}

// GetManagedFields returns object's ManagedFields.
func (o *NodeObject) GetManagedFields() []metav1.ManagedFieldsEntry {
return o.ManagedFields
}

// SetManagedFields sets object's ManagedFields.
func (o *NodeObject) SetManagedFields(managedFields []metav1.ManagedFieldsEntry) {
o.ManagedFields = managedFields
}

// DeepCopyObject returns a deep copy of the object.
func (o *NodeObject) DeepCopyObject() runtime.Object {
panic("implement me")
}
Loading

0 comments on commit 19f02b6

Please sign in to comment.