Skip to content

Commit

Permalink
Adding fields selector
Browse files Browse the repository at this point in the history
  • Loading branch information
sdminonne committed Mar 10, 2015
1 parent 9aa7449 commit 925fa6b
Show file tree
Hide file tree
Showing 46 changed files with 725 additions and 116 deletions.
7 changes: 4 additions & 3 deletions pkg/apiserver/apiserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
apierrs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
Expand Down Expand Up @@ -216,7 +217,7 @@ type SimpleRESTStorage struct {
// These are set when Watch is called
fakeWatch *watch.FakeWatcher
requestedLabelSelector labels.Selector
requestedFieldSelector labels.Selector
requestedFieldSelector fields.Selector
requestedResourceVersion string
requestedResourceNamespace string

Expand All @@ -230,7 +231,7 @@ type SimpleRESTStorage struct {
injectedFunction func(obj runtime.Object) (returnObj runtime.Object, err error)
}

func (storage *SimpleRESTStorage) List(ctx api.Context, label, field labels.Selector) (runtime.Object, error) {
func (storage *SimpleRESTStorage) List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) {
storage.checkContext(ctx)
result := &SimpleList{
Items: storage.list,
Expand Down Expand Up @@ -296,7 +297,7 @@ func (storage *SimpleRESTStorage) Update(ctx api.Context, obj runtime.Object) (r
}

// Implement ResourceWatcher.
func (storage *SimpleRESTStorage) Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error) {
func (storage *SimpleRESTStorage) Watch(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
storage.checkContext(ctx)
storage.requestedLabelSelector = label
storage.requestedFieldSelector = field
Expand Down
5 changes: 3 additions & 2 deletions pkg/apiserver/interfaces.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package apiserver

import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
Expand All @@ -38,7 +39,7 @@ type RESTLister interface {
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
NewList() runtime.Object
// List selects resources in the storage which match to the selector.
List(ctx api.Context, label, field labels.Selector) (runtime.Object, error)
List(ctx api.Context, label labels.Selector, field fields.Selector) (runtime.Object, error)
}

type RESTGetter interface {
Expand Down Expand Up @@ -100,7 +101,7 @@ type ResourceWatcher interface {
// are supported; an error should be returned if 'field' tries to select on a field that
// isn't supported. 'resourceVersion' allows for continuing/starting a watch at a
// particular version.
Watch(ctx api.Context, label, field labels.Selector, resourceVersion string) (watch.Interface, error)
Watch(ctx api.Context, label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
}

// Redirector know how to return a remote resource's location.
Expand Down
5 changes: 3 additions & 2 deletions pkg/apiserver/resthandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/admission"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"

Expand Down Expand Up @@ -82,7 +83,7 @@ func GetResource(r RESTGetter, ctxFn ContextFunc, namer ScopeNamer, codec runtim
}
}

func parseSelectorQueryParams(query url.Values, version, apiResource string) (label, field labels.Selector, err error) {
func parseSelectorQueryParams(query url.Values, version, apiResource string) (label labels.Selector, field fields.Selector, err error) {
labelString := query.Get("labels")
label, err = labels.Parse(labelString)
if err != nil {
Expand All @@ -93,7 +94,7 @@ func parseSelectorQueryParams(query url.Values, version, apiResource string) (la
return api.Scheme.ConvertFieldLabel(version, apiResource, label, value)
}
fieldString := query.Get("fields")
field, err = labels.ParseAndTransformSelector(fieldString, convertToInternalVersionFunc)
field, err = fields.ParseAndTransformSelector(fieldString, convertToInternalVersionFunc)
if err != nil {
return nil, nil, errors.NewBadRequest(fmt.Sprintf("The 'fields' selector parameter (%s) could not be parsed: %v", fieldString, err))
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/apiserver/watch_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import (
"testing"

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
Expand Down Expand Up @@ -209,7 +210,7 @@ func TestWatchParamParsing(t *testing.T) {

for _, item := range table {
simpleStorage.requestedLabelSelector = labels.Everything()
simpleStorage.requestedFieldSelector = labels.Everything()
simpleStorage.requestedFieldSelector = fields.Everything()
simpleStorage.requestedResourceVersion = "5" // Prove this is set in all cases
simpleStorage.requestedResourceNamespace = ""
dest.RawQuery = item.rawQuery
Expand Down
18 changes: 12 additions & 6 deletions pkg/client/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
Expand Down Expand Up @@ -255,28 +256,34 @@ func (r *Request) RequestURI(uri string) *Request {
return r
}

// ParseSelectorParam parses the given string as a resource label selector.
// ParseSelectorParam parses the given string as a resource selector.
// This is a convenience function so you don't have to first check that it's a
// validly formatted selector.
func (r *Request) ParseSelectorParam(paramName, item string) *Request {
if r.err != nil {
return r
}
var sel labels.Selector
var selector string
var err error
switch paramName {
case "labels":
sel, err = labels.Parse(item)
var lsel labels.Selector
if lsel, err = labels.Parse(item); err == nil {
selector = lsel.String()
}
case "fields":
sel, err = labels.ParseSelector(item)
var fsel fields.Selector
if fsel, err = fields.ParseSelector(item); err == nil {
selector = fsel.String()
}
default:
err = fmt.Errorf("unknown parameter name '%s'", paramName)
}
if err != nil {
r.err = err
return r
}
return r.setParam(paramName, sel.String())
return r.setParam(paramName, selector)
}

// SelectorParam adds the given selector as a query parameter with the name paramName.
Expand Down Expand Up @@ -537,7 +544,6 @@ func (r *Request) DoRaw() ([]byte, error) {
if err != nil {
return nil, err
}

r.resp, err = client.Do(r.req)
if err != nil {
return nil, err
Expand Down
19 changes: 19 additions & 0 deletions pkg/fields/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Copyright 2015 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 fields implements a simple field system, parsing and matching
// selectors with sets of fields.
package fields
62 changes: 62 additions & 0 deletions pkg/fields/fields.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
Copyright 2015 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 fields

import (
"sort"
"strings"
)

// Fields allows you to present fields independently from their storage.
type Fields interface {
// Has returns whether the provided field exists.
Has(field string) (exists bool)

// Get returns the value for the provided field.
Get(field string) (value string)
}

// Set is a map of field:value. It implements Fields.
type Set map[string]string

// String returns all fields listed as a human readable string.
// Conveniently, exactly the format that ParseSelector takes.
func (ls Set) String() string {
selector := make([]string, 0, len(ls))
for key, value := range ls {
selector = append(selector, key+"="+value)
}
// Sort for determinism.
sort.StringSlice(selector).Sort()
return strings.Join(selector, ",")
}

// Has returns whether the provided field exists in the map.
func (ls Set) Has(field string) bool {
_, exists := ls[field]
return exists
}

// Get returns the value in the map for the provided field.
func (ls Set) Get(field string) string {
return ls[field]
}

// AsSelector converts fields into a selectors.
func (ls Set) AsSelector() Selector {
return SelectorFromSet(ls)
}
57 changes: 57 additions & 0 deletions pkg/fields/fields_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
Copyright 2015 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 fields

import (
"testing"
)

func matches(t *testing.T, ls Set, want string) {
if ls.String() != want {
t.Errorf("Expected '%s', but got '%s'", want, ls.String())
}
}

func TestSetString(t *testing.T) {
matches(t, Set{"x": "y"}, "x=y")
matches(t, Set{"foo": "bar"}, "foo=bar")
matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar")
}

func TestFieldHas(t *testing.T) {
fieldHasTests := []struct {
Ls Fields
Key string
Has bool
}{
{Set{"x": "y"}, "x", true},
{Set{"x": ""}, "x", true},
{Set{"x": "y"}, "foo", false},
}
for _, lh := range fieldHasTests {
if has := lh.Ls.Has(lh.Key); has != lh.Has {
t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has)
}
}
}

func TestFieldGet(t *testing.T) {
ls := Set{"x": "y"}
if ls.Get("x") != "y" {
t.Errorf("Set.Get is broken")
}
}
Loading

0 comments on commit 925fa6b

Please sign in to comment.