-
Notifications
You must be signed in to change notification settings - Fork 61
/
duplicate_validator.go
142 lines (118 loc) · 3.67 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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package webhooks
import (
"context"
"github.com/go-logr/logr"
k8serrors "k8s.io/apimachinery/pkg/api/errors"
)
const DuplicateNameErrorType = "DuplicateNameError"
//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, duplicateNameError string) *ValidationError {
logger = logger.WithName("duplicateValidator.ValidateCreate")
err := v.nameRegistry.RegisterName(ctx, namespace, newName)
if err != nil {
logger.Info("failed to register name during create",
"error", err,
"name", newName,
"namespace", namespace,
)
if k8serrors.IsAlreadyExists(err) {
return &ValidationError{
Type: DuplicateNameErrorType,
Message: duplicateNameError,
}
}
return &ValidationError{
Type: UnknownErrorType,
Message: UnknownErrorMessage,
}
}
return nil
}
func (v DuplicateValidator) ValidateUpdate(ctx context.Context, logger logr.Logger, namespace, oldName, newName, duplicateNameError string) *ValidationError {
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 &ValidationError{
Type: UnknownErrorType,
Message: UnknownErrorMessage,
}
}
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 &ValidationError{
Type: DuplicateNameErrorType,
Message: duplicateNameError,
}
}
return &ValidationError{
Type: UnknownErrorType,
Message: UnknownErrorMessage,
}
}
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) *ValidationError {
logger = logger.WithName("duplicateValidator.ValidateDelete")
err := v.nameRegistry.DeregisterName(ctx, namespace, oldName)
if err != nil {
logger.Info("failed to deregister name during delete",
"error", err,
"namespace", namespace,
"name", oldName,
)
if k8serrors.IsNotFound(err) {
return nil
}
return &ValidationError{
Type: UnknownErrorType,
Message: UnknownErrorMessage,
}
}
return nil
}