From c88d0e27394dda3e09bb2d6fe113ec6c91ba89d0 Mon Sep 17 00:00:00 2001 From: holzi1005 Date: Sun, 29 Jun 2025 07:39:38 +0200 Subject: [PATCH] Update main.go --- main.go | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 135 insertions(+), 14 deletions(-) diff --git a/main.go b/main.go index c315dd3..f31df6b 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,9 @@ package main import ( - "bytes" + "crypto/sha1" "crypto/tls" + "encoding/hex" "encoding/json" "fmt" "html/template" @@ -12,19 +13,39 @@ import ( "os" "path/filepath" "strconv" + "strings" ) -type K8sList struct { +type K8sRawList struct { Items []json.RawMessage `json:"items"` } +type BackendServer struct { + Name string + Address string + Port int + Cookie string +} + +type Backend struct { + Name string + Balance string + Servers []BackendServer +} + func getEnv(key, fallback string) string { - if val, exists := os.LookupEnv(key); exists && val != "" { + if val, ok := os.LookupEnv(key); ok && val != "" { return val } return fallback } +func hashString(input string) string { + h := sha1.New() + h.Write([]byte(input)) + return hex.EncodeToString(h.Sum(nil))[:8] +} + func getTemplate(path string) (*template.Template, error) { content, err := os.ReadFile(path) if err != nil { @@ -42,6 +63,7 @@ func getK8sResources(k8sHost, token string, verifySSL bool, resource string) ([] } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Accept", "application/json") + req.Header.Set("User-Agent", "gateway-config") client := &http.Client{} if !verifySSL { @@ -62,7 +84,7 @@ func getK8sResources(k8sHost, token string, verifySSL bool, resource string) ([] return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } - var list K8sList + var list K8sRawList if err := json.NewDecoder(resp.Body).Decode(&list); err != nil { return nil, err } @@ -84,29 +106,128 @@ func main() { log.Fatalf("Invalid KUBERNETES_VERIFYSSL value: %v", err) } - services, err := getK8sResources(k8sHost, k8sToken, verifySSL, "services") + servicesRaw, err := getK8sResources(k8sHost, k8sToken, verifySSL, "services") if err != nil { log.Fatalf("Failed to get services: %v", err) } - endpoints, err := getK8sResources(k8sHost, k8sToken, verifySSL, "endpoints") + endpointsRaw, err := getK8sResources(k8sHost, k8sToken, verifySSL, "endpoints") if err != nil { log.Fatalf("Failed to get endpoints: %v", err) } - tmplAbsPath, _ := filepath.Abs(templatePath) + type Service struct { + Metadata struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + } `json:"metadata"` + Spec struct { + Type string `json:"type"` + Ports []struct { + Port int `json:"port"` + } `json:"ports"` + } `json:"spec"` + } + + type EndpointSubsetAddress struct { + IP string `json:"ip"` + } + + type EndpointSubsetPort struct { + Port int `json:"port"` + } + + type EndpointSubset struct { + Addresses []EndpointSubsetAddress `json:"addresses"` + Ports []EndpointSubsetPort `json:"ports"` + } + + type Endpoint struct { + Metadata struct { + Name string `json:"name"` + Namespace string `json:"namespace"` + } `json:"metadata"` + Subsets []EndpointSubset `json:"subsets"` + } + + services := make([]Service, 0, len(servicesRaw)) + for _, raw := range servicesRaw { + var svc Service + if err := json.Unmarshal(raw, &svc); err != nil { + log.Printf("Warn: failed to unmarshal service: %v", err) + continue + } + services = append(services, svc) + } + + endpoints := make([]Endpoint, 0, len(endpointsRaw)) + for _, raw := range endpointsRaw { + var ep Endpoint + if err := json.Unmarshal(raw, &ep); err != nil { + log.Printf("Warn: failed to unmarshal endpoint: %v", err) + continue + } + endpoints = append(endpoints, ep) + } + + endpointMap := make(map[string]Endpoint) + for _, ep := range endpoints { + key := ep.Metadata.Namespace + "/" + ep.Metadata.Name + endpointMap[key] = ep + } + + backends := []Backend{} + + for _, svc := range services { + if svc.Spec.Type != "LoadBalancer" { + continue + } + key := svc.Metadata.Namespace + "/" + svc.Metadata.Name + ep, found := endpointMap[key] + if !found || len(ep.Subsets) == 0 { + continue + } + + b := Backend{ + Name: "SRV_" + strings.ReplaceAll(svc.Metadata.Name, " ", "-"), + Balance: "leastconn", + } + + servers := []BackendServer{} + serverIndex := 1 + for _, subset := range ep.Subsets { + for _, addr := range subset.Addresses { + for _, port := range subset.Ports { + cookie := hashString(fmt.Sprintf("%s-%s-%d", svc.Metadata.Name, addr.IP, port.Port)) + serverName := fmt.Sprintf("%s_%d", svc.Metadata.Name, serverIndex) + serverIndex++ + + servers = append(servers, BackendServer{ + Name: serverName, + Address: addr.IP, + Port: port.Port, + Cookie: cookie, + }) + } + } + } + b.Servers = servers + + backends = append(backends, b) + } + + tmplAbsPath, err := filepath.Abs(templatePath) + if err != nil { + log.Fatalf("Failed to get absolute path: %v", err) + } tmpl, err := getTemplate(tmplAbsPath) if err != nil { log.Fatalf("Failed to parse template: %v", err) } - var buf bytes.Buffer - err = tmpl.Execute(&buf, map[string]interface{}{ - "services": services, - "endpoints": endpoints, + err = tmpl.Execute(os.Stdout, map[string]interface{}{ + "backends": backends, }) if err != nil { - log.Fatalf("Failed to render template: %v", err) + log.Fatalf("Failed to execute template: %v", err) } - - fmt.Println(buf.String()) }