Ctrl K
ComunidadGuías…

internal/kube/client_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package kube

import (
	"net/http"
	"net/http/httptest"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"k8s.io/client-go/rest"
	"k8s.io/client-go/transport"

	"github.com/sophotechlabs/spinoza/internal/api"
	"github.com/sophotechlabs/spinoza/internal/auth"
)

const validKubeconfig = `apiVersion: v1
kind: Config
current-context: test-ctx
clusters:
- name: test-cluster
  cluster:
    server: https://127.0.0.1:6443
    insecure-skip-tls-verify: true
contexts:
- name: test-ctx
  context:
    cluster: test-cluster
    namespace: test-ns
    user: test-user
users:
- name: test-user
  user:
    token: test-token
`

func writeKubeconfig(t *testing.T, content string) string {
	t.Helper()
	dir := t.TempDir()
	path := filepath.Join(dir, "config")
	if err := os.WriteFile(path, []byte(content), 0o600); err != nil {
		t.Fatalf("write kubeconfig: %v", err)
	}
	return path
}

func TestLoadReturnsBundle(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)
	t.Setenv("KUBECONFIG", path)

	bundle, err := LoadContext(api.ContextRef{}, Options{})
	if err != nil {
		t.Fatalf("Load: %v", err)
	}
	if bundle.Clientset == nil {
		t.Fatal("Clientset = nil, want non-nil")
	}
	if bundle.Dynamic == nil {
		t.Fatal("Dynamic = nil, want non-nil")
	}
	if bundle.Discovery == nil {
		t.Fatal("Discovery = nil, want non-nil")
	}
	if bundle.Mapper == nil {
		t.Fatal("Mapper = nil, want non-nil")
	}
	if bundle.Ref.Name != "test-ctx" {
		t.Fatalf("Ref.Name = %q, want test-ctx", bundle.Ref.Name)
	}
	if bundle.Namespace != "test-ns" {
		t.Fatalf("Namespace = %q, want test-ns", bundle.Namespace)
	}
}

func TestLoadReturnsErrorForEmptyKubeconfig(t *testing.T) {
	path := writeKubeconfig(t, "")
	t.Setenv("KUBECONFIG", path)

	_, err := LoadContext(api.ContextRef{}, Options{})
	if err == nil {
		t.Fatal("Load returned nil error for empty kubeconfig")
	}
}

func TestLoadReturnsErrorForInvalidKubeconfig(t *testing.T) {
	path := writeKubeconfig(t, "not: [valid: yaml")
	t.Setenv("KUBECONFIG", path)

	_, err := LoadContext(api.ContextRef{}, Options{})
	if err == nil {
		t.Fatal("Load returned nil error for invalid kubeconfig")
	}
}

const twoContextKubeconfig = `apiVersion: v1
kind: Config
current-context: beta
clusters:
- name: c1
  cluster:
    server: https://127.0.0.1:6443
    insecure-skip-tls-verify: true
contexts:
- name: beta
  context:
    cluster: c1
    namespace: beta-ns
    user: u1
- name: alpha
  context:
    cluster: c1
    namespace: alpha-ns
    user: u1
users:
- name: u1
  user:
    token: t
`

const otherKubeconfig = `apiVersion: v1
kind: Config
current-context: gamma
clusters:
- name: c2
  cluster:
    server: https://127.0.0.2:6443
    insecure-skip-tls-verify: true
contexts:
- name: gamma
  context:
    cluster: c2
    user: u2
users:
- name: u2
  user:
    token: t
`

func TestReadListsEveryContextSorted(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))

	contexts, err := Read("")
	if err != nil {
		t.Fatalf("Read: %v", err)
	}

	if len(contexts) != 2 || contexts[0].Name != "alpha" || contexts[1].Name != "beta" {
		t.Fatalf("contexts = %v, want them sorted", contexts)
	}
	if contexts[0].Cluster != "c1" {
		t.Fatalf("cluster = %q, want the cluster the context points at", contexts[0].Cluster)
	}
	if contexts[0].Namespace != "alpha-ns" {
		t.Fatalf("namespace = %q", contexts[0].Namespace)
	}
}

