@@ -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