@@ -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
@@ -285,19 +239,16 @@ func (c *ControlPlaneContract) IsUpgrading(obj *unstructured.Unstructured) (bool
285239// A control plane is considered scaling if:
286240// - status.replicas is not yet set.
287241// - spec.replicas != status.replicas.
288- // - spec.replicas != status.updatedReplicas .
242+ // - spec.replicas != status.upToDateReplicas .
289243// - spec.replicas != status.readyReplicas.
290- // - status.unavailableReplicas > 0.
244+ // - spec.replicas != status.availableReplicas.
245+ // NOTE: this function is used only in E2E tests.
291246func (c * ControlPlaneContract ) IsScaling (obj * unstructured.Unstructured , contractVersion string ) (bool , error ) {
292247 desiredReplicas , err := c .Replicas ().Get (obj )
293248 if err != nil {
294- return false , errors .Wrap (err , "failed to get control plane spec replicas" )
249+ return false , errors .Wrapf (err , "failed to get control plane %s" , c . Replicas (). Path (). String () )
295250 }
296251
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-
301252 statusReplicas , err := c .StatusReplicas ().Get (obj )
302253 if err != nil {
303254 if errors .Is (err , ErrFieldNotFound ) {
@@ -306,54 +257,68 @@ func (c *ControlPlaneContract) IsScaling(obj *unstructured.Unstructured, contrac
306257 // so that we can block any operations that expect control plane to be stable.
307258 return true , nil
308259 }
309- return false , errors .Wrap (err , "failed to get control plane status replicas" )
260+ return false , errors .Wrapf (err , "failed to get control plane %s" , c . StatusReplicas (). Path (). String () )
310261 }
311262
312- updatedReplicas , err := c .UpdatedReplicas (contractVersion ).Get (obj )
263+ upToDateReplicas , err := c .UpToDateReplicas (contractVersion ).Get (obj )
313264 if err != nil {
314265 if errors .Is (err , ErrFieldNotFound ) {
315266 // If updatedReplicas is not set on the control plane
316267 // we should consider the control plane to be scaling so that
317268 // we block any operation that expect the control plane to be stable.
318269 return true , nil
319270 }
320- return false , errors .Wrap (err , "failed to get control plane status updatedReplicas" )
271+ return false , errors .Wrapf (err , "failed to get control plane %s" , c . UpToDateReplicas ( contractVersion ). Path (). String () )
321272 }
322273
323- readyReplicas , err := c .ReadyReplicas (contractVersion ).Get (obj )
274+ readyReplicas , err := c .ReadyReplicas ().Get (obj )
324275 if err != nil {
325276 if errors .Is (err , ErrFieldNotFound ) {
326277 // If readyReplicas is not set on the control plane
327278 // we should consider the control plane to be scaling so that
328279 // we block any operation that expect the control plane to be stable.
329280 return true , nil
330281 }
331- return false , errors .Wrap (err , "failed to get control plane status readyReplicas" )
282+ return false , errors .Wrapf (err , "failed to get control plane %s" , c . ReadyReplicas (). Path (). String () )
332283 }
333284
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" )
285+ var availableReplicas * int32
286+ if contractVersion == "v1beta1" {
287+ unavailableReplicas , err := c .V1Beta1UnavailableReplicas ().Get (obj )
288+ if err != nil {
289+ if ! errors .Is (err , ErrFieldNotFound ) {
290+ return false , errors .Wrapf (err , "failed to get control plane %s" , c .V1Beta1UnavailableReplicas ().Path ().String ())
291+ }
292+ // If unavailableReplicas is not set on the control plane we assume it is 0.
293+ // We have to do this as the following happens after clusterctl move with KCP:
294+ // * clusterctl move creates the KCP object without status
295+ // * the KCP controller won't patch the field to 0 if it doesn't exist
296+ // * This is because the patchHelper marshals before/after object to JSON to calculate a diff
297+ // and as the unavailableReplicas field is not a pointer, not set and 0 are both rendered as 0.
298+ // 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.
299+ unavailableReplicas = ptr.To [int64 ](0 )
300+ }
301+ availableReplicas = ptr .To (* desiredReplicas - int32 (* unavailableReplicas ))
302+ } else {
303+ availableReplicas , err = c .AvailableReplicas ().Get (obj )
304+ if err != nil {
305+ if errors .Is (err , ErrFieldNotFound ) {
306+ // If availableReplicas is not set on the control plane
307+ // we should consider the control plane to be scaling so that
308+ // we block any operation that expect the control plane to be stable.
309+ return true , nil
310+ }
311+ return false , errors .Wrapf (err , "failed to get control plane %s" , c .AvailableReplicas ().Path ().String ())
338312 }
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 )
347313 }
348314
349315 // Control plane is still scaling if:
350- // * .spec.replicas, .status.replicas, .status.updatedReplicas,
351- // .status.readyReplicas are not equal and
352- // * unavailableReplicas > 0
316+ // * .spec.replicas, .status.replicas, .status.upToDateReplicas,
317+ // .status.readyReplicas, .status.availableReplicas are not equal.
353318 if * statusReplicas != * desiredReplicas ||
354- * updatedReplicas != * desiredReplicas ||
319+ * upToDateReplicas != * desiredReplicas ||
355320 * readyReplicas != * desiredReplicas ||
356- * unavailableReplicas > 0 {
321+ * availableReplicas != * desiredReplicas {
357322 return true , nil
358323 }
359324 return false , nil
0 commit comments