cloudfoundry/korifi

View on GitHub
controllers/webhooks/validation/duplicate_validator.go

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
package validation
 
import (
"context"
 
"code.cloudfoundry.org/korifi/controllers/webhooks"
"github.com/go-logr/logr"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
)
 
const DuplicateNameErrorType = "DuplicateNameError"
 
type DuplicateValidator struct {
nameRegistry webhooks.NameRegistry
}
 
func NewDuplicateValidator(nameRegistry webhooks.NameRegistry) *DuplicateValidator {
return &DuplicateValidator{
nameRegistry: nameRegistry,
}
}
 
func (v DuplicateValidator) ValidateCreate(ctx context.Context, logger logr.Logger, namespace string, obj webhooks.UniqueClientObject) error {
logger = logger.WithName("duplicateValidator.ValidateCreate")
err := v.nameRegistry.RegisterName(ctx, namespace, obj.UniqueName(), obj.GetNamespace(), obj.GetName())
if err != nil {
logger.Info("failed to register name during create",
"name", obj.UniqueName(),
"namespace", namespace,
"reason", err,
)
 
if k8serrors.IsAlreadyExists(err) {
return duplicateError(obj)
}
 
return unknownError()
}
 
return nil
}
 
Method `DuplicateValidator.ValidateUpdate` has 52 lines of code (exceeds 50 allowed). Consider refactoring.
func (v DuplicateValidator) ValidateUpdate(ctx context.Context, logger logr.Logger, namespace string, oldObj, obj webhooks.UniqueClientObject) error {
if oldObj.UniqueName() == obj.UniqueName() {
return nil
}
 
logger = logger.
WithName("duplicateValidator.ValidateUpdate").
WithValues("namespace", namespace, "oldName", oldObj.UniqueName(), "newName", obj.UniqueName())
 
err := v.nameRegistry.TryLockName(ctx, namespace, oldObj.UniqueName())
if err != nil {
logger.Info("failed to acquire lock on old name", "reason", err)
 
if k8serrors.IsNotFound(err) {
isOwned, ownershipErr := v.nameRegistry.CheckNameOwnership(ctx, namespace, obj.UniqueName(), obj.GetNamespace(), obj.GetName())
if ownershipErr != nil {
logger.Error(ownershipErr, "failed to check ownership on new name")
return unknownError()
}
 
if isOwned {
logger.Info("unique name is already owned by updated object",
"name", obj.UniqueName(),
"updatedObjectKind", obj.GetObjectKind(),
"object", client.ObjectKeyFromObject(obj),
)
return nil
}
}
 
return unknownError()
}
 
logger.V(1).Info("locked-old-name")
 
err = v.nameRegistry.RegisterName(ctx, namespace, obj.UniqueName(), obj.GetNamespace(), obj.GetName())
if err != nil {
// cannot register new name, so unlock old registry entry allowing future renames
unlockErr := v.nameRegistry.UnlockName(ctx, namespace, oldObj.UniqueName())
if unlockErr != nil {
// A locked registry entry will remain, so future name updates will fail until operator intervenes
logger.Info("failed to release lock on old name",
"reason", unlockErr,
)
}
 
logger.Info("failed to register new name during update",
"reason", err,
)
 
if k8serrors.IsAlreadyExists(err) {
return duplicateError(obj)
}
 
return unknownError()
}
logger.V(1).Info("registered-new-name")
 
err = v.nameRegistry.DeregisterName(ctx, namespace, oldObj.UniqueName())
if err != nil {
// We cannot unclaim the old name. It will remain claimed until an operator intervenes.
logger.Info("failed to deregister old name during update",
"reason", err,
)
}
logger.V(1).Info("deregistered-old-name")
 
return nil
}
 
func (v DuplicateValidator) ValidateDelete(ctx context.Context, logger logr.Logger, namespace string, obj webhooks.UniqueClientObject) error {
logger = logger.WithName("duplicateValidator.ValidateDelete")
err := v.nameRegistry.DeregisterName(ctx, namespace, obj.UniqueName())
if err != nil {
logger.Info("failed to deregister name during delete",
"namespace", namespace,
"name", obj.UniqueName(),
"reason", err,
)
 
if k8serrors.IsNotFound(err) {
return nil
}
 
return unknownError()
}
 
return nil
}
 
func duplicateError(obj webhooks.UniqueClientObject) error {
return ValidationError{
Type: DuplicateNameErrorType,
Message: obj.UniqueValidationErrorMessage(),
}.ExportJSONError()
}
 
func unknownError() error {
return ValidationError{
Type: UnknownErrorType,
Message: UnknownErrorMessage,
}.ExportJSONError()
}
 
// check interface is implemented correctly
var _ webhooks.NameValidator = DuplicateValidator{}