From 110641b34c9e220171f319af6e4f48464cc7a3d6 Mon Sep 17 00:00:00 2001 From: xilabao Date: Fri, 10 Mar 2017 14:52:34 +0800 Subject: [PATCH] add kubectl api-resources command --- docs/.generated_docs | 3 + docs/man/man1/kubectl-api-resources.1 | 3 + .../kubectl/kubectl_api-resources.md | 3 + docs/yaml/kubectl/kubectl_api-resources.yaml | 3 + hack/make-rules/test-cmd-util.sh | 2 + pkg/kubectl/cmd/BUILD | 1 + pkg/kubectl/cmd/apiresources.go | 214 ++++++++++++++++++ pkg/kubectl/cmd/cmd.go | 1 + 8 files changed, 230 insertions(+) create mode 100644 docs/man/man1/kubectl-api-resources.1 create mode 100644 docs/user-guide/kubectl/kubectl_api-resources.md create mode 100644 docs/yaml/kubectl/kubectl_api-resources.yaml create mode 100644 pkg/kubectl/cmd/apiresources.go diff --git a/docs/.generated_docs b/docs/.generated_docs index 4886eea60ba..5fcb1243375 100644 --- a/docs/.generated_docs +++ b/docs/.generated_docs @@ -135,6 +135,7 @@ docs/man/man1/kubeadm.1 docs/man/man1/kubectl-alpha-diff.1 docs/man/man1/kubectl-alpha.1 docs/man/man1/kubectl-annotate.1 +docs/man/man1/kubectl-api-resources.1 docs/man/man1/kubectl-api-versions.1 docs/man/man1/kubectl-apply-edit-last-applied.1 docs/man/man1/kubectl-apply-set-last-applied.1 @@ -234,6 +235,7 @@ docs/user-guide/kubectl/kubectl.md docs/user-guide/kubectl/kubectl_alpha.md docs/user-guide/kubectl/kubectl_alpha_diff.md docs/user-guide/kubectl/kubectl_annotate.md +docs/user-guide/kubectl/kubectl_api-resources.md docs/user-guide/kubectl/kubectl_api-versions.md docs/user-guide/kubectl/kubectl_apply.md docs/user-guide/kubectl/kubectl_apply_edit-last-applied.md @@ -329,6 +331,7 @@ docs/user-guide/kubectl/kubectl_version.md docs/yaml/kubectl/kubectl.yaml docs/yaml/kubectl/kubectl_alpha.yaml docs/yaml/kubectl/kubectl_annotate.yaml +docs/yaml/kubectl/kubectl_api-resources.yaml docs/yaml/kubectl/kubectl_api-versions.yaml docs/yaml/kubectl/kubectl_apply.yaml docs/yaml/kubectl/kubectl_attach.yaml diff --git a/docs/man/man1/kubectl-api-resources.1 b/docs/man/man1/kubectl-api-resources.1 new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/man/man1/kubectl-api-resources.1 @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/user-guide/kubectl/kubectl_api-resources.md b/docs/user-guide/kubectl/kubectl_api-resources.md new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/user-guide/kubectl/kubectl_api-resources.md @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/docs/yaml/kubectl/kubectl_api-resources.yaml b/docs/yaml/kubectl/kubectl_api-resources.yaml new file mode 100644 index 00000000000..b6fd7a0f989 --- /dev/null +++ b/docs/yaml/kubectl/kubectl_api-resources.yaml @@ -0,0 +1,3 @@ +This file is autogenerated, but we've stopped checking such files into the +repository to reduce the need for rebases. Please run hack/generate-docs.sh to +populate this file. diff --git a/hack/make-rules/test-cmd-util.sh b/hack/make-rules/test-cmd-util.sh index cbc7a0b89d1..e0219c0a140 100755 --- a/hack/make-rules/test-cmd-util.sh +++ b/hack/make-rules/test-cmd-util.sh @@ -4663,6 +4663,8 @@ runTests() { # Cluster Role # ################ + kubectl "${kube_flags[@]}" api-resources + if kube::test::if_supports_resource "${clusterroles}" ; then record_command run_clusterroles_tests fi diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index e8601c2e38e..3b47a003154 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -9,6 +9,7 @@ go_library( srcs = [ "alpha.go", "annotate.go", + "apiresources.go", "apiversions.go", "apply.go", "apply_edit_last_applied.go", diff --git a/pkg/kubectl/cmd/apiresources.go b/pkg/kubectl/cmd/apiresources.go new file mode 100644 index 00000000000..75d825f21c8 --- /dev/null +++ b/pkg/kubectl/cmd/apiresources.go @@ -0,0 +1,214 @@ +/* +Copyright 2018 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 cmd + +import ( + "fmt" + "io" + "sort" + "strings" + + "github.com/spf13/cobra" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/sets" + "k8s.io/kubernetes/pkg/kubectl/cmd/templates" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" + "k8s.io/kubernetes/pkg/printers" +) + +var ( + apiresources_example = templates.Examples(` + # Print the supported API Resources + kubectl api-resources + + # Print the supported API Resources with more information + kubectl api-resources -o wide + + # Print the supported namespaced resources + kubectl api-resources --namespaced=true + + # Print the supported non-namespaced resources + kubectl api-resources --namespaced=false + + # Print the supported API Resources with specific APIGroup + kubectl api-resources --api-group=extensions`) +) + +// ApiResourcesOptions is the start of the data required to perform the operation. As new fields are added, add them here instead of +// referencing the cmd.Flags() +type ApiResourcesOptions struct { + out io.Writer + + Output string + APIGroup string + Namespaced bool + NoHeaders bool +} + +// groupResource contains the APIGroup and APIResource +type groupResource struct { + APIGroup string + APIResource metav1.APIResource +} + +func NewCmdApiResources(f cmdutil.Factory, out io.Writer) *cobra.Command { + options := &ApiResourcesOptions{ + out: out, + } + + cmd := &cobra.Command{ + Use: "api-resources", + Short: "Print the supported API resources on the server", + Long: "Print the supported API resources on the server", + Example: apiresources_example, + Run: func(cmd *cobra.Command, args []string) { + cmdutil.CheckErr(options.Complete(cmd)) + cmdutil.CheckErr(options.Validate(cmd)) + cmdutil.CheckErr(options.RunApiResources(cmd, f)) + }, + } + cmdutil.AddOutputFlags(cmd) + cmdutil.AddNoHeadersFlags(cmd) + cmd.Flags().StringVar(&options.APIGroup, "api-group", "", "The API group to use when talking to the server.") + cmd.Flags().BoolVar(&options.Namespaced, "namespaced", true, "Namespaced indicates if a resource is namespaced or not.") + return cmd +} + +func (o *ApiResourcesOptions) Complete(cmd *cobra.Command) error { + o.Output = cmdutil.GetFlagString(cmd, "output") + o.NoHeaders = cmdutil.GetFlagBool(cmd, "no-headers") + return nil +} + +func (o *ApiResourcesOptions) Validate(cmd *cobra.Command) error { + validOutputTypes := sets.NewString("", "json", "yaml", "wide", "name", "custom-columns", "custom-columns-file", "go-template", "go-template-file", "jsonpath", "jsonpath-file") + supportedOutputTypes := sets.NewString("", "wide") + outputFormat := cmdutil.GetFlagString(cmd, "output") + if !validOutputTypes.Has(outputFormat) { + return fmt.Errorf("output must be one of '' or 'wide': %v", outputFormat) + } + if !supportedOutputTypes.Has(outputFormat) { + return fmt.Errorf("--output %v is not available in kubectl api-resources", outputFormat) + } + return nil +} + +func (o *ApiResourcesOptions) RunApiResources(cmd *cobra.Command, f cmdutil.Factory) error { + w := printers.GetNewTabWriter(o.out) + defer w.Flush() + + discoveryclient, err := f.DiscoveryClient() + if err != nil { + return err + } + + // Always request fresh data from the server + discoveryclient.Invalidate() + + lists, err := discoveryclient.ServerPreferredResources() + if err != nil { + return err + } + + resources := []groupResource{} + + groupChanged := cmd.Flags().Changed("api-group") + nsChanged := cmd.Flags().Changed("namespaced") + + for _, list := range lists { + if len(list.APIResources) == 0 { + continue + } + gv, err := schema.ParseGroupVersion(list.GroupVersion) + if err != nil { + continue + } + for _, resource := range list.APIResources { + if len(resource.Verbs) == 0 { + continue + } + // filter apiGroup + if groupChanged && o.APIGroup != gv.Group { + continue + } + // filter namespaced + if nsChanged && o.Namespaced != resource.Namespaced { + continue + } + resources = append(resources, groupResource{ + APIGroup: gv.Group, + APIResource: resource, + }) + } + } + + if o.NoHeaders == false { + if err = printContextHeaders(w, o.Output); err != nil { + return err + } + } + + sort.Stable(sortableGroupResource(resources)) + for _, r := range resources { + if o.Output == "wide" { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\t%v\n", + r.APIResource.Name, + strings.Join(r.APIResource.ShortNames, ","), + r.APIGroup, + r.APIResource.Namespaced, + r.APIResource.Kind, + r.APIResource.Verbs); err != nil { + return err + } + } else { + if _, err := fmt.Fprintf(w, "%s\t%s\t%s\t%v\t%s\n", + r.APIResource.Name, + strings.Join(r.APIResource.ShortNames, ","), + r.APIGroup, + r.APIResource.Namespaced, + r.APIResource.Kind); err != nil { + return err + } + } + } + return nil +} + +func printContextHeaders(out io.Writer, output string) error { + columnNames := []string{"NAME", "SHORTNAMES", "APIGROUP", "NAMESPACED", "KIND"} + if output == "wide" { + columnNames = append(columnNames, "VERBS") + } + _, err := fmt.Fprintf(out, "%s\n", strings.Join(columnNames, "\t")) + return err +} + +type sortableGroupResource []groupResource + +func (s sortableGroupResource) Len() int { return len(s) } +func (s sortableGroupResource) Swap(i, j int) { s[i], s[j] = s[j], s[i] } +func (s sortableGroupResource) Less(i, j int) bool { + ret := strings.Compare(s[i].APIGroup, s[j].APIGroup) + if ret > 0 { + return false + } else if ret == 0 { + return strings.Compare(s[i].APIResource.Name, s[j].APIResource.Name) < 0 + } + return true +} diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 8ab38ac0b7c..ced23d09afe 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -346,6 +346,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob cmds.AddCommand(NewCmdPlugin(f, in, out, err)) cmds.AddCommand(NewCmdVersion(f, out)) cmds.AddCommand(NewCmdApiVersions(f, out)) + cmds.AddCommand(NewCmdApiResources(f, out)) cmds.AddCommand(NewCmdOptions(out)) return cmds