BindTSIGKey: add secretTemplate for labels/annotations on the managed Secret

The operator-generated TSIG Secret previously carried only the managed-by
label, so it could not be mirrored to another namespace by emberstack
reflector (which requires reflection-allowed annotations on the source).

Add spec.secretTemplate.{annotations,labels}, applied both when the Secret
is first generated and reconciled onto the existing Secret when the CR
changes (imported secrets are left untouched so we don't fight their
external manager). This lets the external-dns TSIG key be managed in
bind-internal and reflected into the externaldns namespace.
This commit is contained in:
2026-07-20 23:45:41 +10:00
parent 243e776b59
commit 9c81320df8
9 changed files with 222 additions and 5 deletions
+53 -1
View File
@@ -60,7 +60,7 @@ func (r *BindTSIGKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, genErr
}
newSecret := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: key.Namespace, Labels: map[string]string{managedByLabel: managedByValue}},
ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: key.Namespace},
Data: map[string][]byte{
"algorithm": []byte(algorithm),
"keyName": []byte(keyName),
@@ -68,6 +68,7 @@ func (r *BindTSIGKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request)
"key.conf": []byte(bind.KeyClause(keyName, algorithm, material)),
},
}
applySecretTemplate(&newSecret.ObjectMeta, key.Spec.SecretTemplate)
if err := ctrl.SetControllerReference(&key, newSecret, r.Scheme); err != nil {
return ctrl.Result{}, err
}
@@ -77,6 +78,21 @@ func (r *BindTSIGKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request)
logger.Info("generated TSIG key", "key", key.Name, "secret", secretName)
case err != nil:
return ctrl.Result{}, err
default:
// Secret already exists: reconcile the template-managed metadata so that
// annotation/label changes on the CR (e.g. reflection hints) propagate
// without regenerating key material. Skip imported secrets, which are
// owned by an external manager (Vault/VSO, reflector) that we must not
// fight over metadata.
if key.Spec.ImportExisting {
break
}
if updated := applySecretTemplate(&secret.ObjectMeta, key.Spec.SecretTemplate); updated {
if err := r.Update(ctx, &secret); err != nil {
return ctrl.Result{}, err
}
logger.Info("updated TSIG key secret metadata", "key", key.Name, "secret", secretName)
}
}
key.Status.SecretName = secretName
@@ -90,6 +106,42 @@ func (r *BindTSIGKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return ctrl.Result{}, nil
}
// applySecretTemplate stamps the operator-managed label plus any
// user-supplied labels/annotations onto the Secret's metadata. It returns true
// if it mutated meta, so callers can decide whether an update is needed.
func applySecretTemplate(meta *metav1.ObjectMeta, tmpl *bindv1alpha1.SecretMetadata) bool {
changed := false
setLabel := func(k, v string) {
if meta.Labels == nil {
meta.Labels = map[string]string{}
}
if meta.Labels[k] != v {
meta.Labels[k] = v
changed = true
}
}
setAnnotation := func(k, v string) {
if meta.Annotations == nil {
meta.Annotations = map[string]string{}
}
if meta.Annotations[k] != v {
meta.Annotations[k] = v
changed = true
}
}
setLabel(managedByLabel, managedByValue)
if tmpl != nil {
for k, v := range tmpl.Labels {
setLabel(k, v)
}
for k, v := range tmpl.Annotations {
setAnnotation(k, v)
}
}
return changed
}
func (r *BindTSIGKeyReconciler) fail(ctx context.Context, key *bindv1alpha1.BindTSIGKey, reason, msg string) (ctrl.Result, error) {
key.Status.Ready = false
key.Status.ObservedGeneration = key.Generation