mirror of
https://github.com/skoelle/kctl-tui.git
synced 2026-09-17 20:10:24 +00:00
fix: use correct ExternalSecret CRD name for force-sync annotation
The force-sync pre-filled the ExternalSecret object name with the Kubernetes secret name (e.g. job-apply-common-secrets), but the kubectl annotate command must target the ExternalSecret CRD object (e.g. job-apply). Added external_secret_name_template config option with fallback to k8s_secret_name_template.
This commit is contained in:
@@ -49,8 +49,9 @@ type panelModel struct {
|
|||||||
|
|
||||||
deploymentName string
|
deploymentName string
|
||||||
|
|
||||||
awsSecretName string // resolved via secret_name_template (namespace + env)
|
awsSecretName string // resolved via secret_name_template (namespace + env)
|
||||||
k8sSecretName string // resolved via k8s_secret_name_template (namespace only)
|
k8sSecretName string // resolved via k8s_secret_name_template (namespace only)
|
||||||
|
externalSecretName string // resolved via external_secret_name_template (namespace only)
|
||||||
awsValues map[string]string
|
awsValues map[string]string
|
||||||
k8sValues map[string]string
|
k8sValues map[string]string
|
||||||
diffEntries []kctl.SecretDiffEntry
|
diffEntries []kctl.SecretDiffEntry
|
||||||
@@ -316,6 +317,7 @@ func (m *panelModel) afterAWSLogin(execErr error) (tea.Model, tea.Cmd) {
|
|||||||
func (m *panelModel) startSecretsFlow() (tea.Model, tea.Cmd) {
|
func (m *panelModel) startSecretsFlow() (tea.Model, tea.Cmd) {
|
||||||
m.awsSecretName = m.cfg.ResolveSecretName(m.ns, m.currentEnv)
|
m.awsSecretName = m.cfg.ResolveSecretName(m.ns, m.currentEnv)
|
||||||
m.k8sSecretName = m.cfg.ResolveK8sSecretName(m.ns)
|
m.k8sSecretName = m.cfg.ResolveK8sSecretName(m.ns)
|
||||||
|
m.externalSecretName = m.cfg.ResolveExternalSecretName(m.ns)
|
||||||
|
|
||||||
raw, err := kubeexec.GetAWSSecretString(m.awsSecretName, m.cfg.AWSRegion)
|
raw, err := kubeexec.GetAWSSecretString(m.awsSecretName, m.cfg.AWSRegion)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -462,7 +464,7 @@ func (m *panelModel) fromForceSyncConfirm() (tea.Model, tea.Cmd) {
|
|||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
m.step = stepExternalSecretName
|
m.step = stepExternalSecretName
|
||||||
m.input.SetValue(m.k8sSecretName)
|
m.input.SetValue(m.externalSecretName)
|
||||||
m.input.Placeholder = "ExternalSecret object name"
|
m.input.Placeholder = "ExternalSecret object name"
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,14 @@ secret_name_template: "tf-{namespace}-{env}-secrets"
|
|||||||
# Available placeholders: {namespace}.
|
# Available placeholders: {namespace}.
|
||||||
k8s_secret_name_template: "{namespace}-common-secrets"
|
k8s_secret_name_template: "{namespace}-common-secrets"
|
||||||
|
|
||||||
|
# Builds the ExternalSecret CRD object name to annotate when a force-sync
|
||||||
|
# is requested. This is often different from the Kubernetes secret name
|
||||||
|
# because the ExternalSecret CRD and the resulting Secret are separate
|
||||||
|
# objects (e.g. ExternalSecret "job-apply" produces Secret
|
||||||
|
# "job-apply-common-secrets"). Falls back to k8s_secret_name_template
|
||||||
|
# if omitted. Available placeholders: {namespace}.
|
||||||
|
external_secret_name_template: "{namespace}"
|
||||||
|
|
||||||
# Builds the actual kubectl context name/ARN from region, account ID, env,
|
# Builds the actual kubectl context name/ARN from region, account ID, env,
|
||||||
# and context. Available placeholders: {region}, {account_id}, {env},
|
# and context. Available placeholders: {region}, {account_id}, {env},
|
||||||
# {context}. Adjust the literal parts ("tf-", "-1", cluster naming, ARN
|
# {context}. Adjust the literal parts ("tf-", "-1", cluster naming, ARN
|
||||||
|
|||||||
@@ -50,6 +50,13 @@ type Config struct {
|
|||||||
// naming conventions.
|
// naming conventions.
|
||||||
K8sSecretNameTemplate string `yaml:"k8s_secret_name_template"`
|
K8sSecretNameTemplate string `yaml:"k8s_secret_name_template"`
|
||||||
|
|
||||||
|
// ExternalSecretNameTemplate builds the ExternalSecret CRD object
|
||||||
|
// name that should be annotated when a force-sync is requested. This
|
||||||
|
// is often different from the Kubernetes secret name because the
|
||||||
|
// ExternalSecret CRD and the resulting Secret are separate objects.
|
||||||
|
// Falls back to K8sSecretNameTemplate if empty.
|
||||||
|
ExternalSecretNameTemplate string `yaml:"external_secret_name_template"`
|
||||||
|
|
||||||
// ContextTemplate builds the actual kubectl context name/ARN from
|
// ContextTemplate builds the actual kubectl context name/ARN from
|
||||||
// region, account_id, env, and context, e.g.
|
// region, account_id, env, and context, e.g.
|
||||||
// "arn:aws:eks:{region}:{account_id}:cluster/tf-{env}-{context}-1".
|
// "arn:aws:eks:{region}:{account_id}:cluster/tf-{env}-{context}-1".
|
||||||
@@ -134,6 +141,23 @@ func (c Config) ResolveK8sSecretName(namespace string) string {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResolveExternalSecretName builds the ExternalSecret CRD object name for
|
||||||
|
// a given namespace using ExternalSecretNameTemplate. Falls back to
|
||||||
|
// K8sSecretNameTemplate (or SecretNameTemplate if that is also empty) so
|
||||||
|
// that existing configs keep working without changes.
|
||||||
|
func (c Config) ResolveExternalSecretName(namespace string) string {
|
||||||
|
template := c.ExternalSecretNameTemplate
|
||||||
|
if template == "" {
|
||||||
|
template = c.K8sSecretNameTemplate
|
||||||
|
}
|
||||||
|
if template == "" {
|
||||||
|
template = c.SecretNameTemplate
|
||||||
|
}
|
||||||
|
return kctl.ResolveTemplate(template, map[string]string{
|
||||||
|
"namespace": namespace,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// DefaultPath returns the default config file location: ~/.kctl-tui/config.yaml
|
// DefaultPath returns the default config file location: ~/.kctl-tui/config.yaml
|
||||||
func DefaultPath() (string, error) {
|
func DefaultPath() (string, error) {
|
||||||
home, err := os.UserHomeDir()
|
home, err := os.UserHomeDir()
|
||||||
|
|||||||
@@ -112,6 +112,33 @@ func TestResolveK8sSecretName_FallsBackToSecretNameTemplate(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestResolveExternalSecretName_ExplicitTemplate(t *testing.T) {
|
||||||
|
cfg := Config{ExternalSecretNameTemplate: "{namespace}"}
|
||||||
|
got := cfg.ResolveExternalSecretName("job-apply")
|
||||||
|
want := "job-apply"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveExternalSecretName_FallsBackToK8sSecretNameTemplate(t *testing.T) {
|
||||||
|
cfg := Config{K8sSecretNameTemplate: "{namespace}-common-secrets"}
|
||||||
|
got := cfg.ResolveExternalSecretName("job-apply")
|
||||||
|
want := "job-apply-common-secrets"
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestResolveExternalSecretName_FallsBackToSecretNameTemplate(t *testing.T) {
|
||||||
|
cfg := Config{SecretNameTemplate: "tf-{namespace}-{env}-secrets"}
|
||||||
|
got := cfg.ResolveExternalSecretName("job-apply")
|
||||||
|
want := "tf-job-apply-{env}-secrets" // {env} intentionally left unresolved
|
||||||
|
if got != want {
|
||||||
|
t.Fatalf("got %q, want %q", got, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLoginCommand_DefaultsWhenUnset(t *testing.T) {
|
func TestLoginCommand_DefaultsWhenUnset(t *testing.T) {
|
||||||
cfg := Config{}
|
cfg := Config{}
|
||||||
if got := cfg.LoginCommand(); got != DefaultAWSSSOLoginCommand {
|
if got := cfg.LoginCommand(); got != DefaultAWSSSOLoginCommand {
|
||||||
|
|||||||
Reference in New Issue
Block a user