Skip to content

Commit

Permalink
Convert List query parameters via object conversion
Browse files Browse the repository at this point in the history
Convert url.Values -> an object, with appropriate versioning. ListOptions
should also expose parameter names to swagger.
  • Loading branch information
smarterclayton committed Mar 27, 2015
1 parent a2801a5 commit 1618c39
Show file tree
Hide file tree
Showing 29 changed files with 417 additions and 98 deletions.
10 changes: 10 additions & 0 deletions hack/lib/util.sh
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,16 @@ kube::util::wait_for_url() {
return 1
}

# Create a temp dir that'll be deleted at the end of this bash session.
#
# Vars set:
# KUBE_TEMP
kube::util::ensure-temp-dir() {
if [[ -z ${KUBE_TEMP-} ]]; then
KUBE_TEMP=$(mktemp -d -t kubernetes.XXXXXX)
fi
}

# This figures out the host platform without relying on golang. We need this as
# we don't want a golang install to be a prerequisite to building yet we need
# this info to figure out where the final binaries are placed.
Expand Down
17 changes: 17 additions & 0 deletions hack/test-cmd.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ function cleanup()
[[ -n ${PROXY_PID-} ]] && kill ${PROXY_PID} 1>&2 2>/dev/null

kube::etcd::cleanup
rm -rf "${KUBE_TEMP}"

kube::log::status "Clean up complete"
}

trap cleanup EXIT SIGINT

kube::util::ensure-temp-dir
kube::etcd::start

ETCD_HOST=${ETCD_HOST:-127.0.0.1}
Expand Down Expand Up @@ -533,6 +535,7 @@ __EOF__

kube::test::describe_object_assert nodes "127.0.0.1" "Name:" "Labels:" "CreationTimestamp:" "Conditions:" "Addresses:" "Capacity:" "Pods:"


###########
# Minions #
###########
Expand All @@ -548,13 +551,27 @@ __EOF__
kube::test::describe_object_assert minions "127.0.0.1" "Name:" "Conditions:" "Addresses:" "Capacity:" "Pods:"
fi


#####################
# Retrieve multiple #
#####################

kube::log::status "Testing kubectl(${version}:multiget)"
kube::test::get_object_assert 'nodes/127.0.0.1 service/kubernetes' "{{range.items}}{{.$id_field}}:{{end}}" '127.0.0.1:kubernetes:'


###########
# Swagger #
###########

if [[ -n "${version}" ]]; then
# Verify schema
file="${KUBE_TEMP}/schema-${version}.json"
curl -s "http://127.0.0.1:${API_PORT}/swaggerapi/api/${version}" > "${file}"
[[ "$(grep "list of returned" "${file}")" ]]
[[ "$(grep "list of pods" "${file}")" ]]
fi

kube::test::clear_all
done

Expand Down
38 changes: 38 additions & 0 deletions pkg/api/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package api
import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"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 All @@ -28,12 +30,48 @@ import (
var Codec = runtime.CodecFor(Scheme, "")

