package controller import ( "context" "fmt" corev1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" bindv1alpha1 "git.unkin.net/unkin/bind-operator/api/v1alpha1" "git.unkin.net/unkin/bind-operator/internal/bind" ) // BindTSIGKeyReconciler generates TSIG key material into a Secret. type BindTSIGKeyReconciler struct { client.Client Scheme *runtime.Scheme } // +kubebuilder:rbac:groups=bind.unkin.net,resources=bindtsigkeys,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=bind.unkin.net,resources=bindtsigkeys/status,verbs=get;update;patch // +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch;create;update;patch;delete func (r *BindTSIGKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := log.FromContext(ctx) var key bindv1alpha1.BindTSIGKey if err := r.Get(ctx, req.NamespacedName, &key); err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } algorithm := string(key.Spec.Algorithm) if algorithm == "" { algorithm = string(bindv1alpha1.TSIGHMACSHA256) } keyName := key.Spec.KeyName if keyName == "" { keyName = key.Name } secretName := key.Spec.SecretName if secretName == "" { secretName = key.Name + "-tsig" } var secret corev1.Secret err := r.Get(ctx, types.NamespacedName{Namespace: key.Namespace, Name: secretName}, &secret) switch { case apierrors.IsNotFound(err): if key.Spec.ImportExisting { return r.fail(ctx, &key, "SecretMissing", fmt.Sprintf("import secret %s not found", secretName)) } material, genErr := bind.GenerateSecret(bind.SecretBytesForAlgorithm(algorithm)) if genErr != nil { return ctrl.Result{}, genErr } newSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{Name: secretName, Namespace: key.Namespace, Labels: map[string]string{managedByLabel: managedByValue}}, Data: map[string][]byte{ "algorithm": []byte(algorithm), "keyName": []byte(keyName), "secret": []byte(material), "key.conf": []byte(bind.KeyClause(keyName, algorithm, material)), }, } if err := ctrl.SetControllerReference(&key, newSecret, r.Scheme); err != nil { return ctrl.Result{}, err } if err := r.Create(ctx, newSecret); err != nil { return ctrl.Result{}, err } logger.Info("generated TSIG key", "key", key.Name, "secret", secretName) case err != nil: return ctrl.Result{}, err } key.Status.SecretName = secretName key.Status.KeyName = keyName key.Status.Ready = true key.Status.ObservedGeneration = key.Generation setReady(&key.Status.Conditions, key.Generation, true, "KeyReady", "TSIG key material present") if err := r.Status().Update(ctx, &key); err != nil { return ctrl.Result{}, err } return ctrl.Result{}, nil } 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 setReady(&key.Status.Conditions, key.Generation, false, reason, msg) if err := r.Status().Update(ctx, key); err != nil { return ctrl.Result{}, err } return ctrl.Result{RequeueAfter: requeueShort}, nil } func (r *BindTSIGKeyReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&bindv1alpha1.BindTSIGKey{}). Owns(&corev1.Secret{}). Complete(r) }