Skip to content

Commit 50f8270

Browse files
authored
rls: control channel implementation (grpc#5046)
1 parent 7c8a932 commit 50f8270

17 files changed

+1389
-1427
lines changed

balancer/rls/internal/balancer.go

+5-152
Original file line numberDiff line numberDiff line change
@@ -19,183 +19,36 @@
1919
package rls
2020

2121
import (
22-
"sync"
23-
24-
"google.golang.org/grpc"
2522
"google.golang.org/grpc/balancer"
2623
"google.golang.org/grpc/grpclog"
27-
"google.golang.org/grpc/internal/grpcsync"
2824
)
2925

3026
var (
3127
_ balancer.Balancer = (*rlsBalancer)(nil)
3228

33-
// For overriding in tests.
34-
newRLSClientFunc = newRLSClient
35-
logger = grpclog.Component("rls")
29+
logger = grpclog.Component("rls")
3630
)
3731

3832
// rlsBalancer implements the RLS LB policy.
39-
type rlsBalancer struct {
40-
done *grpcsync.Event
41-
cc balancer.ClientConn
42-
opts balancer.BuildOptions
43-
44-
// Mutex protects all the state maintained by the LB policy.
45-
// TODO(easwars): Once we add the cache, we will also have another lock for
46-
// the cache alone.
47-
mu sync.Mutex
48-
lbCfg *lbConfig // Most recently received service config.
49-
rlsCC *grpc.ClientConn // ClientConn to the RLS server.
50-
rlsC *rlsClient // RLS client wrapper.
51-
52-
ccUpdateCh chan *balancer.ClientConnState
53-
}
54-
55-
// run is a long running goroutine which handles all the updates that the
56-
// balancer wishes to handle. The appropriate updateHandler will push the update
57-
// on to a channel that this goroutine will select on, thereby the handling of
58-
// the update will happen asynchronously.
59-
func (lb *rlsBalancer) run() {
60-
for {
61-
// TODO(easwars): Handle other updates like subConn state changes, RLS
62-
// responses from the server etc.
63-
select {
64-
case u := <-lb.ccUpdateCh:
65-
lb.handleClientConnUpdate(u)
66-
case <-lb.done.Done():
67-
return
68-
}
69-
}
70-
}
71-
72-
// handleClientConnUpdate handles updates to the service config.
73-
// If the RLS server name or the RLS RPC timeout changes, it updates the control
74-
// channel accordingly.
75-
// TODO(easwars): Handle updates to other fields in the service config.
76-
func (lb *rlsBalancer) handleClientConnUpdate(ccs *balancer.ClientConnState) {
77-
logger.Infof("rls: service config: %+v", ccs.BalancerConfig)
78-
lb.mu.Lock()
79-
defer lb.mu.Unlock()
80-
81-
if lb.done.HasFired() {
82-
logger.Warning("rls: received service config after balancer close")
83-
return
84-
}
85-
86-
newCfg := ccs.BalancerConfig.(*lbConfig)
87-
if lb.lbCfg.Equal(newCfg) {
88-
logger.Info("rls: new service config matches existing config")
89-
return
90-
}
91-
92-
lb.updateControlChannel(newCfg)
93-
lb.lbCfg = newCfg
94-
}
33+
type rlsBalancer struct{}
9534

96-
// UpdateClientConnState pushes the received ClientConnState update on the
97-
// update channel which will be processed asynchronously by the run goroutine.
98-
// Implements balancer.Balancer interface.
9935
func (lb *rlsBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error {
100-
select {
101-
case lb.ccUpdateCh <- &ccs:
102-
case <-lb.done.Done():
103-
}
36+
logger.Fatal("rls: UpdateClientConnState is not yet unimplemented")
10437
return nil
10538
}
10639

107-
// ResolverErr implements balancer.Balancer interface.
10840
func (lb *rlsBalancer) ResolverError(error) {
109-
// ResolverError is called by gRPC when the name resolver reports an error.
110-
// TODO(easwars): How do we handle this?
11141
logger.Fatal("rls: ResolverError is not yet unimplemented")
11242
}
11343

114-
// UpdateSubConnState implements balancer.Balancer interface.
11544
func (lb *rlsBalancer) UpdateSubConnState(_ balancer.SubConn, _ balancer.SubConnState) {
11645
logger.Fatal("rls: UpdateSubConnState is not yet implemented")
11746
}
11847

119-
// Cleans up the resources allocated by the LB policy including the clientConn
120-
// to the RLS server.
121-
// Implements balancer.Balancer.
12248
func (lb *rlsBalancer) Close() {
123-
lb.mu.Lock()
124-
defer lb.mu.Unlock()
125-
126-
lb.done.Fire()
127-
if lb.rlsCC != nil {
128-
lb.rlsCC.Close()
129-
}
49+
logger.Fatal("rls: Close is not yet implemented")
13050
}
13151

13252
func (lb *rlsBalancer) ExitIdle() {
133-
// TODO: are we 100% sure this should be a nop?
134-
}
135-
136-
// updateControlChannel updates the RLS client if required.
137-
// Caller must hold lb.mu.
138-
func (lb *rlsBalancer) updateControlChannel(newCfg *lbConfig) {
139-
oldCfg := lb.lbCfg
140-
if newCfg.lookupService == oldCfg.lookupService && newCfg.lookupServiceTimeout == oldCfg.lookupServiceTimeout {
141-
return
142-
}
143-
144-
// Use RPC timeout from new config, if different from existing one.
145-
timeout := oldCfg.lookupServiceTimeout
146-
if timeout != newCfg.lookupServiceTimeout {
147-
timeout = newCfg.lookupServiceTimeout
148-
}
149-
150-
if newCfg.lookupService == oldCfg.lookupService {
151-
// This is the case where only the timeout has changed. We will continue
152-
// to use the existing clientConn. but will create a new rlsClient with
153-
// the new timeout.
154-
lb.rlsC = newRLSClientFunc(lb.rlsCC, lb.opts.Target.Endpoint, timeout)
155-
return
156-
}
157-
158-
// This is the case where the RLS server name has changed. We need to create
159-
// a new clientConn and close the old one.
160-
var dopts []grpc.DialOption
161-
if dialer := lb.opts.Dialer; dialer != nil {
162-
dopts = append(dopts, grpc.WithContextDialer(dialer))
163-
}
164-
dopts = append(dopts, dialCreds(lb.opts))
165-
166-
cc, err := grpc.Dial(newCfg.lookupService, dopts...)
167-
if err != nil {
168-
logger.Errorf("rls: dialRLS(%s, %v): %v", newCfg.lookupService, lb.opts, err)
169-
// An error from a non-blocking dial indicates something serious. We
170-
// should continue to use the old control channel if one exists, and
171-
// return so that the rest of the config updates can be processes.
172-
return
173-
}
174-
if lb.rlsCC != nil {
175-
lb.rlsCC.Close()
176-
}
177-
lb.rlsCC = cc
178-
lb.rlsC = newRLSClientFunc(cc, lb.opts.Target.Endpoint, timeout)
179-
}
180-
181-
func dialCreds(opts balancer.BuildOptions) grpc.DialOption {
182-
// The control channel should use the same authority as that of the parent
183-
// channel. This ensures that the identify of the RLS server and that of the
184-
// backend is the same, so if the RLS config is injected by an attacker, it
185-
// cannot cause leakage of private information contained in headers set by
186-
// the application.
187-
server := opts.Target.Authority
188-
switch {
189-
case opts.DialCreds != nil:
190-
if err := opts.DialCreds.OverrideServerName(server); err != nil {
191-
logger.Warningf("rls: OverrideServerName(%s) = (%v), using Insecure", server, err)
192-
return grpc.WithInsecure()
193-
}
194-
return grpc.WithTransportCredentials(opts.DialCreds)
195-
case opts.CredsBundle != nil:
196-
return grpc.WithTransportCredentials(opts.CredsBundle.TransportCredentials())
197-
default:
198-
logger.Warning("rls: no credentials available, using Insecure")
199-
return grpc.WithInsecure()
200-
}
53+
logger.Fatal("rls: ExitIdle is not yet implemented")
20154
}

0 commit comments

Comments
 (0)