From ed722d55f8980f548c12bd16871f05ef7b9fdd21 Mon Sep 17 00:00:00 2001 From: Lemon-miaow Date: Wed, 23 Sep 2026 04:42:59 +0800 Subject: [PATCH] fix(setup): the workload-ns SMTP mirror must carry the target namespace MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'smtpSecretManifest' hardcoded namespace=felis, so the 'configure email' refresh of the minecraft-namespace copies failed before it began: kubectl refuses a manifest whose namespace conflicts with -n (found live: 'the namespace from the provided object "felis" does not match the namespace "minecraft"'), and the felis-config mirror never ran at all because the smtp apply returned early. A later SMTP change could therefore never reach the reaper's pre-reap warnings — the exact failure the refresh was added to close. Render the Secret with the caller's namespace (felis for the control-plane apply, the workload namespace for the mirror). Regression test pins both. --- cmd/felis/tui_smtp.go | 20 ++++++++++++-------- cmd/felis/tui_smtp_test.go | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 cmd/felis/tui_smtp_test.go diff --git a/cmd/felis/tui_smtp.go b/cmd/felis/tui_smtp.go index 1ad81e5..62b9fca 100644 --- a/cmd/felis/tui_smtp.go +++ b/cmd/felis/tui_smtp.go @@ -351,14 +351,18 @@ func applySMTPConfig(ctx context.Context, in smtpInputs) error { return kubectl(ctx, "-n", "felis", "rollout", "status", "deployment/felis-api", "--timeout=180s") } -// smtpSecretManifest renders the felis-smtp Secret (in the control namespace, -// via the caller's apply) the felis-api Deployment injects the relay password -// from. Rendered in-process and piped to `kubectl apply` — the password is -// never a command-line arg, so it never appears in the host process table. -func smtpSecretManifest(password string) ([]byte, error) { +// smtpSecretManifest renders the felis-smtp Secret for the given namespace, the +// one the receiving Deployment/CronJob resolves its secretKeyRef against (felis +// for felis-api, the workload namespace for the reaper's mirror). The namespace +// must be IN the manifest: kubectl rejects a manifest whose namespace conflicts +// with -n, so leaving the control namespace hardcoded made every workload-ns +// replica fail before it started. Rendered in-process and piped to +// `kubectl apply` — the password is never a command-line arg, so it never +// appears in the host process table. +func smtpSecretManifest(password, namespace string) ([]byte, error) { secret := &corev1.Secret{ TypeMeta: metav1.TypeMeta{APIVersion: "v1", Kind: "Secret"}, - ObjectMeta: metav1.ObjectMeta{Name: platform.SMTPSecretName, Namespace: "felis"}, + ObjectMeta: metav1.ObjectMeta{Name: platform.SMTPSecretName, Namespace: namespace}, Type: corev1.SecretTypeOpaque, StringData: map[string]string{ platform.SMTPSecretPasswordKey: password, @@ -372,7 +376,7 @@ func smtpSecretManifest(password string) ([]byte, error) { } func applySMTPSecret(ctx context.Context, password string) error { - manifest, err := smtpSecretManifest(password) + manifest, err := smtpSecretManifest(password, "felis") if err != nil { return err } @@ -396,7 +400,7 @@ func replicateSMTPToWorkloadNamespace(ctx context.Context, password string) erro if ns == "" || ns == "felis" { return nil } - smtpManifest, err := smtpSecretManifest(password) + smtpManifest, err := smtpSecretManifest(password, ns) if err != nil { return err } diff --git a/cmd/felis/tui_smtp_test.go b/cmd/felis/tui_smtp_test.go new file mode 100644 index 0000000..97c78de --- /dev/null +++ b/cmd/felis/tui_smtp_test.go @@ -0,0 +1,37 @@ +package main + +import ( + "strings" + "testing" + + "sigs.k8s.io/yaml" +) + +// TestSMTPSecretManifestCarriesTargetNamespace pins the fix for the +// workload-namespace replica: kubectl refuses a manifest whose namespace +// conflicts with -n ("the namespace from the provided object ... does not +// match"), so the mirror must render felis-smtp with the TARGET namespace — +// otherwise the "configure email" refresh fails on the first apply and the +// felis-config mirror never runs at all. +func TestSMTPSecretManifestCarriesTargetNamespace(t *testing.T) { + for _, ns := range []string{"felis", "minecraft"} { + b, err := smtpSecretManifest("pw", ns) + if err != nil { + t.Fatalf("render for %s: %v", ns, err) + } + var got struct { + Metadata struct { + Namespace string `json:"namespace"` + } `json:"metadata"` + } + if err := yaml.Unmarshal(b, &got); err != nil { + t.Fatalf("unmarshal for %s: %v", ns, err) + } + if got.Metadata.Namespace != ns { + t.Fatalf("manifest namespace = %q, want %q", got.Metadata.Namespace, ns) + } + if !strings.Contains(string(b), "name: felis-smtp") { + t.Fatalf("manifest must still name felis-smtp: %s", b) + } + } +}