Skip to content

Commit 57d5d3e

Browse files
author
Chao Xu
committed
Updates the proxy server to interact with the ClientSet.
The proxy server can now handle multiple backends, each connected to a different server.
1 parent 0af05b1 commit 57d5d3e

File tree

4 files changed

+223
-36
lines changed

4 files changed

+223
-36
lines changed

cmd/proxy/main.go

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ import (
2323
"flag"
2424
"fmt"
2525
"io/ioutil"
26-
"k8s.io/klog"
2726
"net"
2827
"net/http"
2928
"os"
3029
"os/signal"
3130
"syscall"
3231

32+
"k8s.io/klog"
33+
34+
"github.com/google/uuid"
3335
"github.com/prometheus/client_golang/prometheus"
3436
"github.com/spf13/cobra"
3537
"github.com/spf13/pflag"
@@ -79,6 +81,11 @@ type ProxyRunOptions struct {
7981
agentPort uint
8082
// Port we listen for admin connections on.
8183
adminPort uint
84+
85+
// ID of this server.
86+
serverID string
87+
// Number of proxy server instances, should be 1 unless it is a HA server.
88+
serverCount uint
8289
}
8390

8491
func (o *ProxyRunOptions) Flags() *pflag.FlagSet {
@@ -94,6 +101,8 @@ func (o *ProxyRunOptions) Flags() *pflag.FlagSet {
94101
flags.UintVar(&o.serverPort, "server-port", o.serverPort, "Port we listen for server connections on. Set to 0 for UDS.")
95102
flags.UintVar(&o.agentPort, "agent-port", o.agentPort, "Port we listen for agent connections on.")
96103
flags.UintVar(&o.adminPort, "admin-port", o.adminPort, "Port we listen for admin connections on.")
104+
flags.StringVar(&o.serverID, "server-id", o.serverID, "The unique ID of this server.")
105+
flags.UintVar(&o.serverCount, "server-count", o.serverCount, "The number of proxy server instances, should be 1 unless it is an HA server.")
97106
return flags
98107
}
99108

@@ -109,6 +118,8 @@ func (o *ProxyRunOptions) Print() {
109118
klog.Warningf("Server port set to %d.\n", o.serverPort)
110119
klog.Warningf("Agent port set to %d.\n", o.agentPort)
111120
klog.Warningf("Admin port set to %d.\n", o.adminPort)
121+
klog.Warningf("ServerID set to %s.\n", o.serverID)
122+
klog.Warningf("ServerCount set to %d.\n", o.serverCount)
112123
}
113124

114125
func (o *ProxyRunOptions) Validate() error {
@@ -207,6 +218,8 @@ func newProxyRunOptions() *ProxyRunOptions {
207218
serverPort: 8090,
208219
agentPort: 8091,
209220
adminPort: 8092,
221+
serverID: uuid.New().String(),
222+
serverCount: 1,
210223
}
211224
return &o
212225
}
@@ -234,7 +247,7 @@ func (p *Proxy) run(o *ProxyRunOptions) error {
234247
return fmt.Errorf("failed to validate server options with %v", err)
235248
}
236249
ctx, cancel := context.WithCancel(context.Background())
237-
server := agentserver.NewProxyServer()
250+
server := agentserver.NewProxyServer(o.serverID, int(o.serverCount))
238251

239252
klog.Info("Starting master server for client connections.")
240253
masterStop, err := p.runMasterServer(ctx, o, server)

pkg/agent/agentserver/server.go

Lines changed: 132 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,16 @@ package agentserver
1919
import (
2020
"fmt"
2121
"io"
22+
"math/rand"
2223
"net"
24+
"strconv"
25+
"sync"
26+
"time"
2327

28+
"google.golang.org/grpc/metadata"
2429
"k8s.io/klog"
2530
"sigs.k8s.io/apiserver-network-proxy/proto/agent"
31+
"sigs.k8s.io/apiserver-network-proxy/proto/header"
2632
)
2733

2834
// ProxyClientConnection...
@@ -56,22 +62,91 @@ func (c *ProxyClientConnection) send(pkt *agent.Packet) error {
5662

5763
// ProxyServer
5864
type ProxyServer struct {
59-
Backend agent.AgentService_ConnectServer
65+
mu sync.RWMutex //protects the following
66+
// A map between agentID and its grpc connections.
67+
// For a given agent, ProxyServer prefers backends[agentID][0] to send
68+
// traffic, because backends[agentID][1:] are more likely to be closed
69+
// by the agent to deduplicate connections to the same server.
70+
backends map[string][]agent.AgentService_ConnectServer
71+
agentIDs []string
72+
random *rand.Rand
6073

6174
// connID track
6275
Frontends map[int64]*ProxyClientConnection
6376
PendingDial map[int64]*ProxyClientConnection
77+
78+
serverID string // unique ID of this server
79+
serverCount int // Number of proxy server instances, should be 1 unless it is a HA server.
80+
6481
}
6582

6683
var _ agent.AgentServiceServer = &ProxyServer{}
6784

6885
var _ agent.ProxyServiceServer = &ProxyServer{}
6986

87+
func (s *ProxyServer) addBackend(agentID string, conn agent.AgentService_ConnectServer) {
88+
klog.Infof("register Backend %v for agentID %s", conn, agentID)
89+
s.mu.Lock()
90+
defer s.mu.Unlock()
91+
_, ok := s.backends[agentID]
92+
if ok {
93+
s.backends[agentID] = append(s.backends[agentID], conn)
94+
return
95+
}
96+
s.backends[agentID] = []agent.AgentService_ConnectServer{conn}
97+
s.agentIDs = append(s.agentIDs, agentID)
98+
}
99+
100+
func (s *ProxyServer) removeBackend(agentID string, conn agent.AgentService_ConnectServer) {
101+
klog.Infof("remove Backend %v for agentID %s", conn, agentID)
102+
s.mu.Lock()
103+
defer s.mu.Unlock()
104+
backends, ok := s.backends[agentID]
105+
if !ok {
106+
klog.Warningf("can't find agentID %s in the backends", agentID)
107+
return
108+
}
109+
var found bool
110+
for i, c := range backends {
111+
if c == conn {
112+
s.backends[agentID] = append(s.backends[agentID][:i], s.backends[agentID][i+1:]...)
113+
found = true
114+
}
115+
}
116+
if len(s.backends[agentID]) == 0 {
117+
delete(s.backends, agentID)
118+
for i := range s.agentIDs {
119+
if s.agentIDs[i] == agentID {
120+
s.agentIDs[i] = s.agentIDs[len(s.agentIDs)-1]
121+
s.agentIDs = s.agentIDs[:len(s.agentIDs)-1]
122+
break
123+
}
124+
}
125+
}
126+
if !found {
127+
klog.Warningf("can't find conn %v for agentID %s in the backends", conn, agentID)
128+
}
129+
}
130+
131+
func (s *ProxyServer) randomBackend() (agent.AgentService_ConnectServer, error) {
132+
s.mu.RLock()
133+
defer s.mu.RUnlock()
134+
if len(s.backends) == 0 {
135+
return nil, fmt.Errorf("No backend available")
136+
}
137+
agentID := s.agentIDs[s.random.Intn(len(s.agentIDs))]
138+
return s.backends[agentID][0], nil
139+
}
140+
70141
// NewProxyServer creates a new ProxyServer instance
71-
func NewProxyServer() *ProxyServer {
142+
func NewProxyServer(serverID string, serverCount int) *ProxyServer {
72143
return &ProxyServer{
73144
Frontends: make(map[int64]*ProxyClientConnection),
74145
PendingDial: make(map[int64]*ProxyClientConnection),
146+
serverID: serverID,
147+
serverCount: serverCount,
148+
backends: make(map[string][]agent.AgentService_ConnectServer),
149+
random: rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
75150
}
76151
}
77152

@@ -113,17 +188,25 @@ func (s *ProxyServer) serveRecvFrontend(stream agent.ProxyService_ProxyServer, r
113188
klog.Info("start serving frontend stream")
114189

115190
var firstConnID int64
191+
// The first packet should be a DIAL_REQ, we will randomly get a
192+
// backend from s.backends then.
193+
var backend agent.AgentService_ConnectServer
194+
var err error
116195

117196
for pkt := range recvCh {
118197
switch pkt.Type {
119198
case agent.PacketType_DIAL_REQ:
120199
klog.Info(">>> Received DIAL_REQ")
121-
if s.Backend == nil {
122-
klog.Info(">>> No backend found; drop")
200+
// TODO: if we track what agent has historically served
201+
// the address, then we can send the Dial_REQ to the
202+
// same agent. That way we save the agent from creating
203+
// a new connection to the address.
204+
backend, err = s.randomBackend()
205+
if err != nil {
206+
klog.Errorf(">>> failed to get a backend: %v", err)
123207
continue
124208
}
125-
126-
if err := s.Backend.Send(pkt); err != nil {
209+
if err := backend.Send(pkt); err != nil {
127210
klog.Warningf(">>> DIAL_REQ to Backend failed: %v", err)
128211
}
129212
s.PendingDial[pkt.GetDialRequest().Random] = &ProxyClientConnection{
@@ -134,16 +217,17 @@ func (s *ProxyServer) serveRecvFrontend(stream agent.ProxyService_ProxyServer, r
134217
klog.Info(">>> DIAL_REQ sent to backend") // got this. but backend didn't receive anything.
135218

136219
case agent.PacketType_CLOSE_REQ:
137-
klog.Infof(">>> Received CLOSE_REQ(id=%d)", pkt.GetCloseRequest().ConnectID)
138-
if s.Backend == nil {
139-
klog.Info(">>> No backend found; drop")
220+
connID := pkt.GetCloseRequest().ConnectID
221+
klog.Infof(">>> Received CLOSE_REQ(id=%d)", connID)
222+
if backend == nil {
223+
klog.Errorf("backend has not been initialized for connID %d. Client should send a Dial Request first.", connID)
140224
continue
141225
}
142-
143-
if err := s.Backend.Send(pkt); err != nil {
226+
if err := backend.Send(pkt); err != nil {
227+
// TODO: retry with other backends connecting to this agent.
144228
klog.Warningf(">>> CLOSE_REQ to Backend failed: %v", err)
145229
}
146-
klog.Info("CLOSE_REQ sent to backend")
230+
klog.Info(">>> CLOSE_REQ sent to backend")
147231

148232
case agent.PacketType_DATA:
149233
connID := pkt.GetData().ConnectID
@@ -154,12 +238,12 @@ func (s *ProxyServer) serveRecvFrontend(stream agent.ProxyService_ProxyServer, r
154238
klog.Warningf(">>> Data(id=%d) doesn't match first connection id %d", firstConnID, connID)
155239
}
156240

157-
if s.Backend == nil {
158-
klog.Info(">>> No backend found; drop")
241+
if backend == nil {
242+
klog.Errorf("backend has not been initialized for connID %d. Client should send a Dial Request first.", connID)
159243
continue
160244
}
161-
162-
if err := s.Backend.Send(pkt); err != nil {
245+
if err := backend.Send(pkt); err != nil {
246+
// TODO: retry with other backends connecting to this agent.
163247
klog.Warningf(">>> DATA to Backend failed: %v", err)
164248
}
165249
klog.Info(">>> DATA sent to backend")
@@ -179,10 +263,13 @@ func (s *ProxyServer) serveRecvFrontend(stream agent.ProxyService_ProxyServer, r
179263
},
180264
},
181265
}
182-
if s.Backend != nil {
183-
if err := s.Backend.Send(pkt); err != nil {
184-
klog.Warningf(">>> CLOSE_REQ to Backend failed: %v", err)
185-
}
266+
267+
if backend == nil {
268+
klog.Errorf("backend has not been initialized for connID %d. Client should send a Dial Request first.", firstConnID)
269+
return
270+
}
271+
if err := backend.Send(pkt); err != nil {
272+
klog.Warningf(">>> CLOSE_REQ to Backend failed: %v", err)
186273
}
187274
}
188275

@@ -196,20 +283,36 @@ func (s *ProxyServer) serveSend(stream agent.ProxyService_ProxyServer, sendCh <-
196283
}
197284
}
198285

286+
func agentID(stream agent.AgentService_ConnectServer) (string, error) {
287+
md, ok := metadata.FromIncomingContext(stream.Context())
288+
if !ok {
289+
return "", fmt.Errorf("failed to get context")
290+
}
291+
agentIDs := md.Get(header.AgentID)
292+
if len(agentIDs) != 1 {
293+
return "", fmt.Errorf("expected one agent ID in the context, got %v", agentIDs)
294+
}
295+
return agentIDs[0], nil
296+
}
297+
199298
// Connect is for agent to connect to ProxyServer as next hop
200299
func (s *ProxyServer) Connect(stream agent.AgentService_ConnectServer) error {
201-
klog.Info("connect request from Backend")
300+
agentID, err := agentID(stream)
301+
if err != nil {
302+
return err
303+
}
304+
klog.Infof("Connect request from agent %s", agentID)
305+
s.addBackend(agentID, stream)
306+
defer s.removeBackend(agentID, stream)
307+
308+
h := metadata.Pairs(header.ServerID, s.serverID, header.ServerCount, strconv.Itoa(s.serverCount))
309+
if err := stream.SendHeader(h); err != nil {
310+
return err
311+
}
202312

203313
recvCh := make(chan *agent.Packet, 10)
204314
stopCh := make(chan error)
205315

206-
klog.Infof("register Backend %v", stream)
207-
s.Backend = stream
208-
defer func() {
209-
klog.Infof("unregister Backend %v", stream)
210-
s.Backend = nil
211-
}()
212-
213316
go s.serveRecvBackend(stream, recvCh)
214317

215318
defer func() {
@@ -225,6 +328,7 @@ func (s *ProxyServer) Connect(stream agent.AgentService_ConnectServer) error {
225328
}
226329
if err != nil {
227330
klog.Warningf("stream read error: %v", err)
331+
close(stopCh)
228332
return
229333
}
230334

pkg/agent/agentserver/server_test.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
Copyright 2019 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package agentserver
18+
19+
import (
20+
"reflect"
21+
"testing"
22+
23+
"sigs.k8s.io/apiserver-network-proxy/proto/agent"
24+
)
25+
26+
type fakeAgentService_ConnectServer struct {
27+
agent.AgentService_ConnectServer
28+
}
29+
30+
func TestAddRemoveBackends(t *testing.T) {
31+
conn1 := new(fakeAgentService_ConnectServer)
32+
conn12 := new(fakeAgentService_ConnectServer)
33+
conn2 := new(fakeAgentService_ConnectServer)
34+
conn22 := new(fakeAgentService_ConnectServer)
35+
conn3 := new(fakeAgentService_ConnectServer)
36+
37+
p := NewProxyServer("", 1)
38+
p.addBackend("agent1", conn1)
39+
p.removeBackend("agent1", conn1)
40+
expectedBackends := make(map[string][]agent.AgentService_ConnectServer)
41+
expectedAgentIDs := []string{}
42+
if e, a := expectedBackends, p.backends; !reflect.DeepEqual(e, a) {
43+
t.Errorf("expected %v, got %v", e, a)
44+
}
45+
if e, a := expectedAgentIDs, p.agentIDs; !reflect.DeepEqual(e, a) {
46+
t.Errorf("expected %v, got %v", e, a)
47+
}
48+
49+
p = NewProxyServer("", 1)
50+
p.addBackend("agent1", conn1)
51+
p.addBackend("agent1", conn12)
52+
p.addBackend("agent2", conn2)
53+
p.addBackend("agent2", conn22)
54+
p.addBackend("agent3", conn3)
55+
p.removeBackend("agent2", conn2)
56+
p.removeBackend("agent2", conn22)
57+
p.removeBackend("agent1", conn1)
58+
expectedBackends = map[string][]agent.AgentService_ConnectServer{
59+
"agent1": []agent.AgentService_ConnectServer{conn12},
60+
"agent3": []agent.AgentService_ConnectServer{conn3},
61+
}
62+
expectedAgentIDs = []string{"agent1", "agent3"}
63+
if e, a := expectedBackends, p.backends; !reflect.DeepEqual(e, a) {
64+
t.Errorf("expected %v, got %v", e, a)
65+
}
66+
if e, a := expectedAgentIDs, p.agentIDs; !reflect.DeepEqual(e, a) {
67+
t.Errorf("expected %v, got %v", e, a)
68+
}
69+
}

0 commit comments

Comments
 (0)