Repository: knative/client
Pull request #1497 (2021-10-29 20:48:25)

Reword missing API error to mention client update
## Description

Add a mention of client update to common missing API error message. In case of older client with newer backend with removed API, it might confusing why there's a failure.

## Changes

<!-- Please add list of more detailed changes. These changes should be reflected also in the commit messages -->

* Reword missing API error to mention client update

## Reference

<!-- Please add the corresponding issue number which this pull request is about to fix -->
Fixes #

<!--
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
-->


knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/1497#" title="Author self-approved">dsimansk</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fclient).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/client/blob/main/OWNERS)~~ [dsimansk]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) Report
> Merging [#1497](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) (ac749aa) into [main](https://codecov.io/gh/knative/client/commit/6c2f202d56153838af94d90d4014de133ae13039?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) (6c2f202) will **increase** coverage by `0.11%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/knative/client/pull/1497/graphs/tree.svg?width=650&height=150&src=pr&token=bkTp6BzJiV&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative)](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative)

```diff
@@            Coverage Diff             @@
##             main    #1497      +/-   ##
==========================================
+ Coverage   79.21%   79.33%   +0.11%     
==========================================
  Files         162      162              
  Lines        8457     8506      +49     
==========================================
+ Hits         6699     6748      +49     
  Misses       1076     1076              
  Partials      682      682              
```


| [Impacted Files](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) | Coverage Δ | |
|---|---|---|
| [pkg/errors/errors.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2Vycm9ycy9lcnJvcnMuZ28=) | `100.00% <100.00%> (ø)` | |
| [pkg/errors/factory.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2Vycm9ycy9mYWN0b3J5Lmdv) | `96.87% <100.00%> (ø)` | |
| [pkg/kn/commands/channel/list\_types.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2tuL2NvbW1hbmRzL2NoYW5uZWwvbGlzdF90eXBlcy5nbw==) | `64.70% <100.00%> (ø)` | |
| [pkg/kn/commands/source/list\_types.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2tuL2NvbW1hbmRzL3NvdXJjZS9saXN0X3R5cGVzLmdv) | `56.86% <100.00%> (ø)` | |
| [pkg/kn/traffic/compute.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2tuL3RyYWZmaWMvY29tcHV0ZS5nbw==) | `100.00% <0.00%> (ø)` | |
| [pkg/kn/commands/service/update.go](https://codecov.io/gh/knative/client/pull/1497/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-cGtnL2tuL2NvbW1hbmRzL3NlcnZpY2UvdXBkYXRlLmdv) | `81.42% <0.00%> (+0.83%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative). Last update [6c2f202...ac749aa](https://codecov.io/gh/knative/client/pull/1497?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative).

knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/1497#" title="Author self-approved">dsimansk</a>*, *<a href="https://github.com/knative/client/pull/1497#pullrequestreview-794260184" title="LGTM">rhuss</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=knative%2Fclient).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/knative/client/blob/main/OWNERS)~~ [dsimansk,rhuss]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->

Patch:
diff --git a/CHANGELOG.adoc b/CHANGELOG.adoc
index 3ca11e2f..3ea22c68 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -18,12 +18,17 @@
 | | Description | PR
 
 | ✨
-| Deprecate `--autoscale-window` in favor of `--scale-window`
-| https://github.com/knative/client/pull/1454[#1454]
+| Add more explanation to missing API error message
+| https://github.com/knative/client/pull/1497[#1497]
 
 | ✨
 | Deprecate `--concurrency-target` and `--concurrency-utilization` in favor of `--scale-target` and `--scale-utilization`, respectively
-| https://github.com/knative/client/pull/1454[#1454]
+| https://github.com/knative/client/pull/1490[#1490]
+
+| ✨
+| Deprecate `--autoscale-window` in favor of `--scale-window`
+| https://github.com/knative/client/pull/1489[#1489]
+
 |===
 
 ## v0.26.0 (2021-09-22)
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
index e58f3b4c..fba16e25 100644
--- a/pkg/errors/errors.go
+++ b/pkg/errors/errors.go
@@ -17,12 +17,15 @@ package errors
 import (
 	"fmt"
 	"strings"
+	"unicode"
+	"unicode/utf8"
 )
 
-func newInvalidCRD(apiGroup string) *KNError {
+func NewInvalidCRD(apiGroup string) *KNError {
 	parts := strings.Split(apiGroup, ".")
 	name := parts[0]
-	msg := fmt.Sprintf("no Knative %s API found on the backend, please verify the installation", name)
+	msg := fmt.Sprintf("no or newer Knative %s API found on the backend, please verify the installation or "+
+		"update the 'kn' client", firstCharToUpper(name))
 	return NewKNError(msg)
 }
 
@@ -37,3 +40,11 @@ func newNoRouteToHost(errString string) *KNError {
 func newNoKubeConfig(errString string) *KNError {
 	return NewKNError("no kubeconfig has been provided, please use a valid configuration to connect to the cluster")
 }
+
+func firstCharToUpper(s string) string {
+	if len(s) == 0 {
+		return s
+	}
+	r, n := utf8.DecodeRuneInString(s)
+	return string(unicode.ToUpper(r)) + s[n:]
+}
diff --git a/pkg/errors/factory.go b/pkg/errors/factory.go
index 0c8428f8..279e1912 100644
--- a/pkg/errors/factory.go
+++ b/pkg/errors/factory.go
@@ -54,7 +54,7 @@ func newStatusError(err error) error {
 	}
 	var knerr *KNError
 	if isCRDError(errAPIStatus) {
-		knerr = newInvalidCRD(errAPIStatus.Status().Details.Group)
+		knerr = NewInvalidCRD(errAPIStatus.Status().Details.Group)
 		knerr.Status = errAPIStatus
 		return knerr
 	}
diff --git a/pkg/kn/commands/channel/list_types.go b/pkg/kn/commands/channel/list_types.go
index aa75f9ff..94450422 100644
--- a/pkg/kn/commands/channel/list_types.go
+++ b/pkg/kn/commands/channel/list_types.go
@@ -17,7 +17,6 @@ package channel
 
 import (
 	"context"
-	"fmt"
 
 	"github.com/spf13/cobra"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -66,7 +65,7 @@ func NewChannelListTypesCommand(p *commands.KnParams) *cobra.Command {
 				channelListTypes = &unstructured.UnstructuredList{}
 			}
 			if !listTypesFlags.GenericPrintFlags.OutputFlagSpecified() && len(channelListTypes.Items) == 0 {
-				return fmt.Errorf("no channels found on the backend, please verify the installation")
+				return knerrors.NewInvalidCRD("Channels")
 			}
 
 			if channelListTypes.GroupVersionKind().Empty() {
diff --git a/pkg/kn/commands/source/list_types.go b/pkg/kn/commands/source/list_types.go
index 172fef12..f0fbb110 100644
--- a/pkg/kn/commands/source/list_types.go
+++ b/pkg/kn/commands/source/list_types.go
@@ -16,7 +16,6 @@ package source
 
 import (
 	"context"
-	"fmt"
 
 	"knative.dev/client/pkg/sources"
 
@@ -68,7 +67,7 @@ func NewListTypesCommand(p *commands.KnParams) *cobra.Command {
 			}
 
 			if !listTypesFlags.GenericPrintFlags.OutputFlagSpecified() && len(sourceListTypes.Items) == 0 {
-				return fmt.Errorf("no sources found on the backend, please verify the installation")
+				return knerrors.NewInvalidCRD("Sources")
 			}
 
 			if sourceListTypes.GroupVersionKind().Empty() {


Test patch:
diff --git a/pkg/errors/errors_test.go b/pkg/errors/errors_test.go
index 6ed50073..ba94738f 100644
--- a/pkg/errors/errors_test.go
+++ b/pkg/errors/errors_test.go
@@ -17,17 +17,18 @@ package errors
 import (
 	"testing"
 
+	"knative.dev/client/pkg/util"
+
 	"gotest.tools/v3/assert"
 )
 
 func TestNewInvalidCRD(t *testing.T) {
-	err := newInvalidCRD("serving.knative.dev")
-	assert.Error(t, err, "no Knative serving API found on the backend, please verify the installation")
-
-	err = newInvalidCRD("eventing")
-	assert.Error(t, err, "no Knative eventing API found on the backend, please verify the installation")
+	err := NewInvalidCRD("serving.knative.dev")
+	assert.Assert(t, util.ContainsAll(err.Error(), "no or newer Knative Serving API found on the backend", "please verify the installation", "update", "'kn'"))
 
-	err = newInvalidCRD("")
-	assert.Error(t, err, "no Knative  API found on the backend, please verify the installation")
+	err = NewInvalidCRD("eventing")
+	assert.Assert(t, util.ContainsAll(err.Error(), "no or newer Knative Eventing API found on the backend", "please verify the installation", "update", "'kn'"))
 
+	err = NewInvalidCRD("")
+	assert.Assert(t, util.ContainsAll(err.Error(), "no or newer Knative  API found on the backend", "please verify the installation", "update", "'kn'"))
 }
diff --git a/pkg/errors/factory_test.go b/pkg/errors/factory_test.go
index 93a5e925..1a5e6956 100644
--- a/pkg/errors/factory_test.go
+++ b/pkg/errors/factory_test.go
@@ -61,7 +61,7 @@ func TestKnErrorsStatusErrors(t *testing.T) {
 				}
 				return statusError
 			},
-			ExpectedMsg: "no Knative serving API found on the backend, please verify the installation",
+			ExpectedMsg: "no or newer Knative Serving API found on the backend, please verify the installation or update the 'kn' client",
 			Validate: func(t *testing.T, err error, msg string) {
 				assert.Error(t, err, msg)
 			},
diff --git a/pkg/kn/commands/channel/list_types_test.go b/pkg/kn/commands/channel/list_types_test.go
index d3d73f31..49d4e055 100644
--- a/pkg/kn/commands/channel/list_types_test.go
+++ b/pkg/kn/commands/channel/list_types_test.go
@@ -68,7 +68,7 @@ func TestChannelListTypesNoChannelInstalled(t *testing.T) {
 
 	_, err := channelFakeCmd([]string{"channel", "list-types"}, dynamicClient)
 	assert.Check(t, err != nil)
-	assert.Check(t, util.ContainsAll(err.Error(), "no channels found on the backend, please verify the installation"))
+	assert.Check(t, util.ContainsAll(err.Error(), "no", "Knative Channels", "found", "backend", "verify", "installation"))
 }
 
 func TestChannelListTypesNoChannelWithJsonOutput(t *testing.T) {
@@ -86,7 +86,7 @@ func TestChannelListTypesErrorDynamicClient(t *testing.T) {
 
 	_, err := channelFakeCmd([]string{"channel", "list-types"}, dynamicClient)
 	assert.Check(t, err != nil)
-	assert.Check(t, util.ContainsAll(err.Error(), "no channels found on the backend, please verify the installation"))
+	assert.Check(t, util.ContainsAll(err.Error(), "no", "Knative Channels", "found", "backend", "verify", "installation"))
 }
 
 func TestChannelListTypes(t *testing.T) {
diff --git a/pkg/kn/commands/source/list_test.go b/pkg/kn/commands/source/list_test.go
index 38744441..9a568f6a 100644
--- a/pkg/kn/commands/source/list_test.go
+++ b/pkg/kn/commands/source/list_test.go
@@ -56,7 +56,7 @@ func sourceFakeCmd(args []string, objects ...runtime.Object) (output []string, e
 func TestSourceListTypesNoSourcesInstalled(t *testing.T) {
 	_, err := sourceFakeCmd([]string{"source", "list-types"})
 	assert.Check(t, err != nil)
-	assert.Check(t, util.ContainsAll(err.Error(), "no sources", "found", "backend", "verify", "installation"))
+	assert.Check(t, util.ContainsAll(err.Error(), "no", "Knative Sources", "found", "backend", "verify", "installation"))
 }
 
 func TestSourceListTypesNoSourcesWithJsonOutput(t *testing.T) {