Commit 0a715a72 authored by Vish Kannan's avatar Vish Kannan

Merge pull request #10464 from jiangyaoguo/reflector-in-proxy

replace Reflector in kube-proxy
parents 3d1e40bc 79ed954e
...@@ -25,7 +25,6 @@ import ( ...@@ -25,7 +25,6 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd"
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api" clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
...@@ -130,8 +129,7 @@ func (s *ProxyServer) Run(_ []string) error { ...@@ -130,8 +129,7 @@ func (s *ProxyServer) Run(_ []string) error {
} }
config.NewSourceAPI( config.NewSourceAPI(
client.Services(api.NamespaceAll), client,
client.Endpoints(api.NamespaceAll),
30*time.Second, 30*time.Second,
serviceConfig.Channel("api"), serviceConfig.Channel("api"),
endpointsConfig.Channel("api"), endpointsConfig.Channel("api"),
......
...@@ -21,223 +21,41 @@ import ( ...@@ -21,223 +21,41 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/wait"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
"github.com/golang/glog"
) )
// TODO: to use Reflector, need to change the ServicesWatcher to a generic ListerWatcher. // NewSourceAPIserver creates config source that watches for changes to the services and endpoints.
// ServicesWatcher is capable of listing and watching for changes to services across ALL namespaces func NewSourceAPI(c *client.Client, period time.Duration, servicesChan chan<- ServiceUpdate, endpointsChan chan<- EndpointsUpdate) {
type ServicesWatcher interface { servicesLW := cache.NewListWatchFromClient(c, "services", api.NamespaceAll, fields.Everything())
List(label labels.Selector) (*api.ServiceList, error) endpointsLW := cache.NewListWatchFromClient(c, "endpoints", api.NamespaceAll, fields.Everything())
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}
// EndpointsWatcher is capable of listing and watching for changes to endpoints across ALL namespaces
type EndpointsWatcher interface {
List(label labels.Selector) (*api.EndpointsList, error)
Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}
// SourceAPI implements a configuration source for services and endpoints that
// uses the client watch API to efficiently detect changes.
type SourceAPI struct {
s servicesReflector
e endpointsReflector
}
type servicesReflector struct {
watcher ServicesWatcher
services chan<- ServiceUpdate
resourceVersion string
waitDuration time.Duration
reconnectDuration time.Duration
}
type endpointsReflector struct {
watcher EndpointsWatcher
endpoints chan<- EndpointsUpdate
resourceVersion string
waitDuration time.Duration
reconnectDuration time.Duration
}
// NewSourceAPI creates a config source that watches for changes to the services and endpoints. newServicesSourceApiFromLW(servicesLW, period, servicesChan)
func NewSourceAPI(servicesWatcher ServicesWatcher, endpointsWatcher EndpointsWatcher, period time.Duration, services chan<- ServiceUpdate, endpoints chan<- EndpointsUpdate) *SourceAPI { newEndpointsSourceApiFromLW(endpointsLW, period, endpointsChan)
config := &SourceAPI{
s: servicesReflector{
watcher: servicesWatcher,
services: services,
resourceVersion: "",
waitDuration: period,
// prevent hot loops if the server starts to misbehave
reconnectDuration: time.Second * 1,
},
e: endpointsReflector{
watcher: endpointsWatcher,
endpoints: endpoints,
resourceVersion: "",
waitDuration: period,
// prevent hot loops if the server starts to misbehave
reconnectDuration: time.Second * 1,
},
}
go util.Forever(func() { config.s.listAndWatch() }, period)
go util.Forever(func() { config.e.listAndWatch() }, period)
return config
}
func (r *servicesReflector) listAndWatch() {
r.run(&r.resourceVersion)
time.Sleep(wait.Jitter(r.reconnectDuration, 0.0))
}
func (r *endpointsReflector) listAndWatch() {
r.run(&r.resourceVersion)
time.Sleep(wait.Jitter(r.reconnectDuration, 0.0))
} }
// run loops forever looking for changes to services. func newServicesSourceApiFromLW(servicesLW cache.ListerWatcher, period time.Duration, servicesChan chan<- ServiceUpdate) {
func (s *servicesReflector) run(resourceVersion *string) { servicesPush := func(objs []interface{}) {
if len(*resourceVersion) == 0 { var services []api.Service
services, err := s.watcher.List(labels.Everything()) for _, o := range objs {
if err != nil { services = append(services, *(o.(*api.Service)))
glog.Errorf("Unable to load services: %v", err)
// TODO: reconcile with pkg/client/cache which doesn't use reflector.
time.Sleep(wait.Jitter(s.waitDuration, 0.0))
return
} }
*resourceVersion = services.ResourceVersion servicesChan <- ServiceUpdate{Op: SET, Services: services}
// TODO: replace with code to update the
s.services <- ServiceUpdate{Op: SET, Services: services.Items}
} }
watcher, err := s.watcher.Watch(labels.Everything(), fields.Everything(), *resourceVersion) serviceQueue := cache.NewUndeltaStore(servicesPush, cache.MetaNamespaceKeyFunc)
if err != nil { cache.NewReflector(servicesLW, &api.Service{}, serviceQueue, period).Run()
glog.Errorf("Unable to watch for services changes: %v", err)
if !client.IsTimeout(err) {
// Reset so that we do a fresh get request
*resourceVersion = ""
}
time.Sleep(wait.Jitter(s.waitDuration, 0.0))
return
}
defer watcher.Stop()
ch := watcher.ResultChan()
s.watchHandler(resourceVersion, ch, s.services)
}
// watchHandler loops over an event channel and delivers config changes to an update channel.
func (s *servicesReflector) watchHandler(resourceVersion *string, ch <-chan watch.Event, updates chan<- ServiceUpdate) {
for {
select {
case event, ok := <-ch:
if !ok {
glog.V(4).Infof("WatchServices channel closed")
return
}
if event.Object == nil {
glog.Errorf("Got nil over WatchServices channel")
return
}
var service *api.Service
switch obj := event.Object.(type) {
case *api.Service:
service = obj
case *api.Status:
glog.Warningf("Got error status on WatchServices channel: %+v", obj)
*resourceVersion = ""
return
default:
glog.Errorf("Got unexpected object over WatchServices channel: %+v", obj)
*resourceVersion = ""
return
}
*resourceVersion = service.ResourceVersion
switch event.Type {
case watch.Added, watch.Modified:
updates <- ServiceUpdate{Op: ADD, Services: []api.Service{*service}}
case watch.Deleted:
updates <- ServiceUpdate{Op: REMOVE, Services: []api.Service{*service}}
}
}
}
} }
// run loops forever looking for changes to endpoints. func newEndpointsSourceApiFromLW(endpointsLW cache.ListerWatcher, period time.Duration, endpointsChan chan<- EndpointsUpdate) {
func (s *endpointsReflector) run(resourceVersion *string) { endpointsPush := func(objs []interface{}) {
if len(*resourceVersion) == 0 { var endpoints []api.Endpoints
endpoints, err := s.watcher.List(labels.Everything()) for _, o := range objs {
if err != nil { endpoints = append(endpoints, *(o.(*api.Endpoints)))
glog.Errorf("Unable to load endpoints: %v", err)
time.Sleep(wait.Jitter(s.waitDuration, 0.0))
return
}
*resourceVersion = endpoints.ResourceVersion
s.endpoints <- EndpointsUpdate{Op: SET, Endpoints: endpoints.Items}
}
watcher, err := s.watcher.Watch(labels.Everything(), fields.Everything(), *resourceVersion)
if err != nil {
glog.Errorf("Unable to watch for endpoints changes: %v", err)
if !client.IsTimeout(err) {
// Reset so that we do a fresh get request
*resourceVersion = ""
} }
endpointsChan <- EndpointsUpdate{Op: SET, Endpoints: endpoints}
time.Sleep(wait.Jitter(s.waitDuration, 0.0))
return
} }
defer watcher.Stop()
ch := watcher.ResultChan()
s.watchHandler(resourceVersion, ch, s.endpoints)
}
// watchHandler loops over an event channel and delivers config changes to an update channel.
func (s *endpointsReflector) watchHandler(resourceVersion *string, ch <-chan watch.Event, updates chan<- EndpointsUpdate) {
for {
select {
case event, ok := <-ch:
if !ok {
glog.V(4).Infof("WatchEndpoints channel closed")
return
}
if event.Object == nil { endpointQueue := cache.NewUndeltaStore(endpointsPush, cache.MetaNamespaceKeyFunc)
glog.Errorf("Got nil over WatchEndpoints channel") cache.NewReflector(endpointsLW, &api.Endpoints{}, endpointQueue, period).Run()
return
}
var endpoints *api.Endpoints
switch obj := event.Object.(type) {
case *api.Endpoints:
endpoints = obj
case *api.Status:
glog.Warningf("Got error status on WatchEndpoints channel: %+v", obj)
*resourceVersion = ""
return
default:
glog.Errorf("Got unexpected object over WatchEndpoints channel: %+v", obj)
*resourceVersion = ""
return
}
*resourceVersion = endpoints.ResourceVersion
switch event.Type {
case watch.Added, watch.Modified:
updates <- EndpointsUpdate{Op: ADD, Endpoints: []api.Endpoints{*endpoints}}
case watch.Deleted:
updates <- EndpointsUpdate{Op: REMOVE, Endpoints: []api.Endpoints{*endpoints}}
}
}
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment