Repository: knative/client (go)

Issue:
Add "-o url" to "kn service describe" and "kn revision describe"
<!-- If you need to report a security issue with Knative, send an email to knative-security@googlegroups.com. -->

### Feature request

Having a dedicated command to only show the  URL is very helpful in a scripted context so that one does not have to know the specifics of the Service schema to extract the URL via a jsonpath.

<!-- Please describe the feature request and why you would like to have it -->

### Use case

Retrieve the URL to process it further in a script.

<!-- Please add a concrete use case to demonstrate how such a feature would add value for the user. If you don't have a use case for your feature, please remove this section (however providing a good use case increases the likelihood to be picked up) -->

### UI Example

<!-- If this is about a new command or command line options, please let us know how you would add it to the "kn" UI (in the code block below). If this is not applicable for your feature, delete this section. -->

```
url=$(kn service describe myservice -o url)
```

<!--
Optional Classifications, remove the '> ' to select:
> /kind good-first-issue
> /kind proposal
> /kind doc
> /kind cleanup
-->


Pull request:
Add support for service url in svc describe output
## 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 -->

* Added Support for service url display in `kn service describe` as output format

## Reference

<!-- Please add the corresponding issue number which this pull request is about to fix -->
Fixes #907  

<!--
Please add an entrty 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 d3b9d70b..491ffc7e 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -18,6 +18,10 @@
 |===
 | | Description | PR
 
+| 🎁
+| Add "url" output format to return service url in service describe
+| https://github.com/knative/client/pull/916[#916]
+
 | 🐛
 | Fix panic for `kn source apiserver` and `kn source binding` describe with Sink URI
 | https://github.com/knative/client/pull/901[#901]
diff --git a/docs/cmd/kn_service_describe.md b/docs/cmd/kn_service_describe.md
index 4f9d274a..41f81ecc 100644
--- a/docs/cmd/kn_service_describe.md
+++ b/docs/cmd/kn_service_describe.md
@@ -10,13 +10,27 @@ Show details of a service
 kn service describe NAME
 ```
 
+### Examples
+
+```
+
+  # Describe service 'svc' in human friendly format
+  kn service describe svc
+
+  # Describe service 'svc' in YAML format
+  kn service describe svc -o yaml
+
+  # Print only service URL
+  kn service describe svc -o url
+```
+
 ### Options
 
 ```
       --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.
