Unverified Commit ed722d55 authored by Lemon-miaow's avatar Lemon-miaow
Browse files

fix(setup): the workload-ns SMTP mirror must carry the target namespace

'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.
parent 311b1a7e
Loading
Loading
Loading
Loading
+12 −8
Changes for cmd/felis/tui_smtp.go: 12 added lines, 8 removed lines.
Original line number Diff line number Diff line
@@ -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
	}
+37 −0
Changes for cmd/felis/tui_smtp_test.go: 37 added lines, 0 removed lines.
Original line number Diff line number Diff line
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)
		}
	}
}