mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 06:27:05 +00:00
Simplify proxy config for Endpoints by removing Mux.
This commit is contained in:
parent
8d7ba2bea2
commit
596527dafa
@ -308,14 +308,14 @@ func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, err
|
|||||||
serviceConfig := proxyconfig.NewServiceConfig()
|
serviceConfig := proxyconfig.NewServiceConfig()
|
||||||
serviceConfig.RegisterHandler(proxier)
|
serviceConfig.RegisterHandler(proxier)
|
||||||
|
|
||||||
endpointsConfig := proxyconfig.NewEndpointsConfig()
|
endpointsConfig := proxyconfig.NewEndpointsConfig(client.Core().RESTClient(), config.ConfigSyncPeriod)
|
||||||
endpointsConfig.RegisterHandler(endpointsHandler)
|
endpointsConfig.RegisterHandler(endpointsHandler)
|
||||||
|
go endpointsConfig.Run(wait.NeverStop)
|
||||||
|
|
||||||
proxyconfig.NewSourceAPI(
|
proxyconfig.NewSourceAPI(
|
||||||
client.Core().RESTClient(),
|
client.Core().RESTClient(),
|
||||||
config.ConfigSyncPeriod,
|
config.ConfigSyncPeriod,
|
||||||
serviceConfig.Channel("api"),
|
serviceConfig.Channel("api"),
|
||||||
endpointsConfig.Channel("api"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
config.NodeRef = &clientv1.ObjectReference{
|
config.NodeRef = &clientv1.ObjectReference{
|
||||||
|
@ -18,6 +18,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/util/sets"
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/apiserver/pkg/util/flag"
|
"k8s.io/apiserver/pkg/util/flag"
|
||||||
@ -138,7 +139,7 @@ func main() {
|
|||||||
serviceConfig := proxyconfig.NewServiceConfig()
|
serviceConfig := proxyconfig.NewServiceConfig()
|
||||||
serviceConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
serviceConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
||||||
|
|
||||||
endpointsConfig := proxyconfig.NewEndpointsConfig()
|
endpointsConfig := proxyconfig.NewEndpointsConfig(internalClientset.Core().RESTClient(), 15*time.Minute)
|
||||||
endpointsConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
endpointsConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
||||||
|
|
||||||
eventClient, err := clientgoclientset.NewForConfig(clientConfig)
|
eventClient, err := clientgoclientset.NewForConfig(clientConfig)
|
||||||
|
@ -41,6 +41,7 @@ go_library(
|
|||||||
"//vendor:github.com/golang/glog",
|
"//vendor:github.com/golang/glog",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/types",
|
"//vendor:k8s.io/apimachinery/pkg/types",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||||
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
|
"//vendor:k8s.io/client-go/kubernetes/typed/core/v1",
|
||||||
"//vendor:k8s.io/client-go/pkg/api/v1",
|
"//vendor:k8s.io/client-go/pkg/api/v1",
|
||||||
"//vendor:k8s.io/client-go/tools/record",
|
"//vendor:k8s.io/client-go/tools/record",
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
clientv1 "k8s.io/client-go/pkg/api/v1"
|
clientv1 "k8s.io/client-go/pkg/api/v1"
|
||||||
"k8s.io/client-go/tools/record"
|
"k8s.io/client-go/tools/record"
|
||||||
@ -71,11 +72,12 @@ func NewHollowProxyOrDie(
|
|||||||
UID: types.UID(nodeName),
|
UID: types.UID(nodeName),
|
||||||
Namespace: "",
|
Namespace: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
go endpointsConfig.Run(wait.NeverStop)
|
||||||
proxyconfig.NewSourceAPI(
|
proxyconfig.NewSourceAPI(
|
||||||
client.Core().RESTClient(),
|
client.Core().RESTClient(),
|
||||||
15*time.Minute,
|
15*time.Minute,
|
||||||
serviceConfig.Channel("api"),
|
serviceConfig.Channel("api"),
|
||||||
endpointsConfig.Channel("api"),
|
|
||||||
)
|
)
|
||||||
|
|
||||||
hollowProxy, err := proxyapp.NewProxyServer(client, eventClient, config, iptInterface, &FakeProxier{}, broadcaster, recorder, nil, "fake")
|
hollowProxy, err := proxyapp.NewProxyServer(client, eventClient, config, iptInterface, &FakeProxier{}, broadcaster, recorder, nil, "fake")
|
||||||
|
@ -18,11 +18,13 @@ go_library(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
|
"//pkg/client/listers/core/internalversion:go_default_library",
|
||||||
"//pkg/util/config:go_default_library",
|
"//pkg/util/config:go_default_library",
|
||||||
"//vendor:github.com/davecgh/go-spew/spew",
|
"//vendor:github.com/davecgh/go-spew/spew",
|
||||||
"//vendor:github.com/golang/glog",
|
"//vendor:github.com/golang/glog",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/fields",
|
"//vendor:k8s.io/apimachinery/pkg/fields",
|
||||||
|
"//vendor:k8s.io/apimachinery/pkg/labels",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/types",
|
"//vendor:k8s.io/apimachinery/pkg/types",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
"//vendor:k8s.io/apimachinery/pkg/util/runtime",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||||
|
@ -29,31 +29,24 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// NewSourceAPI creates config source that watches for changes to the services and endpoints.
|
// NewSourceAPI creates config source that watches for changes to the services and endpoints.
|
||||||
func NewSourceAPI(c cache.Getter, period time.Duration, servicesChan chan<- ServiceUpdate, endpointsChan chan<- EndpointsUpdate) {
|
func NewSourceAPI(c cache.Getter, period time.Duration, servicesChan chan<- ServiceUpdate) {
|
||||||
servicesLW := cache.NewListWatchFromClient(c, "services", metav1.NamespaceAll, fields.Everything())
|
servicesLW := cache.NewListWatchFromClient(c, "services", metav1.NamespaceAll, fields.Everything())
|
||||||
endpointsLW := cache.NewListWatchFromClient(c, "endpoints", metav1.NamespaceAll, fields.Everything())
|
newSourceAPI(servicesLW, period, servicesChan, wait.NeverStop)
|
||||||
newSourceAPI(servicesLW, endpointsLW, period, servicesChan, endpointsChan, wait.NeverStop)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func newSourceAPI(
|
func newSourceAPI(
|
||||||
servicesLW cache.ListerWatcher,
|
servicesLW cache.ListerWatcher,
|
||||||
endpointsLW cache.ListerWatcher,
|
|
||||||
period time.Duration,
|
period time.Duration,
|
||||||
servicesChan chan<- ServiceUpdate,
|
servicesChan chan<- ServiceUpdate,
|
||||||
endpointsChan chan<- EndpointsUpdate,
|
|
||||||
stopCh <-chan struct{}) {
|
stopCh <-chan struct{}) {
|
||||||
serviceController := NewServiceController(servicesLW, period, servicesChan)
|
serviceController := NewServiceController(servicesLW, period, servicesChan)
|
||||||
go serviceController.Run(stopCh)
|
go serviceController.Run(stopCh)
|
||||||
|
|
||||||
endpointsController := NewEndpointsController(endpointsLW, period, endpointsChan)
|
if !cache.WaitForCacheSync(stopCh, serviceController.HasSynced) {
|
||||||
go endpointsController.Run(stopCh)
|
|
||||||
|
|
||||||
if !cache.WaitForCacheSync(stopCh, serviceController.HasSynced, endpointsController.HasSynced) {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("source controllers not synced"))
|
utilruntime.HandleError(fmt.Errorf("source controllers not synced"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
servicesChan <- ServiceUpdate{Op: SYNCED}
|
servicesChan <- ServiceUpdate{Op: SYNCED}
|
||||||
endpointsChan <- EndpointsUpdate{Op: SYNCED}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendAddService(servicesChan chan<- ServiceUpdate) func(obj interface{}) {
|
func sendAddService(servicesChan chan<- ServiceUpdate) func(obj interface{}) {
|
||||||
@ -99,49 +92,6 @@ func sendDeleteService(servicesChan chan<- ServiceUpdate) func(obj interface{})
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendAddEndpoints(endpointsChan chan<- EndpointsUpdate) func(obj interface{}) {
|
|
||||||
return func(obj interface{}) {
|
|
||||||
endpoints, ok := obj.(*api.Endpoints)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Endpoints: %v", obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
endpointsChan <- EndpointsUpdate{Op: ADD, Endpoints: endpoints}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendUpdateEndpoints(endpointsChan chan<- EndpointsUpdate) func(oldObj, newObj interface{}) {
|
|
||||||
return func(_, newObj interface{}) {
|
|
||||||
endpoints, ok := newObj.(*api.Endpoints)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Endpoints: %v", newObj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
endpointsChan <- EndpointsUpdate{Op: UPDATE, Endpoints: endpoints}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendDeleteEndpoints(endpointsChan chan<- EndpointsUpdate) func(obj interface{}) {
|
|
||||||
return func(obj interface{}) {
|
|
||||||
var endpoints *api.Endpoints
|
|
||||||
switch t := obj.(type) {
|
|
||||||
case *api.Endpoints:
|
|
||||||
endpoints = t
|
|
||||||
case cache.DeletedFinalStateUnknown:
|
|
||||||
var ok bool
|
|
||||||
endpoints, ok = t.Obj.(*api.Endpoints)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Endpoints: %v", t.Obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Endpoints: %v", obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
endpointsChan <- EndpointsUpdate{Op: REMOVE, Endpoints: endpoints}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServiceController creates a controller that is watching services and sending
|
// NewServiceController creates a controller that is watching services and sending
|
||||||
// updates into ServiceUpdate channel.
|
// updates into ServiceUpdate channel.
|
||||||
func NewServiceController(lw cache.ListerWatcher, period time.Duration, ch chan<- ServiceUpdate) cache.Controller {
|
func NewServiceController(lw cache.ListerWatcher, period time.Duration, ch chan<- ServiceUpdate) cache.Controller {
|
||||||
@ -157,19 +107,3 @@ func NewServiceController(lw cache.ListerWatcher, period time.Duration, ch chan<
|
|||||||
)
|
)
|
||||||
return serviceController
|
return serviceController
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEndpointsController creates a controller that is watching endpoints and sending
|
|
||||||
// updates into EndpointsUpdate channel.
|
|
||||||
func NewEndpointsController(lw cache.ListerWatcher, period time.Duration, ch chan<- EndpointsUpdate) cache.Controller {
|
|
||||||
_, endpointsController := cache.NewInformer(
|
|
||||||
lw,
|
|
||||||
&api.Endpoints{},
|
|
||||||
period,
|
|
||||||
cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: sendAddEndpoints(ch),
|
|
||||||
UpdateFunc: sendUpdateEndpoints(ch),
|
|
||||||
DeleteFunc: sendDeleteEndpoints(ch),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return endpointsController
|
|
||||||
}
|
|
||||||
|
@ -166,68 +166,40 @@ func TestNewEndpointsSourceApi_UpdatesAndMultipleEndpoints(t *testing.T) {
|
|||||||
watchResp: fakeWatch,
|
watchResp: fakeWatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan EndpointsUpdate)
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
endpointsController := NewEndpointsController(lw, 30*time.Second, ch)
|
ch := make(chan struct{})
|
||||||
go endpointsController.Run(wait.NeverStop)
|
handler := newEpsHandler(t, nil, func() { ch <- struct{}{} })
|
||||||
|
|
||||||
|
endpointsConfig := newEndpointsConfig(lw, time.Minute)
|
||||||
|
endpointsConfig.RegisterHandler(handler)
|
||||||
|
go endpointsConfig.Run(stopCh)
|
||||||
|
|
||||||
// Add the first endpoints
|
// Add the first endpoints
|
||||||
|
handler.expected = []*api.Endpoints{endpoints1v1}
|
||||||
fakeWatch.Add(endpoints1v1)
|
fakeWatch.Add(endpoints1v1)
|
||||||
got, ok := <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected := EndpointsUpdate{Op: ADD, Endpoints: endpoints1v1}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v; Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add another endpoints
|
// Add another endpoints
|
||||||
|
handler.expected = []*api.Endpoints{endpoints1v1, endpoints2}
|
||||||
fakeWatch.Add(endpoints2)
|
fakeWatch.Add(endpoints2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
// Could be sorted either of these two ways:
|
|
||||||
expected = EndpointsUpdate{Op: ADD, Endpoints: endpoints2}
|
|
||||||
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify endpoints1
|
// Modify endpoints1
|
||||||
|
handler.expected = []*api.Endpoints{endpoints1v2, endpoints2}
|
||||||
fakeWatch.Modify(endpoints1v2)
|
fakeWatch.Modify(endpoints1v2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = EndpointsUpdate{Op: UPDATE, Endpoints: endpoints1v2}
|
|
||||||
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete endpoints1
|
// Delete endpoints1
|
||||||
|
handler.expected = []*api.Endpoints{endpoints2}
|
||||||
fakeWatch.Delete(endpoints1v2)
|
fakeWatch.Delete(endpoints1v2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = EndpointsUpdate{Op: REMOVE, Endpoints: endpoints1v2}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete endpoints2
|
// Delete endpoints2
|
||||||
|
handler.expected = []*api.Endpoints{}
|
||||||
fakeWatch.Delete(endpoints2)
|
fakeWatch.Delete(endpoints2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = EndpointsUpdate{Op: REMOVE, Endpoints: endpoints2}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type svcHandler struct {
|
type svcHandler struct {
|
||||||
@ -286,13 +258,6 @@ func TestInitialSync(t *testing.T) {
|
|||||||
// Wait for both services and endpoints handler.
|
// Wait for both services and endpoints handler.
|
||||||
wg.Add(2)
|
wg.Add(2)
|
||||||
|
|
||||||
svcConfig := NewServiceConfig()
|
|
||||||
epsConfig := NewEndpointsConfig()
|
|
||||||
svcHandler := newSvcHandler(t, []api.Service{*svc2, *svc1}, wg.Done)
|
|
||||||
svcConfig.RegisterHandler(svcHandler)
|
|
||||||
epsHandler := newEpsHandler(t, []*api.Endpoints{eps2, eps1}, wg.Done)
|
|
||||||
epsConfig.RegisterHandler(epsHandler)
|
|
||||||
|
|
||||||
// Setup fake api client.
|
// Setup fake api client.
|
||||||
fakeSvcWatch := watch.NewFake()
|
fakeSvcWatch := watch.NewFake()
|
||||||
svcLW := fakeLW{
|
svcLW := fakeLW{
|
||||||
@ -305,8 +270,16 @@ func TestInitialSync(t *testing.T) {
|
|||||||
watchResp: fakeEpsWatch,
|
watchResp: fakeEpsWatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svcConfig := NewServiceConfig()
|
||||||
|
epsConfig := newEndpointsConfig(epsLW, time.Minute)
|
||||||
|
svcHandler := newSvcHandler(t, []api.Service{*svc2, *svc1}, wg.Done)
|
||||||
|
svcConfig.RegisterHandler(svcHandler)
|
||||||
|
epsHandler := newEpsHandler(t, []*api.Endpoints{eps2, eps1}, wg.Done)
|
||||||
|
epsConfig.RegisterHandler(epsHandler)
|
||||||
|
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
newSourceAPI(svcLW, epsLW, time.Minute, svcConfig.Channel("one"), epsConfig.Channel("two"), stopCh)
|
go epsConfig.Run(stopCh)
|
||||||
|
newSourceAPI(svcLW, time.Minute, svcConfig.Channel("one"), stopCh)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -17,12 +17,20 @@ limitations under the License.
|
|||||||
package config
|
package config
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
"github.com/davecgh/go-spew/spew"
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
|
listers "k8s.io/kubernetes/pkg/client/listers/core/internalversion"
|
||||||
"k8s.io/kubernetes/pkg/util/config"
|
"k8s.io/kubernetes/pkg/util/config"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -76,102 +84,100 @@ type EndpointsConfigHandler interface {
|
|||||||
// EndpointsConfig tracks a set of endpoints configurations.
|
// EndpointsConfig tracks a set of endpoints configurations.
|
||||||
// It accepts "set", "add" and "remove" operations of endpoints via channels, and invokes registered handlers on change.
|
// It accepts "set", "add" and "remove" operations of endpoints via channels, and invokes registered handlers on change.
|
||||||
type EndpointsConfig struct {
|
type EndpointsConfig struct {
|
||||||
mux *config.Mux
|
informer cache.Controller
|
||||||
bcaster *config.Broadcaster
|
lister listers.EndpointsLister
|
||||||
store *endpointsStore
|
handlers []EndpointsConfigHandler
|
||||||
|
// updates channel is used to trigger registered handlers.
|
||||||
|
updates chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewEndpointsConfig creates a new EndpointsConfig.
|
// NewEndpointsConfig creates a new EndpointsConfig.
|
||||||
// It immediately runs the created EndpointsConfig.
|
func NewEndpointsConfig(c cache.Getter, period time.Duration) *EndpointsConfig {
|
||||||
func NewEndpointsConfig() *EndpointsConfig {
|
endpointsLW := cache.NewListWatchFromClient(c, "endpoints", metav1.NamespaceAll, fields.Everything())
|
||||||
// The updates channel is used to send interrupts to the Endpoints handler.
|
return newEndpointsConfig(endpointsLW, period)
|
||||||
// It's buffered because we never want to block for as long as there is a
|
}
|
||||||
// pending interrupt, but don't want to drop them if the handler is doing
|
|
||||||
// work.
|
func newEndpointsConfig(lw cache.ListerWatcher, period time.Duration) *EndpointsConfig {
|
||||||
updates := make(chan struct{}, 1)
|
result := &EndpointsConfig{}
|
||||||
store := &endpointsStore{updates: updates, endpoints: make(map[string]map[types.NamespacedName]*api.Endpoints)}
|
|
||||||
mux := config.NewMux(store)
|
store, informer := cache.NewIndexerInformer(
|
||||||
bcaster := config.NewBroadcaster()
|
lw,
|
||||||
go watchForUpdates(bcaster, store, updates)
|
&api.Endpoints{},
|
||||||
return &EndpointsConfig{mux, bcaster, store}
|
period,
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: result.handleAddEndpoints,
|
||||||
|
UpdateFunc: result.handleUpdateEndpoints,
|
||||||
|
DeleteFunc: result.handleDeleteEndpoints,
|
||||||
|
},
|
||||||
|
cache.Indexers{},
|
||||||
|
)
|
||||||
|
result.informer = informer
|
||||||
|
result.lister = listers.NewEndpointsLister(store)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHandler registers a handler which is called on every endpoints change.
|
// RegisterHandler registers a handler which is called on every endpoints change.
|
||||||
func (c *EndpointsConfig) RegisterHandler(handler EndpointsConfigHandler) {
|
func (c *EndpointsConfig) RegisterHandler(handler EndpointsConfigHandler) {
|
||||||
c.bcaster.Add(config.ListenerFunc(func(instance interface{}) {
|
c.handlers = append(c.handlers, handler)
|
||||||
glog.V(3).Infof("Calling handler.OnEndpointsUpdate()")
|
|
||||||
handler.OnEndpointsUpdate(instance.([]*api.Endpoints))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channel returns a channel to which endpoints updates should be delivered.
|
func (c *EndpointsConfig) Run(stopCh <-chan struct{}) {
|
||||||
func (c *EndpointsConfig) Channel(source string) chan EndpointsUpdate {
|
// The updates channel is used to send interrupts to the Endpoints handler.
|
||||||
ch := c.mux.Channel(source)
|
// It's buffered because we never want to block for as long as there is a
|
||||||
endpointsCh := make(chan EndpointsUpdate)
|
// pending interrupt, but don't want to drop them if the handler is doing
|
||||||
|
// work.
|
||||||
|
c.updates = make(chan struct{}, 1)
|
||||||
|
go c.informer.Run(stopCh)
|
||||||
|
if !cache.WaitForCacheSync(stopCh, c.informer.HasSynced) {
|
||||||
|
utilruntime.HandleError(fmt.Errorf("endpoint controller not synced"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We have synced informers. Now we can start delivering updates
|
||||||
|
// to the registered handler.
|
||||||
go func() {
|
go func() {
|
||||||
for update := range endpointsCh {
|
for range c.updates {
|
||||||
ch <- update
|
endpoints, err := c.lister.List(labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error while listing endpoints from cache: %v", err)
|
||||||
|
// This will cause a retry (if there isn't any other trigger in-flight).
|
||||||
|
c.dispatchUpdate()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if endpoints == nil {
|
||||||
|
endpoints = []*api.Endpoints{}
|
||||||
|
}
|
||||||
|
for i := range c.handlers {
|
||||||
|
glog.V(3).Infof("Calling handler.OnEndpointsUpdate()")
|
||||||
|
c.handlers[i].OnEndpointsUpdate(endpoints)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return endpointsCh
|
// Close updates channel when stopCh is closed.
|
||||||
|
go func() {
|
||||||
|
<-stopCh
|
||||||
|
close(c.updates)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config returns list of all endpoints from underlying store.
|
func (c *EndpointsConfig) handleAddEndpoints(_ interface{}) {
|
||||||
func (c *EndpointsConfig) Config() []api.Endpoints {
|
c.dispatchUpdate()
|
||||||
return c.store.MergedState().([]api.Endpoints)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type endpointsStore struct {
|
func (c *EndpointsConfig) handleUpdateEndpoints(_, _ interface{}) {
|
||||||
endpointLock sync.RWMutex
|
c.dispatchUpdate()
|
||||||
endpoints map[string]map[types.NamespacedName]*api.Endpoints
|
|
||||||
synced bool
|
|
||||||
updates chan<- struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *endpointsStore) Merge(source string, change interface{}) error {
|
func (c *EndpointsConfig) handleDeleteEndpoints(_ interface{}) {
|
||||||
s.endpointLock.Lock()
|
c.dispatchUpdate()
|
||||||
endpoints := s.endpoints[source]
|
}
|
||||||
if endpoints == nil {
|
|
||||||
endpoints = make(map[types.NamespacedName]*api.Endpoints)
|
func (c *EndpointsConfig) dispatchUpdate() {
|
||||||
}
|
|
||||||
update := change.(EndpointsUpdate)
|
|
||||||
switch update.Op {
|
|
||||||
case ADD, UPDATE:
|
|
||||||
glog.V(5).Infof("Adding new endpoint from source %s : %s", source, spew.Sdump(update.Endpoints))
|
|
||||||
name := types.NamespacedName{Namespace: update.Endpoints.Namespace, Name: update.Endpoints.Name}
|
|
||||||
endpoints[name] = update.Endpoints
|
|
||||||
case REMOVE:
|
|
||||||
glog.V(5).Infof("Removing an endpoint %s", spew.Sdump(update.Endpoints))
|
|
||||||
name := types.NamespacedName{Namespace: update.Endpoints.Namespace, Name: update.Endpoints.Name}
|
|
||||||
delete(endpoints, name)
|
|
||||||
case SYNCED:
|
|
||||||
s.synced = true
|
|
||||||
default:
|
|
||||||
glog.V(4).Infof("Received invalid update type: %s", spew.Sdump(update))
|
|
||||||
}
|
|
||||||
s.endpoints[source] = endpoints
|
|
||||||
synced := s.synced
|
|
||||||
s.endpointLock.Unlock()
|
|
||||||
if s.updates != nil && synced {
|
|
||||||
select {
|
select {
|
||||||
case s.updates <- struct{}{}:
|
case c.updates <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
glog.V(4).Infof("Endpoints handler already has a pending interrupt.")
|
glog.V(4).Infof("Endpoints handler already has a pending interrupt.")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *endpointsStore) MergedState() interface{} {
|
|
||||||
s.endpointLock.RLock()
|
|
||||||
defer s.endpointLock.RUnlock()
|
|
||||||
endpoints := make([]*api.Endpoints, 0)
|
|
||||||
for _, sourceEndpoints := range s.endpoints {
|
|
||||||
for _, value := range sourceEndpoints {
|
|
||||||
endpoints = append(endpoints, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return endpoints
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceConfig tracks a set of service configurations.
|
// ServiceConfig tracks a set of service configurations.
|
||||||
|
@ -24,6 +24,7 @@ import (
|
|||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -228,98 +229,110 @@ func TestNewMultipleSourcesServicesMultipleHandlersAddedAndNotified(t *testing.T
|
|||||||
handler2.ValidateServices(t, services)
|
handler2.ValidateServices(t, services)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMultipleSourcesEndpointsMultipleHandlersAddedAndNotified(t *testing.T) {
|
func TestNewEndpointsMultipleHandlersAddedAndNotified(t *testing.T) {
|
||||||
config := NewEndpointsConfig()
|
fakeWatch := watch.NewFake()
|
||||||
config.store.synced = true
|
lw := fakeLW{
|
||||||
channelOne := config.Channel("one")
|
listResp: &api.EndpointsList{Items: []api.Endpoints{}},
|
||||||
channelTwo := config.Channel("two")
|
watchResp: fakeWatch,
|
||||||
|
}
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := newEndpointsConfig(lw, time.Minute)
|
||||||
handler := NewEndpointsHandlerMock()
|
handler := NewEndpointsHandlerMock()
|
||||||
handler2 := NewEndpointsHandlerMock()
|
handler2 := NewEndpointsHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
config.RegisterHandler(handler2)
|
config.RegisterHandler(handler2)
|
||||||
endpointsUpdate1 := CreateEndpointsUpdate(ADD, &api.Endpoints{
|
go config.Run(stopCh)
|
||||||
|
|
||||||
|
endpoints1 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
|
Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
endpointsUpdate2 := CreateEndpointsUpdate(ADD, &api.Endpoints{
|
endpoints2 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
|
Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
channelOne <- endpointsUpdate1
|
fakeWatch.Add(endpoints1)
|
||||||
channelTwo <- endpointsUpdate2
|
fakeWatch.Add(endpoints2)
|
||||||
|
|
||||||
endpoints := []*api.Endpoints{endpointsUpdate2.Endpoints, endpointsUpdate1.Endpoints}
|
endpoints := []*api.Endpoints{endpoints2, endpoints1}
|
||||||
handler.ValidateEndpoints(t, endpoints)
|
handler.ValidateEndpoints(t, endpoints)
|
||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMultipleSourcesEndpointsMultipleHandlersAddRemoveSetAndNotified(t *testing.T) {
|
func TestNewEndpointsMultipleHandlersAddRemoveSetAndNotified(t *testing.T) {
|
||||||
config := NewEndpointsConfig()
|
fakeWatch := watch.NewFake()
|
||||||
config.store.synced = true
|
lw := fakeLW{
|
||||||
channelOne := config.Channel("one")
|
listResp: &api.EndpointsList{Items: []api.Endpoints{}},
|
||||||
channelTwo := config.Channel("two")
|
watchResp: fakeWatch,
|
||||||
|
}
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := newEndpointsConfig(lw, time.Minute)
|
||||||
handler := NewEndpointsHandlerMock()
|
handler := NewEndpointsHandlerMock()
|
||||||
handler2 := NewEndpointsHandlerMock()
|
handler2 := NewEndpointsHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
config.RegisterHandler(handler2)
|
config.RegisterHandler(handler2)
|
||||||
endpointsUpdate1 := CreateEndpointsUpdate(ADD, &api.Endpoints{
|
go config.Run(stopCh)
|
||||||
|
|
||||||
|
endpoints1 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
|
Addresses: []api.EndpointAddress{{IP: "1.1.1.1"}, {IP: "2.2.2.2"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
endpointsUpdate2 := CreateEndpointsUpdate(ADD, &api.Endpoints{
|
endpoints2 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
|
Addresses: []api.EndpointAddress{{IP: "3.3.3.3"}, {IP: "4.4.4.4"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
channelOne <- endpointsUpdate1
|
fakeWatch.Add(endpoints1)
|
||||||
channelTwo <- endpointsUpdate2
|
fakeWatch.Add(endpoints2)
|
||||||
|
|
||||||
endpoints := []*api.Endpoints{endpointsUpdate2.Endpoints, endpointsUpdate1.Endpoints}
|
endpoints := []*api.Endpoints{endpoints2, endpoints1}
|
||||||
handler.ValidateEndpoints(t, endpoints)
|
handler.ValidateEndpoints(t, endpoints)
|
||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Add one more
|
// Add one more
|
||||||
endpointsUpdate3 := CreateEndpointsUpdate(ADD, &api.Endpoints{
|
endpoints3 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foobar"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foobar"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "5.5.5.5"}, {IP: "6.6.6.6"}},
|
Addresses: []api.EndpointAddress{{IP: "5.5.5.5"}, {IP: "6.6.6.6"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
channelTwo <- endpointsUpdate3
|
fakeWatch.Add(endpoints3)
|
||||||
endpoints = []*api.Endpoints{endpointsUpdate2.Endpoints, endpointsUpdate1.Endpoints, endpointsUpdate3.Endpoints}
|
endpoints = []*api.Endpoints{endpoints2, endpoints1, endpoints3}
|
||||||
handler.ValidateEndpoints(t, endpoints)
|
handler.ValidateEndpoints(t, endpoints)
|
||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Update the "foo" service with new endpoints
|
// Update the "foo" service with new endpoints
|
||||||
endpointsUpdate1 = CreateEndpointsUpdate(ADD, &api.Endpoints{
|
endpoints1v2 := &api.Endpoints{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Subsets: []api.EndpointSubset{{
|
Subsets: []api.EndpointSubset{{
|
||||||
Addresses: []api.EndpointAddress{{IP: "7.7.7.7"}},
|
Addresses: []api.EndpointAddress{{IP: "7.7.7.7"}},
|
||||||
Ports: []api.EndpointPort{{Port: 80}},
|
Ports: []api.EndpointPort{{Port: 80}},
|
||||||
}},
|
}},
|
||||||
})
|
}
|
||||||
channelOne <- endpointsUpdate1
|
fakeWatch.Modify(endpoints1v2)
|
||||||
endpoints = []*api.Endpoints{endpointsUpdate2.Endpoints, endpointsUpdate1.Endpoints, endpointsUpdate3.Endpoints}
|
endpoints = []*api.Endpoints{endpoints2, endpoints1v2, endpoints3}
|
||||||
handler.ValidateEndpoints(t, endpoints)
|
handler.ValidateEndpoints(t, endpoints)
|
||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
|
|
||||||
// Remove "bar" service
|
// Remove "bar" endpoints
|
||||||
endpointsUpdate2 = CreateEndpointsUpdate(REMOVE, &api.Endpoints{ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"}})
|
fakeWatch.Delete(endpoints2)
|
||||||
channelTwo <- endpointsUpdate2
|
endpoints = []*api.Endpoints{endpoints1v2, endpoints3}
|
||||||
|
|
||||||
endpoints = []*api.Endpoints{endpointsUpdate1.Endpoints, endpointsUpdate3.Endpoints}
|
|
||||||
handler.ValidateEndpoints(t, endpoints)
|
handler.ValidateEndpoints(t, endpoints)
|
||||||
handler2.ValidateEndpoints(t, endpoints)
|
handler2.ValidateEndpoints(t, endpoints)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user