2019-01-12 04:58:27 +00:00
|
|
|
/*
|
|
|
|
Copyright 2017 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 watch
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"k8s.io/apimachinery/pkg/runtime"
|
|
|
|
"k8s.io/apimachinery/pkg/watch"
|
|
|
|
"k8s.io/client-go/tools/cache"
|
|
|
|
)
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
func newEventProcessor(out chan<- watch.Event) *eventProcessor {
|
|
|
|
return &eventProcessor{
|
|
|
|
out: out,
|
2019-01-12 04:58:27 +00:00
|
|
|
cond: sync.NewCond(&sync.Mutex{}),
|
2019-08-30 18:33:25 +00:00
|
|
|
done: make(chan struct{}),
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
// eventProcessor buffers events and writes them to an out chan when a reader
|
|
|
|
// is waiting. Because of the requirement to buffer events, it synchronizes
|
|
|
|
// input with a condition, and synchronizes output with a channels. It needs to
|
|
|
|
// be able to yield while both waiting on an input condition and while blocked
|
|
|
|
// on writing to the output channel.
|
|
|
|
type eventProcessor struct {
|
|
|
|
out chan<- watch.Event
|
2019-01-12 04:58:27 +00:00
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
cond *sync.Cond
|
|
|
|
buff []watch.Event
|
|
|
|
|
|
|
|
done chan struct{}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
func (e *eventProcessor) run() {
|
|
|
|
for {
|
|
|
|
batch := e.takeBatch()
|
|
|
|
e.writeBatch(batch)
|
|
|
|
if e.stopped() {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
func (e *eventProcessor) takeBatch() []watch.Event {
|
|
|
|
e.cond.L.Lock()
|
|
|
|
defer e.cond.L.Unlock()
|
|
|
|
|
|
|
|
for len(e.buff) == 0 && !e.stopped() {
|
|
|
|
e.cond.Wait()
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
batch := e.buff
|
|
|
|
e.buff = nil
|
|
|
|
return batch
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *eventProcessor) writeBatch(events []watch.Event) {
|
|
|
|
for _, event := range events {
|
|
|
|
select {
|
|
|
|
case e.out <- event:
|
|
|
|
case <-e.done:
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-01-12 04:58:27 +00:00
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
func (e *eventProcessor) push(event watch.Event) {
|
|
|
|
e.cond.L.Lock()
|
|
|
|
defer e.cond.L.Unlock()
|
|
|
|
defer e.cond.Signal()
|
|
|
|
e.buff = append(e.buff, event)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *eventProcessor) stopped() bool {
|
|
|
|
select {
|
|
|
|
case <-e.done:
|
|
|
|
return true
|
|
|
|
default:
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *eventProcessor) stop() {
|
|
|
|
close(e.done)
|
|
|
|
e.cond.Signal()
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewIndexerInformerWatcher will create an IndexerInformer and wrap it into watch.Interface
|
|
|
|
// so you can use it anywhere where you'd have used a regular Watcher returned from Watch method.
|
2019-04-07 17:07:55 +00:00
|
|
|
// it also returns a channel you can use to wait for the informers to fully shutdown.
|
|
|
|
func NewIndexerInformerWatcher(lw cache.ListerWatcher, objType runtime.Object) (cache.Indexer, cache.Controller, watch.Interface, <-chan struct{}) {
|
2019-01-12 04:58:27 +00:00
|
|
|
ch := make(chan watch.Event)
|
|
|
|
w := watch.NewProxyWatcher(ch)
|
2019-08-30 18:33:25 +00:00
|
|
|
e := newEventProcessor(ch)
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
indexer, informer := cache.NewIndexerInformer(lw, objType, 0, cache.ResourceEventHandlerFuncs{
|
|
|
|
AddFunc: func(obj interface{}) {
|
2019-08-30 18:33:25 +00:00
|
|
|
e.push(watch.Event{
|
|
|
|
Type: watch.Added,
|
|
|
|
Object: obj.(runtime.Object),
|
2019-01-12 04:58:27 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
UpdateFunc: func(old, new interface{}) {
|
2019-08-30 18:33:25 +00:00
|
|
|
e.push(watch.Event{
|
|
|
|
Type: watch.Modified,
|
|
|
|
Object: new.(runtime.Object),
|
2019-01-12 04:58:27 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
DeleteFunc: func(obj interface{}) {
|
2019-08-30 18:33:25 +00:00
|
|
|
staleObj, stale := obj.(cache.DeletedFinalStateUnknown)
|
|
|
|
if stale {
|
|
|
|
// We have no means of passing the additional information down using
|
|
|
|
// watch API based on watch.Event but the caller can filter such
|
|
|
|
// objects by checking if metadata.deletionTimestamp is set
|
2021-03-18 22:40:29 +00:00
|
|
|
obj = staleObj.Obj
|
2019-08-30 18:33:25 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
e.push(watch.Event{
|
|
|
|
Type: watch.Deleted,
|
|
|
|
Object: obj.(runtime.Object),
|
2019-01-12 04:58:27 +00:00
|
|
|
})
|
|
|
|
},
|
|
|
|
}, cache.Indexers{})
|
|
|
|
|
2019-08-30 18:33:25 +00:00
|
|
|
go e.run()
|
|
|
|
|
|
|
|
doneCh := make(chan struct{})
|
2019-01-12 04:58:27 +00:00
|
|
|
go func() {
|
2019-04-07 17:07:55 +00:00
|
|
|
defer close(doneCh)
|
2019-08-30 18:33:25 +00:00
|
|
|
defer e.stop()
|
2019-01-12 04:58:27 +00:00
|
|
|
informer.Run(w.StopChan())
|
|
|
|
}()
|
|
|
|
|
2019-04-07 17:07:55 +00:00
|
|
|
return indexer, informer, w, doneCh
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|