From 2c5fc2eb17739c681a80347569401a4f2447caba Mon Sep 17 00:00:00 2001 From: Vladyslav Yurchenko Date: Fri, 3 Apr 2026 01:18:55 +0300 Subject: [PATCH] PT-2189 - Fix cluster selection --- .../pt-k8s-debug-collector/dumper/dumper.go | 32 ++++++- .../dumper/dumper_test.go | 92 +++++++++++++++++++ .../dumper/resources.go | 14 +++ src/go/pt-k8s-debug-collector/main.go | 6 +- 4 files changed, 135 insertions(+), 9 deletions(-) diff --git a/src/go/pt-k8s-debug-collector/dumper/dumper.go b/src/go/pt-k8s-debug-collector/dumper/dumper.go index 3303e9fbe..8c95cdd9f 100644 --- a/src/go/pt-k8s-debug-collector/dumper/dumper.go +++ b/src/go/pt-k8s-debug-collector/dumper/dumper.go @@ -84,14 +84,17 @@ type exportJob struct { } // New return new Dumper object -func New(location, namespace, kubeconfig, forwardport, resource string, skipPodSummary bool, concurrentExportWorkers int) (*Dumper, error) { - var config *rest.Config - +func New(location, namespace, kubeconfig, clusterName, forwardport, resource string, skipPodSummary bool, concurrentExportWorkers int) (*Dumper, error) { safeLog := NewSafeLogger() log.AddHook(&ErrorArchiveHook{safeLogger: safeLog}) - config, err := clientcmd.BuildConfigFromFlags("", kubeconfig) + _, resourceClusterName := parseResourceSpec(resource) + if clusterName == "" { + clusterName = resourceClusterName + } + + config, err := buildRestConfig(kubeconfig, clusterName) if err != nil { return nil, fmt.Errorf("failed to build config from flags: %w", err) } @@ -181,6 +184,27 @@ func New(location, namespace, kubeconfig, forwardport, resource string, skipPodS return d, err } +func buildRestConfig(kubeconfig, clusterName string) (*rest.Config, error) { + if clusterName == "" { + return clientcmd.BuildConfigFromFlags("", kubeconfig) + } + + loadingRules := &clientcmd.ClientConfigLoadingRules{ExplicitPath: kubeconfig} + overrides := &clientcmd.ConfigOverrides{CurrentContext: clusterName} + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) + + rawConfig, err := clientConfig.RawConfig() + if err != nil { + return nil, err + } + + if _, ok := rawConfig.Contexts[clusterName]; !ok { + return nil, fmt.Errorf("context %q not found in kubeconfig", clusterName) + } + + return clientConfig.ClientConfig() +} + // DumpCluster create dump of a cluster in Dumper.location func (d *Dumper) DumpCluster() error { var err error diff --git a/src/go/pt-k8s-debug-collector/dumper/dumper_test.go b/src/go/pt-k8s-debug-collector/dumper/dumper_test.go index 49c3abcaa..f29fb3b8f 100644 --- a/src/go/pt-k8s-debug-collector/dumper/dumper_test.go +++ b/src/go/pt-k8s-debug-collector/dumper/dumper_test.go @@ -15,6 +15,7 @@ package dumper import ( "context" + "os" "testing" corev1 "k8s.io/api/core/v1" @@ -186,6 +187,97 @@ func TestResourceTypeParsing(t *testing.T) { } } +func TestParseResourceSpec(t *testing.T) { + tests := []struct { + name string + resource string + wantResource string + wantClusterName string + }{ + { + name: "plain resource", + resource: "auto", + wantResource: "auto", + wantClusterName: "", + }, + { + name: "resource with cluster suffix", + resource: "auto/k3d-pgv2", + wantResource: "auto", + wantClusterName: "k3d-pgv2", + }, + { + name: "resource is trimmed", + resource: " psmdb/k3d-psmdb ", + wantResource: "psmdb", + wantClusterName: "k3d-psmdb", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotResource, gotClusterName := parseResourceSpec(tt.resource) + if gotResource != tt.wantResource || gotClusterName != tt.wantClusterName { + t.Fatalf("parseResourceSpec(%q) = (%q, %q), want (%q, %q)", tt.resource, gotResource, gotClusterName, tt.wantResource, tt.wantClusterName) + } + }) + } +} + +func TestBuildRestConfigUsesSelectedContext(t *testing.T) { + tmpFile, err := os.CreateTemp("", "kubeconfig-*.yaml") + if err != nil { + t.Fatalf("create temp kubeconfig: %v", err) + } + defer os.Remove(tmpFile.Name()) + + kubeconfig := ` +apiVersion: v1 +kind: Config +clusters: +- cluster: + server: https://default.example + name: default-cluster +- cluster: + server: https://pgv2.example + name: pgv2-cluster +contexts: +- context: + cluster: default-cluster + user: default-user + name: default +- context: + cluster: pgv2-cluster + user: pgv2-user + name: k3d-pgv2 +current-context: default +users: +- name: default-user + user: + token: default-token +- name: pgv2-user + user: + token: pgv2-token +` + + if _, err := tmpFile.WriteString(kubeconfig); err != nil { + t.Fatalf("write temp kubeconfig: %v", err) + } + + if err := tmpFile.Close(); err != nil { + t.Fatalf("close temp kubeconfig: %v", err) + } + + cfg, err := buildRestConfig(tmpFile.Name(), "k3d-pgv2") + if err != nil { + t.Fatalf("buildRestConfig returned error: %v", err) + } + + if cfg.Host != "https://pgv2.example" { + t.Fatalf("buildRestConfig selected host %q, want %q", cfg.Host, "https://pgv2.example") + } +} + // Tests for individual_files.go func TestGetSummarySkipPodSummary(t *testing.T) { diff --git a/src/go/pt-k8s-debug-collector/dumper/resources.go b/src/go/pt-k8s-debug-collector/dumper/resources.go index 04d939d7e..05e79bd04 100644 --- a/src/go/pt-k8s-debug-collector/dumper/resources.go +++ b/src/go/pt-k8s-debug-collector/dumper/resources.go @@ -100,6 +100,20 @@ func (d *Dumper) autoCustomResource() ([]string, error) { return result, nil } +func parseResourceSpec(resource string) (string, string) { + resource = strings.TrimSpace(resource) + if resource == "" { + return "", "" + } + + resourceName, clusterName, found := strings.Cut(resource, "/") + if !found { + return resourceName, "" + } + + return resourceName, clusterName +} + // TODO: rewrite to use map or switch func resourceType(s string) string { if s == "auto" { diff --git a/src/go/pt-k8s-debug-collector/main.go b/src/go/pt-k8s-debug-collector/main.go index f31ccd5d6..90fc4fd5d 100644 --- a/src/go/pt-k8s-debug-collector/main.go +++ b/src/go/pt-k8s-debug-collector/main.go @@ -69,10 +69,6 @@ func (c *cliOptions) AfterApply() error { } } - if len(c.ClusterName) > 0 { - c.Resource += "/" + c.ClusterName - } - ll, err := log.ParseLevel(c.LogLevel) if err != nil { return err @@ -111,7 +107,7 @@ func main() { log.Infof("loaded default kubeconfig: %s", path) } - d, err := dumper.New("cluster-dump", opts.Namespace, opts.Kubeconfig, opts.ForwardPort, opts.Resource, opts.SkipPodSummary, opts.ConcurrentExportWorkers) + d, err := dumper.New("cluster-dump", opts.Namespace, opts.Kubeconfig, opts.ClusterName, opts.ForwardPort, opts.Resource, opts.SkipPodSummary, opts.ConcurrentExportWorkers) if err != nil { log.Error(err) os.Exit(1)