package smartconfig import ( "context" "fmt" "strings" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) // K8SSecretResolver retrieves secrets from Kubernetes. // Usage: ${K8SS:namespace/secretname:key} type K8SSecretResolver struct{} // Resolve retrieves the secret value from Kubernetes. func (r *K8SSecretResolver) Resolve(value string) (string, error) { // Expect format: "namespace/secretname:key" parts := strings.SplitN(value, ":", 2) if len(parts) != 2 { return "", fmt.Errorf("invalid K8S secret format, expected NAMESPACE/SECRET:KEY") } secretPath := parts[0] key := parts[1] pathParts := strings.SplitN(secretPath, "/", 2) if len(pathParts) != 2 { return "", fmt.Errorf("invalid K8S secret path format, expected NAMESPACE/SECRET") } namespace := pathParts[0] secretName := pathParts[1] config, err := rest.InClusterConfig() if err != nil { // Fall back to kubeconfig return "", fmt.Errorf("failed to get K8S config: %w", err) } clientset, err := kubernetes.NewForConfig(config) if err != nil { return "", fmt.Errorf("failed to create K8S client: %w", err) } ctx := context.Background() secret, err := clientset.CoreV1().Secrets(namespace).Get(ctx, secretName, metav1.GetOptions{}) if err != nil { return "", fmt.Errorf("failed to get secret %s/%s: %w", namespace, secretName, err) } data, ok := secret.Data[key] if !ok { return "", fmt.Errorf("key %s not found in secret %s/%s", key, namespace, secretName) } return string(data), nil }