func TestReadReportsAnUnreadableKubeconfig(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, "not: [valid: yaml"))

	_, err := Read("")

	if err == nil {
		t.Fatal("an unreadable kubeconfig was reported as having no contexts")
	}
}

func TestReadNamesAFileThatIsNotThere(t *testing.T) {
	_, err := Read(filepath.Join(t.TempDir(), "absent"))

	if err == nil {
		t.Fatal("a kubeconfig that does not exist read as an empty one")
	}
	if !strings.Contains(err.Error(), "absent") {
		t.Fatalf("error = %v, want the path in it", err)
	}
}

func TestReadingOneFileIgnoresTheDefaultKubeconfig(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))
	other := writeKubeconfig(t, otherKubeconfig)

	contexts, err := Read(other)
	if err != nil {
		t.Fatalf("Read: %v", err)
	}

	if len(contexts) != 1 || contexts[0].Name != "gamma" {
		t.Fatalf("contexts = %v; a named kubeconfig must be read on its own, never merged with the default", contexts)
	}
}

func TestLoadContextTargetsTheNamedContext(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))

	bundle, err := LoadContext(api.ContextRef{Name: "alpha"}, Options{})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Name != "alpha" {
		t.Fatalf("Ref.Name = %q, want the requested one rather than the kubeconfig default", bundle.Ref.Name)
	}
	if bundle.Namespace != "alpha-ns" {
		t.Fatalf("Namespace = %q, want the namespace of the requested context", bundle.Namespace)
	}
}

func TestLoadContextReadsTheKubeconfigOnTheRef(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))
	other := writeKubeconfig(t, otherKubeconfig)

	bundle, err := LoadContext(api.ContextRef{Kubeconfig: other, Name: "gamma"}, Options{})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Kubeconfig != other {
		t.Fatalf("Ref.Kubeconfig = %q, want the file the context came from", bundle.Ref.Kubeconfig)
	}
	if bundle.Config.Host != "https://127.0.0.2:6443" {
		t.Fatalf("host = %q, want the server of the named kubeconfig", bundle.Config.Host)
	}
}

func TestARefWithNoKubeconfigFallsBackToTheOneSpinozaWasStartedWith(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))
	other := writeKubeconfig(t, otherKubeconfig)

	bundle, err := LoadContext(api.ContextRef{}, Options{Kubeconfig: other})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Name != "gamma" {
		t.Fatalf("Ref.Name = %q, want the current context of the --kubeconfig file", bundle.Ref.Name)
	}
}

func TestTheFileSpinozaWasStartedWithIsNamedOnTheRef(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))
	other := writeKubeconfig(t, otherKubeconfig)

	bundle, err := LoadContext(api.ContextRef{}, Options{Kubeconfig: other})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Kubeconfig != other {
		t.Fatalf("Ref.Kubeconfig = %q, want the file spinoza was started with", bundle.Ref.Kubeconfig)
	}
}

func TestAFileOnTheRefIsNotReplacedByTheOneSpinozaWasStartedWith(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, validKubeconfig))
	started := writeKubeconfig(t, twoContextKubeconfig)
	other := writeKubeconfig(t, otherKubeconfig)

	bundle, err := LoadContext(
		api.ContextRef{Kubeconfig: other, Name: "gamma"},
		Options{Kubeconfig: started},
	)
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Kubeconfig != other {
		t.Fatalf("Ref.Kubeconfig = %q, want the file the context came from", bundle.Ref.Kubeconfig)
	}
}

func TestAStartWithNoFileNamedLeavesTheRefWithoutOne(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))

	bundle, err := LoadContext(api.ContextRef{}, Options{})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Kubeconfig != "" {
		t.Fatalf("Ref.Kubeconfig = %q, want the usual lookup rules left alone", bundle.Ref.Kubeconfig)
	}
}

func TestLoadContextRejectsAContextThatIsNotThere(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))

	_, err := LoadContext(api.ContextRef{Name: "gone"}, Options{})

	if err == nil {
		t.Fatal("switching to a context that does not exist reported success")
	}
}

func TestLoadContextWithNoNameKeepsTheDefault(t *testing.T) {
	t.Setenv("KUBECONFIG", writeKubeconfig(t, twoContextKubeconfig))

	bundle, err := LoadContext(api.ContextRef{}, Options{})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Ref.Name != "beta" {
		t.Fatalf("Ref.Name = %q, want the kubeconfig's current-context", bundle.Ref.Name)
	}
}

