/
duplicate_validator.go
124 lines (104 loc) · 3.27 KB
/
duplicate_validator.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package webhooks
import (
"context"
"errors"
"github.com/go-logr/logr"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
var ErrorDuplicateName = errors.New("name already used in namespace")
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 -generate
//counterfeiter:generate -o fake -fake-name NameRegistry . NameRegistry
type NameRegistry interface {
RegisterName(ctx context.Context, namespace, name string) error
DeregisterName(ctx context.Context, namespace, name string) error
TryLockName(ctx context.Context, namespace, name string) error
UnlockName(ctx context.Context, namespace, name string) error
}
type DuplicateValidator struct {
nameRegistry NameRegistry
}
func NewDuplicateValidator(nameRegistry NameRegistry) *DuplicateValidator {
return &DuplicateValidator{
nameRegistry: nameRegistry,
}
}
func (v DuplicateValidator) ValidateCreate(ctx context.Context, logger logr.Logger, namespace, newName string) error {
logger = logger.WithName("duplicateValidator.ValidateCreate")
err := v.nameRegistry.RegisterName(ctx, namespace, newName)
if k8serrors.IsAlreadyExists(err) {
logger.Info("failed to register name during create",
"error", err,
"name", newName,
"namespace", namespace,
)
return ErrorDuplicateName
}
if err != nil {
return err
}
return nil
}
func (v DuplicateValidator) ValidateUpdate(ctx context.Context, logger logr.Logger, namespace, oldName, newName string) error {
logger = logger.WithName("duplicateValidator.ValidateUpdate")
if oldName == newName {
return nil
}
err := v.nameRegistry.TryLockName(ctx, namespace, oldName)
if err != nil {
logger.Info("failed to acquire lock on old name",
"error", err,
"name", oldName,
"namespace", namespace,
)
return err
}
err = v.nameRegistry.RegisterName(ctx, namespace, newName)
if err != nil {
// cannot register new name, so unlock old registry entry allowing future renames
unlockErr := v.nameRegistry.UnlockName(ctx, namespace, oldName)
if unlockErr != nil {
// A locked registry entry will remain, so future name updates will fail until operator intervenes
logger.Error(unlockErr, "failed to release lock on old name",
"name", oldName,
"namespace", namespace,
)
}
logger.Info("failed to register new name during update",
"error", err,
"name", newName,
"namespace", namespace,
)
if k8serrors.IsAlreadyExists(err) {
return ErrorDuplicateName
}
return err
}
err = v.nameRegistry.DeregisterName(ctx, namespace, oldName)
if err != nil {
// We cannot unclaim the old name. It will remain claimed until an operator intervenes.
logger.Error(err, "failed to deregister old name during update",
"name", oldName,
"namespace", namespace,
)
}
return nil
}
func (v DuplicateValidator) ValidateDelete(ctx context.Context, logger logr.Logger, namespace, oldName string) error {
logger = logger.WithName("duplicateValidator.ValidateDelete")
err := v.nameRegistry.DeregisterName(ctx, namespace, oldName)
if k8serrors.IsNotFound(err) {
logger.Info("cannot deregister name: registry entry for name not found",
"namespace", namespace,
"name", oldName,
)
return nil
}
if err != nil {
logger.Error(err, "failed to deregister name during delete",
"namespace", namespace,
"name", oldName,
)
return err
}
return nil
}