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 }