Add watch mechanism to apiserver

Implemented via HTTP and websocket. A test is present but this isn't
yet wired into anything.

Eventual purpose of this is to allow a scheduler to watch for new pods.
Or allow replication controller to watch for new items it controlls.
Generally, it'll be good to turn everything possible into a push instead
of a poll.
This commit is contained in:
Daniel Smith
2014-07-17 10:05:14 -07:00
parent af0ded703f
commit eda30d4f20
7 changed files with 535 additions and 29 deletions

19
pkg/watch/doc.go Normal file
View File

@@ -0,0 +1,19 @@
/*
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 contains a generic watchable interface, and a fake for
// testing code that uses the watch interface.
package watch

96
pkg/watch/watch.go Normal file
View File

@@ -0,0 +1,96 @@
/*
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"
)
// Interface can be implemented by anything that knows how to watch and report changes.
type Interface interface {
// Stops watching. Will close the channel returned by ResultChan(). Releases
// any resources used by the watch.
Stop()
// Returns a chan which will receive all the events. If an error occurs
// or Stop() is called, this channel will be closed, in which case the
// watch should be completely cleaned up.
ResultChan() <-chan *Event
}
// EventType defines the possible types of events.
type EventType string
const (
Added EventType = "ADDED"
Modified EventType = "MODIFIED"
Deleted EventType = "DELETED"
)
// Event represents a single event to a watched resource.
type Event struct {
Type EventType
// If Type == Deleted, then this is the state of the object
// immediately before deletion.
Object interface{}
}
// FakeWatcher lets you test anything that consumes a watch.Interface; threadsafe.
type FakeWatcher struct {
result chan *Event
Stopped bool
sync.Mutex
}
func NewFake() *FakeWatcher {
return &FakeWatcher{
result: make(chan *Event),
}
}
// Stop implements Interface.Stop().
func (f *FakeWatcher) Stop() {
f.Lock()
defer f.Unlock()
close(f.result)
f.Stopped = true
}
func (f *FakeWatcher) ResultChan() <-chan *Event {
return f.result
}
// Add sends an add event.
func (f *FakeWatcher) Add(obj interface{}) {
f.result <- &Event{Added, obj}
}
// Modify sends a modify event.
func (f *FakeWatcher) Modify(obj interface{}) {
f.result <- &Event{Modified, obj}
}
// Delete sends a delete event.
func (f *FakeWatcher) Delete(lastValue interface{}) {
f.result <- &Event{Deleted, lastValue}
}
// Action sends an event of the requested type, for table-based testing.
func (f *FakeWatcher) Action(action EventType, obj interface{}) {
f.result <- &Event{action, obj}
}

66
pkg/watch/watch_test.go Normal file
View File

@@ -0,0 +1,66 @@
/*
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 (
"testing"
)
func TestFake(t *testing.T) {
f := NewFake()
table := []struct {
t EventType
s string
}{
{Added, "foo"},
{Modified, "qux"},
{Modified, "bar"},
{Deleted, "bar"},
}
// Prove that f implements Interface by phrasing this as a function.
consumer := func(w Interface) {
for _, expect := range table {
got, ok := <-w.ResultChan()
if !ok {
t.Fatalf("closed early")
}
if e, a := expect.t, got.Type; e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
if a, ok := got.Object.(string); !ok || a != expect.s {
t.Fatalf("Expected %v, got %v", expect.s, a)
}
}
_, stillOpen := <-w.ResultChan()
if stillOpen {
t.Fatal("Never stopped")
}
}
sender := func() {
f.Add("foo")
f.Action(Modified, "qux")
f.Modify("bar")
f.Delete("bar")
f.Stop()
}
go sender()
consumer(f)
}