func TestLoadContextRaisesTheClientRateLimit(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)
	t.Setenv("KUBECONFIG", path)

	bundle, err := LoadContext(api.ContextRef{}, Options{})
	if err != nil {
		t.Fatalf("load: %v", err)
	}

	if bundle.Config.QPS < 50 {
		t.Fatalf("QPS = %v; client-go defaults to 5, which throttles a fan-out of 150 lists to 30s", bundle.Config.QPS)
	}
	if bundle.Config.Burst < 100 {
		t.Fatalf("Burst = %d, want headroom for the catalog fan-out", bundle.Config.Burst)
	}
}

func TestLoadContextKeepsConfiguredClientRateLimits(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)

	bundle, err := LoadContext(api.ContextRef{}, Options{Kubeconfig: path, QPS: 17, Burst: 29})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	if bundle.Config.QPS != 17 {
		t.Fatalf("QPS = %v, want 17", bundle.Config.QPS)
	}
	if bundle.Config.Burst != 29 {
		t.Fatalf("Burst = %d, want 29", bundle.Config.Burst)
	}
}

func TestLoadContextRecordsTheToolKubeconfigAsTheFallbackSource(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)
	t.Setenv("KUBECONFIG", path)

	bundle, err := LoadContext(api.ContextRef{}, Options{ToolKubeconfig: "/runtime/spinoza/in-cluster.kubeconfig"})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}

	want := "/runtime/spinoza/in-cluster.kubeconfig"
	if bundle.Ref.Kubeconfig != want {
		t.Fatalf("Ref.Kubeconfig = %q, want %q", bundle.Ref.Kubeconfig, want)
	}
}

func TestLoadContextWiresImpersonationIntoItsTransport(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)
	bundle, err := LoadContext(api.ContextRef{}, Options{Kubeconfig: path, Impersonate: true})
	if err != nil {
		t.Fatalf("LoadContext: %v", err)
	}
	recorder := &recordingTripper{}
	wrapped := bundle.Config.WrapTransport(recorder)
	ctx := auth.WithIdentity(t.Context(), auth.Identity{User: "[email protected]", Groups: []string{"platform"}})
	req := httptest.NewRequest(http.MethodGet, "https://apiserver/api/v1/pods", http.NoBody).WithContext(ctx)

	if _, err := wrapped.RoundTrip(req); err != nil {
		t.Fatalf("round trip: %v", err)
	}
	if recorder.seen.Header.Get(transport.ImpersonateUserHeader) != "[email protected]" {
		t.Fatalf("user header = %q", recorder.seen.Header.Get(transport.ImpersonateUserHeader))
	}
	if got := recorder.seen.Header.Values(transport.ImpersonateGroupHeader); len(got) != 1 || got[0] != "platform" {
		t.Fatalf("group headers = %v, want platform", got)
	}
}

func TestBoundedDiscoveryRejectsAMalformedServer(t *testing.T) {
	config := &rest.Config{Host: "://not-a-server"}

	_, err := boundedDiscovery(config)

	if err == nil {
		t.Fatal("a malformed discovery endpoint reported success")
	}
	if !strings.Contains(err.Error(), "kube discovery") {
		t.Fatalf("error = %q, want discovery named", err.Error())
	}
	if config.Timeout != 0 {
		t.Fatalf("input timeout = %v, want the caller's config left unchanged", config.Timeout)
	}
}

func TestLabelNamesTheFileTheContextsCameFrom(t *testing.T) {
	path := writeKubeconfig(t, validKubeconfig)

	if Label(path) != path {
		t.Fatalf("Label = %q, want the path itself", Label(path))
	}
}

func TestLabelOfTheDefaultNamesEveryFileItReads(t *testing.T) {
	first := writeKubeconfig(t, validKubeconfig)
	second := writeKubeconfig(t, otherKubeconfig)
	t.Setenv("KUBECONFIG", first+string(os.PathListSeparator)+second)

	label := Label("")

	if !strings.Contains(label, first) || !strings.Contains(label, second) {
		t.Fatalf("label = %q, want both files KUBECONFIG points at", label)
	}
}