Repository: knative/client
Pull request #886 (2020-06-16 08:09:14)

v0.15.2 release backports
## Description
client v0.15.2 release backports with a bug fix and pinning go 1.14 in go.mod (inline with serving v0.15.x release).

## Changes
* Backport from master:
  -  #866 
  -  #880 
  -  #883
* CHANGELOG update




googlebot: All (the pull request submitter and all commit authors) CLAs are signed, **but** one or more commits were authored or co-authored by someone other than the pull request submitter.

We need to confirm that all authors are ok with their commits being contributed to this project.  Please have them confirm that by leaving a comment that contains only `@googlebot I consent.` in this pull request.

*Note to project maintainer:* There may be cases where the author cannot leave a comment, or the comment is not properly detected as consent.  In those cases, you can manually confirm consent of the commit author(s), and set the `cla` label to `yes` (if enabled on your project).

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fknative%2Fclient%2Fpull%2F886) for more info**.

<!-- need_author_consent -->
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/886#" title="Author self-approved">navidshaikh</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/release-0.15/OWNERS)~~ [navidshaikh]

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":[]} -->
knative-prow-robot: @navidshaikh: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272803491194081280) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: @navidshaikh: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272803491194081280) | `/test pull-knative-client-integration-tests`
pull-knative-client-integration-tests-latest-release | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests-latest-release/1272803491189886977) | `/test pull-knative-client-integration-tests-latest-release`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: @navidshaikh: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272803491194081280) | `/test pull-knative-client-integration-tests`
pull-knative-client-integration-tests-latest-release | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests-latest-release/1272803491189886977) | `/test pull-knative-client-integration-tests-latest-release`
pull-knative-client-unit-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-unit-tests/1272803491194081281) | `/test pull-knative-client-unit-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: @navidshaikh: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272803491194081280) | `/test pull-knative-client-integration-tests`
pull-knative-client-integration-tests-latest-release | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests-latest-release/1272803491189886977) | `/test pull-knative-client-integration-tests-latest-release`
pull-knative-client-unit-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-unit-tests/1272803491194081281) | `/test pull-knative-client-unit-tests`
pull-knative-client-go-coverage | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-go-coverage/1272803491194081282) | `/test pull-knative-client-go-coverage`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
knative-prow-robot: @navidshaikh: The following tests **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272803491194081280) | `/test pull-knative-client-integration-tests`
pull-knative-client-integration-tests-latest-release | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests-latest-release/1272803491189886977) | `/test pull-knative-client-integration-tests-latest-release`
pull-knative-client-unit-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-unit-tests/1272803491194081281) | `/test pull-knative-client-unit-tests`
pull-knative-client-go-coverage | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-go-coverage/1272803491194081282) | `/test pull-knative-client-go-coverage`
pull-knative-client-build-tests | 191a797da04924909d4c495b4436df1f982d0f9d | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-build-tests/1272803491189886976) | `/test pull-knative-client-build-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
navidshaikh: @danielhelfand @dprotaso : Can you please add consent for CLA check ? 
knative-prow-robot: @navidshaikh: The following test **failed**, say `/retest` to rerun all failed tests:

