blob: a4b76ffa634b06a2aaef52b096683174597ca7d4 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Tim Windelschmidt5d0906e2023-07-20 20:23:57 +02004package util
5
6import (
7 "context"
8 "net"
9 "testing"
10
11 "golang.zx2c4.com/wireguard/wgctrl/wgtypes"
12 "google.golang.org/grpc"
13 "google.golang.org/grpc/credentials/insecure"
14 "google.golang.org/grpc/test/bufconn"
15
16 apb "source.monogon.dev/metropolis/node/core/curator/proto/api"
17 cpb "source.monogon.dev/metropolis/proto/common"
18
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020019 "source.monogon.dev/osbase/event/memory"
Tim Windelschmidt5d0906e2023-07-20 20:23:57 +020020)
21
22// TestCurator is a shim Curator implementation that serves pending Watch
23// requests based on data submitted to a channel.
24type TestCurator struct {
25 apb.UnimplementedCuratorServer
26
27 watchC chan *apb.WatchEvent
28 updateReq memory.Value[*apb.UpdateNodeClusterNetworkingRequest]
29}
30
31// Watch implements a minimum Watch which just returns all nodes at once.
32func (t *TestCurator) Watch(_ *apb.WatchRequest, srv apb.Curator_WatchServer) error {
33 ctx := srv.Context()
34 for {
35 select {
36 case <-ctx.Done():
37 return ctx.Err()
38 case ev := <-t.watchC:
39 if err := srv.Send(ev); err != nil {
40 return err
41 }
42 }
43 }
44}
45
46func (t *TestCurator) UpdateNodeClusterNetworking(ctx context.Context, req *apb.UpdateNodeClusterNetworkingRequest) (*apb.UpdateNodeClusterNetworkingResponse, error) {
47 t.updateReq.Set(req)
48 return &apb.UpdateNodeClusterNetworkingResponse{}, nil
49}
50
51// NodeWithPrefixes submits a given node/key/address with prefixes to the Watch
52// event channel.
53func (t *TestCurator) NodeWithPrefixes(key wgtypes.Key, id, address string, prefixes ...string) {
54 var p []*cpb.NodeClusterNetworking_Prefix
55 for _, prefix := range prefixes {
56 p = append(p, &cpb.NodeClusterNetworking_Prefix{Cidr: prefix})
57 }
58 n := &apb.Node{
59 Id: id,
60 Status: &cpb.NodeStatus{
61 ExternalAddress: address,
62 },
63 Clusternet: &cpb.NodeClusterNetworking{
64 WireguardPubkey: key.PublicKey().String(),
65 Prefixes: p,
66 },
67 Roles: &cpb.NodeRoles{
68 ConsensusMember: &cpb.NodeRoles_ConsensusMember{},
69 },
70 }
71 t.watchC <- &apb.WatchEvent{
72 Nodes: []*apb.Node{
73 n,
74 },
75 }
76}
77
78// DeleteNode submits a given node for deletion to the Watch event channel.
79func (t *TestCurator) DeleteNode(id string) {
80 t.watchC <- &apb.WatchEvent{
81 NodeTombstones: []*apb.WatchEvent_NodeTombstone{
82 {
83 NodeId: id,
84 },
85 },
86 }
87}
88
89// MakeTestCurator returns a working TestCurator alongside a grpc connection to
90// it.
91func MakeTestCurator(t *testing.T) (*TestCurator, *grpc.ClientConn) {
92 cur := &TestCurator{
93 watchC: make(chan *apb.WatchEvent),
94 }
95
96 srv := grpc.NewServer()
97 apb.RegisterCuratorServer(srv, cur)
98 externalLis := bufconn.Listen(1024 * 1024)
99 go func() {
100 if err := srv.Serve(externalLis); err != nil {
Tim Windelschmidt88049722024-04-11 23:09:23 +0200101 t.Errorf("GRPC serve failed: %v", err)
102 return
Tim Windelschmidt5d0906e2023-07-20 20:23:57 +0200103 }
104 }()
105 withLocalDialer := grpc.WithContextDialer(func(_ context.Context, _ string) (net.Conn, error) {
106 return externalLis.Dial()
107 })
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +0100108 cl, err := grpc.NewClient("passthrough:///local", withLocalDialer, grpc.WithTransportCredentials(insecure.NewCredentials()))
Tim Windelschmidt5d0906e2023-07-20 20:23:57 +0200109 if err != nil {
Tim Windelschmidt9bd9bd42025-02-14 17:08:52 +0100110 t.Fatalf("Creating GRPC Client failed: %v", err)
Tim Windelschmidt5d0906e2023-07-20 20:23:57 +0200111 }
112
113 return cur, cl
114}