mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Simplify proxy config for Services by removing Mux.
This commit is contained in:
parent
596527dafa
commit
7ce368ccd2
@ -305,19 +305,14 @@ func NewProxyServerDefault(config *options.ProxyServerConfig) (*ProxyServer, err
|
|||||||
// Note: RegisterHandler() calls need to happen before creation of Sources because sources
|
// Note: RegisterHandler() calls need to happen before creation of Sources because sources
|
||||||
// only notify on changes, and the initial update (on process start) may be lost if no handlers
|
// only notify on changes, and the initial update (on process start) may be lost if no handlers
|
||||||
// are registered yet.
|
// are registered yet.
|
||||||
serviceConfig := proxyconfig.NewServiceConfig()
|
serviceConfig := proxyconfig.NewServiceConfig(client.Core().RESTClient(), config.ConfigSyncPeriod)
|
||||||
serviceConfig.RegisterHandler(proxier)
|
serviceConfig.RegisterHandler(proxier)
|
||||||
|
go serviceConfig.Run(wait.NeverStop)
|
||||||
|
|
||||||
endpointsConfig := proxyconfig.NewEndpointsConfig(client.Core().RESTClient(), config.ConfigSyncPeriod)
|
endpointsConfig := proxyconfig.NewEndpointsConfig(client.Core().RESTClient(), config.ConfigSyncPeriod)
|
||||||
endpointsConfig.RegisterHandler(endpointsHandler)
|
endpointsConfig.RegisterHandler(endpointsHandler)
|
||||||
go endpointsConfig.Run(wait.NeverStop)
|
go endpointsConfig.Run(wait.NeverStop)
|
||||||
|
|
||||||
proxyconfig.NewSourceAPI(
|
|
||||||
client.Core().RESTClient(),
|
|
||||||
config.ConfigSyncPeriod,
|
|
||||||
serviceConfig.Channel("api"),
|
|
||||||
)
|
|
||||||
|
|
||||||
config.NodeRef = &clientv1.ObjectReference{
|
config.NodeRef = &clientv1.ObjectReference{
|
||||||
Kind: "Node",
|
Kind: "Node",
|
||||||
Name: hostname,
|
Name: hostname,
|
||||||
|
@ -136,7 +136,7 @@ func main() {
|
|||||||
|
|
||||||
iptInterface := fakeiptables.NewFake()
|
iptInterface := fakeiptables.NewFake()
|
||||||
|
|
||||||
serviceConfig := proxyconfig.NewServiceConfig()
|
serviceConfig := proxyconfig.NewServiceConfig(internalClientset.Core().RESTClient(), 15*time.Minute)
|
||||||
serviceConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
serviceConfig.RegisterHandler(&kubemark.FakeProxyHandler{})
|
||||||
|
|
||||||
endpointsConfig := proxyconfig.NewEndpointsConfig(internalClientset.Core().RESTClient(), 15*time.Minute)
|
endpointsConfig := proxyconfig.NewEndpointsConfig(internalClientset.Core().RESTClient(), 15*time.Minute)
|
||||||
|
@ -17,8 +17,6 @@ limitations under the License.
|
|||||||
package kubemark
|
package kubemark
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/types"
|
"k8s.io/apimachinery/pkg/types"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
"k8s.io/apimachinery/pkg/util/wait"
|
||||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||||
@ -74,11 +72,7 @@ func NewHollowProxyOrDie(
|
|||||||
}
|
}
|
||||||
|
|
||||||
go endpointsConfig.Run(wait.NeverStop)
|
go endpointsConfig.Run(wait.NeverStop)
|
||||||
proxyconfig.NewSourceAPI(
|
go serviceConfig.Run(wait.NeverStop)
|
||||||
client.Core().RESTClient(),
|
|
||||||
15*time.Minute,
|
|
||||||
serviceConfig.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")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -11,7 +11,6 @@ load(
|
|||||||
go_library(
|
go_library(
|
||||||
name = "go_default_library",
|
name = "go_default_library",
|
||||||
srcs = [
|
srcs = [
|
||||||
"api.go",
|
|
||||||
"config.go",
|
"config.go",
|
||||||
"doc.go",
|
"doc.go",
|
||||||
],
|
],
|
||||||
@ -20,14 +19,11 @@ go_library(
|
|||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//pkg/client/listers/core/internalversion: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/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/labels",
|
||||||
"//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/client-go/tools/cache",
|
"//vendor:k8s.io/client-go/tools/cache",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
@ -42,7 +38,6 @@ go_test(
|
|||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
deps = [
|
deps = [
|
||||||
"//pkg/api:go_default_library",
|
"//pkg/api:go_default_library",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/api/equality",
|
|
||||||
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
"//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
"//vendor:k8s.io/apimachinery/pkg/runtime",
|
||||||
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
"//vendor:k8s.io/apimachinery/pkg/util/wait",
|
||||||
|
@ -1,109 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright 2014 The Kubernetes Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
|
||||||
)
|
|
||||||
|
|
||||||
// NewSourceAPI creates config source that watches for changes to the services and endpoints.
|
|
||||||
func NewSourceAPI(c cache.Getter, period time.Duration, servicesChan chan<- ServiceUpdate) {
|
|
||||||
servicesLW := cache.NewListWatchFromClient(c, "services", metav1.NamespaceAll, fields.Everything())
|
|
||||||
newSourceAPI(servicesLW, period, servicesChan, wait.NeverStop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func newSourceAPI(
|
|
||||||
servicesLW cache.ListerWatcher,
|
|
||||||
period time.Duration,
|
|
||||||
servicesChan chan<- ServiceUpdate,
|
|
||||||
stopCh <-chan struct{}) {
|
|
||||||
serviceController := NewServiceController(servicesLW, period, servicesChan)
|
|
||||||
go serviceController.Run(stopCh)
|
|
||||||
|
|
||||||
if !cache.WaitForCacheSync(stopCh, serviceController.HasSynced) {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("source controllers not synced"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
servicesChan <- ServiceUpdate{Op: SYNCED}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendAddService(servicesChan chan<- ServiceUpdate) func(obj interface{}) {
|
|
||||||
return func(obj interface{}) {
|
|
||||||
service, ok := obj.(*api.Service)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Service: %v", obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
servicesChan <- ServiceUpdate{Op: ADD, Service: service}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendUpdateService(servicesChan chan<- ServiceUpdate) func(oldObj, newObj interface{}) {
|
|
||||||
return func(_, newObj interface{}) {
|
|
||||||
service, ok := newObj.(*api.Service)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Service: %v", newObj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
servicesChan <- ServiceUpdate{Op: UPDATE, Service: service}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendDeleteService(servicesChan chan<- ServiceUpdate) func(obj interface{}) {
|
|
||||||
return func(obj interface{}) {
|
|
||||||
var service *api.Service
|
|
||||||
switch t := obj.(type) {
|
|
||||||
case *api.Service:
|
|
||||||
service = t
|
|
||||||
case cache.DeletedFinalStateUnknown:
|
|
||||||
var ok bool
|
|
||||||
service, ok = t.Obj.(*api.Service)
|
|
||||||
if !ok {
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Service: %v", t.Obj))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
utilruntime.HandleError(fmt.Errorf("cannot convert to *api.Service: %v", t))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
servicesChan <- ServiceUpdate{Op: REMOVE, Service: service}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewServiceController creates a controller that is watching services and sending
|
|
||||||
// updates into ServiceUpdate channel.
|
|
||||||
func NewServiceController(lw cache.ListerWatcher, period time.Duration, ch chan<- ServiceUpdate) cache.Controller {
|
|
||||||
_, serviceController := cache.NewInformer(
|
|
||||||
lw,
|
|
||||||
&api.Service{},
|
|
||||||
period,
|
|
||||||
cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: sendAddService(ch),
|
|
||||||
UpdateFunc: sendUpdateService(ch),
|
|
||||||
DeleteFunc: sendDeleteService(ch),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
return serviceController
|
|
||||||
}
|
|
@ -23,10 +23,8 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/apimachinery/pkg/watch"
|
"k8s.io/apimachinery/pkg/watch"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
@ -65,68 +63,40 @@ func TestNewServicesSourceApi_UpdatesAndMultipleServices(t *testing.T) {
|
|||||||
watchResp: fakeWatch,
|
watchResp: fakeWatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
ch := make(chan ServiceUpdate)
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
serviceController := NewServiceController(lw, 30*time.Second, ch)
|
ch := make(chan struct{})
|
||||||
go serviceController.Run(wait.NeverStop)
|
handler := newSvcHandler(t, nil, func() { ch <- struct{}{} })
|
||||||
|
|
||||||
|
serviceConfig := newServiceConfig(lw, time.Minute)
|
||||||
|
serviceConfig.RegisterHandler(handler)
|
||||||
|
go serviceConfig.Run(stopCh)
|
||||||
|
|
||||||
// Add the first service
|
// Add the first service
|
||||||
|
handler.expected = []api.Service{*service1v1}
|
||||||
fakeWatch.Add(service1v1)
|
fakeWatch.Add(service1v1)
|
||||||
got, ok := <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected := ServiceUpdate{Op: ADD, Service: service1v1}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v; Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add another service
|
// Add another service
|
||||||
|
handler.expected = []api.Service{*service1v1, *service2}
|
||||||
fakeWatch.Add(service2)
|
fakeWatch.Add(service2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
// Could be sorted either of these two ways:
|
|
||||||
expected = ServiceUpdate{Op: ADD, Service: service2}
|
|
||||||
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modify service1
|
// Modify service1
|
||||||
|
handler.expected = []api.Service{*service1v2, *service2}
|
||||||
fakeWatch.Modify(service1v2)
|
fakeWatch.Modify(service1v2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = ServiceUpdate{Op: UPDATE, Service: service1v2}
|
|
||||||
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete service1
|
// Delete service1
|
||||||
|
handler.expected = []api.Service{*service2}
|
||||||
fakeWatch.Delete(service1v2)
|
fakeWatch.Delete(service1v2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = ServiceUpdate{Op: REMOVE, Service: service1v2}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete service2
|
// Delete service2
|
||||||
|
handler.expected = []api.Service{}
|
||||||
fakeWatch.Delete(service2)
|
fakeWatch.Delete(service2)
|
||||||
got, ok = <-ch
|
<-ch
|
||||||
if !ok {
|
|
||||||
t.Errorf("Unable to read from channel when expected")
|
|
||||||
}
|
|
||||||
expected = ServiceUpdate{Op: REMOVE, Service: service2}
|
|
||||||
if !apiequality.Semantic.DeepEqual(expected, got) {
|
|
||||||
t.Errorf("Expected %#v, Got %#v", expected, got)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewEndpointsSourceApi_UpdatesAndMultipleEndpoints(t *testing.T) {
|
func TestNewEndpointsSourceApi_UpdatesAndMultipleEndpoints(t *testing.T) {
|
||||||
@ -270,7 +240,7 @@ func TestInitialSync(t *testing.T) {
|
|||||||
watchResp: fakeEpsWatch,
|
watchResp: fakeEpsWatch,
|
||||||
}
|
}
|
||||||
|
|
||||||
svcConfig := NewServiceConfig()
|
svcConfig := newServiceConfig(svcLW, time.Minute)
|
||||||
epsConfig := newEndpointsConfig(epsLW, time.Minute)
|
epsConfig := newEndpointsConfig(epsLW, time.Minute)
|
||||||
svcHandler := newSvcHandler(t, []api.Service{*svc2, *svc1}, wg.Done)
|
svcHandler := newSvcHandler(t, []api.Service{*svc2, *svc1}, wg.Done)
|
||||||
svcConfig.RegisterHandler(svcHandler)
|
svcConfig.RegisterHandler(svcHandler)
|
||||||
@ -279,7 +249,7 @@ func TestInitialSync(t *testing.T) {
|
|||||||
|
|
||||||
stopCh := make(chan struct{})
|
stopCh := make(chan struct{})
|
||||||
defer close(stopCh)
|
defer close(stopCh)
|
||||||
|
go svcConfig.Run(stopCh)
|
||||||
go epsConfig.Run(stopCh)
|
go epsConfig.Run(stopCh)
|
||||||
newSourceAPI(svcLW, time.Minute, svcConfig.Channel("one"), stopCh)
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
@ -18,15 +18,12 @@ package config
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/davecgh/go-spew/spew"
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/fields"
|
"k8s.io/apimachinery/pkg/fields"
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
"k8s.io/apimachinery/pkg/types"
|
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/client-go/tools/cache"
|
"k8s.io/client-go/tools/cache"
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
@ -183,102 +180,101 @@ func (c *EndpointsConfig) dispatchUpdate() {
|
|||||||
// ServiceConfig tracks a set of service configurations.
|
// ServiceConfig tracks a set of service configurations.
|
||||||
// It accepts "set", "add" and "remove" operations of services via channels, and invokes registered handlers on change.
|
// It accepts "set", "add" and "remove" operations of services via channels, and invokes registered handlers on change.
|
||||||
type ServiceConfig struct {
|
type ServiceConfig struct {
|
||||||
mux *config.Mux
|
informer cache.Controller
|
||||||
bcaster *config.Broadcaster
|
lister listers.ServiceLister
|
||||||
store *serviceStore
|
handlers []ServiceConfigHandler
|
||||||
|
// updates channel is used to trigger registered handlers
|
||||||
|
updates chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServiceConfig creates a new ServiceConfig.
|
// NewServiceConfig creates a new ServiceConfig.
|
||||||
// It immediately runs the created ServiceConfig.
|
func NewServiceConfig(c cache.Getter, period time.Duration) *ServiceConfig {
|
||||||
func NewServiceConfig() *ServiceConfig {
|
servicesLW := cache.NewListWatchFromClient(c, "services", metav1.NamespaceAll, fields.Everything())
|
||||||
// The updates channel is used to send interrupts to the Services handler.
|
return newServiceConfig(servicesLW, 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 newServiceConfig(lw cache.ListerWatcher, period time.Duration) *ServiceConfig {
|
||||||
updates := make(chan struct{}, 1)
|
result := &ServiceConfig{}
|
||||||
store := &serviceStore{updates: updates, services: make(map[string]map[types.NamespacedName]*api.Service)}
|
|
||||||
mux := config.NewMux(store)
|
store, informer := cache.NewIndexerInformer(
|
||||||
bcaster := config.NewBroadcaster()
|
lw,
|
||||||
go watchForUpdates(bcaster, store, updates)
|
&api.Service{},
|
||||||
return &ServiceConfig{mux, bcaster, store}
|
period,
|
||||||
|
cache.ResourceEventHandlerFuncs{
|
||||||
|
AddFunc: result.handleAddService,
|
||||||
|
UpdateFunc: result.handleUpdateService,
|
||||||
|
DeleteFunc: result.handleDeleteService,
|
||||||
|
},
|
||||||
|
cache.Indexers{},
|
||||||
|
)
|
||||||
|
result.informer = informer
|
||||||
|
result.lister = listers.NewServiceLister(store)
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterHandler registers a handler which is called on every services change.
|
// RegisterHandler registers a handler which is called on every services change.
|
||||||
func (c *ServiceConfig) RegisterHandler(handler ServiceConfigHandler) {
|
func (c *ServiceConfig) RegisterHandler(handler ServiceConfigHandler) {
|
||||||
c.bcaster.Add(config.ListenerFunc(func(instance interface{}) {
|
c.handlers = append(c.handlers, handler)
|
||||||
glog.V(3).Infof("Calling handler.OnServiceUpdate()")
|
|
||||||
handler.OnServiceUpdate(instance.([]api.Service))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Channel returns a channel to which services updates should be delivered.
|
func (c *ServiceConfig) Run(stopCh <-chan struct{}) {
|
||||||
func (c *ServiceConfig) Channel(source string) chan ServiceUpdate {
|
// The updates channel is used to send interrupts to the Services handler.
|
||||||
ch := c.mux.Channel(source)
|
// It's buffered because we never want to block for as long as there is a
|
||||||
serviceCh := make(chan ServiceUpdate)
|
// 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("service controller not synced"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We hanve synced informers. Now we can start delivering updates
|
||||||
|
// to the registered handler.
|
||||||
go func() {
|
go func() {
|
||||||
for update := range serviceCh {
|
for range c.updates {
|
||||||
ch <- update
|
services, err := c.lister.List(labels.Everything())
|
||||||
|
if err != nil {
|
||||||
|
glog.Errorf("Error while listing services from cache: %v", err)
|
||||||
|
// This will cause a retry (if there isnt' any other trigger in-flight).
|
||||||
|
c.dispatchUpdate()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
svcs := make([]api.Service, 0, len(services))
|
||||||
|
for i := range services {
|
||||||
|
svcs = append(svcs, *services[i])
|
||||||
|
}
|
||||||
|
for i := range c.handlers {
|
||||||
|
glog.V(3).Infof("Calling handler.OnServiceUpdate()")
|
||||||
|
c.handlers[i].OnServiceUpdate(svcs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
return serviceCh
|
// Close updates channel when stopCh is closed.
|
||||||
|
go func() {
|
||||||
|
<-stopCh
|
||||||
|
close(c.updates)
|
||||||
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config returns list of all services from underlying store.
|
func (c *ServiceConfig) handleAddService(_ interface{}) {
|
||||||
func (c *ServiceConfig) Config() []api.Service {
|
c.dispatchUpdate()
|
||||||
return c.store.MergedState().([]api.Service)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type serviceStore struct {
|
func (c *ServiceConfig) handleUpdateService(_, _ interface{}) {
|
||||||
serviceLock sync.RWMutex
|
c.dispatchUpdate()
|
||||||
services map[string]map[types.NamespacedName]*api.Service
|
|
||||||
synced bool
|
|
||||||
updates chan<- struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *serviceStore) Merge(source string, change interface{}) error {
|
func (c *ServiceConfig) handleDeleteService(_ interface{}) {
|
||||||
s.serviceLock.Lock()
|
c.dispatchUpdate()
|
||||||
services := s.services[source]
|
}
|
||||||
if services == nil {
|
|
||||||
services = make(map[types.NamespacedName]*api.Service)
|
func (c *ServiceConfig) dispatchUpdate() {
|
||||||
}
|
select {
|
||||||
update := change.(ServiceUpdate)
|
case c.updates <- struct{}{}:
|
||||||
switch update.Op {
|
|
||||||
case ADD, UPDATE:
|
|
||||||
glog.V(5).Infof("Adding new service from source %s : %s", source, spew.Sdump(update.Service))
|
|
||||||
name := types.NamespacedName{Namespace: update.Service.Namespace, Name: update.Service.Name}
|
|
||||||
services[name] = update.Service
|
|
||||||
case REMOVE:
|
|
||||||
glog.V(5).Infof("Removing a service %s", spew.Sdump(update.Service))
|
|
||||||
name := types.NamespacedName{Namespace: update.Service.Namespace, Name: update.Service.Name}
|
|
||||||
delete(services, name)
|
|
||||||
case SYNCED:
|
|
||||||
s.synced = true
|
|
||||||
default:
|
default:
|
||||||
glog.V(4).Infof("Received invalid update type: %s", spew.Sdump(update))
|
glog.V(4).Infof("Service handler alread has a pending interrupt.")
|
||||||
}
|
}
|
||||||
s.services[source] = services
|
|
||||||
synced := s.synced
|
|
||||||
s.serviceLock.Unlock()
|
|
||||||
if s.updates != nil && synced {
|
|
||||||
select {
|
|
||||||
case s.updates <- struct{}{}:
|
|
||||||
default:
|
|
||||||
glog.V(4).Infof("Service handler already has a pending interrupt.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *serviceStore) MergedState() interface{} {
|
|
||||||
s.serviceLock.RLock()
|
|
||||||
defer s.serviceLock.RUnlock()
|
|
||||||
services := make([]api.Service, 0)
|
|
||||||
for _, sourceServices := range s.services {
|
|
||||||
for _, value := range sourceServices {
|
|
||||||
services = append(services, *value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return services
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// watchForUpdates invokes bcaster.Notify() with the latest version of an object
|
// watchForUpdates invokes bcaster.Notify() with the latest version of an object
|
||||||
|
@ -139,92 +139,89 @@ func CreateEndpointsUpdate(op Operation, endpoints *api.Endpoints) EndpointsUpda
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestNewServiceAddedAndNotified(t *testing.T) {
|
func TestNewServiceAddedAndNotified(t *testing.T) {
|
||||||
config := NewServiceConfig()
|
fakeWatch := watch.NewFake()
|
||||||
config.store.synced = true
|
lw := fakeLW{
|
||||||
channel := config.Channel("one")
|
listResp: &api.ServiceList{Items: []api.Service{}},
|
||||||
|
watchResp: fakeWatch,
|
||||||
|
}
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := newServiceConfig(lw, time.Minute)
|
||||||
handler := NewServiceHandlerMock()
|
handler := NewServiceHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
serviceUpdate := CreateServiceUpdate(ADD, &api.Service{
|
go config.Run(stopCh)
|
||||||
|
|
||||||
|
service := &api.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
||||||
})
|
}
|
||||||
channel <- serviceUpdate
|
fakeWatch.Add(service)
|
||||||
handler.ValidateServices(t, []api.Service{*serviceUpdate.Service})
|
handler.ValidateServices(t, []api.Service{*service})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceAddedRemovedSetAndNotified(t *testing.T) {
|
func TestServiceAddedRemovedSetAndNotified(t *testing.T) {
|
||||||
config := NewServiceConfig()
|
fakeWatch := watch.NewFake()
|
||||||
config.store.synced = true
|
lw := fakeLW{
|
||||||
channel := config.Channel("one")
|
listResp: &api.ServiceList{Items: []api.Service{}},
|
||||||
handler := NewServiceHandlerMock()
|
watchResp: fakeWatch,
|
||||||
config.RegisterHandler(handler)
|
|
||||||
serviceUpdate := CreateServiceUpdate(ADD, &api.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
|
||||||
})
|
|
||||||
channel <- serviceUpdate
|
|
||||||
handler.ValidateServices(t, []api.Service{*serviceUpdate.Service})
|
|
||||||
|
|
||||||
serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
|
|
||||||
})
|
|
||||||
channel <- serviceUpdate2
|
|
||||||
services := []api.Service{*serviceUpdate2.Service, *serviceUpdate.Service}
|
|
||||||
handler.ValidateServices(t, services)
|
|
||||||
|
|
||||||
serviceUpdate3 := CreateServiceUpdate(REMOVE, &api.Service{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
|
||||||
})
|
|
||||||
channel <- serviceUpdate3
|
|
||||||
services = []api.Service{*serviceUpdate2.Service}
|
|
||||||
handler.ValidateServices(t, services)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestNewMultipleSourcesServicesAddedAndNotified(t *testing.T) {
|
|
||||||
config := NewServiceConfig()
|
|
||||||
config.store.synced = true
|
|
||||||
channelOne := config.Channel("one")
|
|
||||||
channelTwo := config.Channel("two")
|
|
||||||
if channelOne == channelTwo {
|
|
||||||
t.Error("Same channel handed back for one and two")
|
|
||||||
}
|
}
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := newServiceConfig(lw, time.Minute)
|
||||||
handler := NewServiceHandlerMock()
|
handler := NewServiceHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
serviceUpdate1 := CreateServiceUpdate(ADD, &api.Service{
|
go config.Run(stopCh)
|
||||||
|
|
||||||
|
service1 := &api.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
||||||
})
|
}
|
||||||
serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
|
fakeWatch.Add(service1)
|
||||||
|
handler.ValidateServices(t, []api.Service{*service1})
|
||||||
|
|
||||||
|
service2 := &api.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
|
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
|
||||||
})
|
}
|
||||||
channelOne <- serviceUpdate1
|
fakeWatch.Add(service2)
|
||||||
channelTwo <- serviceUpdate2
|
services := []api.Service{*service2, *service1}
|
||||||
services := []api.Service{*serviceUpdate2.Service, *serviceUpdate1.Service}
|
handler.ValidateServices(t, services)
|
||||||
|
|
||||||
|
fakeWatch.Delete(service1)
|
||||||
|
services = []api.Service{*service2}
|
||||||
handler.ValidateServices(t, services)
|
handler.ValidateServices(t, services)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNewMultipleSourcesServicesMultipleHandlersAddedAndNotified(t *testing.T) {
|
func TestNewServicesMultipleHandlersAddedAndNotified(t *testing.T) {
|
||||||
config := NewServiceConfig()
|
fakeWatch := watch.NewFake()
|
||||||
config.store.synced = true
|
lw := fakeLW{
|
||||||
channelOne := config.Channel("one")
|
listResp: &api.ServiceList{Items: []api.Service{}},
|
||||||
channelTwo := config.Channel("two")
|
watchResp: fakeWatch,
|
||||||
|
}
|
||||||
|
stopCh := make(chan struct{})
|
||||||
|
defer close(stopCh)
|
||||||
|
|
||||||
|
config := newServiceConfig(lw, time.Minute)
|
||||||
handler := NewServiceHandlerMock()
|
handler := NewServiceHandlerMock()
|
||||||
handler2 := NewServiceHandlerMock()
|
handler2 := NewServiceHandlerMock()
|
||||||
config.RegisterHandler(handler)
|
config.RegisterHandler(handler)
|
||||||
config.RegisterHandler(handler2)
|
config.RegisterHandler(handler2)
|
||||||
serviceUpdate1 := CreateServiceUpdate(ADD, &api.Service{
|
go config.Run(stopCh)
|
||||||
|
|
||||||
|
service1 := &api.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "foo"},
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 10}}},
|
||||||
})
|
}
|
||||||
serviceUpdate2 := CreateServiceUpdate(ADD, &api.Service{
|
service2 := &api.Service{
|
||||||
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
ObjectMeta: metav1.ObjectMeta{Namespace: "testnamespace", Name: "bar"},
|
||||||
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
|
Spec: api.ServiceSpec{Ports: []api.ServicePort{{Protocol: "TCP", Port: 20}}},
|
||||||
})
|
}
|
||||||
channelOne <- serviceUpdate1
|
fakeWatch.Add(service1)
|
||||||
channelTwo <- serviceUpdate2
|
fakeWatch.Add(service2)
|
||||||
services := []api.Service{*serviceUpdate2.Service, *serviceUpdate1.Service}
|
|
||||||
|
services := []api.Service{*service2, *service1}
|
||||||
handler.ValidateServices(t, services)
|
handler.ValidateServices(t, services)
|
||||||
handler2.ValidateServices(t, services)
|
handler2.ValidateServices(t, services)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user