From 4359c79f537946d96e2185df95c429c43f368db3 Mon Sep 17 00:00:00 2001 From: deads2k Date: Tue, 13 Sep 2016 13:32:51 -0400 Subject: [PATCH] add FirstHitRESTMapper for adding thirdparty resources --- pkg/api/meta/firsthit_restmapper.go | 97 ++++++++++++++++++++++++ pkg/client/typed/discovery/restmapper.go | 9 +++ pkg/kubectl/cmd/util/factory.go | 9 ++- 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 pkg/api/meta/firsthit_restmapper.go diff --git a/pkg/api/meta/firsthit_restmapper.go b/pkg/api/meta/firsthit_restmapper.go new file mode 100644 index 00000000000..8a21b54aabb --- /dev/null +++ b/pkg/api/meta/firsthit_restmapper.go @@ -0,0 +1,97 @@ +/* +Copyright 2014 The Kubernetes Authors. + +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 meta + +import ( + "fmt" + + "k8s.io/kubernetes/pkg/api/unversioned" + utilerrors "k8s.io/kubernetes/pkg/util/errors" +) + +// FirstHitRESTMapper is a wrapper for multiple RESTMappers which returns the +// first successful result for the singular requests +type FirstHitRESTMapper struct { + MultiRESTMapper +} + +func (m FirstHitRESTMapper) String() string { + return fmt.Sprintf("FirstHitRESTMapper{\n\t%v\n}", m.MultiRESTMapper) +} + +func (m FirstHitRESTMapper) ResourceFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionResource, error) { + errors := []error{} + for _, t := range m.MultiRESTMapper { + ret, err := t.ResourceFor(resource) + if err == nil { + return ret, nil + } + errors = append(errors, err) + } + + return unversioned.GroupVersionResource{}, collapseAggregateErrors(errors) +} + +func (m FirstHitRESTMapper) KindFor(resource unversioned.GroupVersionResource) (unversioned.GroupVersionKind, error) { + errors := []error{} + for _, t := range m.MultiRESTMapper { + ret, err := t.KindFor(resource) + if err == nil { + return ret, nil + } + errors = append(errors, err) + } + + return unversioned.GroupVersionKind{}, collapseAggregateErrors(errors) +} + +// RESTMapping provides the REST mapping for the resource based on the +// kind and version. This implementation supports multiple REST schemas and +// return the first match. +func (m FirstHitRESTMapper) RESTMapping(gk unversioned.GroupKind, versions ...string) (*RESTMapping, error) { + errors := []error{} + for _, t := range m.MultiRESTMapper { + ret, err := t.RESTMapping(gk, versions...) + if err == nil { + return ret, nil + } + errors = append(errors, err) + } + + return nil, collapseAggregateErrors(errors) +} + +// collapseAggregateErrors returns the minimal errors. it handles empty as nil, handles one item in a list +// by returning the item, and collapses all NoMatchErrors to a single one (since they should all be the same) +func collapseAggregateErrors(errors []error) error { + if len(errors) == 0 { + return nil + } + if len(errors) == 1 { + return errors[0] + } + + allNoMatchErrors := true + for _, err := range errors { + allNoMatchErrors = allNoMatchErrors && IsNoMatchError(err) + } + if allNoMatchErrors { + return errors[0] + } + + return utilerrors.NewAggregate(errors) +} diff --git a/pkg/client/typed/discovery/restmapper.go b/pkg/client/typed/discovery/restmapper.go index 7f6a0d1f40c..49cbe15a37f 100644 --- a/pkg/client/typed/discovery/restmapper.go +++ b/pkg/client/typed/discovery/restmapper.go @@ -17,6 +17,7 @@ limitations under the License. package discovery import ( + "fmt" "sync" "k8s.io/kubernetes/pkg/api/errors" @@ -259,5 +260,13 @@ func (d *DeferredDiscoveryRESTMapper) ResourceSingularizer(resource string) (sin return del.ResourceSingularizer(resource) } +func (d *DeferredDiscoveryRESTMapper) String() string { + del, err := d.getDelegate() + if err != nil { + return fmt.Sprintf("DeferredDiscoveryRESTMapper{%v}", err) + } + return fmt.Sprintf("DeferredDiscoveryRESTMapper{\n\t%v\n}", del) +} + // Make sure it satisfies the interface var _ meta.RESTMapper = &DeferredDiscoveryRESTMapper{} diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 187423e08c3..b8305bcb867 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -315,9 +315,12 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { // TODO eliminate this once we're truly generic. thirdPartyResourceDataMapper := meta.NewDefaultRESTMapper([]unversioned.GroupVersion{extensionsv1beta1.SchemeGroupVersion}, registered.InterfacesFor) thirdPartyResourceDataMapper.Add(extensionsv1beta1.SchemeGroupVersion.WithKind("ThirdPartyResourceData"), meta.RESTScopeNamespace) - mapper = meta.MultiRESTMapper{ - discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor), - thirdPartyResourceDataMapper, + + mapper = meta.FirstHitRESTMapper{ + MultiRESTMapper: meta.MultiRESTMapper{ + discovery.NewDeferredDiscoveryRESTMapper(discoveryClient, registered.InterfacesFor), + thirdPartyResourceDataMapper, + }, } } }