package agent import ( "context" "encoding/json" "fmt" "log/slog" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) // keepNetworkAvailable maintains a NetworkUnavailable=False condition on // the node's status. Calico-node sets this False while it owns CNI; on // shutdown it sets it to True with reason CalicoIsDown, which adds the // node.kubernetes.io/network-unavailable taint and blocks new scheduling. // Once flock-agent is in charge, we own the condition. // // Re-applies every minute — heartbeat-style — so a stale condition from a // previous CNI is overwritten without an explicit transition. func keepNetworkAvailable(ctx context.Context, cfg *rest.Config, node string, logger *slog.Logger) { cs, err := kubernetes.NewForConfig(cfg) if err != nil { logger.Warn("network-condition: kubernetes client", "err", err) return } apply := func() { now := metav1.Now() patch := map[string]interface{}{ "status": map[string]interface{}{ "conditions": []corev1.NodeCondition{{ Type: corev1.NodeNetworkUnavailable, Status: corev1.ConditionFalse, Reason: "FlockReady", Message: "flock-agent owns CNI on this node", LastHeartbeatTime: now, LastTransitionTime: now, }}, }, } body, _ := json.Marshal(patch) _, err := cs.CoreV1().Nodes().Patch(ctx, node, types.MergePatchType, body, metav1.PatchOptions{}, "status") if err != nil { logger.Warn("network-condition: patch failed", "err", err) return } } apply() t := time.NewTicker(60 * time.Second) defer t.Stop() for { select { case <-ctx.Done(): return case <-t.C: apply() } } } // silence unused-import warnings on non-Linux builds where this is unused. var _ = fmt.Sprintf