Repository: knative/client (go)

Issue:
Add machine readable output (-o flag) to kn source binding 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 binding describe
## Description

<!-- Please add a short summary about what the pull request is going to bring on the table -->

## Changes
Add machine readable output (-o flag) to kn source binding describe
<!-- Please add list of more detailed changes. These changes should be reflected also in the commit messages -->

## Reference

<!-- Please add the corresponding issue number which this pull request is about to fix -->
Fixes #1097 
<!--
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 e410532f..777f9e77 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -22,6 +22,10 @@
 | https://github.com/knative/client/pull/1150[#1150]
 
 | 🎁
+| Add machine readable output (-o flag) to kn source binding describe
+| https://github.com/knative/client/pull/1148[#1148]
+
+| 🐛
 | 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_binding_describe.md b/docs/cmd/kn_source_binding_describe.md
index fe762f14..44345674 100644
--- a/docs/cmd/kn_source_binding_describe.md
+++ b/docs/cmd/kn_source_binding_describe.md
@@ -14,16 +14,22 @@ kn source binding describe NAME
 
 ```
 
-  # Describe a sink binding with name 'mysinkbinding'
+  # Describe a sink binding 'mysinkbinding'
   kn source binding describe mysinkbinding
+
+  # Describe a sink binding 'mysinkbinding' in YAML format
+  kn source binding describe mysinkbinding -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/binding/describe.go b/pkg/kn/commands/source/binding/describe.go
index 66e681a1..d3485807 100644
--- a/pkg/kn/commands/source/binding/describe.go
+++ b/pkg/kn/commands/source/binding/describe.go
@@ -18,8 +18,10 @@ import (
 	"errors"
 	"fmt"
 	"sort"
+	"strings"
 
 	"github.com/spf13/cobra"
+	"k8s.io/cli-runtime/pkg/genericclioptions"
 	v1alpha2 "knative.dev/eventing/pkg/apis/sources/v1alpha2"
 	"knative.dev/pkg/tracker"
 
@@ -28,14 +30,23 @@ import (
 	"knative.dev/client/pkg/printers"
 )
 
+var describeExample = `
+  # Describe a sink binding 'mysinkbinding'
+  kn source binding describe mysinkbinding
+
+  # Describe a sink binding 'mysinkbinding' in YAML format
+  kn source binding describe mysinkbinding -o yaml`
+
 // NewBindingDescribeCommand returns a new command for describe a sink binding object
 func NewBindingDescribeCommand(p *commands.KnParams) *cobra.Command {
-	cmd := &cobra.Command{
-		Use:   "describe NAME",
-		Short: "Show details of a sink binding",
-		Example: `
-  # Describe a sink binding with name 'mysinkbinding'
-  kn source binding describe mysinkbinding`,
+
+	// For machine readable output
+	machineReadablePrintFlags := genericclioptions.NewPrintFlags("")
+
+	command := &cobra.Command{
+		Use:     "describe NAME",
+		Short:   "Show details of a sink binding",
+		Example: describeExample,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) != 1 {
 				return errors.New("'kn source binding describe' requires name of the sink binding as single argument")
@@ -55,6 +66,15 @@ func NewBindingDescribeCommand(p *commands.KnParams) *cobra.Command {
 			out := cmd.OutOrStdout()
 			dw := printers.NewPrefixWriter(out)
 
+			// Print out machine readable output if requested
+			if machineReadablePrintFlags.OutputFlagSpecified() {
+				printer, err := machineReadablePrintFlags.ToPrinter()
+				if err != nil {
+					return err
+				}
+				return printer.PrintObj(binding, out)
+			}
+
 			printDetails, err := cmd.Flags().GetBool("verbose")
 			if err != nil {
 				return err
@@ -75,11 +95,12 @@ func NewBindingDescribeCommand(p *commands.KnParams) *cobra.Command {
 			return nil
 		},
 	}
-	flags := cmd.Flags()
+	flags := command.Flags()
 	commands.AddNamespaceFlags(flags, false)
 	flags.BoolP("verbose", "v", false, "More output.")
-
-	return cmd
+	machineReadablePrintFlags.AddFlags(command)
+	command.Flag("output").Usage = fmt.Sprintf("Output format. One of: %s.", strings.Join(machineReadablePrintFlags.AllowedFormats(), "|"))
+	return command
 }
 
 func writeSinkBinding(dw printers.PrefixWriter, binding *v1alpha2.SinkBinding, printDetails bool) {
diff --git a/pkg/sources/v1alpha2/binding_client.go b/pkg/sources/v1alpha2/binding_client.go
index d0d866c9..e3a4d027 100644
--- a/pkg/sources/v1alpha2/binding_client.go
+++ b/pkg/sources/v1alpha2/binding_client.go
@@ -89,6 +89,10 @@ func (c *knBindingClient) GetSinkBinding(name string) (*v1alpha2.SinkBinding, er
 	if err != nil {
 		return nil, knerrors.GetError(err)
 	}
+	err = updateSinkBindingGvk(binding)
+	if err != nil {
+		return nil, err
+	}
 	return binding, nil
 }
 


Test patch:
diff --git a/pkg/kn/commands/source/binding/describe_test.go b/pkg/kn/commands/source/binding/describe_test.go
index 15576435..1552aab5 100644
--- a/pkg/kn/commands/source/binding/describe_test.go
+++ b/pkg/kn/commands/source/binding/describe_test.go
@@ -93,9 +93,24 @@ func TestDescribeWithSinkURI(t *testing.T) {
 	bindingRecorder.Validate()
 }
 
+func TestSinkBindingMachineReadableOutputs(t *testing.T) {
+	bindingClient := clientv1alpha2.NewMockKnSinkBindingClient(t, "mynamespace")
+	bindingRecorder := bindingClient.Recorder()
+
+	bindingRecorder.GetSinkBinding("mybinding", getSinkBindingSource("myapp", map[string]string{"foo": "bar"}, createServiceSink("mysvc", "myservicenamespace")), nil)
+	out, err := executeSinkBindingCommand(bindingClient, nil, "describe", "mybinding", "-o", "yaml")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(out, "kind: SinkBinding", "spec:", "status:", "metadata:"))
+
+	bindingRecorder.Validate()
+}
+
 func getSinkBindingSource(nameOrSelector string, ceOverrides map[string]string, sink duckv1.Destination) *v1alpha2.SinkBinding {
 	binding := &v1alpha2.SinkBinding{
-		TypeMeta: v1.TypeMeta{},
+		TypeMeta: v1.TypeMeta{
+			Kind:       "SinkBinding",
+			APIVersion: "sources.knative.dev/v1alpha2",
+		},
 		ObjectMeta: v1.ObjectMeta{
 			Name: "mysinkbinding",
 		},