diff --git a/pkg/client/unversioned/client.go b/pkg/client/unversioned/client.go index 0381a90b15a..2333672ebf8 100644 --- a/pkg/client/unversioned/client.go +++ b/pkg/client/unversioned/client.go @@ -26,7 +26,6 @@ import ( "github.com/emicklei/go-restful/swagger" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/api/unversioned" "k8s.io/kubernetes/pkg/version" ) @@ -184,26 +183,32 @@ func (c *Client) ValidateComponents() (*api.ComponentStatusList, error) { // SwaggerSchemaInterface has a method to retrieve the swagger schema. Used in // client.Interface type SwaggerSchemaInterface interface { - SwaggerSchema(version string) (*swagger.ApiDeclaration, error) + SwaggerSchema(groupVersion string) (*swagger.ApiDeclaration, error) } // SwaggerSchema retrieves and parses the swagger API schema the server supports. -func (c *Client) SwaggerSchema(version string) (*swagger.ApiDeclaration, error) { - if version == "" { - version = latest.GroupOrDie("").Version +func (c *Client) SwaggerSchema(groupVersion string) (*swagger.ApiDeclaration, error) { + if groupVersion == "" { + return nil, fmt.Errorf("groupVersion cannot be empty") } - vers, err := c.ServerAPIVersions() + groupList, err := c.Discovery().ServerGroups() if err != nil { return nil, err } - + groupVersions := extractGroupVersions(groupList) // This check also takes care the case that kubectl is newer than the running endpoint - if stringDoesntExistIn(version, vers.Versions) { - return nil, fmt.Errorf("API version: %s is not supported by the server. Use one of: %v", version, vers.Versions) + if stringDoesntExistIn(groupVersion, groupVersions) { + return nil, fmt.Errorf("API version: %s is not supported by the server. Use one of: %v", groupVersion, groupVersions) + } + var path string + if groupVersion == "v1" { + path = "/swaggerapi/api/" + groupVersion + } else { + path = "/swaggerapi/apis/" + groupVersion } - body, err := c.Get().AbsPath("/swaggerapi/api/" + version).Do().Raw() + body, err := c.Get().AbsPath(path).Do().Raw() if err != nil { return nil, err } diff --git a/pkg/kubectl/cmd/explain.go b/pkg/kubectl/cmd/explain.go index 15053964b0f..94abb923de6 100644 --- a/pkg/kubectl/cmd/explain.go +++ b/pkg/kubectl/cmd/explain.go @@ -21,6 +21,7 @@ import ( "github.com/spf13/cobra" + "k8s.io/kubernetes/pkg/api/latest" "k8s.io/kubernetes/pkg/kubectl" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" ) @@ -70,13 +71,28 @@ func RunExplain(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []st recursive := cmdutil.GetFlagBool(cmd, "recursive") apiV := cmdutil.GetFlagString(cmd, "api-version") - swagSchema, err := kubectl.GetSwaggerSchema(apiV, client) + mapper, _ := f.Object() + group, inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) if err != nil { return err } - mapper, _ := f.Object() - inModel, fieldsPath, err := kubectl.SplitAndParseResourceRequest(args[0], mapper) + if len(group) == 0 { + // TODO: We should deduce the group for a resource by discovering the supported resources at server. + group, err = mapper.GroupForResource(inModel) + if err != nil { + return err + } + } + + if len(apiV) == 0 { + groupMeta, err := latest.Group(group) + if err != nil { + return err + } + apiV = groupMeta.GroupVersion + } + swagSchema, err := kubectl.GetSwaggerSchema(apiV, client) if err != nil { return err } diff --git a/pkg/kubectl/explain.go b/pkg/kubectl/explain.go index 087c217c55d..fa8da7834b1 100644 --- a/pkg/kubectl/explain.go +++ b/pkg/kubectl/explain.go @@ -24,6 +24,7 @@ import ( "github.com/emicklei/go-restful/swagger" "k8s.io/kubernetes/pkg/api/meta" + apiutil "k8s.io/kubernetes/pkg/api/util" client "k8s.io/kubernetes/pkg/client/unversioned" ) @@ -40,16 +41,17 @@ func GetSwaggerSchema(apiVer string, kubeClient client.Interface) (*swagger.ApiD } // SplitAndParseResourceRequest separates the users input into a model and fields -func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (string, []string, error) { +func SplitAndParseResourceRequest(inResource string, mapper meta.RESTMapper) (string, string, []string, error) { inResource, fieldsPath := splitDotNotation(inResource) + group, inResource := splitGroupFromResource(inResource) inResource, _ = mapper.ResourceSingularizer(expandResourceShortcut(inResource)) - return inResource, fieldsPath, nil + return group, inResource, fieldsPath, nil } // PrintModelDescription prints the description of a specific model or dot path func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swaggerSchema *swagger.ApiDeclaration, r bool) error { recursive = r // this is global for convenience - apiVer := swaggerSchema.ApiVersion + "." + apiVer := apiutil.GetVersion(swaggerSchema.ApiVersion) + "." var pointedModel *swagger.NamedModel for i := range swaggerSchema.Models.List { @@ -61,7 +63,7 @@ func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swa } } if pointedModel == nil { - return fmt.Errorf("Requested resourse: %s doesn't exit", inModel) + return fmt.Errorf("Requested resource: %s doesn't exist", inModel) } if len(fieldsPath) == 0 { @@ -84,6 +86,15 @@ func PrintModelDescription(inModel string, fieldsPath []string, w io.Writer, swa return printModelInfo(w, pointedModel, pointedModelAsProp) } +func splitGroupFromResource(resource string) (string, string) { + seg := strings.SplitN(resource, "/", 2) + if len(seg) == 1 { + return "", seg[0] + } else { + return seg[0], seg[1] + } +} + func splitDotNotation(model string) (string, []string) { var fieldsPath []string dotModel := strings.Split(model, ".")