diff --git a/pkg/registry/event/registry.go b/pkg/registry/event/registry.go index e4cd9073a8f..f36d0163e5b 100644 --- a/pkg/registry/event/registry.go +++ b/pkg/registry/event/registry.go @@ -22,13 +22,14 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" + etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" ) // registry implements custom changes to generic.Etcd. type registry struct { - *generic.Etcd + *etcdgeneric.Etcd ttl uint64 } @@ -42,7 +43,7 @@ func (r registry) Create(ctx api.Context, id string, obj runtime.Object) error { // EtcdHelper. ttl is the time that Events will be retained by the system. func NewEtcdRegistry(h tools.EtcdHelper, ttl uint64) generic.Registry { return registry{ - Etcd: &generic.Etcd{ + Etcd: &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.Event{} }, NewListFunc: func() runtime.Object { return &api.EventList{} }, EndpointName: "events", diff --git a/pkg/registry/event/rest.go b/pkg/registry/event/rest.go index b43088e2a29..83031f6cdb1 100644 --- a/pkg/registry/event/rest.go +++ b/pkg/registry/event/rest.go @@ -89,14 +89,14 @@ func (rs *REST) getAttrs(obj runtime.Object) (objLabels, objFields labels.Set, e return nil, nil, fmt.Errorf("invalid object type") } return labels.Set{}, labels.Set{ - "InvolvedObject.Kind": event.InvolvedObject.Kind, - "InvolvedObject.Name": event.InvolvedObject.Name, - "InvolvedObject.UID": event.InvolvedObject.UID, - "InvolvedObject.APIVersion": event.InvolvedObject.APIVersion, - "InvolvedObject.ResourceVersion": fmt.Sprintf("%s", event.InvolvedObject.ResourceVersion), - "InvolvedObject.FieldPath": event.InvolvedObject.FieldPath, - "Status": event.Status, - "Reason": event.Reason, + "involvedObject.kind": event.InvolvedObject.Kind, + "involvedObject.name": event.InvolvedObject.Name, + "involvedObject.uid": event.InvolvedObject.UID, + "involvedObject.apiVersion": event.InvolvedObject.APIVersion, + "involvedObject.resourceVersion": fmt.Sprintf("%s", event.InvolvedObject.ResourceVersion), + "involvedObject.fieldPath": event.InvolvedObject.FieldPath, + "status": event.Status, + "reason": event.Reason, }, nil } diff --git a/pkg/registry/event/rest_test.go b/pkg/registry/event/rest_test.go index 84c42727c9d..820562485ca 100644 --- a/pkg/registry/event/rest_test.go +++ b/pkg/registry/event/rest_test.go @@ -114,14 +114,14 @@ func TestRESTgetAttrs(t *testing.T) { t.Errorf("diff: %s", util.ObjectDiff(e, a)) } expect := labels.Set{ - "InvolvedObject.Kind": "Pod", - "InvolvedObject.Name": "foo", - "InvolvedObject.UID": "long uid string", - "InvolvedObject.APIVersion": testapi.Version(), - "InvolvedObject.ResourceVersion": "0", - "InvolvedObject.FieldPath": "", - "Status": "tested", - "Reason": "forTesting", + "involvedObject.kind": "Pod", + "involvedObject.name": "foo", + "involvedObject.uid": "long uid string", + "involvedObject.apiVersion": testapi.Version(), + "involvedObject.resourceVersion": "0", + "involvedObject.fieldPath": "", + "status": "tested", + "reason": "forTesting", } if e, a := expect, field; !reflect.DeepEqual(e, a) { t.Errorf("diff: %s", util.ObjectDiff(e, a)) @@ -186,7 +186,7 @@ func TestRESTList(t *testing.T) { reg.ObjectList = &api.EventList{ Items: []api.Event{*eventA, *eventB, *eventC}, } - got, err := rest.List(api.NewContext(), labels.Everything(), labels.Set{"Status": "tested"}.AsSelector()) + got, err := rest.List(api.NewContext(), labels.Everything(), labels.Set{"status": "tested"}.AsSelector()) if err != nil { t.Fatalf("Unexpected error %v", err) } diff --git a/pkg/registry/generic/etcd/doc.go b/pkg/registry/generic/etcd/doc.go new file mode 100644 index 00000000000..c96fbd56f5c --- /dev/null +++ b/pkg/registry/generic/etcd/doc.go @@ -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 etcd has a generic implementation of a registry that +// stores things in etcd. +package etcd diff --git a/pkg/registry/generic/etcd.go b/pkg/registry/generic/etcd/etcd.go similarity index 76% rename from pkg/registry/generic/etcd.go rename to pkg/registry/generic/etcd/etcd.go index e43405635dd..90ca035b707 100644 --- a/pkg/registry/generic/etcd.go +++ b/pkg/registry/generic/etcd/etcd.go @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package generic +package etcd import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" etcderr "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors/etcd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/watch" @@ -50,35 +51,13 @@ type Etcd struct { } // List returns a list of all the items matching m. -func (e *Etcd) List(ctx api.Context, m Matcher) (runtime.Object, error) { +func (e *Etcd) List(ctx api.Context, m generic.Matcher) (runtime.Object, error) { list := e.NewListFunc() err := e.Helper.ExtractToList(e.KeyRoot, list) if err != nil { return nil, err } - return FilterList(list, m) -} - -// FilterList filters any list object that conforms to the api conventions, -// provided that 'm' works with the concrete type of list. -func FilterList(list runtime.Object, m Matcher) (filtered runtime.Object, err error) { - // TODO: push a matcher down into tools.EtcdHelper to avoid all this - // nonsense. This is a lot of unnecessary copies. - items, err := runtime.ExtractList(list) - if err != nil { - return nil, err - } - var filteredItems []runtime.Object - for _, obj := range items { - if match, err := m.Matches(obj); err == nil && match { - filteredItems = append(filteredItems, obj) - } - } - err = runtime.SetList(list, filteredItems) - if err != nil { - return nil, err - } - return list, nil + return generic.FilterList(list, m) } // Create inserts a new item. @@ -89,6 +68,7 @@ func (e *Etcd) Create(ctx api.Context, id string, obj runtime.Object) error { // Update updates the item. func (e *Etcd) Update(ctx api.Context, id string, obj runtime.Object) error { + // TODO: verify that SetObj checks ResourceVersion before succeeding. err := e.Helper.SetObj(e.KeyFunc(id), obj) return etcderr.InterpretUpdateError(err, e.EndpointName, id) } @@ -111,7 +91,7 @@ func (e *Etcd) Delete(ctx api.Context, id string) error { // Watch starts a watch for the items that m matches. // TODO: Detect if m references a single object instead of a list. -func (e *Etcd) Watch(ctx api.Context, m Matcher, resourceVersion uint64) (watch.Interface, error) { +func (e *Etcd) Watch(ctx api.Context, m generic.Matcher, resourceVersion uint64) (watch.Interface, error) { return e.Helper.WatchList(e.KeyRoot, resourceVersion, func(obj runtime.Object) bool { matches, err := m.Matches(obj) return err == nil && matches diff --git a/pkg/registry/generic/etcd_test.go b/pkg/registry/generic/etcd/etcd_test.go similarity index 99% rename from pkg/registry/generic/etcd_test.go rename to pkg/registry/generic/etcd/etcd_test.go index f34eeeaca05..0772bd96fc6 100644 --- a/pkg/registry/generic/etcd_test.go +++ b/pkg/registry/generic/etcd/etcd_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package generic +package etcd import ( "fmt" @@ -25,6 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" "github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi" + "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" "github.com/GoogleCloudPlatform/kubernetes/pkg/tools" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" @@ -90,7 +91,7 @@ func TestEtcdList(t *testing.T) { table := map[string]struct { in tools.EtcdResponseWithError - m Matcher + m generic.Matcher out runtime.Object succeed bool }{ diff --git a/pkg/registry/generic/registry.go b/pkg/registry/generic/registry.go index e34373c0957..d368905cd8e 100644 --- a/pkg/registry/generic/registry.go +++ b/pkg/registry/generic/registry.go @@ -54,6 +54,19 @@ type Matcher interface { Matches(obj runtime.Object) (bool, error) } +// MatcherFunc makes a matcher from the provided function. For easy definition +// of matchers for testing. +func MatcherFunc(f func(obj runtime.Object) (bool, error)) Matcher { + return matcherFunc(f) +} + +type matcherFunc func(obj runtime.Object) (bool, error) + +// Matches calls the embedded function. +func (m matcherFunc) Matches(obj runtime.Object) (bool, error) { + return m(obj) +} + // Registry knows how to store & list any runtime.Object. Can be used for // any object types which don't require special features from the storage // layer. @@ -65,3 +78,29 @@ type Registry interface { Delete(ctx api.Context, id string) error Watch(ctx api.Context, m Matcher, resourceVersion uint64) (watch.Interface, error) } + +// FilterList filters any list object that conforms to the api conventions, +// provided that 'm' works with the concrete type of list. +func FilterList(list runtime.Object, m Matcher) (filtered runtime.Object, err error) { + // TODO: push a matcher down into tools.EtcdHelper to avoid all this + // nonsense. This is a lot of unnecessary copies. + items, err := runtime.ExtractList(list) + if err != nil { + return nil, err + } + var filteredItems []runtime.Object + for _, obj := range items { + match, err := m.Matches(obj) + if err != nil { + return nil, err + } + if match { + filteredItems = append(filteredItems, obj) + } + } + err = runtime.SetList(list, filteredItems) + if err != nil { + return nil, err + } + return list, nil +} diff --git a/pkg/registry/generic/registry_test.go b/pkg/registry/generic/registry_test.go index e0c5b843b9b..81c2ddc731a 100644 --- a/pkg/registry/generic/registry_test.go +++ b/pkg/registry/generic/registry_test.go @@ -18,15 +18,23 @@ package generic import ( "errors" + "reflect" "testing" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" ) -type Ignored struct{} +type Ignored struct { + ID string +} -func (*Ignored) IsAnAPIObject() {} +type IgnoredList struct { + Items []Ignored +} + +func (*Ignored) IsAnAPIObject() {} +func (*IgnoredList) IsAnAPIObject() {} func TestSelectionPredicate(t *testing.T) { table := map[string]struct { @@ -90,3 +98,38 @@ func TestSelectionPredicate(t *testing.T) { } } } + +func TestFilterList(t *testing.T) { + try := &IgnoredList{ + Items: []Ignored{ + {"foo"}, + {"bar"}, + {"baz"}, + {"qux"}, + {"zot"}, + }, + } + expect := &IgnoredList{ + Items: []Ignored{ + {"bar"}, + {"baz"}, + }, + } + + got, err := FilterList(try, + MatcherFunc(func(obj runtime.Object) (bool, error) { + i, ok := obj.(*Ignored) + if !ok { + return false, errors.New("wrong type") + } + return i.ID[0] == 'b', nil + }), + ) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + + if e, a := expect, got; !reflect.DeepEqual(e, a) { + t.Errorf("Expected %#v, got %#v", e, a) + } +}