diff --git a/pkg/runtime/k8s_runtime.go b/pkg/runtime/k8s_runtime.go index 1a2c7677..74fd3dfc 100644 --- a/pkg/runtime/k8s_runtime.go +++ b/pkg/runtime/k8s_runtime.go @@ -52,11 +52,29 @@ type KubernetesRuntime struct { ListAllNamespaces bool // When true, List() queries all namespaces for scion pods } +var serviceAccountNamespacePath = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + func NewKubernetesRuntime(client *k8s.Client) *KubernetesRuntime { return &KubernetesRuntime{ Client: client, - DefaultNamespace: "default", + DefaultNamespace: defaultKubernetesNamespace(), + } +} + +func defaultKubernetesNamespace() string { + for _, envKey := range []string{"SCION_K8S_NAMESPACE", "POD_NAMESPACE"} { + if ns := strings.TrimSpace(os.Getenv(envKey)); ns != "" { + return ns + } } + + if data, err := os.ReadFile(serviceAccountNamespacePath); err == nil { + if ns := strings.TrimSpace(string(data)); ns != "" { + return ns + } + } + + return "default" } // isGKEScheduling returns true when GKE Autopilot scheduling tolerance diff --git a/pkg/runtime/k8s_runtime_test.go b/pkg/runtime/k8s_runtime_test.go index a45017b4..940823c9 100644 --- a/pkg/runtime/k8s_runtime_test.go +++ b/pkg/runtime/k8s_runtime_test.go @@ -16,6 +16,8 @@ package runtime import ( "context" + "os" + "path/filepath" "testing" "github.com/GoogleCloudPlatform/scion/pkg/api" @@ -235,3 +237,60 @@ func TestKubernetesRuntime_BuildPod_Env(t *testing.T) { t.Errorf("LOGNAME not found in pod env") } } + +func TestDefaultKubernetesNamespace(t *testing.T) { + t.Run("env overrides default", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "scion") + t.Setenv("SCION_K8S_NAMESPACE", "") + if got := defaultKubernetesNamespace(); got != "scion" { + t.Fatalf("defaultKubernetesNamespace() = %q, want %q", got, "scion") + } + }) + + t.Run("serviceaccount file used when env missing", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "") + t.Setenv("SCION_K8S_NAMESPACE", "") + + tmpDir := t.TempDir() + nsFile := filepath.Join(tmpDir, "namespace") + if err := os.WriteFile(nsFile, []byte("scion-from-file\n"), 0644); err != nil { + t.Fatalf("failed to write temp namespace file: %v", err) + } + + prev := serviceAccountNamespacePath + serviceAccountNamespacePath = nsFile + defer func() { serviceAccountNamespacePath = prev }() + + if got := defaultKubernetesNamespace(); got != "scion-from-file" { + t.Fatalf("defaultKubernetesNamespace() = %q, want %q", got, "scion-from-file") + } + }) + + t.Run("default fallback", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "") + t.Setenv("SCION_K8S_NAMESPACE", "") + + prev := serviceAccountNamespacePath + serviceAccountNamespacePath = filepath.Join(t.TempDir(), "missing") + defer func() { serviceAccountNamespacePath = prev }() + + if got := defaultKubernetesNamespace(); got != "default" { + t.Fatalf("defaultKubernetesNamespace() = %q, want %q", got, "default") + } + }) +} + +func TestNewKubernetesRuntime_UsesDetectedNamespace(t *testing.T) { + clientset := k8sfake.NewClientset() + scheme := k8sruntime.NewScheme() + fc := fake.NewSimpleDynamicClient(scheme) + client := k8s.NewTestClient(fc, clientset) + + t.Setenv("POD_NAMESPACE", "scion") + t.Setenv("SCION_K8S_NAMESPACE", "") + + r := NewKubernetesRuntime(client) + if r.DefaultNamespace != "scion" { + t.Fatalf("DefaultNamespace = %q, want %q", r.DefaultNamespace, "scion") + } +}