+  -o, --output string                 Output format. One of: json|yaml|name|go-template|go-template-file|template|templatefile|jsonpath|jsonpath-file|url.
       --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.
 ```
diff --git a/pkg/kn/commands/service/describe.go b/pkg/kn/commands/service/describe.go
index f8191520..ea62cf36 100644
--- a/pkg/kn/commands/service/describe.go
+++ b/pkg/kn/commands/service/describe.go
@@ -20,6 +20,7 @@ import (
 	"io"
 	"sort"
 	"strconv"
+	"strings"
 
 	"k8s.io/apimachinery/pkg/util/sets"
 	"k8s.io/cli-runtime/pkg/genericclioptions"
@@ -70,6 +71,16 @@ type revisionDesc struct {
 // As this command does not do any writes/updates, it's just a matter of fallbacks.
 // [/REMOVE COMMENT WHEN MOVING TO 0.7.0]
 
+var describe_example = `
+  # Describe service 'svc' in human friendly format
+  kn service describe svc
+
+  # Describe service 'svc' in YAML format
+  kn service describe svc -o yaml
+
+  # Print only service URL
+  kn service describe svc -o url`
+
 // NewServiceDescribeCommand returns a new command for describing a service.
 func NewServiceDescribeCommand(p *commands.KnParams) *cobra.Command {
 
@@ -77,8 +88,9 @@ func NewServiceDescribeCommand(p *commands.KnParams) *cobra.Command {
 	machineReadablePrintFlags := genericclioptions.NewPrintFlags("")
 
 	command := &cobra.Command{
-		Use:   "describe NAME",
-		Short: "Show details of a service",
+		Use:     "describe NAME",
+		Short:   "Show details of a service",
+		Example: describe_example,
 		RunE: func(cmd *cobra.Command, args []string) error {
 			if len(args) != 1 {
 				return errors.New("'service describe' requires the service name given as single argument")
@@ -102,11 +114,16 @@ func NewServiceDescribeCommand(p *commands.KnParams) *cobra.Command {
 
 			// Print out machine readable output if requested
 			if machineReadablePrintFlags.OutputFlagSpecified() {
+				out := cmd.OutOrStdout()
+				if strings.ToLower(*machineReadablePrintFlags.OutputFormat) == "url" {
+					fmt.Fprintf(out, "%s\n", extractURL(service))
+					return nil
+				}
 				printer, err := machineReadablePrintFlags.ToPrinter()
 				if err != nil {
 					return err
 				}
-				return printer.PrintObj(service, cmd.OutOrStdout())
+				return printer.PrintObj(service, out)
 			}
 
 			printDetails, err = cmd.Flags().GetBool("verbose")
@@ -126,6 +143,7 @@ func NewServiceDescribeCommand(p *commands.KnParams) *cobra.Command {
 	commands.AddNamespaceFlags(flags, false)
 	flags.BoolP("verbose", "v", false, "More output.")
 	machineReadablePrintFlags.AddFlags(command)
+	command.Flag("output").Usage = fmt.Sprintf("Output format. One of: %s.", strings.Join(append(machineReadablePrintFlags.AllowedFormats(), "url"), "|"))
 	return command
 }
 


Test patch:
diff --git a/lib/test/broker.go b/lib/test/broker.go
index 99dcb953..3784f176 100644
--- a/lib/test/broker.go
+++ b/lib/test/broker.go
@@ -19,14 +19,16 @@ import (
 	"time"
 
 	"k8s.io/apimachinery/pkg/util/wait"
+	"knative.dev/eventing/pkg/apis/eventing/v1beta1"
 )
 
 // LabelNamespaceForDefaultBroker adds label 'knative-eventing-injection=enabled' to the configured namespace
 func LabelNamespaceForDefaultBroker(r *KnRunResultCollector) error {
-	_, err := Kubectl{}.Run("label", "namespace", r.KnTest().Kn().Namespace(), "knative-eventing-injection=enabled")
+	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.InjectionAnnotation + "=enabled"}
+	_, err := Kubectl{}.Run(cmd...)
 
 	if err != nil {
-		r.T().Fatalf("Error executing 'kubectl label namespace %s knative-eventing-injection=enabled'. Error: %s", r.KnTest().Kn().Namespace(), err.Error())
+		r.T().Fatalf("error executing '%s': %s", strings.Join(cmd, " "), err.Error())
 	}
 
 	return wait.PollImmediate(10*time.Second, 5*time.Minute, func() (bool, error) {
@@ -41,8 +43,9 @@ func LabelNamespaceForDefaultBroker(r *KnRunResultCollector) error {
 
 // UnlabelNamespaceForDefaultBroker removes label 'knative-eventing-injection=enabled' from the configured namespace
 func UnlabelNamespaceForDefaultBroker(r *KnRunResultCollector) {
-	_, err := Kubectl{}.Run("label", "namespace", r.KnTest().Kn().Namespace(), "knative-eventing-injection-")
+	cmd := []string{"label", "namespace", r.KnTest().Kn().Namespace(), v1beta1.InjectionAnnotation + "-"}
+	_, err := Kubectl{}.Run(cmd...)
 	if err != nil {
-		r.T().Fatalf("Error executing 'kubectl label namespace %s knative-eventing-injection-'. Error: %s", r.KnTest().Kn().Namespace(), err.Error())
+		r.T().Fatalf("error executing '%s': %s", strings.Join(cmd, " "), err.Error())
 	}
 }
diff --git a/pkg/kn/commands/service/describe_test.go b/pkg/kn/commands/service/describe_test.go
index e86d36bd..381365ab 100644
--- a/pkg/kn/commands/service/describe_test.go
+++ b/pkg/kn/commands/service/describe_test.go
@@ -506,6 +506,23 @@ func TestServiceDescribeMachineReadable(t *testing.T) {
 	r.Validate()
 }
 
+func TestServiceDescribeURL(t *testing.T) {
+	client := knclient.NewMockKnServiceClient(t)
+
+	// Recording:
+	r := client.Recorder()
+
+	// Prepare service
+	expectedService := createTestService("foo", []string{"rev1", "rev2"}, goodConditions())
+	r.GetService("foo", &expectedService, nil)
+
+	output, err := executeServiceCommand(client, "describe", "foo", "-o", "url")
+	assert.NilError(t, err)
+	assert.Assert(t, util.ContainsAll(output, "foo.default.example.com"))
+
+	r.Validate()
+}
+
 func validateServiceOutput(t *testing.T, service string, output string) {
 	assert.Assert(t, cmp.Regexp("Name:\\s+"+service, output))
 	assert.Assert(t, cmp.Regexp("Namespace:\\s+default", output))
diff --git a/test/common.sh b/test/common.sh
index b0c8f4d3..02ba4e77 100644
--- a/test/common.sh
+++ b/test/common.sh
@@ -36,6 +36,11 @@ function knative_setup() {
 
   if [ "${eventing_version}" = "latest" ]; then
     start_latest_knative_eventing
+
+    # install the sugar controller
+    kubectl apply --filename https://storage.googleapis.com/knative-nightly/eventing/latest/eventing-sugar-controller.yaml
+    wait_until_pods_running knative-eventing || return 1
+
   else
     start_release_knative_eventing "${eventing_version}"
   fi