Test name | Commit | Details | Rerun command
--- | --- | --- | ---
pull-knative-client-integration-tests | ff2d17b9d9bc2573c062096c054f2a3d0ec2fb38 | [link](https://prow.knative.dev/view/gcs/knative-prow/pr-logs/pull/knative_client/886/pull-knative-client-integration-tests/1272811041109053442) | `/test pull-knative-client-integration-tests`

[Full PR test history](https://gubernator.knative.dev/pr/knative_client/886). [Your PR dashboard](https://gubernator.knative.dev/pr/navidshaikh).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
navidshaikh: @googlebot I consent. 

/retest
googlebot: CLAs look good, thanks!

ℹ️ **Googlers: [Go here](https://goto.google.com/prinfo/https%3A%2F%2Fgithub.com%2Fknative%2Fclient%2Fpull%2F886) for more info**.

<!-- ok -->
danielhelfand: > @danielhelfand @dprotaso : Can you please add consent for CLA check ?

@googlebot I consent.
knative-prow-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/client/pull/886#" title="Author self-approved">navidshaikh</a>*, *<a href="https://github.com/knative/client/pull/886#pullrequestreview-431546620" 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/release-0.15/OWNERS)~~ [navidshaikh,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 d726ecd8..191777ca 100644
--- a/CHANGELOG.adoc
+++ b/CHANGELOG.adoc
@@ -12,6 +12,25 @@
 | https://github.com/knative/client/pull/[#]
 ////
 
+## v0.15.2 (2020-06-16)
+
+[cols="1,10,3", options="header", width="100%"]
+|===
+| | Description | PR
+
+| 🐛
+| Fix build.sh for macOS users
+| https://github.com/knative/client/pull/883[#883]
+
+| 🐛
+| Return error message when using --untag with nonexistent tag
+| https://github.com/knative/client/pull/880[#880]
+
+| ✨
+| Update go.mod to specify the module is go1.14
+| https://github.com/knative/client/pull/866[#866]
+|===
+
 ## v0.15.1 (2020-06-03)
 
 [cols="1,10,3", options="header", width="100%"]
diff --git a/go.mod b/go.mod
index c5dbddce..d31736c0 100644
--- a/go.mod
+++ b/go.mod
@@ -32,4 +32,4 @@ replace (
 	k8s.io/code-generator => k8s.io/code-generator v0.16.4
 )
 
-go 1.13
+go 1.14
diff --git a/hack/build.sh b/hack/build.sh
index 214d2aa5..b61e4c9c 100755
--- a/hack/build.sh
+++ b/hack/build.sh
@@ -110,21 +110,27 @@ go_fmt() {
   find $(echo $source_dirs) -name "*.go" -print0 | xargs -0 gofmt -s -w
 }
 
+# Run a go tool, get it first if necessary.
+run_go_tool() {
+  local tool=$2
+  local install_failed=0
+  if [ -z "$(which ${tool})" ]; then
+    local temp_dir="$(mktemp -d)"
+    pushd "${temp_dir}" > /dev/null 2>&1
+    GOFLAGS="" go get "$1" || install_failed=1
+    popd > /dev/null 2>&1
+    rm -rf "${temp_dir}"
+  fi
+  (( install_failed )) && return ${install_failed}
+  shift 2
+  ${tool} "$@"
+}
+
+
 source_format() {
   set +e
-  which goimports >/dev/null 2>&1
-  if [ $? -ne 0 ]; then
-     echo "✋ No 'goimports' found. Please use"
-     echo "✋   go install golang.org/x/tools/cmd/goimports"
-     echo "✋ to enable import cleanup. Import cleanup skipped."
-
-     # Run go fmt instead
-     go_fmt
-  else
-     echo "🧽 ${X}Format"
-     goimports -w $(echo $source_dirs)
-     find $(echo $source_dirs) -name "*.go" -print0 | xargs -0 gofmt -s -w
-  fi
+  run_go_tool golang.org/x/tools/cmd/goimports goimports -w $(echo $source_dirs)
+  find $(echo $source_dirs) -name "*.go" -print0 | xargs -0 gofmt -s -w
   set -e
 }
 
diff --git a/hack/verify-codegen.sh b/hack/verify-codegen.sh
index ee9036a3..511cabc0 100755
--- a/hack/verify-codegen.sh
+++ b/hack/verify-codegen.sh
@@ -20,9 +20,6 @@ set -o pipefail
 
 source $(dirname $0)/../scripts/test-infra/library.sh
 
-# Needed later
-go install golang.org/x/tools/cmd/goimports
-
 "${REPO_ROOT_DIR}"/hack/build.sh --codegen
 if output="$(git status --porcelain)" && [ -z "$output" ]; then
   echo "${REPO_ROOT_DIR} is up to date."
diff --git a/pkg/kn/commands/service/update.go b/pkg/kn/commands/service/update.go
index 3499472b..c8b64410 100644
--- a/pkg/kn/commands/service/update.go
+++ b/pkg/kn/commands/service/update.go
@@ -92,7 +92,7 @@ func NewServiceUpdateCommand(p *commands.KnParams) *cobra.Command {
 				}
 
 				if trafficFlags.Changed(cmd) {
-					traffic, err := traffic.Compute(cmd, service.Spec.Traffic, &trafficFlags)
+					traffic, err := traffic.Compute(cmd, service.Spec.Traffic, &trafficFlags, service.Name)
 					if err != nil {
 						return nil, err
 					}
diff --git a/pkg/kn/traffic/compute.go b/pkg/kn/traffic/compute.go
index ab0d6bb6..ef027bc2 100644
--- a/pkg/kn/traffic/compute.go
+++ b/pkg/kn/traffic/compute.go
@@ -83,13 +83,15 @@ func (e ServiceTraffic) isTagPresent(tag string) bool {
 	return false
 }
 
-func (e ServiceTraffic) untagRevision(tag string) {
+func (e ServiceTraffic) untagRevision(tag string, serviceName string) bool {
 	for i, target := range e {
 		if target.Tag == tag {
 			e[i].Tag = ""
-			break
+			return true
 		}
 	}
+
+	return false
 }
 
 func (e ServiceTraffic) isRevisionPresent(revision string) bool {
@@ -267,7 +269,8 @@ func verifyInputSanity(trafficFlags *flags.Traffic) error {
 }
 
 // Compute takes service traffic targets and updates per given traffic flags
-func Compute(cmd *cobra.Command, targets []servingv1.TrafficTarget, trafficFlags *flags.Traffic) ([]servingv1.TrafficTarget, error) {
+func Compute(cmd *cobra.Command, targets []servingv1.TrafficTarget,
+	trafficFlags *flags.Traffic, serviceName string) ([]servingv1.TrafficTarget, error) {
 	err := verifyInputSanity(trafficFlags)
 	if err != nil {
 		return nil, err
@@ -276,8 +279,17 @@ func Compute(cmd *cobra.Command, targets []servingv1.TrafficTarget, trafficFlags
 	traffic := newServiceTraffic(targets)
 
 	// First precedence: Untag revisions
+	var errTagNames []string
 	for _, tag := range trafficFlags.UntagRevisions {
-		traffic.untagRevision(tag)
+		tagExists := traffic.untagRevision(tag, serviceName)
+		if !tagExists {
+			errTagNames = append(errTagNames, tag)
+		}
+	}
+
+	// Return all errors from untagging revisions
+	if len(errTagNames) > 0 {
+		return nil, fmt.Errorf("tag(s) %s not present for any revisions of service %s", strings.Join(errTagNames, ", "), serviceName)
 	}
 
 	for _, each := range trafficFlags.RevisionsTags {
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 8f7f05e1..9ebeaafe 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -83,6 +83,7 @@ github.com/mailru/easyjson/buffer
 github.com/mailru/easyjson/jlexer
 github.com/mailru/easyjson/jwriter
 # github.com/mitchellh/go-homedir v1.1.0
+## explicit
 github.com/mitchellh/go-homedir
 # github.com/mitchellh/mapstructure v1.1.2
 github.com/mitchellh/mapstructure
@@ -108,13 +109,16 @@ github.com/spf13/afero/mem
 # github.com/spf13/cast v1.3.1
 github.com/spf13/cast
 # github.com/spf13/cobra v0.0.6 => github.com/chmouel/cobra v0.0.0-20191021105835-a78788917390
+## explicit
 github.com/spf13/cobra
 github.com/spf13/cobra/doc
 # github.com/spf13/jwalterweatherman v1.1.0
 github.com/spf13/jwalterweatherman
 # github.com/spf13/pflag v1.0.5
+## explicit
 github.com/spf13/pflag
 # github.com/spf13/viper v1.6.2
+## explicit
 github.com/spf13/viper
 # github.com/subosito/gotenv v1.2.0
 github.com/subosito/gotenv
@@ -130,6 +134,7 @@ go.uber.org/zap/internal/color
 go.uber.org/zap/internal/exit
 go.uber.org/zap/zapcore
 # golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
+## explicit
 golang.org/x/crypto/ssh/terminal
 # golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
 golang.org/x/net/context
@@ -184,12 +189,14 @@ gopkg.in/ini.v1
 # gopkg.in/yaml.v2 v2.3.0
 gopkg.in/yaml.v2
 # gotest.tools v2.2.0+incompatible
+## explicit
 gotest.tools/assert
 gotest.tools/assert/cmp
 gotest.tools/internal/difflib
 gotest.tools/internal/format
 gotest.tools/internal/source
 # k8s.io/api v0.17.4 => k8s.io/api v0.16.4
+## explicit
 k8s.io/api/admissionregistration/v1
 k8s.io/api/admissionregistration/v1beta1
 k8s.io/api/apps/v1
@@ -229,6 +236,7 @@ k8s.io/api/storage/v1
 k8s.io/api/storage/v1alpha1
 k8s.io/api/storage/v1beta1
 # k8s.io/apimachinery v0.17.4 => k8s.io/apimachinery v0.16.4
+## explicit
 k8s.io/apimachinery/pkg/api/apitesting/fuzzer
 k8s.io/apimachinery/pkg/api/equality
 k8s.io/apimachinery/pkg/api/errors
@@ -278,6 +286,7 @@ k8s.io/apimachinery/pkg/watch
 k8s.io/apimachinery/third_party/forked/golang/json
 k8s.io/apimachinery/third_party/forked/golang/reflect
 # k8s.io/cli-runtime v0.17.3 => k8s.io/cli-runtime v0.16.4
+## explicit
 k8s.io/cli-runtime/pkg/genericclioptions
 k8s.io/cli-runtime/pkg/kustomize
 k8s.io/cli-runtime/pkg/kustomize/k8sdeps
@@ -291,6 +300,7 @@ k8s.io/cli-runtime/pkg/kustomize/k8sdeps/validator
 k8s.io/cli-runtime/pkg/printers
 k8s.io/cli-runtime/pkg/resource
 # k8s.io/client-go v11.0.1-0.20190805182717-6502b5e7b1b5+incompatible => k8s.io/client-go v0.16.4
+## explicit
 k8s.io/client-go/discovery
 k8s.io/client-go/discovery/cached/disk
 k8s.io/client-go/dynamic
@@ -462,6 +472,7 @@ k8s.io/utils/buffer
 k8s.io/utils/integer
 k8s.io/utils/trace
 # knative.dev/eventing v0.15.1-0.20200528220601-a61a6784a053
+## explicit
 knative.dev/eventing/pkg/apis/config
 knative.dev/eventing/pkg/apis/configs
 knative.dev/eventing/pkg/apis/configs/v1alpha1
@@ -488,6 +499,7 @@ knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha2
 knative.dev/eventing/pkg/client/clientset/versioned/typed/sources/v1alpha2/fake
 knative.dev/eventing/pkg/logging
 # knative.dev/pkg v0.0.0-20200528142800-1c6815d7e4c9
+## explicit
 knative.dev/pkg/apis
 knative.dev/pkg/apis/duck
 knative.dev/pkg/apis/duck/v1
@@ -504,6 +516,7 @@ knative.dev/pkg/profiling
 knative.dev/pkg/ptr
 knative.dev/pkg/tracker
 # knative.dev/serving v0.15.1-0.20200601175503-4eab87b2ad07
+## explicit
 knative.dev/serving/pkg/apis/autoscaling
 knative.dev/serving/pkg/apis/autoscaling/v1alpha1
 knative.dev/serving/pkg/apis/config
@@ -541,4 +554,12 @@ sigs.k8s.io/kustomize/pkg/transformers/config
 sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig
 sigs.k8s.io/kustomize/pkg/types
 # sigs.k8s.io/yaml v1.2.0
+## explicit
 sigs.k8s.io/yaml
+# github.com/spf13/cobra => github.com/chmouel/cobra v0.0.0-20191021105835-a78788917390
+# k8s.io/api => k8s.io/api v0.16.4
+# k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.16.4
+# k8s.io/apimachinery => k8s.io/apimachinery v0.16.4
+# k8s.io/cli-runtime => k8s.io/cli-runtime v0.16.4
+# k8s.io/client-go => k8s.io/client-go v0.16.4
+# k8s.io/code-generator => k8s.io/code-generator v0.16.4


Test patch:
diff --git a/lib/test/service.go b/lib/test/service.go
index 508e106e..69f8613b 100644
--- a/lib/test/service.go
+++ b/lib/test/service.go
@@ -72,6 +72,15 @@ func ServiceUpdate(r *KnRunResultCollector, serviceName string, args ...string)
 	assert.Check(r.T(), util.ContainsAllIgnoreCase(out.Stdout, "updating", "service", serviceName, "ready"))
 }
 
+// ServiceUpdateWithError verifies service update operation with given arguments in sync mode
+// when expecting an error
+func ServiceUpdateWithError(r *KnRunResultCollector, serviceName string, args ...string) {
+	fullArgs := append([]string{}, "service", "update", serviceName)
+	fullArgs = append(fullArgs, args...)
+	out := r.KnTest().Kn().Run(fullArgs...)
+	r.AssertError(out)
+}
+
 // ServiceDelete verifies service deletion in sync mode
 func ServiceDelete(r *KnRunResultCollector, serviceName string) {
 	out := r.KnTest().Kn().Run("service", "delete", "--wait", serviceName)
diff --git a/pkg/kn/commands/service/update_test.go b/pkg/kn/commands/service/update_test.go
index 0549bbf8..6b7ee027 100644
--- a/pkg/kn/commands/service/update_test.go
+++ b/pkg/kn/commands/service/update_test.go
@@ -737,6 +737,15 @@ func TestServiceUpdateDeletionTimestampNotNil(t *testing.T) {
 	assert.ErrorContains(t, err, "service")
 }
 
+func TestServiceUpdateTagDoesNotExist(t *testing.T) {
+	orig := newEmptyService()
+
+	_, _, _, err := fakeServiceUpdate(orig, []string{
+		"service", "update", "foo", "--untag", "foo", "--no-wait"})
+
+	assert.Assert(t, util.ContainsAll(err.Error(), "tag(s)", "foo", "not present", "service", "foo"))
+}
+
 func newEmptyService() *servingv1.Service {
 	ret := &servingv1.Service{
 		TypeMeta: metav1.TypeMeta{
diff --git a/pkg/kn/traffic/compute_test.go b/pkg/kn/traffic/compute_test.go
index 470e5a59..71262b30 100644
--- a/pkg/kn/traffic/compute_test.go
+++ b/pkg/kn/traffic/compute_test.go
@@ -193,7 +193,7 @@ func TestCompute(t *testing.T) {
 			testCmd, tFlags := newTestTrafficCommand()
 			testCmd.SetArgs(testCase.inputFlags)
 			testCmd.Execute()
-			targets, err := Compute(testCmd, testCase.existingTraffic, tFlags)
+			targets, err := Compute(testCmd, testCase.existingTraffic, tFlags, "serviceName")
 			if err != nil {
 				t.Fatal(err)
 			}
@@ -278,12 +278,24 @@ func TestComputeErrMsg(t *testing.T) {
 			[]string{"--traffic", "echo-v1=40", "--traffic", "echo-v1=60"},
 			"repetition of revision reference echo-v1 is not allowed, use only once with --traffic flag",
 		},
+		{
+			"untag single tag that does not exist",
+			append(newServiceTraffic([]servingv1.TrafficTarget{}), newTarget("latest", "echo-v1", 100, false)),
+			[]string{"--untag", "foo"},
+			"tag(s) foo not present for any revisions of service serviceName",
+		},
+		{
+			"untag multiple tags that do not exist",
+			append(newServiceTraffic([]servingv1.TrafficTarget{}), newTarget("latest", "echo-v1", 100, false)),
+			[]string{"--untag", "foo", "--untag", "bar"},
+			"tag(s) foo, bar not present for any revisions of service serviceName",
+		},
 	} {
 		t.Run(testCase.name, func(t *testing.T) {
 			testCmd, tFlags := newTestTrafficCommand()
 			testCmd.SetArgs(testCase.inputFlags)
 			testCmd.Execute()
-			_, err := Compute(testCmd, testCase.existingTraffic, tFlags)
+			_, err := Compute(testCmd, testCase.existingTraffic, tFlags, "serviceName")
 			assert.Error(t, err, testCase.errMsg)
 		})
 	}
diff --git a/test/e2e/service_test.go b/test/e2e/service_test.go
index de49e645..900c5d3a 100644
--- a/test/e2e/service_test.go
+++ b/test/e2e/service_test.go
@@ -59,6 +59,10 @@ func TestService(t *testing.T) {
 	t.Log("create service private and make public")
 	serviceCreatePrivateUpdatePublic(r, "hello-private-public")
 
+	t.Log("error message from --untag with tag that doesn't exist")
+	test.ServiceCreate(r, "untag")
+	serviceUntagTagThatDoesNotExist(r, "untag")
+
 	t.Log("delete all services in a namespace")
 	test.ServiceCreate(r, "svc1")
 	test.ServiceCreate(r, "service2")
@@ -140,6 +144,15 @@ func serviceMultipleDelete(r *test.KnRunResultCollector, existService, nonexistS
 	assert.Check(r.T(), strings.Contains(out.Stdout, expectedErr), "Failed to get 'not found' error")
 }
 
+func serviceUntagTagThatDoesNotExist(r *test.KnRunResultCollector, serviceName string) {
+	out := r.KnTest().Kn().Run("service", "list", serviceName)
+	r.AssertNoError(out)
+	assert.Check(r.T(), strings.Contains(out.Stdout, serviceName), "Service "+serviceName+" does not exist for test (but should exist)")
+
+	out = r.KnTest().Kn().Run("service", "update", serviceName, "--untag", "foo", "--no-wait")
+	assert.Check(r.T(), util.ContainsAll(out.Stderr, "tag(s)", "foo", "not present", "service", "untag"), "Expected error message for using --untag with nonexistent tag")
+}
+
 func serviceDeleteAll(r *test.KnRunResultCollector) {
 	out := r.KnTest().Kn().Run("service", "list")
 	r.AssertNoError(out)
diff --git a/test/e2e/traffic_split_test.go b/test/e2e/traffic_split_test.go
index 53bcc0ec..ac1ee49b 100644
--- a/test/e2e/traffic_split_test.go
+++ b/test/e2e/traffic_split_test.go
@@ -105,7 +105,7 @@ func TestTrafficSplit(t *testing.T) {
 
 			// make ordered fields per tflags (tag, revision, percent, latest)
 			expectedTargets := []TargetFields{newTargetFields("v1", rev1, 50, false), newTargetFields("v2", rev2, 50, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -127,7 +127,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--traffic", fmt.Sprintf("%s=20,%s=80", rev1, rev2))
 
 			expectedTargets := []TargetFields{newTargetFields("", rev1, 20, false), newTargetFields("", rev2, 80, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -148,7 +148,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--tag", fmt.Sprintf("%s=%s", rev1, "candidate"))
 
 			expectedTargets := []TargetFields{newTargetFields("", rev2, 100, true), newTargetFields("candidate", rev1, 0, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -171,7 +171,7 @@ func TestTrafficSplit(t *testing.T) {
 				"--traffic", "candidate=2%,@latest=98%")
 
 			expectedTargets := []TargetFields{newTargetFields("", rev2, 98, true), newTargetFields("candidate", rev1, 2, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -204,7 +204,7 @@ func TestTrafficSplit(t *testing.T) {
 			// there will be 2 targets in existing block 1. @latest, 2.for revision $rev2
 			// target for rev1 is removed as it had no traffic and we untagged it's tag current
 			expectedTargets := []TargetFields{newTargetFields("", rev3, 100, true), newTargetFields("current", rev2, 0, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -225,7 +225,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--untag", "testing", "--tag", "@latest=staging")
 
 			expectedTargets := []TargetFields{newTargetFields("staging", rev1, 100, true)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -250,7 +250,7 @@ func TestTrafficSplit(t *testing.T) {
 
 			expectedTargets := []TargetFields{newTargetFields("", rev2, 100, true),
 				newTargetFields("staging", rev1, 0, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -277,7 +277,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--untag", "old", "--traffic", "@latest=100")
 
 			expectedTargets := []TargetFields{newTargetFields("", rev2, 100, true)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -300,7 +300,7 @@ func TestTrafficSplit(t *testing.T) {
 				"--traffic", "stable=50%,current=50%")
 
 			expectedTargets := []TargetFields{newTargetFields("stable", rev1, 50, false), newTargetFields("current", rev1, 50, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -324,7 +324,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--traffic", "@latest=100")
 
 			expectedTargets := []TargetFields{newTargetFields("", rev2, 100, true)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -343,7 +343,7 @@ func TestTrafficSplit(t *testing.T) {
 			test.ServiceUpdate(r, serviceName, "--tag", "@latest=current")
 
 			expectedTargets := []TargetFields{newTargetFields("current", rev1, 100, true)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -373,7 +373,7 @@ func TestTrafficSplit(t *testing.T) {
 
 			expectedTargets := []TargetFields{newTargetFields("current", rev2, 0, true),
 				newTargetFields("testing", rev1, 100, false)}
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
@@ -405,17 +405,74 @@ func TestTrafficSplit(t *testing.T) {
 				// In spec of traffic block (not status) either latestReadyRevision:true or revisionName can be given per target
 				newTargetFields("latest", rev2, 0, false)}
 
-			verifyTargets(r, serviceName, expectedTargets)
+			verifyTargets(r, serviceName, expectedTargets, false)
+			test.ServiceDelete(r, serviceName)
+		},
+	)
+	t.Run("UntagNonExistentTag",
+		func(t *testing.T) {
+			t.Log("use --untag on a tag that does not exist")
+			r := test.NewKnRunResultCollector(t, it)
+			defer r.DumpIfFailed()
+
+			serviceName := test.GetNextServiceName(serviceBase)
+			rev1 := fmt.Sprintf("%s-rev-1", serviceName)
+			serviceCreateWithOptions(r, serviceName, "--revision-name", rev1)
+
+			// existing state: a revision exist with latest tag
+			test.ServiceUpdate(r, serviceName, "--tag", fmt.Sprintf("%s=latest", rev1))
+
+			// desired state of revision tags: rev1=latest
+			// attempt to untag a tag that does not exist for the service's revisions
+			test.ServiceUpdateWithError(r, serviceName, "--untag", "foo")
+
+			// state should remain the same as error from --untag will stop service update
+			expectedTargets := []TargetFields{
+				newTargetFields("", rev1, 100, true),
+				newTargetFields("latest", rev1, 0, false),
+			}
+
+			verifyTargets(r, serviceName, expectedTargets, true)
+			test.ServiceDelete(r, serviceName)
+		},
+	)
+	t.Run("UntagNonExistentTagAndValidTag",
+		func(t *testing.T) {
+			t.Log("use --untag on a tag that does not exist in addition to a tag that exists")
+			r := test.NewKnRunResultCollector(t, it)
+			defer r.DumpIfFailed()
+
+			serviceName := test.GetNextServiceName(serviceBase)
+			rev1 := fmt.Sprintf("%s-rev-1", serviceName)
+			serviceCreateWithOptions(r, serviceName, "--revision-name", rev1)
+
+			// existing state: a revision exist with latest tag
+			test.ServiceUpdate(r, serviceName, "--tag", fmt.Sprintf("%s=latest", rev1))
+
+			// desired state of revision tags: rev1=latest
+			// attempt to untag a tag that does not exist for the service's revisions (foo)
+			// and also untag a tag that exists (latest)
+			test.ServiceUpdateWithError(r, serviceName, "--untag", "latest", "--untag", "foo")
+
+			// state should remain the same as error from --untag will stop service update
+			expectedTargets := []TargetFields{
+				newTargetFields("", rev1, 100, true),
+				newTargetFields("latest", rev1, 0, false),
+			}
+
+			verifyTargets(r, serviceName, expectedTargets, true)
 			test.ServiceDelete(r, serviceName)
 		},
 	)
 }
 
-func verifyTargets(r *test.KnRunResultCollector, serviceName string, expectedTargets []TargetFields) {
+func verifyTargets(r *test.KnRunResultCollector, serviceName string, expectedTargets []TargetFields, expectErr bool) {
 	out := test.ServiceDescribeWithJSONPath(r, serviceName, targetsJsonPath)
 	assert.Check(r.T(), out != "")
 	actualTargets, err := splitTargets(out, targetsSeparator, len(expectedTargets))
-	assert.NilError(r.T(), err)
+	if !expectErr {
+		assert.NilError(r.T(), err)
+	}
 	formattedActualTargets := formatActualTargets(r.T(), r.KnTest(), actualTargets)
 	assert.DeepEqual(r.T(), expectedTargets, formattedActualTargets)
 	if r.T().Failed() {