func init() {
Scheme.AddDefaultingFuncs(
func(obj *ListOptions) {
obj.LabelSelector = labels.Everything()
obj.FieldSelector = fields.Everything()
},
)
Scheme.AddConversionFuncs(
func(in *util.Time, out *util.Time, s conversion.Scope) error {
// Cannot deep copy these, because time.Time has unexported fields.
*out = *in
return nil
},
func(in *string, out *labels.Selector, s conversion.Scope) error {
selector, err := labels.Parse(*in)
if err != nil {
return err
}
*out = selector
return nil
},
func(in *string, out *fields.Selector, s conversion.Scope) error {
selector, err := fields.ParseSelector(*in)
if err != nil {
return err
}
*out = selector
return nil
},
func(in *labels.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
},
func(in *fields.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
},
func(in *resource.Quantity, out *resource.Quantity, s conversion.Scope) error {
// Cannot deep copy these, because inf.Dec has unexported fields.
*out = *in.Copy()
Expand Down
8 changes: 8 additions & 0 deletions pkg/api/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"

"github.com/davecgh/go-spew/spew"
Expand Down Expand Up @@ -63,6 +65,12 @@ var Semantic = conversion.EqualitiesOrDie(
func(a, b util.Time) bool {
return a.UTC() == b.UTC()
},
func(a, b labels.Selector) bool {
return a.String() == b.String()
},
func(a, b fields.Selector) bool {
return a.String() == b.String()
},
)

var standardResources = util.NewStringSet(
Expand Down
1 change: 1 addition & 0 deletions pkg/api/meta/restmapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

// TODO: move everything in this file to pkg/api/rest
package meta

import (
Expand Down
8 changes: 8 additions & 0 deletions pkg/api/meta/restmapper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,18 @@ func (fakeCodec) DecodeInto([]byte, runtime.Object) error {

type fakeConvertor struct{}

func (fakeConvertor) Convert(in, out interface{}) error {
return nil
}

func (fakeConvertor) ConvertToVersion(in runtime.Object, _ string) (runtime.Object, error) {
return in, nil
}

func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
return label, value, nil
}

var validCodec = fakeCodec{}
var validAccessor = resourceAccessor{}
var validConvertor = fakeConvertor{}
Expand Down
6 changes: 4 additions & 2 deletions pkg/api/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&DeleteOptions{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
&PersistentVolumeClaimList{},
&DeleteOptions{},
&ListOptions{},
)
// Legacy names are supported
Scheme.AddKnownTypeWithName("", "Minion", &Node{})
Expand Down Expand Up @@ -90,8 +91,9 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*DeleteOptions) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}
func (*PersistentVolumeClaimList) IsAnAPIObject() {}
func (*DeleteOptions) IsAnAPIObject() {}
func (*ListOptions) IsAnAPIObject() {}
2 changes: 1 addition & 1 deletion pkg/api/serialization_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ func TestList(t *testing.T) {
}

var nonRoundTrippableTypes = util.NewStringSet("ContainerManifest", "ContainerManifestList")
var nonInternalRoundTrippableTypes = util.NewStringSet("List")
var nonInternalRoundTrippableTypes = util.NewStringSet("List", "ListOptions")

func TestRoundTripTypes(t *testing.T) {
// api.Scheme.Log(t)
Expand Down
7 changes: 7 additions & 0 deletions pkg/api/testing/fuzzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (

"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
Expand Down Expand Up @@ -88,6 +90,11 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
j.SelfLink = c.RandString()
},
func(j *api.ListOptions, c fuzz.Continue) {
// TODO: add some parsing
j.LabelSelector, _ = labels.Parse("a=b")
j.FieldSelector, _ = fields.ParseSelector("a=b")
},
func(j *api.PodPhase, c fuzz.Continue) {
statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
*j = statuses[c.Rand.Intn(len(statuses))]
Expand Down
12 changes: 12 additions & 0 deletions pkg/api/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package api

import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/types"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
Expand Down Expand Up @@ -1168,6 +1170,16 @@ type DeleteOptions struct {
GracePeriodSeconds *int64 `json:"gracePeriodSeconds"`
}

// ListOptions is the query options to a standard REST list call
type ListOptions struct {
TypeMeta `json:",inline"`

// A selector based on labels
LabelSelector labels.Selector
// A selector based on fields
FieldSelector fields.Selector
}

// Status is a return value for calls that don't return other objects.
// TODO: this could go in apiserver, but I'm including it here so clients needn't
// import both.
Expand Down
6 changes: 4 additions & 2 deletions pkg/api/unversioned.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,20 @@ func PreV1Beta3(version string) bool {
return version == "v1beta1" || version == "v1beta2"
}

// TODO: remove me when watch is refactored
func LabelSelectorQueryParam(version string) string {
if PreV1Beta3(version) {
return "labels"
}
return "label-selector"
return "labelSelector"
}

// TODO: remove me when watch is refactored
func FieldSelectorQueryParam(version string) string {
if PreV1Beta3(version) {
return "fields"
}
return "field-selector"
return "fieldSelector"
}

// String returns available api versions as a human-friendly version string.
Expand Down
8 changes: 4 additions & 4 deletions pkg/api/v1beta1/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -1494,7 +1494,7 @@ func init() {
}

// Add field conversion funcs.
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "pods",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "Pod",
func(label, value string) (string, string, error) {
switch label {
case "name":
Expand All @@ -1514,7 +1514,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "replicationControllers",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "ReplicationController",
func(label, value string) (string, string, error) {
switch label {
case "name":
Expand All @@ -1529,7 +1529,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "events",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "Event",
func(label, value string) (string, string, error) {
switch label {
case "involvedObject.kind",
Expand All @@ -1551,7 +1551,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "namespaces",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "Namespace",
func(label, value string) (string, string, error) {
switch label {
case "status.phase":
Expand Down
6 changes: 4 additions & 2 deletions pkg/api/v1beta1/register.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ func init() {
&NamespaceList{},
&Secret{},
&SecretList{},
&DeleteOptions{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
&PersistentVolumeClaimList{},
&DeleteOptions{},
&ListOptions{},
)
// Future names are supported
api.Scheme.AddKnownTypeWithName("v1beta1", "Node", &Minion{})
Expand Down Expand Up @@ -97,8 +98,9 @@ func (*Namespace) IsAnAPIObject() {}
func (*NamespaceList) IsAnAPIObject() {}
func (*Secret) IsAnAPIObject() {}
func (*SecretList) IsAnAPIObject() {}
func (*DeleteOptions) IsAnAPIObject() {}
func (*PersistentVolume) IsAnAPIObject() {}
func (*PersistentVolumeList) IsAnAPIObject() {}
func (*PersistentVolumeClaim) IsAnAPIObject() {}
func (*PersistentVolumeClaimList) IsAnAPIObject() {}
func (*DeleteOptions) IsAnAPIObject() {}
func (*ListOptions) IsAnAPIObject() {}
10 changes: 10 additions & 0 deletions pkg/api/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -985,6 +985,16 @@ type DeleteOptions struct {
GracePeriodSeconds *int64 `json:"gracePeriodSeconds" description:"the duration in seconds to wait before deleting this object; defaults to a per object value if not specified; zero means delete immediately"`
}

// ListOptions is the query options to a standard REST list call
type ListOptions struct {
TypeMeta `json:",inline"`

// A selector based on labels
LabelSelector string `json:"labels" description:"a selector to restrict the list of returned objects by their labels; defaults to everything"`
// A selector based on fields
FieldSelector string `json:"fields" description:"a selector to restrict the list of returned objects by their fields; defaults to everything"`
}

// Status is a return value for calls that don't return other objects.
// TODO: this could go in apiserver, but I'm including it here so clients needn't
// import both.
Expand Down
8 changes: 4 additions & 4 deletions pkg/api/v1beta2/conversion.go
Original file line number Diff line number Diff line change
Expand Up @@ -1420,7 +1420,7 @@ func init() {
}

// Add field conversion funcs.
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "pods",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "Pod",
func(label, value string) (string, string, error) {
switch label {
case "name":
Expand All @@ -1440,7 +1440,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "replicationControllers",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "ReplicationController",
func(label, value string) (string, string, error) {
switch label {
case "name":
Expand All @@ -1455,7 +1455,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "events",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta2", "Event",
func(label, value string) (string, string, error) {
switch label {
case "involvedObject.kind",
Expand All @@ -1477,7 +1477,7 @@ func init() {
// If one of the conversion functions is malformed, detect it immediately.
panic(err)
}
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "namespaces",
err = newer.Scheme.AddFieldLabelConversionFunc("v1beta1", "Namespace",
func(label, value string) (string, string, error) {
switch label {
case "status.phase":
Expand Down
Loading

0 comments on commit 1618c39

Please sign in to comment.