package iplookup import ( "context" "fmt" "net" "edge-infra.dev/pkg/lib/fog" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) const ( KIND string = "DaemonSet" ) var ( ErrInvalidQuery = fmt.Errorf("query provided is invalid") ErrNoReturn = fmt.Errorf("nothing to return") ErrInvalidIP = fmt.Errorf("returned pod has invalid ip") ErrNotInCluster = rest.ErrNotInCluster ) // Struct containing details of the daemonset to be found type Query struct { Namespace string Daemonset string } type IPLookup struct { clientset kubernetes.Interface } // Returns a new IPLookup struct which can be used to find the IP of kubernetes // resources // Must be run witin a kubernetes cluster func New() (*IPLookup, error) { cfg, err := rest.InClusterConfig() if err != nil { return nil, err } cl, err := kubernetes.NewForConfig(cfg) if err != nil { return nil, err } return &IPLookup{ clientset: cl, }, nil } // Returns the IP address of the pod on the given nodeID belonging to the // daemonset in query // Returns [ErrNoReturn] when no pods were found that matched the given node and // query func (ip *IPLookup) GetDaemonsetPodIP(ctx context.Context, nodeID string, query Query) (net.IP, error) { if query.Daemonset == "" || query.Namespace == "" { return nil, ErrInvalidQuery } return ip.daemonsetPodIPLookup(ctx, nodeID, query) } func (ip *IPLookup) daemonsetPodIPLookup(ctx context.Context, nodeID string, query Query) (net.IP, error) { log := fog.FromContext(ctx) log.Info("Querying kubernetes client", "Node Name", nodeID, "Namespace", query.Namespace, "Daemonset", query.Daemonset, ) // The clientset appears to do rate limiting before checking the context is done // Manually check context done to exit sooner in the case of an expired context select { case <-ctx.Done(): return nil, fmt.Errorf("context done: %w", ctx.Err()) default: } pods, err := ip.clientset.CoreV1().Pods(query.Namespace).List(ctx, metav1.ListOptions{}) if err != nil { return nil, fmt.Errorf("failed to query kubernetes client: %w", err) } log.Info("Query discovered pods.", "noPods", len(pods.Items)) for _, pod := range pods.Items { podip, err := checkPod(pod, nodeID, query) if err == nil { log.Info("pod found satisfying query", "podName", pod.Name) return podip, nil } else if err == ErrInvalidIP { return nil, err } } return nil, ErrNoReturn } func checkPod(pod v1.Pod, nodeID string, query Query) (ip net.IP, err error) { if pod.Spec.NodeName == nodeID && pod.ObjectMeta.Namespace == query.Namespace { for _, own := range pod.OwnerReferences { if own.Kind == KIND && own.Name == query.Daemonset { ip = net.ParseIP(pod.Status.PodIP) if ip != nil { return ip, nil } return nil, ErrInvalidIP } } } return nil, ErrNoReturn }