package main import ( "bytes" "crypto/tls" "encoding/json" "fmt" "html/template" "io" "log" "net/http" "os" "path/filepath" "strconv" ) type K8sList struct { Items []json.RawMessage `json:"items"` } func getEnv(key, fallback string) string { if val, exists := os.LookupEnv(key); exists && val != "" { return val } return fallback } func getTemplate(path string) (*template.Template, error) { content, err := os.ReadFile(path) if err != nil { return nil, err } return template.New("haproxy").Parse(string(content)) } func getK8sResources(k8sHost, token string, verifySSL bool, resource string) ([]json.RawMessage, error) { url := fmt.Sprintf("%s/api/v1/%s", k8sHost, resource) req, err := http.NewRequest("GET", url, nil) if err != nil { return nil, err } req.Header.Set("Authorization", "Bearer "+token) req.Header.Set("Accept", "application/json") client := &http.Client{} if !verifySSL { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client.Transport = tr } resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)) } var list K8sList if err := json.NewDecoder(resp.Body).Decode(&list); err != nil { return nil, err } return list.Items, nil } func main() { k8sHost := getEnv("KUBERNETES_HOST", "") k8sToken := getEnv("KUBERNETES_TOKEN", "") verifySSLStr := getEnv("KUBERNETES_VERIFYSSL", "false") templatePath := getEnv("HAPROXY_TEMPLATE", "haproxy.tmpl") if k8sHost == "" || k8sToken == "" { log.Fatal("KUBERNETES_HOST and KUBERNETES_TOKEN must be set") } verifySSL, err := strconv.ParseBool(verifySSLStr) if err != nil { log.Fatalf("Invalid KUBERNETES_VERIFYSSL value: %v", err) } services, err := getK8sResources(k8sHost, k8sToken, verifySSL, "services") if err != nil { log.Fatalf("Failed to get services: %v", err) } endpoints, err := getK8sResources(k8sHost, k8sToken, verifySSL, "endpoints") if err != nil { log.Fatalf("Failed to get endpoints: %v", err) } tmplAbsPath, _ := filepath.Abs(templatePath) 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, }) if err != nil { log.Fatalf("Failed to render template: %v", err) } fmt.Println(buf.String()) }