From 1cb797e355f75a14c4fdd69d396331d845353b76 Mon Sep 17 00:00:00 2001 From: David Eads Date: Mon, 30 Apr 2018 08:04:43 -0400 Subject: [PATCH] acknowledge that creation of a restmapper can fail and that we cannot have a default --- pkg/kubectl/cmd/apply_set_last_applied.go | 2 - pkg/kubectl/cmd/auth/cani.go | 5 +- pkg/kubectl/cmd/autoscale.go | 8 ++- pkg/kubectl/cmd/create/create.go | 5 +- .../cmd/create/create_clusterrole_test.go | 8 ++- pkg/kubectl/cmd/create/create_role.go | 5 +- pkg/kubectl/cmd/create/create_role_test.go | 8 ++- pkg/kubectl/cmd/drain.go | 2 - pkg/kubectl/cmd/drain_test.go | 1 - pkg/kubectl/cmd/explain.go | 8 ++- pkg/kubectl/cmd/expose.go | 5 +- pkg/kubectl/cmd/rollout/rollout_pause.go | 4 -- pkg/kubectl/cmd/rollout/rollout_resume.go | 4 -- pkg/kubectl/cmd/rollout/rollout_undo.go | 3 - pkg/kubectl/cmd/run.go | 5 +- pkg/kubectl/cmd/set/set_selector.go | 4 -- pkg/kubectl/cmd/testing/fake.go | 8 +-- pkg/kubectl/cmd/util/factory.go | 2 +- pkg/kubectl/cmd/util/factory_builder.go | 10 ++- .../cmd/util/factory_object_mapping.go | 13 +--- pkg/kubectl/cmd/util/factory_test.go | 1 + pkg/kubectl/resource/builder.go | 18 +++++ pkg/kubectl/resource/interfaces.go | 2 + pkg/kubectl/resource/mapper.go | 72 +++++++++++-------- test/integration/apiserver/print_test.go | 7 +- .../etcd/etcd_storage_path_test.go | 5 +- 26 files changed, 130 insertions(+), 85 deletions(-) diff --git a/pkg/kubectl/cmd/apply_set_last_applied.go b/pkg/kubectl/cmd/apply_set_last_applied.go index 032cc131ab4..f4246b36e2c 100644 --- a/pkg/kubectl/cmd/apply_set_last_applied.go +++ b/pkg/kubectl/cmd/apply_set_last_applied.go @@ -46,7 +46,6 @@ type SetLastAppliedOptions struct { FilenameOptions resource.FilenameOptions infoList []*resource.Info - mapper meta.RESTMapper namespace string enforceNamespace bool dryRun bool @@ -117,7 +116,6 @@ func (o *SetLastAppliedOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) o.output = cmdutil.GetFlagString(cmd, "output") o.shortOutput = o.output == "name" - o.mapper = f.RESTMapper() var err error o.namespace, o.enforceNamespace, err = f.DefaultNamespace() if err != nil { diff --git a/pkg/kubectl/cmd/auth/cani.go b/pkg/kubectl/cmd/auth/cani.go index 7640a473f48..d0269f2f8a9 100644 --- a/pkg/kubectl/cmd/auth/cani.go +++ b/pkg/kubectl/cmd/auth/cani.go @@ -127,7 +127,10 @@ func (o *CanIOptions) Complete(f cmdutil.Factory, args []string) error { break } 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]) if len(resourceTokens) > 1 { o.ResourceName = resourceTokens[1] diff --git a/pkg/kubectl/cmd/autoscale.go b/pkg/kubectl/cmd/autoscale.go index 97534836e44..bde54956891 100644 --- a/pkg/kubectl/cmd/autoscale.go +++ b/pkg/kubectl/cmd/autoscale.go @@ -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 { + var err error o.dryRun = cmdutil.GetFlagBool(cmd, "dry-run") o.createAnnotation = cmdutil.GetFlagBool(cmd, cmdutil.ApplyAnnotationsFlag) o.builder = f.NewBuilder() o.canBeAutoscaled = f.CanBeAutoscaled - o.mapper = f.RESTMapper() + o.mapper, err = f.RESTMapper() + if err != nil { + return err + } + o.clientForMapping = f.ClientForMapping o.args = args o.RecordFlags.Complete(f.Command(cmd, false)) - var err error o.Recorder, err = o.RecordFlags.ToRecorder() if err != nil { return err diff --git a/pkg/kubectl/cmd/create/create.go b/pkg/kubectl/cmd/create/create.go index d4524e39790..1eb57bb1d30 100644 --- a/pkg/kubectl/cmd/create/create.go +++ b/pkg/kubectl/cmd/create/create.go @@ -389,7 +389,10 @@ func RunCreateSubcommand(f cmdutil.Factory, options *CreateSubcommandOptions) er if err != nil { return err } - mapper := f.RESTMapper() + mapper, err := f.RESTMapper() + if err != nil { + return err + } if !options.DryRun { // create subcommands have compiled knowledge of things they create, so type them directly gvks, _, err := legacyscheme.Scheme.ObjectKinds(obj) diff --git a/pkg/kubectl/cmd/create/create_clusterrole_test.go b/pkg/kubectl/cmd/create/create_clusterrole_test.go index caddc639e13..8609e63c19e 100644 --- a/pkg/kubectl/cmd/create/create_clusterrole_test.go +++ b/pkg/kubectl/cmd/create/create_clusterrole_test.go @@ -437,8 +437,12 @@ func TestClusterRoleValidate(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { - test.clusterRoleOptions.Mapper = tf.RESTMapper() - err := test.clusterRoleOptions.Validate() + var err error + test.clusterRoleOptions.Mapper, err = tf.RESTMapper() + if err != nil { + t.Fatal(err) + } + err = test.clusterRoleOptions.Validate() if test.expectErr && err == nil { t.Errorf("%s: expect error happens, but validate passes.", name) } diff --git a/pkg/kubectl/cmd/create/create_role.go b/pkg/kubectl/cmd/create/create_role.go index 29895d51821..c3ba4130ae4 100644 --- a/pkg/kubectl/cmd/create/create_role.go +++ b/pkg/kubectl/cmd/create/create_role.go @@ -205,7 +205,10 @@ func (o *CreateRoleOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, args o.ResourceNames = resourceNames // 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.OutputFormat = cmdutil.GetFlagString(cmd, "output") diff --git a/pkg/kubectl/cmd/create/create_role_test.go b/pkg/kubectl/cmd/create/create_role_test.go index c1a75a3770e..bad465e4d48 100644 --- a/pkg/kubectl/cmd/create/create_role_test.go +++ b/pkg/kubectl/cmd/create/create_role_test.go @@ -339,8 +339,12 @@ func TestValidate(t *testing.T) { } for name, test := range tests { - test.roleOptions.Mapper = tf.RESTMapper() - err := test.roleOptions.Validate() + var err error + test.roleOptions.Mapper, err = tf.RESTMapper() + if err != nil { + t.Fatal(err) + } + err = test.roleOptions.Validate() if test.expectErr && err == nil { t.Errorf("%s: expect error happens but validate passes.", name) } diff --git a/pkg/kubectl/cmd/drain.go b/pkg/kubectl/cmd/drain.go index 92fe50d6d8e..99ae24d01ee 100644 --- a/pkg/kubectl/cmd/drain.go +++ b/pkg/kubectl/cmd/drain.go @@ -29,7 +29,6 @@ import ( corev1 "k8s.io/api/core/v1" policyv1beta1 "k8s.io/api/policy/v1beta1" apierrors "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" "k8s.io/apimachinery/pkg/labels" @@ -68,7 +67,6 @@ type DrainOptions struct { DeleteLocalData bool Selector string PodSelector string - mapper meta.RESTMapper nodeInfos []*resource.Info typer runtime.ObjectTyper diff --git a/pkg/kubectl/cmd/drain_test.go b/pkg/kubectl/cmd/drain_test.go index 34670e488dc..0d7219000a1 100644 --- a/pkg/kubectl/cmd/drain_test.go +++ b/pkg/kubectl/cmd/drain_test.go @@ -836,7 +836,6 @@ func TestDeletePods(t *testing.T) { o := DrainOptions{ PrintFlags: printers.NewPrintFlags("drained"), } - o.mapper = tf.RESTMapper() o.Out = os.Stdout o.ToPrinter = func(operation string) (printers.ResourcePrinterFunc, error) { diff --git a/pkg/kubectl/cmd/explain.go b/pkg/kubectl/cmd/explain.go index cf4793fee65..120cff94e67 100644 --- a/pkg/kubectl/cmd/explain.go +++ b/pkg/kubectl/cmd/explain.go @@ -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 { + var err error + o.Recursive = cmdutil.GetFlagBool(cmd, "recursive") 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() if err != nil { return err diff --git a/pkg/kubectl/cmd/expose.go b/pkg/kubectl/cmd/expose.go index a5d64a9589e..4313f4e39f6 100644 --- a/pkg/kubectl/cmd/expose.go +++ b/pkg/kubectl/cmd/expose.go @@ -187,7 +187,10 @@ func (o *ExposeServiceOptions) Complete(f cmdutil.Factory, cmd *cobra.Command) e o.MapBasedSelectorForObject = f.MapBasedSelectorForObject o.PortsForObject = f.PortsForObject o.ProtocolsForObject = f.ProtocolsForObject - o.Mapper = f.RESTMapper() + o.Mapper, err = f.RESTMapper() + if err != nil { + return err + } o.LabelsForObject = f.LabelsForObject o.Namespace, o.EnforceNamespace, err = f.DefaultNamespace() diff --git a/pkg/kubectl/cmd/rollout/rollout_pause.go b/pkg/kubectl/cmd/rollout/rollout_pause.go index 10962de5727..52b06bd9728 100644 --- a/pkg/kubectl/cmd/rollout/rollout_pause.go +++ b/pkg/kubectl/cmd/rollout/rollout_pause.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -43,7 +42,6 @@ type PauseConfig struct { ToPrinter func(string) (printers.ResourcePrinterFunc, error) Pauser func(info *resource.Info) ([]byte, error) - Mapper meta.RESTMapper Infos []*resource.Info genericclioptions.IOStreams @@ -105,8 +103,6 @@ func (o *PauseConfig) CompletePause(f cmdutil.Factory, cmd *cobra.Command, args return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } - o.Mapper = f.RESTMapper() - o.Pauser = f.Pauser cmdNamespace, enforceNamespace, err := f.DefaultNamespace() diff --git a/pkg/kubectl/cmd/rollout/rollout_resume.go b/pkg/kubectl/cmd/rollout/rollout_resume.go index a25ed405ba3..b0e1ab8218a 100644 --- a/pkg/kubectl/cmd/rollout/rollout_resume.go +++ b/pkg/kubectl/cmd/rollout/rollout_resume.go @@ -21,7 +21,6 @@ import ( "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/types" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/kubernetes/pkg/api/legacyscheme" @@ -43,7 +42,6 @@ type ResumeConfig struct { ToPrinter func(string) (printers.ResourcePrinterFunc, error) Resumer func(object *resource.Info) ([]byte, error) - Mapper meta.RESTMapper Infos []*resource.Info genericclioptions.IOStreams @@ -103,8 +101,6 @@ func (o *ResumeConfig) CompleteResume(f cmdutil.Factory, cmd *cobra.Command, arg return cmdutil.UsageErrorf(cmd, "%s", cmd.Use) } - o.Mapper = f.RESTMapper() - o.Resumer = f.Resumer cmdNamespace, enforceNamespace, err := f.DefaultNamespace() diff --git a/pkg/kubectl/cmd/rollout/rollout_undo.go b/pkg/kubectl/cmd/rollout/rollout_undo.go index aef52b80540..81057108f9a 100644 --- a/pkg/kubectl/cmd/rollout/rollout_undo.go +++ b/pkg/kubectl/cmd/rollout/rollout_undo.go @@ -19,7 +19,6 @@ package rollout import ( "io" - "k8s.io/apimachinery/pkg/api/meta" utilerrors "k8s.io/apimachinery/pkg/util/errors" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/kubectl" @@ -41,7 +40,6 @@ type UndoOptions struct { ToPrinter func(string) (printers.ResourcePrinterFunc, error) Rollbackers []kubectl.Rollbacker - Mapper meta.RESTMapper Infos []*resource.Info ToRevision int64 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.Mapper = f.RESTMapper() o.Out = out o.DryRun = cmdutil.GetDryRunFlag(cmd) diff --git a/pkg/kubectl/cmd/run.go b/pkg/kubectl/cmd/run.go index a544b4290f4..98769839a82 100644 --- a/pkg/kubectl/cmd/run.go +++ b/pkg/kubectl/cmd/run.go @@ -643,7 +643,10 @@ func (o *RunOptions) createGeneratedObject(f cmdutil.Factory, cmd *cobra.Command 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 groupVersionKinds, _, err := legacyscheme.Scheme.ObjectKinds(obj) if err != nil { diff --git a/pkg/kubectl/cmd/set/set_selector.go b/pkg/kubectl/cmd/set/set_selector.go index ad181a452af..e50a14509ad 100644 --- a/pkg/kubectl/cmd/set/set_selector.go +++ b/pkg/kubectl/cmd/set/set_selector.go @@ -59,7 +59,6 @@ type SetSelectorOptions struct { Recorder genericclioptions.Recorder builder *resource.Builder - mapper meta.RESTMapper genericclioptions.IOStreams } @@ -140,9 +139,6 @@ func (o *SetSelectorOptions) Complete(f cmdutil.Factory, cmd *cobra.Command, arg return err } - mapper := f.RESTMapper() - o.mapper = mapper - o.resources, o.selector, err = getResourcesAndSelector(args) if err != nil { return err diff --git a/pkg/kubectl/cmd/testing/fake.go b/pkg/kubectl/cmd/testing/fake.go index 671cf5f437d..aacb908762f 100644 --- a/pkg/kubectl/cmd/testing/fake.go +++ b/pkg/kubectl/cmd/testing/fake.go @@ -338,7 +338,7 @@ func (f *TestFactory) Command(*cobra.Command, bool) string { } func (f *TestFactory) NewBuilder() *resource.Builder { - mapper := f.RESTMapper() + mapper, err := f.RESTMapper() return resource.NewBuilder( &resource.Mapper{ @@ -352,7 +352,7 @@ func (f *TestFactory) NewBuilder() *resource.Builder { Decoder: unstructured.UnstructuredJSONScheme, }, f.CategoryExpander(), - ) + ).AddError(err) } func (f *TestFactory) KubernetesClientSet() (*kubernetes.Clientset, error) { @@ -426,7 +426,7 @@ func (f *TestFactory) ClientSetForVersion(requiredVersion *schema.GroupVersion) return f.ClientSet() } -func (f *TestFactory) RESTMapper() meta.RESTMapper { +func (f *TestFactory) RESTMapper() (meta.RESTMapper, error) { groupResources := testDynamicResources() mapper := discovery.NewRESTMapper(groupResources) // 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 fakeDs := &fakeCachedDiscoveryClient{} expander := cmdutil.NewShortcutExpander(mapper, fakeDs) - return expander + return expander, nil } func (f *TestFactory) LogsForObject(object, options runtime.Object, timeout time.Duration) (*restclient.Request, error) { diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 87da23dba8d..38ee9742cc3 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -164,7 +164,7 @@ type ClientAccessFactory interface { // Generally they provide object typing and functions that build requests based on the negotiated clients. type ObjectMappingFactory interface { // Returns interfaces for dealing with arbitrary runtime.Objects. - RESTMapper() meta.RESTMapper + RESTMapper() (meta.RESTMapper, error) // Returns interface for expanding categories like `all`. CategoryExpander() categories.CategoryExpander // Returns a RESTClient for working with the specified RESTMapping or an error. This is intended diff --git a/pkg/kubectl/cmd/util/factory_builder.go b/pkg/kubectl/cmd/util/factory_builder.go index c5363e25515..472f89b5ef9 100644 --- a/pkg/kubectl/cmd/util/factory_builder.go +++ b/pkg/kubectl/cmd/util/factory_builder.go @@ -47,7 +47,7 @@ func NewBuilderFactory(clientAccessFactory ClientAccessFactory, objectMappingFac // NewBuilder returns a new resource builder for structured api objects. func (f *ring2Factory) NewBuilder() *resource.Builder { clientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.ClientForMapping) - mapper := f.objectMappingFactory.RESTMapper() + mapper, mapperErr := f.objectMappingFactory.RESTMapper() unstructuredClientMapperFunc := resource.ClientMapperFunc(f.objectMappingFactory.UnstructuredClientForMapping) @@ -65,7 +65,7 @@ func (f *ring2Factory) NewBuilder() *resource.Builder { Decoder: unstructured.UnstructuredJSONScheme, }, categoryExpander, - ) + ).AddError(mapperErr) } // 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 } 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 } diff --git a/pkg/kubectl/cmd/util/factory_object_mapping.go b/pkg/kubectl/cmd/util/factory_object_mapping.go index 45b29f5dba1..ffbee1f2fee 100644 --- a/pkg/kubectl/cmd/util/factory_object_mapping.go +++ b/pkg/kubectl/cmd/util/factory_object_mapping.go @@ -69,11 +69,8 @@ func NewObjectMappingFactory(clientAccessFactory ClientAccessFactory) ObjectMapp return f } -// objectLoader attempts to perform discovery against the server, and will fall back to -// the built in mapper if necessary. It supports unstructured objects either way, since -// 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) { +// RESTMapper returns a mapper. +func (f *ring1Factory) RESTMapper() (meta.RESTMapper, error) { discoveryClient, err := f.clientAccessFactory.DiscoveryClient() if err != nil { return nil, err @@ -83,11 +80,7 @@ func (f *ring1Factory) restMapper() (meta.RESTMapper, error) { mapper := discovery.NewDeferredDiscoveryRESTMapper(discoveryClient) // TODO: should this also indicate it recognizes typed objects? expander := NewShortcutExpander(mapper, discoveryClient) - return expander, err -} - -func (f *ring1Factory) RESTMapper() meta.RESTMapper { - return meta.NewLazyRESTMapperLoader(f.restMapper) + return expander, nil } func (f *ring1Factory) CategoryExpander() categories.CategoryExpander { diff --git a/pkg/kubectl/cmd/util/factory_test.go b/pkg/kubectl/cmd/util/factory_test.go index d2813ef3be8..6f0154926b2 100644 --- a/pkg/kubectl/cmd/util/factory_test.go +++ b/pkg/kubectl/cmd/util/factory_test.go @@ -35,6 +35,7 @@ import ( "k8s.io/apimachinery/pkg/watch" manualfake "k8s.io/client-go/rest/fake" testcore "k8s.io/client-go/testing" + "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/api/testapi" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake" diff --git a/pkg/kubectl/resource/builder.go b/pkg/kubectl/resource/builder.go index a2de3ca6868..299d9eb199b 100644 --- a/pkg/kubectl/resource/builder.go +++ b/pkg/kubectl/resource/builder.go @@ -56,6 +56,9 @@ type Builder struct { // it does not ever need to rely upon discovery. objectTyper runtime.ObjectTyper + // local indicates that we cannot make server calls + local bool + errs []error paths []Visitor @@ -142,6 +145,14 @@ func (b *Builder) Schema(schema validation.Schema) *Builder { 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) // 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 @@ -192,6 +203,7 @@ func (b *Builder) Unstructured() *Builder { return b } b.mapper = b.unstructured + b.mapper.localFn = b.isLocal b.objectTyper = unstructuredscheme.NewUnstructuredObjectTyper() return b @@ -212,6 +224,7 @@ func (b *Builder) Internal(typer runtime.ObjectTyper) *Builder { return b } b.mapper = b.internal + b.mapper.localFn = b.isLocal b.objectTyper = typer return b @@ -227,12 +240,17 @@ func (b *Builder) LocalParam(local bool) *Builder { // Local will avoid asking the server for results. func (b *Builder) Local() *Builder { + b.local = true mapper := *b.mapper mapper.ClientMapper = DisabledClientForMapping{ClientMapper: mapper.ClientMapper} b.mapper = &mapper return b } +func (b *Builder) isLocal() bool { + return b.local +} + // Mapper returns a copy of the current mapper. func (b *Builder) Mapper() *Mapper { mapper := *b.mapper diff --git a/pkg/kubectl/resource/interfaces.go b/pkg/kubectl/resource/interfaces.go index 8b75d41dbf9..383649e5773 100644 --- a/pkg/kubectl/resource/interfaces.go +++ b/pkg/kubectl/resource/interfaces.go @@ -22,6 +22,8 @@ import ( client "k8s.io/client-go/rest" ) +type RESTMapperFunc func() (meta.RESTMapper, error) + // RESTClient is a client helper for dealing with RESTful resources // in a generic way. type RESTClient interface { diff --git a/pkg/kubectl/resource/mapper.go b/pkg/kubectl/resource/mapper.go index 61f1ee1c56c..c188a19f8d9 100644 --- a/pkg/kubectl/resource/mapper.go +++ b/pkg/kubectl/resource/mapper.go @@ -28,6 +28,9 @@ import ( // Mapper is a convenience struct for holding references to the interfaces // needed to create Info for arbitrary objects. type Mapper struct { + // localFn indicates the call can't make server requests + localFn func() bool + RESTMapper meta.RESTMapper ClientMapper ClientMapper 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) } - 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) namespace, _ := metadataAccessor.Namespace(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj) - return &Info{ - Client: client, - Mapping: mapping, - + ret := &Info{ Source: source, Namespace: namespace, Name: name, ResourceVersion: resourceVersion, 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 @@ -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) } - groupVersionKind := groupVersionKinds[0] + gvk := groupVersionKinds[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) namespace, _ := metadataAccessor.Namespace(obj) resourceVersion, _ := metadataAccessor.ResourceVersion(obj) - return &Info{ - Client: client, - Mapping: mapping, - + ret := &Info{ Namespace: namespace, Name: name, ResourceVersion: resourceVersion, 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: diff --git a/test/integration/apiserver/print_test.go b/test/integration/apiserver/print_test.go index e6e19fdcfd2..85bfa4c3f4e 100644 --- a/test/integration/apiserver/print_test.go +++ b/test/integration/apiserver/print_test.go @@ -147,8 +147,11 @@ func TestServerSidePrint(t *testing.T) { printer := newFakePrinter(printersinternal.AddHandlers) 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() { // 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") { diff --git a/test/integration/etcd/etcd_storage_path_test.go b/test/integration/etcd/etcd_storage_path_test.go index 0756210ea46..711f37c5519 100644 --- a/test/integration/etcd/etcd_storage_path_test.go +++ b/test/integration/etcd/etcd_storage_path_test.go @@ -811,7 +811,10 @@ func startRealMasterOrDie(t *testing.T, certDir string) (*allClient, clientv3.KV 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 }