Skip to content

Commit f035f87

Browse files
committed
Implement grace period for KCP remote conditions
Signed-off-by: Stefan Büringer [email protected]
1 parent 7957510 commit f035f87

File tree

16 files changed

+881
-230
lines changed

16 files changed

+881
-230
lines changed

api/v1beta1/machine_types.go

+5-9
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,11 @@ const (
206206
// during the deletion workflow, or by a users.
207207
MachineNodeDeletedV1Beta2Reason = ObjectDeletedV1Beta2Reason
208208

209-
// MachineNodeRemoteConnectionFailedV1Beta2Reason surfaces that the remote connection failed.
210-
// If the remote connection probe failed for longer than remote conditions grace period,
211-
// this reason is used when setting NodeHealthy and NodeReady conditions to `Unknown`.
212-
MachineNodeRemoteConnectionFailedV1Beta2Reason = RemoteConnectionFailedV1Beta2Reason
213-
214-
// MachineNodeRemoteConnectionDownV1Beta2Reason surfaces that the remote connection is down.
215-
// This is used when setting NodeHealthy and NodeReady conditions to `Unknown`
216-
// when the connection is down and they haven't been set yet.
217-
MachineNodeRemoteConnectionDownV1Beta2Reason = RemoteConnectionDownV1Beta2Reason
209+
// MachineNodeInspectionFailedV1Beta2Reason documents a failure when inspecting the status of a Node.
210+
MachineNodeInspectionFailedV1Beta2Reason = InspectionFailedV1Beta2Reason
211+
212+
// MachineNodeConnectionDownV1Beta2Reason surfaces that the connection to the workload cluster is down.
213+
MachineNodeConnectionDownV1Beta2Reason = ConnectionDownV1Beta2Reason
218214
)
219215

220216
// Machine's HealthCheckSucceeded condition and corresponding reasons that will be used in v1Beta2 API version.

api/v1beta1/v1beta2_condition_consts.go

