Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
c5ca0322
Commit
c5ca0322
authored
Jul 30, 2014
by
Daniel Smith
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #706 from brendandburns/proxy
Revert the revert and fix tests.
parents
59c5fb11
2cbe2c18
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
216 additions
and
22 deletions
+216
-22
proxier.go
pkg/proxy/proxier.go
+107
-14
proxier_test.go
pkg/proxy/proxier_test.go
+109
-8
No files found.
pkg/proxy/proxier.go
View file @
c5ca0322
...
@@ -20,22 +20,34 @@ import (
...
@@ -20,22 +20,34 @@ import (
"fmt"
"fmt"
"io"
"io"
"net"
"net"
"strconv"
"sync"
"time"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/golang/glog"
"github.com/golang/glog"
)
)
type
serviceInfo
struct
{
port
int
active
bool
listener
net
.
Listener
lock
sync
.
Mutex
}
// Proxier is a simple proxy for tcp connections between a localhost:lport and services that provide
// Proxier is a simple proxy for tcp connections between a localhost:lport and services that provide
// the actual implementations.
// the actual implementations.
type
Proxier
struct
{
type
Proxier
struct
{
loadBalancer
LoadBalancer
loadBalancer
LoadBalancer
serviceMap
map
[
string
]
int
serviceMap
map
[
string
]
*
serviceInfo
// protects 'serviceMap'
serviceLock
sync
.
Mutex
}
}
// NewProxier returns a newly created and correctly initialized instance of Proxier.
// NewProxier returns a newly created and correctly initialized instance of Proxier.
func
NewProxier
(
loadBalancer
LoadBalancer
)
*
Proxier
{
func
NewProxier
(
loadBalancer
LoadBalancer
)
*
Proxier
{
return
&
Proxier
{
loadBalancer
:
loadBalancer
,
serviceMap
:
make
(
map
[
string
]
int
)}
return
&
Proxier
{
loadBalancer
:
loadBalancer
,
serviceMap
:
make
(
map
[
string
]
*
serviceInfo
)}
}
}
func
copyBytes
(
in
,
out
*
net
.
TCPConn
)
{
func
copyBytes
(
in
,
out
*
net
.
TCPConn
)
{
...
@@ -59,10 +71,52 @@ func proxyConnection(in, out *net.TCPConn) {
...
@@ -59,10 +71,52 @@ func proxyConnection(in, out *net.TCPConn) {
go
copyBytes
(
out
,
in
)
go
copyBytes
(
out
,
in
)
}
}
// StopProxy stops a proxy for the named service. It stops the proxy loop and closes the socket.
func
(
proxier
*
Proxier
)
StopProxy
(
service
string
)
error
{
// TODO: delete from map here?
info
,
found
:=
proxier
.
getServiceInfo
(
service
)
if
!
found
{
return
fmt
.
Errorf
(
"unknown service: %s"
,
service
)
}
info
.
lock
.
Lock
()
defer
info
.
lock
.
Unlock
()
return
proxier
.
stopProxyInternal
(
info
)
}
// Requires that info.lock be held before calling.
func
(
proxier
*
Proxier
)
stopProxyInternal
(
info
*
serviceInfo
)
error
{
info
.
active
=
false
return
info
.
listener
.
Close
()
}
func
(
proxier
*
Proxier
)
getServiceInfo
(
service
string
)
(
*
serviceInfo
,
bool
)
{
proxier
.
serviceLock
.
Lock
()
defer
proxier
.
serviceLock
.
Unlock
()
info
,
ok
:=
proxier
.
serviceMap
[
service
]
return
info
,
ok
}
func
(
proxier
*
Proxier
)
setServiceInfo
(
service
string
,
info
*
serviceInfo
)
{
proxier
.
serviceLock
.
Lock
()
defer
proxier
.
serviceLock
.
Unlock
()
proxier
.
serviceMap
[
service
]
=
info
}
// AcceptHandler begins accepting incoming connections from listener and proxying the connections to the load-balanced endpoints.
// AcceptHandler begins accepting incoming connections from listener and proxying the connections to the load-balanced endpoints.
// It never returns.
// It never returns.
func
(
proxier
Proxier
)
AcceptHandler
(
service
string
,
listener
net
.
Listener
)
{
func
(
proxier
*
Proxier
)
AcceptHandler
(
service
string
,
listener
net
.
Listener
)
{
info
,
found
:=
proxier
.
getServiceInfo
(
service
)
if
!
found
{
glog
.
Errorf
(
"Failed to find service: %s"
,
service
)
return
}
for
{
for
{
info
.
lock
.
Lock
()
if
!
info
.
active
{
info
.
lock
.
Unlock
()
break
}
info
.
lock
.
Unlock
()
inConn
,
err
:=
listener
.
Accept
()
inConn
,
err
:=
listener
.
Accept
()
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Errorf
(
"Accept failed: %v"
,
err
)
glog
.
Errorf
(
"Accept failed: %v"
,
err
)
...
@@ -92,30 +146,47 @@ func (proxier Proxier) AcceptHandler(service string, listener net.Listener) {
...
@@ -92,30 +146,47 @@ func (proxier Proxier) AcceptHandler(service string, listener net.Listener) {
}
}
// addService starts listening for a new service on a given port.
// addService starts listening for a new service on a given port.
func
(
proxier
Proxier
)
addService
(
service
string
,
port
int
)
error
{
func
(
proxier
*
Proxier
)
addService
(
service
string
,
port
int
)
(
net
.
Listener
,
error
)
{
// Make sure we can start listening on the port before saying all's well.
// Make sure we can start listening on the port before saying all's well.
l
,
err
:=
net
.
Listen
(
"tcp"
,
fmt
.
Sprintf
(
":%d"
,
port
))
l
,
err
:=
net
.
Listen
(
"tcp"
,
fmt
.
Sprintf
(
":%d"
,
port
))
if
err
!=
nil
{
if
err
!=
nil
{
return
err
return
nil
,
err
}
}
proxier
.
addServiceCommon
(
service
,
l
)
proxier
.
addServiceCommon
(
service
,
l
)
return
nil
return
l
,
nil
}
}
// used to globally lock around unused ports. Only used in testing.
var
unusedPortLock
sync
.
Mutex
// addService starts listening for a new service, returning the port it's using.
// addService starts listening for a new service, returning the port it's using.
// For testing on a system with unknown ports used.
// For testing on a system with unknown ports used.
func
(
proxier
Proxier
)
addServiceOnUnusedPort
(
service
string
)
(
string
,
error
)
{
func
(
proxier
*
Proxier
)
addServiceOnUnusedPort
(
service
string
)
(
string
,
error
)
{
unusedPortLock
.
Lock
()
defer
unusedPortLock
.
Unlock
()
// Make sure we can start listening on the port before saying all's well.
// Make sure we can start listening on the port before saying all's well.
l
,
err
:=
net
.
Listen
(
"tcp"
,
":0"
)
l
,
err
:=
net
.
Listen
(
"tcp"
,
":0"
)
if
err
!=
nil
{
if
err
!=
nil
{
return
""
,
err
return
""
,
err
}
}
proxier
.
addServiceCommon
(
service
,
l
)
_
,
port
,
err
:=
net
.
SplitHostPort
(
l
.
Addr
()
.
String
())
_
,
port
,
err
:=
net
.
SplitHostPort
(
l
.
Addr
()
.
String
())
if
err
!=
nil
{
return
""
,
err
}
portNum
,
err
:=
strconv
.
Atoi
(
port
)
if
err
!=
nil
{
return
""
,
err
}
proxier
.
setServiceInfo
(
service
,
&
serviceInfo
{
port
:
portNum
,
active
:
true
,
listener
:
l
,
})
proxier
.
addServiceCommon
(
service
,
l
)
return
port
,
nil
return
port
,
nil
}
}
func
(
proxier
Proxier
)
addServiceCommon
(
service
string
,
l
net
.
Listener
)
{
func
(
proxier
*
Proxier
)
addServiceCommon
(
service
string
,
l
net
.
Listener
)
{
glog
.
Infof
(
"Listening for %s on %s"
,
service
,
l
.
Addr
()
.
String
())
glog
.
Infof
(
"Listening for %s on %s"
,
service
,
l
.
Addr
()
.
String
())
// If that succeeds, start the accepting loop.
// If that succeeds, start the accepting loop.
go
proxier
.
AcceptHandler
(
service
,
l
)
go
proxier
.
AcceptHandler
(
service
,
l
)
...
@@ -123,19 +194,41 @@ func (proxier Proxier) addServiceCommon(service string, l net.Listener) {
...
@@ -123,19 +194,41 @@ func (proxier Proxier) addServiceCommon(service string, l net.Listener) {
// OnUpdate receives update notices for the updated services and start listening newly added services.
// OnUpdate receives update notices for the updated services and start listening newly added services.
// It implements "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config".ServiceConfigHandler.OnUpdate.
// It implements "github.com/GoogleCloudPlatform/kubernetes/pkg/proxy/config".ServiceConfigHandler.OnUpdate.
func
(
proxier
Proxier
)
OnUpdate
(
services
[]
api
.
Service
)
{
func
(
proxier
*
Proxier
)
OnUpdate
(
services
[]
api
.
Service
)
{
glog
.
Infof
(
"Received update notice: %+v"
,
services
)
glog
.
Infof
(
"Received update notice: %+v"
,
services
)
serviceNames
:=
util
.
StringSet
{}
for
_
,
service
:=
range
services
{
for
_
,
service
:=
range
services
{
port
,
exists
:=
proxier
.
serviceMap
[
service
.
ID
]
serviceNames
.
Insert
(
service
.
ID
)
if
exists
&&
port
==
service
.
Port
{
info
,
exists
:=
proxier
.
getServiceInfo
(
service
.
ID
)
if
exists
&&
info
.
port
==
service
.
Port
{
continue
continue
}
}
if
exists
{
// Stop the old proxier.
proxier
.
StopProxy
(
service
.
ID
)
}
glog
.
Infof
(
"Adding a new service %s on port %d"
,
service
.
ID
,
service
.
Port
)
glog
.
Infof
(
"Adding a new service %s on port %d"
,
service
.
ID
,
service
.
Port
)
err
:=
proxier
.
addService
(
service
.
ID
,
service
.
Port
)
listener
,
err
:=
proxier
.
addService
(
service
.
ID
,
service
.
Port
)
if
err
!=
nil
{
if
err
!=
nil
{
glog
.
Infof
(
"Failed to start listening for %s on %d"
,
service
.
ID
,
service
.
Port
)
glog
.
Infof
(
"Failed to start listening for %s on %d"
,
service
.
ID
,
service
.
Port
)
continue
continue
}
}
proxier
.
serviceMap
[
service
.
ID
]
=
service
.
Port
proxier
.
setServiceInfo
(
service
.
ID
,
&
serviceInfo
{
port
:
service
.
Port
,
active
:
true
,
listener
:
listener
,
})
}
proxier
.
serviceLock
.
Lock
()
defer
proxier
.
serviceLock
.
Unlock
()
for
name
,
info
:=
range
proxier
.
serviceMap
{
info
.
lock
.
Lock
()
if
!
serviceNames
.
Has
(
name
)
&&
info
.
active
{
glog
.
Infof
(
"Removing service: %s"
,
name
)
proxier
.
stopProxyInternal
(
info
)
}
info
.
lock
.
Unlock
()
}
}
}
}
pkg/proxy/proxier_test.go
View file @
c5ca0322
...
@@ -20,7 +20,9 @@ import (
...
@@ -20,7 +20,9 @@ import (
"fmt"
"fmt"
"io"
"io"
"net"
"net"
"strconv"
"testing"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
)
)
...
@@ -45,6 +47,24 @@ func echoServer(t *testing.T, addr string) (string, error) {
...
@@ -45,6 +47,24 @@ func echoServer(t *testing.T, addr string) (string, error) {
return
port
,
err
return
port
,
err
}
}
func
testEchoConnection
(
t
*
testing
.
T
,
address
,
port
string
)
{
conn
,
err
:=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
address
,
port
))
if
err
!=
nil
{
t
.
Fatalf
(
"error connecting to proxy: %v"
,
err
)
}
magic
:=
"aaaaa"
if
_
,
err
:=
conn
.
Write
([]
byte
(
magic
));
err
!=
nil
{
t
.
Fatalf
(
"error writing to proxy: %v"
,
err
)
}
buf
:=
make
([]
byte
,
5
)
if
_
,
err
:=
conn
.
Read
(
buf
);
err
!=
nil
{
t
.
Fatalf
(
"error reading from proxy: %v"
,
err
)
}
if
string
(
buf
)
!=
magic
{
t
.
Fatalf
(
"bad echo from proxy: got: %q, expected %q"
,
string
(
buf
),
magic
)
}
}
func
TestProxy
(
t
*
testing
.
T
)
{
func
TestProxy
(
t
*
testing
.
T
)
{
port
,
err
:=
echoServer
(
t
,
"127.0.0.1:0"
)
port
,
err
:=
echoServer
(
t
,
"127.0.0.1:0"
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -61,19 +81,100 @@ func TestProxy(t *testing.T) {
...
@@ -61,19 +81,100 @@ func TestProxy(t *testing.T) {
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"error adding new service: %#v"
,
err
)
t
.
Fatalf
(
"error adding new service: %#v"
,
err
)
}
}
testEchoConnection
(
t
,
"127.0.0.1"
,
proxyPort
)
}
func
TestProxyStop
(
t
*
testing
.
T
)
{
port
,
err
:=
echoServer
(
t
,
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
lb
:=
NewLoadBalancerRR
()
lb
.
OnUpdate
([]
api
.
Endpoints
{{
JSONBase
:
api
.
JSONBase
{
ID
:
"echo"
},
Endpoints
:
[]
string
{
net
.
JoinHostPort
(
"127.0.0.1"
,
port
)}}})
p
:=
NewProxier
(
lb
)
proxyPort
,
err
:=
p
.
addServiceOnUnusedPort
(
"echo"
)
if
err
!=
nil
{
t
.
Fatalf
(
"error adding new service: %#v"
,
err
)
}
conn
,
err
:=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
conn
,
err
:=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
if
err
!=
nil
{
if
err
!=
nil
{
t
.
Fatalf
(
"error connecting to proxy: %v"
,
err
)
t
.
Fatalf
(
"error connecting to proxy: %v"
,
err
)
}
}
magic
:=
"aaaaa"
conn
.
Close
()
if
_
,
err
:=
conn
.
Write
([]
byte
(
magic
));
err
!=
nil
{
t
.
Fatalf
(
"error writing to proxy: %v"
,
err
)
p
.
StopProxy
(
"echo"
)
// Wait for the port to really close.
time
.
Sleep
(
2
*
time
.
Second
)
_
,
err
=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
if
err
==
nil
{
t
.
Fatalf
(
"Unexpected non-error."
)
}
}
buf
:=
make
([]
byte
,
5
)
}
if
_
,
err
:=
conn
.
Read
(
buf
);
err
!=
nil
{
t
.
Fatalf
(
"error reading from proxy: %v"
,
err
)
func
TestProxyUpdateDelete
(
t
*
testing
.
T
)
{
port
,
err
:=
echoServer
(
t
,
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
}
if
string
(
buf
)
!=
magic
{
t
.
Fatalf
(
"bad echo from proxy: got: %q, expected %q"
,
string
(
buf
),
magic
)
lb
:=
NewLoadBalancerRR
()
lb
.
OnUpdate
([]
api
.
Endpoints
{{
JSONBase
:
api
.
JSONBase
{
ID
:
"echo"
},
Endpoints
:
[]
string
{
net
.
JoinHostPort
(
"127.0.0.1"
,
port
)}}})
p
:=
NewProxier
(
lb
)
proxyPort
,
err
:=
p
.
addServiceOnUnusedPort
(
"echo"
)
if
err
!=
nil
{
t
.
Fatalf
(
"error adding new service: %#v"
,
err
)
}
conn
,
err
:=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
if
err
!=
nil
{
t
.
Fatalf
(
"error connecting to proxy: %v"
,
err
)
}
conn
.
Close
()
p
.
OnUpdate
([]
api
.
Service
{})
// Wait for the port to close.
time
.
Sleep
(
2
*
time
.
Second
)
_
,
err
=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
if
err
==
nil
{
t
.
Fatalf
(
"Unexpected non-error."
)
}
}
func
TestProxyUpdatePort
(
t
*
testing
.
T
)
{
port
,
err
:=
echoServer
(
t
,
"127.0.0.1:0"
)
if
err
!=
nil
{
t
.
Fatal
(
err
)
}
lb
:=
NewLoadBalancerRR
()
lb
.
OnUpdate
([]
api
.
Endpoints
{{
JSONBase
:
api
.
JSONBase
{
ID
:
"echo"
},
Endpoints
:
[]
string
{
net
.
JoinHostPort
(
"127.0.0.1"
,
port
)}}})
p
:=
NewProxier
(
lb
)
proxyPort
,
err
:=
p
.
addServiceOnUnusedPort
(
"echo"
)
if
err
!=
nil
{
t
.
Fatalf
(
"error adding new service: %#v"
,
err
)
}
// add a new dummy listener in order to get a port that is free
l
,
_
:=
net
.
Listen
(
"tcp"
,
":0"
)
_
,
port
,
_
=
net
.
SplitHostPort
(
l
.
Addr
()
.
String
())
portNum
,
_
:=
strconv
.
Atoi
(
port
)
l
.
Close
()
// Wait for the socket to actually get free.
time
.
Sleep
(
2
*
time
.
Second
)
p
.
OnUpdate
([]
api
.
Service
{
{
JSONBase
:
api
.
JSONBase
{
ID
:
"echo"
},
Port
:
portNum
},
})
time
.
Sleep
(
2
*
time
.
Second
)
_
,
err
=
net
.
Dial
(
"tcp"
,
net
.
JoinHostPort
(
"127.0.0.1"
,
proxyPort
))
if
err
==
nil
{
t
.
Fatalf
(
"Unexpected non-error."
)
}
}
testEchoConnection
(
t
,
"127.0.0.1"
,
port
)
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment