From d418b5a318c11bbd245297b765e2d087f1b60ee8 Mon Sep 17 00:00:00 2001 From: mattliu007 Date: Tue, 11 Aug 2026 15:58:16 +0800 Subject: [PATCH] fix: #1424 --- .../providers/k8s-secret/k8s_secret.go | 14 ++++++- .../k8s-secret/k8s_secret_client_test.go | 41 +++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 pkg/core/deployer/providers/k8s-secret/k8s_secret_client_test.go diff --git a/pkg/core/deployer/providers/k8s-secret/k8s_secret.go b/pkg/core/deployer/providers/k8s-secret/k8s_secret.go index 88ad66255..e9840b25c 100644 --- a/pkg/core/deployer/providers/k8s-secret/k8s_secret.go +++ b/pkg/core/deployer/providers/k8s-secret/k8s_secret.go @@ -224,14 +224,24 @@ func createK8sClient(kubeConfig string) (*rest.RESTClient, error) { return nil, err } - // InClusterConfig does not set GroupVersion or NegotiatedSerializer, - // but both are required by rest.RESTClientFor. + // InClusterConfig does not set GroupVersion, NegotiatedSerializer or APIPath, + // but all of them are required by rest.RESTClientFor. + // Note that an empty APIPath makes every request target "/v1/namespaces/..." + // instead of "/api/v1/namespaces/...", which the API server rejects with + // 404 "the server could not find the requested resource". if config.GroupVersion == nil { config.GroupVersion = &schema.GroupVersion{Group: "", Version: "v1"} } if config.NegotiatedSerializer == nil { config.NegotiatedSerializer = scheme.Codecs.WithoutConversion() } + if config.APIPath == "" { + if config.GroupVersion.Group == "" { + config.APIPath = "/api" + } else { + config.APIPath = "/apis" + } + } client, err := rest.RESTClientFor(config) if err != nil { diff --git a/pkg/core/deployer/providers/k8s-secret/k8s_secret_client_test.go b/pkg/core/deployer/providers/k8s-secret/k8s_secret_client_test.go new file mode 100644 index 000000000..df04a2f1f --- /dev/null +++ b/pkg/core/deployer/providers/k8s-secret/k8s_secret_client_test.go @@ -0,0 +1,41 @@ +package k8ssecret + +import ( + "strings" + "testing" +) + +const testKubeConfig = `apiVersion: v1 +kind: Config +clusters: + - name: test + cluster: + server: https://127.0.0.1:6443 + insecure-skip-tls-verify: true +contexts: + - name: test + context: + cluster: test + user: test +current-context: test +users: + - name: test + user: + token: test-token +` + +// Secrets live in the core API group, which is served under "/api". +// If APIPath is left empty, rest.RESTClientFor builds requests against +// "/v1/namespaces/..." and the API server answers 404 +// ("the server could not find the requested resource"). +func TestCreateK8sClientSetsCoreAPIPath(t *testing.T) { + client, err := createK8sClient(testKubeConfig) + if err != nil { + t.Fatalf("createK8sClient() returned an unexpected error: %v", err) + } + + const want = "/api/v1" + if got := client.Get().URL().Path; !strings.HasPrefix(got, want) { + t.Errorf("request path = %q, want it to start with %q", got, want) + } +}