From c1390a0836e948e042e5615b3932dafd074ad71d Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Tue, 7 Apr 2015 12:05:38 -0700 Subject: [PATCH] version field selector field names in the client --- pkg/client/request.go | 110 ++++++++++++++++++------ pkg/kubelet/config/apiserver.go | 4 +- pkg/kubelet/kubelet.go | 2 +- plugin/pkg/scheduler/factory/factory.go | 13 +-- 4 files changed, 88 insertions(+), 41 deletions(-) diff --git a/pkg/client/request.go b/pkg/client/request.go index 0b53c4959e7..7f52805433a 100644 --- a/pkg/client/request.go +++ b/pkg/client/request.go @@ -248,36 +248,85 @@ func (r *Request) RequestURI(uri string) *Request { return r } -/* -// 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 +const ( + // A constant that clients can use to refer in a field selector to the object name field. + // Will be automatically emitted as the correct name for the API version. + ObjectNameField = "metadata.name" + PodHost = "spec.host" +) + +type clientFieldNameToAPIVersionFieldName map[string]string + +func (c clientFieldNameToAPIVersionFieldName) filterField(field, value string) (newField, newValue string, err error) { + newFieldName, ok := c[field] + if !ok { + return "", "", fmt.Errorf("%v - %v - no field mapping defined", field, value) } - var selector string - var err error - switch paramName { - case "labels": - var lsel labels.Selector - if lsel, err = labels.Parse(item); err == nil { - selector = lsel.String() - } - case "fields": - var fsel fields.Selector - if fsel, err = fields.ParseSelector(item); err == nil { - selector = fsel.String() - } - default: - err = fmt.Errorf("unknown parameter name '%s'", paramName) + return newFieldName, value, nil +} + +type resourceTypeToFieldMapping map[string]clientFieldNameToAPIVersionFieldName + +func (r resourceTypeToFieldMapping) filterField(resourceType, field, value string) (newField, newValue string, err error) { + fMapping, ok := r[resourceType] + if !ok { + return "", "", fmt.Errorf("%v - %v - %v - no field mapping defined", resourceType, field, value) } + return fMapping.filterField(field, value) +} + +type versionToResourceToFieldMapping map[string]resourceTypeToFieldMapping + +func (v versionToResourceToFieldMapping) filterField(apiVersion, resourceType, field, value string) (newField, newValue string, err error) { + rMapping, ok := v[apiVersion] + if !ok { + glog.Warningf("field selector: %v - %v - %v - %v: need to check if this is versioned correctly.", apiVersion, resourceType, field, value) + return field, value, nil + } + newField, newValue, err = rMapping.filterField(resourceType, field, value) if err != nil { - r.err = err - return r + // This is only a warning until we find and fix all of the client's usages. + glog.Warningf("field selector: %v - %v - %v - %v: need to check if this is versioned correctly.", apiVersion, resourceType, field, value) + return field, value, nil } - return r.setParam(paramName, selector) -}*/ + return newField, newValue, nil +} + +var fieldMappings = versionToResourceToFieldMapping{ + "v1beta1": resourceTypeToFieldMapping{ + "nodes": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "name", + }, + "minions": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "name", + }, + "pods": clientFieldNameToAPIVersionFieldName{ + PodHost: "DesiredState.Host", + }, + }, + "v1beta2": resourceTypeToFieldMapping{ + "nodes": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "name", + }, + "minions": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "name", + }, + "pods": clientFieldNameToAPIVersionFieldName{ + PodHost: "DesiredState.Host", + }, + }, + "v1beta3": resourceTypeToFieldMapping{ + "nodes": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "metadata.name", + }, + "minions": clientFieldNameToAPIVersionFieldName{ + ObjectNameField: "metadata.name", + }, + "pods": clientFieldNameToAPIVersionFieldName{ + PodHost: "spec.host", + }, + }, +} // FieldsSelectorParam adds the given selector as a query parameter with the name paramName. func (r *Request) FieldsSelectorParam(s fields.Selector) *Request { @@ -287,7 +336,14 @@ func (r *Request) FieldsSelectorParam(s fields.Selector) *Request { if s.Empty() { return r } - return r.setParam(api.FieldSelectorQueryParam(r.apiVersion), s.String()) + s2, err := s.Transform(func(field, value string) (newField, newValue string, err error) { + return fieldMappings.filterField(r.apiVersion, r.resource, field, value) + }) + if err != nil { + r.err = err + return r + } + return r.setParam(api.FieldSelectorQueryParam(r.apiVersion), s2.String()) } // LabelsSelectorParam adds the given selector as a query parameter diff --git a/pkg/kubelet/config/apiserver.go b/pkg/kubelet/config/apiserver.go index 2da0f709208..d2d4616f861 100644 --- a/pkg/kubelet/config/apiserver.go +++ b/pkg/kubelet/config/apiserver.go @@ -28,8 +28,8 @@ import ( ) // NewSourceApiserver creates a config source that watches and pulls from the apiserver. -func NewSourceApiserver(client *client.Client, hostname string, updates chan<- interface{}) { - lw := cache.NewListWatchFromClient(client, "pods", api.NamespaceAll, fields.OneTermEqualSelector(getHostFieldLabel(client.APIVersion()), hostname)) +func NewSourceApiserver(c *client.Client, hostname string, updates chan<- interface{}) { + lw := cache.NewListWatchFromClient(c, "pods", api.NamespaceAll, fields.OneTermEqualSelector(client.PodHost, hostname)) newSourceApiserverFromLW(lw, updates) } diff --git a/pkg/kubelet/kubelet.go b/pkg/kubelet/kubelet.go index e5721061470..53d3e4f941f 100644 --- a/pkg/kubelet/kubelet.go +++ b/pkg/kubelet/kubelet.go @@ -168,7 +168,7 @@ func NewMainKubelet( if kubeClient != nil { // TODO: cache.NewListWatchFromClient is limited as it takes a client implementation rather // than an interface. There is no way to construct a list+watcher using resource name. - fieldSelector := fields.Set{"metadata.name": hostname}.AsSelector() + fieldSelector := fields.Set{client.ObjectNameField: hostname}.AsSelector() listWatch := &cache.ListWatch{ ListFunc: func() (runtime.Object, error) { // TODO: Use List() with fieldSelector when it is supported. diff --git a/plugin/pkg/scheduler/factory/factory.go b/plugin/pkg/scheduler/factory/factory.go index 96981a01411..7f9a2bb9e21 100644 --- a/plugin/pkg/scheduler/factory/factory.go +++ b/plugin/pkg/scheduler/factory/factory.go @@ -208,7 +208,7 @@ func (f *ConfigFactory) CreateFromKeys(predicateKeys, priorityKeys util.StringSe // Returns a cache.ListWatch that finds all pods that need to be // scheduled. func (factory *ConfigFactory) createUnassignedPodLW() *cache.ListWatch { - return cache.NewListWatchFromClient(factory.Client, "pods", api.NamespaceAll, fields.Set{getHostFieldLabel(factory.Client.APIVersion()): ""}.AsSelector()) + return cache.NewListWatchFromClient(factory.Client, "pods", api.NamespaceAll, fields.Set{client.PodHost: ""}.AsSelector()) } func parseSelectorOrDie(s string) fields.Selector { @@ -224,7 +224,7 @@ func parseSelectorOrDie(s string) fields.Selector { // TODO: return a ListerWatcher interface instead? func (factory *ConfigFactory) createAssignedPodLW() *cache.ListWatch { return cache.NewListWatchFromClient(factory.Client, "pods", api.NamespaceAll, - parseSelectorOrDie(getHostFieldLabel(factory.Client.APIVersion())+"!=")) + parseSelectorOrDie(client.PodHost+"!=")) } // createMinionLW returns a cache.ListWatch that gets all changes to minions. @@ -295,15 +295,6 @@ func (factory *ConfigFactory) makeDefaultErrorFunc(backoff *podBackoff, podQueue } } -func getHostFieldLabel(apiVersion string) string { - switch apiVersion { - case "v1beta1", "v1beta2": - return "DesiredState.Host" - default: - return "spec.host" - } -} - // nodeEnumerator allows a cache.Poller to enumerate items in an api.NodeList type nodeEnumerator struct { *api.NodeList