Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion backend/pkg/kubeconfig/kubeconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/base64"
"errors"
"fmt"
"io/fs"
"net/http"
"net/http/httputil"
"net/url"
Expand Down Expand Up @@ -460,9 +461,23 @@ type ContextLoadError struct {
// It returns an error if the file cannot be read.
// It will return valid (contexts, ContextLoadErrors,nil) and errors if there are any errors in the file.
func LoadContextsFromFile(kubeConfigPath string, source int) ([]Context, []ContextLoadError, error) {
// An empty path or a path that does not exist on disk is not an error
// condition for this loader: there is simply nothing to read. The
// in-cluster mode leaves KubeConfigPath empty, and the dynamic-cluster
// persistence file is absent on a fresh deployment until the user adds
// the first dynamic cluster. Treating both as ERRORs produced log
// spam reported in issues #4724 and #4401.
if kubeConfigPath == "" {
return nil, nil, nil
}

data, err := os.ReadFile(kubeConfigPath) //nolint:gosec
if err != nil {
return nil, nil, fmt.Errorf("error reading kubeconfig file: %v", err)
if errors.Is(err, fs.ErrNotExist) {
return nil, nil, nil
}

return nil, nil, fmt.Errorf("error reading kubeconfig file: %w", err)
}

skipProxySetup := source != KubeConfig
Expand Down
46 changes: 36 additions & 10 deletions backend/pkg/kubeconfig/kubeconfig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,28 @@ func TestLoadAndStoreKubeConfigs(t *testing.T) {
require.Equal(t, "minikube", ctx.Name)
})

t.Run("invalid_file", func(t *testing.T) {
kubeConfigFile := "invalid_kubeconfig"
t.Run("missing_file", func(t *testing.T) {
// Regression for issues #4724 and #4401: a kubeconfig path that does
// not exist on disk (in-cluster dynamic-cluster persistence file,
// fresh deployments) used to surface as an ERROR. It should now be
// treated as "no contexts to load".
err := kubeconfig.LoadAndStoreKubeConfigs(
contextStore, "missing_kubeconfig", kubeconfig.KubeConfig, nil,
)
require.NoError(t, err)
})

err := kubeconfig.LoadAndStoreKubeConfigs(contextStore, kubeConfigFile, kubeconfig.KubeConfig, nil)
require.Error(t, err)
t.Run("empty_path", func(t *testing.T) {
// Regression for issues #4724 and #4401: in-cluster mode leaves
// KubeConfigPath empty; passing "" should not error.
store := kubeconfig.NewContextStore()

err := kubeconfig.LoadAndStoreKubeConfigs(store, "", kubeconfig.KubeConfig, nil)
require.NoError(t, err)

contexts, err := store.GetContexts()
require.NoError(t, err)
require.Empty(t, contexts)
})
}

Expand All @@ -95,13 +112,22 @@ func TestLoadContextsFromKubeConfigFile(t *testing.T) {
require.Equal(t, 2, len(contexts), "Expected 2 contexts from valid file")
})

t.Run("invalid_file", func(t *testing.T) {
kubeConfigFile := "invalid_kubeconfig"
t.Run("missing_file", func(t *testing.T) {
// Regression for issues #4724 and #4401. See TestLoadAndStoreKubeConfigs.
contexts, contextErrors, err := kubeconfig.LoadContextsFromFile(
"missing_kubeconfig", kubeconfig.KubeConfig,
)
require.NoError(t, err, "missing file is not an error condition")
require.Empty(t, contextErrors)
require.Empty(t, contexts)
})

contexts, contextErrors, err := kubeconfig.LoadContextsFromFile(kubeConfigFile, kubeconfig.KubeConfig)
require.Error(t, err, "Expected error for invalid file")
require.Empty(t, contextErrors, "Expected no context errors for invalid file")
require.Empty(t, contexts, "Expected no contexts from invalid file")
t.Run("empty_path", func(t *testing.T) {
// Regression for issues #4724 and #4401: in-cluster mode passes "".
contexts, contextErrors, err := kubeconfig.LoadContextsFromFile("", kubeconfig.KubeConfig)
require.NoError(t, err)
require.Empty(t, contextErrors)
require.Empty(t, contexts)
})

t.Run("autherror", func(t *testing.T) {
Expand Down
Loading