Repository: knative/pkg
Pull request #2590 (2022-09-12 08:05:41)

Register webhook GVKs for validating callbacks
When a callback is registered for a gvk that isn't part of the handlers that callback is never called since there is no webhook rule associated with that GVK.

Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>

<!-- Thanks for sending a pull request! -->

# Changes

<!-- 
Describe your changes here- ideally you can get that description straight from
your descriptive commit message(s)! 

- :gift: Add new feature
- :bug: Fix bug
- :broom: Update or clean up current behavior
- :wastebasket: Remove feature or internal logic
-->

- Register webhook GVKs for validating callbacks
- Add unit test

<!--
In addition, categorize the changes you're making using the "/kind" Prow command, example:

/kind bug

Supported kinds are: api-change, bug, cleanup, deprecation, removal, documentation, enhancement, performance

-->
/kind bug

<!--
*Automatically closes linked issue when PR is merged.
Usage: `Fixes #<issue number>`, or `Fixes (paste link of issue)`.
_If PR is about `failing-tests or flakes`, please post the related issues/tests in a comment and do not use `Fixes`_*
-->
Fixes #

<!-- Please include the 'why' behind your changes if no issue exists -->

**Release Note**

<!--
:page_facing_up: If this change has user-visible impact, write a release note in the block
below. Include the string "action required" if additional action is required of
users switching to the new release, for example in case of a breaking change.

Write as if you are speaking to users, not other Knative contributors. If this
change has no user-visible impact, no release-note is needed.
-->
```release-note
Register webhook GVKs for validating callbacks
```
<!--

**Docs**

:book: If this change has user-visible impact, link to an issue or PR in
https://github.com/knative/docs.

See here for guidance on getting permanent links to files: https://help.github.com/en/articles/getting-permanent-links-to-files

Please use the following format for linking documentation:
- [knative/docs]: <issue or pr link>
- [Feature Track]: <link>
- [Usage]: <link>
- [Other doc]: <link>

```docs

```
-->

knative-prow[bot]: @pierDipi: The label(s) `kind/<kind>` cannot be applied, because the repository doesn't have them.

<details>

