diff --git a/build/visible_to/BUILD b/build/visible_to/BUILD index 6963f4960f..9d1ad058c6 100644 --- a/build/visible_to/BUILD +++ b/build/visible_to/BUILD @@ -174,6 +174,7 @@ package_group( "//pkg/kubectl/cmd", "//pkg/kubectl/cmd/auth", "//pkg/kubectl/cmd/config", + "//pkg/kubectl/cmd/resource", "//pkg/kubectl/cmd/rollout", "//pkg/kubectl/cmd/set", "//pkg/kubectl/cmd/templates", @@ -194,6 +195,7 @@ package_group( packages = [ "//pkg/kubectl/cmd", "//pkg/kubectl/cmd/auth", + "//pkg/kubectl/cmd/resource", "//pkg/kubectl/cmd/set", ], ) @@ -223,6 +225,7 @@ package_group( "//pkg/kubectl/cmd", "//pkg/kubectl/cmd/auth", "//pkg/kubectl/cmd/config", + "//pkg/kubectl/cmd/resource", "//pkg/kubectl/cmd/rollout", "//pkg/kubectl/cmd/set", "//pkg/kubectl/cmd/testing", @@ -290,6 +293,7 @@ package_group( "//pkg/kubectl/cmd", "//pkg/kubectl/cmd/auth", "//pkg/kubectl/cmd/config", + "//pkg/kubectl/cmd/resource", "//pkg/kubectl/cmd/rollout", "//pkg/kubectl/cmd/set", "//pkg/kubectl/cmd/testing", diff --git a/pkg/kubectl/cmd/BUILD b/pkg/kubectl/cmd/BUILD index eb34543956..58a7af370a 100644 --- a/pkg/kubectl/cmd/BUILD +++ b/pkg/kubectl/cmd/BUILD @@ -44,7 +44,6 @@ go_library( "exec.go", "explain.go", "expose.go", - "get.go", "help.go", "label.go", "logs.go", @@ -80,12 +79,12 @@ go_library( "//pkg/kubectl/apply/strategy:go_default_library", "//pkg/kubectl/cmd/auth:go_default_library", "//pkg/kubectl/cmd/config:go_default_library", + "//pkg/kubectl/cmd/resource:go_default_library", "//pkg/kubectl/cmd/rollout:go_default_library", "//pkg/kubectl/cmd/set:go_default_library", "//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", "//pkg/kubectl/cmd/util/editor:go_default_library", - "//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/explain:go_default_library", "//pkg/kubectl/metricsutil:go_default_library", "//pkg/kubectl/plugins:go_default_library", @@ -179,7 +178,6 @@ go_test( "edit_test.go", "exec_test.go", "expose_test.go", - "get_test.go", "label_test.go", "logs_test.go", "patch_test.go", @@ -206,13 +204,11 @@ go_test( "//pkg/api/ref:go_default_library", "//pkg/api/testapi:go_default_library", "//pkg/api/testing:go_default_library", - "//pkg/api/v1:go_default_library", "//pkg/apis/batch:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/kubectl:go_default_library", "//pkg/kubectl/cmd/testing:go_default_library", "//pkg/kubectl/cmd/util:go_default_library", - "//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/plugins:go_default_library", "//pkg/kubectl/resource:go_default_library", "//pkg/kubectl/scheme:go_default_library", @@ -228,7 +224,6 @@ go_test( "//vendor/k8s.io/api/policy/v1beta1:go_default_library", "//vendor/k8s.io/api/rbac/v1:go_default_library", "//vendor/k8s.io/api/rbac/v1beta1:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", @@ -236,21 +231,16 @@ go_test( "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/intstr:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/strategicpatch:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", - "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", "//vendor/k8s.io/client-go/dynamic:go_default_library", "//vendor/k8s.io/client-go/rest:go_default_library", "//vendor/k8s.io/client-go/rest/fake:go_default_library", - "//vendor/k8s.io/client-go/rest/watch:go_default_library", "//vendor/k8s.io/client-go/tools/remotecommand:go_default_library", - "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", "//vendor/k8s.io/metrics/pkg/apis/metrics/v1alpha1:go_default_library", "//vendor/k8s.io/utils/exec:go_default_library", ], @@ -268,6 +258,7 @@ filegroup( ":package-srcs", "//pkg/kubectl/cmd/auth:all-srcs", "//pkg/kubectl/cmd/config:all-srcs", + "//pkg/kubectl/cmd/resource:all-srcs", "//pkg/kubectl/cmd/rollout:all-srcs", "//pkg/kubectl/cmd/set:all-srcs", "//pkg/kubectl/cmd/templates:all-srcs", diff --git a/pkg/kubectl/cmd/annotate.go b/pkg/kubectl/cmd/annotate.go index 3d53db9697..f014134bbc 100644 --- a/pkg/kubectl/cmd/annotate.go +++ b/pkg/kubectl/cmd/annotate.go @@ -65,14 +65,16 @@ type AnnotateOptions struct { var ( annotateLong = templates.LongDesc(` - Update the annotations on one or more resources. + Update the annotations on one or more resources - * An annotation is a key/value pair that can hold larger (compared to a label), and possibly not human-readable, data. - * It is intended to store non-identifying auxiliary data, especially data manipulated by tools and system extensions. - * If --overwrite is true, then existing annotations can be overwritten, otherwise attempting to overwrite an annotation will result in an error. - * If --resource-version is specified, then updates will use this resource version, otherwise the existing resource-version will be used. + All Kubernetes objects support the ability to store additional data with the object as + annotations. Annotations are key/value pairs that can be larger than labels and include + arbitrary string values such as structured JSON. Tools and system extensions may use + annotations to store their own data. - ` + validResources) + Attempting to set an annotation that already exists will fail unless --overwrite is set. + If --resource-version is specified and does not match the current resource version on + the server the command will fail.`) annotateExample = templates.Examples(i18n.T(` # Update pod 'foo' with the annotation 'description' and the value 'my frontend'. @@ -113,7 +115,7 @@ func NewCmdAnnotate(f cmdutil.Factory, out io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "annotate [--overwrite] (-f FILENAME | TYPE NAME) KEY_1=VAL_1 ... KEY_N=VAL_N [--resource-version=version]", Short: i18n.T("Update the annotations on a resource"), - Long: annotateLong, + Long: annotateLong + "\n\n" + cmdutil.ValidResourceTypeList(f), Example: annotateExample, Run: func(cmd *cobra.Command, args []string) { if err := options.Complete(out, cmd, args); err != nil { diff --git a/pkg/kubectl/cmd/cmd.go b/pkg/kubectl/cmd/cmd.go index 1538bb4a50..850da6dda2 100644 --- a/pkg/kubectl/cmd/cmd.go +++ b/pkg/kubectl/cmd/cmd.go @@ -24,6 +24,7 @@ import ( "k8s.io/client-go/tools/clientcmd" "k8s.io/kubernetes/pkg/kubectl/cmd/auth" cmdconfig "k8s.io/kubernetes/pkg/kubectl/cmd/config" + "k8s.io/kubernetes/pkg/kubectl/cmd/resource" "k8s.io/kubernetes/pkg/kubectl/cmd/rollout" "k8s.io/kubernetes/pkg/kubectl/cmd/set" "k8s.io/kubernetes/pkg/kubectl/cmd/templates" @@ -199,51 +200,6 @@ __custom_func() { ;; esac } -` - - // If you add a resource to this list, please also take a look at pkg/kubectl/kubectl.go - // and add a short forms entry in expandResourceShortcut() when appropriate. - // TODO: This should be populated using the discovery information from apiserver. - validResources = `Valid resource types include: - - * all - * certificatesigningrequests (aka 'csr') - * clusterrolebindings - * clusterroles - * componentstatuses (aka 'cs') - * configmaps (aka 'cm') - * controllerrevisions - * cronjobs - * customresourcedefinition (aka 'crd') - * daemonsets (aka 'ds') - * deployments (aka 'deploy') - * endpoints (aka 'ep') - * events (aka 'ev') - * horizontalpodautoscalers (aka 'hpa') - * ingresses (aka 'ing') - * jobs - * limitranges (aka 'limits') - * namespaces (aka 'ns') - * networkpolicies (aka 'netpol') - * nodes (aka 'no') - * persistentvolumeclaims (aka 'pvc') - * persistentvolumes (aka 'pv') - * poddisruptionbudgets (aka 'pdb') - * podpreset - * pods (aka 'po') - * podsecuritypolicies (aka 'psp') - * podtemplates - * replicasets (aka 'rs') - * replicationcontrollers (aka 'rc') - * resourcequotas (aka 'quota') - * rolebindings - * roles - * secrets - * serviceaccounts (aka 'sa') - * services (aka 'svc') - * statefulsets (aka 'sts') - * storageclasses (aka 'sc') - ` ) @@ -297,7 +253,7 @@ func NewKubectlCommand(f cmdutil.Factory, in io.Reader, out, err io.Writer) *cob { Message: "Basic Commands (Intermediate):", Commands: []*cobra.Command{ - NewCmdGet(f, out, err), + resource.NewCmdGet(f, out, err), NewCmdExplain(f, out, err), NewCmdEdit(f, out, err), NewCmdDelete(f, out, err), diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 6b405aeaee..736f2b0ee7 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -38,6 +38,7 @@ import ( "k8s.io/client-go/rest/fake" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/testapi" + apitesting "k8s.io/kubernetes/pkg/api/testing" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/scheme" @@ -85,6 +86,52 @@ func defaultClientConfigForVersion(version *schema.GroupVersion) *restclient.Con } } +func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) { + pods := &api.PodList{ + ListMeta: metav1.ListMeta{ + ResourceVersion: "15", + }, + Items: []api.Pod{ + { + ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"}, + Spec: apitesting.DeepEqualSafePodSpec(), + }, + { + ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"}, + Spec: apitesting.DeepEqualSafePodSpec(), + }, + }, + } + svc := &api.ServiceList{ + ListMeta: metav1.ListMeta{ + ResourceVersion: "16", + }, + Items: []api.Service{ + { + ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"}, + Spec: api.ServiceSpec{ + SessionAffinity: "None", + Type: api.ServiceTypeClusterIP, + }, + }, + }, + } + rc := &api.ReplicationControllerList{ + ListMeta: metav1.ListMeta{ + ResourceVersion: "17", + }, + Items: []api.ReplicationController{ + { + ObjectMeta: metav1.ObjectMeta{Name: "rc1", Namespace: "test", ResourceVersion: "18"}, + Spec: api.ReplicationControllerSpec{ + Replicas: 1, + }, + }, + }, + } + return pods, svc, rc +} + type testPrinter struct { Objects []runtime.Object Err error diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 579d521de9..91d8306378 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -37,20 +37,19 @@ import ( ) var ( - describe_long = templates.LongDesc(` - Show details of a specific resource or group of resources. - It includes the uninitialized objects, unless --include-uninitialized=false is explicitly set. - This command joins many API calls together to form a detailed description of a - given resource or group of resources. + describeLong = templates.LongDesc(` + Show details of a specific resource or group of resources + + Print a detailed description of the selected resources, including related resources such + as events or controllers. You may select a single object by name, all objects of that + type, provide a name prefix, or label selector. For example: $ kubectl describe TYPE NAME_PREFIX will first check for an exact match on TYPE and NAME_PREFIX. If no such resource - exists, it will output details for every resource that has a name prefixed with NAME_PREFIX. + exists, it will output details for every resource that has a name prefixed with NAME_PREFIX.`) - ` + validResources) - - describe_example = templates.Examples(i18n.T(` + describeExample = templates.Examples(i18n.T(` # Describe a node kubectl describe nodes kubernetes-node-emt8.c.myproject.internal @@ -83,8 +82,8 @@ func NewCmdDescribe(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "describe (-f FILENAME | TYPE [NAME_PREFIX | -l label] | TYPE/NAME)", Short: i18n.T("Show details of a specific resource or group of resources"), - Long: describe_long, - Example: describe_example, + Long: describeLong + "\n\n" + cmdutil.ValidResourceTypeList(f), + Example: describeExample, Run: func(cmd *cobra.Command, args []string) { err := RunDescribe(f, out, cmdErr, cmd, args, options, describerSettings) cmdutil.CheckErr(err) @@ -113,7 +112,7 @@ func RunDescribe(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, a enforceNamespace = false } if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) { - fmt.Fprint(cmdErr, "You must specify the type of resource to describe. ", validResources) + fmt.Fprint(cmdErr, "You must specify the type of resource to describe. ", cmdutil.ValidResourceTypeList(f)) return cmdutil.UsageErrorf(cmd, "Required resource not specified.") } diff --git a/pkg/kubectl/cmd/explain.go b/pkg/kubectl/cmd/explain.go index ec9d07ba53..a339df7c81 100644 --- a/pkg/kubectl/cmd/explain.go +++ b/pkg/kubectl/cmd/explain.go @@ -32,9 +32,15 @@ import ( var ( explainLong = templates.LongDesc(` - Documentation of resources. - - ` + validResources) + List the fields for supported resources + + This command describes the fields associated with each supported API resource. + Fields are identified via a simple JSONPath identifier: + + .[.] + + Add the --recursive flag to display all of the fields at once without descriptions. + Information about each field is retrieved from the server in OpenAPI format.`) explainExamples = templates.Examples(i18n.T(` # Get the documentation of the resource and its fields @@ -49,7 +55,7 @@ func NewCmdExplain(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { cmd := &cobra.Command{ Use: "explain RESOURCE", Short: i18n.T("Documentation of resources"), - Long: explainLong, + Long: explainLong + "\n\n" + cmdutil.ValidResourceTypeList(f), Example: explainExamples, Run: func(cmd *cobra.Command, args []string) { err := RunExplain(f, out, cmdErr, cmd, args) @@ -65,7 +71,7 @@ func NewCmdExplain(f cmdutil.Factory, out, cmdErr io.Writer) *cobra.Command { // RunExplain executes the appropriate steps to print a model's documentation func RunExplain(f cmdutil.Factory, out, cmdErr io.Writer, cmd *cobra.Command, args []string) error { if len(args) == 0 { - fmt.Fprintf(cmdErr, "You must specify the type of resource to explain. %s\n", validResources) + fmt.Fprintf(cmdErr, "You must specify the type of resource to explain. %s\n", cmdutil.ValidResourceTypeList(f)) return cmdutil.UsageErrorf(cmd, "Required resource not specified.") } if len(args) > 1 { diff --git a/pkg/kubectl/cmd/resource/BUILD b/pkg/kubectl/cmd/resource/BUILD new file mode 100644 index 0000000000..71cf74bc5e --- /dev/null +++ b/pkg/kubectl/cmd/resource/BUILD @@ -0,0 +1,78 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["get.go"], + importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/resource", + visibility = ["//visibility:public"], + deps = [ + "//pkg/api:go_default_library", + "//pkg/kubectl:go_default_library", + "//pkg/kubectl/cmd/templates:go_default_library", + "//pkg/kubectl/cmd/util:go_default_library", + "//pkg/kubectl/cmd/util/openapi:go_default_library", + "//pkg/kubectl/resource:go_default_library", + "//pkg/kubectl/util/i18n:go_default_library", + "//pkg/printers:go_default_library", + "//pkg/util/interrupt:go_default_library", + "//vendor/github.com/golang/glog:go_default_library", + "//vendor/github.com/spf13/cobra:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + ], +) + +go_test( + name = "go_default_test", + srcs = ["get_test.go"], + data = [ + "//examples:config", + "//test/fixtures", + ], + importpath = "k8s.io/kubernetes/pkg/kubectl/cmd/resource", + library = ":go_default_library", + deps = [ + "//pkg/api:go_default_library", + "//pkg/api/testapi:go_default_library", + "//pkg/api/testing:go_default_library", + "//pkg/api/v1:go_default_library", + "//pkg/kubectl/cmd/testing:go_default_library", + "//pkg/kubectl/cmd/util:go_default_library", + "//pkg/kubectl/cmd/util/openapi:go_default_library", + "//pkg/kubectl/scheme:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/json:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/streaming:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/watch:go_default_library", + "//vendor/k8s.io/client-go/dynamic:go_default_library", + "//vendor/k8s.io/client-go/rest:go_default_library", + "//vendor/k8s.io/client-go/rest/fake:go_default_library", + "//vendor/k8s.io/client-go/rest/watch:go_default_library", + "//vendor/k8s.io/kube-openapi/pkg/util/proto:go_default_library", + ], +) + +filegroup( + name = "package-srcs", + srcs = glob(["**"]), + tags = ["automanaged"], + visibility = ["//visibility:private"], +) + +filegroup( + name = "all-srcs", + srcs = [":package-srcs"], + tags = ["automanaged"], + visibility = ["//visibility:public"], +) diff --git a/pkg/kubectl/cmd/get.go b/pkg/kubectl/cmd/resource/get.go similarity index 97% rename from pkg/kubectl/cmd/get.go rename to pkg/kubectl/cmd/resource/get.go index 8d6da27387..2e4cacfc73 100644 --- a/pkg/kubectl/cmd/get.go +++ b/pkg/kubectl/cmd/resource/get.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package resource import ( "encoding/json" @@ -56,14 +56,17 @@ type GetOptions struct { var ( getLong = templates.LongDesc(` - Display one or many resources. - - ` + validResources + ` + Display one or many resources + Prints a table of the most important information about the specified resources. + You can filter the list using a label selector and the --selector flag. If the + desired resource type is namespaced you will only see results in your current + namespace unless you pass --all-namespaces. + This command will hide resources that have completed, such as pods that are in the Succeeded or Failed phases. You can see the full results for any - resource by providing the '--show-all' flag, but this flag does not include - the uninitialized objects by default, unless '--include-uninitialized' is explicitly set. + resource by providing the --show-all flag. Uninitialized objects are not + shown unless --include-uninitialized is passed. By specifying the output as 'template' and providing a Go template as the value of the --template flag, you can filter the attributes of the fetched resources.`) @@ -120,7 +123,7 @@ func NewCmdGet(f cmdutil.Factory, out io.Writer, errOut io.Writer) *cobra.Comman cmd := &cobra.Command{ Use: "get [(-o|--output=)json|yaml|wide|custom-columns=...|custom-columns-file=...|go-template=...|go-template-file=...|jsonpath=...|jsonpath-file=...] (TYPE [NAME | -l label] | TYPE/NAME ...) [flags]", Short: i18n.T("Display one or many resources"), - Long: getLong, + Long: getLong + "\n\n" + cmdutil.ValidResourceTypeList(f), Example: getExample, Run: func(cmd *cobra.Command, args []string) { err := RunGet(f, out, errOut, cmd, args, options) @@ -191,7 +194,7 @@ func RunGet(f cmdutil.Factory, out, errOut io.Writer, cmd *cobra.Command, args [ } if len(args) == 0 && cmdutil.IsFilenameSliceEmpty(options.Filenames) { - fmt.Fprint(errOut, "You must specify the type of resource to get. ", validResources) + fmt.Fprint(errOut, "You must specify the type of resource to get. ", cmdutil.ValidResourceTypeList(f)) fullCmdName := cmd.Parent().CommandPath() usageString := "Required resource not specified." diff --git a/pkg/kubectl/cmd/get_test.go b/pkg/kubectl/cmd/resource/get_test.go similarity index 93% rename from pkg/kubectl/cmd/get_test.go rename to pkg/kubectl/cmd/resource/get_test.go index bc5b5569cb..1054004fbe 100644 --- a/pkg/kubectl/cmd/get_test.go +++ b/pkg/kubectl/cmd/resource/get_test.go @@ -14,11 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -package cmd +package resource import ( "bytes" encjson "encoding/json" + "fmt" "io" "io/ioutil" "net/http" @@ -35,6 +36,7 @@ import ( "k8s.io/apimachinery/pkg/runtime/serializer/json" "k8s.io/apimachinery/pkg/runtime/serializer/streaming" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" restclient "k8s.io/client-go/rest" "k8s.io/client-go/rest/fake" restclientwatch "k8s.io/client-go/rest/watch" @@ -44,6 +46,7 @@ import ( apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/v1" cmdtesting "k8s.io/kubernetes/pkg/kubectl/cmd/testing" + cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util" "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi" "k8s.io/kubernetes/pkg/kubectl/scheme" ) @@ -55,6 +58,75 @@ func init() { scheme.Scheme.AddConversionFuncs(v1.Convert_v1_PodSecurityContext_To_api_PodSecurityContext) } +var unstructuredSerializer = dynamic.ContentConfig().NegotiatedSerializer + +func defaultHeader() http.Header { + header := http.Header{} + header.Set("Content-Type", runtime.ContentTypeJSON) + return header +} + +func defaultClientConfig() *restclient.Config { + return &restclient.Config{ + APIPath: "/api", + ContentConfig: restclient.ContentConfig{ + NegotiatedSerializer: scheme.Codecs, + ContentType: runtime.ContentTypeJSON, + GroupVersion: &scheme.Registry.GroupOrDie(api.GroupName).GroupVersion, + }, + } +} + +func defaultClientConfigForVersion(version *schema.GroupVersion) *restclient.Config { + return &restclient.Config{ + APIPath: "/api", + ContentConfig: restclient.ContentConfig{ + NegotiatedSerializer: scheme.Codecs, + ContentType: runtime.ContentTypeJSON, + GroupVersion: version, + }, + } +} + +type testPrinter struct { + Objects []runtime.Object + Err error + GenericPrinter bool +} + +func (t *testPrinter) PrintObj(obj runtime.Object, out io.Writer) error { + t.Objects = append(t.Objects, obj) + fmt.Fprintf(out, "%#v", obj) + return t.Err +} + +// TODO: implement HandledResources() +func (t *testPrinter) HandledResources() []string { + return []string{} +} + +func (t *testPrinter) AfterPrint(output io.Writer, res string) error { + return nil +} + +func (t *testPrinter) IsGeneric() bool { + return t.GenericPrinter +} + +func objBody(codec runtime.Codec, obj runtime.Object) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader([]byte(runtime.EncodeOrDie(codec, obj)))) +} + +func stringBody(body string) io.ReadCloser { + return ioutil.NopCloser(bytes.NewReader([]byte(body))) +} + +func initTestErrorHandler(t *testing.T) { + cmdutil.BehaviorOnFatal(func(str string, code int) { + t.Errorf("Error running command (exit code %d): %s", code, str) + }) +} + func testData() (*api.PodList, *api.ServiceList, *api.ReplicationControllerList) { pods := &api.PodList{ ListMeta: metav1.ListMeta{ @@ -295,7 +367,7 @@ func TestGetObjectsFiltered(t *testing.T) { {args: []string{"pods"}, flags: map[string]string{"show-all": "true"}, resp: pods, expect: []runtime.Object{first, second}}, {args: []string{"pods/foo"}, resp: first, expect: []runtime.Object{first}, genericPrinter: true}, {args: []string{"pods"}, flags: map[string]string{"output": "yaml"}, resp: pods, expect: []runtime.Object{second}}, - {args: []string{}, flags: map[string]string{"filename": "../../../examples/storage/cassandra/cassandra-controller.yaml"}, resp: pods, expect: []runtime.Object{first, second}}, + {args: []string{}, flags: map[string]string{"filename": "../../../../examples/storage/cassandra/cassandra-controller.yaml"}, resp: pods, expect: []runtime.Object{first, second}}, {args: []string{"pods"}, resp: pods, expect: []runtime.Object{second}}, {args: []string{"pods"}, flags: map[string]string{"show-all": "true", "output": "yaml"}, resp: pods, expect: []runtime.Object{first, second}}, @@ -467,7 +539,7 @@ func TestGetObjectsIdentifiedByFile(t *testing.T) { cmd := NewCmdGet(f, buf, errBuf) cmd.SetOutput(buf) - cmd.Flags().Set("filename", "../../../examples/storage/cassandra/cassandra-controller.yaml") + cmd.Flags().Set("filename", "../../../../examples/storage/cassandra/cassandra-controller.yaml") cmd.Run(cmd, []string{}) expected := []runtime.Object{&pods.Items[0]} @@ -955,9 +1027,8 @@ func TestWatchSelector(t *testing.T) { case "/namespaces/test/pods": if req.URL.Query().Get("watch") == "true" { return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[2:])}, nil - } else { - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil } + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil default: t.Fatalf("request url: %#v,and request: %#v", req.URL, req) return nil, nil @@ -1055,7 +1126,7 @@ func TestWatchResourceIdentifiedByFile(t *testing.T) { cmd.SetOutput(buf) cmd.Flags().Set("watch", "true") - cmd.Flags().Set("filename", "../../../examples/storage/cassandra/cassandra-controller.yaml") + cmd.Flags().Set("filename", "../../../../examples/storage/cassandra/cassandra-controller.yaml") cmd.Run(cmd, []string{}) expected := []runtime.Object{&pods[1], events[2].Object, events[3].Object} @@ -1125,9 +1196,8 @@ func TestWatchOnlyList(t *testing.T) { case "/namespaces/test/pods": if req.URL.Query().Get("watch") == "true" { return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: watchBody(codec, events[2:])}, nil - } else { - return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil } + return &http.Response{StatusCode: 200, Header: defaultHeader(), Body: objBody(codec, podList)}, nil default: t.Fatalf("request url: %#v,and request: %#v", req.URL, req) return nil, nil diff --git a/pkg/kubectl/cmd/util/BUILD b/pkg/kubectl/cmd/util/BUILD index f14c501e93..4f7754cfa2 100644 --- a/pkg/kubectl/cmd/util/BUILD +++ b/pkg/kubectl/cmd/util/BUILD @@ -33,6 +33,7 @@ go_library( "//pkg/client/unversioned:go_default_library", "//pkg/controller:go_default_library", "//pkg/kubectl:go_default_library", + "//pkg/kubectl/cmd/templates:go_default_library", "//pkg/kubectl/cmd/util/openapi:go_default_library", "//pkg/kubectl/cmd/util/openapi/validation:go_default_library", "//pkg/kubectl/plugins:go_default_library", diff --git a/pkg/kubectl/cmd/util/printing.go b/pkg/kubectl/cmd/util/printing.go index 8e26c25599..7954fc8e24 100644 --- a/pkg/kubectl/cmd/util/printing.go +++ b/pkg/kubectl/cmd/util/printing.go @@ -24,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/runtime" "k8s.io/kubernetes/pkg/kubectl" + "k8s.io/kubernetes/pkg/kubectl/cmd/templates" "k8s.io/kubernetes/pkg/kubectl/resource" "k8s.io/kubernetes/pkg/printers" printersinternal "k8s.io/kubernetes/pkg/printers/internalversion" @@ -216,3 +217,53 @@ func maybeWrapSortingPrinter(cmd *cobra.Command, printer printers.ResourcePrinte } return printer } + +// ValidResourceTypeList returns a multi-line string containing the valid resources. May +// be called before the factory is initialized. +// TODO: This function implementation should be replaced with a real implementation from the +// discovery service. +func ValidResourceTypeList(f ClientAccessFactory) string { + // TODO: Should attempt to use the cached discovery list or fallback to a static list + // that is calculated from code compiled into the factory. + return templates.LongDesc(`Valid resource types include: + + * all + * certificatesigningrequests (aka 'csr') + * clusterrolebindings + * clusterroles + * componentstatuses (aka 'cs') + * configmaps (aka 'cm') + * controllerrevisions + * cronjobs + * customresourcedefinition (aka 'crd') + * daemonsets (aka 'ds') + * deployments (aka 'deploy') + * endpoints (aka 'ep') + * events (aka 'ev') + * horizontalpodautoscalers (aka 'hpa') + * ingresses (aka 'ing') + * jobs + * limitranges (aka 'limits') + * namespaces (aka 'ns') + * networkpolicies (aka 'netpol') + * nodes (aka 'no') + * persistentvolumeclaims (aka 'pvc') + * persistentvolumes (aka 'pv') + * poddisruptionbudgets (aka 'pdb') + * podpreset + * pods (aka 'po') + * podsecuritypolicies (aka 'psp') + * podtemplates + * replicasets (aka 'rs') + * replicationcontrollers (aka 'rc') + * resourcequotas (aka 'quota') + * rolebindings + * roles + * secrets + * serviceaccounts (aka 'sa') + * services (aka 'svc') + * statefulsets (aka 'sts') + * storageclasses (aka 'sc') + + `) +}