Repository: knative/client (go)

Issue:
Add machine readable output (-o flag) to kn source ping describe
use case: `kn source binding describe NAME -oyaml`

see `kn service describe NAME -oyaml` for ref

/kind good-first-issue

Pull request description:
Add machine readable output (-o flag) to kn source ping describe
Signed-off-by: Arghya Sadhu <arghya88@gmail.com>


## Description

<!-- Please add a short summary about what the pull request is going to bring on the table -->

## Changes

<!-- Please add list of more detailed changes. These changes should be reflected also in the commit messages -->

*Add machine readable output (-o flag) to kn source ping describe

## Reference

<!-- Please add the corresponding issue number which this pull request is about to fix -->
Fixes #1098

<!--
Please add an entry to CHANGELOG.adoc file, too, as part of your Pull Request.

In the following cases, add a short description of PR to the unreleased section in CHANGELOG.adoc:

- 🎁 New feature
- 🐛 Bug fix
- ✨ Feature Update
- 🐣 Refactoring
- 🗑️ Remove feature or internal logic

See other entries in CHANGELOG.adoc as an example for how to add the entry, including a reference to the corresponding issue/PR

PLEASE DON'T ADD THAT LINE HERE IN THE PULL-REQUEST DESCRIPTION BUT DIRECTLY IN CHANGELOG.ADOC AND ADD CHANGELOG.ADOC AS PART OF YOUR PULL-REQUEST.
-->

<!--
To automatically lint go code in this pull request uncomment the line below. You get feedback as comments on your pull request then -->

<!--
/lint
-->


Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index ab911b23..e410532f 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -17,7 +17,11 @@
 |===
 | | Description | PR
 
-| 🐛
+| 🎁
+| Add machine readable output (-o flag) to kn source ping describe
+| https://github.com/knative/client/pull/1150[#1150]
+
+| 🎁
 | Add machine readable output (-o flag) to kn source apiserver describe
 | https://github.com/knative/client/pull/1146[#1146]
 
diff --git a/docs/cmd/kn_source_ping_describe.md b/docs/cmd/kn_source_ping_describe.md
index 068576a7..bf7eb736 100644
--- a/docs/cmd/kn_source_ping_describe.md
+++ b/docs/cmd/kn_source_ping_describe.md
@@ -14,16 +14,22 @@ kn source ping describe NAME
 
 ```
 
-  # Describe a Ping source with name 'myping'
+  # Describe a ping source 'myping'
   kn source ping describe myping
+
+  # Describe a ping source 'myping' in YAML format
+  kn source ping describe myping -o yaml
 ```
 
 ### Options
 
 ```
-  -h, --help               help for describe
-  -n, --namespace string   Specify the namespace to operate in.
-  -v, --verbose            More output.
+      --allow-missing-template-keys   If true, ignore any errors in templates when a field or map key is missing in the template. Only applies to golang and jsonpath output formats. (default true)
+  -h, --help                          help for describe
+  -n, --namespace string              Specify the namespace to operate in.
+  -o, --output string                 Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file.
+      --template string               Template string or path to template file to use when -o=go-template, -o=go-template-file. The template format is golang templates [http://golang.org/pkg/text/template/#pkg-overview].
+  -v, --verbose                       More output.
 ```
 
 ### Options inherited from parent commands
diff --git a/pkg/kn/commands/source/ping/describe.go b/pkg/kn/commands/source/ping/describe.go
index affa3aeb..d5d12d2a 100644
--- a/pkg/kn/commands/source/ping/describe.go
+++ b/pkg/kn/commands/source/ping/describe.go
@@ -16,25 +16,36 @@ package ping
 
 import (
 	"errors"
+	"fmt"
 	"sort"
+	"strings"
 
 	"github.com/spf13/cobra"
-	v1alpha2 "knative.dev/eventing/pkg/apis/sources/v1alpha2"
+	"k8s.io/cli-runtime/pkg/genericclioptions"
+	"knative.dev/eventing/pkg/apis/sources/v1alpha2"
 
 	"knative.dev/client/lib/printing"
 	"knative.dev/client/pkg/kn/commands"
 	"knative.dev/client/pkg/printers"
 )
 