In response to [this](https://github.com/knative/pkg/pull/2590):

>When a callback is registered for a gvk that isn't part of the handlers that callback is never called since there is no webhook rule associated with that GVK.
>
>Signed-off-by: Pierangelo Di Pilato <pierdipi@redhat.com>
>
>
>
># Changes
>
>
>
>-
>-
>-
>
>
>/kind <kind>
>
>
>Fixes #
>
>
>
>**Release Note**
>
>
>```release-note
>
>```
>
>**Docs**
>
>
>```docs
>
>```
>


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.
</details>
knative-prow[bot]: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/knative/pkg/pull/2590#" title="Author self-approved">pierDipi</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign dprotaso for approval by writing `/assign @dprotaso` in a comment. For more information see:[The Kubernetes Code Review Process](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process).

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

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

- **[webhook/OWNERS](https://github.com/knative/pkg/blob/main/webhook/OWNERS)**

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":["dprotaso"]} -->
codecov[bot]: # [Codecov](https://codecov.io/gh/knative/pkg/pull/2590?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) Report
> Merging [#2590](https://codecov.io/gh/knative/pkg/pull/2590?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) (6ad30a7) into [main](https://codecov.io/gh/knative/pkg/commit/93b66e6a8700ca4d50e42cd2d2ae423454adcb12?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) (93b66e6) will **increase** coverage by `0.03%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main    #2590      +/-   ##
==========================================
+ Coverage   81.25%   81.29%   +0.03%     
==========================================
  Files         162      162              
  Lines        9801     9820      +19     
==========================================
+ Hits         7964     7983      +19     
  Misses       1597     1597              
  Partials      240      240              
```


| [Impacted Files](https://codecov.io/gh/knative/pkg/pull/2590?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative) | Coverage Δ | |
|---|---|---|
| [...k/resourcesemantics/validation/reconcile\_config.go](https://codecov.io/gh/knative/pkg/pull/2590/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative#diff-d2ViaG9vay9yZXNvdXJjZXNlbWFudGljcy92YWxpZGF0aW9uL3JlY29uY2lsZV9jb25maWcuZ28=) | `82.85% <100.00%> (+2.69%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=knative)

pierDipi: /cc @dprotaso 

Patch:
diff --git a/configmap/hash-gen/main.go b/configmap/hash-gen/main.go
index 5846167f9..a2b154d92 100644
--- a/configmap/hash-gen/main.go
+++ b/configmap/hash-gen/main.go
@@ -20,7 +20,6 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"log"
 	"os"
 
@@ -39,7 +38,7 @@ func main() {
 // processFile reads the ConfigMap manifest from a file and adds or updates the label
 // containing the checksum of it's _example data if present.
 func processFile(fileName string) error {
-	in, err := ioutil.ReadFile(fileName)
+	in, err := os.ReadFile(fileName)
 	if err != nil {
 		return fmt.Errorf("failed to read file: %w", err)
 	}
@@ -50,7 +49,7 @@ func processFile(fileName string) error {
 	}
 
 	//nolint:gosec // This is not security critical so open permissions are fine.
-	if err := ioutil.WriteFile(fileName, out, 0644); err != nil {
+	if err := os.WriteFile(fileName, out, 0644); err != nil {
 		return fmt.Errorf("failed to write file: %w", err)
 	}
 	return nil
diff --git a/configmap/load.go b/configmap/load.go
index ee40fbf0c..56ad1e4ab 100644
--- a/configmap/load.go
+++ b/configmap/load.go
@@ -17,7 +17,6 @@ limitations under the License.
 package configmap
 
 import (
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
@@ -47,7 +46,7 @@ func Load(p string) (map[string]string, error) {
 		if info.IsDir() {
 			return nil
 		}
-		b, err := ioutil.ReadFile(p)
+		b, err := os.ReadFile(p)
 		if err != nil {
 			return err
 		}
diff --git a/go.mod b/go.mod
index d5dd9e1b4..36e386449 100644
--- a/go.mod
+++ b/go.mod
@@ -49,7 +49,8 @@ require (
 	k8s.io/code-generator v0.24.4
 	k8s.io/gengo v0.0.0-20220613173612-397b4ae3bce7
 	k8s.io/klog/v2 v2.70.2-0.20220707122935-0990e81f1a8f
-	knative.dev/hack v0.0.0-20220823140917-8d1e4ccf9dc3
+	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
+	knative.dev/hack v0.0.0-20220913095247-7556452c2b54
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -103,7 +104,6 @@ require (
 	gopkg.in/inf.v0 v0.9.1 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 	k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42 // indirect
-	k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 // indirect
 	sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect
 	sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect
 )
diff --git a/go.sum b/go.sum
index ce59a49ce..c6005ca9c 100644
--- a/go.sum
+++ b/go.sum
@@ -1180,8 +1180,8 @@ k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdi
 k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9 h1:HNSDgDCrr/6Ly3WEGKZftiE7IY19Vz2GdbOCyI4qqhc=
 k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-knative.dev/hack v0.0.0-20220823140917-8d1e4ccf9dc3 h1:umaeMRecA0g5g48L9tnEAkTBIitr9eKWMyJYo9YttAA=
-knative.dev/hack v0.0.0-20220823140917-8d1e4ccf9dc3/go.mod h1:t/azP8I/Cygaw+87O7rkAPrNRjCelmtfSzWzu/9TM7I=
+knative.dev/hack v0.0.0-20220913095247-7556452c2b54 h1:JycKYjjK9+noO3IWvRES6gW65SMUf/efNhaXRqh3u+A=
+knative.dev/hack v0.0.0-20220913095247-7556452c2b54/go.mod h1:yk2OjGDsbEnQjfxdm0/HJKS2WqTLEFg/N6nUs6Rqx3Q=
 pgregory.net/rapid v0.3.3 h1:jCjBsY4ln4Atz78QoBWxUEvAHaFyNDQg9+WU62aCn1U=
 pgregory.net/rapid v0.3.3/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
diff --git a/metrics/resource_view.go b/metrics/resource_view.go
index 236fd588b..69e4ab435 100644
--- a/metrics/resource_view.go
+++ b/metrics/resource_view.go
@@ -29,7 +29,7 @@ import (
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
 	"go.opencensus.io/tag"
-	"k8s.io/apimachinery/pkg/util/clock"
+	"k8s.io/utils/clock"
 )
 
 type storedViews struct {
@@ -53,7 +53,7 @@ type meters struct {
 	meters         map[string]*meterExporter
 	factory        ResourceExporterFactory
 	lock           sync.Mutex
-	clock          clock.Clock
+	clock          clock.WithTicker
 	ticker         clock.Ticker
 	tickerStopChan chan struct{}
 }
@@ -65,7 +65,7 @@ var (
 	resourceViews = storedViews{}
 	allMeters     = meters{
 		meters: map[string]*meterExporter{"": &defaultMeter},
-		clock:  clock.Clock(clock.RealClock{}),
+		clock:  clock.RealClock{},
 	}
 
 	cleanupOnce                = new(sync.Once)
diff --git a/network/error_handler.go b/network/error_handler.go
index 763766489..d0b6beedc 100644
--- a/network/error_handler.go
+++ b/network/error_handler.go
@@ -17,8 +17,8 @@ limitations under the License.
 package network
 
 import (
-	"io/ioutil"
 	"net/http"
+	"os"
 
 	"go.uber.org/zap"
 )
@@ -36,7 +36,7 @@ func ErrorHandler(logger *zap.SugaredLogger) func(http.ResponseWriter, *http.Req
 }
 
 func readSockStat(logger *zap.SugaredLogger) string {
-	b, err := ioutil.ReadFile("/proc/net/sockstat")
+	b, err := os.ReadFile("/proc/net/sockstat")
 	if err != nil {
 		logger.Errorw("Unable to read sockstat", zap.Error(err))
 		return ""
diff --git a/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go b/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go
deleted file mode 100644
index ff97612df..000000000
--- a/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
-Copyright 2014 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-package clock
-
-import (
-	"time"
-
-	clocks "k8s.io/utils/clock"
-	testclocks "k8s.io/utils/clock/testing"
-)
-
-// PassiveClock allows for injecting fake or real clocks into code
-// that needs to read the current time but does not support scheduling
-// activity in the future.
-//
-// Deprecated: Use k8s.io/utils/clock.PassiveClock instead.
-type PassiveClock = clocks.PassiveClock
-
-// Clock allows for injecting fake or real clocks into code that
-// needs to do arbitrary things based on time.
-//
-// Deprecated: Use k8s.io/utils/clock.WithTickerAndDelayedExecution instead.
-type Clock = clocks.WithTickerAndDelayedExecution
-
-// Deprecated: Use k8s.io/utils/clock.RealClock instead.
-type RealClock = clocks.RealClock
-
-// FakePassiveClock implements PassiveClock, but returns an arbitrary time.
-//
-// Deprecated: Use k8s.io/utils/clock/testing.FakePassiveClock instead.
-type FakePassiveClock = testclocks.FakePassiveClock
-
-// FakeClock implements Clock, but returns an arbitrary time.
-//
-// Deprecated: Use k8s.io/utils/clock/testing.FakeClock instead.
-type FakeClock = testclocks.FakeClock
-
-// NewFakePassiveClock returns a new FakePassiveClock.
-//
-// Deprecated: Use k8s.io/utils/clock/testing.NewFakePassiveClock instead.
-func NewFakePassiveClock(t time.Time) *testclocks.FakePassiveClock {
-	return testclocks.NewFakePassiveClock(t)
-}
-
-// NewFakeClock returns a new FakeClock.
-//
-// Deprecated: Use k8s.io/utils/clock/testing.NewFakeClock instead.
-func NewFakeClock(t time.Time) *testclocks.FakeClock {
-	return testclocks.NewFakeClock(t)
-}
-
-// IntervalClock implements Clock, but each invocation of Now steps
-// the clock forward the specified duration.
-//
-// WARNING: most of the Clock methods just `panic`;
-// only PassiveClock is honestly implemented.
-// The alternative, SimpleIntervalClock, has only the
-// PassiveClock methods.
-//
-// Deprecated: Use k8s.io/utils/clock/testing.SimpleIntervalClock instead.
-type IntervalClock = testclocks.IntervalClock
-
-// Timer allows for injecting fake or real timers into code that
-// needs to do arbitrary things based on time.
-//
-// Deprecated: Use k8s.io/utils/clock.Timer instead.
-type Timer = clocks.Timer
-
-// Ticker defines the Ticker interface.
-//
-// Deprecated: Use k8s.io/utils/clock.Ticker instead.
-type Ticker = clocks.Ticker
diff --git a/vendor/knative.dev/hack/go.work b/vendor/knative.dev/hack/go.work
new file mode 100644
index 000000000..d9a0de87f
--- /dev/null
+++ b/vendor/knative.dev/hack/go.work
@@ -0,0 +1,7 @@
+go 1.18
+
+use (
+	.
+	schema
+	test
+)
diff --git a/vendor/knative.dev/hack/go.work.sum b/vendor/knative.dev/hack/go.work.sum
new file mode 100644
index 000000000..cd1e58259
--- /dev/null
+++ b/vendor/knative.dev/hack/go.work.sum
@@ -0,0 +1,12 @@
+github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
+github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
+github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
+golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
+golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
+k8s.io/apimachinery v0.20.6 h1:R5p3SlhaABYShQSO6LpPsYHjV05Q+79eBUR0Ut/f4tk=
+k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
+k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
+sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06 h1:zD2IemQ4LmOcAumeiyDWXKUI2SO0NYDe3H6QGvPOVgU=
+sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c=
+golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
diff --git a/vendor/knative.dev/hack/infra-library.sh b/vendor/knative.dev/hack/infra-library.sh
index 0d0e002f4..662adfecc 100644
--- a/vendor/knative.dev/hack/infra-library.sh
+++ b/vendor/knative.dev/hack/infra-library.sh
@@ -91,8 +91,8 @@ function create_test_cluster() {
   fi
 
   case "$1" in
-    gke) create_gke_test_cluster "$2" "$3" "$4" ;;
-    kind) create_kind_test_cluster "$2" "$3" "$4" ;;
+    gke) create_gke_test_cluster "$2" "$3" "${4:-}" ;;
+    kind) create_kind_test_cluster "$2" "$3" "${4:-}" ;;
     *) echo "unsupported provider: $1"; exit 1 ;;
   esac
 
@@ -126,12 +126,14 @@ function create_gke_test_cluster() {
   # We are disabling logs and metrics on Boskos Clusters by default as they are not used. Manually set ENABLE_GKE_TELEMETRY to true to enable telemetry
   # and ENABLE_PREEMPTIBLE_NODES to true to create preemptible/spot VMs. VM Preemption is a rare event and shouldn't be distruptive given the fault tolerant nature of our tests.
   local extra_gcloud_flags=""
-  if [[ "$ENABLE_GKE_TELEMETRY" != "true" ]]; then
+  if [[ "${ENABLE_GKE_TELEMETRY:-}" != "true" ]]; then
     extra_gcloud_flags="${extra_gcloud_flags} --logging=NONE --monitoring=NONE"
   fi
 
-  if [[ "$ENABLE_PREEMPTIBLE_NODES" == "true" ]]; then
+  if [[ "${ENABLE_PREEMPTIBLE_NODES:-}" == "true" ]]; then
     extra_gcloud_flags="${extra_gcloud_flags} --preemptible"
   fi
-  run_kntest kubetest2 gke "${_custom_flags[@]}" --test-command="${_test_command[*]}" --extra-gcloud-flags="${extra_gcloud_flags}"
+  run_kntest kubetest2 gke "${_custom_flags[@]}" \
+    --test-command="${_test_command[*]}" \
+    --extra-gcloud-flags="${extra_gcloud_flags}"
 }
diff --git a/vendor/knative.dev/hack/library.sh b/vendor/knative.dev/hack/library.sh
index 26cd00cb8..faac80df7 100644
--- a/vendor/knative.dev/hack/library.sh
+++ b/vendor/knative.dev/hack/library.sh
@@ -40,7 +40,19 @@ fi
 readonly IS_PROW
 [[ ! -v REPO_ROOT_DIR ]] && REPO_ROOT_DIR="$(git rev-parse --show-toplevel)"
 readonly REPO_ROOT_DIR
-readonly REPO_NAME="${REPO_NAME:-$(basename "${REPO_ROOT_DIR}")}"
+
+# Resolves the repository name given a root directory.
+# Parameters: $1 - repository root directory.
+function __resolveRepoName() {
+  local repoName
+  repoName="$(basename "${1:-$(git rev-parse --show-toplevel)}")"
+  repoName="${repoName#knative-sandbox-}" # Remove knative-sandbox- prefix if any
+  repoName="${repoName#knative-}" # Remove knative- prefix if any
+  echo "${repoName}"
+}
+default_repo_name="$(__resolveRepoName "${REPO_ROOT_DIR}")"
+readonly REPO_NAME="${REPO_NAME:-$default_repo_name}"
+unset default_repo_name
 
 # Useful flags about the current OS
 IS_LINUX=0
@@ -65,10 +77,6 @@ if [[ -z "${ARTIFACTS:-}" ]]; then
 fi
 mkdir -p "$ARTIFACTS"
 
-
-# On a Prow job, redirect stderr to stdout so it's synchronously added to log
-(( IS_PROW )) && exec 2>&1
-
 # Return the major version of a release.
 # For example, "v0.2.1" returns "0"
 # Parameters: $1 - release version label.
@@ -94,11 +102,49 @@ function patch_version() {
   echo "${tokens[2]}"
 }
 
-# Print error message and exit 1
+# Calculates the hashcode for a given string.
+# Parameters: $* - string to be hashed.
+# See: https://stackoverflow.com/a/48863502/844449
+function hashCode() {
+  local input="$1"
+  local -i h=0
+  for ((i = 0; i < ${#input}; i++)); do
+    # val is ASCII val
+    printf -v val "%d" "'${input:$i:1}"
+    hval=$((31 * h + val))
+    # hash scheme
+    if ((hval > 2147483647)); then
+      h=$(( (hval - 2147483648) % 2147483648 ))
+    elif ((hval < -2147483648)); then
+      h=$(( (hval + 2147483648) % 2147483648 ))
+    else
+      h=$(( hval ))
+    fi
+  done
+  # final hashCode in decimal
+  printf "%d" $h
+}
+
+# Calculates the retcode for a given string. Makes sure the return code is
+# non-zero.
+# Parameters: $* - string to be hashed.
+function calcRetcode() {
+  local rc=1
+  local rcc
+  rcc="$(hashCode "$*")"
+  if [[ $rcc != 0 ]]; then
+    rc=$(( rcc % 255 ))
+  fi
+  echo "$rc"
+}
+
+# Print error message and call exit(n) where n calculated from the error message.
 # Parameters: $1..$n - error message to be displayed
+# Globals: abort_retcode will change the default retcode to be returned
 function abort() {
-  echo "error: $*"
-  exit 1
+  make_banner '*' "ERROR: $*" >&2
+  readonly abort_retcode="${abort_retcode:-$(calcRetcode "$*")}"
+  exit "$abort_retcode"
 }
 
 # Display a box banner.
@@ -106,11 +152,13 @@ function abort() {
 #             $2 - banner message.
 function make_banner() {
   local msg="$1$1$1$1 $2 $1$1$1$1"
-  local border="${msg//[-0-9A-Za-z _.,\/()\']/$1}"
+  local border="${msg//[^$1]/$1}"
   echo -e "${border}\n${msg}\n${border}"
   # TODO(adrcunha): Remove once logs have timestamps on Prow
   # For details, see https://github.com/kubernetes/test-infra/issues/10100
-  echo -e "$1$1$1$1 $(TZ='UTC' date)\n${border}"
+  if (( IS_PROW )); then
+    echo -e "$1$1$1$1 $(TZ='UTC' date --rfc-3339=ns)\n${border}"
+  fi
 }
 
 # Simple header for logging purposes.
@@ -126,7 +174,7 @@ function subheader() {
 
 # Simple warning banner for logging purposes.
 function warning() {
-  make_banner "!" "$1"
+  make_banner '!' "WARN: $*" >&2
 }
 
 # Checks whether the given function exists.
@@ -448,14 +496,14 @@ function report_go_test() {
   logfile="${xml/junit_/go_test_}"
   logfile="${logfile/.xml/.jsonl}"
   echo "Running go test with args: ${go_test_args[*]}"
+  local gotest_retcode=0
   go_run gotest.tools/gotestsum@v1.8.0 \
     --format "${GO_TEST_VERBOSITY:-testname}" \
     --junitfile "${xml}" \
     --junitfile-testsuite-name relative \
     --junitfile-testcase-classname relative \
     --jsonfile "${logfile}" \
-    -- "${go_test_args[@]}"
-  local gotest_retcode=$?
+    -- "${go_test_args[@]}" || gotest_retcode=$?
   echo "Finished run, return code is ${gotest_retcode}"
 
   echo "XML report written to ${xml}"
@@ -558,6 +606,9 @@ function go_run() {
   if [[ "$package" != *@* ]]; then
     abort 'Package for "go_run" needs to have @version'
   fi
+  if [[ "$package" == *@latest ]] && [[ "$package" != knative.dev* ]]; then
+    warning 'Using @latest version for external dependencies is unsafe. Use numbered version!'
+  fi
   shift 1
   GORUN_PATH="${GORUN_PATH:-$(go env GOPATH)}"
   # Some CI environments may have non-writable GOPATH
@@ -567,7 +618,6 @@ function go_run() {
   export GORUN_PATH
   GOPATH="${GORUN_PATH}" \
   GOFLAGS='' \
-  GO111MODULE='' \
     go run "$package" "$@"
 }
 
@@ -577,6 +627,7 @@ function go_run() {
 #             $3..$n - parameters passed to the tool.
 # Deprecated: use go_run instead
 function run_go_tool() {
+  warning 'The "run_go_tool" function is deprecated. Use "go_run" instead.'
   local package=$1
   # If no `@version` is provided, default to adding `@latest`
   if [[ "$package" != *@* ]]; then
@@ -601,6 +652,25 @@ function add_trap {
   done
 }
 
+# Run a command, described by $1, for every go module in the project.
+# Parameters: $1      - Description of the command being run,
+#             $2 - $n - Arguments to pass to the command.
+function foreach_go_module() {
+  local failed=0
+  local -r cmd="$1"
+  shift
+  local gomod_dir
+  while read -r gomod_dir; do
+    pushd "$gomod_dir" > /dev/null
+    "$cmd" "$@" || failed=$?
+    popd > /dev/null
+    if (( failed )); then
+      echo "Command '${cmd}' failed in module $gomod_dir: $failed" >&2
+      return $failed
+    fi
+  done < <(go_run knative.dev/test-infra/tools/modscope@latest ls -p)
+}
+
 # Update go deps.
 # Parameters (parsed as flags):
 #   "--upgrade", bool, do upgrade.
@@ -613,14 +683,18 @@ function add_trap {
 # global env var: FLOATING_DEPS
 # --upgrade will set GOPROXY to direct unless it is already set.
 function go_update_deps() {
-  cd "${REPO_ROOT_DIR}" || return 1
+  foreach_go_module __go_update_deps_for_module "$@"
+}
+
+function __go_update_deps_for_module() {
+  ( # do not modify the environment
+  set -Eeuo pipefail
 
-  export GO111MODULE=on
   export GOFLAGS=""
   export GONOSUMDB="${GONOSUMDB:-},knative.dev/*"
   export GONOPROXY="${GONOPROXY:-},knative.dev/*"
 
-  echo "=== Update Deps for Golang"
+  echo "=== Update Deps for Golang module: $(go_mod_module_name)"
 
   local UPGRADE=0
   local RELEASE="v9000.1" # release v9000 is so far in the future, it will always pick the default branch.
@@ -646,7 +720,7 @@ function go_update_deps() {
     else
       group "Upgrading to release ${RELEASE}"
     fi
-    FLOATING_DEPS+=( $(go_run knative.dev/test-infra/buoy@latest float ${REPO_ROOT_DIR}/go.mod "${buoyArgs[@]}") )
+    FLOATING_DEPS+=( $(go_run knative.dev/test-infra/buoy@latest float ./go.mod "${buoyArgs[@]}") )
     if [[ ${#FLOATING_DEPS[@]} > 0 ]]; then
       echo "Floating deps to ${FLOATING_DEPS[@]}"
       go get -d ${FLOATING_DEPS[@]}
@@ -664,6 +738,9 @@ function go_update_deps() {
   go mod vendor 2>&1 |  grep -v "ignoring symlink" || true
   eval "$orig_pipefail_opt"
 
+  if ! [ -d vendor ]; then
+    return 0
+  fi
   group "Removing unwanted vendor files"
 
   # Remove unwanted vendor files
@@ -680,13 +757,15 @@ function go_update_deps() {
 
   group "Removing broken symlinks"
   remove_broken_symlinks ./vendor
+  )
 }
 
+
 # Return the go module name of the current module.
 # Intended to be used like:
 #   export MODULE_NAME=$(go_mod_module_name)
 function go_mod_module_name() {
-  go mod graph | cut -d' ' -f 1 | grep -v '@' | head -1
+  go_run knative.dev/test-infra/tools/modscope@latest current
 }
 
 # Return a GOPATH to a temp directory. Works around the out-of-GOPATH issues
@@ -717,11 +796,10 @@ function run_kntest() {
 # Parameters: $1 - output file, relative to repo root dir.
 #             $2 - directory to inspect.
 function update_licenses() {
-  cd "${REPO_ROOT_DIR}" || return 1
   local dst=$1
   local dir=$2
   shift
-  go_run github.com/google/go-licenses@v1.2.0 \
+  go_run github.com/google/go-licenses@v1.2.1 \
     save "${dir}" --save_path="${dst}" --force || \
     { echo "--- FAIL: go-licenses failed to update licenses"; return 1; }
 }
@@ -729,7 +807,7 @@ function update_licenses() {
 # Run go-licenses to check for forbidden licenses.
 function check_licenses() {
   # Check that we don't have any forbidden licenses.
-  go_run github.com/google/go-licenses@v1.2.0 \
+  go_run github.com/google/go-licenses@v1.2.1 \
     check "${REPO_ROOT_DIR}/..." || \
     { echo "--- FAIL: go-licenses failed the license check"; return 1; }
 }
@@ -762,7 +840,7 @@ function is_protected_project() {
 # Remove symlinks in a path that are broken or lead outside the repo.
 # Parameters: $1 - path name, e.g. vendor
 function remove_broken_symlinks() {
-  for link in $(find $1 -type l); do
+  for link in $(find "$1" -type l); do
     # Remove broken symlinks
     if [[ ! -e ${link} ]]; then
       unlink ${link}
diff --git a/vendor/knative.dev/hack/release.sh b/vendor/knative.dev/hack/release.sh
index 6944310ac..703d90344 100644
--- a/vendor/knative.dev/hack/release.sh
+++ b/vendor/knative.dev/hack/release.sh
@@ -29,6 +29,10 @@ readonly REPO_UPSTREAM="https://github.com/${ORG_NAME}/${REPO_NAME}"
 readonly NIGHTLY_GCR="gcr.io/knative-nightly/github.com/${ORG_NAME}/${REPO_NAME}"
 readonly RELEASE_GCR="gcr.io/knative-releases/github.com/${ORG_NAME}/${REPO_NAME}"
 
+# Signing identities for knative releases.
+readonly NIGHTLY_SIGNING_IDENTITY="signer@knative-nightly.iam.gserviceaccount.com"
+readonly RELEASE_SIGNING_IDENTITY="signer@knative-releases.iam.gserviceaccount.com"
+
 # Georeplicate images to {us,eu,asia}.gcr.io
 readonly GEO_REPLICATION=(us eu asia)
 
@@ -94,11 +98,12 @@ RELEASE_NOTES=""
 RELEASE_BRANCH=""
 RELEASE_GCS_BUCKET="knative-nightly/${REPO_NAME}"
 RELEASE_DIR=""
-KO_FLAGS="-P --platform=all"
+KO_FLAGS="-P --platform=all --image-refs=imagerefs.txt"
 VALIDATION_TESTS="./test/presubmit-tests.sh"
 ARTIFACTS_TO_PUBLISH=""
 FROM_NIGHTLY_RELEASE=""
 FROM_NIGHTLY_RELEASE_GCS=""
+SIGNING_IDENTITY=""
 export KO_DOCKER_REPO="gcr.io/knative-nightly"
 # Build stripped binary to reduce size
 export GOFLAGS="-ldflags=-s -ldflags=-w"
@@ -301,6 +306,34 @@ function build_from_source() {
   if [[ $? -ne 0 ]]; then
     abort "error building the release"
   fi
+  sign_release || abort "error signing the release"
+}
+
+# Build a release from source.
+function sign_release() {
+  if [ -z "${SIGN_IMAGES:-}" ]; then # Temporary Feature Gate
+    return 0
+  fi
+  ## Sign the images with cosign
+  ## For now, check if ko has created imagerefs.txt file. In the future, missing image refs will break
+  ## the release for all jobs that publish images.
+  if [[ -f "imagerefs.txt" ]]; then
+      echo "Signing Images with the identity ${SIGNING_IDENTITY}"
+      COSIGN_EXPERIMENTAL=1 cosign sign $(cat imagerefs.txt) --recursive --identity-token="$(
+        gcloud auth print-identity-token --audiences=sigstore \
+        --include-email \
+        --impersonate-service-account="${SIGNING_IDENTITY}")"
+  fi
+
+  ## Check if there is checksums.txt file. If so, sign the checksum file
+  if [[ -f "checksums.txt" ]]; then
+      echo "Signing Images with the identity ${SIGNING_IDENTITY}"
+      COSIGN_EXPERIMENTAL=1 cosign sign-blob checksums.txt --output-signature checksums.txt.sig --identity-token="$(
+        gcloud auth print-identity-token --audiences=sigstore \
+        --include-email \
+        --impersonate-service-account="${SIGNING_IDENTITY}")"
+      ARTIFACTS_TO_PUBLISH="${ARTIFACTS_TO_PUBLISH} checksums.txt.sig"
+  fi
 }
 
 # Copy tagged images from the nightly GCR to the release GCR, tagging them 'latest'.
@@ -375,10 +408,12 @@ function parse_flags() {
             ;;
           --release-gcr)
             KO_DOCKER_REPO=$1
+            SIGNING_IDENTITY=$RELEASE_SIGNING_IDENTITY
             has_gcr_flag=1
             ;;
           --release-gcs)
             RELEASE_GCS_BUCKET=$1
+            SIGNING_IDENTITY=$RELEASE_SIGNING_IDENTITY
             RELEASE_DIR=""
             has_gcs_flag=1
             ;;
@@ -449,6 +484,11 @@ function parse_flags() {
     [[ -z "${RELEASE_DIR}" ]] && RELEASE_DIR="${REPO_ROOT_DIR}"
   fi
 
+  # Set signing identity for cosign, it would already be set to the RELEASE one if the release-gcr/release-gcs flags are set
+  if [[ -z "${SIGNING_IDENTITY}" ]]; then
+    SIGNING_IDENTITY="${NIGHTLY_SIGNING_IDENTITY}"
+  fi
+
   [[ -z "${RELEASE_GCS_BUCKET}" && -z "${RELEASE_DIR}" ]] && abort "--release-gcs or --release-dir must be used"
   if [[ -n "${RELEASE_DIR}" ]]; then
     mkdir -p "${RELEASE_DIR}" || abort "cannot create release dir '${RELEASE_DIR}'"
@@ -481,6 +521,7 @@ function parse_flags() {
   readonly RELEASE_DIR
   readonly VALIDATION_TESTS
   readonly FROM_NIGHTLY_RELEASE
+  readonly SIGNING_IDENTITY
 }
 
 # Run tests (unless --skip-tests was passed). Conveniently displays a banner indicating so.
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 86da18cb4..8f7cd3e2b 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -719,7 +719,6 @@ k8s.io/apimachinery/pkg/runtime/serializer/versioning
 k8s.io/apimachinery/pkg/selection
 k8s.io/apimachinery/pkg/types
 k8s.io/apimachinery/pkg/util/cache
-k8s.io/apimachinery/pkg/util/clock
 k8s.io/apimachinery/pkg/util/diff
 k8s.io/apimachinery/pkg/util/errors
 k8s.io/apimachinery/pkg/util/framer
@@ -1099,8 +1098,8 @@ k8s.io/utils/net
 k8s.io/utils/pointer
 k8s.io/utils/strings/slices
 k8s.io/utils/trace
-# knative.dev/hack v0.0.0-20220823140917-8d1e4ccf9dc3
-## explicit; go 1.17
+# knative.dev/hack v0.0.0-20220913095247-7556452c2b54
+## explicit; go 1.18
 knative.dev/hack
 # sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2
 ## explicit; go 1.17
diff --git a/webhook/resourcesemantics/validation/reconcile_config.go b/webhook/resourcesemantics/validation/reconcile_config.go
index a838351c8..dfc361991 100644
--- a/webhook/resourcesemantics/validation/reconcile_config.go
+++ b/webhook/resourcesemantics/validation/reconcile_config.go
@@ -32,6 +32,7 @@ import (
 	"k8s.io/client-go/kubernetes"
 	admissionlisters "k8s.io/client-go/listers/admissionregistration/v1"
 	corelisters "k8s.io/client-go/listers/core/v1"
+
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/kmp"
 	"knative.dev/pkg/logging"
@@ -99,7 +100,7 @@ func (ac *reconciler) Reconcile(ctx context.Context, key string) error {
 func (ac *reconciler) reconcileValidatingWebhook(ctx context.Context, caCert []byte) error {
 	logger := logging.FromContext(ctx)
 
-	rules := make([]admissionregistrationv1.RuleWithOperations, 0, len(ac.handlers))
+	rules := make([]admissionregistrationv1.RuleWithOperations, 0, len(ac.handlers)+len(ac.callbacks))
 	for gvk, config := range ac.handlers {
 		plural := strings.ToLower(flect.Pluralize(gvk.Kind))
 
@@ -143,6 +144,29 @@ func (ac *reconciler) reconcileValidatingWebhook(ctx context.Context, caCert []b
 			},
 		})
 	}
+	for gvk, callback := range ac.callbacks {
+		if _, ok := ac.handlers[gvk]; ok {
+			continue
+		}
+		plural := strings.ToLower(flect.Pluralize(gvk.Kind))
+		resources := []string{plural, plural + "/status"}
+
+		verbs := make([]admissionregistrationv1.OperationType, 0, len(callback.supportedVerbs))
+		for verb := range callback.supportedVerbs {
+			verbs = append(verbs, admissionregistrationv1.OperationType(verb))
+		}
+		// supportedVerbs is a map which doesn't provide a stable order in for loops.
+		sort.Slice(verbs, func(i, j int) bool { return string(verbs[i]) < string(verbs[j]) })
+
+		rules = append(rules, admissionregistrationv1.RuleWithOperations{
+			Operations: verbs,
+			Rule: admissionregistrationv1.Rule{
+				APIGroups:   []string{gvk.Group},
+				APIVersions: []string{gvk.Version},
+				Resources:   resources,
+			},
+		})
+	}
 
 	for _, r := range rules {
 		logging.FromContext(ctx).Debugf("Rule: %+v", r)
diff --git a/websocket/connection.go b/websocket/connection.go
index 601ee951d..6aa1e6109 100644
--- a/websocket/connection.go
+++ b/websocket/connection.go
@@ -22,7 +22,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net/http/httputil"
 	"sync"
 	"time"
@@ -302,7 +301,7 @@ func (c *ManagedConnection) read() error {
 	// and if that channel is set.
 	// TODO(markusthoemmes): Return the messageType along with the payload.
 	if c.messageChan != nil && (messageType == websocket.TextMessage || messageType == websocket.BinaryMessage) {
-		if message, _ := ioutil.ReadAll(reader); message != nil {
+		if message, _ := io.ReadAll(reader); message != nil {
 			c.messageChan <- message
 		}
 	}


Test patch:
diff --git a/configmap/hash-gen/main_test.go b/configmap/hash-gen/main_test.go
index ffac0bc6d..7dcaba8cf 100644
--- a/configmap/hash-gen/main_test.go
+++ b/configmap/hash-gen/main_test.go
@@ -17,7 +17,6 @@ limitations under the License.
 package main
 
 import (
-	"io/ioutil"
 	"os"
 	"path"
 	"testing"
@@ -28,7 +27,7 @@ import (
 func TestProcess(t *testing.T) {
 	for _, test := range []string{"add", "update", "nothing"} {
 		t.Run(test, func(t *testing.T) {
-			in, err := ioutil.ReadFile(path.Join("testdata", test+".yaml"))
+			in, err := os.ReadFile(path.Join("testdata", test+".yaml"))
 			if err != nil {
 				t.Fatal("Failed to load test fixture:", err)
 			}
@@ -38,7 +37,7 @@ func TestProcess(t *testing.T) {
 				t.Fatal("Expected no error but got", err)
 			}
 
-			want, err := ioutil.ReadFile(path.Join("testdata", test+"_want.yaml"))
+			want, err := os.ReadFile(path.Join("testdata", test+"_want.yaml"))
 			if err != nil && !os.IsNotExist(err) {
 				t.Fatal("Failed to load test fixture:", err)
 			}
diff --git a/configmap/load_test.go b/configmap/load_test.go
index 313868457..0516b437e 100644
--- a/configmap/load_test.go
+++ b/configmap/load_test.go
@@ -18,7 +18,6 @@ package configmap
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path"
 	"testing"
@@ -33,7 +32,7 @@ func TestLoad(t *testing.T) {
 		"a.b.c":  "blah",
 		".z.y.x": "hidden!",
 	}
-	tmpdir, err := ioutil.TempDir("", "")
+	tmpdir, err := os.MkdirTemp("", "")
 	if err != nil {
 		t.Fatal("TempDir() =", err)
 	}
@@ -60,7 +59,7 @@ func TestLoad(t *testing.T) {
 	// Write out the files as they should be loaded.
 	for k, v := range want {
 		// Write the actual file to $/.{timestamp}/key
-		if err := ioutil.WriteFile(path.Join(tsDir, k), []byte(v), 0644); err != nil {
+		if err := os.WriteFile(path.Join(tsDir, k), []byte(v), 0644); err != nil {
 			t.Fatalf("WriteFile(..{ts}/%s) = %v", k, err)
 		}
 		// Symlink $/key => $/..data/key
@@ -91,7 +90,7 @@ func TestLoadError(t *testing.T) {
 // 		"a.b.c":  "blah",
 // 		".z.y.x": "hidden!",
 // 	}
-// 	tmpdir, err := ioutil.TempDir("", "")
+// 	tmpdir, err := os.MkdirTemp("", "")
 // 	if err != nil {
 // 		t.Fatal("TempDir() =", err)
 // 	}
@@ -99,7 +98,7 @@ func TestLoadError(t *testing.T) {
 
 // 	// Write out the files as write-only, so we fail reading.
 // 	for k, v := range written {
-// 		err := ioutil.WriteFile(path.Join(tmpdir, k), []byte(v), 0200)
+// 		err := os.WriteFile(path.Join(tmpdir, k), []byte(v), 0200)
 // 		if err != nil {
 // 			t.Fatalf("WriteFile(%q) = %v", k, err)
 // 		}
@@ -111,7 +110,7 @@ func TestLoadError(t *testing.T) {
 // }
 
 func TestReadSymlinkedFileError(t *testing.T) {
-	tmpdir, err := ioutil.TempDir("", "")
+	tmpdir, err := os.MkdirTemp("", "")
 	if err != nil {
 		t.Fatal("TempDir() =", err)
 	}
diff --git a/configmap/testing/configmap.go b/configmap/testing/configmap.go
index 909036892..58cf9a21b 100644
--- a/configmap/testing/configmap.go
+++ b/configmap/testing/configmap.go
@@ -18,7 +18,7 @@ package testing
 
 import (
 	"fmt"
-	"io/ioutil"
+	"os"
 	"strings"
 	"testing"
 	"unicode"
@@ -45,7 +45,7 @@ func ConfigMapFromTestFile(t testing.TB, name string, allowed ...string) *corev1
 func ConfigMapsFromTestFile(t testing.TB, name string, allowed ...string) (*corev1.ConfigMap, *corev1.ConfigMap) {
 	t.Helper()
 
-	b, err := ioutil.ReadFile(fmt.Sprintf("testdata/%s.yaml", name))
+	b, err := os.ReadFile(fmt.Sprintf("testdata/%s.yaml", name))
 	if err != nil {
 		t.Fatal("ReadFile() =", err)
 	}
diff --git a/metrics/client_test.go b/metrics/client_test.go
index ba6de4557..9ad2ac958 100644
--- a/metrics/client_test.go
+++ b/metrics/client_test.go
@@ -18,7 +18,7 @@ package metrics
 
 import (
 	"bytes"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/url"
 	"testing"
@@ -96,7 +96,7 @@ func TestClientMetrics(t *testing.T) {
 	stub := ClientFunc(func(req *http.Request) (*http.Response, error) {
 		return &http.Response{
 			StatusCode: http.StatusOK,
-			Body:       ioutil.NopCloser(bytes.NewBufferString("hi")),
+			Body:       io.NopCloser(bytes.NewBufferString("hi")),
 		}, nil
 	})
 
diff --git a/metrics/e2e_test.go b/metrics/e2e_test.go
index 627062065..67ea352c3 100644
--- a/metrics/e2e_test.go
+++ b/metrics/e2e_test.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"sort"
@@ -166,7 +165,7 @@ func TestMetricsExport(t *testing.T) {
 				t.Fatalf("failed to fetch prometheus metrics: %+v", err)
 			}
 			defer resp.Body.Close()
-			body, err := ioutil.ReadAll(resp.Body)
+			body, err := io.ReadAll(resp.Body)
 			if err != nil {
 				t.Fatalf("failed to read prometheus response: %+v", err)
 			}
diff --git a/metrics/opencensus_exporter_test.go b/metrics/opencensus_exporter_test.go
index d827b7d93..a642f2802 100644
--- a/metrics/opencensus_exporter_test.go
+++ b/metrics/opencensus_exporter_test.go
@@ -15,8 +15,8 @@ package metrics
 import (
 	"crypto/tls"
 	"fmt"
-	"io/ioutil"
 	"net"
+	"os"
 	"path/filepath"
 	"testing"
 
@@ -31,11 +31,11 @@ import (
 )
 
 func TestOpenCensusConfig(t *testing.T) {
-	cert, err := ioutil.ReadFile(filepath.Join("testdata", "client-cert.pem"))
+	cert, err := os.ReadFile(filepath.Join("testdata", "client-cert.pem"))
 	if err != nil {
 		t.Fatal("Couldn't find testdata/client-cert.pem:", err)
 	}
-	key, err := ioutil.ReadFile(filepath.Join("testdata", "client-key.pem"))
+	key, err := os.ReadFile(filepath.Join("testdata", "client-key.pem"))
 	if err != nil {
 		t.Fatal("Couldn't find testdata/client-key.pem:", err)
 	}
diff --git a/metrics/resource_view_test.go b/metrics/resource_view_test.go
index 52ac8df5d..f80486657 100644
--- a/metrics/resource_view_test.go
+++ b/metrics/resource_view_test.go
@@ -24,8 +24,8 @@ import (
 	"go.opencensus.io/resource"
 	"go.opencensus.io/stats"
 	"go.opencensus.io/stats/view"
-	"k8s.io/apimachinery/pkg/util/clock"
 	"k8s.io/apimachinery/pkg/util/wait"
+	testingclock "k8s.io/utils/clock/testing"
 )
 
 var (
@@ -129,8 +129,8 @@ func TestSetFactory(t *testing.T) {
 }
 
 func TestAllMetersExpiration(t *testing.T) {
-	allMeters.clock = clock.Clock(clock.NewFakeClock(time.Now()))
-	fakeClock := allMeters.clock.(*clock.FakeClock)
+	fakeClock := testingclock.NewFakeClock(time.Now())
+	allMeters.clock = fakeClock
 	ClearMetersForTest() // t+0m
 
 	// Add resource123
@@ -199,8 +199,8 @@ func TestAllMetersExpiration(t *testing.T) {
 }
 
 func TestIfAllMeterResourcesAreRemoved(t *testing.T) {
-	allMeters.clock = clock.Clock(clock.NewFakeClock(time.Now()))
-	fakeClock := allMeters.clock.(*clock.FakeClock)
+	fakeClock := testingclock.NewFakeClock(time.Now())
+	allMeters.clock = fakeClock
 	ClearMetersForTest() // t+0m
 	// Register many resources at once
 	for i := 1; i <= 100; i++ {
diff --git a/test/cleanup_test.go b/test/cleanup_test.go
index 96f888548..e90ac5a34 100644
--- a/test/cleanup_test.go
+++ b/test/cleanup_test.go
@@ -20,7 +20,6 @@ import (
 	"bytes"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"os/exec"
 	"strings"
@@ -43,8 +42,7 @@ func TestCleanupOnInterrupt(t *testing.T) {
 		return
 	}
 
-	// TODO: Move to os.CreateTemp when we adopt 1.16 more widely
-	readyFile, err := ioutil.TempFile("", "")
+	readyFile, err := os.CreateTemp("", "")
 	if err != nil {
 		t.Fatalf("failed to setup tests")
 	}
diff --git a/test/gcs/gcs.go b/test/gcs/gcs.go
index d39652f50..380548d5f 100644
--- a/test/gcs/gcs.go
+++ b/test/gcs/gcs.go
@@ -21,7 +21,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"os"
 	"path"
 	"strings"
@@ -223,7 +222,7 @@ func (g *GCSClient) ReadObject(ctx context.Context, bucketName, objPath string)
 		return contents, err
 	}
 	defer f.Close()
-	return ioutil.ReadAll(f)
+	return io.ReadAll(f)
 }
 
 // NewReader creates a new Reader of a gcs file.
diff --git a/test/gcs/mock/mock.go b/test/gcs/mock/mock.go
index 3113f026d..7ee63f3dc 100644
--- a/test/gcs/mock/mock.go
+++ b/test/gcs/mock/mock.go
@@ -18,7 +18,6 @@ package mock
 
 import (
 	"context"
-	"io/ioutil"
 	"os"
 	"path/filepath"
 	"strings"
@@ -434,7 +433,7 @@ func (c *clientMocker) Upload(ctx context.Context, bkt, objPath, filePath string
 		return NewNoObjectError(bkt, objName, dir)
 	}
 
-	content, err := ioutil.ReadFile(filePath)
+	content, err := os.ReadFile(filePath)
 	if err != nil {
 		return err
 	}
diff --git a/test/gcs/mock/mock_test.go b/test/gcs/mock/mock_test.go
index 395398445..40eb77743 100644
--- a/test/gcs/mock/mock_test.go
+++ b/test/gcs/mock/mock_test.go
@@ -21,7 +21,6 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path"
 	"reflect"
@@ -971,7 +970,7 @@ func TestDownload(t *testing.T) {
 				return
 			}
 
-			fileContent, err := ioutil.ReadFile(file)
+			fileContent, err := os.ReadFile(file)
 			if err != nil {
 				t.Fatalf("cannot read content %v, error %v", file, err)
 			}
@@ -989,7 +988,7 @@ func TestUpload(t *testing.T) {
 	project1 := "test-project1"
 	object1 := "dir/object1"
 	file := "test/upload"
-	content, err := ioutil.ReadFile(file)
+	content, err := os.ReadFile(file)
 	if err != nil {
 		t.Fatalf("cannot read content %v, error %v", file, err)
 	}
diff --git a/test/ghutil/client.go b/test/ghutil/client.go
index 8c14509f1..2dc162e0b 100644
--- a/test/ghutil/client.go
+++ b/test/ghutil/client.go
@@ -22,8 +22,8 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"io/ioutil"
 	"log"
+	"os"
 	"strings"
 	"time"
 
@@ -73,7 +73,7 @@ type GithubClient struct {
 
 // NewGithubClient explicitly authenticates to github with giving token and returns a handle
 func NewGithubClient(tokenFilePath string) (*GithubClient, error) {
-	b, err := ioutil.ReadFile(tokenFilePath)
+	b, err := os.ReadFile(tokenFilePath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/test/interactive/docker.go b/test/interactive/docker.go
index 3b295921f..290918034 100644
--- a/test/interactive/docker.go
+++ b/test/interactive/docker.go
@@ -19,7 +19,6 @@ package interactive
 
 import (
 	"fmt"
-	"io/ioutil"
 	"log"
 	"os"
 	"path"
@@ -82,7 +81,7 @@ func (d *Docker) AddMount(typeStr, source, target string, optAdditionalArgs ...s
 // Returns a function to clean up the mount (but does not delete the directory).
 // Uses sudo and probably only works on Linux
 func (d *Docker) AddRWOverlay(externalDirectory, internalDirectory string) func() {
-	tmpDir, err := ioutil.TempDir("", "overlay")
+	tmpDir, err := os.MkdirTemp("", "overlay")
 	if err != nil {
 		log.Fatal(err)
 	}
diff --git a/test/logstream/v2/stream_test.go b/test/logstream/v2/stream_test.go
index 4244adc0e..63fdee858 100644
--- a/test/logstream/v2/stream_test.go
+++ b/test/logstream/v2/stream_test.go
@@ -20,7 +20,7 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strings"
 	"testing"
@@ -385,7 +385,7 @@ func (f *fakePods) GetLogs(podName string, opts *corev1.PodLogOptions) *restclie
 		Client: fakerest.CreateHTTPClient(func(request *http.Request) (*http.Response, error) {
 			resp := &http.Response{
 				StatusCode: http.StatusOK,
-				Body: ioutil.NopCloser(
+				Body: io.NopCloser(
 					strings.NewReader(logsForContainer(opts.Container))),
 			}
 			return resp, nil
diff --git a/test/mako/config/benchmark.go b/test/mako/config/benchmark.go
index 3ddff5cdd..64f1a812c 100644
--- a/test/mako/config/benchmark.go
+++ b/test/mako/config/benchmark.go
@@ -18,7 +18,6 @@ package config
 
 import (
 	"fmt"
-	"io/ioutil"
 	"log"
 	"os"
 	"path/filepath"
@@ -49,7 +48,7 @@ func getBenchmark() (*mpb.BenchmarkInfo, error) {
 	// Read the Mako config file for this environment.
 	data, koerr := readFileFromKoData(env + ".config")
 	if koerr != nil {
-		data, err = ioutil.ReadFile(filepath.Join(configMako, env+".config"))
+		data, err = os.ReadFile(filepath.Join(configMako, env+".config"))
 		if err != nil {
 			//nolint: errorlint // It's fine not to wrap here.
 			return nil, fmt.Errorf("cannot load both from kodata and from config mako config map: %s, %s", koerr.Error(), err.Error())
@@ -72,5 +71,5 @@ func readFileFromKoData(name string) ([]byte, error) {
 		return nil, fmt.Errorf("%q does not exist or is empty", koDataPathEnvName)
 	}
 	fullFilename := filepath.Join(koDataPath, name)
-	return ioutil.ReadFile(fullFilename)
+	return os.ReadFile(fullFilename)
 }
diff --git a/test/slackutil/http.go b/test/slackutil/http.go
index 072860ff3..41577c9d3 100644
--- a/test/slackutil/http.go
+++ b/test/slackutil/http.go
@@ -20,7 +20,7 @@ package slackutil
 
 import (
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/url"
 )
@@ -49,5 +49,5 @@ func handleResponse(resp *http.Response) ([]byte, error) {
 	if resp.StatusCode != http.StatusOK {
 		return nil, fmt.Errorf("http response code is not StatusOK: '%v'", resp.StatusCode)
 	}
-	return ioutil.ReadAll(resp.Body)
+	return io.ReadAll(resp.Body)
 }
diff --git a/test/slackutil/message_read.go b/test/slackutil/message_read.go
index fe13126b0..6feeb52f8 100644
--- a/test/slackutil/message_read.go
+++ b/test/slackutil/message_read.go
@@ -22,8 +22,8 @@ import (
 	"encoding/json"
 	"fmt"
 	"html"
-	"io/ioutil"
 	"net/url"
+	"os"
 	"strconv"
 	"strings"
 	"time"
@@ -44,7 +44,7 @@ type readClient struct {
 
 // NewReadClient reads token file and stores it for later authentication
 func NewReadClient(userName, tokenPath string) (ReadOperations, error) {
-	b, err := ioutil.ReadFile(tokenPath)
+	b, err := os.ReadFile(tokenPath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/test/slackutil/message_write.go b/test/slackutil/message_write.go
index 6c0413cd5..eefd17d8b 100644
--- a/test/slackutil/message_write.go
+++ b/test/slackutil/message_write.go
@@ -21,7 +21,7 @@ package slackutil
 import (
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"os"
 	"strings"
 
 	"net/url"
@@ -42,7 +42,7 @@ type writeClient struct {
 
 // NewWriteClient reads token file and stores it for later authentication
 func NewWriteClient(userName, tokenPath string) (WriteOperations, error) {
-	b, err := ioutil.ReadFile(tokenPath)
+	b, err := os.ReadFile(tokenPath)
 	if err != nil {
 		return nil, err
 	}
diff --git a/test/spoof/spoof.go b/test/spoof/spoof.go
index 147a64adc..789d4bd68 100644
--- a/test/spoof/spoof.go
+++ b/test/spoof/spoof.go
@@ -23,7 +23,6 @@ import (
 	"errors"
 	"fmt"
 	"io"
-	"io/ioutil"
 	"net"
 	"net/http"
 	"net/url"
@@ -188,7 +187,7 @@ func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker, check
 		}
 		defer rawResp.Body.Close()
 
-		body, err := ioutil.ReadAll(rawResp.Body)
+		body, err := io.ReadAll(rawResp.Body)
 		if err != nil {
 			return true, err
 		}
diff --git a/test/upgrade/execute_failures_test.go b/test/upgrade/execute_failures_test.go
index cd5a19ddc..a11448f07 100644
--- a/test/upgrade/execute_failures_test.go
+++ b/test/upgrade/execute_failures_test.go
@@ -18,7 +18,7 @@ package upgrade_test
 
 import (
 	"fmt"
-	"io/ioutil"
+	"io"
 	"os"
 	"testing"
 
@@ -89,6 +89,6 @@ func captureStdOutput(call func()) string {
 	call()
 
 	_ = w.Close()
-	out, _ := ioutil.ReadAll(r)
+	out, _ := io.ReadAll(r)
 	return string(out)
 }
diff --git a/test/zipkin/util.go b/test/zipkin/util.go
index 5e9cb5feb..481dddb42 100644
--- a/test/zipkin/util.go
+++ b/test/zipkin/util.go
@@ -22,7 +22,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/url"
 	"strconv"
@@ -226,7 +226,7 @@ func jsonTrace(traceID string) ([]model.SpanModel, error) {
 	}
 	defer resp.Body.Close()
 
-	body, err := ioutil.ReadAll(resp.Body)
+	body, err := io.ReadAll(resp.Body)
 	if err != nil {
 		return empty, err
 	}
diff --git a/vendor/knative.dev/hack/e2e-tests.sh b/vendor/knative.dev/hack/e2e-tests.sh
index 57222f32c..b8cba420f 100644
--- a/vendor/knative.dev/hack/e2e-tests.sh
+++ b/vendor/knative.dev/hack/e2e-tests.sh
@@ -54,6 +54,7 @@ function go_test_e2e() {
 
 # Setup the test cluster for running the tests.
 function setup_test_cluster() {
+  (
   # Fail fast during setup.
   set -o errexit
   set -o pipefail
@@ -76,7 +77,7 @@ function setup_test_cluster() {
   # Setup KO_DOCKER_REPO if it is a GKE cluster. Incorporate an element of
   # randomness to ensure that each run properly publishes images. Don't
   # owerwrite KO_DOCKER_REPO if already set.
-  [ -z "${KO_DOCKER_REPO}" ] && \
+  [ -z "${KO_DOCKER_REPO:-}" ] && \
     [[ "${k8s_cluster}" =~ ^gke_.* ]] && \
     export KO_DOCKER_REPO=gcr.io/${E2E_PROJECT_ID}/${REPO_NAME}-e2e-img/${RANDOM}
 
@@ -108,6 +109,7 @@ function setup_test_cluster() {
   if function_exists test_setup; then
     test_setup || fail_test "test setup failed"
   fi
+  )
 }
 
 # Signal (as return code and in the logs) that all E2E tests passed.
@@ -120,12 +122,14 @@ function success() {
 }
 
 # Exit test, dumping current state info.
-# Parameters: $1 - error message (optional).
+# Parameters: $* - error message (optional).
 function fail_test() {
-  [[ -n $1 ]] && echo "ERROR: $1"
-  dump_cluster_state
-  dump_metrics
-  exit 1
+  local message="$*"
+  if [[ -n ${message:-} ]]; then
+    message='test failed'
+  fi
+  add_trap "dump_cluster_state;dump_metrics" EXIT
+  abort "${message}"
 }
 
 SKIP_TEARDOWNS=0
diff --git a/vendor/knative.dev/hack/presubmit-tests.sh b/vendor/knative.dev/hack/presubmit-tests.sh
index d9549f51b..1277aed4a 100644
--- a/vendor/knative.dev/hack/presubmit-tests.sh
+++ b/vendor/knative.dev/hack/presubmit-tests.sh
@@ -122,7 +122,12 @@ function report_build_test() {
 # * `go build` on the entire repo
 # * check licenses in all go packages
 function default_build_test_runner() {
+  foreach_go_module __build_test_runner_for_module
+}
+
+function __build_test_runner_for_module() {
   local failed=0
+  subheader "Build tests for $(go_mod_module_name)"
   # Run verify-codegen check
   if [[ -f ./hack/verify-codegen.sh ]]; then
     subheader "Checking autogenerated code is up-to-date"
@@ -132,18 +137,25 @@ function default_build_test_runner() {
   # verify-codegen (as md files can be auto-generated in some repos).
   (( IS_DOCUMENTATION_PR )) && return ${failed}
   # Don't merge these two lines, or return code will always be 0.
+  # Get all build tags in go code (ignore /vendor, /hack and /third_party)
+  local tags
+  tags="$(grep -I -r '// +build' . | grep -v '/vendor/' | \
+    grep -v '/hack/' | \
+    grep -v '/third_party' | \
+    cut -f3 -d' ' | \
+    tr ',' '\n' | \
+    sort | uniq | \
+    grep -v '^!' | \
+    paste -s -d, /dev/stdin)"
   local go_pkg_dirs
-  go_pkg_dirs="$(go list ./...)" || return 1
-  # Skip build test if there is no go code
-  [[ -z "${go_pkg_dirs}" ]] && return ${failed}
+  go_pkg_dirs="$(go list -tags "${tags}" ./...)" || return $?
+  if [[ -z "${go_pkg_dirs}" ]]; then
+    subheader "No golang code found, skipping build tests"
+    return 0
+  fi
   # Ensure all the code builds
   subheader "Checking that go code builds"
-  # Get all build tags in go code (ignore /vendor, /hack and /third_party)
-  local tags
-  tags="$(find . \
-    -path './vendor' -prune -o -path './hack' -prune -o -path './third_party' -prune \
-    -o -type f -name '*.go' -exec grep '// +build' {} + \
-    | cut -f3 -d' ' | tr ',' '\n' | uniq | sort | tr '\n' ' ')"
+
   report_build_test Build_Go \
     go test -vet=off -tags "${tags}" -exec echo ./... || failed=2
 
@@ -185,6 +197,11 @@ function run_unit_tests() {
 
 # Default unit test runner that runs all go tests in the repo.
 function default_unit_test_runner() {
+  foreach_go_module __unit_test_runner_for_module
+}
+
+function __unit_test_runner_for_module() {
+  subheader "Unit tests for $(go_mod_module_name)"
   report_go_test -short -race -count 1 ./...
 }
 
@@ -222,15 +239,15 @@ function run_integration_tests() {
 # Default integration test runner that runs all `test/e2e-*tests.sh`.
 function default_integration_test_runner() {
   local failed=0
-  find test/ ! -name "$(printf "*\n*")" -name "e2e-*tests.sh" -maxdepth 1 > tmp
-  while IFS= read -r e2e_test
-  do
-    echo "Running integration test ${e2e_test}"
-    if ! ${e2e_test}; then
-      failed=1
-      step_failed "${e2e_test}"
+
+  while read -r e2e_test; do
+    subheader "Running integration test ${e2e_test}"
+    "${e2e_test}" || failed=$?
+    if (( failed )); then
+      echo "${e2e_test} failed: $failed" >&2
+      return $failed
     fi
-  done < tmp
+  done < <(find test/ -maxdepth 1 ! -name "$(printf "*\n*")" -name "e2e-*tests.sh")
   return ${failed}
 }
 
@@ -266,16 +283,16 @@ function main() {
     git version
     echo ">> ko version"
     [[ -f /ko_version ]] && cat /ko_version || echo "unknown"
-    if [[ "${DOCKER_IN_DOCKER_ENABLED}" == "true" ]]; then
+    if [[ "${DOCKER_IN_DOCKER_ENABLED:-}" == "true" ]]; then
       echo ">> docker version"
       docker version
     fi
     if type java > /dev/null; then
       echo ">> java version"
       java -version
-      echo "JAVA_HOME: $JAVA_HOME"
+      echo "JAVA_HOME: ${JAVA_HOME:-}"
     fi
-    if type mvn > /dev/null; then
+    if command -v mvn > /dev/null; then
       echo ">> maven version"
       mvn --version
     fi
@@ -338,18 +355,18 @@ function main() {
       exit 0
     fi
     for test_to_run in "${TESTS_TO_RUN[@]}"; do
-      ${test_to_run} || { failed=1; step_failed "${test_to_run}"; }
+      ${test_to_run} || { failed=$?; step_failed "${test_to_run}"; }
     done
   fi
 
-  run_build_tests || { failed=1; step_failed "run_build_tests"; }
+  run_build_tests || { failed=$?; step_failed "run_build_tests"; }
   # If PRESUBMIT_TEST_FAIL_FAST is set to true, don't run unit tests if build tests failed
   if (( ! PRESUBMIT_TEST_FAIL_FAST )) || (( ! failed )); then
-    run_unit_tests || { failed=1; step_failed "run_unit_tests"; }
+    run_unit_tests || { failed=$?; step_failed "run_unit_tests"; }
   fi
   # If PRESUBMIT_TEST_FAIL_FAST is set to true, don't run integration tests if build/unit tests failed
   if (( ! PRESUBMIT_TEST_FAIL_FAST )) || (( ! failed )); then
-    run_integration_tests || { failed=1; step_failed "run_integration_tests"; }
+    run_integration_tests || { failed=$?; step_failed "run_integration_tests"; }
   fi
 
   exit ${failed}
diff --git a/webhook/admission_integration_test.go b/webhook/admission_integration_test.go
index caf176097..ae4305997 100644
--- a/webhook/admission_integration_test.go
+++ b/webhook/admission_integration_test.go
@@ -19,7 +19,7 @@ import (
 	"bytes"
 	"context"
 	"encoding/json"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"net/url"
 	"path"
@@ -180,7 +180,7 @@ func TestAdmissionValidResponseForResourceTLS(t *testing.T) {
 		}
 
 		defer response.Body.Close()
-		responseBody, err := ioutil.ReadAll(response.Body)
+		responseBody, err := io.ReadAll(response.Body)
 		if err != nil {
 			t.Error("Failed to read response body", err)
 			return
@@ -305,7 +305,7 @@ func TestAdmissionValidResponseForResource(t *testing.T) {
 		}
 
 		defer response.Body.Close()
-		responseBody, err := ioutil.ReadAll(response.Body)
+		responseBody, err := io.ReadAll(response.Body)
 		if err != nil {
 			t.Error("Failed to read response body", err)
 			return
@@ -434,7 +434,7 @@ func TestAdmissionInvalidResponseForResource(t *testing.T) {
 	}
 
 	defer response.Body.Close()
-	respBody, err := ioutil.ReadAll(response.Body)
+	respBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}
@@ -551,7 +551,7 @@ func TestAdmissionWarningResponseForResource(t *testing.T) {
 	}
 
 	defer response.Body.Close()
-	respBody, err := ioutil.ReadAll(response.Body)
+	respBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}
@@ -656,7 +656,7 @@ func TestAdmissionValidResponseForRequestBody(t *testing.T) {
 		}
 
 		defer response.Body.Close()
-		responseBody, err := ioutil.ReadAll(response.Body)
+		responseBody, err := io.ReadAll(response.Body)
 		if err != nil {
 			t.Error("Failed to read response body", err)
 			return
diff --git a/webhook/conversion_integration_test.go b/webhook/conversion_integration_test.go
index 56aeb4f66..3b99d0091 100644
--- a/webhook/conversion_integration_test.go
+++ b/webhook/conversion_integration_test.go
@@ -21,7 +21,7 @@ import (
 	"context"
 	"encoding/json"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"testing"
 
@@ -123,7 +123,7 @@ func TestConversionValidResponse(t *testing.T) {
 		t.Errorf("Response status code = %v, wanted %v", got, want)
 	}
 
-	responseBody, err := ioutil.ReadAll(response.Body)
+	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}
@@ -212,7 +212,7 @@ func TestConversionInvalidResponse(t *testing.T) {
 		t.Errorf("Response status code = %v, wanted %v", got, want)
 	}
 
-	responseBody, err := ioutil.ReadAll(response.Body)
+	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}
diff --git a/webhook/resourcesemantics/validation/reconcile_config_test.go b/webhook/resourcesemantics/validation/reconcile_config_test.go
index fd4c93163..d4721a819 100644
--- a/webhook/resourcesemantics/validation/reconcile_config_test.go
+++ b/webhook/resourcesemantics/validation/reconcile_config_test.go
@@ -33,6 +33,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/wait"
 	clientgotesting "k8s.io/client-go/testing"
+
 	"knative.dev/pkg/configmap"
 	"knative.dev/pkg/controller"
 	"knative.dev/pkg/ptr"
@@ -79,6 +80,13 @@ func TestReconcile(t *testing.T) {
 
 	// These are the rules we expect given the context of "handlers".
 	expectedRules := []admissionregistrationv1.RuleWithOperations{{
+		Operations: []admissionregistrationv1.OperationType{"CREATE", "DELETE", "UPDATE"},
+		Rule: admissionregistrationv1.Rule{
+			APIGroups:   []string{"pkg.knative.dev"},
+			APIVersions: []string{"v1alpha1"},
+			Resources:   []string{"callbackresources", "callbackresources/status"},
+		},
+	}, {
 		Operations: []admissionregistrationv1.OperationType{"CREATE", "UPDATE", "DELETE"},
 		Rule: admissionregistrationv1.Rule{
 			APIGroups:   []string{"pkg.knative.dev"},
@@ -420,7 +428,8 @@ func TestReconcile(t *testing.T) {
 			},
 			path: path,
 
-			handlers: handlers,
+			handlers:  handlers,
+			callbacks: callbacks,
 
 			client:       kubeclient.Get(ctx),
 			vwhlister:    listers.GetValidatingWebhookConfigurationLister(),
diff --git a/webhook/resourcesemantics/validation/validation_admit_test.go b/webhook/resourcesemantics/validation/validation_admit_test.go
index 1a1bf51eb..1af76ae1f 100644
--- a/webhook/resourcesemantics/validation/validation_admit_test.go
+++ b/webhook/resourcesemantics/validation/validation_admit_test.go
@@ -38,6 +38,7 @@ import (
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apimachinery/pkg/util/wait"
 	fakekubeclientset "k8s.io/client-go/kubernetes/fake"
+
 	"knative.dev/pkg/apis"
 	"knative.dev/pkg/system"
 	"knative.dev/pkg/webhook"
@@ -93,6 +94,11 @@ var (
 			Version: "v1beta1",
 			Kind:    "Resource",
 		}: NewCallback(resourceCallback, webhook.Create, webhook.Update, webhook.Delete),
+		{
+			Group:   "pkg.knative.dev",
+			Version: "v1alpha1",
+			Kind:    "CallbackResource",
+		}: NewCallback(resourceCallback, webhook.Create, webhook.Update, webhook.Delete),
 	}
 	initialResourceWebhook = &admissionregistrationv1.ValidatingWebhookConfiguration{
 		ObjectMeta: metav1.ObjectMeta{
diff --git a/webhook/webhook_integration_test.go b/webhook/webhook_integration_test.go
index fd5b0626a..b81b2f4eb 100644
--- a/webhook/webhook_integration_test.go
+++ b/webhook/webhook_integration_test.go
@@ -19,7 +19,7 @@ package webhook
 import (
 	"context"
 	"fmt"
-	"io/ioutil"
+	"io"
 	"net/http"
 	"strings"
 	"testing"
@@ -91,7 +91,7 @@ func TestMissingContentType(t *testing.T) {
 	}
 
 	defer response.Body.Close()
-	responseBody, err := ioutil.ReadAll(response.Body)
+	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}
@@ -147,7 +147,7 @@ func testEmptyRequestBody(t *testing.T, controller interface{}) {
 	}
 	defer response.Body.Close()
 
-	responseBody, err := ioutil.ReadAll(response.Body)
+	responseBody, err := io.ReadAll(response.Body)
 	if err != nil {
 		t.Fatal("Failed to read response body", err)
 	}