Skip to content

Commit d8f858d

Browse files
committed
react to feedback, make the interface tighter on the gate
Signed-off-by: Scott Nichols <[email protected]>
1 parent 8cc1013 commit d8f858d

File tree

5 files changed

+60
-55
lines changed

5 files changed

+60
-55
lines changed

pkg/controller/gate.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,7 @@ type Gate interface {
99
// Register to call a callback function when all given GVKs are marked true. If the callback is unblocked, the
1010
// registration is removed.
1111
Register(callback func(), gvks ...schema.GroupVersionKind)
12-
// True marks a given gvk true, and then looks for unlocked callbacks. Returns if there was an update or not.
13-
True(gvk schema.GroupVersionKind) bool
14-
// False marks a given gvk false. Returns if there was an update or not.
15-
False(gkv schema.GroupVersionKind) bool
12+
// Set marks the associated condition to the given value. If the condition is already set as
13+
// that value, then this is a no-op. Returns true if there was an update detected.
14+
Set(gvk schema.GroupVersionKind, ready bool) bool
1615
}

pkg/gate/gate.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,13 @@ type Gate[T comparable] struct {
2929
fns []gated[T]
3030
}
3131

32+
// gated is an internal tracking resource.
3233
type gated[T comparable] struct {
33-
fn func()
34-
depends []T
34+
// fn is the function callback we will invoke when all the dependent conditions are true.
35+
fn func()
36+
// depends is the list of conditions this gated function is waiting on. This is an AND.
37+
depends []T
38+
// released means the gated function has been invoked and we can garbage collect this gated function.
3539
released bool
3640
}
3741

@@ -46,19 +50,9 @@ func (g *Gate[T]) Register(fn func(), depends ...T) {
4650
g.process()
4751
}
4852

