@@ -6,11 +6,15 @@ package cilium
66import (
77 "context"
88 "fmt"
9+ "time"
910
11+ "github.com/go-logr/logr"
1012 "github.com/spf13/pflag"
13+ appsv1 "k8s.io/api/apps/v1"
1114 corev1 "k8s.io/api/core/v1"
1215 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1316 clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
17+ "sigs.k8s.io/cluster-api/controllers/remote"
1418 runtimehooksv1 "sigs.k8s.io/cluster-api/exp/runtime/hooks/api/v1alpha1"
1519 ctrl "sigs.k8s.io/controller-runtime"
1620 ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -19,10 +23,12 @@ import (
1923 commonhandlers "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers"
2024 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/handlers/lifecycle"
2125 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/clustertopology/variables"
26+ capiutils "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/common/pkg/capi/utils"
2227 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/addons"
2328 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/generic/lifecycle/config"
2429 "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/options"
2530 handlersutils "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/handlers/utils"
31+ "github.com/nutanix-cloud-native/cluster-api-runtime-extensions-nutanix/pkg/wait"
2632)
2733
2834type CNIConfig struct {
@@ -221,7 +227,8 @@ func (c *CiliumCNI) apply(
221227 ),
222228 c .client ,
223229 helmChart ,
224- )
230+ ).
231+ WithDefaultWaiter ()
225232 case "" :
226233 resp .SetStatus (runtimehooksv1 .ResponseStatusFailure )
227234 resp .SetMessage ("strategy not specified for Cilium CNI addon" )
@@ -231,11 +238,129 @@ func (c *CiliumCNI) apply(
231238 return
232239 }
233240
234- if err := strategy . Apply (ctx , cluster , targetNamespace , log ); err != nil {
241+ if err := runApply (ctx , c . client , cluster , strategy , targetNamespace , log ); err != nil {
235242 resp .SetStatus (runtimehooksv1 .ResponseStatusFailure )
236243 resp .SetMessage (err .Error ())
237244 return
238245 }
239246
240247 resp .SetStatus (runtimehooksv1 .ResponseStatusSuccess )
241248}
249+
250+ func runApply (
251+ ctx context.Context ,
252+ client ctrlclient.Client ,
253+ cluster * clusterv1.Cluster ,
254+ strategy addons.Applier ,
255+ targetNamespace string ,
256+ log logr.Logger ,
257+ ) error {
258+ if err := strategy .Apply (ctx , cluster , targetNamespace , log ); err != nil {
259+ return err
260+ }
261+
262+ // If skip kube-proxy is not set, return early.
263+ // Otherwise, wait for Cilium to be rolled out and then cleanup kube-proxy if installed.
264+ if ! capiutils .ShouldSkipKubeProxy (cluster ) {
265+ return nil
266+ }
267+
268+ log .Info (
269+ fmt .Sprintf ("Waiting for Cilium to be ready for cluster %s" , ctrlclient .ObjectKeyFromObject (cluster )),
270+ )
271+ if err := waitForCiliumToBeReady (ctx , client , cluster ); err != nil {
272+ return fmt .Errorf ("failed to wait for Cilium to be ready: %w" , err )
273+ }
274+
275+ log .Info (
276+ fmt .Sprintf ("Cleaning up kube-proxy for cluster %s" , ctrlclient .ObjectKeyFromObject (cluster )),
277+ )
278+ if err := cleanupKubeProxy (ctx , client , cluster ); err != nil {
279+ return fmt .Errorf ("failed to cleanup kube-proxy: %w" , err )
280+ }
281+
282+ return nil
283+ }
284+
285+ const (
286+ kubeProxyName = "kube-proxy"
287+ kubeProxyNamespace = "kube-system"
288+ )
289+
290+ func waitForCiliumToBeReady (
291+ ctx context.Context ,
292+ c ctrlclient.Client ,
293+ cluster * clusterv1.Cluster ,
294+ ) error {
295+ remoteClient , err := remote .NewClusterClient (
296+ ctx ,
297+ "" ,
298+ c ,
299+ ctrlclient .ObjectKeyFromObject (cluster ),
300+ )
301+ if err != nil {
302+ return fmt .Errorf ("error creating remote cluster client: %w" , err )
303+ }
304+
305+ ds := & appsv1.DaemonSet {
306+ ObjectMeta : metav1.ObjectMeta {
307+ Name : defaultCiliumReleaseName ,
308+ Namespace : defaultCiliumNamespace ,
309+ },
310+ }
311+ if err := wait .ForObject (
312+ ctx ,
313+ wait.ForObjectInput [* appsv1.DaemonSet ]{
314+ Reader : remoteClient ,
315+ Target : ds .DeepCopy (),
316+ Check : func (_ context.Context , obj * appsv1.DaemonSet ) (bool , error ) {
317+ return obj .Status .NumberAvailable == obj .Status .DesiredNumberScheduled && obj .Status .NumberUnavailable == 0 , nil
318+ },
319+ Interval : 1 * time .Second ,
320+ Timeout : 30 * time .Second ,
321+ },
322+ ); err != nil {
323+ return fmt .Errorf (
324+ "failed to wait for DaemonSet %s to be Ready: %w" ,
325+ ctrlclient .ObjectKeyFromObject (ds ),
326+ err ,
327+ )
328+ }
329+
330+ return nil
331+ }
332+
333+ // cleanupKubeProxy cleans up kube-proxy DaemonSet and ConfigMap on the remote cluster when kube-proxy is disabled.
334+ func cleanupKubeProxy (ctx context.Context , c ctrlclient.Client , cluster * clusterv1.Cluster ) error {
335+ remoteClient , err := remote .NewClusterClient (
336+ ctx ,
337+ "" ,
338+ c ,
339+ ctrlclient .ObjectKeyFromObject (cluster ),
340+ )
341+ if err != nil {
342+ return fmt .Errorf ("error creating remote cluster client: %w" , err )
343+ }
344+
345+ objs := []ctrlclient.Object {
346+ & appsv1.DaemonSet {
347+ ObjectMeta : metav1.ObjectMeta {
348+ Name : kubeProxyName ,
349+ Namespace : kubeProxyNamespace ,
350+ },
351+ },
352+ & corev1.ConfigMap {
353+ ObjectMeta : metav1.ObjectMeta {
354+ Name : kubeProxyName ,
355+ Namespace : kubeProxyNamespace ,
356+ },
357+ },
358+ }
359+ for _ , obj := range objs {
360+ if err := ctrlclient .IgnoreNotFound (remoteClient .Delete (ctx , obj )); err != nil {
361+ return fmt .Errorf ("failed to delete %s/%s: %w" , obj .GetNamespace (), obj .GetName (), err )
362+ }
363+ }
364+
365+ return nil
366+ }
0 commit comments