@@ -31,6 +31,7 @@ import (
31
31
clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
32
32
controlplanev1 "sigs.k8s.io/cluster-api/controlplane/kubeadm/api/v1beta1"
33
33
"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal"
34
+ "sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/etcd"
34
35
"sigs.k8s.io/cluster-api/util/collections"
35
36
"sigs.k8s.io/cluster-api/util/conditions"
36
37
v1beta2conditions "sigs.k8s.io/cluster-api/util/conditions/v1beta2"
@@ -167,8 +168,8 @@ func (r *KubeadmControlPlaneReconciler) updateV1beta2Status(ctx context.Context,
167
168
setMachinesReadyCondition (ctx , controlPlane .KCP , controlPlane .Machines )
168
169
setMachinesUpToDateCondition (ctx , controlPlane .KCP , controlPlane .Machines )
169
170
setRemediatingCondition (ctx , controlPlane .KCP , controlPlane .MachinesToBeRemediatedByKCP (), controlPlane .UnhealthyMachines ())
170
-
171
- // TODO: Available, Deleting
171
+ // TODO: Deleting
172
+ setAvailableCondition ( ctx , controlPlane . KCP , controlPlane . IsEtcdManaged (), controlPlane . EtcdMembers , controlPlane . EtcdMembersAgreeOnMemberList , controlPlane . EtcdMembersAgreeOnClusterID , controlPlane . EtcdMembersAndMachinesAreMatching , controlPlane . Machines )
172
173
}
173
174
174
175
func setReplicas (_ context.Context , kcp * controlplanev1.KubeadmControlPlane , machines collections.Machines ) {
@@ -423,6 +424,139 @@ func setRemediatingCondition(ctx context.Context, kcp *controlplanev1.KubeadmCon
423
424
})
424
425
}
425
426
427
+ func setAvailableCondition (_ context.Context , kcp * controlplanev1.KubeadmControlPlane , etcdIsManaged bool , etcdMembers []* etcd.Member , etcdMembersAgreeOnMemberList bool , etcdMembersAgreeOnClusterID bool , etcdMembersAndMachinesAreMatching bool , machines collections.Machines ) {
428
+ if ! kcp .Status .Initialized {
429
+ v1beta2conditions .Set (kcp , metav1.Condition {
430
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
431
+ Status : metav1 .ConditionFalse ,
432
+ Reason : controlplanev1 .KubeadmControlPlaneNotAvailableV1Beta2Reason ,
433
+ Message : "Control plane not yet initialized" ,
434
+ })
435
+ return
436
+ }
437
+
438
+ if etcdIsManaged && etcdMembers == nil {
439
+ v1beta2conditions .Set (kcp , metav1.Condition {
440
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
441
+ Status : metav1 .ConditionUnknown ,
442
+ Reason : controlplanev1 .KubeadmControlPlaneAvailableInspectionFailedV1Beta2Reason ,
443
+ Message : "Failed to get etcd members" ,
444
+ })
445
+ return
446
+ }
447
+
448
+ if etcdIsManaged && ! etcdMembersAgreeOnMemberList {
449
+ v1beta2conditions .Set (kcp , metav1.Condition {
450
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
451
+ Status : metav1 .ConditionFalse ,
452
+ Reason : controlplanev1 .KubeadmControlPlaneNotAvailableV1Beta2Reason ,
453
+ Message : "At least one etcd member reports a list of etcd members different than the list reported by other members" ,
454
+ })
455
+ return
456
+ }
457
+
458
+ if etcdIsManaged && ! etcdMembersAgreeOnClusterID {
459
+ v1beta2conditions .Set (kcp , metav1.Condition {
460
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
461
+ Status : metav1 .ConditionFalse ,
462
+ Reason : controlplanev1 .KubeadmControlPlaneNotAvailableV1Beta2Reason ,
463
+ Message : "At least one etcd member reports a cluster ID different than the cluster ID reported by other members" ,
464
+ })
465
+ return
466
+ }
467
+
468
+ if etcdIsManaged && ! etcdMembersAndMachinesAreMatching {
469
+ v1beta2conditions .Set (kcp , metav1.Condition {
470
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
471
+ Status : metav1 .ConditionFalse ,
472
+ Reason : controlplanev1 .KubeadmControlPlaneNotAvailableV1Beta2Reason ,
473
+ Message : "The list of etcd members does not match the list of Machines and Nodes" ,
474
+ })
475
+ return
476
+ }
477
+
478
+ // Determine control plane availability looking at machines conditions, which at this stage are
479
+ // already surfacing status from etcd member and all control plane pods hosted on every machine.
480
+ // Note: we intentionally use the number of etcd members for determine the etcd quorum because
481
+ // etcd members could not match with machines, e.g. while provisioning a new machine.
482
+ etcdQuorum := (len (etcdMembers ) / 2.0 ) + 1
483
+ k8sControlPlaneHealthy := 0
484
+ etcdMembersHealthy := 0
485
+ for _ , machine := range machines {
486
+ // if external etcd, only look at the status of the K8s control plane components on this machine.
487
+ if ! etcdIsManaged {
488
+ if v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineAPIServerPodHealthyV1Beta2Condition ) &&
489
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineControllerManagerPodHealthyV1Beta2Condition ) &&
490
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineSchedulerPodHealthyV1Beta2Condition ) {
491
+ k8sControlPlaneHealthy ++
492
+ }
493
+ continue
494
+ }
495
+
496
+ // Otherwise, etcd is managed.
497
+ // In this case, when looking at the k8s control plane we should consider how kubeadm layouts control plane components,
498
+ // and more specifically:
499
+ // - API server on one machine only connect to the local etcd member
500
+ // - ControllerManager and scheduler on a machine connect to the local API server (not to the control plane endpoint)
501
+ // As a consequence, we consider the K8s control plane on this machine healthy only if everything is healthy.
502
+
503
+ if v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Condition ) {
504
+ etcdMembersHealthy ++
505
+ }
506
+
507
+ if v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineAPIServerPodHealthyV1Beta2Condition ) &&
508
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineControllerManagerPodHealthyV1Beta2Condition ) &&
509
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineSchedulerPodHealthyV1Beta2Condition ) &&
510
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Condition ) &&
511
+ v1beta2conditions .IsTrue (machine , controlplanev1 .KubeadmControlPlaneMachineEtcdPodHealthyV1Beta2Condition ) {
512
+ k8sControlPlaneHealthy ++
513
+ }
514
+ }
515
+
516
+ if kcp .DeletionTimestamp .IsZero () &&
517
+ (! etcdIsManaged || etcdMembersHealthy >= etcdQuorum ) &&
518
+ k8sControlPlaneHealthy >= 1 &&
519
+ v1beta2conditions .IsTrue (kcp , controlplanev1 .KubeadmControlPlaneCertificatesAvailableV1Beta2Condition ) {
520
+ v1beta2conditions .Set (kcp , metav1.Condition {
521
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
522
+ Status : metav1 .ConditionTrue ,
523
+ Reason : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Reason ,
524
+ })
525
+ return
526
+ }
527
+
528
+ messages := []string {}
529
+ if ! kcp .DeletionTimestamp .IsZero () {
530
+ messages = append (messages , "Control plane metadata.deletionTimestamp is set" )
531
+ }
532
+
533
+ if ! v1beta2conditions .IsTrue (kcp , controlplanev1 .KubeadmControlPlaneCertificatesAvailableV1Beta2Condition ) {
534
+ messages = append (messages , "Control plane certificates are not available" )
535
+ }
536
+
537
+ if etcdIsManaged && etcdMembersHealthy < etcdQuorum {
538
+ switch etcdMembersHealthy {
539
+ case 0 :
540
+ messages = append (messages , fmt .Sprintf ("There are no healthy etcd member, at least %d required" , etcdQuorum ))
541
+ case 1 :
542
+ messages = append (messages , fmt .Sprintf ("There is 1 healthy etcd member, at least %d required" , etcdQuorum ))
543
+ default :
544
+ messages = append (messages , fmt .Sprintf ("There are %d healthy etcd members, at least %d required" , etcdMembersHealthy , etcdQuorum ))
545
+ }
546
+ }
547
+
548
+ if k8sControlPlaneHealthy < 1 {
549
+ messages = append (messages , "There are no healthy control plane instances, at least 1 required" )
550
+ }
551
+
552
+ v1beta2conditions .Set (kcp , metav1.Condition {
553
+ Type : controlplanev1 .KubeadmControlPlaneAvailableV1Beta2Condition ,
554
+ Status : metav1 .ConditionFalse ,
555
+ Reason : controlplanev1 .KubeadmControlPlaneNotAvailableV1Beta2Reason ,
556
+ Message : strings .Join (messages , ";" ),
557
+ })
558
+ }
559
+
426
560
func aggregateStaleMachines (machines collections.Machines ) string {
427
561
if len (machines ) == 0 {
428
562
return ""
0 commit comments