49-
// True marks the associated condition as true. If the condition is already true, then this is a no-op.
50-
// Returns if there was an update detected. Thread safe.
51-
func (g *Gate[T]) True(condition T) bool {
52-
return g.markCondition(condition, true)
53-
}
54-
55-
// False marks the associated condition as false. If the condition is already false, then this is a no-op.
56-
// Returns if there was an update detected. Thread safe.
57-
func (g *Gate[T]) False(condition T) bool {
58-
return g.markCondition(condition, false)
59-
}
60-
61-
func (g *Gate[T]) markCondition(condition T, ready bool) bool {
53+
// Set marks the associated condition to the given value. If the condition is already set as that value, then this is a
54+
// no-op. Returns true if there was an update detected. Thread safe.
55+
func (g *Gate[T]) Set(condition T, value bool) bool {
6256
g.mux.Lock()
6357

6458
if g.conditions == nil {
@@ -68,9 +62,9 @@ func (g *Gate[T]) markCondition(condition T, ready bool) bool {
6862
old, found := g.conditions[condition]
6963

7064
updated := false
71-
if !found || old != ready {
65+
if !found || old != value {
7266
updated = true
73-
g.conditions[condition] = ready
67+
g.conditions[condition] = value
7468
}
7569
// process() would also like to lock the mux, so we must unlock here directly and not use defer.
7670
g.mux.Unlock()
@@ -87,22 +81,25 @@ func (g *Gate[T]) process() {
8781
defer g.mux.Unlock()
8882

8983
for i := range g.fns {
84+
// release controls if we should release the function.
9085
release := true
9186

9287
for _, dep := range g.fns[i].depends {
93-
if ready := g.conditions[dep]; !ready {
88+
if !g.conditions[dep] {
9489
release = false
9590
}
9691
}
9792

9893
if release {
9994
fn := g.fns[i].fn
95+
// mark the function released so we can garbage collect after we are done with the loop.
10096
g.fns[i].released = true
10197
// Need to capture a copy of fn or else we would be accessing a deleted member when the go routine runs.
10298
go fn()
10399
}
104100
}
105101

102+
// garbage collect released functions.
106103
g.fns = slices.DeleteFunc(g.fns, func(a gated[T]) bool {
107104
return a.released
108105
})

pkg/gate/gate_test.go

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ func TestGate_Integration(t *testing.T) {
108108
}, "condition1")
109109

110110
// Set condition to true (will be initialized as false first)
111-
g.True("condition1")
111+
g.Set("condition1", true)
112112
return called
113113
},
114114
want: want{
@@ -124,8 +124,8 @@ func TestGate_Integration(t *testing.T) {
124124
}, "condition1", "condition2")
125125

126126
// Set both conditions to true
127-
g.True("condition1")
128-
g.True("condition2")
127+
g.Set("condition1", true)
128+
g.Set("condition2", true)
129129
return called
130130
},
131131
want: want{
@@ -141,7 +141,7 @@ func TestGate_Integration(t *testing.T) {
141141
}, "condition1", "condition2")
142142

143143
// Set only one condition to true
144-
g.True("condition1")
144+
g.Set("condition1", true)
145145
return called
146146
},
147147
want: want{
@@ -153,8 +153,8 @@ func TestGate_Integration(t *testing.T) {
153153
setup: func(g *gate.Gate[string]) chan bool {
154154
called := make(chan bool, 1)
155155

156-
g.True("condition1")
157-
g.True("condition2")
156+
g.Set("condition1", true)
157+
g.Set("condition2", true)
158158

159159
g.Register(func() {
160160
called <- true
@@ -175,8 +175,8 @@ func TestGate_Integration(t *testing.T) {
175175
}, "condition1")
176176

177177
// Set condition to true then false (function already called when true)
178-
g.True("condition1")
179-
g.False("condition1")
178+
g.Set("condition1", true)
179+
g.Set("condition1", false)
180180
return called
181181
},
182182
want: want{
@@ -192,9 +192,9 @@ func TestGate_Integration(t *testing.T) {
192192
}, "condition1")
193193

194194
// Set condition multiple times
195-
g.True("condition1")
196-
g.False("condition1")
197-
g.True("condition1")
195+
g.Set("condition1", true)
196+
g.Set("condition1", false)
197+
g.Set("condition1", true)
198198
return called
199199
},
200200
want: want{
@@ -259,7 +259,7 @@ func TestGate_Concurrency(t *testing.T) {
259259
wg.Wait()
260260

261261
// Set condition to true once
262-
g.True("shared-condition")
262+
g.Set("shared-condition", true)
263263

264264
// Give some time for goroutines to execute
265265
time.Sleep(100 * time.Millisecond)
@@ -286,9 +286,9 @@ func TestGate_TypeSafety(t *testing.T) {
286286
called = true
287287
}, 1, 2, 3)
288288

289-
intGate.True(1)
290-
intGate.True(2)
291-
intGate.True(3)
289+
intGate.Set(1, true)
290+
intGate.Set(2, true)
291+
intGate.Set(3, true)
292292

293293
// Give some time for goroutine to execute
294294
time.Sleep(10 * time.Millisecond)

pkg/reconciler/customresourcesgate/reconciler.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ type Reconciler struct {
3737
gate controller.Gate
3838
}
3939

40-
// +kubebuilder:rbac:groups=apiextensions.k8s.io,resources=customresourcedefinitions,verbs=get;list;watch
41-
4240
// Reconcile reconciles CustomResourceDefinitions and reports ready and unready GVKs to the gate.
4341
func (r *Reconciler) Reconcile(_ context.Context, crd *apiextensionsv1.CustomResourceDefinition) (ctrl.Result, error) {
4442
// If there is no gate, then we don't need to do work.
@@ -55,7 +53,7 @@ func (r *Reconciler) Reconcile(_ context.Context, crd *apiextensionsv1.CustomRes
5553
case !established || !crd.GetDeletionTimestamp().IsZero():
5654
for gvk := range gkvs {
5755
r.log.Debug("gvk is not ready", "gvk", gvk)
58-
r.gate.False(gvk)
56+
r.gate.Set(gvk, false)
5957
}
6058

6159
return ctrl.Result{}, nil
@@ -65,7 +63,7 @@ func (r *Reconciler) Reconcile(_ context.Context, crd *apiextensionsv1.CustomRes
6563
for gvk, served := range gkvs {
6664
if served {
6765
r.log.Debug("gvk is ready", "gvk", gvk)
68-
r.gate.True(gvk)
66+
r.gate.Set(gvk, true)
6967
}
7068
}
7169
}

pkg/reconciler/customresourcesgate/reconciler_test.go

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ package customresourcesgate
1818

1919
import (
2020
"context"
21+
"slices"
22+
"strings"
2123
"testing"
2224

2325
"github.com/google/go-cmp/cmp"
@@ -271,23 +273,20 @@ func NewMockGate() *MockGate {
271273
}
272274
}
273275

274-
func (m *MockGate) True(gvk schema.GroupVersionKind) bool {
275-
if m.TrueCalls == nil {
276-
m.TrueCalls = make([]schema.GroupVersionKind, 0)
277-
}
278-
279-
m.TrueCalls = append(m.TrueCalls, gvk)
276+
func (m *MockGate) Set(gvk schema.GroupVersionKind, value bool) bool {
277+
if value {
278+
if m.TrueCalls == nil {
279+
m.TrueCalls = make([]schema.GroupVersionKind, 0)
280+
}
280281

281-
return true
282-
}
282+
m.TrueCalls = append(m.TrueCalls, gvk)
283+
} else {
284+
if m.FalseCalls == nil {
285+
m.FalseCalls = make([]schema.GroupVersionKind, 0)
286+
}
283287

284-
func (m *MockGate) False(gvk schema.GroupVersionKind) bool {
285-
if m.FalseCalls == nil {
286-
m.FalseCalls = make([]schema.GroupVersionKind, 0)
288+
m.FalseCalls = append(m.FalseCalls, gvk)
287289
}
288-
289-
m.FalseCalls = append(m.FalseCalls, gvk)
290-
291290
return true
292291
}
293292

@@ -570,10 +569,22 @@ func TestReconcile(t *testing.T) {
570569

571570
// Only check gate calls if gate is not nil
572571
if tc.fields.gate != nil {
572+
slices.SortFunc(tc.want.trueCalls, func(a, b schema.GroupVersionKind) int {
573+
return strings.Compare(a.Kind, b.Kind)
574+
})
575+
slices.SortFunc(tc.fields.gate.TrueCalls, func(a, b schema.GroupVersionKind) int {
576+
return strings.Compare(a.Kind, b.Kind)
577+
})
573578
if diff := cmp.Diff(tc.want.trueCalls, tc.fields.gate.TrueCalls); diff != "" {
574579
t.Errorf("\n%s\ngate.True calls: -want, +got:\n%s", tc.reason, diff)
575580
}
576581

582+
slices.SortFunc(tc.want.falseCalls, func(a, b schema.GroupVersionKind) int {
583+
return strings.Compare(a.Kind, b.Kind)
584+
})
585+
slices.SortFunc(tc.fields.gate.FalseCalls, func(a, b schema.GroupVersionKind) int {
586+
return strings.Compare(a.Kind, b.Kind)
587+
})
577588
if diff := cmp.Diff(tc.want.falseCalls, tc.fields.gate.FalseCalls); diff != "" {
578589
t.Errorf("\n%s\ngate.False calls: -want, +got:\n%s", tc.reason, diff)
579590
}

0 commit comments

Comments
 (0)