-
Notifications
You must be signed in to change notification settings - Fork 6
/
ensure_component.go
96 lines (84 loc) · 2.87 KB
/
ensure_component.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
package component
import (
"context"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
applycorev1 "k8s.io/client-go/applyconfigurations/core/v1"
"github.com/authzed/controller-idioms/handler"
"github.com/authzed/controller-idioms/queue"
"github.com/authzed/controller-idioms/typedctx"
)
// Annotator is any type that can have annotations added to it. All standard
// applyconfiguration packages from client-go implement this type. Custom types
// should implement it themselves.
type Annotator[T any] interface {
WithAnnotations(entries map[string]string) T
}
// EnsureComponentByHash is a handler.Handler implementation that
// will create a component object and ensure it has the computed spec.
type EnsureComponentByHash[K KubeObject, A Annotator[A]] struct {
*HashableComponent[K]
ctrls queue.OperationsContext
nn typedctx.MustValueContext[types.NamespacedName]
applyObject func(ctx context.Context, apply A) (K, error)
deleteObject func(ctx context.Context, nn types.NamespacedName) error
newObj func(ctx context.Context) A
}
var _ handler.ContextHandler = &EnsureComponentByHash[*corev1.Service, *applycorev1.ServiceApplyConfiguration]{}
// NewEnsureComponentByHash returns a new EnsureComponentByHash handler.
func NewEnsureComponentByHash[K KubeObject, A Annotator[A]](
component *HashableComponent[K],
owner typedctx.MustValueContext[types.NamespacedName],
ctrls queue.OperationsContext,
applyObj func(ctx context.Context, apply A) (K, error),
deleteObject func(ctx context.Context, nn types.NamespacedName) error,
newObj func(ctx context.Context) A,
) *EnsureComponentByHash[K, A] {
return &EnsureComponentByHash[K, A]{
ctrls: ctrls,
HashableComponent: component,
nn: owner,
applyObject: applyObj,
deleteObject: deleteObject,
newObj: newObj,
}
}
func (e *EnsureComponentByHash[K, A]) Handle(ctx context.Context) {
ownedObjs := e.List(ctx, e.nn.MustValue(ctx))
newObj := e.newObj(ctx)
hash := e.Hash(newObj)
newObj = newObj.WithAnnotations(map[string]string{e.HashAnnotationKey: hash})
matchingObjs := make([]K, 0)
extraObjs := make([]K, 0)
for _, o := range ownedObjs {
annotations := o.GetAnnotations()
if annotations == nil {
extraObjs = append(extraObjs, o)
}
if e.Equal(annotations[e.HashAnnotationKey], hash) {
matchingObjs = append(matchingObjs, o)
} else {
extraObjs = append(extraObjs, o)
}
}
if len(matchingObjs) == 0 {
// apply if no matching KubeObject in cluster
_, err := e.applyObject(ctx, newObj)
if err != nil {
e.ctrls.RequeueErr(ctx, err)
return
}
}
if len(matchingObjs) == 1 {
// delete extra objects
for _, o := range extraObjs {
if err := e.deleteObject(ctx, types.NamespacedName{
Namespace: o.GetNamespace(),
Name: o.GetName(),
}); err != nil {
e.ctrls.RequeueErr(ctx, err)
return
}
}
}
}