From dd2c9c578df35a7ba21f082e1e0e2f298a58f073 Mon Sep 17 00:00:00 2001 From: Jan Chaloupka Date: Wed, 20 Apr 2016 19:27:32 +0200 Subject: [PATCH] Introduce kubectl describe --show-events Introduce DescriberSettings for Describer display options Introduce --show-events flag and DescriberSettings in Describer methods Introduce unit-tests Regenerated kubectl describe docs Add events flag tests to test-cmd.sh Signed-off-by: dhodovsk@redhat.com Signed-off-by: jchaloup@redhat.com --- contrib/completions/bash/kubectl | 1 + docs/man/man1/kubectl-describe.1 | 4 + docs/user-guide/kubectl/kubectl_describe.md | 3 +- docs/yaml/kubectl/kubectl_describe.yaml | 3 + hack/lib/test.sh | 70 +++++++++ hack/test-cmd.sh | 60 +++++++ hack/verify-flags/known-flags.txt | 1 + pkg/kubectl/cmd/cmd_test.go | 4 +- pkg/kubectl/cmd/describe.go | 14 +- pkg/kubectl/cmd/describe_test.go | 40 +++++ pkg/kubectl/describe.go | 163 ++++++++++++-------- pkg/kubectl/describe_test.go | 123 ++++++++++++++- 12 files changed, 410 insertions(+), 76 deletions(-) diff --git a/contrib/completions/bash/kubectl b/contrib/completions/bash/kubectl index 3cc679556a7..bcc6894728a 100644 --- a/contrib/completions/bash/kubectl +++ b/contrib/completions/bash/kubectl @@ -497,6 +497,7 @@ _kubectl_describe() flags+=("-R") flags+=("--selector=") two_word_flags+=("-l") + flags+=("--show-events") flags+=("--alsologtostderr") flags+=("--api-version=") flags+=("--as=") diff --git a/docs/man/man1/kubectl-describe.1 b/docs/man/man1/kubectl-describe.1 index 0d18f9639a0..4a6506b74cf 100644 --- a/docs/man/man1/kubectl-describe.1 +++ b/docs/man/man1/kubectl-describe.1 @@ -51,6 +51,10 @@ componentstatuses (cs), endpoints (ep), and secrets. \fB\-l\fP, \fB\-\-selector\fP="" Selector (label query) to filter on +.PP +\fB\-\-show\-events\fP=true + If true, display events related to the described object. + .SH OPTIONS INHERITED FROM PARENT COMMANDS .PP diff --git a/docs/user-guide/kubectl/kubectl_describe.md b/docs/user-guide/kubectl/kubectl_describe.md index b92e804f89f..1c6123b547f 100644 --- a/docs/user-guide/kubectl/kubectl_describe.md +++ b/docs/user-guide/kubectl/kubectl_describe.md @@ -89,6 +89,7 @@ kubectl describe pods frontend --include-extended-apis[=true]: If true, include definitions of new APIs via calls to the API server. [default true] -R, --recursive[=false]: If true, process directory recursively. -l, --selector="": Selector (label query) to filter on + --show-events[=true]: If true, display events related to the described object. ``` ### Options inherited from parent commands @@ -123,7 +124,7 @@ kubectl describe pods frontend * [kubectl](kubectl.md) - kubectl controls the Kubernetes cluster manager -###### Auto generated by spf13/cobra on 30-Mar-2016 +###### Auto generated by spf13/cobra on 26-Apr-2016 [![Analytics](https://kubernetes-site.appspot.com/UA-36037335-10/GitHub/docs/user-guide/kubectl/kubectl_describe.md?pixel)]() diff --git a/docs/yaml/kubectl/kubectl_describe.yaml b/docs/yaml/kubectl/kubectl_describe.yaml index c62f47b113c..678fef12722 100644 --- a/docs/yaml/kubectl/kubectl_describe.yaml +++ b/docs/yaml/kubectl/kubectl_describe.yaml @@ -33,6 +33,9 @@ options: - name: selector shorthand: l usage: Selector (label query) to filter on +- name: show-events + default_value: "true" + usage: If true, display events related to the described object. inherited_options: - name: alsologtostderr default_value: "false" diff --git a/hack/lib/test.sh b/hack/lib/test.sh index bb694b3f12d..8d47a3f488f 100644 --- a/hack/lib/test.sh +++ b/hack/lib/test.sh @@ -108,6 +108,44 @@ kube::test::describe_object_assert() { return 0 } +kube::test::describe_object_events_assert() { + local resource=$1 + local object=$2 + local showevents=${3:-"true"} + + if [[ -z "${3:-}" ]]; then + result=$(eval kubectl describe "${kube_flags[@]}" $resource $object) + else + result=$(eval kubectl describe "${kube_flags[@]}" "--show-events=$showevents" $resource $object) + fi + + if [[ -n $(echo "$result" | grep "No events.\|Events:") ]]; then + local has_events="true" + else + local has_events="false" + fi + if [[ $showevents == $has_events ]]; then + echo -n ${green} + echo "Successful describe" + echo "$result" + echo ${reset} + return 0 + else + echo ${bold}${red} + echo "FAIL" + if [[ $showevents == "false" ]]; then + echo " Events information should not be described in:" + else + echo " Events information not found in:" + fi + echo $result + echo ${reset}${red} + caller + echo ${reset} + return 1 + fi +} + kube::test::describe_resource_assert() { local resource=$1 local matches=${@:2} @@ -136,6 +174,38 @@ kube::test::describe_resource_assert() { return 0 } +kube::test::describe_resource_events_assert() { + local resource=$1 + local showevents=${2:-"true"} + + result=$(eval kubectl describe "${kube_flags[@]}" "--show-events=$showevents" $resource) + + if [[ $(echo "$result" | grep "No events.\|Events:") ]]; then + local has_events="true" + else + local has_events="false" + fi + if [[ $showevents == $has_events ]]; then + echo -n ${green} + echo "Successful describe" + echo "$result" + echo -n ${reset} + return 0 + else + echo ${bold}${red} + echo "FAIL" + if [[ $showevents == "false" ]]; then + echo " Events information should not be described in:" + else + echo " Events information not found in:" + fi + echo $result + caller + echo ${reset} + return 1 + fi +} + kube::test::if_has_string() { local message=$1 local match=$2 diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 61dd2119962..480d8ddacb3 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -368,9 +368,21 @@ runTests() { kube::test::get_object_jsonpath_assert 'pods/valid-pod' "{$id_field}" 'valid-pod' # Describe command should print detailed information kube::test::describe_object_assert pods 'valid-pod' "Name:" "Image:" "Node:" "Labels:" "Status:" "Controllers" + # Describe command should print events information by default + kube::test::describe_object_events_assert pods 'valid-pod' + # Describe command should not print events information when show-events=false + kube::test::describe_object_events_assert pods 'valid-pod' false + # Describe command should print events information when show-events=true + kube::test::describe_object_events_assert pods 'valid-pod' true # Describe command (resource only) should print detailed information kube::test::describe_resource_assert pods "Name:" "Image:" "Node:" "Labels:" "Status:" "Controllers" + # Describe command should print events information by default + kube::test::describe_resource_events_assert pods + # Describe command should not print events information when show-events=false + kube::test::describe_resource_events_assert pods false + # Describe command should print events information when show-events=true + kube::test::describe_resource_events_assert pods true ### Validate Export ### kube::test::get_object_assert 'pods/valid-pod' "{{.metadata.namespace}} {{.metadata.name}}" ' valid-pod' "--export=true" @@ -1220,8 +1232,20 @@ __EOF__ kube::test::get_object_assert services "{{range.items}}{{$id_field}}:{{end}}" 'kubernetes:redis-master:' # Describe command should print detailed information kube::test::describe_object_assert services 'redis-master' "Name:" "Labels:" "Selector:" "IP:" "Port:" "Endpoints:" "Session Affinity:" + # Describe command should print events information by default + kube::test::describe_object_events_assert services 'redis-master' + # Describe command should not print events information when show-events=false + kube::test::describe_object_events_assert services 'redis-master' false + # Describe command should print events information when show-events=true + kube::test::describe_object_events_assert services 'redis-master' true # Describe command (resource only) should print detailed information kube::test::describe_resource_assert services "Name:" "Labels:" "Selector:" "IP:" "Port:" "Endpoints:" "Session Affinity:" + # Describe command should print events information by default + kube::test::describe_resource_events_assert services + # Describe command should not print events information when show-events=false + kube::test::describe_resource_events_assert services false + # Describe command should print events information when show-events=true + kube::test::describe_resource_events_assert services true ### Dump current redis-master service output_service=$(kubectl get service redis-master -o json --output-version=v1 "${kube_flags[@]}") @@ -1327,8 +1351,20 @@ __EOF__ kube::test::get_object_assert rc "{{range.items}}{{$id_field}}:{{end}}" 'frontend:' # Describe command should print detailed information kube::test::describe_object_assert rc 'frontend' "Name:" "Image(s):" "Labels:" "Selector:" "Replicas:" "Pods Status:" + # Describe command should print events information by default + kube::test::describe_object_events_assert rc 'frontend' + # Describe command should not print events information when show-events=false + kube::test::describe_object_events_assert rc 'frontend' false + # Describe command should print events information when show-events=true + kube::test::describe_object_events_assert rc 'frontend' true # Describe command (resource only) should print detailed information kube::test::describe_resource_assert rc "Name:" "Name:" "Image(s):" "Labels:" "Selector:" "Replicas:" "Pods Status:" + # Describe command should print events information by default + kube::test::describe_resource_events_assert rc + # Describe command should not print events information when show-events=false + kube::test::describe_resource_events_assert rc false + # Describe command should print events information when show-events=true + kube::test::describe_resource_events_assert rc true ### Scale replication controller frontend with current-replicas and replicas # Pre-condition: 3 replicas @@ -1582,8 +1618,20 @@ __EOF__ kube::test::get_object_assert rs "{{range.items}}{{$id_field}}:{{end}}" 'frontend:' # Describe command should print detailed information kube::test::describe_object_assert rs 'frontend' "Name:" "Image(s):" "Labels:" "Selector:" "Replicas:" "Pods Status:" + # Describe command should print events information by default + kube::test::describe_object_events_assert rs 'frontend' + # Describe command should not print events information when show-events=false + kube::test::describe_object_events_assert rs 'frontend' false + # Describe command should print events information when show-events=true + kube::test::describe_object_events_assert rs 'frontend' true # Describe command (resource only) should print detailed information kube::test::describe_resource_assert rs "Name:" "Name:" "Image(s):" "Labels:" "Selector:" "Replicas:" "Pods Status:" + # Describe command should print events information by default + kube::test::describe_resource_events_assert rs + # Describe command should not print events information when show-events=false + kube::test::describe_resource_events_assert rs false + # Describe command should print events information when show-events=true + kube::test::describe_resource_events_assert rs true ### Scale replica set frontend with current-replicas and replicas # Pre-condition: 3 replicas @@ -1877,8 +1925,20 @@ __EOF__ kube::test::get_object_assert nodes "{{range.items}}{{$id_field}}:{{end}}" '127.0.0.1:' kube::test::describe_object_assert nodes "127.0.0.1" "Name:" "Labels:" "CreationTimestamp:" "Conditions:" "Addresses:" "Capacity:" "Pods:" + # Describe command should print events information by default + kube::test::describe_object_events_assert nodes "127.0.0.1" + # Describe command should not print events information when show-events=false + kube::test::describe_object_events_assert nodes "127.0.0.1" false + # Describe command should print events information when show-events=true + kube::test::describe_object_events_assert nodes "127.0.0.1" true # Describe command (resource only) should print detailed information kube::test::describe_resource_assert nodes "Name:" "Labels:" "CreationTimestamp:" "Conditions:" "Addresses:" "Capacity:" "Pods:" + # Describe command should print events information by default + kube::test::describe_resource_events_assert nodes + # Describe command should not print events information when show-events=false + kube::test::describe_resource_events_assert nodes false + # Describe command should print events information when show-events=true + kube::test::describe_resource_events_assert nodes true ### kubectl patch update can mark node unschedulable # Pre-condition: node is schedulable diff --git a/hack/verify-flags/known-flags.txt b/hack/verify-flags/known-flags.txt index e19caed43e5..f410f48ee01 100644 --- a/hack/verify-flags/known-flags.txt +++ b/hack/verify-flags/known-flags.txt @@ -378,6 +378,7 @@ service-overrides service-sync-period session-affinity show-all +show-events show-labels shutdown-fd shutdown-fifo diff --git a/pkg/kubectl/cmd/cmd_test.go b/pkg/kubectl/cmd/cmd_test.go index 73c4de9fced..170c33262c5 100644 --- a/pkg/kubectl/cmd/cmd_test.go +++ b/pkg/kubectl/cmd/cmd_test.go @@ -151,12 +151,14 @@ func (t *testPrinter) HandledResources() []string { type testDescriber struct { Name, Namespace string + Settings kubectl.DescriberSettings Output string Err error } -func (t *testDescriber) Describe(namespace, name string) (output string, err error) { +func (t *testDescriber) Describe(namespace, name string, describerSettings kubectl.DescriberSettings) (output string, err error) { t.Namespace, t.Name = namespace, name + t.Settings = describerSettings return t.Output, t.Err } diff --git a/pkg/kubectl/cmd/describe.go b/pkg/kubectl/cmd/describe.go index 0f55f998818..e16c5756d27 100644 --- a/pkg/kubectl/cmd/describe.go +++ b/pkg/kubectl/cmd/describe.go @@ -73,6 +73,7 @@ kubectl describe pods frontend` func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { options := &DescribeOptions{} + describerSettings := &kubectl.DescriberSettings{} validArgs := kubectl.DescribableResources() argAliases := kubectl.ResourceAliases(validArgs) @@ -83,7 +84,7 @@ func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { Long: describe_long, Example: describe_example, Run: func(cmd *cobra.Command, args []string) { - err := RunDescribe(f, out, cmd, args, options) + err := RunDescribe(f, out, cmd, args, options, describerSettings) cmdutil.CheckErr(err) }, ValidArgs: validArgs, @@ -93,11 +94,12 @@ func NewCmdDescribe(f *cmdutil.Factory, out io.Writer) *cobra.Command { kubectl.AddJsonFilenameFlag(cmd, &options.Filenames, usage) cmdutil.AddRecursiveFlag(cmd, &options.Recursive) cmd.Flags().StringP("selector", "l", "", "Selector (label query) to filter on") + cmd.Flags().BoolVar(&describerSettings.ShowEvents, "show-events", true, "If true, display events related to the described object.") cmdutil.AddInclude3rdPartyFlags(cmd) return cmd } -func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DescribeOptions) error { +func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []string, options *DescribeOptions, describerSettings *kubectl.DescriberSettings) error { selector := cmdutil.GetFlagString(cmd, "selector") cmdNamespace, enforceNamespace, err := f.DefaultNamespace() if err != nil { @@ -126,7 +128,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s infos, err := r.Infos() if err != nil { if apierrors.IsNotFound(err) && len(args) == 2 { - return DescribeMatchingResources(mapper, typer, f, cmdNamespace, args[0], args[1], out, err) + return DescribeMatchingResources(mapper, typer, f, cmdNamespace, args[0], args[1], describerSettings, out, err) } allErrs = append(allErrs, err) } @@ -138,7 +140,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s allErrs = append(allErrs, err) continue } - s, err := describer.Describe(info.Namespace, info.Name) + s, err := describer.Describe(info.Namespace, info.Name, *describerSettings) if err != nil { allErrs = append(allErrs, err) continue @@ -149,7 +151,7 @@ func RunDescribe(f *cmdutil.Factory, out io.Writer, cmd *cobra.Command, args []s return utilerrors.NewAggregate(allErrs) } -func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, out io.Writer, originalError error) error { +func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper, f *cmdutil.Factory, namespace, rsrc, prefix string, describerSettings *kubectl.DescriberSettings, out io.Writer, originalError error) error { r := resource.NewBuilder(mapper, typer, resource.ClientMapperFunc(f.ClientForMapping), f.Decoder(true)). NamespaceParam(namespace).DefaultNamespace(). ResourceTypeOrNameArgs(true, rsrc). @@ -173,7 +175,7 @@ func DescribeMatchingResources(mapper meta.RESTMapper, typer runtime.ObjectTyper info := infos[ix] if strings.HasPrefix(info.Name, prefix) { isFound = true - s, err := describer.Describe(info.Namespace, info.Name) + s, err := describer.Describe(info.Namespace, info.Name, *describerSettings) if err != nil { return err } diff --git a/pkg/kubectl/cmd/describe_test.go b/pkg/kubectl/cmd/describe_test.go index cc7312ee8c3..c8b9905ece6 100644 --- a/pkg/kubectl/cmd/describe_test.go +++ b/pkg/kubectl/cmd/describe_test.go @@ -100,3 +100,43 @@ func TestDescribeListObjects(t *testing.T) { t.Errorf("unexpected output: %s", buf.String()) } } + +func TestDescribeObjectShowEvents(t *testing.T) { + pods, _, _ := testData() + f, tf, codec := NewAPIFactory() + d := &testDescriber{Output: "test output"} + tf.Describer = d + tf.Client = &fake.RESTClient{ + Codec: codec, + Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, + } + + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + cmd := NewCmdDescribe(f, buf) + cmd.Flags().Set("show-events", "true") + cmd.Run(cmd, []string{"pods"}) + if d.Settings.ShowEvents != true { + t.Errorf("ShowEvents = true expected, got ShowEvents = %v", d.Settings.ShowEvents) + } +} + +func TestDescribeObjectSkipEvents(t *testing.T) { + pods, _, _ := testData() + f, tf, codec := NewAPIFactory() + d := &testDescriber{Output: "test output"} + tf.Describer = d + tf.Client = &fake.RESTClient{ + Codec: codec, + Resp: &http.Response{StatusCode: 200, Body: objBody(codec, pods)}, + } + + tf.Namespace = "test" + buf := bytes.NewBuffer([]byte{}) + cmd := NewCmdDescribe(f, buf) + cmd.Flags().Set("show-events", "false") + cmd.Run(cmd, []string{"pods"}) + if d.Settings.ShowEvents != false { + t.Errorf("ShowEvents = false expected, got ShowEvents = %v", d.Settings.ShowEvents) + } +} diff --git a/pkg/kubectl/describe.go b/pkg/kubectl/describe.go index c95fa0c1053..1fdf431272f 100644 --- a/pkg/kubectl/describe.go +++ b/pkg/kubectl/describe.go @@ -54,7 +54,13 @@ import ( // if the output could not be generated. Implementers typically // abstract the retrieval of the named object from a remote server. type Describer interface { - Describe(namespace, name string) (output string, err error) + Describe(namespace, name string, describerSettings DescriberSettings) (output string, err error) +} + +// DescriberSettings holds display configuration for each object +// describer to control what is printed. +type DescriberSettings struct { + ShowEvents bool } // ObjectDescriber is an interface for displaying arbitrary objects with extra @@ -151,7 +157,7 @@ type NamespaceDescriber struct { client.Interface } -func (d *NamespaceDescriber) Describe(namespace, name string) (string, error) { +func (d *NamespaceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { ns, err := d.Namespaces().Get(name) if err != nil { return "", err @@ -307,7 +313,7 @@ type LimitRangeDescriber struct { client.Interface } -func (d *LimitRangeDescriber) Describe(namespace, name string) (string, error) { +func (d *LimitRangeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { lr := d.LimitRanges(namespace) limitRange, err := lr.Get(name) @@ -394,7 +400,7 @@ type ResourceQuotaDescriber struct { client.Interface } -func (d *ResourceQuotaDescriber) Describe(namespace, name string) (string, error) { +func (d *ResourceQuotaDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { rq := d.ResourceQuotas(namespace) resourceQuota, err := rq.Get(name) @@ -463,29 +469,33 @@ type PodDescriber struct { client.Interface } -func (d *PodDescriber) Describe(namespace, name string) (string, error) { +func (d *PodDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { pod, err := d.Pods(namespace).Get(name) if err != nil { - eventsInterface := d.Events(namespace) - selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil) - options := api.ListOptions{FieldSelector: selector} - events, err2 := eventsInterface.List(options) - if err2 == nil && len(events.Items) > 0 { - return tabbedString(func(out io.Writer) error { - fmt.Fprintf(out, "Pod '%v': error '%v', but found events.\n", name, err) - DescribeEvents(events, out) - return nil - }) + if describerSettings.ShowEvents { + eventsInterface := d.Events(namespace) + selector := eventsInterface.GetFieldSelector(&name, &namespace, nil, nil) + options := api.ListOptions{FieldSelector: selector} + events, err2 := eventsInterface.List(options) + if describerSettings.ShowEvents && err2 == nil && len(events.Items) > 0 { + return tabbedString(func(out io.Writer) error { + fmt.Fprintf(out, "Pod '%v': error '%v', but found events.\n", name, err) + DescribeEvents(events, out) + return nil + }) + } } return "", err } var events *api.EventList - if ref, err := api.GetReference(pod); err != nil { - glog.Errorf("Unable to construct reference to '%#v': %v", pod, err) - } else { - ref.Kind = "" - events, _ = d.Events(namespace).Search(ref) + if describerSettings.ShowEvents { + if ref, err := api.GetReference(pod); err != nil { + glog.Errorf("Unable to construct reference to '%#v': %v", pod, err) + } else { + ref.Kind = "" + events, _ = d.Events(namespace).Search(ref) + } } return describePod(pod, events) @@ -692,7 +702,7 @@ type PersistentVolumeDescriber struct { client.Interface } -func (d *PersistentVolumeDescriber) Describe(namespace, name string) (string, error) { +func (d *PersistentVolumeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.PersistentVolumes() pv, err := c.Get(name) @@ -742,7 +752,7 @@ type PersistentVolumeClaimDescriber struct { client.Interface } -func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string) (string, error) { +func (d *PersistentVolumeClaimDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.PersistentVolumeClaims(namespace) pvc, err := c.Get(name) @@ -971,7 +981,7 @@ type ReplicationControllerDescriber struct { client.Interface } -func (d *ReplicationControllerDescriber) Describe(namespace, name string) (string, error) { +func (d *ReplicationControllerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { rc := d.ReplicationControllers(namespace) pc := d.Pods(namespace) @@ -985,7 +995,10 @@ func (d *ReplicationControllerDescriber) Describe(namespace, name string) (strin return "", err } - events, _ := d.Events(namespace).Search(controller) + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.Events(namespace).Search(controller) + } return describeReplicationController(controller, events, running, waiting, succeeded, failed) } @@ -1034,7 +1047,7 @@ type ReplicaSetDescriber struct { client.Interface } -func (d *ReplicaSetDescriber) Describe(namespace, name string) (string, error) { +func (d *ReplicaSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { rsc := d.Extensions().ReplicaSets(namespace) pc := d.Pods(namespace) @@ -1053,7 +1066,10 @@ func (d *ReplicaSetDescriber) Describe(namespace, name string) (string, error) { return "", err } - events, _ := d.Events(namespace).Search(rs) + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.Events(namespace).Search(rs) + } return describeReplicaSet(rs, events, running, waiting, succeeded, failed) } @@ -1080,13 +1096,16 @@ type JobDescriber struct { client *client.Client } -func (d *JobDescriber) Describe(namespace, name string) (string, error) { +func (d *JobDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { job, err := d.client.Extensions().Jobs(namespace).Get(name) if err != nil { return "", err } - events, _ := d.client.Events(namespace).Search(job) + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.client.Events(namespace).Search(job) + } return describeJob(job, events) } @@ -1125,7 +1144,7 @@ type DaemonSetDescriber struct { client.Interface } -func (d *DaemonSetDescriber) Describe(namespace, name string) (string, error) { +func (d *DaemonSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { dc := d.Extensions().DaemonSets(namespace) pc := d.Pods(namespace) @@ -1143,7 +1162,10 @@ func (d *DaemonSetDescriber) Describe(namespace, name string) (string, error) { return "", err } - events, _ := d.Events(namespace).Search(daemon) + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.Events(namespace).Search(daemon) + } return describeDaemonSet(daemon, events, running, waiting, succeeded, failed) } @@ -1176,7 +1198,7 @@ type SecretDescriber struct { client.Interface } -func (d *SecretDescriber) Describe(namespace, name string) (string, error) { +func (d *SecretDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.Secrets(namespace) secret, err := c.Get(name) @@ -1214,13 +1236,13 @@ type IngressDescriber struct { client.Interface } -func (i *IngressDescriber) Describe(namespace, name string) (string, error) { +func (i *IngressDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := i.Extensions().Ingress(namespace) ing, err := c.Get(name) if err != nil { return "", err } - return i.describeIngress(ing) + return i.describeIngress(ing, describerSettings) } func (i *IngressDescriber) describeBackend(ns string, backend *extensions.IngressBackend) string { @@ -1243,7 +1265,7 @@ func (i *IngressDescriber) describeBackend(ns string, backend *extensions.Ingres return formatEndpoints(endpoints, sets.NewString(spName)) } -func (i *IngressDescriber) describeIngress(ing *extensions.Ingress) (string, error) { +func (i *IngressDescriber) describeIngress(ing *extensions.Ingress, describerSettings DescriberSettings) (string, error) { return tabbedString(func(out io.Writer) error { fmt.Fprintf(out, "Name:\t%v\n", ing.Name) fmt.Fprintf(out, "Namespace:\t%v\n", ing.Namespace) @@ -1276,9 +1298,11 @@ func (i *IngressDescriber) describeIngress(ing *extensions.Ingress) (string, err } describeIngressAnnotations(out, ing.Annotations) - events, _ := i.Events(ing.Namespace).Search(ing) - if events != nil { - DescribeEvents(events, out) + if describerSettings.ShowEvents { + events, _ := i.Events(ing.Namespace).Search(ing) + if events != nil { + DescribeEvents(events, out) + } } return nil }) @@ -1315,7 +1339,7 @@ type ServiceDescriber struct { client.Interface } -func (d *ServiceDescriber) Describe(namespace, name string) (string, error) { +func (d *ServiceDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.Services(namespace) service, err := c.Get(name) @@ -1324,8 +1348,10 @@ func (d *ServiceDescriber) Describe(namespace, name string) (string, error) { } endpoints, _ := d.Endpoints(namespace).Get(name) - events, _ := d.Events(namespace).Search(service) - + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.Events(namespace).Search(service) + } return describeService(service, endpoints, events) } @@ -1386,7 +1412,7 @@ type EndpointsDescriber struct { client.Interface } -func (d *EndpointsDescriber) Describe(namespace, name string) (string, error) { +func (d *EndpointsDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.Endpoints(namespace) ep, err := c.Get(name) @@ -1394,7 +1420,10 @@ func (d *EndpointsDescriber) Describe(namespace, name string) (string, error) { return "", err } - events, _ := d.Events(namespace).Search(ep) + var events *api.EventList + if describerSettings.ShowEvents { + events, _ = d.Events(namespace).Search(ep) + } return describeEndpoints(ep, events) } @@ -1456,7 +1485,7 @@ type ServiceAccountDescriber struct { client.Interface } -func (d *ServiceAccountDescriber) Describe(namespace, name string) (string, error) { +func (d *ServiceAccountDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.ServiceAccounts(namespace) serviceAccount, err := c.Get(name) @@ -1537,7 +1566,7 @@ type NodeDescriber struct { client.Interface } -func (d *NodeDescriber) Describe(namespace, name string) (string, error) { +func (d *NodeDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { mc := d.Nodes() node, err := mc.Get(name) if err != nil { @@ -1560,12 +1589,14 @@ func (d *NodeDescriber) Describe(namespace, name string) (string, error) { } var events *api.EventList - if ref, err := api.GetReference(node); err != nil { - glog.Errorf("Unable to construct reference to '%#v': %v", node, err) - } else { - // TODO: We haven't decided the namespace for Node object yet. - ref.UID = types.UID(ref.Name) - events, _ = d.Events("").Search(ref) + if describerSettings.ShowEvents { + if ref, err := api.GetReference(node); err != nil { + glog.Errorf("Unable to construct reference to '%#v': %v", node, err) + } else { + // TODO: We haven't decided the namespace for Node object yet. + ref.UID = types.UID(ref.Name) + events, _ = d.Events("").Search(ref) + } } return describeNode(node, nodeNonTerminatedPodsList, events, canViewPods) @@ -1636,7 +1667,7 @@ type PetSetDescriber struct { client *client.Client } -func (p *PetSetDescriber) Describe(namespace, name string) (string, error) { +func (p *PetSetDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { ps, err := p.client.Apps().PetSets(namespace).Get(name) if err != nil { return "", err @@ -1664,9 +1695,11 @@ func (p *PetSetDescriber) Describe(namespace, name string) (string, error) { fmt.Fprintf(out, "CreationTimestamp:\t%s\n", ps.CreationTimestamp.Time.Format(time.RFC1123Z)) fmt.Fprintf(out, "Pods Status:\t%d Running / %d Waiting / %d Succeeded / %d Failed\n", running, waiting, succeeded, failed) describeVolumes(ps.Spec.Template.Spec.Volumes, out, "") - events, _ := p.client.Events(namespace).Search(ps) - if events != nil { - DescribeEvents(events, out) + if describerSettings.ShowEvents { + events, _ := p.client.Events(namespace).Search(ps) + if events != nil { + DescribeEvents(events, out) + } } return nil }) @@ -1677,7 +1710,7 @@ type HorizontalPodAutoscalerDescriber struct { client *client.Client } -func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (string, error) { +func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { hpa, err := d.client.Extensions().HorizontalPodAutoscalers(namespace).Get(name) if err != nil { return "", err @@ -1719,9 +1752,11 @@ func (d *HorizontalPodAutoscalerDescriber) Describe(namespace, name string) (str } } - events, _ := d.client.Events(namespace).Search(hpa) - if events != nil { - DescribeEvents(events, out) + if describerSettings.ShowEvents { + events, _ := d.client.Events(namespace).Search(hpa) + if events != nil { + DescribeEvents(events, out) + } } return nil }) @@ -1828,7 +1863,7 @@ type DeploymentDescriber struct { clientset.Interface } -func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) { +func (dd *DeploymentDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { d, err := dd.Extensions().Deployments(namespace).Get(name) if err != nil { return "", err @@ -1862,9 +1897,11 @@ func (dd *DeploymentDescriber) Describe(namespace, name string) (string, error) } fmt.Fprintf(out, "NewReplicaSet:\t%s\n", printReplicaSetsByLabels(newRSs)) } - events, err := dd.Core().Events(namespace).Search(d) - if err == nil && events != nil { - DescribeEvents(events, out) + if describerSettings.ShowEvents { + events, err := dd.Core().Events(namespace).Search(d) + if err == nil && events != nil { + DescribeEvents(events, out) + } } return nil }) @@ -1952,7 +1989,7 @@ type ConfigMapDescriber struct { client.Interface } -func (d *ConfigMapDescriber) Describe(namespace, name string) (string, error) { +func (d *ConfigMapDescriber) Describe(namespace, name string, describerSettings DescriberSettings) (string, error) { c := d.ConfigMaps(namespace) configMap, err := c.Get(name) diff --git a/pkg/kubectl/describe_test.go b/pkg/kubectl/describe_test.go index 6098865b748..f073d3050cd 100644 --- a/pkg/kubectl/describe_test.go +++ b/pkg/kubectl/describe_test.go @@ -49,7 +49,7 @@ func TestDescribePod(t *testing.T) { }) c := &describeClient{T: t, Namespace: "foo", Interface: fake} d := PodDescriber{c} - out, err := d.Describe("foo", "bar") + out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -67,7 +67,7 @@ func TestDescribeService(t *testing.T) { }) c := &describeClient{T: t, Namespace: "foo", Interface: fake} d := ServiceDescriber{c} - out, err := d.Describe("foo", "bar") + out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -110,7 +110,7 @@ func TestPodDescribeResultsSorted(t *testing.T) { d := PodDescriber{c} // Act - out, err := d.Describe("foo", "bar") + out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) // Assert if err != nil { @@ -492,7 +492,7 @@ func TestPersistentVolumeDescriber(t *testing.T) { for name, pv := range tests { fake := testclient.NewSimpleFake(pv) c := PersistentVolumeDescriber{fake} - str, err := c.Describe("foo", "bar") + str, err := c.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("Unexpected error for test %s: %v", name, err) } @@ -513,7 +513,7 @@ func TestDescribeDeployment(t *testing.T) { }, }) d := DeploymentDescriber{fake} - out, err := d.Describe("foo", "bar") + out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) if err != nil { t.Errorf("unexpected error: %v", err) } @@ -521,3 +521,116 @@ func TestDescribeDeployment(t *testing.T) { t.Errorf("unexpected out: %s", out) } } + +func TestDescribeEvents(t *testing.T) { + + events := &api.EventList{ + Items: []api.Event{ + { + Source: api.EventSource{Component: "kubelet"}, + Message: "Item 1", + FirstTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), + LastTimestamp: unversioned.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)), + Count: 1, + Type: api.EventTypeNormal, + }, + }, + } + + m := map[string]Describer{ + "DaemonSetDescriber": &DaemonSetDescriber{ + testclient.NewSimpleFake(&extensions.DaemonSet{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + "DeploymentDescriber": &DeploymentDescriber{ + fake.NewSimpleClientset(&extensions.Deployment{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + "EndpointsDescriber": &EndpointsDescriber{ + testclient.NewSimpleFake(&api.Endpoints{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + // TODO(jchaloup): add tests for: + // - HorizontalPodAutoscalerDescriber + // - IngressDescriber + // - JobDescriber + "NodeDescriber": &NodeDescriber{ + testclient.NewSimpleFake(&api.Node{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + SelfLink: "url/url/url", + }, + }, events), + }, + "PodDescriber": &PodDescriber{ + testclient.NewSimpleFake(&api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + SelfLink: "url/url/url", + }, + }, events), + }, + "ReplicaSetDescriber": &ReplicaSetDescriber{ + testclient.NewSimpleFake(&extensions.ReplicaSet{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + "ReplicationControllerDescriber": &ReplicationControllerDescriber{ + testclient.NewSimpleFake(&api.ReplicationController{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + "Service": &ServiceDescriber{ + testclient.NewSimpleFake(&api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: "bar", + Namespace: "foo", + }, + }, events), + }, + } + + for name, d := range m { + out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true}) + if err != nil { + t.Errorf("unexpected error for %q: %v", name, err) + } + if !strings.Contains(out, "bar") { + t.Errorf("unexpected out for %q: %s", name, out) + } + if !strings.Contains(out, "Events:") { + t.Errorf("events not found for %q when ShowEvents=true: %s", name, out) + } + + out, err = d.Describe("foo", "bar", DescriberSettings{ShowEvents: false}) + if err != nil { + t.Errorf("unexpected error for %q: %s", name, err) + } + if !strings.Contains(out, "bar") { + t.Errorf("unexpected out for %q: %s", name, out) + } + if strings.Contains(out, "Events:") { + t.Errorf("events found for %q when ShowEvents=false: %s", name, out) + } + } +}