sigs.k8s.io/cluster-api@v1.7.1/controlplane/kubeadm/internal/controllers/status.go (about) 1 /* 2 Copyright 2020 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 controllers 18 19 import ( 20 "context" 21 22 "github.com/pkg/errors" 23 24 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1" 25 controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1" 26 "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal" 27 "sigs.k8s.io/cluster-api/util/collections" 28 "sigs.k8s.io/cluster-api/util/conditions" 29 ) 30 31 // updateStatus is called after every reconcilitation loop in a defer statement to always make sure we have the 32 // resource status subresourcs up-to-date. 33 func (r *KubeadmControlPlaneReconciler) updateStatus(ctx context.Context, controlPlane *internal.ControlPlane) error { 34 selector := collections.ControlPlaneSelectorForCluster(controlPlane.Cluster.Name) 35 // Copy label selector to its status counterpart in string format. 36 // This is necessary for CRDs including scale subresources. 37 controlPlane.KCP.Status.Selector = selector.String() 38 39 controlPlane.KCP.Status.UpdatedReplicas = int32(len(controlPlane.UpToDateMachines())) 40 41 replicas := int32(len(controlPlane.Machines)) 42 desiredReplicas := *controlPlane.KCP.Spec.Replicas 43 44 // set basic data that does not require interacting with the workload cluster 45 controlPlane.KCP.Status.Replicas = replicas 46 controlPlane.KCP.Status.ReadyReplicas = 0 47 controlPlane.KCP.Status.UnavailableReplicas = replicas 48 49 // Return early if the deletion timestamp is set, because we don't want to try to connect to the workload cluster 50 // and we don't want to report resize condition (because it is set to deleting into reconcile delete). 51 if !controlPlane.KCP.DeletionTimestamp.IsZero() { 52 return nil 53 } 54 55 machinesWithHealthyAPIServer := controlPlane.Machines.Filter(collections.HealthyAPIServer()) 56 lowestVersion := machinesWithHealthyAPIServer.LowestVersion() 57 if lowestVersion != nil { 58 controlPlane.KCP.Status.Version = lowestVersion 59 } 60 61 switch { 62 // We are scaling up 63 case replicas < desiredReplicas: 64 conditions.MarkFalse(controlPlane.KCP, controlplanev1.ResizedCondition, controlplanev1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up control plane to %d replicas (actual %d)", desiredReplicas, replicas) 65 // We are scaling down 66 case replicas > desiredReplicas: 67 conditions.MarkFalse(controlPlane.KCP, controlplanev1.ResizedCondition, controlplanev1.ScalingDownReason, clusterv1.ConditionSeverityWarning, "Scaling down control plane to %d replicas (actual %d)", desiredReplicas, replicas) 68 69 // This means that there was no error in generating the desired number of machine objects 70 conditions.MarkTrue(controlPlane.KCP, controlplanev1.MachinesCreatedCondition) 71 default: 72 // make sure last resize operation is marked as completed. 73 // NOTE: we are checking the number of machines ready so we report resize completed only when the machines 74 // are actually provisioned (vs reporting completed immediately after the last machine object is created). 75 readyMachines := controlPlane.Machines.Filter(collections.IsReady()) 76 if int32(len(readyMachines)) == replicas { 77 conditions.MarkTrue(controlPlane.KCP, controlplanev1.ResizedCondition) 78 } 79 80 // This means that there was no error in generating the desired number of machine objects 81 conditions.MarkTrue(controlPlane.KCP, controlplanev1.MachinesCreatedCondition) 82 } 83 84 workloadCluster, err := controlPlane.GetWorkloadCluster(ctx) 85 if err != nil { 86 return errors.Wrap(err, "failed to create remote cluster client") 87 } 88 status, err := workloadCluster.ClusterStatus(ctx) 89 if err != nil { 90 return err 91 } 92 controlPlane.KCP.Status.ReadyReplicas = status.ReadyNodes 93 controlPlane.KCP.Status.UnavailableReplicas = replicas - status.ReadyNodes 94 95 // This only gets initialized once and does not change if the kubeadm config map goes away. 96 if status.HasKubeadmConfig { 97 controlPlane.KCP.Status.Initialized = true 98 conditions.MarkTrue(controlPlane.KCP, controlplanev1.AvailableCondition) 99 } 100 101 if controlPlane.KCP.Status.ReadyReplicas > 0 { 102 controlPlane.KCP.Status.Ready = true 103 } 104 105 // Surface lastRemediation data in status. 106 // LastRemediation is the remediation currently in progress, in any, or the 107 // most recent of the remediation we are keeping track on machines. 108 var lastRemediation *RemediationData 109 110 if v, ok := controlPlane.KCP.Annotations[controlplanev1.RemediationInProgressAnnotation]; ok { 111 remediationData, err := RemediationDataFromAnnotation(v) 112 if err != nil { 113 return err 114 } 115 lastRemediation = remediationData 116 } else { 117 for _, m := range controlPlane.Machines.UnsortedList() { 118 if v, ok := m.Annotations[controlplanev1.RemediationForAnnotation]; ok { 119 remediationData, err := RemediationDataFromAnnotation(v) 120 if err != nil { 121 return err 122 } 123 if lastRemediation == nil || lastRemediation.Timestamp.Time.Before(remediationData.Timestamp.Time) { 124 lastRemediation = remediationData 125 } 126 } 127 } 128 } 129 130 if lastRemediation != nil { 131 controlPlane.KCP.Status.LastRemediation = lastRemediation.ToStatus() 132 } 133 return nil 134 }