@@ -20,10 +20,13 @@ import (
2020 "context"
2121
2222 "github.com/pkg/errors"
23+ v1 "k8s.io/api/core/v1"
2324 apierrors "k8s.io/apimachinery/pkg/api/errors"
2425 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2526 "k8s.io/apimachinery/pkg/labels"
2627 "k8s.io/apimachinery/pkg/runtime"
28+ "k8s.io/apimachinery/pkg/types"
29+ "k8s.io/apimachinery/pkg/util/intstr"
2730 addonsv1alpha1 "sigs.k8s.io/cluster-api-addon-provider-helm/api/v1alpha1"
2831 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
2932 "sigs.k8s.io/cluster-api/util/conditions"
@@ -45,6 +48,15 @@ type HelmChartProxyReconciler struct {
4548 WatchFilterValue string
4649}
4750
51+ // helmReleaseProxyRolloutMeta is used to gather HelmReleaseProxy rollout
52+ // metadata for matching clusters.
53+ type helmReleaseProxyRolloutMeta struct {
54+ cluster clusterv1.Cluster
55+
56+ // Identifies whether HelmReleaseProxy exists for the cluster.
57+ hrpExists bool
58+ }
59+
4860// SetupWithManager sets up the controller with the Manager.
4961func (r * HelmChartProxyReconciler ) SetupWithManager (ctx context.Context , mgr ctrl.Manager , options controller.Options ) error {
5062 log := ctrl .LoggerFrom (ctx )
@@ -197,6 +209,101 @@ func (r *HelmChartProxyReconciler) reconcileNormal(ctx context.Context, helmChar
197209 }
198210 }
199211
212+ // RolloutStepSize is defined; check if count of HelmReleaseProxies matches
213+ // the count of matching clusters.
214+ if helmChartProxy .Spec .RolloutStepSize != nil {
215+ if len (clusters ) != len (helmReleaseProxies ) {
216+ // Set HelmReleaseProxiesRolloutCompletedCondition to false as
217+ // HelmReleaseProxies are being rolled out.
218+ conditions .MarkFalse (
219+ helmChartProxy ,
220+ addonsv1alpha1 .HelmReleaseProxiesRolloutCompletedCondition ,
221+ addonsv1alpha1 .HelmReleaseProxiesRolloutNotReady ,
222+ clusterv1 .ConditionSeverityInfo ,
223+ "%d Helm release proxies not yet rolled out" ,
224+ len (clusters )- len (helmReleaseProxies ),
225+ )
226+
227+ // Identifies clusters by their NamespacedName and gathers their
228+ // helmReleaseProxyRolloutMeta.
229+ clusterRolloutMeta := map [string ]* helmReleaseProxyRolloutMeta {}
230+ for _ , cls := range clusters {
231+ clusterRolloutMeta [types.NamespacedName {Namespace : cls .Namespace , Name : cls .Name }.String ()] = & helmReleaseProxyRolloutMeta {cluster : cls }
232+ }
233+ for _ , hrp := range helmReleaseProxies {
234+ hrpClsRef := hrp .Spec .ClusterRef
235+ rltMeta := clusterRolloutMeta [types.NamespacedName {Namespace : hrpClsRef .Namespace , Name : hrpClsRef .Name }.String ()]
236+ rltMeta .hrpExists = true
237+ }
238+
239+ hrpReadyCond := helmChartProxy .GetHelmReleaseProxyReadyCondition ()
240+ // If HelmReleaseProxiesReadyCondition is false, reconcile existing
241+ // HelmReleaseProxies and exit.
242+ if hrpReadyCond != nil && (hrpReadyCond .Status == v1 .ConditionFalse ) {
243+ for _ , hrpRltMeta := range clusterRolloutMeta {
244+ if hrpRltMeta .hrpExists {
245+ // Don't reconcile if the Cluster is being deleted
246+ if ! hrpRltMeta .cluster .DeletionTimestamp .IsZero () {
247+ continue
248+ }
249+
250+ err := r .reconcileForCluster (ctx , helmChartProxy , hrpRltMeta .cluster )
251+ if err != nil {
252+ return err
253+ }
254+ }
255+ }
256+ return nil
257+ }
258+
259+ // HelmReleaseProxyReadyCondition is True; continue with reconciling the
260+ // next batch of HelmReleaseProxies.
261+ count := 0
262+ stepSize , err := intstr .GetScaledValueFromIntOrPercent (helmChartProxy .Spec .RolloutStepSize , len (clusters ), true )
263+ if err != nil {
264+ return err
265+ }
266+ for _ , hrpRltMeta := range clusterRolloutMeta {
267+ // The next batch of helmReleaseProxies have been reconciled.
268+ if count >= stepSize {
269+ return nil
270+ }
271+
272+ // Skip reconciling the cluster if its HelmReleaseProxy already exists.
273+ if hrpRltMeta .hrpExists {
274+ continue
275+ }
276+
277+ // Don't reconcile if the Cluster is being deleted
278+ if ! hrpRltMeta .cluster .DeletionTimestamp .IsZero () {
279+ continue
280+ }
281+
282+ err := r .reconcileForCluster (ctx , helmChartProxy , hrpRltMeta .cluster )
283+ if err != nil {
284+ return err
285+ }
286+ count ++
287+ }
288+
289+ // In cases where the count of remaining HelmReleaseProxies to be rolled
290+ // out is less than rollout step size.
291+ return nil
292+ }
293+ // RolloutStepSize is defined and all HelmReleaseProxies have been rolled out.
294+ conditions .MarkTrue (helmChartProxy , addonsv1alpha1 .HelmReleaseProxiesRolloutCompletedCondition )
295+ } else {
296+ // RolloutStepSize is undefined. Set HelmReleaseProxiesRolloutCompletedCondition to True with reason.
297+ conditions .MarkTrueWithNegativePolarity (
298+ helmChartProxy ,
299+ addonsv1alpha1 .HelmReleaseProxiesRolloutCompletedCondition ,
300+ addonsv1alpha1 .HelmReleaseProxiesRolloutUndefined ,
301+ clusterv1 .ConditionSeverityInfo ,
302+ "HelmChartProxy does not use Rollout Step Size" ,
303+ )
304+ }
305+
306+ // Continue with reconciling for all clusters after initial rollout.
200307 for _ , cluster := range clusters {
201308 // Don't reconcile if the Cluster is being deleted
202309 if ! cluster .DeletionTimestamp .IsZero () {
@@ -321,6 +428,7 @@ func patchHelmChartProxy(ctx context.Context, patchHelper *patch.Helper, helmCha
321428 conditions .WithConditions (
322429 addonsv1alpha1 .HelmReleaseProxySpecsUpToDateCondition ,
323430 addonsv1alpha1 .HelmReleaseProxiesReadyCondition ,
431+ addonsv1alpha1 .HelmReleaseProxiesRolloutCompletedCondition ,
324432 ),
325433 )
326434
@@ -332,6 +440,7 @@ func patchHelmChartProxy(ctx context.Context, patchHelper *patch.Helper, helmCha
332440 clusterv1 .ReadyCondition ,
333441 addonsv1alpha1 .HelmReleaseProxySpecsUpToDateCondition ,
334442 addonsv1alpha1 .HelmReleaseProxiesReadyCondition ,
443+ addonsv1alpha1 .HelmReleaseProxiesRolloutCompletedCondition ,
335444 }},
336445 patch.WithStatusObservedGeneration {},
337446 )
0 commit comments