sigs.k8s.io/cluster-api@v1.6.3/internal/controllers/topology/cluster/scope/state.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scope
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/pkg/errors"
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  
    29  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    30  	expv1 "sigs.k8s.io/cluster-api/exp/api/v1beta1"
    31  )
    32  
    33  // ClusterState holds all the objects representing the state of a managed Cluster topology.
    34  // NOTE: please note that we are going to deal with two different type state, the current state as read from the API server,
    35  // and the desired state resulting from processing the ClusterBlueprint.
    36  type ClusterState struct {
    37  	// Cluster holds the Cluster object.
    38  	Cluster *clusterv1.Cluster
    39  
    40  	// InfrastructureCluster holds the infrastructure cluster object referenced by the Cluster.
    41  	InfrastructureCluster *unstructured.Unstructured
    42  
    43  	// ControlPlane holds the controlplane object referenced by the Cluster.
    44  	ControlPlane *ControlPlaneState
    45  
    46  	// MachineDeployments holds the machine deployments in the Cluster.
    47  	MachineDeployments MachineDeploymentsStateMap
    48  
    49  	// MachinePools holds the MachinePools in the Cluster.
    50  	MachinePools MachinePoolsStateMap
    51  }
    52  
    53  // ControlPlaneState holds all the objects representing the state of a managed control plane.
    54  type ControlPlaneState struct {
    55  	// Object holds the ControlPlane object.
    56  	Object *unstructured.Unstructured
    57  
    58  	// InfrastructureMachineTemplate holds the infrastructure template referenced by the ControlPlane object.
    59  	InfrastructureMachineTemplate *unstructured.Unstructured
    60  
    61  	// MachineHealthCheckClass holds the MachineHealthCheck for this ControlPlane.
    62  	// +optional
    63  	MachineHealthCheck *clusterv1.MachineHealthCheck
    64  }
    65  
    66  // MachineDeploymentsStateMap holds a collection of MachineDeployment states.
    67  type MachineDeploymentsStateMap map[string]*MachineDeploymentState
    68  
    69  // Upgrading returns the list of the machine deployments
    70  // that are upgrading.
    71  func (mds MachineDeploymentsStateMap) Upgrading(ctx context.Context, c client.Client) ([]string, error) {
    72  	names := []string{}
    73  	for _, md := range mds {
    74  		upgrading, err := md.IsUpgrading(ctx, c)
    75  		if err != nil {
    76  			return nil, errors.Wrap(err, "failed to list upgrading MachineDeployments")
    77  		}
    78  		if upgrading {
    79  			names = append(names, md.Object.Name)
    80  		}
    81  	}
    82  	return names, nil
    83  }
    84  
    85  // MachineDeploymentState holds all the objects representing the state of a managed deployment.
    86  type MachineDeploymentState struct {
    87  	// Object holds the MachineDeployment object.
    88  	Object *clusterv1.MachineDeployment
    89  
    90  	// BootstrapTemplate holds the bootstrap template referenced by the MachineDeployment object.
    91  	BootstrapTemplate *unstructured.Unstructured
    92  
    93  	// InfrastructureMachineTemplate holds the infrastructure machine template referenced by the MachineDeployment object.
    94  	InfrastructureMachineTemplate *unstructured.Unstructured
    95  
    96  	// MachineHealthCheck holds a MachineHealthCheck linked to the MachineDeployment object.
    97  	// +optional
    98  	MachineHealthCheck *clusterv1.MachineHealthCheck
    99  }
   100  
   101  // IsUpgrading determines if the MachineDeployment is upgrading.
   102  // A machine deployment is considered upgrading if at least one of the Machines of this
   103  // MachineDeployment has a different version.
   104  func (md *MachineDeploymentState) IsUpgrading(ctx context.Context, c client.Client) (bool, error) {
   105  	// If the MachineDeployment has no version there is no definitive way to check if it is upgrading. Therefore, return false.
   106  	// Note: This case should not happen.
   107  	if md.Object.Spec.Template.Spec.Version == nil {
   108  		return false, nil
   109  	}
   110  	selectorMap, err := metav1.LabelSelectorAsMap(&md.Object.Spec.Selector)
   111  	if err != nil {
   112  		return false, errors.Wrapf(err, "failed to check if MachineDeployment %s is upgrading: failed to convert label selector to map", md.Object.Name)
   113  	}
   114  	machines := &clusterv1.MachineList{}
   115  	if err := c.List(ctx, machines, client.InNamespace(md.Object.Namespace), client.MatchingLabels(selectorMap)); err != nil {
   116  		return false, errors.Wrapf(err, "failed to check if MachineDeployment %s is upgrading: failed to list Machines", md.Object.Name)
   117  	}
   118  	mdVersion := *md.Object.Spec.Template.Spec.Version
   119  	// Check if the versions of the all the Machines match the MachineDeployment version.
   120  	for i := range machines.Items {
   121  		machine := machines.Items[i]
   122  		if machine.Spec.Version == nil {
   123  			return false, fmt.Errorf("failed to check if MachineDeployment %s is upgrading: Machine %s has no version", md.Object.Name, machine.Name)
   124  		}
   125  		if *machine.Spec.Version != mdVersion {
   126  			return true, nil
   127  		}
   128  	}
   129  	return false, nil
   130  }
   131  
   132  // MachinePoolsStateMap holds a collection of MachinePool states.
   133  type MachinePoolsStateMap map[string]*MachinePoolState
   134  
   135  // Upgrading returns the list of the machine pools
   136  // that are upgrading.
   137  func (mps MachinePoolsStateMap) Upgrading(ctx context.Context, c client.Client) ([]string, error) {
   138  	names := []string{}
   139  	for _, mp := range mps {
   140  		upgrading, err := mp.IsUpgrading(ctx, c)
   141  		if err != nil {
   142  			return nil, errors.Wrap(err, "failed to list upgrading MachinePools")
   143  		}
   144  		if upgrading {
   145  			names = append(names, mp.Object.Name)
   146  		}
   147  	}
   148  	return names, nil
   149  }
   150  
   151  // MachinePoolState holds all the objects representing the state of a managed pool.
   152  type MachinePoolState struct {
   153  	// Object holds the MachinePool object.
   154  	Object *expv1.MachinePool
   155  
   156  	// BootstrapObject holds the MachinePool bootstrap object.
   157  	BootstrapObject *unstructured.Unstructured
   158  
   159  	// InfrastructureMachinePoolObject holds the infrastructure machine template referenced by the MachinePool object.
   160  	InfrastructureMachinePoolObject *unstructured.Unstructured
   161  }
   162  
   163  // IsUpgrading determines if the MachinePool is upgrading.
   164  // A machine pool is considered upgrading if at least one of the Machines of this
   165  // MachinePool has a different version.
   166  func (mp *MachinePoolState) IsUpgrading(ctx context.Context, c client.Client) (bool, error) {
   167  	// If the MachinePool has no version there is no definitive way to check if it is upgrading. Therefore, return false.
   168  	// Note: This case should not happen.
   169  	if mp.Object.Spec.Template.Spec.Version == nil {
   170  		return false, nil
   171  	}
   172  	mpVersion := *mp.Object.Spec.Template.Spec.Version
   173  	// Check if the kubelet versions of the MachinePool noderefs match the MachinePool version.
   174  	for _, nodeRef := range mp.Object.Status.NodeRefs {
   175  		node := &corev1.Node{}
   176  		if err := c.Get(ctx, client.ObjectKey{Name: nodeRef.Name}, node); err != nil {
   177  			return false, fmt.Errorf("failed to check if MachinePool %s is upgrading: failed to get Node %s", mp.Object.Name, nodeRef.Name)
   178  		}
   179  		if mpVersion != node.Status.NodeInfo.KubeletVersion {
   180  			return true, nil
   181  		}
   182  	}
   183  	return false, nil
   184  }