Skip to content

Commit 99892c6

Browse files
Add conditions to the KubeadmControlPlane object
1 parent b9f2389 commit 99892c6

10 files changed

+179
-4
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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 v1alpha3
18+
19+
import clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
20+
21+
// Conditions and condition Reasons for the KubeadmControlPlane object
22+
23+
// kubeadmControlPlaneConditionsCount defines the total number of conditions on the KubeadmControlPlane object.
24+
const KubeadmControlPlaneConditionsCount = 2
25+
26+
const (
27+
// MachinesReady reports an aggregate of current status of the machines controlled by the KubeadmControlPlane.
28+
MachinesReadyCondition clusterv1.ConditionType = "MachinesReady"
29+
)
30+
31+
const (
32+
// CertificatesGeneratedCondition documents that cluster certificates were generated as part of the
33+
// processing of a a KubeadmControlPlane object.
34+
CertificatesGeneratedCondition clusterv1.ConditionType = "CertificatesGenerated"
35+
)
36+
37+
const (
38+
// InitializedCondition documents that the control plane was initialized and the first control plane instance
39+
// has completed the kubeadm init operation, and as a consequence, there is an API server instance ready for
40+
// processing requests.
41+
InitializedCondition clusterv1.ConditionType = "Initialized"
42+
)
43+
44+
const (
45+
// ControlPlaneUpgradingCondition signals that the KubeadmControlPlane successfully completed the
46+
// upgrade of the controlled machines.
47+
ControlPlaneUpgradingCondition clusterv1.ConditionType = "UpgradeSucceeded"
48+
49+
// UpgradingReason documents a KubeadmControlPlane executing an upgrade.
50+
UpgradingReason = "Upgrading"
51+
)
52+
53+
const (
54+
// ResizeSucceededCondition signals that the KubeadmControlPlane is currently resizing the controlled machines.
55+
ResizeSucceededCondition clusterv1.ConditionType = "ResizeSucceeded"
56+
57+
// ScalingUpReason documents a KubeadmControlPlane scaling up the controlled machines.
58+
ScalingUpReason = "ScalingUp"
59+
60+
// ScalingDownReason documents a KubeadmControlPlane scaling down the controlled machines.
61+
ScalingDownReason = "ScalingDown"
62+
)

controlplane/kubeadm/api/v1alpha3/kubeadm_control_plane_types.go

