2014-08-03 07:00:42 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 Google Inc. All rights reserved.
|
|
|
|
|
|
|
|
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 cache
|
|
|
|
|
|
|
|
import (
|
|
|
|
"reflect"
|
|
|
|
"time"
|
|
|
|
|
2014-09-02 17:55:27 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
2014-08-03 07:00:42 +00:00
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
|
|
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
|
|
|
|
"github.com/golang/glog"
|
|
|
|
)
|
|
|
|
|
2014-08-03 22:36:36 +00:00
|
|
|
// Reflector watches a specified resource and causes all changes to be reflected in the given store.
|
|
|
|
type Reflector struct {
|
2014-08-14 22:42:05 +00:00
|
|
|
// The type of object we expect to place in the store.
|
2014-08-03 07:00:42 +00:00
|
|
|
expectedType reflect.Type
|
2014-08-14 22:42:05 +00:00
|
|
|
// The destination to sync up with the watch source
|
|
|
|
store Store
|
2014-08-18 21:47:20 +00:00
|
|
|
// watchFactory is called to initiate watches.
|
2014-08-14 22:42:05 +00:00
|
|
|
watchFactory WatchFactory
|
2014-08-18 21:47:20 +00:00
|
|
|
// period controls timing between one watch ending and
|
2014-08-14 22:42:05 +00:00
|
|
|
// the beginning of the next one.
|
2014-08-18 21:47:20 +00:00
|
|
|
period time.Duration
|
2014-08-03 07:00:42 +00:00
|
|
|
}
|
|
|
|
|
2014-08-14 22:42:05 +00:00
|
|
|
// WatchFactory should begin a watch at the specified version.
|
|
|
|
type WatchFactory func(resourceVersion uint64) (watch.Interface, error)
|
|
|
|
|
2014-09-02 10:00:28 +00:00
|
|
|
// NewReflector creates a new Reflector object which will keep the given store up to
|
2014-08-03 22:36:36 +00:00
|
|
|
// date with the server's contents for the given resource. Reflector promises to
|
2014-08-03 07:00:42 +00:00
|
|
|
// only put things in the store that have the type of expectedType.
|
2014-08-14 22:42:05 +00:00
|
|
|
func NewReflector(watchFactory WatchFactory, expectedType interface{}, store Store) *Reflector {
|
2014-08-03 22:36:36 +00:00
|
|
|
gc := &Reflector{
|
2014-08-14 22:42:05 +00:00
|
|
|
watchFactory: watchFactory,
|
2014-08-03 07:00:42 +00:00
|
|
|
store: store,
|
|
|
|
expectedType: reflect.TypeOf(expectedType),
|
2014-08-18 21:47:20 +00:00
|
|
|
period: time.Second,
|
2014-08-03 07:00:42 +00:00
|
|
|
}
|
|
|
|
return gc
|
|
|
|
}
|
|
|
|
|
2014-08-14 22:42:05 +00:00
|
|
|
// Run starts a watch and handles watch events. Will restart the watch if it is closed.
|
|
|
|
// Run starts a goroutine and returns immediately.
|
2014-08-03 22:36:36 +00:00
|
|
|
func (gc *Reflector) Run() {
|
2014-08-14 22:42:05 +00:00
|
|
|
var resourceVersion uint64
|
2014-08-03 22:36:36 +00:00
|
|
|
go util.Forever(func() {
|
2014-08-14 22:42:05 +00:00
|
|
|
w, err := gc.watchFactory(resourceVersion)
|
2014-08-03 22:36:36 +00:00
|
|
|
if err != nil {
|
2014-08-14 22:42:05 +00:00
|
|
|
glog.Errorf("failed to watch %v: %v", gc.expectedType, err)
|
2014-08-03 22:36:36 +00:00
|
|
|
return
|
|
|
|
}
|
2014-08-14 22:42:05 +00:00
|
|
|
gc.watchHandler(w, &resourceVersion)
|
2014-08-18 21:47:20 +00:00
|
|
|
}, gc.period)
|
2014-08-03 07:00:42 +00:00
|
|
|
}
|
|
|
|
|
2014-08-14 22:42:05 +00:00
|
|
|
// watchHandler watches w and keeps *resourceVersion up to date.
|
|
|
|
func (gc *Reflector) watchHandler(w watch.Interface, resourceVersion *uint64) {
|
2014-08-03 07:00:42 +00:00
|
|
|
for {
|
|
|
|
event, ok := <-w.ResultChan()
|
|
|
|
if !ok {
|
|
|
|
glog.Errorf("unexpected watch close")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if e, a := gc.expectedType, reflect.TypeOf(event.Object); e != a {
|
|
|
|
glog.Errorf("expected type %v, but watch event object had type %v", e, a)
|
|
|
|
continue
|
|
|
|
}
|
2014-09-02 17:55:27 +00:00
|
|
|
jsonBase, err := runtime.FindJSONBase(event.Object)
|
2014-08-03 07:00:42 +00:00
|
|
|
if err != nil {
|
|
|
|
glog.Errorf("unable to understand watch event %#v", event)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
switch event.Type {
|
|
|
|
case watch.Added:
|
|
|
|
gc.store.Add(jsonBase.ID(), event.Object)
|
|
|
|
case watch.Modified:
|
|
|
|
gc.store.Update(jsonBase.ID(), event.Object)
|
|
|
|
case watch.Deleted:
|
2014-08-18 21:47:20 +00:00
|
|
|
// TODO: Will any consumers need access to the "last known
|
|
|
|
// state", which is passed in event.Object? If so, may need
|
|
|
|
// to change this.
|
|
|
|
gc.store.Delete(jsonBase.ID())
|
2014-08-03 07:00:42 +00:00
|
|
|
default:
|
|
|
|
glog.Errorf("unable to understand watch event %#v", event)
|
|
|
|
}
|
2014-08-18 21:47:20 +00:00
|
|
|
*resourceVersion = jsonBase.ResourceVersion() + 1
|
2014-08-03 07:00:42 +00:00
|
|
|
}
|
|
|
|
}
|