2025-01-29 23:44:09 +02:00
package v1
import (
"context"
"fmt"
"strings"
2026-03-11 14:18:09 +01:00
"github.com/google/uuid"
2025-01-29 23:44:09 +02:00
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
netbirdiov1 "github.com/netbirdio/kubernetes-operator/api/v1"
)
// nolint:unused
// log is for logging in this package.
var nbsetupkeylog = logf . Log . WithName ( "nbsetupkey-resource" )
// SetupNBSetupKeyWebhookWithManager registers the webhook for NBSetupKey in the manager.
func SetupNBSetupKeyWebhookWithManager ( mgr ctrl . Manager ) error {
2026-03-11 14:18:09 +01:00
return ctrl . NewWebhookManagedBy ( mgr , & netbirdiov1 . NBSetupKey {}).
2025-01-29 23:44:09 +02:00
WithValidator ( & NBSetupKeyCustomValidator { client : mgr . GetClient ()}).
Complete ()
}
// NBSetupKeyCustomValidator struct is responsible for validating the NBSetupKey resource
// when it is created, updated, or deleted.
type NBSetupKeyCustomValidator struct {
client client . Client
}
2026-03-11 14:18:09 +01:00
var _ admission . Validator [ * netbirdiov1 . NBSetupKey ] = & NBSetupKeyCustomValidator {}
2025-01-29 23:44:09 +02:00
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type NBSetupKey.
2026-03-11 14:18:09 +01:00
func ( v * NBSetupKeyCustomValidator ) ValidateCreate ( ctx context . Context , nbSetupKey * netbirdiov1 . NBSetupKey ) ( admission . Warnings , error ) {
2025-01-29 23:44:09 +02:00
nbsetupkeylog . Info ( "Validating NBSetupKey" , "namespace" , nbSetupKey . Namespace , "name" , nbSetupKey . Name )
if nbSetupKey . Spec . SecretKeyRef . Name == "" {
return nil , fmt . Errorf ( "spec.secretKeyRef.name is required" )
}
if nbSetupKey . Spec . SecretKeyRef . Key == "" {
return nil , fmt . Errorf ( "spec.secretKeyRef.key is required" )
}
var secret corev1 . Secret
err := v . client . Get ( ctx , types . NamespacedName { Namespace : nbSetupKey . Namespace , Name : nbSetupKey . Spec . SecretKeyRef . Name }, & secret )
if err != nil {
if errors . IsNotFound ( err ) {
return admission . Warnings { fmt . Sprintf ( "secret %s/%s not found" , nbSetupKey . Namespace , nbSetupKey . Spec . SecretKeyRef . Name )}, nil
}
return nil , err
}
uuidBytes , ok := secret . Data [ nbSetupKey . Spec . SecretKeyRef . Key ]
if ! ok {
return admission . Warnings { fmt . Sprintf ( "key %s in secret %s/%s not found" , nbSetupKey . Spec . SecretKeyRef . Key , nbSetupKey . Namespace , nbSetupKey . Spec . SecretKeyRef . Name )}, nil
}
_ , err = uuid . Parse ( string ( uuidBytes ))
if err != nil {
return admission . Warnings { fmt . Sprintf ( "setupkey %s in secret %s/%s is not a valid setup key" , nbSetupKey . Spec . SecretKeyRef . Key , nbSetupKey . Namespace , nbSetupKey . Spec . SecretKeyRef . Name )}, nil
}
return nil , nil
}
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type NBSetupKey.
2026-03-11 14:18:09 +01:00
func ( v * NBSetupKeyCustomValidator ) ValidateUpdate ( ctx context . Context , old , new * netbirdiov1 . NBSetupKey ) ( admission . Warnings , error ) {
return v . ValidateCreate ( ctx , new )
2025-01-29 23:44:09 +02:00
}
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type NBSetupKey.
2026-03-11 14:18:09 +01:00
func ( v * NBSetupKeyCustomValidator ) ValidateDelete ( ctx context . Context , nbSetupKey * netbirdiov1 . NBSetupKey ) ( admission . Warnings , error ) {
2025-01-29 23:44:09 +02:00
nbsetupkeylog . Info ( "Validating NBSetupKey deletion" , "namespace" , nbSetupKey . Namespace , "name" , nbSetupKey . Name )
var pods corev1 . PodList
err := v . client . List ( ctx , & pods , client . InNamespace ( nbSetupKey . Namespace ))
if err != nil {
return nil , err
}
//nolint:prealloc
var invalidPods [] string
for _ , p := range pods . Items {
// If annotation doesn't exist, or doesn't match NBSetupKey being deleted, ignore
if v , ok := p . Annotations [ setupKeyAnnotation ]; ! ok || v != nbSetupKey . Name {
continue
}
invalidPods = append ( invalidPods , p . Name )
}
if len ( invalidPods ) > 0 {
return nil , fmt . Errorf ( "NBSetupKey is in-use by %d pods: %s" , len ( invalidPods ), strings . Join ( invalidPods , "," ))
}
return nil , nil
}