+var describeExample = `
+  # Describe a ping source 'myping'
+  kn source ping describe myping
+
+  # Describe a ping source 'myping' in YAML format
+  kn source ping describe myping -o yaml`
+
 // NewPingDescribeCommand returns a new command for describe a Ping source object
 func NewPingDescribeCommand(p *commands.KnParams) *cobra.Command {
 
-	pingDescribe := &cobra.Command{
-		Use:   "describe NAME",
-		Short: "Show details of a ping source",
-		Example: `
-  # Describe a Ping source with name 'myping'
-  kn source ping describe myping`,
+	// For machine readable output
+	machineReadablePrintFlags := genericclioptions.NewPrintFlags("")
+
+	command := &cobra.Command{
+		Use:     "describe NAME",
+		Short:   "Show details of a ping source",
+		Example: describeExample,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) != 1 {
 				return errors.New("'kn source ping describe' requires name of the source as single argument")
@@ -52,6 +63,15 @@ func NewPingDescribeCommand(p *commands.KnParams) *cobra.Command {
 			}
 
 			out := cmd.OutOrStdout()
+
+			// Print out machine readable output if requested
+			if machineReadablePrintFlags.OutputFlagSpecified() {
+				printer, err := machineReadablePrintFlags.ToPrinter()
+				if err != nil {
+					return err
+				}
+				return printer.PrintObj(pingSource, out)
+			}
 			dw := printers.NewPrefixWriter(out)
 
 			printDetails, err := cmd.Flags().GetBool("verbose")
@@ -89,11 +109,12 @@ func NewPingDescribeCommand(p *commands.KnParams) *cobra.Command {
 			return nil
 		},
 	}
-	flags := pingDescribe.Flags()
+	flags := command.Flags()
 	commands.AddNamespaceFlags(flags, false)
 	flags.BoolP("verbose", "v", false, "More output.")
-
-	return pingDescribe
+	machineReadablePrintFlags.AddFlags(command)
+	command.Flag("output").Usage = fmt.Sprintf("Output format. One of: %s.", strings.Join(machineReadablePrintFlags.AllowedFormats(), "|"))
+	return command
 }
 
 func writePingSource(dw printers.PrefixWriter, source *v1alpha2.PingSource, printDetails bool) {
diff --git a/pkg/sources/v1alpha2/ping_client.go b/pkg/sources/v1alpha2/ping_client.go
index 9eedf7b2..c3fe9adc 100644
--- a/pkg/sources/v1alpha2/ping_client.go
+++ b/pkg/sources/v1alpha2/ping_client.go
@@ -18,6 +18,8 @@ import (
 	"context"
 	"fmt"
 
+	knerrors "knative.dev/client/pkg/errors"
+
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"knative.dev/eventing/pkg/apis/sources/v1alpha2"
 
@@ -87,7 +89,15 @@ func (c *pingSourcesClient) DeletePingSource(name string) error {
 }
 
 func (c *pingSourcesClient) GetPingSource(name string) (*v1alpha2.PingSource, error) {
-	return c.client.Get(context.TODO(), name, metav1.GetOptions{})
+	source, err := c.client.Get(context.TODO(), name, metav1.GetOptions{})
+	if err != nil {
+		return nil, knerrors.GetError(err)
+	}
+	err = updateSourceGVK(source)
+	if err != nil {
+		return nil, err
+	}
+	return source, nil
 }
 
 // ListPingSource returns the available Ping sources


Test patch:
diff --git a/pkg/kn/commands/source/ping/describe_test.go b/pkg/kn/commands/source/ping/describe_test.go
index e226cd05..223ae299 100644
--- a/pkg/kn/commands/source/ping/describe_test.go
+++ b/pkg/kn/commands/source/ping/describe_test.go
@@ -55,6 +55,18 @@ func TestDescribeURI(t *testing.T) {
 	pingRecorder.Validate()
 }
 
+func TestDescribeMachineReadable(t *testing.T) {
+	pingClient := clientv1alpha2.NewMockKnPingSourceClient(t, "mynamespace")
+
+	pingRecorder := pingClient.Recorder()
+	pingRecorder.GetPingSource("testsource-uri", getPingSourceSinkURI(), nil)
+
+	out, err := executePingSourceCommand(pingClient, nil, "describe", "testsource-uri", "-o", "yaml")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(out, "kind: PingSource", "spec:", "status:", "metadata:"))
+	pingRecorder.Validate()
+}
+
 func TestDescribeError(t *testing.T) {
 	pingClient := clientv1alpha2.NewMockKnPingSourceClient(t, "mynamespace")
 
@@ -71,7 +83,10 @@ func TestDescribeError(t *testing.T) {
 
 func getPingSourceSinkURI() *v1alpha2.PingSource {
 	return &v1alpha2.PingSource{
-		TypeMeta: metav1.TypeMeta{},
+		TypeMeta: metav1.TypeMeta{
+			Kind:       "PingSource",
+			APIVersion: "sources.knative.dev/v1",
+		},
 		ObjectMeta: metav1.ObjectMeta{
 			Name:      "testsource-uri",
 			Namespace: "mynamespace",