1/*
2 *
3 * Copyright 2019 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19// Package fakexds provides fake implementation of multiple types, for use in
20// xds tests.
21package fakexds
22
23import (
24	"sync"
25
26	"google.golang.org/grpc/xds/internal/balancer/lrs"
27	xdsclient "google.golang.org/grpc/xds/internal/client"
28	"google.golang.org/grpc/xds/internal/testutils"
29)
30
31// Client is a fake implementation of an xds client. It exposes a bunch of
32// channels to signal the occurrence of various events.
33type Client struct {
34	name         string
35	suWatchCh    *testutils.Channel
36	edsWatchCh   *testutils.Channel
37	suCancelCh   *testutils.Channel
38	edsCancelCh  *testutils.Channel
39	loadReportCh *testutils.Channel
40	closeCh      *testutils.Channel
41
42	mu        sync.Mutex
43	serviceCb func(xdsclient.ServiceUpdate, error)
44	edsCb     func(*xdsclient.EDSUpdate, error)
45}
46
47// WatchService registers a LDS/RDS watch.
48func (xdsC *Client) WatchService(target string, callback func(xdsclient.ServiceUpdate, error)) func() {
49	xdsC.mu.Lock()
50	defer xdsC.mu.Unlock()
51
52	xdsC.serviceCb = callback
53	xdsC.suWatchCh.Send(target)
54	return func() {
55		xdsC.suCancelCh.Send(nil)
56	}
57}
58
59// WaitForWatchService waits for WatchService to be invoked on this client
60// within a reasonable timeout, and returns the serviceName being watched.
61func (xdsC *Client) WaitForWatchService() (string, error) {
62	val, err := xdsC.suWatchCh.Receive()
63	return val.(string), err
64}
65
66// InvokeWatchServiceCallback invokes the registered service watch callback.
67func (xdsC *Client) InvokeWatchServiceCallback(cluster string, err error) {
68	xdsC.mu.Lock()
69	defer xdsC.mu.Unlock()
70
71	xdsC.serviceCb(xdsclient.ServiceUpdate{Cluster: cluster}, err)
72}
73
74// WatchEDS registers an EDS watch for provided clusterName.
75func (xdsC *Client) WatchEDS(clusterName string, callback func(*xdsclient.EDSUpdate, error)) (cancel func()) {
76	xdsC.mu.Lock()
77	defer xdsC.mu.Unlock()
78
79	xdsC.edsCb = callback
80	xdsC.edsWatchCh.Send(clusterName)
81	return func() {
82		xdsC.edsCancelCh.Send(nil)
83	}
84}
85
86// WaitForWatchEDS waits for WatchEDS to be invoked on this client within a
87// reasonable timeout, and returns the clusterName being watched.
88func (xdsC *Client) WaitForWatchEDS() (string, error) {
89	val, err := xdsC.edsWatchCh.Receive()
90	return val.(string), err
91}
92
93// InvokeWatchEDSCallback invokes the registered edsWatch callback.
94func (xdsC *Client) InvokeWatchEDSCallback(update *xdsclient.EDSUpdate, err error) {
95	xdsC.mu.Lock()
96	defer xdsC.mu.Unlock()
97
98	xdsC.edsCb(update, err)
99}
100
101// ReportLoadArgs wraps the arguments passed to ReportLoad.
102type ReportLoadArgs struct {
103	// Server is the name of the server to which the load is reported.
104	Server string
105	// Cluster is the name of the cluster for which load is reported.
106	Cluster string
107}
108
109// ReportLoad starts reporting load about clusterName to server.
110func (xdsC *Client) ReportLoad(server string, clusterName string, loadStore lrs.Store) (cancel func()) {
111	xdsC.loadReportCh.Send(ReportLoadArgs{Server: server, Cluster: clusterName})
112	return func() {}
113}
114
115// WaitForReportLoad waits for ReportLoad to be invoked on this client within a
116// reasonable timeout, and returns the arguments passed to it.
117func (xdsC *Client) WaitForReportLoad() (ReportLoadArgs, error) {
118	val, err := xdsC.loadReportCh.Receive()
119	return val.(ReportLoadArgs), err
120}
121
122// Close closes the xds client.
123func (xdsC *Client) Close() {
124	xdsC.closeCh.Send(nil)
125}
126
127// Name returns the name of the xds client.
128func (xdsC *Client) Name() string {
129	return xdsC.name
130}
131
132// NewClient returns a new fake xds client.
133func NewClient() *Client {
134	return NewClientWithName("")
135}
136
137// NewClientWithName returns a new fake xds client with the provided name. This
138// is used in cases where multiple clients are created in the tests and we need
139// to make sure the client is created for the expected balancer name.
140func NewClientWithName(name string) *Client {
141	return &Client{
142		name:         name,
143		suWatchCh:    testutils.NewChannel(),
144		edsWatchCh:   testutils.NewChannel(),
145		suCancelCh:   testutils.NewChannel(),
146		edsCancelCh:  testutils.NewChannel(),
147		loadReportCh: testutils.NewChannel(),
148		closeCh:      testutils.NewChannel(),
149	}
150}
151