Split httpWatcher into watch.StreamWatcher and tools.APIEventDecoder.
This commit is contained in:
91
pkg/watch/iowatcher.go
Normal file
91
pkg/watch/iowatcher.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
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 watch
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
// Decoder allows StreamWatcher to watch any stream for which a Decoder can be written.
|
||||
type Decoder interface {
|
||||
// Decode should return the type of event, the decoded object, or an error.
|
||||
// An error will cause StreamWatcher to call Close(). Decode should block until
|
||||
// it has data or an error occurs.
|
||||
Decode() (action EventType, object interface{}, err error)
|
||||
|
||||
// Close should close the underlying io.Reader, signalling to the source of
|
||||
// the stream that it is no longer being watched. Close() must cause any
|
||||
// outstanding call to Decode() to return with an error of some sort.
|
||||
Close()
|
||||
}
|
||||
|
||||
// StreamWatcher turns any stream for which you can write a Decoder interface
|
||||
// into a watch.Interface.
|
||||
type StreamWatcher struct {
|
||||
source Decoder
|
||||
result chan Event
|
||||
sync.Mutex
|
||||
stopped bool
|
||||
}
|
||||
|
||||
// NewStreamWatcher creates a StreamWatcher from the given decoder.
|
||||
func NewStreamWatcher(d Decoder) *StreamWatcher {
|
||||
sw := &StreamWatcher{
|
||||
source: d,
|
||||
// It's easy for a consumer to add buffering via an extra
|
||||
// goroutine/channel, but impossible for them to remove it,
|
||||
// so nonbuffered is better.
|
||||
result: make(chan Event),
|
||||
}
|
||||
go sw.receive()
|
||||
return sw
|
||||
}
|
||||
|
||||
// ResultChan implements Interface.
|
||||
func (sw *StreamWatcher) ResultChan() <-chan Event {
|
||||
return sw.result
|
||||
}
|
||||
|
||||
// Stop implements Interface.
|
||||
func (sw *StreamWatcher) Stop() {
|
||||
// Call Close() exactly once by locking and setting a flag.
|
||||
sw.Lock()
|
||||
defer sw.Unlock()
|
||||
if !sw.stopped {
|
||||
sw.stopped = true
|
||||
sw.source.Close()
|
||||
}
|
||||
}
|
||||
|
||||
// In a loop, read a result from the decoder and send down the result channel.
|
||||
func (sw *StreamWatcher) receive() {
|
||||
defer close(sw.result)
|
||||
defer sw.Stop()
|
||||
defer util.HandleCrash()
|
||||
for {
|
||||
action, obj, err := sw.source.Decode()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sw.result <- Event{
|
||||
Type: action,
|
||||
Object: obj,
|
||||
}
|
||||
}
|
||||
}
|
65
pkg/watch/iowatcher_test.go
Normal file
65
pkg/watch/iowatcher_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
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 watch
|
||||
|
||||
import (
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type fakeDecoder struct {
|
||||
items chan Event
|
||||
}
|
||||
|
||||
func (f fakeDecoder) Decode() (action EventType, object interface{}, err error) {
|
||||
item, open := <-f.items
|
||||
if !open {
|
||||
return action, nil, io.EOF
|
||||
}
|
||||
return item.Type, item.Object, nil
|
||||
}
|
||||
|
||||
func (f fakeDecoder) Close() {
|
||||
close(f.items)
|
||||
}
|
||||
|
||||
func TestStreamWatcher(t *testing.T) {
|
||||
table := []Event{
|
||||
{Added, "foo"},
|
||||
}
|
||||
|
||||
fd := fakeDecoder{make(chan Event, 5)}
|
||||
sw := NewStreamWatcher(fd)
|
||||
|
||||
for _, item := range table {
|
||||
fd.items <- item
|
||||
got, open := <-sw.ResultChan()
|
||||
if !open {
|
||||
t.Errorf("unexpected early close")
|
||||
}
|
||||
if e, a := item, got; !reflect.DeepEqual(e, a) {
|
||||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
||||
sw.Stop()
|
||||
_, open := <-sw.ResultChan()
|
||||
if open {
|
||||
t.Errorf("Unexpected failure to close")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user