Skip to content

Commit dec704c

Browse files
committed
use watch to wait gateway and listeners set to not programmed
1 parent 0a585ba commit dec704c

File tree

4 files changed

+123
-3
lines changed

4 files changed

+123
-3
lines changed

test/envtest/watch.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ const (
1717
clientWatchTimeout = 45 * time.Second
1818
)
1919

20+
// TODO: migrate all usage of `watch` in envtest package to use `helper.Watch`.
21+
2022
// watch is a wrapper around watch.Interface.
2123
// It is useful to provide a type-safe way to access the watch.Interface so that
2224
// the callers do not use an invalid watches when using watchFor().

test/helpers/watch.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package helpers
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/kr/pretty"
9+
"github.com/stretchr/testify/require"
10+
apiwatch "k8s.io/apimachinery/pkg/watch"
11+
"sigs.k8s.io/controller-runtime/pkg/client"
12+
)
13+
14+
// WatchFor watches for an event of type eventType using the provided watch.Interface.
15+
// It returns when either context is done - and then it marks the test as failed -
16+
// or when the event has been received and predicate returned true.
17+
func WatchFor[
18+
T client.Object,
19+
](
20+
t *testing.T,
21+
ctx context.Context,
22+
w apiwatch.Interface,
23+
eventType apiwatch.EventType,
24+
timeout time.Duration,
25+
predicate func(T) bool,
26+
failMsg string,
27+
) T {
28+
t.Helper()
29+
30+
ctx, cancel := context.WithTimeout(ctx, timeout)
31+
defer cancel()
32+
33+
var (
34+
obj T
35+
receivedAtLeastOneObj bool
36+
)
37+
for found := false; !found; {
38+
select {
39+
case <-ctx.Done():
40+
if receivedAtLeastOneObj {
41+
require.Failf(t, failMsg, "Last object received:\n%v", pretty.Sprint(obj))
42+
} else {
43+
require.Fail(t, failMsg)
44+
}
45+
case e := <-w.ResultChan():
46+
if e.Type != eventType {
47+
continue
48+
}
49+
var ok bool
50+
obj, ok = e.Object.(T)
51+
if !ok {
52+
continue
53+
}
54+
receivedAtLeastOneObj = true
55+
if !predicate(obj) {
56+
continue
57+
}
58+
found = true
59+
}
60+
}
61+
return obj
62+
}

test/helpers/watch_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package helpers
2+
3+
import (
4+
"testing"
5+
"time"
6+
7+
"github.com/stretchr/testify/require"
8+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
9+
apiwatch "k8s.io/apimachinery/pkg/watch"
10+
fakectrlruntimeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
11+
12+
configurationv1 "github.com/kong/kong-operator/api/configuration/v1"
13+
"github.com/kong/kong-operator/modules/manager/scheme"
14+
)
15+
16+
func TestWatch(t *testing.T) {
17+
var (
18+
ctx = t.Context()
19+
cl = fakectrlruntimeclient.NewClientBuilder().
20+
WithScheme(scheme.Get()).
21+
Build()
22+
consumer = &configurationv1.KongConsumer{
23+
ObjectMeta: metav1.ObjectMeta{
24+
Name: "test-consumer",
25+
},
26+
}
27+
)
28+
29+
wConsumer, err := cl.Watch(ctx, &configurationv1.KongConsumerList{})
30+
require.NoError(t, err)
31+
require.NoError(t, cl.Create(ctx, consumer))
32+
WatchFor(t, ctx, wConsumer, apiwatch.Added,
33+
time.Second,
34+
func(c *configurationv1.KongConsumer) bool {
35+
return c.Name == consumer.Name
36+
},
37+
"error",
38+
)
39+
}

test/integration/gateway_test.go

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2020
"k8s.io/apimachinery/pkg/types"
2121
"k8s.io/apimachinery/pkg/util/intstr"
22+
apiwatch "k8s.io/apimachinery/pkg/watch"
2223
"sigs.k8s.io/controller-runtime/pkg/client"
2324
gatewayv1 "sigs.k8s.io/gateway-api/apis/v1"
2425

@@ -27,6 +28,7 @@ import (
2728
operatorv2beta1 "github.com/kong/kong-operator/api/gateway-operator/v2beta1"
2829
konnectv1alpha1 "github.com/kong/kong-operator/api/konnect/v1alpha1"
2930
konnectv1alpha2 "github.com/kong/kong-operator/api/konnect/v1alpha2"
31+
"github.com/kong/kong-operator/modules/manager/scheme"
3032
"github.com/kong/kong-operator/pkg/consts"
3133
"github.com/kong/kong-operator/pkg/gatewayapi"
3234
gatewayutils "github.com/kong/kong-operator/pkg/utils/gateway"
@@ -48,6 +50,14 @@ func TestGatewayEssentials(t *testing.T) {
4850
require.NoError(t, err)
4951
cleaner.Add(gatewayClass)
5052

53+
t.Log("setting up watch for Gateways")
54+
cl, err := client.NewWithWatch(GetEnv().Cluster().Config(), client.Options{
55+
Scheme: scheme.Get(),
56+
})
57+
require.NoError(t, err, "failed to setup a client for watching gateways")
58+
wGateway, err := cl.Watch(GetCtx(), &gatewayv1.GatewayList{}, client.InNamespace(namespace.Name))
59+
require.NoError(t, err, "failed to start watching gateways")
60+
5161
t.Log("deploying Gateway resource")
5262
gatewayNN := types.NamespacedName{
5363
Name: uuid.NewString(),
@@ -110,9 +120,16 @@ func TestGatewayEssentials(t *testing.T) {
110120
t.Log("deleting dataplane")
111121
require.NoError(t, dataplaneClient.Delete(GetCtx(), dataplane.Name, metav1.DeleteOptions{}))
112122

113-
t.Log("verifying Gateway gets and its listeners are marked as not Programmed")
114-
require.Eventually(t, testutils.Not(testutils.GatewayIsProgrammed(t, GetCtx(), gatewayNN, clients)), testutils.GatewayReadyTimeLimit, 100*time.Millisecond)
115-
require.Eventually(t, testutils.Not(testutils.GatewayListenersAreProgrammed(t, GetCtx(), gatewayNN, clients)), testutils.GatewayReadyTimeLimit, 100*time.Millisecond)
123+
t.Logf("verifying Gateway gets and its listeners are marked as not Programmed at %v", time.Now())
124+
_ = helpers.WatchFor(t, GetCtx(), wGateway, apiwatch.Modified,
125+
testutils.GatewayReadyTimeLimit,
126+
func(gw *gatewayv1.Gateway) bool {
127+
return gw.Name == gatewayNN.Name && gw.Namespace == gatewayNN.Namespace &&
128+
!gatewayutils.IsProgrammed(gw) && !gatewayutils.AreListenersProgrammed(gw)
129+
},
130+
"Did not see gateway and all its listeners' Programmed condition set to False",
131+
)
132+
t.Logf("see gateway and all its listeners marked as not programmed at %v in watch", time.Now())
116133

117134
t.Log("verifying that the ControlPlane becomes provisioned again")
118135
require.Eventually(t, testutils.GatewayControlPlaneIsProvisioned(t, GetCtx(), gateway, clients), 45*time.Second, time.Second)

0 commit comments

Comments
 (0)