// Package kubernetes provides Kubernetes-based implementations of port interfaces. package kubernetes import ( "fmt" "os" "path/filepath" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/clientcmd" ) // ClientConfig holds configuration for the Kubernetes client. type ClientConfig struct { // Namespace is the K8s namespace to operate in. Namespace string // Kubeconfig is the path to the kubeconfig file (optional, for local dev). Kubeconfig string } // NewClient creates a new Kubernetes clientset. // When running in-cluster, it uses the service account token. // When running locally, it uses the kubeconfig file. func NewClient(cfg ClientConfig) (*kubernetes.Clientset, error) { var config *rest.Config var err error // Try in-cluster config first (when running in K8s) config, err = rest.InClusterConfig() if err != nil { // Fall back to kubeconfig for local development kubeconfigPath := cfg.Kubeconfig if kubeconfigPath == "" { kubeconfigPath = defaultKubeconfigPath() } config, err = clientcmd.BuildConfigFromFlags("", kubeconfigPath) if err != nil { return nil, fmt.Errorf("failed to create k8s config: %w", err) } } clientset, err := kubernetes.NewForConfig(config) if err != nil { return nil, fmt.Errorf("failed to create k8s clientset: %w", err) } return clientset, nil } // NewClientOrNil creates a K8s client, returning nil if it fails. // This is useful for graceful fallback to hardcoded projects. func NewClientOrNil(cfg ClientConfig) *kubernetes.Clientset { client, err := NewClient(cfg) if err != nil { return nil } return client } // defaultKubeconfigPath returns the default kubeconfig path. func defaultKubeconfigPath() string { if kubeconfig := os.Getenv("KUBECONFIG"); kubeconfig != "" { return kubeconfig } home, err := os.UserHomeDir() if err != nil { return "" } return filepath.Join(home, ".kube", "config") }