Repository: kubernetes-sigs/node-feature-discovery
Pull request #1755 (2024-07-03 14:18:56)

Use worker DS OwnerReference for NF's

netlify[bot]: ### <span aria-hidden="true">👷</span> Deploy Preview for *kubernetes-sigs-nfd* processing.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | dc064c802ed4715ff0331374dcae7ed2f50c1f57 |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kubernetes-sigs-nfd/deploys/66855dd318d6e60008158505 |
ArangoGutierrez: /assign @marquiz 
k8s-ci-robot: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1755#pullrequestreview-2158431110" title="Approved">adrianchiris</a>*, *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1755#" title="Author self-approved">ArangoGutierrez</a>*

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

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/kubernetes-sigs/node-feature-discovery/blob/master/OWNERS)~~ [ArangoGutierrez]

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/README.md b/README.md
index 52d318d5c..219d6cba0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # Node Feature Discovery
 
-[![Go Report Card](https://goreportcard.com/badge/sigs.k8s.io/node-feature-discovery)](https://goreportcard.com/report/sigs.k8s.io/node-feature-discovery)
+[![Go Report Card](https://goreportcard.com/badge/github.com/kubernetes-sigs/node-feature-discovery)](https://goreportcard.com/report/github.com/kubernetes-sigs/node-feature-discovery)
 [![Prow Build](https://prow.k8s.io/badge.svg?jobs=post-node-feature-discovery-push-images)](https://prow.k8s.io/job-history/gs/kubernetes-jenkins/logs/post-node-feature-discovery-push-images)
 [![Prow E2E-Test](https://prow.k8s.io/badge.svg?jobs=postsubmit-node-feature-discovery-e2e-test)](https://prow.k8s.io/job-history/gs/kubernetes-jenkins/logs/postsubmit-node-feature-discovery-e2e-test)
 
@@ -12,7 +12,7 @@ features and system configuration!
 #### Quick-start – the short-short version
 
 ```bash
-$ kubectl apply -k https://github.com/kubernetes-sigs/node-feature-discovery/deployment/overlays/default?ref=v0.16.1
+$ kubectl apply -k https://github.com/kubernetes-sigs/node-feature-discovery/deployment/overlays/default?ref=v0.16.0
   namespace/node-feature-discovery created
   customresourcedefinition.apiextensions.k8s.io/nodefeaturerules.nfd.k8s-sigs.io created
   customresourcedefinition.apiextensions.k8s.io/nodefeatures.nfd.k8s-sigs.io created
diff --git a/cloudbuild.yaml b/cloudbuild.yaml
index da6d0d2bf..c975b0e85 100644
--- a/cloudbuild.yaml
+++ b/cloudbuild.yaml
@@ -1,4 +1,4 @@
-timeout: 3000s
+timeout: 2000s
 steps:
   - name: gcr.io/k8s-staging-test-infra/gcb-docker-gcloud:v20211118-2f2d816b90
     entrypoint: scripts/test-infra/push-image.sh
diff --git a/cmd/nfd-worker/main.go b/cmd/nfd-worker/main.go
index 7d41aa562..20a259381 100644
--- a/cmd/nfd-worker/main.go
+++ b/cmd/nfd-worker/main.go
@@ -83,7 +83,7 @@ func main() {
 
 	// Get new NfdWorker instance
 	args.GrpcHealthPort = GrpcHealthPort
-	instance, err := worker.NewNfdWorker(args)
+	instance, err := worker.NewNfdWorker(worker.WithArgs(args))
 	if err != nil {
 		klog.ErrorS(err, "failed to initialize NfdWorker instance")
 		os.Exit(1)
diff --git a/deployment/base/rbac/worker-role.yaml b/deployment/base/rbac/worker-role.yaml
index 72f261e9e..2b299c797 100644
--- a/deployment/base/rbac/worker-role.yaml
+++ b/deployment/base/rbac/worker-role.yaml
@@ -11,3 +11,9 @@ rules:
   - create
   - get
   - update
+- apiGroups:
+  - ""
+  resources:
+  - pods
+  verbs:
+  - get
diff --git a/deployment/helm/node-feature-discovery/templates/master.yaml b/deployment/helm/node-feature-discovery/templates/master.yaml
index 496a443f9..3a584209e 100644
--- a/deployment/helm/node-feature-discovery/templates/master.yaml
+++ b/deployment/helm/node-feature-discovery/templates/master.yaml
@@ -115,9 +115,6 @@ spec:
             - "-feature-gates={{ $key }}={{ $value }}"
             {{- end }}
             - "-metrics={{ .Values.master.metricsPort  | default "8081" }}"
-            {{- with .Values.master.args }}
-            {{- toYaml . | nindent 12 }}
-            {{- end }}
           volumeMounts:
             {{- if .Values.tls.enable }}
             - name: nfd-master-cert
diff --git a/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml b/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
index 19dd4e192..4f4ac76c7 100644
--- a/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
+++ b/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
@@ -53,9 +53,6 @@ spec:
           {{- if .Values.gc.interval | empty | not }}
           - "-gc-interval={{ .Values.gc.interval }}"
           {{- end }}
-          {{- with .Values.gc.args }}
-          {{- toYaml . | nindent 10 }}
-          {{- end }}
         resources:
       {{- toYaml .Values.gc.resources | nindent 12 }}
         securityContext:
diff --git a/deployment/helm/node-feature-discovery/templates/role.yaml b/deployment/helm/node-feature-discovery/templates/role.yaml
index 3a872e572..52c69eb19 100644
--- a/deployment/helm/node-feature-discovery/templates/role.yaml
+++ b/deployment/helm/node-feature-discovery/templates/role.yaml
@@ -15,5 +15,10 @@ rules:
   - create
   - get
   - update
+- apiGroups:
+  - ""
+  resources:
+  - pods
+  verbs:
+  - get
 {{- end }}
-
diff --git a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
index 1fc32bbdb..1221cfd2d 100644
--- a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
+++ b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
@@ -91,9 +91,6 @@ spec:
           - "-kubelet-state-dir="
           {{- end }}
           - -metrics={{ .Values.topologyUpdater.metricsPort | default "8081"}}
-          {{- with .Values.topologyUpdater.args }}
-          {{- toYaml . | nindent 10 }}
-          {{- end }}
         ports:
           - name: metrics
             containerPort: {{ .Values.topologyUpdater.metricsPort | default "8081"}}
diff --git a/deployment/helm/node-feature-discovery/templates/worker.yaml b/deployment/helm/node-feature-discovery/templates/worker.yaml
index d775debca..f2a2419fc 100644
--- a/deployment/helm/node-feature-discovery/templates/worker.yaml
+++ b/deployment/helm/node-feature-discovery/templates/worker.yaml
@@ -85,9 +85,6 @@ spec:
         - "-feature-gates={{ $key }}={{ $value }}"
 {{- end }}
         - "-metrics={{ .Values.worker.metricsPort | default "8081"}}"
-        {{- with .Values.gc.args }}
-        {{- toYaml . | nindent 8 }}
-        {{- end }}
         ports:
           - name: metrics
             containerPort: {{ .Values.worker.metricsPort | default "8081"}}
diff --git a/deployment/helm/node-feature-discovery/values.yaml b/deployment/helm/node-feature-discovery/values.yaml
index e3574ac23..3b60627dd 100644
--- a/deployment/helm/node-feature-discovery/values.yaml
+++ b/deployment/helm/node-feature-discovery/values.yaml
@@ -20,7 +20,6 @@ priorityClassName: ""
 
 master:
   enable: true
-  args: []
   config: ### <NFD-MASTER-CONF-START-DO-NOT-REMOVE>
     # noPublish: false
     # autoDefaultNs: true
@@ -142,7 +141,6 @@ master:
 
 worker:
   enable: true
-  args: []
   config: ### <NFD-WORKER-CONF-START-DO-NOT-REMOVE>
     #core:
     #  labelWhiteList:
@@ -460,7 +458,6 @@ topologyUpdater:
 ### <NFD-TOPOLOGY-UPDATER-CONF-END-DO-NOT-REMOVE>
 
   enable: false
-  args: []
   createCRDs: false
 
   serviceAccount:
@@ -515,7 +512,6 @@ topologyUpdater:
 
 gc:
   enable: true
-  args: []
   replicaCount: 1
 
   serviceAccount:
diff --git a/docs/deployment/helm.md b/docs/deployment/helm.md
index 2c56e0e54..615d208c6 100644
--- a/docs/deployment/helm.md
+++ b/docs/deployment/helm.md
@@ -143,7 +143,6 @@ API's you need to install the prometheus operator in your cluster.
 | `master.deploymentAnnotations` | dict | {}                                      | NFD master deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
 | `master.nfdApiParallelism` | integer | 10                                      | Specifies the maximum number of concurrent node updates. |
 | `master.config`            | dict    |                                         | NFD master [configuration](../reference/master-configuration-reference) |
-| `master.args`              | array   | []                                      | Additional [command line arguments](../reference/master-commandline-reference.md) to pass to nfd-master |
 
 ### Worker pod parameters
 
@@ -167,7 +166,6 @@ API's you need to install the prometheus operator in your cluster.
 | `worker.priorityClassName`        | string |         | NFD worker pod [priority class](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/)                                                                                    |
 | `worker.annotations`              | dict   | {}      | NFD worker pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                                                                                         |
 | `worker.daemonsetAnnotations`     | dict   | {}      | NFD worker daemonset [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                                                                                  |
-| `worker.args`                     | array   | []     | Additional [command line arguments](../reference/worker-commandline-reference.md) to pass to nfd-worker |
 
 ### Topology updater parameters
 
@@ -197,7 +195,6 @@ API's you need to install the prometheus operator in your cluster.
 | `topologyUpdater.config`                      | dict   |                  | [configuration](../reference/topology-updater-configuration-reference)                                                                                                                                |
 | `topologyUpdater.podSetFingerprint`           | bool   | true             | Enables compute and report of pod fingerprint in NRT objects.                                                                                                                                         |
 | `topologyUpdater.kubeletStateDir`             | string | /var/lib/kubelet | Specifies kubelet state directory path for watching state and checkpoint files. Empty value disables kubelet state tracking.                                                                          |
-| `topologyUpdater.args`                        | array   | []              | Additional [command line arguments](../reference/topology-updater-commandline-reference.md) to pass to nfd-topology-updater |
 
 ### Garbage collector parameters
 
@@ -219,7 +216,6 @@ API's you need to install the prometheus operator in your cluster.
 | `gc.annotations`                      | dict   | {}      | Garbage collector pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                  |
 | `gc.deploymentAnnotations`            | dict   | {}      | Garbage collector deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)          |
 | `gc.affinity`                         | dict   | {}      | Garbage collector pod [affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/)    |
-| `gc.args`                             | array   | []     | Additional [command line arguments](../reference/gc-commandline-reference.md) to pass to nfd-gc |
 
 <!-- Links -->
 [rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
diff --git a/docs/deployment/index.md b/docs/deployment/index.md
index 85e525d9f..bcabda876 100644
--- a/docs/deployment/index.md
+++ b/docs/deployment/index.md
@@ -7,7 +7,7 @@ sort: 2
 # Deployment
 
 Node Feature Discovery can be deployed on any recent version of Kubernetes
-(v1.24+).
+(v1.21+).
 
 See [Image variants](image-variants.md) for description of the different NFD
 container images available.
diff --git a/docs/deployment/kustomize.md b/docs/deployment/kustomize.md
index 18d03ec77..76e561469 100644
--- a/docs/deployment/kustomize.md
+++ b/docs/deployment/kustomize.md
@@ -20,7 +20,7 @@ deploy NFD. Customization of the deployment is done by maintaining
 declarative overlays on top of the base overlays in NFD.
 
 To follow the deployment instructions here,
-[kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl) v1.24 or
+[kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl) v1.21 or
 later is required.
 
 The kustomize overlays provided in the repo can be used directly:
diff --git a/docs/developer-guide/index.md b/docs/developer-guide/index.md
index 94ab5b699..86a42aef8 100644
--- a/docs/developer-guide/index.md
+++ b/docs/developer-guide/index.md
@@ -238,6 +238,13 @@ the Kubernetes API, pass the `-no-publish` flag to nfd-topology-updater.
 > run stand-alone directly with `docker run`. See
 > the [template spec](https://github.com/kubernetes-sigs/node-feature-discovery/blob/{{site.release}}/deployment/components/topology-updater/topologyupdater-mounts.yaml)
 > for up-to-date information about the required volume mounts.
+>
+> [PodResource API][podresource-api] is a prerequisite for
+> nfd-topology-updater.  Preceding Kubernetes v1.23, the `kubelet` must be
+> started with the following flag:
+> `--feature-gates=KubeletPodResourcesGetAllocatable=true`. Starting
+> Kubernetes v1.23, the `GetAllocatableResources` is enabled by default through
+> `KubeletPodResourcesGetAllocatable` [feature gate][feature-gate].
 
 ## Running with Tilt
 
diff --git a/docs/get-started/introduction.md b/docs/get-started/introduction.md
index 5b6f6288d..d3c2ea606 100644
--- a/docs/get-started/introduction.md
+++ b/docs/get-started/introduction.md
@@ -19,7 +19,7 @@ This software enables node feature discovery for Kubernetes. It detects
 hardware features available on each node in a Kubernetes cluster, and
 advertises those features using node labels and optionally node extended
 resources, annotations and node taints. Node Feature Discovery is compatible
-with any recent version of Kubernetes (v1.24+).
+with any recent version of Kubernetes (v1.21+).
 
 NFD consists of four software components:
 
diff --git a/docs/reference/versions.md b/docs/reference/versions.md
index 8754eaf39..9e9d36bca 100644
--- a/docs/reference/versions.md
+++ b/docs/reference/versions.md
@@ -37,7 +37,3 @@ in **X+2**.
 
 The same policy (support for 1 release after deprecation) also applies to Helm
 chart parameters.
-
-## Kubernetes compatibility
-
-Node Feature Discovery is compatible with Kubernetes v1.24 and later.
diff --git a/docs/usage/features.md b/docs/usage/features.md
index 93ffb2f4b..5434c5b0d 100644
--- a/docs/usage/features.md
+++ b/docs/usage/features.md
@@ -106,7 +106,6 @@ configuration options for details.
 | AVX512VPOPCNTDQ    | AVX-512 vector population count doubleword and quadword |
 | AVXNECONVERT       | AVX-NE-CONVERT instructions |
 | AVXVNNIINT8        | AVX-VNNI-INT8 instructions |
-| AVXVNNIINT16       | AVX-VNNI-INT16 instructions |
 | CMPCCXADD          | CMPCCXADD instructions |
 | ENQCMD             | Enqueue Command |
 | GFNI               | Galois Field New Instructions |
diff --git a/go.mod b/go.mod
index 4b188d46e..2c54db5ba 100644
--- a/go.mod
+++ b/go.mod
@@ -20,7 +20,7 @@ require (
 	github.com/stretchr/testify v1.9.0
 	github.com/vektra/errors v0.0.0-20140903201135-c64d83aba85a
 	golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3
-	golang.org/x/net v0.27.0
+	golang.org/x/net v0.26.0
 	golang.org/x/time v0.5.0
 	google.golang.org/grpc v1.63.2
 	google.golang.org/protobuf v1.34.2
@@ -120,12 +120,12 @@ require (
 	go.opentelemetry.io/proto/otlp v1.0.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.26.0 // indirect
-	golang.org/x/crypto v0.25.0 // indirect
+	golang.org/x/crypto v0.24.0 // indirect
 	golang.org/x/mod v0.17.0 // indirect
 	golang.org/x/oauth2 v0.17.0 // indirect
 	golang.org/x/sync v0.7.0 // indirect
-	golang.org/x/sys v0.22.0 // indirect
-	golang.org/x/term v0.22.0 // indirect
+	golang.org/x/sys v0.21.0 // indirect
+	golang.org/x/term v0.21.0 // indirect
 	golang.org/x/text v0.16.0 // indirect
 	golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect
 	google.golang.org/appengine v1.6.8 // indirect
diff --git a/go.sum b/go.sum
index a0a4491eb..d67890478 100644
--- a/go.sum
+++ b/go.sum
@@ -305,8 +305,8 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
 golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
-golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
+golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
+golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
 golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
 golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
@@ -320,8 +320,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
-golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
+golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
+golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
 golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
 golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -340,12 +340,12 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
-golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
+golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
-golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
+golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
+golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go
index b9a1de156..64bf5c680 100644
--- a/pkg/nfd-master/nfd-master.go
+++ b/pkg/nfd-master/nfd-master.go
@@ -1059,7 +1059,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli k8sclient.Interface, node *corev1.No
 // nodeFeatureRule custom resorce. If empty list of taints is passed, currently
 // NFD owned taints and annotations are removed from the node.
 func setTaints(cli k8sclient.Interface, taints []corev1.Taint, node *corev1.Node) error {
-	// De-serialize the taints annotation into corev1.Taint type for comparison below.
+	// De-serialize the taints annotation into corev1.Taint type for comparision below.
 	var err error
 	oldTaints := []corev1.Taint{}
 	if val, ok := node.Annotations[nfdv1alpha1.NodeTaintsAnnotation]; ok {
@@ -1482,7 +1482,7 @@ func stringToNsNames(cslist, ns string) []string {
 	return names
 }
 
-// Separate denied namespaces into two lists:
+// Seperate denied namespaces into two lists:
 // one contains wildcard namespaces the other contains normal namespaces
 func preProcessDeniedNamespaces(deniedNs map[string]struct{}) (normalDeniedNs map[string]struct{}, wildcardDeniedNs map[string]struct{}) {
 	normalDeniedNs = map[string]struct{}{}
diff --git a/pkg/nfd-worker/nfd-worker.go b/pkg/nfd-worker/nfd-worker.go
index 1c728771d..b9bb87f60 100644
--- a/pkg/nfd-worker/nfd-worker.go
+++ b/pkg/nfd-worker/nfd-worker.go
@@ -39,6 +39,7 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/apimachinery/pkg/util/validation"
+	k8sclient "k8s.io/client-go/kubernetes"
 	"k8s.io/klog/v2"
 	klogutils "sigs.k8s.io/node-feature-discovery/pkg/utils/klog"
 	"sigs.k8s.io/yaml"
@@ -130,10 +131,12 @@ type nfdWorker struct {
 	kubernetesNamespace string
 	grpcClient          pb.LabelerClient
 	healthServer        *grpc.Server
+	k8sClient           k8sclient.Interface
 	nfdClient           *nfdclient.Clientset
 	stop                chan struct{} // channel for signaling stop
 	featureSources      []source.FeatureSource
 	labelSources        []source.LabelSource
+	ownerReference      []metav1.OwnerReference
 }
 
 // This ticker can represent infinite and normal intervals.
@@ -141,30 +144,70 @@ type infiniteTicker struct {
 	*time.Ticker
 }
 
+// NfdWorkerOption sets properties of the NfdWorker instance.
+type NfdWorkerOption interface {
+	apply(*nfdWorker)
+}
+
+// WithArgs is used for passing settings from command line arguments.
+func WithArgs(args *Args) NfdWorkerOption {
+	return &nfdMWorkerOpt{f: func(n *nfdWorker) { n.args = *args }}
+}
+
+// WithKuberneteClient forces to use the given kubernetes client, without
+// initializing one from kubeconfig.
+func WithKubernetesClient(cli k8sclient.Interface) NfdWorkerOption {
+	return &nfdMWorkerOpt{f: func(n *nfdWorker) { n.k8sClient = cli }}
+}
+
+type nfdMWorkerOpt struct {
+	f func(*nfdWorker)
+}
+
+func (f *nfdMWorkerOpt) apply(n *nfdWorker) {
+	f.f(n)
+}
+
 // NewNfdWorker creates new NfdWorker instance.
-func NewNfdWorker(args *Args) (NfdWorker, error) {
+func NewNfdWorker(opts ...NfdWorkerOption) (NfdWorker, error) {
 	nfd := &nfdWorker{
-		args:                *args,
 		config:              &NFDConfig{},
 		kubernetesNamespace: utils.GetKubernetesNamespace(),
 		stop:                make(chan struct{}),
 	}
 
+	for _, o := range opts {
+		o.apply(nfd)
+	}
+
 	// Check TLS related args
-	if args.CertFile != "" || args.KeyFile != "" || args.CaFile != "" {
-		if args.CertFile == "" {
+	if nfd.args.CertFile != "" || nfd.args.KeyFile != "" || nfd.args.CaFile != "" {
+		if nfd.args.CertFile == "" {
 			return nfd, fmt.Errorf("-cert-file needs to be specified alongside -key-file and -ca-file")
 		}
-		if args.KeyFile == "" {
+		if nfd.args.KeyFile == "" {
 			return nfd, fmt.Errorf("-key-file needs to be specified alongside -cert-file and -ca-file")
 		}
-		if args.CaFile == "" {
+		if nfd.args.CaFile == "" {
 			return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
 		}
 	}
 
-	if args.ConfigFile != "" {
-		nfd.configFilePath = filepath.Clean(args.ConfigFile)
+	if nfd.args.ConfigFile != "" {
+		nfd.configFilePath = filepath.Clean(nfd.args.ConfigFile)
+	}
+
+	// k8sClient might've been set via opts by tests
+	if nfd.k8sClient == nil {
+		kubeconfig, err := utils.GetKubeconfig(nfd.args.Kubeconfig)
+		if err != nil {
+			return nfd, err
+		}
+		cli, err := k8sclient.NewForConfig(kubeconfig)
+		if err != nil {
+			return nfd, err
+		}
+		nfd.k8sClient = cli
 	}
 
 	return nfd, nil
@@ -271,6 +314,37 @@ func (w *nfdWorker) Run() error {
 	labelTrigger.Reset(w.config.Core.SleepInterval.Duration)
 	defer labelTrigger.Stop()
 
+	// Create owner ref
+	ownerReference := []metav1.OwnerReference{}
+	// Get pod owner reference
+	podName := os.Getenv("POD_NAME")
+
+	// Add pod owner reference if it exists
+	if podName != "" {
+		if selfPod, err := w.k8sClient.CoreV1().Pods(w.kubernetesNamespace).Get(context.TODO(), podName, metav1.GetOptions{}); err != nil {
+			klog.ErrorS(err, "failed to get self pod, cannot inherit ownerReference for NodeFeature")
+			return err
+		} else {
+			ownerReference = append(ownerReference, selfPod.OwnerReferences...)
+		}
+
+		podUID := os.Getenv("POD_UID")
+		if podUID != "" {
+			ownerReference = append(ownerReference, metav1.OwnerReference{
+				APIVersion: "v1",
+				Kind:       "Pod",
+				Name:       podName,
+				UID:        types.UID(podUID),
+			})
+		} else {
+			klog.InfoS("Cannot append POD ownerReference to NodeFeature, POD_UID not specified")
+		}
+	} else {
+		klog.InfoS("Cannot set NodeFeature owner references, POD_NAME not specified")
+	}
+
+	w.ownerReference = ownerReference
+
 	// Register to metrics server
 	if w.args.MetricsPort > 0 {
 		m := utils.CreateMetricsServer(w.args.MetricsPort,
@@ -713,25 +787,6 @@ func (m *nfdWorker) updateNodeFeatureObject(labels Labels) error {
 
 	features := source.GetAllFeatures()
 
-	// Create owner ref
-	ownerRefs := []metav1.OwnerReference{}
-	podName := os.Getenv("POD_NAME")
-	podUID := os.Getenv("POD_UID")
-	if podName != "" && podUID != "" {
-		isTrue := true
-		ownerRefs = []metav1.OwnerReference{
-			{
-				APIVersion: "v1",
-				Kind:       "Pod",
-				Name:       podName,
-				UID:        types.UID(podUID),
-				Controller: &isTrue,
-			},
-		}
-	} else {
-		klog.InfoS("Cannot set NodeFeature owner reference, POD_NAME and/or POD_UID not specified")
-	}
-
 	// TODO: we could implement some simple caching of the object, only get it
 	// every 10 minutes or so because nobody else should really be modifying it
 	if nfr, err := cli.NfdV1alpha1().NodeFeatures(namespace).Get(context.TODO(), nodename, metav1.GetOptions{}); errors.IsNotFound(err) {
@@ -740,7 +795,7 @@ func (m *nfdWorker) updateNodeFeatureObject(labels Labels) error {
 				Name:            nodename,
 				Annotations:     map[string]string{nfdv1alpha1.WorkerVersionAnnotation: version.Get()},
 				Labels:          map[string]string{nfdv1alpha1.NodeFeatureObjNodeNameLabel: nodename},
-				OwnerReferences: ownerRefs,
+				OwnerReferences: m.ownerReference,
 			},
 			Spec: nfdv1alpha1.NodeFeatureSpec{
 				Features: *features,
@@ -761,7 +816,7 @@ func (m *nfdWorker) updateNodeFeatureObject(labels Labels) error {
 		nfrUpdated := nfr.DeepCopy()
 		nfrUpdated.Annotations = map[string]string{nfdv1alpha1.WorkerVersionAnnotation: version.Get()}
 		nfrUpdated.Labels = map[string]string{nfdv1alpha1.NodeFeatureObjNodeNameLabel: nodename}
-		nfrUpdated.OwnerReferences = ownerRefs
+		nfrUpdated.OwnerReferences = m.ownerReference
 		nfrUpdated.Spec = nfdv1alpha1.NodeFeatureSpec{
 			Features: *features,
 			Labels:   labels,
diff --git a/source/custom/static_features.go b/source/custom/static_features.go
index 9357e7330..bcb91d3b8 100644
--- a/source/custom/static_features.go
+++ b/source/custom/static_features.go
@@ -24,7 +24,7 @@ import (
 // e.g RMDA related features. NFD configuration file may extend these custom features by adding rules.
 func getStaticRules() []nfdv1alpha1.Rule {
 	return []nfdv1alpha1.Rule{
-		{
+		nfdv1alpha1.Rule{
 			Name:   "RDMA capable static rule",
 			Labels: map[string]string{"rdma.capable": "true"},
 			MatchFeatures: nfdv1alpha1.FeatureMatcher{
@@ -38,7 +38,7 @@ func getStaticRules() []nfdv1alpha1.Rule {
 				},
 			},
 		},
-		{
+		nfdv1alpha1.Rule{
 			Name:   "RDMA available static rule",
 			Labels: map[string]string{"rdma.available": "true"},
 			MatchFeatures: nfdv1alpha1.FeatureMatcher{
diff --git a/source/fake/fake.go b/source/fake/fake.go
index ea577d94f..573fdb862 100644
--- a/source/fake/fake.go
+++ b/source/fake/fake.go
@@ -62,20 +62,20 @@ func newDefaultConfig() *Config {
 			"attr_3": "10",
 		},
 		InstanceFeatures: []FakeInstance{
-			{
+			FakeInstance{
 				"name":   "instance_1",
 				"attr_1": "true",
 				"attr_2": "false",
 				"attr_3": "10",
 				"attr_4": "foobar",
 			},
-			{
+			FakeInstance{
 				"name":   "instance_2",
 				"attr_1": "true",
 				"attr_2": "true",
 				"attr_3": "100",
 			},
-			{
+			FakeInstance{
 				"name": "instance_3",
 			},
 		},


Test patch:
diff --git a/api/nfd/v1alpha1/feature_test.go b/api/nfd/v1alpha1/feature_test.go
index 9d2a68b40..e73c4d5db 100644
--- a/api/nfd/v1alpha1/feature_test.go
+++ b/api/nfd/v1alpha1/feature_test.go
@@ -146,13 +146,13 @@ func TestFeature(t *testing.T) {
 	f2.Instances["dom.inst"] = NewInstanceFeatures(*NewInstanceFeature(map[string]string{"a1": "v3.1", "a3": "v3.3"}))
 	f2.MergeInto(&f)
 	expectedFeatures = *NewFeatures()
-	expectedFeatures.Flags["dom.flag"] = FlagFeatureSet{Elements: map[string]Nil{"k1": {}, "k2": {}, "k3": {}}}
+	expectedFeatures.Flags["dom.flag"] = FlagFeatureSet{Elements: map[string]Nil{"k1": Nil{}, "k2": Nil{}, "k3": Nil{}}}
 	expectedFeatures.Attributes["dom.attr"] = AttributeFeatureSet{Elements: map[string]string{"k1": "v1.override", "k2": "v2"}}
 	expectedFeatures.Instances["dom.inst"] = InstanceFeatureSet{
 		Elements: []InstanceFeature{
-			{Attributes: map[string]string{"a1": "v1.1", "a2": "v1.2"}},
-			{Attributes: map[string]string{"a1": "v2.1", "a2": "v2.2"}},
-			{Attributes: map[string]string{"a1": "v3.1", "a3": "v3.3"}},
+			InstanceFeature{Attributes: map[string]string{"a1": "v1.1", "a2": "v1.2"}},
+			InstanceFeature{Attributes: map[string]string{"a1": "v2.1", "a2": "v2.2"}},
+			InstanceFeature{Attributes: map[string]string{"a1": "v3.1", "a3": "v3.3"}},
 		},
 	}
 	assert.Equal(t, expectedFeatures, f)
@@ -184,7 +184,7 @@ func TestFeatureSpec(t *testing.T) {
 
 	expectedFeatures.Labels["l1"] = "v1.override"
 	expectedFeatures.Labels["l3"] = "v3"
-	expectedFeatures.Features.Flags["dom.flag2"] = FlagFeatureSet{Elements: map[string]Nil{"k3": {}}}
+	expectedFeatures.Features.Flags["dom.flag2"] = FlagFeatureSet{Elements: map[string]Nil{"k3": Nil{}}}
 
 	f2.MergeInto(&f)
 	assert.Equal(t, expectedFeatures, f)
diff --git a/pkg/apis/nfd/nodefeaturerule/expression-api_test.go b/pkg/apis/nfd/nodefeaturerule/expression-api_test.go
index c6f778159..ba15e2d94 100644
--- a/pkg/apis/nfd/nodefeaturerule/expression-api_test.go
+++ b/pkg/apis/nfd/nodefeaturerule/expression-api_test.go
@@ -213,14 +213,14 @@ func TestMatchInstances(t *testing.T) {
 
 		{name: "2", input: []I{}, output: O{}, result: assert.False, err: assert.Nil}, // zero instances -> false
 
-		{name: "3", input: []I{{Attributes: A{}}}, output: O{A{}}, result: assert.True, err: assert.Nil}, // one "empty" instance
+		{name: "3", input: []I{I{Attributes: A{}}}, output: O{A{}}, result: assert.True, err: assert.Nil}, // one "empty" instance
 
 		{name: "4",
 			mes: `
 foo: { op: Exists }
 bar: { op: Lt, value: ["10"] }
 `,
-			input:  []I{{Attributes: A{"foo": "1"}}, {Attributes: A{"bar": "1"}}},
+			input:  []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "1"}}},
 			output: O{},
 			result: assert.False, err: assert.Nil},
 
@@ -229,7 +229,7 @@ bar: { op: Lt, value: ["10"] }
 foo: { op: Exists }
 bar: { op: Lt, value: ["10"] }
 `,
-			input:  []I{{Attributes: A{"foo": "1"}}, {Attributes: A{"foo": "2", "bar": "1"}}},
+			input:  []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"foo": "2", "bar": "1"}}},
 			output: O{A{"foo": "2", "bar": "1"}},
 			result: assert.True, err: assert.Nil},
 
@@ -237,7 +237,7 @@ bar: { op: Lt, value: ["10"] }
 			mes: `
 bar: { op: Lt, value: ["10"] }
 `,
-			input:  []I{{Attributes: A{"foo": "1"}}, {Attributes: A{"bar": "0x1"}}},
+			input:  []I{I{Attributes: A{"foo": "1"}}, I{Attributes: A{"bar": "0x1"}}},
 			result: assert.False, err: assert.NotNil},
 	}
 
diff --git a/pkg/nfd-worker/nfd-worker-internal_test.go b/pkg/nfd-worker/nfd-worker-internal_test.go
index 101177684..067653d64 100644
--- a/pkg/nfd-worker/nfd-worker-internal_test.go
+++ b/pkg/nfd-worker/nfd-worker-internal_test.go
@@ -27,6 +27,7 @@ import (
 	. "github.com/smartystreets/goconvey/convey"
 	"github.com/stretchr/testify/mock"
 	"github.com/vektra/errors"
+	fakeclient "k8s.io/client-go/kubernetes/fake"
 
 	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/labeler"
@@ -97,7 +98,8 @@ func makeFakeFeatures(names []string) (source.FeatureLabels, Labels) {
 
 func TestConfigParse(t *testing.T) {
 	Convey("When parsing configuration", t, func() {
-		w, err := NewNfdWorker(&Args{})
+		w, err := NewNfdWorker(WithArgs(&Args{}),
+			WithKubernetesClient(fakeclient.NewSimpleClientset()))
 		So(err, ShouldBeNil)
 		worker := w.(*nfdWorker)
 		overrides := `{"core": {"labelSources": ["fake"],"noPublish": true},"sources": {"cpu": {"cpuid": {"attributeBlacklist": ["foo","bar"]}}}}`
@@ -222,13 +224,13 @@ core:
 `)
 
 		noPublish := true
-		w, err := NewNfdWorker(&Args{
+		w, err := NewNfdWorker(WithArgs(&Args{
 			ConfigFile: configFile,
 			Overrides: ConfigOverrideArgs{
 				FeatureSources: &utils.StringSliceVal{"fake"},
 				LabelSources:   &utils.StringSliceVal{"fake"},
 				NoPublish:      &noPublish},
-		})
+		}), WithKubernetesClient(fakeclient.NewSimpleClientset()))
 		So(err, ShouldBeNil)
 		worker := w.(*nfdWorker)
 
@@ -307,7 +309,8 @@ func TestNewNfdWorker(t *testing.T) {
 
 		Convey("without any args specified", func() {
 			args := &Args{}
-			w, err := NewNfdWorker(args)
+			w, err := NewNfdWorker(WithArgs(args),
+				WithKubernetesClient(fakeclient.NewSimpleClientset()))
 			Convey("no error should be returned", func() {
 				So(err, ShouldBeNil)
 			})
@@ -324,7 +327,8 @@ func TestNewNfdWorker(t *testing.T) {
 			args := &Args{Overrides: ConfigOverrideArgs{
 				LabelSources:   &utils.StringSliceVal{"fake"},
 				FeatureSources: &utils.StringSliceVal{"cpu"}}}
-			w, err := NewNfdWorker(args)
+			w, err := NewNfdWorker(WithArgs(args),
+				WithKubernetesClient(fakeclient.NewSimpleClientset()))
 			Convey("no error should be returned", func() {
 				So(err, ShouldBeNil)
 			})
@@ -373,7 +377,7 @@ func TestCreateFeatureLabels(t *testing.T) {
 
 func TestAdvertiseFeatureLabels(t *testing.T) {
 	Convey("When advertising labels", t, func() {
-		w, err := NewNfdWorker(&Args{})
+		w, err := NewNfdWorker(WithArgs(&Args{}), WithKubernetesClient(fakeclient.NewSimpleClientset()))
 		So(err, ShouldBeNil)
 		worker := w.(*nfdWorker)
 
diff --git a/pkg/nfd-worker/nfd-worker_test.go b/pkg/nfd-worker/nfd-worker_test.go
index c5582d064..42fe8b71b 100644
--- a/pkg/nfd-worker/nfd-worker_test.go
+++ b/pkg/nfd-worker/nfd-worker_test.go
@@ -90,9 +90,12 @@ func teardownTest(ctx testContext) {
 func TestNewNfdWorker(t *testing.T) {
 	Convey("When initializing new NfdWorker instance", t, func() {
 		Convey("When one of -cert-file, -key-file or -ca-file is missing", func() {
-			_, err := worker.NewNfdWorker(&worker.Args{CertFile: "crt", KeyFile: "key"})
-			_, err2 := worker.NewNfdWorker(&worker.Args{KeyFile: "key", CaFile: "ca"})
-			_, err3 := worker.NewNfdWorker(&worker.Args{CertFile: "crt", CaFile: "ca"})
+			_, err := worker.NewNfdWorker(worker.WithArgs(&worker.Args{CertFile: "crt", KeyFile: "key"}),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
+			_, err2 := worker.NewNfdWorker(worker.WithArgs(&worker.Args{KeyFile: "key", CaFile: "ca"}),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
+			_, err3 := worker.NewNfdWorker(worker.WithArgs(&worker.Args{CertFile: "crt", CaFile: "ca"}),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
 			Convey("An error should be returned", func() {
 				So(err, ShouldNotBeNil)
 				So(err2, ShouldNotBeNil)
@@ -112,7 +115,8 @@ func TestRun(t *testing.T) {
 				Oneshot:   true,
 				Overrides: worker.ConfigOverrideArgs{LabelSources: &utils.StringSliceVal{"fake"}},
 			}
-			fooasdf, _ := worker.NewNfdWorker(args)
+			fooasdf, _ := worker.NewNfdWorker(worker.WithArgs(args),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
 			err := fooasdf.Run()
 			Convey("No error should be returned", func() {
 				So(err, ShouldBeNil)
@@ -141,7 +145,8 @@ func TestRunTls(t *testing.T) {
 				Oneshot:            true,
 				Overrides:          worker.ConfigOverrideArgs{LabelSources: &utils.StringSliceVal{"fake"}},
 			}
-			w, _ := worker.NewNfdWorker(&workerArgs)
+			w, _ := worker.NewNfdWorker(worker.WithArgs(&workerArgs),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
 			err := w.Run()
 			Convey("No error should be returned", func() {
 				So(err, ShouldBeNil)
diff --git a/scripts/test-infra/build-image.sh b/scripts/test-infra/build-image.sh
new file mode 100755
index 000000000..cf47b919e
--- /dev/null
+++ b/scripts/test-infra/build-image.sh
@@ -0,0 +1,4 @@
+#!/bin/bash -e
+
+# local build
+make image
diff --git a/scripts/test-infra/test-e2e-presubmit.sh b/scripts/test-infra/test-e2e-presubmit.sh
index 5f9f3583e..6905d148f 100755
--- a/scripts/test-infra/test-e2e-presubmit.sh
+++ b/scripts/test-infra/test-e2e-presubmit.sh
@@ -2,32 +2,66 @@
 set -o pipefail
 
 # Configure environment
-export KIND_VERSION="v0.23.0"
-export KIND_NODE_IMAGE="kindest/node:v1.30.2"
-export CLUSTER_NAME="nfd-e2e"
+KIND_IMAGE="kindest/node:v1.25.3"
+export IMAGE_REGISTRY="localhost:5001"
+export CLUSTER_NAME=$(git describe --tags --dirty --always)
 export KUBECONFIG="/tmp/kubeconfig_$CLUSTER_NAME"
-export IMAGE_REPO="registry.local/node-feature-discovery"
-export IMAGE_TAG_NAME=$(git describe --tags --dirty --always)
 
-# Install kind
-go install sigs.k8s.io/kind@$KIND_VERSION
+# create registry container unless it already exists
+reg_name='kind-registry'
+reg_port='5001'
+if [ "$(docker inspect -f '{{.State.Running}}' "${reg_name}" 2>/dev/null || true)" != 'true' ]; then
+  docker run \
+    -d --restart=always -p "127.0.0.1:${reg_port}:5000" --name "${reg_name}" \
+    registry:2
+fi
 
 # create a cluster with the local registry enabled in containerd
-cat <<EOF | kind create cluster --kubeconfig $KUBECONFIG --image $KIND_NODE_IMAGE --config=-
+cat <<EOF | kind create cluster --kubeconfig $KUBECONFIG --config=-
 kind: Cluster
 apiVersion: kind.x-k8s.io/v1alpha4
 name: $CLUSTER_NAME
 nodes:
 - role: control-plane
+  image: $KIND_IMAGE
 - role: worker
+  image: $KIND_IMAGE
 - role: worker
+  image: $KIND_IMAGE
+containerdConfigPatches:
+- |-
+  [plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:${reg_port}"]
+    endpoint = ["http://${reg_name}:5000"]
+EOF
+
+# connect the registry to the cluster network if not already connected
+if [ "$(docker inspect -f='{{json .NetworkSettings.Networks.kind}}' "${reg_name}")" = 'null' ]; then
+  docker network connect "kind" "${reg_name}"
+fi
+
+cat <<EOF | kubectl apply -f -
+apiVersion: v1
+kind: ConfigMap
+metadata:
+  name: local-registry-hosting
+  namespace: kube-public
+data:
+  localRegistryHosting.v1: |
+    host: "localhost:${reg_port}"
+    help: "https://kind.sigs.k8s.io/docs/user/local-registry/"
 EOF
 
 # build the local image
-make image IMAGE_REPO=$IMAGE_REPO IMAGE_TAG_NAME=$IMAGE_TAG_NAME
+make image
 
 # push the image to the local registry
-kind load docker-image $IMAGE_REPO:$IMAGE_TAG_NAME --name $CLUSTER_NAME
+make push
 
 # run the tests
-make e2e-test IMAGE_REPO=$IMAGE_REPO IMAGE_TAG_NAME=$IMAGE_TAG_NAME E2E_PULL_IF_NOT_PRESENT=true
+make e2e-test || rc=$?
+
+# clean up the environment
+kind delete cluster --kubeconfig $KUBECONFIG --name $CLUSTER_NAME
+echo "Deleting ${reg_name} container ..."
+docker rm -f "${reg_name}"
+exit $rc
diff --git a/scripts/test-infra/test-e2e.sh b/scripts/test-infra/test-e2e.sh
index 80eeaed22..8d351c23e 100755
--- a/scripts/test-infra/test-e2e.sh
+++ b/scripts/test-infra/test-e2e.sh
@@ -1,33 +1,28 @@
 #!/bin/bash -e
 
+# Install deps
+curl -o /usr/local/bin/aws-iam-authenticator -L https://github.com/kubernetes-sigs/aws-iam-authenticator/releases/download/v0.5.7/aws-iam-authenticator_0.5.7_linux_amd64
+chmod a+x /usr/local/bin/aws-iam-authenticator
+
+curl -o /usr/local/bin/kubectl -L https://dl.k8s.io/release/v1.24.0/bin/linux/amd64/kubectl
+chmod a+x /usr/local/bin/kubectl
+
+
 # Configure environment
-export KIND_VERSION="v0.23.0"
-export KIND_NODE_IMAGE="kindest/node:v1.30.2"
-export CLUSTER_NAME="nfd-e2e"
 export KUBECONFIG=`pwd`/kubeconfig
-export IMAGE_REGISTRY="gcr.io/k8s-staging-nfd"
+export E2E_TEST_CONFIG=`pwd`/e2e-test-config
 export E2E_TEST_FULL_IMAGE=true
 
-# Install kind
-go install sigs.k8s.io/kind@$KIND_VERSION
+echo "$KUBECONFIG_DATA" > "$KUBECONFIG"
+echo "$E2E_TEST_CONFIG_DATA" > "$E2E_TEST_CONFIG"
 
-# create a cluster with the local registry enabled in containerd
-cat <<EOF | kind create cluster --kubeconfig $KUBECONFIG --image $KIND_NODE_IMAGE --config=-
-kind: Cluster
-apiVersion: kind.x-k8s.io/v1alpha4
-name: $CLUSTER_NAME
-nodes:
-- role: control-plane
-- role: worker
-- role: worker
-EOF
 
 # Wait for the image to be built and published
 i=1
 while true; do
     if make poll-images; then
         break
-    elif [ $i -ge 55 ]; then
+    elif [ $i -ge 35 ]; then
         echo "ERROR: too many tries when polling for image"
         exit 1
     fi
diff --git a/source/pci/pci_test.go b/source/pci/pci_test.go
index 778b961e6..c5a7e40b5 100644
--- a/source/pci/pci_test.go
+++ b/source/pci/pci_test.go
@@ -49,16 +49,16 @@ func TestPciSource(t *testing.T) {
 	// Specify expected "raw" features. These are always the same for the same
 	// mocked sysfs.
 	expectedFeatures := map[string]*nfdv1alpha1.Features{
-		"rootfs-empty": {
+		"rootfs-empty": &nfdv1alpha1.Features{
 			Flags:      map[string]nfdv1alpha1.FlagFeatureSet{},
 			Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{},
 			Instances:  map[string]nfdv1alpha1.InstanceFeatureSet{},
 		},
-		"rootfs-1": {
+		"rootfs-1": &nfdv1alpha1.Features{
 			Flags:      map[string]nfdv1alpha1.FlagFeatureSet{},
 			Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{},
 			Instances: map[string]nfdv1alpha1.InstanceFeatureSet{
-				"device": {
+				"device": nfdv1alpha1.InstanceFeatureSet{
 					Elements: []nfdv1alpha1.InstanceFeature{
 						{
 							Attributes: map[string]string{
diff --git a/test/e2e/utils/rbac.go b/test/e2e/utils/rbac.go
index 902609eec..67470e331 100644
--- a/test/e2e/utils/rbac.go
+++ b/test/e2e/utils/rbac.go
@@ -224,6 +224,11 @@ func createRoleWorker(ctx context.Context, cs clientset.Interface, ns string) (*
 				Resources: []string{"nodefeatures"},
 				Verbs:     []string{"create", "get", "update"},
 			},
+			{
+				APIGroups: []string{""},
+				Resources: []string{"pods"},
+				Verbs:     []string{"get"},
+			},
 		},
 	}
 	return cs.RbacV1().Roles(ns).Update(ctx, cr, metav1.UpdateOptions{})