diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 442903d3e67..038a0eae70d 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -1291,6 +1291,17 @@ __EOF__ kubectl delete all -l app=cassandra "${kube_flags[@]}" + ########### + # Explain # + ########### + + kube::log::status "Testing kubectl(${version}:explain)" + kubectl explain pods + # shortcuts work + kubectl explain po + kubectl explain po.status.message + + ########### # Swagger # ########### diff --git a/pkg/kubectl/cmd/explain.go b/pkg/kubectl/cmd/explain.go index 170275a9ff1..5ce13633a47 100644 --- a/pkg/kubectl/cmd/explain.go +++ b/pkg/kubectl/cmd/explain.go @@ -65,11 +65,6 @@ func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st return cmdutil.UsageError(cmd, "We accept only this format: explain RESOURCE") } - client, err := f.Client() - if err != nil { - return err - } - recursive := cmdutil.GetFlagBool(cmd, "recursive") apiVersionString := cmdutil.GetFlagString(cmd, "api-version") apiVersion := unversioned.GroupVersion{} @@ -103,10 +98,10 @@ func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st } } - swagSchema, err := kubectl.GetSwaggerSchema(apiVersion, client) + schema, err := f.SwaggerSchema(apiVersion) if err != nil { return err } - return kubectl.PrintModelDescription(inModel, fieldsPath, out, swagSchema, recursive) + return kubectl.PrintModelDescription(inModel, fieldsPath, out, schema, recursive) } diff --git a/pkg/kubectl/cmd/util/factory.go b/pkg/kubectl/cmd/util/factory.go index 41559304ccd..99f8501bc55 100644 --- a/pkg/kubectl/cmd/util/factory.go +++ b/pkg/kubectl/cmd/util/factory.go @@ -29,6 +29,7 @@ import ( "strconv" "time" + "github.com/emicklei/go-restful/swagger" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -88,6 +89,8 @@ type Factory struct { LogsForObject func(object, options runtime.Object) (*client.Request, error) // Returns a schema that can validate objects stored on disk. Validator func(validate bool, cacheDir string) (validation.Schema, error) + // SwaggerSchema returns the schema declaration for the provided group version. + SwaggerSchema func(unversioned.GroupVersion) (*swagger.ApiDeclaration, error) // Returns the default namespace to use in cases where no // other namespace is specified and whether the namespace was // overriden. @@ -304,6 +307,13 @@ func NewFactory(optionalClientConfig clientcmd.ClientConfig) *Factory { } return validation.NullSchema{}, nil }, + SwaggerSchema: func(version unversioned.GroupVersion) (*swagger.ApiDeclaration, error) { + client, err := clients.ClientForVersion(&version) + if err != nil { + return nil, err + } + return client.SwaggerSchema(version) + }, DefaultNamespace: func() (string, bool, error) { return clientConfig.Namespace() }, diff --git a/pkg/kubectl/explain.go b/pkg/kubectl/explain.go index 57b072c9889..d8b9a147257 100644 --- a/pkg/kubectl/explain.go +++ b/pkg/kubectl/explain.go @@ -24,27 +24,16 @@ import ( "github.com/emicklei/go-restful/swagger" "k8s.io/kubernetes/pkg/api/meta" - "k8s.io/kubernetes/pkg/api/unversioned" apiutil "k8s.io/kubernetes/pkg/api/util" - client "k8s.io/kubernetes/pkg/client/unversioned" ) var allModels = make(map[string]*swagger.NamedModel) var recursive = false // this is global for convenience, can become int for multiple levels -// GetSwaggerSchema returns the swagger spec from master -func GetSwaggerSchema(version unversioned.GroupVersion, kubeClient client.Interface) (*swagger.ApiDeclaration, error) { - swaggerSchema, err := kubeClient.SwaggerSchema(version) - if err != nil { - return nil, fmt.Errorf("couldn't read swagger schema from server: %v", err) - } - return swaggerSchema, nil -} - // SplitAndParseResourceRequest separates the users input into a model and fields func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (string, []string, error) { inResource, fieldsPath := splitDotNotation(inResource) - inResource, _ = mapper.ResourceSingularizer(expandResourceShortcut(inResource)) + inResource, _ = mapper.ResourceSingularizer(inResource) return inResource, fieldsPath, nil } @@ -63,7 +52,7 @@ func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swa } } if pointedModel == nil { - return fmt.Errorf("Requested resource: %s doesn't exist", inModel) + return fmt.Errorf("requested resource %q is not defined", inModel) } if len(fieldsPath) == 0 { @@ -80,7 +69,7 @@ func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swa return printPrimitive(w, prop) } } else { - return fmt.Errorf("field: %s doesn't exist", field) + return fmt.Errorf("field %q does not exist", field) } } return printModelInfo(w, pointedModel, pointedModelAsProp) diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 6ecb4d278f6..e4d91d5288a 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -91,6 +91,11 @@ func (e ShortcutExpander) ResourceIsValid(resource string) bool { return e.RESTMapper.ResourceIsValid(expandResourceShortcut(resource)) } +// ResourceSingularizer expands the named resource and then singularizes it. +func (e ShortcutExpander) ResourceSingularizer(resource string) (string, error) { + return e.RESTMapper.ResourceSingularizer(expandResourceShortcut(resource)) +} + // expandResourceShortcut will return the expanded version of resource // (something that a pkg/api/meta.RESTMapper can understand), if it is // indeed a shortcut. Otherwise, will return resource unmodified.