package main import ( "context" "fmt" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/client-go/kubernetes" "log" "slices" "strconv" ) type Cluster struct { namespaces map[string]v1.Namespace clientset *kubernetes.Clientset // map of namespace to list of all pods pods map[string][]v1.Pod } func NewCluster(clientset *kubernetes.Clientset) (*Cluster, error) { cluster := &Cluster{ clientset: clientset, namespaces: make(map[string]v1.Namespace), pods: make(map[string][]v1.Pod), } nslist, err := cluster.clientset.CoreV1().Namespaces().List(context.Background(), metav1.ListOptions{}) if err != nil { return nil, err } for _, ns := range nslist.Items { cluster.namespaces[ns.Name] = ns podList, err := cluster.clientset.CoreV1().Pods(ns.Name).List(context.Background(), metav1.ListOptions{}) if err != nil { return nil, err } cluster.pods[ns.Name] = podList.Items } return cluster, nil } func (c *Cluster) Pods(application *Application) []v1.Pod { selector, err := metav1.LabelSelectorAsSelector(application.Selector()) if err != nil { log.Fatalf("Error creating selector: %v", err) } pods := c.pods[application.Namespace.Name] pods = slices.DeleteFunc(slices.Clone(pods), func(pod v1.Pod) bool { return !selector.Matches(labels.Set(pod.Labels)) }) return pods } func (c *Cluster) ServiceAccounts(application *Application) []string { var res []string for _, pod := range c.Pods(application) { if !slices.Contains(res, pod.Spec.ServiceAccountName) { res = append(res, pod.Spec.ServiceAccountName) } } return res } func (c *Cluster) OwnerReferences(application *Application) []string { var ownerReferences []string for _, pod := range c.Pods(application) { //log.Printf(" %s %v", pod.Name, pod.OwnerReferences) for _, ownerReference := range pod.OwnerReferences { owner := ownerReference.Kind + "/" + ownerReference.Name if !slices.Contains(ownerReferences, owner) { ownerReferences = append(ownerReferences, owner) } } } return ownerReferences } func (c *Cluster) IsLinkerdEnabled(application *Application) bool { pods := c.Pods(application) ndisabled := 0 for _, pod := range pods { if pod.Annotations["linkerd.io/inject"] == "enabled" { return true } if pod.Annotations["linkerd.io/inject"] == "disabled" { ndisabled++ } } if ndisabled == len(pods) { return false } ns := c.namespaces[application.Namespace.Name] return ns.Annotations["linkerd.io/inject"] == "enabled" } func (c *Cluster) NamespaceLIst() []v1.Namespace { return MapValues(c.namespaces) } func (c *Cluster) Namespace(name string) v1.Namespace { return c.namespaces[name] } func (c *Cluster) PodList(namespace string) []v1.Pod { return c.pods[namespace] } func (c *Cluster) PortNumbers(application *Application) []Port { if !c.IsLinkerdEnabled(application) { return nil } tcpPorts := make(map[int]Port) udpPorts := make(map[int]Port) for _, pod := range c.Pods(application) { for _, container := range pod.Spec.Containers { for _, port := range container.Ports { switch port.Protocol { case "TCP": tcpPorts[int(port.ContainerPort)] = Port{ Port: strconv.Itoa(int(port.ContainerPort)), Protocol: string(port.Protocol), } case "UDP": udpPorts[int(port.ContainerPort)] = Port{ Port: strconv.Itoa(int(port.ContainerPort)), Protocol: string(port.Protocol), } default: panic(fmt.Sprintf("Unknown port type for pod %s/%s: %s", pod.Namespace, pod.Name, port.Protocol)) } } } } res := MapValues(tcpPorts) res = append(res, MapValues(udpPorts)...) return res }