+13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package v1alpha3
1919
import (
2020
corev1 "k8s.io/api/core/v1"
2121
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
22+
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
2223

2324
cabpkv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1alpha3"
2425
"sigs.k8s.io/cluster-api/errors"
@@ -116,6 +117,10 @@ type KubeadmControlPlaneStatus struct {
116117
// ObservedGeneration is the latest generation observed by the controller.
117118
// +optional
118119
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
120+
121+
// Conditions defines current service state of the KubeadmControlPlane.
122+
// +optional
123+
Conditions clusterv1.Conditions `json:"conditions,omitempty"`
119124
}
120125

121126
// +kubebuilder:object:root=true
@@ -139,6 +144,14 @@ type KubeadmControlPlane struct {
139144
Status KubeadmControlPlaneStatus `json:"status,omitempty"`
140145
}
141146

147+
func (c *KubeadmControlPlane) GetConditions() clusterv1.Conditions {
148+
return c.Status.Conditions
149+
}
150+
151+
func (c *KubeadmControlPlane) SetConditions(conditions clusterv1.Conditions) {
152+
c.Status.Conditions = conditions
153+
}
154+
142155
// +kubebuilder:object:root=true
143156

144157
// KubeadmControlPlaneList contains a list of KubeadmControlPlane.

controlplane/kubeadm/api/v1alpha3/zz_generated.deepcopy.go

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

controlplane/kubeadm/config/crd/bases/controlplane.cluster.x-k8s.io_kubeadmcontrolplanes.yaml

+44
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,50 @@ spec:
10471047
status:
10481048
description: KubeadmControlPlaneStatus defines the observed state of KubeadmControlPlane.
10491049
properties:
1050+
conditions:
1051+
description: Conditions defines current service state of the KubeadmControlPlane.
1052+
items:
1053+
description: Condition defines an observation of a Cluster API resource
1054+
operational state.
1055+
properties:
1056+
lastTransitionTime:
1057+
description: Last time the condition transitioned from one status
1058+
to another. This should be when the underlying condition changed.
1059+
If that is not known, then using the time when the API field
1060+
changed is acceptable.
1061+
format: date-time
1062+
type: string
1063+
message:
1064+
description: A human readable message indicating details about
1065+
the transition. This field may be empty.
1066+
type: string
1067+
reason:
1068+
description: The reason for the condition's last transition
1069+
in CamelCase. The specific API may choose whether or not this
1070+
field is considered a guaranteed API. This field may not be
1071+
empty.
1072+
type: string
1073+
severity:
1074+
description: Severity provides an explicit classification of
1075+
Reason code, so the users or machines can immediately understand
1076+
the current situation and act accordingly. The Severity field
1077+
MUST be set only when Status=False.
1078+
type: string
1079+
status:
1080+
description: Status of the condition, one of True, False, Unknown.
1081+
type: string
1082+
type:
1083+
description: Type of condition in CamelCase or in foo.example.com/CamelCase.
1084+
Many .condition.type values are consistent across resources
1085+
like Available, but because arbitrary conditions can be useful
1086+
(see .node.status.conditions), the ability to deconflict is
1087+
important.
1088+
type: string
1089+
required:
1090+
- status
1091+
- type
1092+
type: object
1093+
type: array
10501094
failureMessage:
10511095
description: ErrorMessage indicates that there is a terminal problem
10521096
reconciling the state, and will be set to a descriptive error message.

controlplane/kubeadm/controllers/controller.go

+36-2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ import (
4949
capierrors "sigs.k8s.io/cluster-api/errors"
5050
"sigs.k8s.io/cluster-api/util"
5151
"sigs.k8s.io/cluster-api/util/annotations"
52+
"sigs.k8s.io/cluster-api/util/conditions"
5253
"sigs.k8s.io/cluster-api/util/patch"
5354
"sigs.k8s.io/cluster-api/util/predicates"
5455
"sigs.k8s.io/cluster-api/util/secret"
@@ -161,6 +162,13 @@ func (r *KubeadmControlPlaneReconciler) Reconcile(req ctrl.Request) (res ctrl.Re
161162
}
162163
}
163164

165+
// Always update the readyCondition; the summary is represented using the "1 of x completed" notation.
166+
conditions.SetSummary(kcp, conditions.WithConditionOrder(
167+
controlplanev1.MachinesReadyCondition,
168+
controlplanev1.ControlPlaneUpgradingCondition,
169+
controlplanev1.ResizeSucceededCondition,
170+
))
171+
164172
// Always attempt to update status.
165173
if err := r.updateStatus(ctx, kcp, cluster); err != nil {
166174
var connFailure *internal.RemoteClusterConnectionError
@@ -222,6 +230,9 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster *
222230
logger.Error(err, "unable to lookup or create cluster certificates")
223231
return ctrl.Result{}, err
224232
}
233+
if certificates.HasGenerated() {
234+
conditions.MarkTrue(kcp, controlplanev1.CertificatesGeneratedCondition)
235+
}
225236

226237
// If ControlPlaneEndpoint is not set, return early
227238
if cluster.Spec.ControlPlaneEndpoint.IsZero() {
@@ -255,11 +266,23 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster *
255266
}
256267

257268
controlPlane := internal.NewControlPlane(cluster, kcp, ownedMachines)
258-
requireUpgrade := controlPlane.MachinesNeedingUpgrade()
269+
270+
// Aggregate the operational state of all the machines; while aggregating we are adding the
271+
// source ref to the aggregate reason (reason@machine/name) so the problem can be easily tracked down to its source.
272+
conditions.SetAggregateCondition(controlPlane.KCP, controlplanev1.MachinesReadyCondition, ownedMachines.ConditionGetters(), conditions.AddSourceRef())
273+
259274
// Upgrade takes precedence over other operations
260-
if len(requireUpgrade) > 0 {
275+
requireUpgrade := controlPlane.MachinesNeedingUpgrade()
276+
switch {
277+
case len(requireUpgrade) > 0:
261278
logger.Info("Upgrading Control Plane")
279+
conditions.MarkFalse(controlPlane.KCP, controlplanev1.ControlPlaneUpgradingCondition, controlplanev1.UpgradingReason, clusterv1.ConditionSeverityWarning, "Upgrading to %s version (%d new version, %d old)", kcp.Spec.Version, kcp.Status.UpdatedReplicas, len(requireUpgrade))
262280
return r.upgradeControlPlane(ctx, cluster, kcp, controlPlane)
281+
default:
282+
// make sure last upgrade operation is marked as completed (if any)
283+
if conditions.Has(controlPlane.KCP, controlplanev1.ControlPlaneUpgradingCondition) {
284+
conditions.MarkTrue(controlPlane.KCP, controlplanev1.ControlPlaneUpgradingCondition)
285+
}
263286
}
264287

265288
// If we've made it this far, we can assume that all ownedMachines are up to date
@@ -271,16 +294,24 @@ func (r *KubeadmControlPlaneReconciler) reconcile(ctx context.Context, cluster *
271294
case numMachines < desiredReplicas && numMachines == 0:
272295
// Create new Machine w/ init
273296
logger.Info("Initializing control plane", "Desired", desiredReplicas, "Existing", numMachines)
297+
conditions.MarkFalse(controlPlane.KCP, controlplanev1.ResizeSucceededCondition, controlplanev1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up to %d replicas (actual %d)", desiredReplicas, numMachines)
274298
return r.initializeControlPlane(ctx, cluster, kcp, controlPlane)
275299
// We are scaling up
276300
case numMachines < desiredReplicas && numMachines > 0:
277301
// Create a new Machine w/ join
278302
logger.Info("Scaling up control plane", "Desired", desiredReplicas, "Existing", numMachines)
303+
conditions.MarkFalse(controlPlane.KCP, controlplanev1.ResizeSucceededCondition, controlplanev1.ScalingUpReason, clusterv1.ConditionSeverityWarning, "Scaling up to %d replicas (actual %d)", desiredReplicas, numMachines)
279304
return r.scaleUpControlPlane(ctx, cluster, kcp, controlPlane)
280305
// We are scaling down
281306
case numMachines > desiredReplicas:
282307
logger.Info("Scaling down control plane", "Desired", desiredReplicas, "Existing", numMachines)
308+
conditions.MarkFalse(controlPlane.KCP, controlplanev1.ResizeSucceededCondition, controlplanev1.ScalingDownReason, clusterv1.ConditionSeverityWarning, "Scaling down to %d replicas (actual %d)", desiredReplicas, numMachines)
283309
return r.scaleDownControlPlane(ctx, cluster, kcp, controlPlane)
310+
default:
311+
// make sure last resize operation is marked as completed (if any)
312+
if conditions.Has(controlPlane.KCP, controlplanev1.ResizeSucceededCondition) {
313+
conditions.MarkTrue(controlPlane.KCP, controlplanev1.ResizeSucceededCondition)
314+
}
284315
}
285316

286317
// Get the workload cluster client.
@@ -328,6 +359,9 @@ func (r *KubeadmControlPlaneReconciler) reconcileDelete(ctx context.Context, clu
328359
return ctrl.Result{}, nil
329360
}
330361

362+
// TODO: consider if this makes sense, given that it seems going down from 3 to 0 immediately
363+
conditions.MarkFalse(kcp, "Ready", "Deleting", clusterv1.ConditionSeverityInfo, "Deleting all replicas (actual %d)", len(ownedMachines))
364+
331365
// Verify that only control plane machines remain
332366
if len(allMachines) != len(ownedMachines) {
333367
logger.V(2).Info("Waiting for worker nodes to be deleted first")

controlplane/kubeadm/controllers/scale_test.go

-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import (
2323
"time"
2424

2525
. "github.com/onsi/gomega"
26-
2726
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2827
"k8s.io/apimachinery/pkg/runtime"
2928
"k8s.io/client-go/kubernetes/scheme"

controlplane/kubeadm/controllers/status.go

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/hash"
2626
"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/machinefilters"
2727
"sigs.k8s.io/cluster-api/util"
28+
"sigs.k8s.io/cluster-api/util/conditions"
2829
)
2930

3031
// updateStatus is called after every reconcilitation loop in a defer statement to always make sure we have the
@@ -69,6 +70,7 @@ func (r *KubeadmControlPlaneReconciler) updateStatus(ctx context.Context, kcp *c
6970
// This only gets initialized once and does not change if the kubeadm config map goes away.
7071
if status.HasKubeadmConfig {
7172
kcp.Status.Initialized = true
73+
conditions.MarkTrue(kcp, controlplanev1.InitializedCondition)
7274
}
7375

7476
if kcp.Status.ReadyReplicas > 0 {

controlplane/kubeadm/controllers/status_test.go

+2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"testing"
2323

2424
. "github.com/onsi/gomega"
25+
"sigs.k8s.io/cluster-api/util/conditions"
2526

2627
corev1 "k8s.io/api/core/v1"
2728
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -197,6 +198,7 @@ func TestKubeadmControlPlaneReconciler_updateStatusAllMachinesReady(t *testing.T
197198
g.Expect(kcp.Status.FailureMessage).To(BeNil())
198199
g.Expect(kcp.Status.FailureReason).To(BeEquivalentTo(""))
199200
g.Expect(kcp.Status.Initialized).To(BeTrue())
201+
g.Expect(conditions.Has(kcp, controlplanev1.InitializedCondition)).To(BeTrue())
200202
g.Expect(kcp.Status.Ready).To(BeTrue())
201203
}
202204

controlplane/kubeadm/internal/etcd/util/set.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ func (s UInt64Set) IsSuperset(s2 UInt64Set) bool {
148148
return true
149149
}
150150

151-
// Equal returns true if and only if s1 is equal (as a set) to s2.
151+
// Equals returns true if and only if s1 is equal (as a set) to s2.
152152
// Two sets are equal if their membership is identical.
153153
// (In practice, this means same elements, order doesn't matter)
154154
func (s UInt64Set) Equal(s2 UInt64Set) bool {

controlplane/kubeadm/internal/machine_collection.go

+11
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
3434
"sigs.k8s.io/cluster-api/controlplane/kubeadm/internal/machinefilters"
3535
"sigs.k8s.io/cluster-api/util"
36+
"sigs.k8s.io/cluster-api/util/conditions"
3637
)
3738

3839
// FilterableMachineCollection is a set of Machines
@@ -134,3 +135,13 @@ func (s FilterableMachineCollection) DeepCopy() FilterableMachineCollection {
134135
}
135136
return result
136137
}
138+
139+
// ConditionGetters returns the slice with machines converted into conditions.Getter.
140+
func (s FilterableMachineCollection) ConditionGetters() []conditions.Getter {
141+
res := make([]conditions.Getter, 0, len(s))
142+
for _, v := range s {
143+
value := *v
144+
res = append(res, &value)
145+
}
146+
return res
147+
}

0 commit comments

Comments
 (0)