acknowledge that creation of a restmapper can fail and that we cannot have a default

pull/8/head
David Eads 2018-04-30 08:04:43 -04:00
parent ef0d1ab819
commit 1cb797e355
26 changed files with 130 additions and 85 deletions

View File

@ -46,7 +46,6 @@ type SetLastAppliedOptions struct {
FilenameOptions resource.FilenameOptions FilenameOptions resource.FilenameOptions
infoList []*resource.Info infoList []*resource.Info
mapper meta.RESTMapper
namespace string namespace string
enforceNamespace bool enforceNamespace bool
dryRun bool dryRun bool
@ -117,7 +116,6 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command)
o.output = cmdutil.GetFlagString(cmd, "output") o.output = cmdutil.GetFlagString(cmd, "output")
o.shortOutput = o.output == "name" o.shortOutput = o.output == "name"
o.mapper = f.RESTMapper()
var err error var err error
o.namespace, o.enforceNamespace, err = f.DefaultNamespace() o.namespace, o.enforceNamespace, err = f.DefaultNamespace()
if err != nil { if err != nil {

View File

@ -127,7 +127,10 @@ func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error {
break break
} }
resourceTokens := strings.SplitN(args[1], "/", 2) resourceTokens := strings.SplitN(args[1], "/", 2)
restMapper := f.RESTMapper() restMapper, err := f.RESTMapper()
if err != nil {
return err
}
o.Resource = o.resourceFor(restMapper, resourceTokens[0]) o.Resource = o.resourceFor(restMapper, resourceTokens[0])
if len(resourceTokens) > 1 { if len(resourceTokens) > 1 {
o.ResourceName = resourceTokens[1] o.ResourceName = resourceTokens[1]

View File

@ -127,16 +127,20 @@ func NewCmdAutoscale(f cmdutil.Factory, ioStreams genericclioptions.IOStreams) *
} }
func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error { func (o *AutoscaleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args []string) error {
var err error
o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run") o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run")
o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag)
o.builder = f.NewBuilder() o.builder = f.NewBuilder()
o.canBeAutoscaled = f.CanBeAutoscaled o.canBeAutoscaled = f.CanBeAutoscaled
o.mapper = f.RESTMapper() o.mapper, err = f.RESTMapper()
if err != nil {
return err
}
o.clientForMapping = f.ClientForMapping o.clientForMapping = f.ClientForMapping
o.args = args o.args = args
o.RecordFlags.Complete(f.Command(cmd, false)) o.RecordFlags.Complete(f.Command(cmd, false))
var err error
o.Recorder, err = o.RecordFlags.ToRecorder() o.Recorder, err = o.RecordFlags.ToRecorder()
if err != nil { if err != nil {
return err return err

View File

@ -389,7 +389,10 @@ func RunCreateSubcommand(f cmdutil.Factory, options *CreateSubcommandOptions) er
if err != nil { if err != nil {
return err return err
} }
mapper := f.RESTMapper() mapper, err := f.RESTMapper()
if err != nil {
return err
}
if !options.DryRun { if !options.DryRun {
// create subcommands have compiled knowledge of things they create, so type them directly // create subcommands have compiled knowledge of things they create, so type them directly
gvks, _, err := legacyscheme.Scheme.ObjectKinds(obj) gvks, _, err := legacyscheme.Scheme.ObjectKinds(obj)

View File

@ -437,8 +437,12 @@ func TestClusterRoleValidate(t *testing.T) {
for name, test := range tests { for name, test := range tests {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
test.clusterRoleOptions.Mapper = tf.RESTMapper() var err error
err := test.clusterRoleOptions.Validate() test.clusterRoleOptions.Mapper, err = tf.RESTMapper()
if err != nil {
t.Fatal(err)
}
err = test.clusterRoleOptions.Validate()
if test.expectErr && err == nil { if test.expectErr && err == nil {
t.Errorf("%s: expect error happens, but validate passes.", name) t.Errorf("%s: expect error happens, but validate passes.", name)
} }

View File

@ -205,7 +205,10 @@ func (o *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args
o.ResourceNames = resourceNames o.ResourceNames = resourceNames
// Complete other options for Run. // Complete other options for Run.
o.Mapper = f.RESTMapper() o.Mapper, err = f.RESTMapper()
if err != nil {
return err
}
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd)
o.OutputFormat = cmdutil.GetFlagString(cmd, "output") o.OutputFormat = cmdutil.GetFlagString(cmd, "output")

View File

@ -339,8 +339,12 @@ func TestValidate(t *testing.T) {
} }
for name, test := range tests { for name, test := range tests {
test.roleOptions.Mapper = tf.RESTMapper() var err error
err := test.roleOptions.Validate() test.roleOptions.Mapper, err = tf.RESTMapper()
if err != nil {
t.Fatal(err)
}
err = test.roleOptions.Validate()
if test.expectErr && err == nil { if test.expectErr && err == nil {
t.Errorf("%s: expect error happens but validate passes.", name) t.Errorf("%s: expect error happens but validate passes.", name)
} }

View File

@ -29,7 +29,6 @@ import (
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
policyv1beta1 "k8s.io/api/policy/v1beta1" policyv1beta1 "k8s.io/api/policy/v1beta1"
apierrors "k8s.io/apimachinery/pkg/api/errors" apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/labels"
@ -68,7 +67,6 @@ type DrainOptions struct {
DeleteLocalData bool DeleteLocalData bool
Selector string Selector string
PodSelector string PodSelector string
mapper meta.RESTMapper
nodeInfos []*resource.Info nodeInfos []*resource.Info
typer runtime.ObjectTyper typer runtime.ObjectTyper

View File

@ -836,7 +836,6 @@ func TestDeletePods(t *testing.T) {
o := DrainOptions{ o := DrainOptions{
PrintFlags: printers.NewPrintFlags("drained"), PrintFlags: printers.NewPrintFlags("drained"),
} }
o.mapper = tf.RESTMapper()
o.Out = os.Stdout o.Out = os.Stdout
o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) {

View File

@ -91,12 +91,16 @@ func NewCmdExplain(parent string, f cmdutil.Factory, streams genericclioptions.I
} }
func (o *ExplainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error { func (o *ExplainOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) error {
var err error
o.Recursive = cmdutil.GetFlagBool(cmd, "recursive") o.Recursive = cmdutil.GetFlagBool(cmd, "recursive")
o.ApiVersion = cmdutil.GetFlagString(cmd, "api-version") o.ApiVersion = cmdutil.GetFlagString(cmd, "api-version")
o.Mapper = f.RESTMapper() o.Mapper, err = f.RESTMapper()
if err != nil {
return err
}
var err error
o.Schema, err = f.OpenAPISchema() o.Schema, err = f.OpenAPISchema()
if err != nil { if err != nil {
return err return err

View File

@ -187,7 +187,10 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e
o.MapBasedSelectorForObject = f.MapBasedSelectorForObject o.MapBasedSelectorForObject = f.MapBasedSelectorForObject
o.PortsForObject = f.PortsForObject o.PortsForObject = f.PortsForObject
o.ProtocolsForObject = f.ProtocolsForObject o.ProtocolsForObject = f.ProtocolsForObject
o.Mapper = f.RESTMapper() o.Mapper, err = f.RESTMapper()
if err != nil {
return err
}
o.LabelsForObject = f.LabelsForObject o.LabelsForObject = f.LabelsForObject
o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace()

View File

@ -21,7 +21,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
@ -43,7 +42,6 @@ type PauseConfig struct {
ToPrinter func(string) (printers.ResourcePrinterFunc, error) ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Pauser func(info *resource.Info) ([]byte, error) Pauser func(info *resource.Info) ([]byte, error)
Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
genericclioptions.IOStreams genericclioptions.IOStreams
@ -105,8 +103,6 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args
return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) return cmdutil.UsageErrorf(cmd, "%s", cmd.Use)
} }
o.Mapper = f.RESTMapper()
o.Pauser = f.Pauser o.Pauser = f.Pauser
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()

View File

@ -21,7 +21,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
@ -43,7 +42,6 @@ type ResumeConfig struct {
ToPrinter func(string) (printers.ResourcePrinterFunc, error) ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Resumer func(object *resource.Info) ([]byte, error) Resumer func(object *resource.Info) ([]byte, error)
Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
genericclioptions.IOStreams genericclioptions.IOStreams
@ -103,8 +101,6 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg
return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) return cmdutil.UsageErrorf(cmd, "%s", cmd.Use)
} }
o.Mapper = f.RESTMapper()
o.Resumer = f.Resumer o.Resumer = f.Resumer
cmdNamespace, enforceNamespace, err := f.DefaultNamespace() cmdNamespace, enforceNamespace, err := f.DefaultNamespace()

View File

@ -19,7 +19,6 @@ package rollout
import ( import (
"io" "io"
"k8s.io/apimachinery/pkg/api/meta"
utilerrors "k8s.io/apimachinery/pkg/util/errors" utilerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/kubectl" "k8s.io/kubernetes/pkg/kubectl"
@ -41,7 +40,6 @@ type UndoOptions struct {
ToPrinter func(string) (printers.ResourcePrinterFunc, error) ToPrinter func(string) (printers.ResourcePrinterFunc, error)
Rollbackers []kubectl.Rollbacker Rollbackers []kubectl.Rollbacker
Mapper meta.RESTMapper
Infos []*resource.Info Infos []*resource.Info
ToRevision int64 ToRevision int64
DryRun bool DryRun bool
@ -107,7 +105,6 @@ func (o *UndoOptions) CompleteUndo(f cmdutil.Factory, cmd *cobra.Command, out io
} }
o.ToRevision = cmdutil.GetFlagInt64(cmd, "to-revision") o.ToRevision = cmdutil.GetFlagInt64(cmd, "to-revision")
o.Mapper = f.RESTMapper()
o.Out = out o.Out = out
o.DryRun = cmdutil.GetDryRunFlag(cmd) o.DryRun = cmdutil.GetDryRunFlag(cmd)

View File

@ -643,7 +643,10 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command
return nil, err return nil, err
} }
mapper := f.RESTMapper() mapper, err := f.RESTMapper()
if err != nil {
return nil, err
}
// run has compiled knowledge of the thing is is creating // run has compiled knowledge of the thing is is creating
groupVersionKinds, _, err := legacyscheme.Scheme.ObjectKinds(obj) groupVersionKinds, _, err := legacyscheme.Scheme.ObjectKinds(obj)
if err != nil { if err != nil {

View File

@ -59,7 +59,6 @@ type SetSelectorOptions struct {
Recorder genericclioptions.Recorder Recorder genericclioptions.Recorder
builder *resource.Builder builder *resource.Builder
mapper meta.RESTMapper
genericclioptions.IOStreams genericclioptions.IOStreams
} }
@ -140,9 +139,6 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg
return err return err
} }
mapper := f.RESTMapper()
o.mapper = mapper
o.resources, o.selector, err = getResourcesAndSelector(args) o.resources, o.selector, err = getResourcesAndSelector(args)
if err != nil { if err != nil {
return err return err

View File

@ -338,7 +338,7 @@ func (f *TestFactory) Command(*cobra.Command, bool) string {
} }
func (f *TestFactory) NewBuilder() *resource.Builder { func (f *TestFactory) NewBuilder() *resource.Builder {
mapper := f.RESTMapper() mapper, err := f.RESTMapper()
return resource.NewBuilder( return resource.NewBuilder(
&resource.Mapper{ &resource.Mapper{
@ -352,7 +352,7 @@ func (f *TestFactory) NewBuilder() *resource.Builder {
Decoder: unstructured.UnstructuredJSONScheme, Decoder: unstructured.UnstructuredJSONScheme,
}, },
f.CategoryExpander(), f.CategoryExpander(),
) ).AddError(err)
} }
func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) { func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) {
@ -426,7 +426,7 @@ func (f *TestFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion)
return f.ClientSet() return f.ClientSet()
} }
func (f *TestFactory) RESTMapper() meta.RESTMapper { func (f *TestFactory) RESTMapper() (meta.RESTMapper, error) {
groupResources := testDynamicResources() groupResources := testDynamicResources()
mapper := discovery.NewRESTMapper(groupResources) mapper := discovery.NewRESTMapper(groupResources)
// for backwards compatibility with existing tests, allow rest mappings from the scheme to show up // for backwards compatibility with existing tests, allow rest mappings from the scheme to show up
@ -441,7 +441,7 @@ func (f *TestFactory) RESTMapper() meta.RESTMapper {
// TODO: should probably be the external scheme // TODO: should probably be the external scheme
fakeDs := &fakeCachedDiscoveryClient{} fakeDs := &fakeCachedDiscoveryClient{}
expander := cmdutil.NewShortcutExpander(mapper, fakeDs) expander := cmdutil.NewShortcutExpander(mapper, fakeDs)
return expander return expander, nil
} }
func (f *TestFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) { func (f *TestFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) {

View File

@ -164,7 +164,7 @@ type ClientAccessFactory interface {
// Generally they provide object typing and functions that build requests based on the negotiated clients. // Generally they provide object typing and functions that build requests based on the negotiated clients.
type ObjectMappingFactory interface { type ObjectMappingFactory interface {
// Returns interfaces for dealing with arbitrary runtime.Objects. // Returns interfaces for dealing with arbitrary runtime.Objects.
RESTMapper() meta.RESTMapper RESTMapper() (meta.RESTMapper, error)
// Returns interface for expanding categories like `all`. // Returns interface for expanding categories like `all`.
CategoryExpander() categories.CategoryExpander CategoryExpander() categories.CategoryExpander
// Returns a RESTClient for working with the specified RESTMapping or an error. This is intended // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended

View File

@ -47,7 +47,7 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac
// NewBuilder returns a new resource builder for structured api objects. // NewBuilder returns a new resource builder for structured api objects.
func (f *ring2Factory) NewBuilder() *resource.Builder { func (f *ring2Factory) NewBuilder() *resource.Builder {
clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping) clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping)
mapper := f.objectMappingFactory.RESTMapper() mapper, mapperErr := f.objectMappingFactory.RESTMapper()
unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping) unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping)
@ -65,7 +65,7 @@ func (f *ring2Factory) NewBuilder() *resource.Builder {
Decoder: unstructured.UnstructuredJSONScheme, Decoder: unstructured.UnstructuredJSONScheme,
}, },
categoryExpander, categoryExpander,
) ).AddError(mapperErr)
} }
// PluginLoader loads plugins from a path set by the KUBECTL_PLUGINS_PATH env var. // PluginLoader loads plugins from a path set by the KUBECTL_PLUGINS_PATH env var.
@ -97,7 +97,11 @@ func (f *ring2Factory) ScaleClient() (scaleclient.ScalesGetter, error) {
return nil, err return nil, err
} }
resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient) resolver := scaleclient.NewDiscoveryScaleKindResolver(discoClient)
mapper := f.objectMappingFactory.RESTMapper() mapper, err := f.objectMappingFactory.RESTMapper()
if err != nil {
return nil, err
}
return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil return scaleclient.New(restClient, mapper, dynamic.LegacyAPIPathResolverFunc, resolver), nil
} }

View File

@ -69,11 +69,8 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp
return f return f
} }
// objectLoader attempts to perform discovery against the server, and will fall back to // RESTMapper returns a mapper.
// the built in mapper if necessary. It supports unstructured objects either way, since func (f *ring1Factory) RESTMapper() (meta.RESTMapper, error) {
// the underlying Scheme supports Unstructured. The mapper will return converters that can
// convert versioned types to unstructured and back.
func (f *ring1Factory) restMapper() (meta.RESTMapper, error) {
discoveryClient, err := f.clientAccessFactory.DiscoveryClient() discoveryClient, err := f.clientAccessFactory.DiscoveryClient()
if err != nil { if err != nil {
return nil, err return nil, err
@ -83,11 +80,7 @@ func (f *ring1Factory) restMapper() (meta.RESTMapper, error) {
mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient) mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient)
// TODO: should this also indicate it recognizes typed objects? // TODO: should this also indicate it recognizes typed objects?
expander := NewShortcutExpander(mapper, discoveryClient) expander := NewShortcutExpander(mapper, discoveryClient)
return expander, err return expander, nil
}
func (f *ring1Factory) RESTMapper() meta.RESTMapper {
return meta.NewLazyRESTMapperLoader(f.restMapper)
} }
func (f *ring1Factory) CategoryExpander() categories.CategoryExpander { func (f *ring1Factory) CategoryExpander() categories.CategoryExpander {

View File

@ -35,6 +35,7 @@ import (
"k8s.io/apimachinery/pkg/watch" "k8s.io/apimachinery/pkg/watch"
manualfake "k8s.io/client-go/rest/fake" manualfake "k8s.io/client-go/rest/fake"
testcore "k8s.io/client-go/testing" testcore "k8s.io/client-go/testing"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/api/testapi" "k8s.io/kubernetes/pkg/api/testapi"
api "k8s.io/kubernetes/pkg/apis/core" api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"

View File

@ -56,6 +56,9 @@ type Builder struct {
// it does not ever need to rely upon discovery. // it does not ever need to rely upon discovery.
objectTyper runtime.ObjectTyper objectTyper runtime.ObjectTyper
// local indicates that we cannot make server calls
local bool
errs []error errs []error
paths []Visitor paths []Visitor
@ -142,6 +145,14 @@ func (b *Builder) Schema(schema validation.Schema) *Builder {
return b return b
} }
func (b *Builder) AddError(err error) *Builder {
if err == nil {
return b
}
b.errs = append(b.errs, err)
return b
}
// FilenameParam groups input in two categories: URLs and files (files, directories, STDIN) // FilenameParam groups input in two categories: URLs and files (files, directories, STDIN)
// If enforceNamespace is false, namespaces in the specs will be allowed to // If enforceNamespace is false, namespaces in the specs will be allowed to
// override the default namespace. If it is true, namespaces that don't match // override the default namespace. If it is true, namespaces that don't match
@ -192,6 +203,7 @@ func (b *Builder) Unstructured() *Builder {
return b return b
} }
b.mapper = b.unstructured b.mapper = b.unstructured
b.mapper.localFn = b.isLocal
b.objectTyper = unstructuredscheme.NewUnstructuredObjectTyper() b.objectTyper = unstructuredscheme.NewUnstructuredObjectTyper()
return b return b
@ -212,6 +224,7 @@ func (b *Builder) Internal(typer runtime.ObjectTyper) *Builder {
return b return b
} }
b.mapper = b.internal b.mapper = b.internal
b.mapper.localFn = b.isLocal
b.objectTyper = typer b.objectTyper = typer
return b return b
@ -227,12 +240,17 @@ func (b *Builder) LocalParam(local bool) *Builder {
// Local will avoid asking the server for results. // Local will avoid asking the server for results.
func (b *Builder) Local() *Builder { func (b *Builder) Local() *Builder {
b.local = true
mapper := *b.mapper mapper := *b.mapper
mapper.ClientMapper = DisabledClientForMapping{ClientMapper: mapper.ClientMapper} mapper.ClientMapper = DisabledClientForMapping{ClientMapper: mapper.ClientMapper}
b.mapper = &mapper b.mapper = &mapper
return b return b
} }
func (b *Builder) isLocal() bool {
return b.local
}
// Mapper returns a copy of the current mapper. // Mapper returns a copy of the current mapper.
func (b *Builder) Mapper() *Mapper { func (b *Builder) Mapper() *Mapper {
mapper := *b.mapper mapper := *b.mapper

View File

@ -22,6 +22,8 @@ import (
client "k8s.io/client-go/rest" client "k8s.io/client-go/rest"
) )
type RESTMapperFunc func() (meta.RESTMapper, error)
// RESTClient is a client helper for dealing with RESTful resources // RESTClient is a client helper for dealing with RESTful resources
// in a generic way. // in a generic way.
type RESTClient interface { type RESTClient interface {

View File

@ -28,6 +28,9 @@ import (
// Mapper is a convenience struct for holding references to the interfaces // Mapper is a convenience struct for holding references to the interfaces
// needed to create Info for arbitrary objects. // needed to create Info for arbitrary objects.
type Mapper struct { type Mapper struct {
// localFn indicates the call can't make server requests
localFn func() bool
RESTMapper meta.RESTMapper RESTMapper meta.RESTMapper
ClientMapper ClientMapper ClientMapper ClientMapper
Decoder runtime.Decoder Decoder runtime.Decoder
@ -42,31 +45,34 @@ func (m *Mapper) InfoForData(data []byte, source string) (*Info, error) {
return nil, fmt.Errorf("unable to decode %q: %v", source, err) return nil, fmt.Errorf("unable to decode %q: %v", source, err)
} }
mapping, err := m.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
}
client, err := m.ClientMapper.ClientForMapping(mapping)
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
name, _ := metadataAccessor.Name(obj) name, _ := metadataAccessor.Name(obj)
namespace, _ := metadataAccessor.Namespace(obj) namespace, _ := metadataAccessor.Namespace(obj)
resourceVersion, _ := metadataAccessor.ResourceVersion(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
return &Info{ ret := &Info{
Client: client,
Mapping: mapping,
Source: source, Source: source,
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
Object: obj, Object: obj,
}, nil }
if m.localFn == nil || !m.localFn() {
mapping, err := m.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %q: %v", source, err)
}
ret.Mapping = mapping
client, err := m.ClientMapper.ClientForMapping(mapping)
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
ret.Client = client
}
return ret, nil
} }
// InfoForObject creates an Info object for the given Object. An error is returned // InfoForObject creates an Info object for the given Object. An error is returned
@ -78,33 +84,37 @@ func (m *Mapper) InfoForObject(obj runtime.Object, typer runtime.ObjectTyper, pr
return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err) return nil, fmt.Errorf("unable to get type info from the object %q: %v", reflect.TypeOf(obj), err)
} }
groupVersionKind := groupVersionKinds[0] gvk := groupVersionKinds[0]
if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 { if len(groupVersionKinds) > 1 && len(preferredGVKs) > 0 {
groupVersionKind = preferredObjectKind(groupVersionKinds, preferredGVKs) gvk = preferredObjectKind(groupVersionKinds, preferredGVKs)
} }
mapping, err := m.RESTMapper.RESTMapping(groupVersionKind.GroupKind(), groupVersionKind.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %v: %v", groupVersionKind, err)
}
client, err := m.ClientMapper.ClientForMapping(mapping)
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
name, _ := metadataAccessor.Name(obj) name, _ := metadataAccessor.Name(obj)
namespace, _ := metadataAccessor.Namespace(obj) namespace, _ := metadataAccessor.Namespace(obj)
resourceVersion, _ := metadataAccessor.ResourceVersion(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj)
return &Info{ ret := &Info{
Client: client,
Mapping: mapping,
Namespace: namespace, Namespace: namespace,
Name: name, Name: name,
ResourceVersion: resourceVersion, ResourceVersion: resourceVersion,
Object: obj, Object: obj,
}, nil }
if m.localFn == nil || !m.localFn() {
mapping, err := m.RESTMapper.RESTMapping(gvk.GroupKind(), gvk.Version)
if err != nil {
return nil, fmt.Errorf("unable to recognize %v", err)
}
ret.Mapping = mapping
client, err := m.ClientMapper.ClientForMapping(mapping)
if err != nil {
return nil, fmt.Errorf("unable to connect to a server to handle %q: %v", mapping.Resource, err)
}
ret.Client = client
}
return ret, nil
} }
// preferredObjectKind picks the possibility that most closely matches the priority list in this order: // preferredObjectKind picks the possibility that most closely matches the priority list in this order:

View File

@ -147,8 +147,11 @@ func TestServerSidePrint(t *testing.T) {
printer := newFakePrinter(printersinternal.AddHandlers) printer := newFakePrinter(printersinternal.AddHandlers)
factory := util.NewFactory(clientcmd.NewDefaultClientConfig(*createKubeConfig(s.URL), &clientcmd.ConfigOverrides{})) factory := util.NewFactory(clientcmd.NewDefaultClientConfig(*createKubeConfig(s.URL), &clientcmd.ConfigOverrides{}))
mapper := factory.RESTMapper() mapper, err := factory.RESTMapper()
if err != nil {
t.Errorf("unexpected error getting mapper: %v", err)
return
}
for gvk, apiType := range legacyscheme.Scheme.AllKnownTypes() { for gvk, apiType := range legacyscheme.Scheme.AllKnownTypes() {
// we do not care about internal objects or lists // TODO make sure this is always true // we do not care about internal objects or lists // TODO make sure this is always true
if gvk.Version == runtime.APIVersionInternal || strings.HasSuffix(apiType.Name(), "List") { if gvk.Version == runtime.APIVersionInternal || strings.HasSuffix(apiType.Name(), "List") {

View File

@ -811,7 +811,10 @@ func startRealMasterOrDie(t *testing.T, certDir string) (*allClient, clientv3.KV
t.Fatal(err) t.Fatal(err)
} }
mapper := util.NewFactory(clientcmd.NewDefaultClientConfig(*clientcmdapi.NewConfig(), &clientcmd.ConfigOverrides{})).RESTMapper() mapper, err := util.NewFactory(clientcmd.NewDefaultClientConfig(*clientcmdapi.NewConfig(), &clientcmd.ConfigOverrides{})).RESTMapper()
if err != nil {
t.Fatal(err)
}
return client, kvClient, mapper return client, kvClient, mapper
} }