Skip to content

Commit 5e3fd7f

Browse files
Stop using deprecated counters in controllers
1 parent 59af9c5 commit 5e3fd7f

32 files changed

+282
-530
lines changed

exp/runtime/hooks/api/v1alpha1/topologymutation_variable_types.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ type ControlPlaneBuiltins struct {
125125

126126
// replicas is the value of the replicas field of the ControlPlane object.
127127
// +optional
128-
Replicas *int64 `json:"replicas,omitempty"`
128+
Replicas *int32 `json:"replicas,omitempty"`
129129

130130
// machineTemplate is the value of the .spec.machineTemplate field of the ControlPlane object.
131131
// +optional

exp/runtime/hooks/api/v1alpha1/zz_generated.deepcopy.go

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

exp/topology/desiredstate/desired_state.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ func (g *generator) computeControlPlane(ctx context.Context, s *scope.Scope, inf
354354
// NOTE: If the Topology.ControlPlane.replicas value is nil, it is assumed that the control plane controller
355355
// does not implement support for this field and the ControlPlane object is generated without the number of Replicas.
356356
if s.Blueprint.Topology.ControlPlane.Replicas != nil {
357-
if err := contract.ControlPlane().Replicas().Set(controlPlane, int64(*s.Blueprint.Topology.ControlPlane.Replicas)); err != nil {
357+
if err := contract.ControlPlane().Replicas().Set(controlPlane, *s.Blueprint.Topology.ControlPlane.Replicas); err != nil {
358358
return nil, errors.Wrapf(err, "failed to set %s in the ControlPlane object", contract.ControlPlane().Replicas().Path())
359359
}
360360
}

exp/topology/desiredstate/desired_state_test.go

+3-7
Original file line numberDiff line numberDiff line change
@@ -1859,13 +1859,9 @@ func TestComputeMachineDeployment(t *testing.T) {
18591859
WithStatus(clusterv1.MachineDeploymentStatus{
18601860
ObservedGeneration: 2,
18611861
Replicas: 2,
1862-
Deprecated: &clusterv1.MachineDeploymentDeprecatedStatus{
1863-
V1Beta1: &clusterv1.MachineDeploymentV1Beta1DeprecatedStatus{
1864-
ReadyReplicas: 2,
1865-
UpdatedReplicas: 2,
1866-
AvailableReplicas: 2,
1867-
},
1868-
},
1862+
ReadyReplicas: ptr.To[int32](2),
1863+
UpToDateReplicas: ptr.To[int32](2),
1864+
AvailableReplicas: ptr.To[int32](2),
18691865
}).
18701866
Build()
18711867
mdsState = duplicateMachineDeploymentsState(mdsState)

internal/contract/controlplane.go

+58-94
Original file line numberDiff line numberDiff line change
@@ -93,95 +93,49 @@ func (c *ControlPlaneContract) ControlPlaneEndpoint() *ControlPlaneEndpoint {
9393
// NOTE: When working with unstructured there is no way to understand if the ControlPlane provider
9494
// do support a field in the type definition from the fact that a field is not set in a given instance.
9595
// This is why in we are deriving if replicas is required from the ClusterClass in the topology reconciler code.
96-
func (c *ControlPlaneContract) Replicas() *Int64 {
97-
return &Int64{
96+
func (c *ControlPlaneContract) Replicas() *Int32 {
97+
return &Int32{
9898
path: []string{"spec", "replicas"},
9999
}
100100
}
101101

102102
// StatusReplicas provide access to the status.replicas field in a ControlPlane object, if any. Applies to implementations using replicas.
103-
func (c *ControlPlaneContract) StatusReplicas() *Int64 {
104-
return &Int64{
103+
func (c *ControlPlaneContract) StatusReplicas() *Int32 {
104+
return &Int32{
105105
path: []string{"status", "replicas"},
106106
}
107107
}
108108

109-
// UpdatedReplicas provide access to the status.updatedReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
110-
// TODO (v1beta2): Rename to V1Beta1DeprecatedUpdatedReplicas and make sure we are only using this method for compatibility with old contracts.
111-
func (c *ControlPlaneContract) UpdatedReplicas(contractVersion string) *Int64 {
112-
if contractVersion == "v1beta1" {
113-
return &Int64{
114-
path: []string{"status", "updatedReplicas"},
115-
}
116-
}
117-
118-
return &Int64{
119-
path: []string{"status", "deprecated", "v1beta1", "updatedReplicas"},
120-
}
121-
}
122-
123109
// ReadyReplicas provide access to the status.readyReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
124-
// TODO (v1beta2): Rename to V1Beta1DeprecatedReadyReplicas and make sure we are only using this method for compatibility with old contracts.
125-
func (c *ControlPlaneContract) ReadyReplicas(contractVersion string) *Int64 {
126-
if contractVersion == "v1beta1" {
127-
return &Int64{
128-
path: []string{"status", "readyReplicas"},
129-
}
130-
}
131-
132-
return &Int64{
133-
path: []string{"status", "deprecated", "v1beta1", "readyReplicas"},
134-
}
135-
}
136-
137-
// UnavailableReplicas provide access to the status.unavailableReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
138-
// TODO (v1beta2): Rename to V1Beta1DeprecatedUnavailableReplicas and make sure we are only using this method for compatibility with old contracts.
139-
func (c *ControlPlaneContract) UnavailableReplicas(contractVersion string) *Int64 {
140-
if contractVersion == "v1beta1" {
141-
return &Int64{
142-
path: []string{"status", "unavailableReplicas"},
143-
}
144-
}
145-
146-
return &Int64{
147-
path: []string{"status", "deprecated", "v1beta1", "unavailableReplicas"},
148-
}
149-
}
150-
151-
// V1Beta2ReadyReplicas provide access to the status.readyReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
152-
// TODO (v1beta2): Drop V1Beta2 prefix..
153-
func (c *ControlPlaneContract) V1Beta2ReadyReplicas(contractVersion string) *Int32 {
154-
if contractVersion == "v1beta1" {
155-
return &Int32{
156-
path: []string{"status", "v1beta2", "readyReplicas"},
157-
}
158-
}
159-
110+
// NOTE: readyReplicas changed semantic in v1beta2 contract.
111+
func (c *ControlPlaneContract) ReadyReplicas() *Int32 {
160112
return &Int32{
161113
path: []string{"status", "readyReplicas"},
162114
}
163115
}
164116

165-
// V1Beta2AvailableReplicas provide access to the status.availableReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
166-
// TODO (v1beta2): Drop V1Beta2 prefix.x.
167-
func (c *ControlPlaneContract) V1Beta2AvailableReplicas(contractVersion string) *Int32 {
168-
if contractVersion == "v1beta1" {
169-
return &Int32{
170-
path: []string{"status", "v1beta2", "availableReplicas"},
171-
}
172-
}
173-
117+
// AvailableReplicas provide access to the status.availableReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
118+
// NOTE: availableReplicas was introduced by the v1beta2 contract; use unavailableReplicas for the v1beta1 contract.
119+
func (c *ControlPlaneContract) AvailableReplicas() *Int32 {
174120
return &Int32{
175121
path: []string{"status", "availableReplicas"},
176122
}
177123
}
178124

179-
// V1Beta2UpToDateReplicas provide access to the status.upToDateReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
180-
// TODO (v1beta2): Drop V1Beta2 prefix.ix.
181-
func (c *ControlPlaneContract) V1Beta2UpToDateReplicas(contractVersion string) *Int32 {
125+
// V1Beta1UnavailableReplicas provide access to the status.unavailableReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
126+
// NOTE: use availableReplicas when working with the v1beta2 contract.
127+
func (c *ControlPlaneContract) V1Beta1UnavailableReplicas() *Int64 {
128+
return &Int64{
129+
path: []string{"status", "unavailableReplicas"},
130+
}
131+
}
132+
133+
// UpToDateReplicas provide access to the status.upToDateReplicas field in a ControlPlane object, if any. Applies to implementations using replicas.
134+
// NOTE: upToDateReplicas was introduced by the v1beta2 contract; code will fall back to updatedReplicas for the v1beta1 contract.
135+
func (c *ControlPlaneContract) UpToDateReplicas(contractVersion string) *Int32 {
182136
if contractVersion == "v1beta1" {
183137
return &Int32{
184-
path: []string{"status", "v1beta2", "upToDateReplicas"},
138+
path: []string{"status", "updatedReplicas"},
185139
}
186140
}
187141

@@ -291,13 +245,9 @@ func (c *ControlPlaneContract) IsUpgrading(obj *unstructured.Unstructured) (bool
291245
func (c *ControlPlaneContract) IsScaling(obj *unstructured.Unstructured, contractVersion string) (bool, error) {
292246
desiredReplicas, err := c.Replicas().Get(obj)
293247
if err != nil {
294-
return false, errors.Wrap(err, "failed to get control plane spec replicas")
248+
return false, errors.Wrapf(err, "failed to get control plane %s", c.Replicas().Path().String())
295249
}
296250

297-
// TODO (v1beta2): Add a new code path using v1beta2 replica counters
298-
// note: currently we are still always using v1beta1 counters no matter if they are moved under deprecated
299-
// but we should stop doing this ASAP
300-
301251
statusReplicas, err := c.StatusReplicas().Get(obj)
302252
if err != nil {
303253
if errors.Is(err, ErrFieldNotFound) {
@@ -306,54 +256,68 @@ func (c *ControlPlaneContract) IsScaling(obj *unstructured.Unstructured, contrac
306256
// so that we can block any operations that expect control plane to be stable.
307257
return true, nil
308258
}
309-
return false, errors.Wrap(err, "failed to get control plane status replicas")
259+
return false, errors.Wrapf(err, "failed to get control plane %s", c.StatusReplicas().Path().String())
310260
}
311261

312-
updatedReplicas, err := c.UpdatedReplicas(contractVersion).Get(obj)
262+
upToDatedReplicas, err := c.UpToDateReplicas(contractVersion).Get(obj)
313263
if err != nil {
314264
if errors.Is(err, ErrFieldNotFound) {
315265
// If updatedReplicas is not set on the control plane
316266
// we should consider the control plane to be scaling so that
317267
// we block any operation that expect the control plane to be stable.
318268
return true, nil
319269
}
320-
return false, errors.Wrap(err, "failed to get control plane status updatedReplicas")
270+
return false, errors.Wrapf(err, "failed to get control plane %s", c.UpToDateReplicas(contractVersion).Path().String())
321271
}
322272

323-
readyReplicas, err := c.ReadyReplicas(contractVersion).Get(obj)
273+
readyReplicas, err := c.ReadyReplicas().Get(obj)
324274
if err != nil {
325275
if errors.Is(err, ErrFieldNotFound) {
326276
// If readyReplicas is not set on the control plane
327277
// we should consider the control plane to be scaling so that
328278
// we block any operation that expect the control plane to be stable.
329279
return true, nil
330280
}
331-
return false, errors.Wrap(err, "failed to get control plane status readyReplicas")
281+
return false, errors.Wrapf(err, "failed to get control plane %s", c.ReadyReplicas().Path().String())
332282
}
333283

334-
unavailableReplicas, err := c.UnavailableReplicas(contractVersion).Get(obj)
335-
if err != nil {
336-
if !errors.Is(err, ErrFieldNotFound) {
337-
return false, errors.Wrap(err, "failed to get control plane status unavailableReplicas")
284+
var availableReplicas *int32
285+
if contractVersion == "v1beta1" {
286+
unavailableReplicas, err := c.V1Beta1UnavailableReplicas().Get(obj)
287+
if err != nil {
288+
if !errors.Is(err, ErrFieldNotFound) {
289+
return false, errors.Wrapf(err, "failed to get control plane %s", c.V1Beta1UnavailableReplicas().Path().String())
290+
}
291+
// If unavailableReplicas is not set on the control plane we assume it is 0.
292+
// We have to do this as the following happens after clusterctl move with KCP:
293+
// * clusterctl move creates the KCP object without status
294+
// * the KCP controller won't patch the field to 0 if it doesn't exist
295+
// * This is because the patchHelper marshals before/after object to JSON to calculate a diff
296+
// and as the unavailableReplicas field is not a pointer, not set and 0 are both rendered as 0.
297+
// If before/after of the field is the same (i.e. 0), there is no diff and thus also no patch to set it to 0.
298+
unavailableReplicas = ptr.To[int64](0)
299+
}
300+
availableReplicas = ptr.To(*desiredReplicas - int32(*unavailableReplicas))
301+
} else {
302+
availableReplicas, err = c.AvailableReplicas().Get(obj)
303+
if err != nil {
304+
if errors.Is(err, ErrFieldNotFound) {
305+
// If readyReplicas is not set on the control plane
306+
// we should consider the control plane to be scaling so that
307+
// we block any operation that expect the control plane to be stable.
308+
return true, nil
309+
}
310+
return false, errors.Wrapf(err, "failed to get control plane %s", c.AvailableReplicas().Path().String())
338311
}
339-
// If unavailableReplicas is not set on the control plane we assume it is 0.
340-
// We have to do this as the following happens after clusterctl move with KCP:
341-
// * clusterctl move creates the KCP object without status
342-
// * the KCP controller won't patch the field to 0 if it doesn't exist
343-
// * This is because the patchHelper marshals before/after object to JSON to calculate a diff
344-
// and as the unavailableReplicas field is not a pointer, not set and 0 are both rendered as 0.
345-
// If before/after of the field is the same (i.e. 0), there is no diff and thus also no patch to set it to 0.
346-
unavailableReplicas = ptr.To[int64](0)
347312
}
348313

349314
// Control plane is still scaling if:
350-
// * .spec.replicas, .status.replicas, .status.updatedReplicas,
351-
// .status.readyReplicas are not equal and
352-
// * unavailableReplicas > 0
315+
// * .spec.replicas, .status.replicas, .status.upToDateReplicas,
316+
// .status.readyReplicas, .status.availableReplicas are not equal.
353317
if *statusReplicas != *desiredReplicas ||
354-
*updatedReplicas != *desiredReplicas ||
318+
*upToDatedReplicas != *desiredReplicas ||
355319
*readyReplicas != *desiredReplicas ||
356-
*unavailableReplicas > 0 {
320+
*availableReplicas != *desiredReplicas {
357321
return true, nil
358322
}
359323
return false, nil

0 commit comments

Comments
 (0)