From 3019da796f18dc7338dff10ca8be957a9c17076f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:27:45 +0000 Subject: [PATCH 1/4] Initial plan From fdac36f5197cd46126a242ab8de86411af94b10c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:31:16 +0000 Subject: [PATCH 2/4] feat: detect namespace from service account file in LoadInClusterConfig Co-authored-by: basebandit <8973567+basebandit@users.noreply.github.com> --- cluster/manager.go | 32 ++++++++++++++++++++- cluster/manager_test.go | 62 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) diff --git a/cluster/manager.go b/cluster/manager.go index 3f0a572..aa0b184 100644 --- a/cluster/manager.go +++ b/cluster/manager.go @@ -78,11 +78,14 @@ func (cm *Manager) LoadInClusterConfig(name string) error { return fmt.Errorf("failed to connect to cluster: %w", err) } + // Detect the namespace from the service account namespace file + namespace := detectInClusterNamespace() + contextInfo := &kai.ContextInfo{ Name: name, Cluster: "in-cluster", User: "service-account", - Namespace: "default", + Namespace: namespace, ServerURL: config.Host, ConfigPath: "", IsActive: true, @@ -97,6 +100,7 @@ func (cm *Manager) LoadInClusterConfig(name string) error { slog.Info("in-cluster config loaded", slog.String("context", name), slog.String("server", config.Host), + slog.String("namespace", namespace), ) return nil @@ -511,6 +515,32 @@ func validateFile(path string) error { return nil } +// detectInClusterNamespace reads the namespace from the service account namespace file +// when running inside a Kubernetes pod. Falls back to "default" if the file cannot be read. +func detectInClusterNamespace() string { + const namespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + + // #nosec G304 - This is a well-known Kubernetes service account file path + data, err := os.ReadFile(namespaceFile) + if err != nil { + slog.Debug("failed to read namespace from service account file, using default", + slog.String("file", namespaceFile), + slog.String("error", err.Error()), + ) + return "default" + } + + namespace := strings.TrimSpace(string(data)) + if namespace == "" { + slog.Debug("namespace file is empty, using default", + slog.String("file", namespaceFile), + ) + return "default" + } + + return namespace +} + func ptr[T any](v T) *T { return &v } diff --git a/cluster/manager_test.go b/cluster/manager_test.go index f98daeb..bde6a7d 100644 --- a/cluster/manager_test.go +++ b/cluster/manager_test.go @@ -3,6 +3,7 @@ package cluster import ( "os" "path/filepath" + "strings" "testing" "github.com/basebandit/kai" @@ -50,6 +51,67 @@ func TestExtendedClusterManager(t *testing.T) { func TestInClusterConfig(t *testing.T) { t.Run("LoadInClusterConfig", testLoadInClusterConfig) + t.Run("DetectInClusterNamespace", testDetectInClusterNamespace) +} + +func testDetectInClusterNamespace(t *testing.T) { + t.Run("NamespaceFileDoesNotExist", func(t *testing.T) { + // When the namespace file doesn't exist, should return "default" + namespace := detectInClusterNamespace() + assert.Equal(t, "default", namespace) + }) + + t.Run("NamespaceFileExists", func(t *testing.T) { + // Create a temporary directory and file to simulate the service account namespace file + tmpDir := t.TempDir() + namespaceFile := filepath.Join(tmpDir, "namespace") + + // Write a test namespace to the file + testNamespace := "my-custom-namespace" + err := os.WriteFile(namespaceFile, []byte(testNamespace), 0600) + require.NoError(t, err) + + // Temporarily replace the constant path (we'll need to refactor detectInClusterNamespace to accept a path parameter for testing) + // For now, we'll test the logic by reading directly + data, err := os.ReadFile(namespaceFile) + require.NoError(t, err) + namespace := strings.TrimSpace(string(data)) + assert.Equal(t, testNamespace, namespace) + }) + + t.Run("NamespaceFileIsEmpty", func(t *testing.T) { + // Create a temporary empty file + tmpDir := t.TempDir() + namespaceFile := filepath.Join(tmpDir, "namespace") + + err := os.WriteFile(namespaceFile, []byte(""), 0600) + require.NoError(t, err) + + // Read and verify empty file returns default + data, err := os.ReadFile(namespaceFile) + require.NoError(t, err) + namespace := strings.TrimSpace(string(data)) + if namespace == "" { + namespace = "default" + } + assert.Equal(t, "default", namespace) + }) + + t.Run("NamespaceFileWithWhitespace", func(t *testing.T) { + // Create a temporary file with whitespace + tmpDir := t.TempDir() + namespaceFile := filepath.Join(tmpDir, "namespace") + + testNamespace := "my-namespace" + err := os.WriteFile(namespaceFile, []byte(" "+testNamespace+" \n"), 0600) + require.NoError(t, err) + + // Read and verify whitespace is trimmed + data, err := os.ReadFile(namespaceFile) + require.NoError(t, err) + namespace := strings.TrimSpace(string(data)) + assert.Equal(t, testNamespace, namespace) + }) } func testLoadInClusterConfig(t *testing.T) { From 5c6a73deff1b603f9b338974856d32cef2667fe9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:32:24 +0000 Subject: [PATCH 3/4] refactor: make detectInClusterNamespace testable with custom path parameter Co-authored-by: basebandit <8973567+basebandit@users.noreply.github.com> --- cluster/manager.go | 10 +++++++--- cluster/manager_test.go | 44 +++++++++++++++++++---------------------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/cluster/manager.go b/cluster/manager.go index aa0b184..2779871 100644 --- a/cluster/manager.go +++ b/cluster/manager.go @@ -79,7 +79,7 @@ func (cm *Manager) LoadInClusterConfig(name string) error { } // Detect the namespace from the service account namespace file - namespace := detectInClusterNamespace() + namespace := detectInClusterNamespace("") contextInfo := &kai.ContextInfo{ Name: name, @@ -517,8 +517,12 @@ func validateFile(path string) error { // detectInClusterNamespace reads the namespace from the service account namespace file // when running inside a Kubernetes pod. Falls back to "default" if the file cannot be read. -func detectInClusterNamespace() string { - const namespaceFile = "/var/run/secrets/kubernetes.io/serviceaccount/namespace" +// If customPath is provided and not empty, it will be used instead of the default Kubernetes path. +func detectInClusterNamespace(customPath string) string { + namespaceFile := "/var/run/secrets/kubernetes.io/serviceaccount/namespace" + if customPath != "" { + namespaceFile = customPath + } // #nosec G304 - This is a well-known Kubernetes service account file path data, err := os.ReadFile(namespaceFile) diff --git a/cluster/manager_test.go b/cluster/manager_test.go index bde6a7d..d7847e9 100644 --- a/cluster/manager_test.go +++ b/cluster/manager_test.go @@ -3,7 +3,6 @@ package cluster import ( "os" "path/filepath" - "strings" "testing" "github.com/basebandit/kai" @@ -57,7 +56,7 @@ func TestInClusterConfig(t *testing.T) { func testDetectInClusterNamespace(t *testing.T) { t.Run("NamespaceFileDoesNotExist", func(t *testing.T) { // When the namespace file doesn't exist, should return "default" - namespace := detectInClusterNamespace() + namespace := detectInClusterNamespace("/nonexistent/path/namespace") assert.Equal(t, "default", namespace) }) @@ -67,16 +66,13 @@ func testDetectInClusterNamespace(t *testing.T) { namespaceFile := filepath.Join(tmpDir, "namespace") // Write a test namespace to the file - testNamespace := "my-custom-namespace" - err := os.WriteFile(namespaceFile, []byte(testNamespace), 0600) + testNs := "my-custom-namespace" + err := os.WriteFile(namespaceFile, []byte(testNs), 0600) require.NoError(t, err) - // Temporarily replace the constant path (we'll need to refactor detectInClusterNamespace to accept a path parameter for testing) - // For now, we'll test the logic by reading directly - data, err := os.ReadFile(namespaceFile) - require.NoError(t, err) - namespace := strings.TrimSpace(string(data)) - assert.Equal(t, testNamespace, namespace) + // Test the actual function + namespace := detectInClusterNamespace(namespaceFile) + assert.Equal(t, testNs, namespace) }) t.Run("NamespaceFileIsEmpty", func(t *testing.T) { @@ -87,13 +83,8 @@ func testDetectInClusterNamespace(t *testing.T) { err := os.WriteFile(namespaceFile, []byte(""), 0600) require.NoError(t, err) - // Read and verify empty file returns default - data, err := os.ReadFile(namespaceFile) - require.NoError(t, err) - namespace := strings.TrimSpace(string(data)) - if namespace == "" { - namespace = "default" - } + // Test that empty file returns "default" + namespace := detectInClusterNamespace(namespaceFile) assert.Equal(t, "default", namespace) }) @@ -102,15 +93,20 @@ func testDetectInClusterNamespace(t *testing.T) { tmpDir := t.TempDir() namespaceFile := filepath.Join(tmpDir, "namespace") - testNamespace := "my-namespace" - err := os.WriteFile(namespaceFile, []byte(" "+testNamespace+" \n"), 0600) + testNs := "my-namespace" + err := os.WriteFile(namespaceFile, []byte(" "+testNs+" \n"), 0600) require.NoError(t, err) - // Read and verify whitespace is trimmed - data, err := os.ReadFile(namespaceFile) - require.NoError(t, err) - namespace := strings.TrimSpace(string(data)) - assert.Equal(t, testNamespace, namespace) + // Test that whitespace is trimmed + namespace := detectInClusterNamespace(namespaceFile) + assert.Equal(t, testNs, namespace) + }) + + t.Run("DefaultPath", func(t *testing.T) { + // When no custom path is provided, should use default path + // Since the default path won't exist in test environment, it should return "default" + namespace := detectInClusterNamespace("") + assert.Equal(t, "default", namespace) }) } From e969b7bb8fab54e4e47e33fd4308a310ecaa7d4b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 1 Feb 2026 12:32:59 +0000 Subject: [PATCH 4/4] style: remove trailing whitespace Co-authored-by: basebandit <8973567+basebandit@users.noreply.github.com> --- cluster/manager.go | 6 +++--- cluster/manager_test.go | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cluster/manager.go b/cluster/manager.go index 2779871..69176c2 100644 --- a/cluster/manager.go +++ b/cluster/manager.go @@ -523,7 +523,7 @@ func detectInClusterNamespace(customPath string) string { if customPath != "" { namespaceFile = customPath } - + // #nosec G304 - This is a well-known Kubernetes service account file path data, err := os.ReadFile(namespaceFile) if err != nil { @@ -533,7 +533,7 @@ func detectInClusterNamespace(customPath string) string { ) return "default" } - + namespace := strings.TrimSpace(string(data)) if namespace == "" { slog.Debug("namespace file is empty, using default", @@ -541,7 +541,7 @@ func detectInClusterNamespace(customPath string) string { ) return "default" } - + return namespace } diff --git a/cluster/manager_test.go b/cluster/manager_test.go index d7847e9..96c9fea 100644 --- a/cluster/manager_test.go +++ b/cluster/manager_test.go @@ -59,49 +59,49 @@ func testDetectInClusterNamespace(t *testing.T) { namespace := detectInClusterNamespace("/nonexistent/path/namespace") assert.Equal(t, "default", namespace) }) - + t.Run("NamespaceFileExists", func(t *testing.T) { // Create a temporary directory and file to simulate the service account namespace file tmpDir := t.TempDir() namespaceFile := filepath.Join(tmpDir, "namespace") - + // Write a test namespace to the file testNs := "my-custom-namespace" err := os.WriteFile(namespaceFile, []byte(testNs), 0600) require.NoError(t, err) - + // Test the actual function namespace := detectInClusterNamespace(namespaceFile) assert.Equal(t, testNs, namespace) }) - + t.Run("NamespaceFileIsEmpty", func(t *testing.T) { // Create a temporary empty file tmpDir := t.TempDir() namespaceFile := filepath.Join(tmpDir, "namespace") - + err := os.WriteFile(namespaceFile, []byte(""), 0600) require.NoError(t, err) - + // Test that empty file returns "default" namespace := detectInClusterNamespace(namespaceFile) assert.Equal(t, "default", namespace) }) - + t.Run("NamespaceFileWithWhitespace", func(t *testing.T) { // Create a temporary file with whitespace tmpDir := t.TempDir() namespaceFile := filepath.Join(tmpDir, "namespace") - + testNs := "my-namespace" err := os.WriteFile(namespaceFile, []byte(" "+testNs+" \n"), 0600) require.NoError(t, err) - + // Test that whitespace is trimmed namespace := detectInClusterNamespace(namespaceFile) assert.Equal(t, testNs, namespace) }) - + t.Run("DefaultPath", func(t *testing.T) { // When no custom path is provided, should use default path // Since the default path won't exist in test environment, it should return "default"