+4-11
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ const (
106106
// set to false and with the OwnerRemediated condition set to false by the MachineHealthCheck controller.
107107
RemediatingV1Beta2Reason = "Remediating"
108108

109-
// NotRemediatingV1Beta2Reason surfaces when an object does not own any machines marked as not healthy
110-
// by the MachineHealthCheck controller.
109+
// NotRemediatingV1Beta2Reason surfaces when an object does not own any machines with HealthCheckSucceeded
110+
// set to false and with the OwnerRemediated condition set to false by the MachineHealthCheck controller.
111111
NotRemediatingV1Beta2Reason = "NotRemediating"
112112

113113
// NoReplicasV1Beta2Reason surfaces when an object that manage replicas does not have any.
@@ -142,15 +142,8 @@ const (
142142
// PausedV1Beta2Reason surfaces when an object is paused.
143143
PausedV1Beta2Reason = "Paused"
144144

145-
// RemoteConnectionFailedV1Beta2Reason surfaces that the remote connection failed.
146-
// This is typically used when setting remote conditions (e.g. `NodeHealthy`) to `Unknown`
147-
// after the remote connection probe didn't succeed for remote conditions grace period.
148-
RemoteConnectionFailedV1Beta2Reason = "RemoteConnectionFailed"
149-
150-
// RemoteConnectionDownV1Beta2Reason surfaces that the remote connection is down.
151-
// This is typically used when setting remote conditions (e.g. `NodeHealthy`) to `Unknown`
152-
// when the connection is down and they haven't been set yet.
153-
RemoteConnectionDownV1Beta2Reason = "RemoteConnectionDown"
145+
// ConnectionDownV1Beta2Reason surfaces that the connection to the workload cluster is down.
146+
ConnectionDownV1Beta2Reason = "ConnectionDown"
154147

155148
// DeletionTimestampNotSetV1Beta2Reason surfaces when an object is not deleting because the
156149
// DeletionTimestamp is not set.

controlplane/kubeadm/api/v1beta1/v1beta2_condition_consts.go

+16-4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ const (
5252
// etcd cluster hosted on KubeadmControlPlane controlled machines.
5353
KubeadmControlPlaneEtcdClusterInspectionFailedV1Beta2Reason = clusterv1.InspectionFailedV1Beta2Reason
5454

55+
// KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason surfaces that the connection to the workload
56+
// cluster is down.
57+
KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason = clusterv1.ConnectionDownV1Beta2Reason
58+
5559
// KubeadmControlPlaneEtcdClusterHealthyV1Beta2Reason surfaces when the etcd cluster hosted on KubeadmControlPlane
5660
// machines is healthy.
5761
KubeadmControlPlaneEtcdClusterHealthyV1Beta2Reason = "Healthy"
@@ -77,6 +81,10 @@ const (
7781
// control plane components hosted on KubeadmControlPlane controlled machines.
7882
KubeadmControlPlaneControlPlaneComponentsInspectionFailedV1Beta2Reason = clusterv1.InspectionFailedV1Beta2Reason
7983

84+
// KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason surfaces that the connection to the workload
85+
// cluster is down.
86+
KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason = clusterv1.ConnectionDownV1Beta2Reason
87+
8088
// KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Reason surfaces when the Kubernetes control plane components
8189
// hosted on KubeadmControlPlane machines are healthy.
8290
KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Reason = "Healthy"
@@ -233,13 +241,13 @@ const (
233241
// pod hosted on a KubeadmControlPlane controlled machine.
234242
KubeadmControlPlaneMachinePodInspectionFailedV1Beta2Reason = clusterv1.InspectionFailedV1Beta2Reason
235243

244+
// KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason surfaces that the connection to the workload
245+
// cluster is down.
246+
KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason = clusterv1.ConnectionDownV1Beta2Reason
247+
236248
// KubeadmControlPlaneMachinePodDeletingV1Beta2Reason surfaces when the machine hosting control plane components
237249
// is being deleted.
238250
KubeadmControlPlaneMachinePodDeletingV1Beta2Reason = "Deleting"
239-
240-
// KubeadmControlPlaneMachinePodInternalErrorV1Beta2Reason surfaces unexpected failures when reading pod hosted
241-
// on a KubeadmControlPlane controlled machine.
242-
KubeadmControlPlaneMachinePodInternalErrorV1Beta2Reason = clusterv1.InternalErrorV1Beta2Reason
243251
)
244252

245253
// EtcdMemberHealthy condition and corresponding reasons that will be used for KubeadmControlPlane controlled machines in v1Beta2 API version.
@@ -257,6 +265,10 @@ const (
257265
// etcd member hosted on a KubeadmControlPlane controlled machine.
258266
KubeadmControlPlaneMachineEtcdMemberInspectionFailedV1Beta2Reason = clusterv1.InspectionFailedV1Beta2Reason
259267

268+
// KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason surfaces that the connection to the workload
269+
// cluster is down.
270+
KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason = clusterv1.ConnectionDownV1Beta2Reason
271+
260272
// KubeadmControlPlaneMachineEtcdMemberDeletingV1Beta2Reason surfaces when the machine hosting an etcd member
261273
// is being deleted.
262274
KubeadmControlPlaneMachineEtcdMemberDeletingV1Beta2Reason = "Deleting"

controlplane/kubeadm/controllers/alias.go

+3
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ type KubeadmControlPlaneReconciler struct {
4040
// WatchFilterValue is the label value used to filter events prior to reconciliation.
4141
WatchFilterValue string
4242

43+
RemoteConditionsGracePeriod time.Duration
44+
4345
// Deprecated: DeprecatedInfraMachineNaming. Name the InfraStructureMachines after the InfraMachineTemplate.
4446
DeprecatedInfraMachineNaming bool
4547
}
@@ -53,6 +55,7 @@ func (r *KubeadmControlPlaneReconciler) SetupWithManager(ctx context.Context, mg
5355
EtcdDialTimeout: r.EtcdDialTimeout,
5456
EtcdCallTimeout: r.EtcdCallTimeout,
5557
WatchFilterValue: r.WatchFilterValue,
58+
RemoteConditionsGracePeriod: r.RemoteConditionsGracePeriod,
5659
DeprecatedInfraMachineNaming: r.DeprecatedInfraMachineNaming,
5760
}).SetupWithManager(ctx, mgr, options)
5861
}

controlplane/kubeadm/internal/controllers/controller.go

+146-11
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ type KubeadmControlPlaneReconciler struct {
8686
// WatchFilterValue is the label value used to filter events prior to reconciliation.
8787
WatchFilterValue string
8888

89+
RemoteConditionsGracePeriod time.Duration
90+
8991
// Deprecated: DeprecatedInfraMachineNaming. Name the InfraStructureMachines after the InfraMachineTemplate.
9092
DeprecatedInfraMachineNaming bool
9193

@@ -95,6 +97,14 @@ type KubeadmControlPlaneReconciler struct {
9597
}
9698

9799
func (r *KubeadmControlPlaneReconciler) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
100+
if r.Client == nil || r.SecretCachingClient == nil || r.ClusterCache == nil ||
101+
r.EtcdDialTimeout == time.Duration(0) || r.EtcdCallTimeout == time.Duration(0) ||
102+
r.RemoteConditionsGracePeriod < 2*time.Minute {
103+
return errors.New("Client, SecretCachingClient and ClusterCache must not be nil and " +
104+
"EtcdDialTimeout and EtcdCallTimeout must not be 0 and " +
105+
"RemoteConditionsGracePeriod must not be < 2m")
106+
}
107+
98108
predicateLog := ctrl.LoggerFrom(ctx).WithValues("controller", "kubeadmcontrolplane")
99109
c, err := ctrl.NewControllerManagedBy(mgr).
100110
For(&controlplanev1.KubeadmControlPlane{}).
@@ -111,7 +121,8 @@ func (r *KubeadmControlPlaneReconciler) SetupWithManager(ctx context.Context, mg
111121
),
112122
),
113123
).
114-
WatchesRawSource(r.ClusterCache.GetClusterSource("kubeadmcontrolplane", r.ClusterToKubeadmControlPlane)).
124+
WatchesRawSource(r.ClusterCache.GetClusterSource("kubeadmcontrolplane", r.ClusterToKubeadmControlPlane,
125+
clustercache.WatchForProbeFailure(r.RemoteConditionsGracePeriod))).
115126
Build(r)
116127
if err != nil {
117128
return errors.Wrap(err, "failed setting up with a controller manager")
@@ -802,45 +813,169 @@ func (r *KubeadmControlPlaneReconciler) syncMachines(ctx context.Context, contro
802813

803814
// reconcileControlPlaneConditions is responsible of reconciling conditions reporting the status of static pods and
804815
// the status of the etcd cluster.
805-
func (r *KubeadmControlPlaneReconciler) reconcileControlPlaneConditions(ctx context.Context, controlPlane *internal.ControlPlane) error {
816+
func (r *KubeadmControlPlaneReconciler) reconcileControlPlaneConditions(ctx context.Context, controlPlane *internal.ControlPlane) (reterr error) {
806817
// If the cluster is not yet initialized, there is no way to connect to the workload cluster and fetch information
807818
// for updating conditions. Return early.
808-
if !controlPlane.KCP.Status.Initialized {
819+
// We additionally check for the Available condition. The Available condition is set at the same time
820+
// as .status.initialized and is never changed to false again. Below we'll need the transition time of the
821+
// Available condition to check if the remote conditions grace period is already reached.
822+
// Note: The Machine controller uses the ControlPlaneInitialized condition on the Cluster instead for
823+
// the same check. We don't use the ControlPlaneInitialized condition from the Cluster here because KCP
824+
// Reconcile does (currently) not get triggered from condition changes to the Cluster object.
825+
controlPlaneInitialized := conditions.Get(controlPlane.KCP, controlplanev1.AvailableCondition)
826+
if !controlPlane.KCP.Status.Initialized ||
827+
controlPlaneInitialized == nil || controlPlaneInitialized.Status != corev1.ConditionTrue {
809828
v1beta2conditions.Set(controlPlane.KCP, metav1.Condition{
810829
Type: controlplanev1.KubeadmControlPlaneEtcdClusterHealthyV1Beta2Condition,
811830
Status: metav1.ConditionUnknown,
812831
Reason: controlplanev1.KubeadmControlPlaneEtcdClusterInspectionFailedV1Beta2Reason,
813-
Message: "Waiting for remote connection",
832+
Message: "Waiting for Cluster control plane to be initialized",
814833
})
815834

816835
v1beta2conditions.Set(controlPlane.KCP, metav1.Condition{
817836
Type: controlplanev1.KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Condition,
818837
Status: metav1.ConditionUnknown,
819838
Reason: controlplanev1.KubeadmControlPlaneControlPlaneComponentsInspectionFailedV1Beta2Reason,
820-
Message: "Waiting for remote connection",
839+
Message: "Waiting for Cluster control plane to be initialized",
821840
})
822841

823842
return nil
824843
}
825844

845+
defer func() {
846+
// Patch machines with the updated conditions.
847+
reterr = kerrors.NewAggregate([]error{reterr, controlPlane.PatchMachines(ctx)})
848+
}()
849+
850+
// Remote conditions grace period is counted from the later of last probe success and control plane initialized.
851+
lastProbeSuccessTime := r.ClusterCache.GetLastProbeSuccessTimestamp(ctx, client.ObjectKeyFromObject(controlPlane.Cluster))
852+
if time.Since(maxTime(lastProbeSuccessTime, controlPlaneInitialized.LastTransitionTime.Time)) > r.RemoteConditionsGracePeriod {
853+
// Overwrite conditions to ConnectionDown.
854+
setConditionsToUnknown(setConditionsToUnknownInput{
855+
ControlPlane: controlPlane,
856+
Overwrite: true,
857+
EtcdClusterHealthyReason: controlplanev1.KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason,
858+
ControlPlaneComponentsHealthyReason: controlplanev1.KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason,
859+
StaticPodReason: controlplanev1.KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason,
860+
EtcdMemberHealthyReason: controlplanev1.KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason,
861+
Message: lastProbeSuccessMessage(lastProbeSuccessTime),
862+
})
863+
return errors.Errorf("connection to the workload cluster is down")
864+
}
865+
826866
workloadCluster, err := controlPlane.GetWorkloadCluster(ctx)
827867
if err != nil {
828-
return errors.Wrap(err, "cannot get remote client to workload cluster")
868+
if errors.Is(err, clustercache.ErrClusterNotConnected) {
869+
// If conditions are not set, set them to ConnectionDown.
870+
setConditionsToUnknown(setConditionsToUnknownInput{
871+
ControlPlane: controlPlane,
872+
Overwrite: false, // Don't overwrite.
873+
EtcdClusterHealthyReason: controlplanev1.KubeadmControlPlaneEtcdClusterConnectionDownV1Beta2Reason,
874+
ControlPlaneComponentsHealthyReason: controlplanev1.KubeadmControlPlaneControlPlaneComponentsConnectionDownV1Beta2Reason,
875+
StaticPodReason: controlplanev1.KubeadmControlPlaneMachinePodConnectionDownV1Beta2Reason,
876+
EtcdMemberHealthyReason: controlplanev1.KubeadmControlPlaneMachineEtcdMemberConnectionDownV1Beta2Reason,
877+
Message: lastProbeSuccessMessage(lastProbeSuccessTime),
878+
})
879+
return errors.Wrap(err, "cannot get client for the workload cluster")
880+
}
881+
882+
// Overwrite conditions to InspectionFailed.
883+
setConditionsToUnknown(setConditionsToUnknownInput{
884+
ControlPlane: controlPlane,
885+
Overwrite: true,
886+
EtcdClusterHealthyReason: controlplanev1.KubeadmControlPlaneEtcdClusterInspectionFailedV1Beta2Reason,
887+
ControlPlaneComponentsHealthyReason: controlplanev1.KubeadmControlPlaneControlPlaneComponentsInspectionFailedV1Beta2Reason,
888+
StaticPodReason: controlplanev1.KubeadmControlPlaneMachinePodInspectionFailedV1Beta2Reason,
889+
EtcdMemberHealthyReason: controlplanev1.KubeadmControlPlaneMachineEtcdMemberInspectionFailedV1Beta2Reason,
890+
Message: "Please check controller logs for errors",
891+
})
892+
return errors.Wrap(err, "cannot get client for the workload cluster")
829893
}
830894

831895
// Update conditions status
832896
workloadCluster.UpdateStaticPodConditions(ctx, controlPlane)
833897
workloadCluster.UpdateEtcdConditions(ctx, controlPlane)
834898

835-
// Patch machines with the updated conditions.
836-
if err := controlPlane.PatchMachines(ctx); err != nil {
837-
return err
838-
}
839-
840899
// KCP will be patched at the end of Reconcile to reflect updated conditions, so we can return now.
841900
return nil
842901
}
843902

903+
type setConditionsToUnknownInput struct {
904+
ControlPlane *internal.ControlPlane
905+
Overwrite bool
906+
EtcdClusterHealthyReason string
907+
ControlPlaneComponentsHealthyReason string
908+
StaticPodReason string
909+
EtcdMemberHealthyReason string
910+
Message string
911+
}
912+
913+
func setConditionsToUnknown(input setConditionsToUnknownInput) {
914+
etcdClusterHealthySet := v1beta2conditions.Has(input.ControlPlane.KCP, controlplanev1.KubeadmControlPlaneEtcdClusterHealthyV1Beta2Condition)
915+
controlPlaneComponentsHealthySet := v1beta2conditions.Has(input.ControlPlane.KCP, controlplanev1.KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Condition)
916+
917+
if input.Overwrite || !etcdClusterHealthySet {
918+
v1beta2conditions.Set(input.ControlPlane.KCP, metav1.Condition{
919+
Type: controlplanev1.KubeadmControlPlaneEtcdClusterHealthyV1Beta2Condition,
920+
Status: metav1.ConditionUnknown,
921+
Reason: input.EtcdClusterHealthyReason,
922+
Message: input.Message,
923+
})
924+
for _, machine := range input.ControlPlane.Machines {
925+
if input.ControlPlane.IsEtcdManaged() {
926+
v1beta2conditions.Set(machine, metav1.Condition{
927+
Type: controlplanev1.KubeadmControlPlaneMachineEtcdMemberHealthyV1Beta2Condition,
928+
Status: metav1.ConditionUnknown,
929+
Reason: input.EtcdMemberHealthyReason,
930+
Message: input.Message,
931+
})
932+
}
933+
}
934+
}
935+
936+
if input.Overwrite || !controlPlaneComponentsHealthySet {
937+
v1beta2conditions.Set(input.ControlPlane.KCP, metav1.Condition{
938+
Type: controlplanev1.KubeadmControlPlaneControlPlaneComponentsHealthyV1Beta2Condition,
939+
Status: metav1.ConditionUnknown,
940+
Reason: input.ControlPlaneComponentsHealthyReason,
941+
Message: input.Message,
942+
})
943+
944+
allMachinePodV1beta2Conditions := []string{
945+
controlplanev1.KubeadmControlPlaneMachineAPIServerPodHealthyV1Beta2Condition,
946+
controlplanev1.KubeadmControlPlaneMachineControllerManagerPodHealthyV1Beta2Condition,
947+
controlplanev1.KubeadmControlPlaneMachineSchedulerPodHealthyV1Beta2Condition,
948+
}
949+
if input.ControlPlane.IsEtcdManaged() {
950+
allMachinePodV1beta2Conditions = append(allMachinePodV1beta2Conditions, controlplanev1.KubeadmControlPlaneMachineEtcdPodHealthyV1Beta2Condition)
951+
}
952+
for _, machine := range input.ControlPlane.Machines {
953+
for _, condition := range allMachinePodV1beta2Conditions {
954+
v1beta2conditions.Set(machine, metav1.Condition{
955+
Type: condition,
956+
Status: metav1.ConditionUnknown,
957+
Reason: input.StaticPodReason,
958+
Message: input.Message,
959+
})
960+
}
961+
}
962+
}
963+
}
964+
965+
func lastProbeSuccessMessage(lastProbeSuccessTime time.Time) string {
966+
if lastProbeSuccessTime.IsZero() {
967+
return ""
968+
}
969+
return fmt.Sprintf("Last successful probe at %s", lastProbeSuccessTime.Format(time.RFC3339))
970+
}
971+
972+
func maxTime(t1, t2 time.Time) time.Time {
973+
if t1.After(t2) {
974+
return t1
975+
}
976+
return t2
977+
}
978+
844979
// reconcileEtcdMembers ensures the number of etcd members is in sync with the number of machines/nodes.
845980
// This is usually required after a machine deletion.
846981
//

0 commit comments

Comments
 (0)