From fbd69ab33432234cc99e7911991ffbebcd102cd4 Mon Sep 17 00:00:00 2001 From: Dmitry Ilyevsky Date: Thu, 4 Jun 2026 00:31:07 -0700 Subject: [PATCH] [cmd/k8s] send install namespace to the onboarding manifest endpoint apoxy k8s install rewrites namespaced objects to --namespace client-side, but the cosmos-generated manifest hardcoded "apoxy" in the ClusterRoleBinding subject and the controller's runtime config, which the client rewrite can't touch. Installing into a non-"apoxy" namespace left the ServiceAccount unbound and the controller operating in "apoxy" for its secrets and leader-election lease, crashlooping on forbidden errors. cosmos now accepts a namespace query param on /v1/onboarding/k8s.yaml and stamps it into every namespaced object, the CRB subject, and the config. Pass the --namespace flag through so non-default install namespaces are honored server-side. Empty/default ("apoxy") is unchanged. --- pkg/cmd/k8s.go | 17 +++++++++++++---- pkg/cmd/k8s_test.go | 14 +++++++++++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/pkg/cmd/k8s.go b/pkg/cmd/k8s.go index b7eb393b..f1b8238a 100644 --- a/pkg/cmd/k8s.go +++ b/pkg/cmd/k8s.go @@ -48,7 +48,7 @@ var ( encoder = runtimejson.NewYAMLSerializer(runtimejson.DefaultMetaFactory, scheme.Scheme, scheme.Scheme) ) -func onboardingPath(clusterName, mirror, image, version string) string { +func onboardingPath(clusterName, mirror, image, version, namespace string) string { path := "/v1/onboarding/k8s.yaml" params := url.Values{} if clusterName != "" { @@ -63,19 +63,28 @@ func onboardingPath(clusterName, mirror, image, version string) string { if version != "" { params.Set("version", version) } + if namespace != "" { + // Tell cosmos which namespace the controller is installed into so the + // generated manifest stamps it into every namespaced object, the + // ClusterRoleBinding subject, and the controller's runtime config. + // Without this the server defaults to "apoxy" and a non-default + // install namespace leaves the SA unbound and the controller pointed + // at the wrong namespace. + params.Set("namespace", namespace) + } if len(params) == 0 { return path } return path + "?" + params.Encode() } -func getYAML(clusterName, mirror, image, version string) ([]byte, error) { +func getYAML(clusterName, mirror, image, version, namespace string) ([]byte, error) { c, err := config.DefaultAPIClient() if err != nil { return nil, err } - resp, err := c.SendRequest(http.MethodGet, onboardingPath(clusterName, mirror, image, version), nil) + resp, err := c.SendRequest(http.MethodGet, onboardingPath(clusterName, mirror, image, version, namespace), nil) if err != nil { return nil, err } @@ -1113,7 +1122,7 @@ will automatically connect to the Apoxy API and begin managing your in-cluster A } } - yamlz, err := getYAML(clusterName, mirror, image, version) + yamlz, err := getYAML(clusterName, mirror, image, version, namespace) if err != nil { return fmt.Errorf("failed to get YAML: %w", err) } diff --git a/pkg/cmd/k8s_test.go b/pkg/cmd/k8s_test.go index 0ac8d708..3c5594c0 100644 --- a/pkg/cmd/k8s_test.go +++ b/pkg/cmd/k8s_test.go @@ -7,7 +7,7 @@ import ( ) func TestOnboardingPath(t *testing.T) { - got := onboardingPath("silent-hill", "gateway", "docker.io/apoxy/apoxy:v0.1.6-dev-849f400", "") + got := onboardingPath("silent-hill", "gateway", "docker.io/apoxy/apoxy:v0.1.6-dev-849f400", "", "") want := "/v1/onboarding/k8s.yaml?cluster_name=silent-hill&image=docker.io%2Fapoxy%2Fapoxy%3Av0.1.6-dev-849f400&mirror=gateway" if got != want { t.Fatalf("onboardingPath() = %q, want %q", got, want) @@ -15,15 +15,23 @@ func TestOnboardingPath(t *testing.T) { } func TestOnboardingPathWithVersion(t *testing.T) { - got := onboardingPath("silent-hill", "gateway", "", "v0.3.0") + got := onboardingPath("silent-hill", "gateway", "", "v0.3.0", "") want := "/v1/onboarding/k8s.yaml?cluster_name=silent-hill&mirror=gateway&version=v0.3.0" if got != want { t.Fatalf("onboardingPath() = %q, want %q", got, want) } } +func TestOnboardingPathWithNamespace(t *testing.T) { + got := onboardingPath("silent-hill", "gateway", "", "", "platform") + want := "/v1/onboarding/k8s.yaml?cluster_name=silent-hill&mirror=gateway&namespace=platform" + if got != want { + t.Fatalf("onboardingPath() = %q, want %q", got, want) + } +} + func TestOnboardingPathWithoutParams(t *testing.T) { - got := onboardingPath("", "", "", "") + got := onboardingPath("", "", "", "", "") want := "/v1/onboarding/k8s.yaml" if got != want { t.Fatalf("onboardingPath() = %q, want %q", got, want)