Repository: kubernetes-sigs/node-feature-discovery
Pull request #1659 (2024-04-06 09:03:14)

add ARMv7 support
this allows the nfd to run on arm devices like the Raspberry Pi >= 2 
with ARMv7 core or ARMv8 in 32Bit mode

see #426


k8s-ci-robot: [APPROVALNOTIFIER] This PR is **NOT APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1659#" title="Author self-approved">Links2004</a>*
**Once this PR has been reviewed and has the lgtm label**, please assign [marquiz](https://github.com/marquiz) for approval. 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=kubernetes-sigs%2Fnode-feature-discovery).

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

- **[OWNERS](https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/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":["marquiz"]} -->
linux-foundation-easycla[bot]:  <a href="https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1659/#/?version=2"><img src="https://s3.amazonaws.com/cla-project-logo-prod/cla-not-signed.svg" alt="CLA Not Signed" align="left" height="28" width="328"></a><br/><br /><ul><li><a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1659/#/?version=2' target='_blank'>:x:</a> - login: @Links2004 / name: Markus . The commit (31146cd794601caf41e5fb7fbd4a37e7bc82775d) is not authorized under a signed CLA. <a href='https://api.easycla.lfx.linuxfoundation.org/v2/repository-provider/github/sign/20677499/63999998/1659/#/?version=2' target='_blank'>Please click here to be authorized</a>. For further assistance with EasyCLA, <a href='https://jira.linuxfoundation.org/servicedesk/customer/portal/4' target='_blank'>please submit a support request ticket</a>.</li></ul>
k8s-ci-robot: Welcome @Links2004! <br><br>It looks like this is your first PR to <a href='https://github.com/kubernetes-sigs/node-feature-discovery'>kubernetes-sigs/node-feature-discovery</a> 🎉. Please refer to our [pull request process documentation](https://git.k8s.io/community/contributors/guide/pull-requests.md) to help your PR have a smooth ride to approval. <br><br>You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. [Here is the bot commands documentation](https://go.k8s.io/bot-commands). <br><br>You can also check if kubernetes-sigs/node-feature-discovery has [its own contribution guidelines](https://github.com/kubernetes-sigs/node-feature-discovery/tree/master/CONTRIBUTING.md). <br><br>You may want to refer to our [testing guide](https://git.k8s.io/community/contributors/devel/sig-testing/testing.md) if you run into trouble with your tests not passing. <br><br>If you are having difficulty getting your pull request seen, please follow the [recommended escalation practices](https://github.com/kubernetes/community/blob/master/contributors/guide/pull-requests.md#why-is-my-pull-request-not-getting-reviewed). Also, for tips and tricks in the contribution process you may want to read the [Kubernetes contributor cheat sheet](https://git.k8s.io/community/contributors/guide/contributor-cheatsheet/README.md). We want to make sure your contribution gets all the attention it needs! <br><br>Thank you, and welcome to Kubernetes. :smiley:
k8s-ci-robot: Hi @Links2004. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fnode-feature-discovery).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
</details>

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


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | 31146cd794601caf41e5fb7fbd4a37e7bc82775d |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/kubernetes-sigs-nfd/deploys/66110fd5073be300088f8a0e |
Links2004: local build via 
```
make image-all
```

is working, test image can be found here: https://github.com/users/Links2004/packages/container/node-feature-discovery/200401656?tag=v0.16.0-devel-132-g1696c658-dirty-minimal


ArangoGutierrez: /ok-to-test
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/1659#pullrequestreview-1986116469" title="Approved">ArangoGutierrez</a>*, *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1659#" title="Author self-approved">Links2004</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":[]} -->
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 30.52%. Comparing base [(`1696c65`)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/commit/1696c6589edf47d0e47ec5b80acabef7c390e98c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs) to head [(`31146cd`)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs).
> Report is 18 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659/graphs/tree.svg?width=650&height=150&src=pr&token=mEZgtJQ1vg&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)

```diff
@@            Coverage Diff             @@
##           master    #1659      +/-   ##
==========================================
- Coverage   30.54%   30.52%   -0.03%     
==========================================
  Files         102      102              
  Lines        9628     9646      +18     
==========================================
+ Hits         2941     2944       +3     
- Misses       6416     6431      +15     
  Partials      271      271              
```


[see 5 files with indirect coverage changes](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)

</details>
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 30.52%. Comparing base [(`1696c65`)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/commit/1696c6589edf47d0e47ec5b80acabef7c390e98c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs) to head [(`31146cd`)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs).
> Report is 20 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


[![Impacted file tree graph](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659/graphs/tree.svg?width=650&height=150&src=pr&token=mEZgtJQ1vg&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)

```diff
@@            Coverage Diff             @@
##           master    #1659      +/-   ##
==========================================
- Coverage   30.54%   30.52%   -0.03%     
==========================================
  Files         102      102              
  Lines        9628     9646      +18     
==========================================
+ Hits         2941     2944       +3     
- Misses       6416     6431      +15     
  Partials      271      271              
```


[see 5 files with indirect coverage changes](https://app.codecov.io/gh/kubernetes-sigs/node-feature-discovery/pull/1659/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kubernetes-sigs)

</details>
marquiz: Thanks @Links2004.

Small nits about the commit message (see [k8s commit message guidelines](https://www.kubernetes.dev/docs/guide/pull-requests/#commit-message-guidelines):
- start title with a capital letter
- have properly formatted sentences in the commit message body (start wit ha capital letter, end with a period)
- wrap lines (to 72 chars) in the message body

The build-image-cross-generic prow job takes way too much time with this. We need to do something about that 🧐 Either omit armv7 from the test job or try increasing resources of the prow job. @ArangoGutierrez WDYT?

Links2004: I can force push a new commit messages if it helps.
ArangoGutierrez: > Thanks @Links2004.
> 
> Small nits about the commit message (see [k8s commit message guidelines](https://www.kubernetes.dev/docs/guide/pull-requests/#commit-message-guidelines):
> 
> * start title with a capital letter
> * have properly formatted sentences in the commit message body (start wit ha capital letter, end with a period)
> * wrap lines (to 72 chars) in the message body
> 
> The build-image-cross-generic prow job takes way too much time with this. We need to do something about that 🧐 Either omit armv7 from the test job or try increasing resources of the prow job. @ArangoGutierrez WDYT?

Umm I agree, we should add some logic to only build armv7 during PostSubmit and not on PreSubmit
ArangoGutierrez: @Links2004 NFD as a PROW job that runs on each PR, specifically https://github.com/kubernetes/test-infra/blob/master/config/jobs/kubernetes-sigs/node-feature-discovery/node-feature-discovery-presubmits-generic.yaml#L56 builds images using buildx using the script https://github.com/kubernetes-sigs/node-feature-discovery/blob/master/scripts/test-infra/build-image-cross.sh. we do agree and accept the addition of ARMV7 , what we don't want is to add extra builds during testing time. so please make the needed changes to prevent this and future archs from being built during test time. Extra ARCHes should be for end users to build and manage on their own. 

Patch:
diff --git a/Dockerfile b/Dockerfile
index 1cbd1edcf..df6a405f2 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,7 +7,6 @@ FROM ${BUILDER_IMAGE} as builder
 
 # Get (cache) deps in a separate layer
 COPY go.mod go.sum /go/node-feature-discovery/
-COPY api/nfd/go.mod api/nfd/go.sum /go/node-feature-discovery/api/nfd/
 
 WORKDIR /go/node-feature-discovery
 
diff --git a/Makefile b/Makefile
index f77e7a9a9..12514dd3a 100644
--- a/Makefile
+++ b/Makefile
@@ -63,7 +63,7 @@ BUILD_FLAGS = -tags osusergo,netgo \
               -ldflags "-s -w -extldflags=-static -X sigs.k8s.io/node-feature-discovery/pkg/version.version=$(VERSION) -X sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath.pathPrefix=$(HOSTMOUNT_PREFIX)"
 
 # multi-arch build with buildx
-IMAGE_ALL_PLATFORMS ?= linux/amd64,linux/arm64
+IMAGE_ALL_PLATFORMS ?= linux/amd64,linux/arm64,linux/arm/v7
 
 # enable buildx
 ensure-buildx:
diff --git a/README.md b/README.md
index 176ec5ff5..2842d45e3 100644
--- a/README.md
+++ b/README.md
@@ -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.15.4
+$ kubectl apply -k https://github.com/kubernetes-sigs/node-feature-discovery/deployment/overlays/default?ref=v0.15.3
   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/api/nfd/go.mod b/api/nfd/go.mod
deleted file mode 100644
index f1e4ce1ea..000000000
--- a/api/nfd/go.mod
+++ /dev/null
@@ -1,29 +0,0 @@
-module sigs.k8s.io/node-feature-discovery/api/nfd
-
-go 1.22
-
-require (
-	github.com/gogo/protobuf v1.3.2
-	github.com/stretchr/testify v1.8.4
-	k8s.io/api v0.29.0
-	k8s.io/apimachinery v0.29.0
-)
-
-require (
-	github.com/davecgh/go-spew v1.1.1 // indirect
-	github.com/go-logr/logr v1.3.0 // indirect
-	github.com/google/gofuzz v1.2.0 // indirect
-	github.com/json-iterator/go v1.1.12 // indirect
-	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
-	github.com/modern-go/reflect2 v1.0.2 // indirect
-	github.com/pmezard/go-difflib v1.0.0 // indirect
-	golang.org/x/net v0.20.0 // indirect
-	golang.org/x/text v0.14.0 // indirect
-	gopkg.in/inf.v0 v0.9.1 // indirect
-	gopkg.in/yaml.v2 v2.4.0 // indirect
-	gopkg.in/yaml.v3 v3.0.1 // indirect
-	k8s.io/klog/v2 v2.110.1 // indirect
-	k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect
-	sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
-	sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
-)
diff --git a/api/nfd/go.sum b/api/nfd/go.sum
deleted file mode 100644
index a51fc0e40..000000000
--- a/api/nfd/go.sum
+++ /dev/null
@@ -1,91 +0,0 @@
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY=
-github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
-github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
-github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
-github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
-github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
-github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-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/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
-golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
-golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
-golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
-gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
-gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-k8s.io/api v0.29.0 h1:NiCdQMY1QOp1H8lfRyeEf8eOwV6+0xA6XEE44ohDX2A=
-k8s.io/api v0.29.0/go.mod h1:sdVmXoz2Bo/cb77Pxi71IPTSErEW32xa4aXwKH7gfBA=
-k8s.io/apimachinery v0.29.0 h1:+ACVktwyicPz0oc6MTMLwa2Pw3ouLAfAon1wPLtG48o=
-k8s.io/apimachinery v0.29.0/go.mod h1:eVBxQ/cwiJxH58eK/jd/vAk4mrxmVlnpBH5J2GbMeis=
-k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0=
-k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo=
-k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
-k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
-sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
-sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
diff --git a/cmd/nfd-master/main.go b/cmd/nfd-master/main.go
index 08a35324f..40dba0588 100644
--- a/cmd/nfd-master/main.go
+++ b/cmd/nfd-master/main.go
@@ -109,7 +109,7 @@ func main() {
 
 	// Get new NfdMaster instance
 	args.GrpcHealthPort = GrpcHealthPort
-	instance, err := master.NewNfdMaster(master.WithArgs(args))
+	instance, err := master.NewNfdMaster(args)
 	if err != nil {
 		klog.ErrorS(err, "failed to initialize NfdMaster instance")
 		os.Exit(1)
diff --git a/cmd/nfd-topology-updater/main.go b/cmd/nfd-topology-updater/main.go
index e5b77edee..f758c83c1 100644
--- a/cmd/nfd-topology-updater/main.go
+++ b/cmd/nfd-topology-updater/main.go
@@ -36,7 +36,6 @@ const (
 	// ProgramName is the canonical name of this program
 	ProgramName       = "nfd-topology-updater"
 	kubeletSecurePort = 10250
-	GrpcHealthPort    = 8082
 )
 
 var DefaultKubeletStateDir = path.Join(string(hostpath.VarDir), "lib", "kubelet")
@@ -55,7 +54,6 @@ func main() {
 	utils.ConfigureGrpcKlog()
 
 	// Get new TopologyUpdater instance
-	args.GrpcHealthPort = GrpcHealthPort
 	instance, err := topology.NewTopologyUpdater(*args, *resourcemonitorArgs)
 	if err != nil {
 		klog.ErrorS(err, "failed to initialize topology updater instance")
diff --git a/deployment/base/gc/gc.yaml b/deployment/base/gc/gc.yaml
index d97b83a7a..42591b60b 100644
--- a/deployment/base/gc/gc.yaml
+++ b/deployment/base/gc/gc.yaml
@@ -19,13 +19,6 @@ spec:
         - name: nfd-gc
           image: gcr.io/k8s-staging-nfd/node-feature-discovery:master
           imagePullPolicy: Always
-          resources:
-            limits:
-              cpu: 20m
-              memory: 1Gi
-            requests:
-              cpu: 10m
-              memory: 128Mi
           command:
             - "nfd-gc"
           ports:
diff --git a/deployment/base/master/master-deployment.yaml b/deployment/base/master/master-deployment.yaml
index e0b659d01..25313d0d6 100644
--- a/deployment/base/master/master-deployment.yaml
+++ b/deployment/base/master/master-deployment.yaml
@@ -21,13 +21,6 @@ spec:
         - name: nfd-master
           image: gcr.io/k8s-staging-nfd/node-feature-discovery:master
           imagePullPolicy: Always
-          resources:
-            limits:
-              cpu: 300m
-              memory: 4Gi
-            requests:
-              cpu: 100m
-              memory: 128Mi
           livenessProbe:
             grpc:
               port: 8082
diff --git a/deployment/base/topologyupdater-daemonset/topologyupdater-daemonset.yaml b/deployment/base/topologyupdater-daemonset/topologyupdater-daemonset.yaml
index 5d11e9b15..28abd3a4b 100644
--- a/deployment/base/topologyupdater-daemonset/topologyupdater-daemonset.yaml
+++ b/deployment/base/topologyupdater-daemonset/topologyupdater-daemonset.yaml
@@ -19,27 +19,9 @@ spec:
         - name: nfd-topology-updater
           image: gcr.io/k8s-staging-nfd/node-feature-discovery:master
           imagePullPolicy: Always
-          livenessProbe:
-            grpc:
-              port: 8082
-            initialDelaySeconds: 10
-            periodSeconds: 10
-          readinessProbe:
-            grpc:
-              port: 8082
-            initialDelaySeconds: 5
-            periodSeconds: 10
-            failureThreshold: 10
           command:
             - "nfd-topology-updater"
           args: []
-          resources:
-            limits:
-              cpu: 100m
-              memory: 60Mi
-            requests:
-              cpu: 50m
-              memory: 40Mi
           ports:
             - name: metrics
               containerPort: 8081
diff --git a/deployment/base/worker-daemonset/worker-daemonset.yaml b/deployment/base/worker-daemonset/worker-daemonset.yaml
index 955157877..c9eb432d0 100644
--- a/deployment/base/worker-daemonset/worker-daemonset.yaml
+++ b/deployment/base/worker-daemonset/worker-daemonset.yaml
@@ -32,13 +32,6 @@ spec:
             failureThreshold: 10
           command:
             - "nfd-worker"
-          resources:
-            limits:
-              cpu: 200m
-              memory: 512Mi
-            requests:
-              cpu: 5m
-              memory: 64Mi
           args:
             - "-server=nfd-master:8080"
           ports:
diff --git a/deployment/helm/node-feature-discovery/templates/prometheus.yaml b/deployment/helm/node-feature-discovery/templates/prometheus.yaml
index 3d680e24e..b9f4b4640 100644
--- a/deployment/helm/node-feature-discovery/templates/prometheus.yaml
+++ b/deployment/helm/node-feature-discovery/templates/prometheus.yaml
@@ -12,7 +12,7 @@ metadata:
 spec:
   podMetricsEndpoints:
     - honorLabels: true
-      interval: {{ .Values.prometheus.scrapeInterval }}
+      interval: 10s
       path: /metrics
       port: metrics
       scheme: http
diff --git a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
index 1221cfd2d..af62107f9 100644
--- a/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
+++ b/deployment/helm/node-feature-discovery/templates/topologyupdater.yaml
@@ -41,17 +41,6 @@ spec:
       - name: topology-updater
         image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
         imagePullPolicy: "{{ .Values.image.pullPolicy }}"
-        livenessProbe:
-          grpc:
-            port: 8082
-          initialDelaySeconds: 10
-          periodSeconds: 10
-        readinessProbe:
-          grpc:
-            port: 8082
-          initialDelaySeconds: 5
-          periodSeconds: 10
-          failureThreshold: 10
         env:
         - name: NODE_NAME
           valueFrom:
diff --git a/deployment/helm/node-feature-discovery/values.yaml b/deployment/helm/node-feature-discovery/values.yaml
index 22ac96541..77d4f8038 100644
--- a/deployment/helm/node-feature-discovery/values.yaml
+++ b/deployment/helm/node-feature-discovery/values.yaml
@@ -94,13 +94,17 @@ master:
     type: ClusterIP
     port: 8080
 
-  resources:
-    limits:
-      cpu: 300m
-      memory: 4Gi
-    requests:
-      cpu: 100m
-      memory: 128Mi
+  resources: {}
+    # We usually recommend not to specify default resources and to leave this as a conscious
+    # choice for the user. This also increases chances charts run on environments with little
+    # resources, such as Minikube. If you do want to specify resources, uncomment the following
+    # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
 
   nodeSelector: {}
 
@@ -421,13 +425,17 @@ worker:
   # Does not work on systems without /usr/src AND a read-only /usr, such as Talos
   mountUsrSrc: false
 
-  resources:
-    limits:
-      cpu: 200m
-      memory: 512Mi
-    requests:
-      cpu: 5m
-      memory: 64Mi
+  resources: {}
+    # We usually recommend not to specify default resources and to leave this as a conscious
+    # choice for the user. This also increases chances charts run on environments with little
+    # resources, such as Minikube. If you do want to specify resources, uncomment the following
+    # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
 
   nodeSelector: {}
 
@@ -475,27 +483,17 @@ topologyUpdater:
     readOnlyRootFilesystem: true
     runAsUser: 0
 
-  # livenessProbe: {}
-    ## NOTE: Currently not configurable, defaults are provided for the sake of extra documentation.
-    # grpc:
-    #   port: 8082
-    # initialDelaySeconds: 10
-    # periodSeconds: 10
-  # readinessProbe: {}
-    ## NOTE: Currently not configurable, defaults are provided for the sake of extra documentation.
-    # grpc:
-    #   port: 8082
-    # initialDelaySeconds: 5
-    # periodSeconds: 10
-    # failureThreshold: 10
-
-  resources:
-    limits:
-      cpu: 100m
-      memory: 60Mi
-    requests:
-      cpu: 50m
-      memory: 40Mi
+  resources: {}
+    # We usually recommend not to specify default resources and to leave this as a conscious
+    # choice for the user. This also increases chances charts run on environments with little
+    # resources, such as Minikube. If you do want to specify resources, uncomment the following
+    # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
 
   nodeSelector: {}
   tolerations: []
@@ -519,13 +517,17 @@ gc:
 
   podSecurityContext: {}
 
-  resources:
-    limits:
-      cpu: 20m
-      memory: 1Gi
-    requests:
-      cpu: 10m
-      memory: 128Mi
+  resources: {}
+    # We usually recommend not to specify default resources and to leave this as a conscious
+    # choice for the user. This also increases chances charts run on environments with little
+    # resources, such as Minikube. If you do want to specify resources, uncomment the following
+    # lines, adjust them as necessary, and remove the curly braces after 'resources:'.
+    # limits:
+    #   cpu: 100m
+    #   memory: 128Mi
+    # requests:
+    #   cpu: 100m
+    #   memory: 128Mi
 
   metricsPort: 8081
 
@@ -549,5 +551,4 @@ tls:
 
 prometheus:
   enable: false
-  scrapeInterval: 10s
   labels: {}
diff --git a/docs/deployment/helm.md b/docs/deployment/helm.md
index fe77c4d5e..ae3baa8b2 100644
--- a/docs/deployment/helm.md
+++ b/docs/deployment/helm.md
@@ -101,7 +101,6 @@ Chart parameters are available.
 | `featureGates.NodeFeatureAPI`| bool   | true                                    | Enable the [NodeFeature](../usage/custom-resources.md#nodefeature) CRD API for communicating node features. This will automatically disable the gRPC communication. **NOTE**: this parameter is related to the deprecated gRPC API and will be removed with it in a future release    |
 | `prometheus.enable`   | bool   | false                                   | Specifies whether to expose metrics using prometheus operator                                                                                |
 | `prometheus.labels`   | dict   | {}                                      | Specifies labels for use with the prometheus operator to control how it is selected                                                        |
-| `prometheus.scrapeInterval`    | string   | 10s                                      | Specifies the interval by which metrics are scraped                                                        |
 | `priorityClassName`   | string |                                         | The name of the PriorityClass to be used for the NFD pods. |
 
 Metrics are configured to be exposed using prometheus operator API's by
@@ -132,8 +131,8 @@ API's you need to install the prometheus operator in your cluster.
 | `master.rbac.create`       | bool    | true                                    | Specifies whether to create [RBAC][rbac] configuration for nfd-master |
 | `master.service.type`      | string  | ClusterIP                               | NFD master service type. **NOTE**: this parameter is related to the deprecated gRPC API and will be removed with it in a future release |
 | `master.service.port`      | integer | 8080                                    | NFD master service port. **NOTE**: this parameter is related to the deprecated gRPC API and will be removed with it in a future release |
-| `master.resources.limits`  | dict    | {cpu: 300m, memory: 4Gi}                | NFD master pod [resources limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
-| `master.resources.requests`| dict    | {cpu: 100m, memory: 128Mi}              | NFD master pod [resources requests](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
+| `master.resources`         | dict    | {}                                      | NFD master pod [resources management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/) |
+| `master.nodeSelector`      | dict    | {}                                      | NFD master pod [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector) |
 | `master.tolerations`       | dict    | _Scheduling to master node is disabled_ | NFD master pod [tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/) |
 | `master.annotations`       | dict    | {}                                      | NFD master pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/) |
 | `master.affinity`          | dict    |                                         | NFD master pod required [node affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/) |
@@ -156,8 +155,7 @@ API's you need to install the prometheus operator in your cluster.
 | `worker.serviceAccount.name`      | string |         | The name of the service account to use for nfd-worker. If not set and create is true, a name is generated using the fullname template (suffixed with `-worker`)                                        |
 | `worker.rbac.create`              | bool   | true    | Specifies whether to create [RBAC][rbac] configuration for nfd-worker                                                                                                                                 |
 | `worker.mountUsrSrc`              | bool   | false   | Specifies whether to allow users to mount the hostpath /user/src. Does not work on systems without /usr/src AND a read-only /usr                                                                      |
-| `worker.resources.limits`         | dict   | {cpu: 200m, memory: 512Mi}             | NFD worker pod [resources limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
-| `worker.resources.requests`       | dict   | {cpu: 5m, memory: 64Mi}              | NFD worker pod [resources requests](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
+| `worker.resources`                | dict   | {}      | NFD worker pod [resources management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)                                                                             |
 | `worker.nodeSelector`             | dict   | {}      | NFD worker pod [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector)                                                                                |
 | `worker.tolerations`              | dict   | {}      | NFD worker pod [node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)                                                                                     |
 | `worker.priorityClassName`        | string |         | NFD worker pod [priority class](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/)                                                                                    |
@@ -182,8 +180,7 @@ API's you need to install the prometheus operator in your cluster.
 | `topologyUpdater.watchNamespace`              | string | `*`              | Namespace to watch pods, `*` for all namespaces                                                                                                                                                       |
 | `topologyUpdater.podSecurityContext`          | dict   | {}               | [PodSecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) holds pod-level security attributes and common container settings |
 | `topologyUpdater.securityContext`             | dict   | {}               | Container [security settings](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container)                                                    |
-| `topologyUpdater.resources.limits`            | dict   | {cpu: 100m, memory: 60Mi}                | NFD Topology Updater pod [resources limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
-| `topologyUpdater.resources.requests`          | dict   | {cpu: 50m, memory: 40Mi}              | NFD Topology Updater pod [resources requests](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
+| `topologyUpdater.resources`                   | dict   | {}               | Topology updater pod [resources management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)                                                                           |
 | `topologyUpdater.nodeSelector`                | dict   | {}               | Topology updater pod [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector)                                                                           |
 | `topologyUpdater.tolerations`                 | dict   | {}               | Topology updater pod [node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)                                                                                |
 | `topologyUpdater.annotations`                 | dict   | {}               | Topology updater pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                                                                                    |
@@ -205,8 +202,7 @@ API's you need to install the prometheus operator in your cluster.
 | `gc.rbac.create`                      | bool   | true    | Specifies whether to create [RBAC][rbac] configuration for garbage collector                                                         |
 | `gc.interval`                         | string | 1h      | Time between periodic garbage collector runs                                                                                          |
 | `gc.podSecurityContext`               | dict   | {}      | [PodSecurityContext](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod) holds pod-level security attributes and common container settings |
-| `gc.resources.limits`                 | dict   | {cpu: 20m, memory: 1Gi}                | NFD Garbage Collector pod [resources limits](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
-| `gc.resources.requests`               | dict   | {cpu: 10m, memory: 128Mi}              | NFD Garbage Collector pod [resources requests](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#requests-and-limits) |
+| `gc.resources`                        | dict   | {}      | Garbage collector pod [resources management](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/)         |
 | `gc.metricsPort`                      | integer | 8081   | Port on which to serve Prometheus metrics                                                                                            |
 | `gc.nodeSelector`                     | dict   | {}      | Garbage collector pod [node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector)         |
 | `gc.tolerations`                      | dict   | {}      | Garbage collector pod [node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/)              |
diff --git a/go.mod b/go.mod
index d2bd3fbf0..71b7028b9 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,7 @@ go 1.22
 
 require (
 	github.com/fsnotify/fsnotify v1.7.0
+	github.com/gogo/protobuf v1.3.2
 	github.com/golang/protobuf v1.5.4
 	github.com/google/go-cmp v0.6.0
 	github.com/google/uuid v1.5.0
@@ -35,7 +36,6 @@ require (
 	k8s.io/kubernetes v1.29.0
 	k8s.io/pod-security-admission v0.29.0
 	k8s.io/utils v0.0.0-20230726121419-3b25d923346b
-	sigs.k8s.io/node-feature-discovery/api/nfd v0.0.0-00010101000000-000000000000
 	sigs.k8s.io/yaml v1.3.0
 )
 
@@ -92,7 +92,6 @@ require (
 	github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
 	github.com/godbus/dbus/v5 v5.1.0 // indirect
 	github.com/gofrs/uuid v4.4.0+incompatible // indirect
-	github.com/gogo/protobuf v1.3.2 // indirect
 	github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
 	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/mock v1.6.0 // indirect
@@ -232,5 +231,4 @@ replace (
 	k8s.io/mount-utils => k8s.io/mount-utils v0.29.0
 	k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.29.0
 	k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.29.0
-	sigs.k8s.io/node-feature-discovery/api/nfd => ./api/nfd
 )
diff --git a/hack/update_codegen.sh b/hack/update_codegen.sh
index bcd697d9f..77a530d0b 100755
--- a/hack/update_codegen.sh
+++ b/hack/update_codegen.sh
@@ -18,55 +18,51 @@ set -o errexit
 set -o nounset
 set -o pipefail
 
-TMP_VENDOR_DIR=gen-vendor
-
-function cleanup() {
-  echo "Cleaning up..."
-  rm -rf ${TMP_VENDOR_DIR}
-  rm sigs.k8s.io
-  # We need to clean up the go.mod file since code-generator adds temporary library to the go.mod file.
-  "${GO_CMD}" mod tidy
-}
-
-trap cleanup EXIT
 GO_CMD=${1:-go}
 NFD_ROOT=$(realpath $(dirname ${BASH_SOURCE[0]})/..)
 
-"${GO_CMD}" mod vendor
-
-# Go generate
-"${GO_CMD}" generate ./cmd/... ./pkg/... ./source/...
-
-# Generate CRDs
-controller-gen object crd output:crd:stdout paths=./api/... > deployment/base/nfd-crds/nfd-api-crds.yaml
-mkdir -p deployment/helm/node-feature-discovery/crds
-cp deployment/base/nfd-crds/nfd-api-crds.yaml deployment/helm/node-feature-discovery/crds
+go mod vendor
 
-# Generate clientset and informers
-mv vendor ${TMP_VENDOR_DIR}
-CODEGEN_PKG=${CODEGEN_PKG:-$(ls -d -1 ./${TMP_VENDOR_DIR}/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
+CODEGEN_PKG=${CODEGEN_PKG:-$(cd "${NFD_ROOT}"; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
 
 cd $(dirname ${BASH_SOURCE[0]})/..
 
 source "${CODEGEN_PKG}/kube_codegen.sh"
 
-# TODO: https://github.com/kubernetes/code-generator/issues/165 has been closed
-# we need to bump the version of code-generator to v0.30.0 once it's released
+# TODO: remove the workaround when the issue is solved in the code-generator
+# (https://github.com/kubernetes/code-generator/issues/165).
+# Here, we create the soft link named "sigs.k8s.io" to the parent directory of
+# node-feature-discovery to ensure the layout required by the kube_codegen.sh script.
 ln -s .. sigs.k8s.io
+trap "rm sigs.k8s.io" EXIT
+
+CODEGEN_PKG=${CODEGEN_PKG:-$(ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ../code-generator)}
+
+source "${CODEGEN_PKG}/kube_codegen.sh"
+
+go generate ./cmd/... ./pkg/... ./source/...
+
+controller-gen object crd output:crd:stdout paths=./pkg/apis/... > deployment/base/nfd-crds/nfd-api-crds.yaml
+
+mkdir -p deployment/helm/node-feature-discovery/crds
+cp deployment/base/nfd-crds/nfd-api-crds.yaml deployment/helm/node-feature-discovery/crds
 
 # Generating conversion and defaults functions
 kube::codegen::gen_helpers \
-  --input-pkg-root sigs.k8s.io/node-feature-discovery/api/nfd \
+  --input-pkg-root sigs.k8s.io/node-feature-discovery/apis \
   --output-base "${NFD_ROOT}" \
   --boilerplate ${NFD_ROOT}/hack/boilerplate.go.txt
 
-# HACK: manually patching the auto-generated code as code-generator cannot
-# properly handle deepcopy of MatchExpressionSet.
-sed s'/out = new(map\[string\]\*MatchExpression)/out = new(MatchExpressionSet)/' -i api/nfd/v1alpha1/zz_generated.deepcopy.go
-
 kube::codegen::gen_client \
-  --input-pkg-root sigs.k8s.io/node-feature-discovery/api \
-  --output-pkg-root sigs.k8s.io/node-feature-discovery/api/generated \
+  --input-pkg-root sigs.k8s.io/node-feature-discovery/pkg/apis \
+  --output-pkg-root sigs.k8s.io/node-feature-discovery/pkg/generated \
   --output-base "${NFD_ROOT}" \
   --boilerplate ${NFD_ROOT}/hack/boilerplate.go.txt \
   --with-watch
+
+# HACK: manually patching the auto-generated code as code-generator cannot
+# properly handle deepcopy of MatchExpressionSet.
+sed s'/out = new(map\[string\]\*MatchExpression)/out = new(MatchExpressionSet)/' -i pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
+
+# We need to clean up the go.mod file since code-generator adds temporary library to the go.mod file.
+"${GO_CMD}" mod tidy
\ No newline at end of file
diff --git a/api/nfd/v1alpha1/annotations_labels.go b/pkg/apis/nfd/v1alpha1/annotations_labels.go
similarity index 95%
rename from api/nfd/v1alpha1/annotations_labels.go
rename to pkg/apis/nfd/v1alpha1/annotations_labels.go
index a447f1dcd..b1f848245 100644
--- a/api/nfd/v1alpha1/annotations_labels.go
+++ b/pkg/apis/nfd/v1alpha1/annotations_labels.go
@@ -74,7 +74,4 @@ const (
 
 	// FeatureAnnotationSubNsSuffix is the suffix for allowed feature annotation sub-namespaces.
 	FeatureAnnotationSubNsSuffix = "." + FeatureAnnotationNs
-
-	// FeatureAnnotationValueSizeLimit is the maximum allowed length for the value of a feature annotation.
-	FeatureAnnotationValueSizeLimit = 1 << 10
 )
diff --git a/api/nfd/v1alpha1/doc.go b/pkg/apis/nfd/v1alpha1/doc.go
similarity index 100%
rename from api/nfd/v1alpha1/doc.go
rename to pkg/apis/nfd/v1alpha1/doc.go
diff --git a/api/nfd/v1alpha1/feature.go b/pkg/apis/nfd/v1alpha1/feature.go
similarity index 100%
rename from api/nfd/v1alpha1/feature.go
rename to pkg/apis/nfd/v1alpha1/feature.go
diff --git a/api/nfd/v1alpha1/feature_test.go b/pkg/apis/nfd/v1alpha1/feature_test.go
similarity index 100%
rename from api/nfd/v1alpha1/feature_test.go
rename to pkg/apis/nfd/v1alpha1/feature_test.go
diff --git a/api/nfd/v1alpha1/generate.sh b/pkg/apis/nfd/v1alpha1/generate.sh
similarity index 100%
rename from api/nfd/v1alpha1/generate.sh
rename to pkg/apis/nfd/v1alpha1/generate.sh
diff --git a/api/nfd/v1alpha1/generated.pb.go b/pkg/apis/nfd/v1alpha1/generated.pb.go
similarity index 100%
rename from api/nfd/v1alpha1/generated.pb.go
rename to pkg/apis/nfd/v1alpha1/generated.pb.go
diff --git a/api/nfd/v1alpha1/generated.proto b/pkg/apis/nfd/v1alpha1/generated.proto
similarity index 96%
rename from api/nfd/v1alpha1/generated.proto
rename to pkg/apis/nfd/v1alpha1/generated.proto
index dab35a16a..7c652a9ae 100644
--- a/api/nfd/v1alpha1/generated.proto
+++ b/pkg/apis/nfd/v1alpha1/generated.proto
@@ -22,7 +22,7 @@ syntax = "proto2";
 package v1alpha1;
 
 // Package-wide variables from generator "generated".
-option go_package = "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1";
+option go_package = "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1";
 
 // AttributeFeatureSet is a set of features having string value.
 //
diff --git a/pkg/apis/nfd/nodefeaturerule/expression.go b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression.go
similarity index 99%
rename from pkg/apis/nfd/nodefeaturerule/expression.go
rename to pkg/apis/nfd/v1alpha1/nodefeaturerule/expression.go
index fbc1e1e56..7438f3bb1 100644
--- a/pkg/apis/nfd/nodefeaturerule/expression.go
+++ b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression.go
@@ -26,7 +26,7 @@ import (
 	"golang.org/x/exp/maps"
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 var matchOps = map[nfdv1alpha1.MatchOp]struct{}{
diff --git a/pkg/apis/nfd/nodefeaturerule/rule.go b/pkg/apis/nfd/v1alpha1/nodefeaturerule/rule.go
similarity index 99%
rename from pkg/apis/nfd/nodefeaturerule/rule.go
rename to pkg/apis/nfd/v1alpha1/nodefeaturerule/rule.go
index 7e9f7fa71..dd436d115 100644
--- a/pkg/apis/nfd/nodefeaturerule/rule.go
+++ b/pkg/apis/nfd/v1alpha1/nodefeaturerule/rule.go
@@ -27,7 +27,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
 
diff --git a/api/nfd/v1alpha1/register.go b/pkg/apis/nfd/v1alpha1/register.go
similarity index 100%
rename from api/nfd/v1alpha1/register.go
rename to pkg/apis/nfd/v1alpha1/register.go
diff --git a/api/nfd/v1alpha1/types.go b/pkg/apis/nfd/v1alpha1/types.go
similarity index 100%
rename from api/nfd/v1alpha1/types.go
rename to pkg/apis/nfd/v1alpha1/types.go
diff --git a/api/nfd/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
similarity index 93%
rename from api/nfd/v1alpha1/zz_generated.deepcopy.go
rename to pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
index 94748d2ad..d4d4cc5de 100644
--- a/api/nfd/v1alpha1/zz_generated.deepcopy.go
+++ b/pkg/apis/nfd/v1alpha1/zz_generated.deepcopy.go
@@ -1,29 +1,12 @@
 //go:build !ignore_autogenerated
-// +build !ignore_autogenerated
 
-/*
-Copyright 2024 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.
-*/
-
-// Code generated by deepcopy-gen. DO NOT EDIT.
+// Code generated by controller-gen. DO NOT EDIT.
 
 package v1alpha1
 
 import (
-	v1 "k8s.io/api/core/v1"
-	runtime "k8s.io/apimachinery/pkg/runtime"
+	"k8s.io/api/core/v1"
+	"k8s.io/apimachinery/pkg/runtime"
 )
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
@@ -36,7 +19,6 @@ func (in *AttributeFeatureSet) DeepCopyInto(out *AttributeFeatureSet) {
 			(*out)[key] = val
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AttributeFeatureSet.
@@ -57,7 +39,6 @@ func (in FeatureMatcher) DeepCopyInto(out *FeatureMatcher) {
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
-		return
 	}
 }
 
@@ -85,7 +66,8 @@ func (in *FeatureMatcherTerm) DeepCopyInto(out *FeatureMatcherTerm) {
 				if val == nil {
 					(*out)[key] = nil
 				} else {
-					in, out := &val, &outVal
+					inVal := (*in)[key]
+					in, out := &inVal, &outVal
 					*out = new(MatchExpression)
 					(*in).DeepCopyInto(*out)
 				}
@@ -98,7 +80,6 @@ func (in *FeatureMatcherTerm) DeepCopyInto(out *FeatureMatcherTerm) {
 		*out = new(MatchExpression)
 		(*in).DeepCopyInto(*out)
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeatureMatcherTerm.
@@ -135,7 +116,6 @@ func (in *Features) DeepCopyInto(out *Features) {
 			(*out)[key] = *val.DeepCopy()
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Features.
@@ -158,7 +138,6 @@ func (in *FlagFeatureSet) DeepCopyInto(out *FlagFeatureSet) {
 			(*out)[key] = val
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlagFeatureSet.
@@ -181,7 +160,6 @@ func (in *InstanceFeature) DeepCopyInto(out *InstanceFeature) {
 			(*out)[key] = val
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceFeature.
@@ -204,7 +182,6 @@ func (in *InstanceFeatureSet) DeepCopyInto(out *InstanceFeatureSet) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceFeatureSet.
@@ -227,7 +204,6 @@ func (in *MatchAnyElem) DeepCopyInto(out *MatchAnyElem) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchAnyElem.
@@ -248,7 +224,6 @@ func (in *MatchExpression) DeepCopyInto(out *MatchExpression) {
 		*out = make(MatchValue, len(*in))
 		copy(*out, *in)
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MatchExpression.
@@ -271,13 +246,13 @@ func (in MatchExpressionSet) DeepCopyInto(out *MatchExpressionSet) {
 			if val == nil {
 				(*out)[key] = nil
 			} else {
-				in, out := &val, &outVal
+				inVal := (*in)[key]
+				in, out := &inVal, &outVal
 				*out = new(MatchExpression)
 				(*in).DeepCopyInto(*out)
 			}
 			(*out)[key] = outVal
 		}
-		return
 	}
 }
 
@@ -297,7 +272,6 @@ func (in MatchValue) DeepCopyInto(out *MatchValue) {
 		in := &in
 		*out = make(MatchValue, len(*in))
 		copy(*out, *in)
-		return
 	}
 }
 
@@ -314,7 +288,6 @@ func (in MatchValue) DeepCopy() MatchValue {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *Nil) DeepCopyInto(out *Nil) {
 	*out = *in
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Nil.
@@ -333,7 +306,6 @@ func (in *NodeFeature) DeepCopyInto(out *NodeFeature) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	in.Spec.DeepCopyInto(&out.Spec)
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeature.
@@ -366,7 +338,6 @@ func (in *NodeFeatureList) DeepCopyInto(out *NodeFeatureList) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureList.
@@ -393,7 +364,6 @@ func (in *NodeFeatureRule) DeepCopyInto(out *NodeFeatureRule) {
 	out.TypeMeta = in.TypeMeta
 	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
 	in.Spec.DeepCopyInto(&out.Spec)
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRule.
@@ -426,7 +396,6 @@ func (in *NodeFeatureRuleList) DeepCopyInto(out *NodeFeatureRuleList) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleList.
@@ -457,7 +426,6 @@ func (in *NodeFeatureRuleSpec) DeepCopyInto(out *NodeFeatureRuleSpec) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleSpec.
@@ -481,7 +449,6 @@ func (in *NodeFeatureSpec) DeepCopyInto(out *NodeFeatureSpec) {
 			(*out)[key] = val
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureSpec.
@@ -546,7 +513,6 @@ func (in *Rule) DeepCopyInto(out *Rule) {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
 	}
-	return
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Rule.
diff --git a/pkg/apis/nfd/validate/validate.go b/pkg/apis/nfd/validate/validate.go
index daa65bb02..a7c40c56a 100644
--- a/pkg/apis/nfd/validate/validate.go
+++ b/pkg/apis/nfd/validate/validate.go
@@ -25,7 +25,7 @@ import (
 	k8sQuantity "k8s.io/apimachinery/pkg/api/resource"
 	k8svalidation "k8s.io/apimachinery/pkg/util/validation"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 var (
@@ -57,7 +57,7 @@ func MatchFeatures(matchFeature nfdv1alpha1.FeatureMatcher) []error {
 	var validationErr []error
 
 	for _, match := range matchFeature {
-		nameSplit := strings.Split(match.Feature, ".")
+		nameSplit := strings.SplitN(match.Feature, ".", 2)
 		if len(nameSplit) != 2 {
 			validationErr = append(validationErr, fmt.Errorf("invalid feature name %v (not <domain>.<feature>), cannot be used for templating", match.Feature))
 		}
@@ -155,8 +155,8 @@ func Annotation(key, value string) error {
 	}
 
 	// Validate annotation value
-	if len(value) > nfdv1alpha1.FeatureAnnotationValueSizeLimit {
-		return fmt.Errorf("invalid value: too long: feature annotations must not be longer than %d characters", nfdv1alpha1.FeatureAnnotationValueSizeLimit)
+	if errs := k8svalidation.IsValidLabelValue(value); len(errs) > 0 {
+		return fmt.Errorf("invalid value %q: %s", value, strings.Join(errs, "; "))
 	}
 
 	return nil
diff --git a/api/generated/clientset/versioned/clientset.go b/pkg/generated/clientset/versioned/clientset.go
similarity index 98%
rename from api/generated/clientset/versioned/clientset.go
rename to pkg/generated/clientset/versioned/clientset.go
index 9891a3436..a2e8df121 100644
--- a/api/generated/clientset/versioned/clientset.go
+++ b/pkg/generated/clientset/versioned/clientset.go
@@ -25,7 +25,7 @@ import (
 	discovery "k8s.io/client-go/discovery"
 	rest "k8s.io/client-go/rest"
 	flowcontrol "k8s.io/client-go/util/flowcontrol"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/typed/nfd/v1alpha1"
 )
 
 type Interface interface {
diff --git a/api/generated/clientset/versioned/fake/clientset_generated.go b/pkg/generated/clientset/versioned/fake/clientset_generated.go
similarity index 93%
rename from api/generated/clientset/versioned/fake/clientset_generated.go
rename to pkg/generated/clientset/versioned/fake/clientset_generated.go
index 18d2a4f8f..68fd18efa 100644
--- a/api/generated/clientset/versioned/fake/clientset_generated.go
+++ b/pkg/generated/clientset/versioned/fake/clientset_generated.go
@@ -24,9 +24,9 @@ import (
 	"k8s.io/client-go/discovery"
 	fakediscovery "k8s.io/client-go/discovery/fake"
 	"k8s.io/client-go/testing"
-	clientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
-	fakenfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake"
+	clientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/typed/nfd/v1alpha1"
+	fakenfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake"
 )
 
 // NewSimpleClientset returns a clientset that will respond with the provided objects.
diff --git a/api/generated/clientset/versioned/fake/doc.go b/pkg/generated/clientset/versioned/fake/doc.go
similarity index 100%
rename from api/generated/clientset/versioned/fake/doc.go
rename to pkg/generated/clientset/versioned/fake/doc.go
diff --git a/api/generated/clientset/versioned/fake/register.go b/pkg/generated/clientset/versioned/fake/register.go
similarity index 96%
rename from api/generated/clientset/versioned/fake/register.go
rename to pkg/generated/clientset/versioned/fake/register.go
index 3c4d45a31..66ab54d87 100644
--- a/api/generated/clientset/versioned/fake/register.go
+++ b/pkg/generated/clientset/versioned/fake/register.go
@@ -24,7 +24,7 @@ import (
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 var scheme = runtime.NewScheme()
diff --git a/api/generated/clientset/versioned/scheme/doc.go b/pkg/generated/clientset/versioned/scheme/doc.go
similarity index 100%
rename from api/generated/clientset/versioned/scheme/doc.go
rename to pkg/generated/clientset/versioned/scheme/doc.go
diff --git a/api/generated/clientset/versioned/scheme/register.go b/pkg/generated/clientset/versioned/scheme/register.go
similarity index 96%
rename from api/generated/clientset/versioned/scheme/register.go
rename to pkg/generated/clientset/versioned/scheme/register.go
index f822ef608..6dd6f0124 100644
--- a/api/generated/clientset/versioned/scheme/register.go
+++ b/pkg/generated/clientset/versioned/scheme/register.go
@@ -24,7 +24,7 @@ import (
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 var Scheme = runtime.NewScheme()
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/doc.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/doc.go
similarity index 100%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/doc.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/doc.go
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/doc.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/doc.go
similarity index 100%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/doc.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/doc.go
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go
similarity index 95%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go
index 1a6e91c48..a1925c362 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nfd_client.go
@@ -21,7 +21,7 @@ package fake
 import (
 	rest "k8s.io/client-go/rest"
 	testing "k8s.io/client-go/testing"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/typed/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/typed/nfd/v1alpha1"
 )
 
 type FakeNfdV1alpha1 struct {
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go
similarity index 98%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go
index f956c8217..bd2787201 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeature.go
@@ -26,7 +26,7 @@ import (
 	types "k8s.io/apimachinery/pkg/types"
 	watch "k8s.io/apimachinery/pkg/watch"
 	testing "k8s.io/client-go/testing"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // FakeNodeFeatures implements NodeFeatureInterface
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go
similarity index 98%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go
index f096ae5e3..8b58f4113 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/fake/fake_nodefeaturerule.go
@@ -26,7 +26,7 @@ import (
 	types "k8s.io/apimachinery/pkg/types"
 	watch "k8s.io/apimachinery/pkg/watch"
 	testing "k8s.io/client-go/testing"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // FakeNodeFeatureRules implements NodeFeatureRuleInterface
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/generated_expansion.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/generated_expansion.go
similarity index 100%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/generated_expansion.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/generated_expansion.go
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go
similarity index 95%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go
index 0a733e974..ec8e63630 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nfd_client.go
@@ -22,8 +22,8 @@ import (
 	"net/http"
 
 	rest "k8s.io/client-go/rest"
-	"sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	"sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
 )
 
 type NfdV1alpha1Interface interface {
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go
similarity index 97%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go
index f159be867..07bf8259c 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeature.go
@@ -26,8 +26,8 @@ import (
 	types "k8s.io/apimachinery/pkg/types"
 	watch "k8s.io/apimachinery/pkg/watch"
 	rest "k8s.io/client-go/rest"
-	scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	scheme "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
 )
 
 // NodeFeaturesGetter has a method to return a NodeFeatureInterface.
diff --git a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go
similarity index 97%
rename from api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go
rename to pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go
index 843c13768..6353a3d74 100644
--- a/api/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go
+++ b/pkg/generated/clientset/versioned/typed/nfd/v1alpha1/nodefeaturerule.go
@@ -26,8 +26,8 @@ import (
 	types "k8s.io/apimachinery/pkg/types"
 	watch "k8s.io/apimachinery/pkg/watch"
 	rest "k8s.io/client-go/rest"
-	scheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	scheme "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
 )
 
 // NodeFeatureRulesGetter has a method to return a NodeFeatureRuleInterface.
diff --git a/api/generated/informers/externalversions/factory.go b/pkg/generated/informers/externalversions/factory.go
similarity index 97%
rename from api/generated/informers/externalversions/factory.go
rename to pkg/generated/informers/externalversions/factory.go
index 54812d547..25984d575 100644
--- a/api/generated/informers/externalversions/factory.go
+++ b/pkg/generated/informers/externalversions/factory.go
@@ -27,9 +27,9 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	cache "k8s.io/client-go/tools/cache"
-	versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
-	nfd "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/nfd"
+	versioned "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	internalinterfaces "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/internalinterfaces"
+	nfd "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/nfd"
 )
 
 // SharedInformerOption defines the functional option type for SharedInformerFactory.
diff --git a/api/generated/informers/externalversions/generic.go b/pkg/generated/informers/externalversions/generic.go
similarity index 96%
rename from api/generated/informers/externalversions/generic.go
rename to pkg/generated/informers/externalversions/generic.go
index ca1ed50a5..fc95fbea0 100644
--- a/api/generated/informers/externalversions/generic.go
+++ b/pkg/generated/informers/externalversions/generic.go
@@ -23,7 +23,7 @@ import (
 
 	schema "k8s.io/apimachinery/pkg/runtime/schema"
 	cache "k8s.io/client-go/tools/cache"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // GenericInformer is type of SharedIndexInformer which will locate and delegate to other
diff --git a/api/generated/informers/externalversions/internalinterfaces/factory_interfaces.go b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
similarity index 95%
rename from api/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
rename to pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
index 7f1153741..78f938add 100644
--- a/api/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
+++ b/pkg/generated/informers/externalversions/internalinterfaces/factory_interfaces.go
@@ -24,7 +24,7 @@ import (
 	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	cache "k8s.io/client-go/tools/cache"
-	versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
+	versioned "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
 )
 
 // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
diff --git a/api/generated/informers/externalversions/nfd/interface.go b/pkg/generated/informers/externalversions/nfd/interface.go
similarity index 92%
rename from api/generated/informers/externalversions/nfd/interface.go
rename to pkg/generated/informers/externalversions/nfd/interface.go
index 8c6835831..9193cca6b 100644
--- a/api/generated/informers/externalversions/nfd/interface.go
+++ b/pkg/generated/informers/externalversions/nfd/interface.go
@@ -19,8 +19,8 @@ limitations under the License.
 package nfd
 
 import (
-	internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/nfd/v1alpha1"
+	internalinterfaces "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/internalinterfaces"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/nfd/v1alpha1"
 )
 
 // Interface provides access to each of this group's versions.
diff --git a/api/generated/informers/externalversions/nfd/v1alpha1/interface.go b/pkg/generated/informers/externalversions/nfd/v1alpha1/interface.go
similarity index 96%
rename from api/generated/informers/externalversions/nfd/v1alpha1/interface.go
rename to pkg/generated/informers/externalversions/nfd/v1alpha1/interface.go
index d92f42ab0..85eb8ed39 100644
--- a/api/generated/informers/externalversions/nfd/v1alpha1/interface.go
+++ b/pkg/generated/informers/externalversions/nfd/v1alpha1/interface.go
@@ -19,7 +19,7 @@ limitations under the License.
 package v1alpha1
 
 import (
-	internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
+	internalinterfaces "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/internalinterfaces"
 )
 
 // Interface provides access to all the informers in this group version.
diff --git a/api/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go b/pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go
similarity index 92%
rename from api/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go
rename to pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go
index 570a73114..997488cab 100644
--- a/api/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go
+++ b/pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeature.go
@@ -26,10 +26,10 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	watch "k8s.io/apimachinery/pkg/watch"
 	cache "k8s.io/client-go/tools/cache"
-	versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	versioned "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	internalinterfaces "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/internalinterfaces"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/listers/nfd/v1alpha1"
 )
 
 // NodeFeatureInformer provides access to a shared informer and lister for
diff --git a/api/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go b/pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go
similarity index 92%
rename from api/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go
rename to pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go
index 47fae6dfb..fb0d1c362 100644
--- a/api/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go
+++ b/pkg/generated/informers/externalversions/nfd/v1alpha1/nodefeaturerule.go
@@ -26,10 +26,10 @@ import (
 	runtime "k8s.io/apimachinery/pkg/runtime"
 	watch "k8s.io/apimachinery/pkg/watch"
 	cache "k8s.io/client-go/tools/cache"
-	versioned "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	internalinterfaces "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions/internalinterfaces"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	versioned "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	internalinterfaces "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions/internalinterfaces"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/generated/listers/nfd/v1alpha1"
 )
 
 // NodeFeatureRuleInformer provides access to a shared informer and lister for
diff --git a/api/generated/listers/nfd/v1alpha1/expansion_generated.go b/pkg/generated/listers/nfd/v1alpha1/expansion_generated.go
similarity index 100%
rename from api/generated/listers/nfd/v1alpha1/expansion_generated.go
rename to pkg/generated/listers/nfd/v1alpha1/expansion_generated.go
diff --git a/api/generated/listers/nfd/v1alpha1/nodefeature.go b/pkg/generated/listers/nfd/v1alpha1/nodefeature.go
similarity index 98%
rename from api/generated/listers/nfd/v1alpha1/nodefeature.go
rename to pkg/generated/listers/nfd/v1alpha1/nodefeature.go
index ca3a184da..14f0d08b9 100644
--- a/api/generated/listers/nfd/v1alpha1/nodefeature.go
+++ b/pkg/generated/listers/nfd/v1alpha1/nodefeature.go
@@ -22,7 +22,7 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/tools/cache"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // NodeFeatureLister helps list NodeFeatures.
diff --git a/api/generated/listers/nfd/v1alpha1/nodefeaturerule.go b/pkg/generated/listers/nfd/v1alpha1/nodefeaturerule.go
similarity index 97%
rename from api/generated/listers/nfd/v1alpha1/nodefeaturerule.go
rename to pkg/generated/listers/nfd/v1alpha1/nodefeaturerule.go
index 6f8bf2b11..99c5edb71 100644
--- a/api/generated/listers/nfd/v1alpha1/nodefeaturerule.go
+++ b/pkg/generated/listers/nfd/v1alpha1/nodefeaturerule.go
@@ -22,7 +22,7 @@ import (
 	"k8s.io/apimachinery/pkg/api/errors"
 	"k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/tools/cache"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // NodeFeatureRuleLister helps list NodeFeatureRules.
diff --git a/pkg/kubectl-nfd/dryrun.go b/pkg/kubectl-nfd/dryrun.go
index ec06a3c35..c502887ca 100644
--- a/pkg/kubectl-nfd/dryrun.go
+++ b/pkg/kubectl-nfd/dryrun.go
@@ -25,8 +25,8 @@ import (
 
 	corev1 "k8s.io/api/core/v1"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
-	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/nodefeaturerule"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1/nodefeaturerule"
 	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/validate"
 )
 
diff --git a/pkg/kubectl-nfd/validate.go b/pkg/kubectl-nfd/validate.go
index 43d70df09..35424bd3f 100644
--- a/pkg/kubectl-nfd/validate.go
+++ b/pkg/kubectl-nfd/validate.go
@@ -24,7 +24,7 @@ import (
 	"k8s.io/apimachinery/pkg/api/resource"
 	"sigs.k8s.io/yaml"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/validate"
 )
 
diff --git a/pkg/labeler/labeler.pb.go b/pkg/labeler/labeler.pb.go
index 742f4a77e..7f9bdf685 100644
--- a/pkg/labeler/labeler.pb.go
+++ b/pkg/labeler/labeler.pb.go
@@ -30,7 +30,7 @@ import (
 	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
 	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
 	reflect "reflect"
-	v1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	v1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	sync "sync"
 )
 
@@ -158,37 +158,35 @@ var File_labeler_proto protoreflect.FileDescriptor
 
 var file_labeler_proto_rawDesc = []byte{
 	0x0a, 0x0d, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
-	0x07, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x1a, 0x43, 0x73, 0x69, 0x67, 0x73, 0x2e, 0x6b,
-	0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2d, 0x66, 0x65, 0x61, 0x74, 0x75,
-	0x72, 0x65, 0x2d, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69,
-	0x2f, 0x6e, 0x66, 0x64, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x67, 0x65,
-	0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfa, 0x01,
-	0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x66, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
-	0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x66, 0x64, 0x56, 0x65, 0x72, 0x73,
-	0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61, 0x6d, 0x65,
-	0x12, 0x3d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b,
-	0x32, 0x25, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x61,
-	0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61, 0x62, 0x65,
-	0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12,
-	0x2e, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28,
-	0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x65, 0x61,
-	0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x1a,
-	0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10,
-	0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79,
-	0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x10, 0x0a, 0x0e, 0x53, 0x65,
-	0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x4c, 0x0a, 0x07,
-	0x4c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x09, 0x53, 0x65, 0x74, 0x4c, 0x61,
-	0x62, 0x65, 0x6c, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x53,
-	0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
-	0x17, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62,
-	0x65, 0x6c, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e, 0x73, 0x69,
-	0x67, 0x73, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65, 0x2d, 0x66,
-	0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79,
-	0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x07, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x1a, 0x25, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x70,
+	0x69, 0x73, 0x2f, 0x6e, 0x66, 0x64, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f,
+	0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
+	0xfa, 0x01, 0x0a, 0x10, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71,
+	0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x6e, 0x66, 0x64, 0x5f, 0x76, 0x65, 0x72, 0x73,
+	0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6e, 0x66, 0x64, 0x56, 0x65,
+	0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1b, 0x0a, 0x09, 0x6e, 0x6f, 0x64, 0x65, 0x5f, 0x6e, 0x61,
+	0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x6f, 0x64, 0x65, 0x4e, 0x61,
+	0x6d, 0x65, 0x12, 0x3d, 0x0a, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03,
+	0x28, 0x0b, 0x32, 0x25, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74,
+	0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4c, 0x61,
+	0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x6c, 0x61, 0x62, 0x65, 0x6c,
+	0x73, 0x12, 0x2e, 0x0a, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x18, 0x04, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x12, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46,
+	0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x73, 0x52, 0x08, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65,
+	0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x10, 0x0a, 0x0e,
+	0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x32, 0x4c,
+	0x0a, 0x07, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x12, 0x41, 0x0a, 0x09, 0x53, 0x65, 0x74,
+	0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x12, 0x19, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72,
+	0x2e, 0x53, 0x65, 0x74, 0x4c, 0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x1a, 0x17, 0x2e, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x2e, 0x53, 0x65, 0x74, 0x4c,
+	0x61, 0x62, 0x65, 0x6c, 0x73, 0x52, 0x65, 0x70, 0x6c, 0x79, 0x22, 0x00, 0x42, 0x30, 0x5a, 0x2e,
+	0x73, 0x69, 0x67, 0x73, 0x2e, 0x6b, 0x38, 0x73, 0x2e, 0x69, 0x6f, 0x2f, 0x6e, 0x6f, 0x64, 0x65,
+	0x2d, 0x66, 0x65, 0x61, 0x74, 0x75, 0x72, 0x65, 0x2d, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65,
+	0x72, 0x79, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x61, 0x62, 0x65, 0x6c, 0x65, 0x72, 0x62, 0x06,
+	0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/pkg/labeler/labeler.proto b/pkg/labeler/labeler.proto
index 57cd8daaa..10630d229 100644
--- a/pkg/labeler/labeler.proto
+++ b/pkg/labeler/labeler.proto
@@ -18,7 +18,7 @@ syntax = "proto3";
 
 option go_package = "sigs.k8s.io/node-feature-discovery/pkg/labeler";
 
-import "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1/generated.proto";
+import "pkg/apis/nfd/v1alpha1/generated.proto";
 
 package labeler;
 
diff --git a/pkg/nfd-gc/nfd-gc.go b/pkg/nfd-gc/nfd-gc.go
index fd0e7f4a9..00cc33e10 100644
--- a/pkg/nfd-gc/nfd-gc.go
+++ b/pkg/nfd-gc/nfd-gc.go
@@ -31,8 +31,8 @@ import (
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/klog/v2"
 
-	nfdclientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/version"
 )
diff --git a/pkg/nfd-master/nfd-api-controller.go b/pkg/nfd-master/nfd-api-controller.go
index 0ed3fe81c..af86b2b45 100644
--- a/pkg/nfd-master/nfd-api-controller.go
+++ b/pkg/nfd-master/nfd-api-controller.go
@@ -26,11 +26,11 @@ import (
 	"k8s.io/client-go/tools/cache"
 	"k8s.io/klog/v2"
 
-	nfdclientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdscheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	nfdinformers "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions"
-	nfdlisters "sigs.k8s.io/node-feature-discovery/api/generated/listers/nfd/v1alpha1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	nfdscheme "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
+	nfdinformers "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions"
+	nfdlisters "sigs.k8s.io/node-feature-discovery/pkg/generated/listers/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
 
@@ -55,7 +55,7 @@ func init() {
 
 func newNfdController(config *restclient.Config, nfdApiControllerOptions nfdApiControllerOptions) (*nfdController, error) {
 	c := &nfdController{
-		stopChan:           make(chan struct{}),
+		stopChan:           make(chan struct{}, 1),
 		updateAllNodesChan: make(chan struct{}, 1),
 		updateOneNodeChan:  make(chan string),
 	}
diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go
index 5b3f8fd9c..7b9dcce6c 100644
--- a/pkg/nfd-master/nfd-master.go
+++ b/pkg/nfd-master/nfd-master.go
@@ -53,8 +53,8 @@ import (
 	taintutils "k8s.io/kubernetes/pkg/util/taints"
 	"sigs.k8s.io/yaml"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
-	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/nodefeaturerule"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1/nodefeaturerule"
 	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/validate"
 	"sigs.k8s.io/node-feature-discovery/pkg/features"
 	pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
@@ -76,7 +76,7 @@ type NFDConfig struct {
 	AutoDefaultNs     bool
 	DenyLabelNs       utils.StringSetVal
 	ExtraLabelNs      utils.StringSetVal
-	LabelWhiteList    *regexp.Regexp
+	LabelWhiteList    utils.RegexpVal
 	NoPublish         bool
 	ResourceLabels    utils.StringSetVal
 	EnableTaints      bool
@@ -149,7 +149,7 @@ type nfdMaster struct {
 	server          *grpc.Server
 	healthServer    *grpc.Server
 	stop            chan struct{}
-	ready           chan struct{}
+	ready           chan bool
 	k8sClient       k8sclient.Interface
 	nodeUpdaterPool *nodeUpdaterPool
 	deniedNs
@@ -157,54 +157,37 @@ type nfdMaster struct {
 }
 
 // NewNfdMaster creates a new NfdMaster server instance.
-func NewNfdMaster(opts ...NfdMasterOption) (NfdMaster, error) {
-	nfd := &nfdMaster{
+func NewNfdMaster(args *Args) (NfdMaster, error) {
+	nfd := &nfdMaster{args: *args,
 		nodeName:  utils.NodeName(),
 		namespace: utils.GetKubernetesNamespace(),
-		ready:     make(chan struct{}),
-		stop:      make(chan struct{}),
+		ready:     make(chan bool, 1),
+		stop:      make(chan struct{}, 1),
 	}
 
-	for _, o := range opts {
-		o.apply(nfd)
-	}
-
-	if nfd.args.Instance != "" {
-		if ok, _ := regexp.MatchString(`^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`, nfd.args.Instance); !ok {
+	if args.Instance != "" {
+		if ok, _ := regexp.MatchString(`^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`, args.Instance); !ok {
 			return nfd, fmt.Errorf("invalid -instance %q: instance name "+
 				"must start and end with an alphanumeric character and may only contain "+
-				"alphanumerics, `-`, `_` or `.`", nfd.args.Instance)
+				"alphanumerics, `-`, `_` or `.`", args.Instance)
 		}
 	}
 
 	// Check TLS related args
-	if nfd.args.CertFile != "" || nfd.args.KeyFile != "" || nfd.args.CaFile != "" {
-		if nfd.args.CertFile == "" {
+	if args.CertFile != "" || args.KeyFile != "" || args.CaFile != "" {
+		if args.CertFile == "" {
 			return nfd, fmt.Errorf("-cert-file needs to be specified alongside -key-file and -ca-file")
 		}
-		if nfd.args.KeyFile == "" {
+		if args.KeyFile == "" {
 			return nfd, fmt.Errorf("-key-file needs to be specified alongside -cert-file and -ca-file")
 		}
-		if nfd.args.CaFile == "" {
+		if args.CaFile == "" {
 			return nfd, fmt.Errorf("-ca-file needs to be specified alongside -cert-file and -key-file")
 		}
 	}
 
-	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
+	if args.ConfigFile != "" {
+		nfd.configFilePath = filepath.Clean(args.ConfigFile)
 	}
 
 	nfd.nodeUpdaterPool = newNodeUpdaterPool(nfd)
@@ -212,32 +195,9 @@ func NewNfdMaster(opts ...NfdMasterOption) (NfdMaster, error) {
 	return nfd, nil
 }
 
-// NfdMasterOption sets properties of the NfdMaster instance.
-type NfdMasterOption interface {
-	apply(*nfdMaster)
-}
-
-// WithArgs is used for passing settings from command line arguments.
-func WithArgs(args *Args) NfdMasterOption {
-	return &nfdMasterOpt{f: func(n *nfdMaster) { n.args = *args }}
-}
-
-// WithKuberneteClient forces to use the given kubernetes client, without
-// initializing one from kubeconfig.
-func WithKubernetesClient(cli k8sclient.Interface) NfdMasterOption {
-	return &nfdMasterOpt{f: func(n *nfdMaster) { n.k8sClient = cli }}
-}
-
-type nfdMasterOpt struct {
-	f func(*nfdMaster)
-}
-
-func (f *nfdMasterOpt) apply(n *nfdMaster) {
-	f.f(n)
-}
-
 func newDefaultConfig() *NFDConfig {
 	return &NFDConfig{
+		LabelWhiteList:    utils.RegexpVal{Regexp: *regexp.MustCompile("")},
 		DenyLabelNs:       utils.StringSetVal{},
 		ExtraLabelNs:      utils.StringSetVal{},
 		NoPublish:         false,
@@ -312,7 +272,7 @@ func (m *nfdMaster) Run() error {
 	}
 
 	// Run gRPC server
-	grpcErr := make(chan error)
+	grpcErr := make(chan error, 1)
 	// If the NodeFeature API is enabled, don'tregister the labeler API
 	// server. Otherwise, register the labeler server.
 	if !features.NFDFeatureGate.Enabled(features.NodeFeatureAPI) {
@@ -336,6 +296,7 @@ func (m *nfdMaster) Run() error {
 	}
 
 	// Notify that we're ready to accept connections
+	m.ready <- true
 	close(m.ready)
 
 	// NFD-Master main event loop
@@ -436,7 +397,7 @@ func (m *nfdMaster) runGrpcServer(errChan chan<- error) {
 	klog.InfoS("gRPC server serving", "port", m.args.Port)
 
 	// Run gRPC server
-	grpcErr := make(chan error)
+	grpcErr := make(chan error, 1)
 	go func() {
 		defer lis.Close()
 		grpcErr <- m.server.Serve(lis)
@@ -481,7 +442,7 @@ func (m *nfdMaster) nfdAPIUpdateHandler() {
 				}
 			} else {
 				for nodeName := range updateNodes {
-					m.nodeUpdaterPool.addNode(nodeName)
+					m.nodeUpdaterPool.queue.Add(nodeName)
 				}
 			}
 
@@ -514,10 +475,15 @@ func (m *nfdMaster) Stop() {
 // Wait until NfdMaster is able able to accept connections.
 func (m *nfdMaster) WaitForReady(timeout time.Duration) bool {
 	select {
-	case <-m.ready:
-		return true
+	case ready, ok := <-m.ready:
+		// Ready if the flag is true or the channel has been closed
+		if ready || !ok {
+			return true
+		}
 	case <-time.After(timeout):
+		return false
 	}
+	// We should never end-up here
 	return false
 }
 
@@ -537,7 +503,7 @@ func (m *nfdMaster) prune() error {
 		klog.InfoS("pruning node...", "nodeName", node.Name)
 
 		// Prune labels and extended resources
-		err := m.updateNodeObject(&node, Labels{}, Annotations{}, ExtendedResources{}, []corev1.Taint{})
+		err := m.updateNodeObject(node.Name, Labels{}, Annotations{}, ExtendedResources{}, []corev1.Taint{})
 		if err != nil {
 			nodeUpdateFailures.Inc()
 			return fmt.Errorf("failed to prune node %q: %v", node.Name, err)
@@ -642,8 +608,8 @@ func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1
 	}
 
 	// Skip if label doesn't match labelWhiteList
-	if m.config.LabelWhiteList != nil && !m.config.LabelWhiteList.MatchString(base) {
-		return "", fmt.Errorf("%s (%s) does not match the whitelist (%s)", base, name, m.config.LabelWhiteList.String())
+	if !m.config.LabelWhiteList.Regexp.MatchString(base) {
+		return "", fmt.Errorf("%s (%s) does not match the whitelist (%s)", base, name, m.config.LabelWhiteList.Regexp.String())
 	}
 
 	return filteredValue, nil
@@ -726,13 +692,8 @@ func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.Se
 		klog.InfoS("gRPC SetLabels request received", "nodeName", r.NodeName)
 	}
 	if !m.config.NoPublish {
-		// Fetch the node object.
-		node, err := m.getNode(r.NodeName)
-		if err != nil {
-			return &pb.SetLabelsReply{}, err
-		}
 		// Create labels et al
-		if err := m.refreshNodeFeatures(node, r.GetLabels(), r.GetFeatures()); err != nil {
+		if err := m.refreshNodeFeatures(r.NodeName, r.GetLabels(), r.GetFeatures()); err != nil {
 			nodeUpdateFailures.Inc()
 			return &pb.SetLabelsReply{}, err
 		}
@@ -749,21 +710,21 @@ func (m *nfdMaster) nfdAPIUpdateAllNodes() error {
 	}
 
 	for _, node := range nodes.Items {
-		m.nodeUpdaterPool.addNode(node.Name)
+		m.nodeUpdaterPool.queue.Add(node.Name)
 	}
 
 	return nil
 }
 
-func (m *nfdMaster) nfdAPIUpdateOneNode(node *corev1.Node) error {
+func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
 	if m.nfdController == nil || m.nfdController.featureLister == nil {
 		return nil
 	}
 
-	sel := k8sLabels.SelectorFromSet(k8sLabels.Set{nfdv1alpha1.NodeFeatureObjNodeNameLabel: node.Name})
+	sel := k8sLabels.SelectorFromSet(k8sLabels.Set{nfdv1alpha1.NodeFeatureObjNodeNameLabel: nodeName})
 	objs, err := m.nfdController.featureLister.List(sel)
 	if err != nil {
-		return fmt.Errorf("failed to get NodeFeature resources for node %q: %w", node.Name, err)
+		return fmt.Errorf("failed to get NodeFeature resources for node %q: %w", nodeName, err)
 	}
 
 	// Sort our objects
@@ -783,7 +744,11 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(node *corev1.Node) error {
 		return objs[i].Namespace < objs[j].Namespace
 	})
 
-	klog.V(1).InfoS("processing of node initiated by NodeFeature API", "nodeName", node.Name)
+	if m.config.NoPublish {
+		return nil
+	}
+
+	klog.V(1).InfoS("processing of node initiated by NodeFeature API", "nodeName", nodeName)
 
 	features := nfdv1alpha1.NewNodeFeatureSpec()
 
@@ -810,7 +775,7 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(node *corev1.Node) error {
 	// Update node labels et al. This may also mean removing all NFD-owned
 	// labels (et al.), for example  in the case no NodeFeature objects are
 	// present.
-	if err := m.refreshNodeFeatures(node, features.Labels, &features.Features); err != nil {
+	if err := m.refreshNodeFeatures(nodeName, features.Labels, &features.Features); err != nil {
 		return err
 	}
 
@@ -855,14 +820,14 @@ func filterExtendedResource(name, value string, features *nfdv1alpha1.Features)
 	return filteredValue, nil
 }
 
-func (m *nfdMaster) refreshNodeFeatures(node *corev1.Node, labels map[string]string, features *nfdv1alpha1.Features) error {
+func (m *nfdMaster) refreshNodeFeatures(nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
 	if m.config.AutoDefaultNs {
 		labels = addNsToMapKeys(labels, nfdv1alpha1.FeatureLabelNs)
 	} else if labels == nil {
 		labels = make(map[string]string)
 	}
 
-	crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(node.Name, features)
+	crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
 
 	// Mix in CR-originated labels
 	maps.Copy(labels, crLabels)
@@ -884,14 +849,9 @@ func (m *nfdMaster) refreshNodeFeatures(node *corev1.Node, labels map[string]str
 		taints = filterTaints(crTaints)
 	}
 
-	if m.config.NoPublish {
-		klog.V(1).InfoS("node update skipped, NoPublish=true", "nodeName", node.Name)
-		return nil
-	}
-
-	err := m.updateNodeObject(node, labels, annotations, extendedResources, taints)
+	err := m.updateNodeObject(nodeName, labels, annotations, extendedResources, taints)
 	if err != nil {
-		klog.ErrorS(err, "failed to update node", "nodeName", node.Name)
+		klog.ErrorS(err, "failed to update node", "nodeName", nodeName)
 		return err
 	}
 
@@ -901,9 +861,14 @@ func (m *nfdMaster) refreshNodeFeatures(node *corev1.Node, labels map[string]str
 // setTaints sets node taints and annotations based on the taints passed via
 // nodeFeatureRule custom resorce. If empty list of taints is passed, currently
 // NFD owned taints and annotations are removed from the node.
-func (m *nfdMaster) setTaints(taints []corev1.Taint, node *corev1.Node) error {
+func (m *nfdMaster) setTaints(taints []corev1.Taint, nodeName string) error {
+	// Fetch the node object.
+	node, err := m.getNode(nodeName)
+	if err != nil {
+		return err
+	}
+
 	// 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 {
 		sts := strings.Split(val, ",")
@@ -940,10 +905,11 @@ func (m *nfdMaster) setTaints(taints []corev1.Taint, node *corev1.Node) error {
 	}
 
 	if taintsUpdated {
-		if err := controller.PatchNodeTaints(context.TODO(), m.k8sClient, node.Name, node, newNode); err != nil {
+		err = controller.PatchNodeTaints(context.TODO(), m.k8sClient, nodeName, node, newNode)
+		if err != nil {
 			return fmt.Errorf("failed to patch the node %v", node.Name)
 		}
-		klog.InfoS("updated node taints", "nodeName", node.Name)
+		klog.InfoS("updated node taints", "nodeName", nodeName)
 	}
 
 	// Update node annotation that holds the taints managed by us
@@ -960,10 +926,11 @@ func (m *nfdMaster) setTaints(taints []corev1.Taint, node *corev1.Node) error {
 
 	patches := createPatches([]string{nfdv1alpha1.NodeTaintsAnnotation}, node.Annotations, newAnnotations, "/metadata/annotations")
 	if len(patches) > 0 {
-		if err := m.patchNode(node.Name, patches); err != nil {
+		err = m.patchNode(node.Name, patches)
+		if err != nil {
 			return fmt.Errorf("error while patching node object: %w", err)
 		}
-		klog.V(1).InfoS("patched node annotations for taints", "nodeName", node.Name)
+		klog.V(1).InfoS("patched node annotations for taints", "nodeName", nodeName)
 	}
 	return nil
 }
@@ -1057,7 +1024,13 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
 // updateNodeObject ensures the Kubernetes node object is up to date,
 // creating new labels and extended resources where necessary and removing
 // outdated ones. Also updates the corresponding annotations.
-func (m *nfdMaster) updateNodeObject(node *corev1.Node, labels Labels, featureAnnotations Annotations, extendedResources ExtendedResources, taints []corev1.Taint) error {
+func (m *nfdMaster) updateNodeObject(nodeName string, labels Labels, featureAnnotations Annotations, extendedResources ExtendedResources, taints []corev1.Taint) error {
+	// Get the worker node object
+	node, err := m.getNode(nodeName)
+	if err != nil {
+		return err
+	}
+
 	annotations := make(Annotations)
 
 	// Store names of labels in an annotation
@@ -1110,7 +1083,7 @@ func (m *nfdMaster) updateNodeObject(node *corev1.Node, labels Labels, featureAn
 
 	// patch node status with extended resource changes
 	statusPatches := m.createExtendedResourcePatches(node, extendedResources)
-	err := m.patchNodeStatus(node.Name, statusPatches)
+	err = m.patchNodeStatus(node.Name, statusPatches)
 	if err != nil {
 		return fmt.Errorf("error while patching extended resources: %w", err)
 	}
@@ -1123,13 +1096,13 @@ func (m *nfdMaster) updateNodeObject(node *corev1.Node, labels Labels, featureAn
 
 	if len(patches) > 0 || len(statusPatches) > 0 {
 		nodeUpdates.Inc()
-		klog.InfoS("node updated", "nodeName", node.Name)
+		klog.InfoS("node updated", "nodeName", nodeName)
 	} else {
-		klog.V(1).InfoS("no updates to node", "nodeName", node.Name)
+		klog.V(1).InfoS("no updates to node", "nodeName", nodeName)
 	}
 
 	// Set taints
-	err = m.setTaints(taints, node)
+	err = m.setTaints(taints, node.Name)
 	if err != nil {
 		return err
 	}
@@ -1245,7 +1218,7 @@ func (m *nfdMaster) configure(filepath string, overrides string) error {
 		c.EnableTaints = *m.args.Overrides.EnableTaints
 	}
 	if m.args.Overrides.LabelWhiteList != nil {
-		c.LabelWhiteList = &m.args.Overrides.LabelWhiteList.Regexp
+		c.LabelWhiteList = *m.args.Overrides.LabelWhiteList
 	}
 	if m.args.Overrides.ResyncPeriod != nil {
 		c.ResyncPeriod = *m.args.Overrides.ResyncPeriod
@@ -1264,6 +1237,18 @@ func (m *nfdMaster) configure(filepath string, overrides string) error {
 		return err
 	}
 
+	if !c.NoPublish {
+		kubeconfig, err := utils.GetKubeconfig(m.args.Kubeconfig)
+		if err != nil {
+			return err
+		}
+		cli, err := k8sclient.NewForConfig(kubeconfig)
+		if err != nil {
+			return err
+		}
+		m.k8sClient = cli
+	}
+
 	// Pre-process DenyLabelNS into 2 lists: one for normal ns, and the other for wildcard ns
 	normalDeniedNs, wildcardDeniedNs := preProcessDeniedNamespaces(c.DenyLabelNs)
 	m.deniedNs.normal = normalDeniedNs
diff --git a/pkg/nfd-master/node-updater-pool.go b/pkg/nfd-master/node-updater-pool.go
index f98b4f235..99948cc5f 100644
--- a/pkg/nfd-master/node-updater-pool.go
+++ b/pkg/nfd-master/node-updater-pool.go
@@ -28,7 +28,7 @@ import (
 
 type nodeUpdaterPool struct {
 	queue workqueue.RateLimitingInterface
-	sync.RWMutex
+	sync.Mutex
 
 	wg        sync.WaitGroup
 	nfdMaster *nfdMaster
@@ -53,9 +53,9 @@ func (u *nodeUpdaterPool) processNodeUpdateRequest(queue workqueue.RateLimitingI
 	nodeUpdateRequests.Inc()
 
 	// Check if node exists
-	if node, err := u.nfdMaster.getNode(nodeName); apierrors.IsNotFound(err) {
+	if _, err := u.nfdMaster.getNode(nodeName); apierrors.IsNotFound(err) {
 		klog.InfoS("node not found, skip update", "nodeName", nodeName)
-	} else if err := u.nfdMaster.nfdAPIUpdateOneNode(node); err != nil {
+	} else if err := u.nfdMaster.nfdAPIUpdateOneNode(nodeName); err != nil {
 		if n := queue.NumRequeues(nodeName); n < 15 {
 			klog.InfoS("retrying node update", "nodeName", nodeName, "lastError", err, "numRetries", n)
 		} else {
@@ -114,9 +114,3 @@ func (u *nodeUpdaterPool) stop() {
 	u.queue.ShutDown()
 	u.wg.Wait()
 }
-
-func (u *nodeUpdaterPool) addNode(nodeName string) {
-	u.RLock()
-	defer u.RUnlock()
-	u.queue.Add(nodeName)
-}
diff --git a/pkg/nfd-topology-updater/nfd-topology-updater.go b/pkg/nfd-topology-updater/nfd-topology-updater.go
index e65edbea2..131547b87 100644
--- a/pkg/nfd-topology-updater/nfd-topology-updater.go
+++ b/pkg/nfd-topology-updater/nfd-topology-updater.go
@@ -18,16 +18,12 @@ package nfdtopologyupdater
 
 import (
 	"fmt"
-	"net"
 	"net/url"
 	"os"
 	"path/filepath"
 
 	"golang.org/x/net/context"
 
-	"google.golang.org/grpc"
-	"google.golang.org/grpc/health"
-	"google.golang.org/grpc/health/grpc_health_v1"
 	"k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
@@ -62,7 +58,6 @@ type Args struct {
 	KubeConfigFile  string
 	ConfigFile      string
 	KubeletStateDir string
-	GrpcHealthPort  int
 
 	Klog map[string]*utils.KlogFlagVal
 }
@@ -90,7 +85,6 @@ type nfdTopologyUpdater struct {
 	ownerRefs           []metav1.OwnerReference
 	k8sClient           k8sclient.Interface
 	kubeletConfigFunc   func() (*kubeletconfigv1beta1.KubeletConfiguration, error)
-	healthServer        *grpc.Server
 }
 
 // NewTopologyUpdater creates a new NfdTopologyUpdater instance.
@@ -111,7 +105,7 @@ func NewTopologyUpdater(args Args, resourcemonitorArgs resourcemonitor.Args) (Nf
 	nfd := &nfdTopologyUpdater{
 		args:                args,
 		resourcemonitorArgs: resourcemonitorArgs,
-		stop:                make(chan struct{}),
+		stop:                make(chan struct{}, 1),
 		nodeName:            utils.NodeName(),
 		eventSource:         eventSource,
 		config:              &NFDConfig{},
@@ -134,29 +128,6 @@ func (w *nfdTopologyUpdater) detectTopologyPolicyAndScope() (string, string, err
 	return klConfig.TopologyManagerPolicy, klConfig.TopologyManagerScope, nil
 }
 
-func (w *nfdTopologyUpdater) startGrpcHealthServer(errChan chan<- error) error {
-	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", w.args.GrpcHealthPort))
-	if err != nil {
-		return fmt.Errorf("failed to listen: %w", err)
-	}
-
-	s := grpc.NewServer()
-	grpc_health_v1.RegisterHealthServer(s, health.NewServer())
-	klog.InfoS("gRPC health server serving", "port", w.args.GrpcHealthPort)
-
-	go func() {
-		defer func() {
-			lis.Close()
-		}()
-		if err := s.Serve(lis); err != nil {
-			errChan <- fmt.Errorf("gRPC health server exited with an error: %w", err)
-		}
-		klog.InfoS("gRPC health server stopped")
-	}()
-	w.healthServer = s
-	return nil
-}
-
 // Run nfdTopologyUpdater. Returns if a fatal error is encountered, or, after
 // one request if OneShot is set to 'true' in the updater args.
 func (w *nfdTopologyUpdater) Run() error {
@@ -207,6 +178,7 @@ func (w *nfdTopologyUpdater) Run() error {
 	// CAUTION: these resources are expected to change rarely - if ever.
 	// So we are intentionally do this once during the process lifecycle.
 	// TODO: Obtain node resources dynamically from the podresource API
+	// zonesChannel := make(chan v1alpha1.ZoneList)
 	var zones v1alpha2.ZoneList
 
 	excludeList := resourcemonitor.NewExcludeResourceList(w.config.ExcludeList, w.nodeName)
@@ -215,20 +187,8 @@ func (w *nfdTopologyUpdater) Run() error {
 		return fmt.Errorf("failed to obtain node resource information: %w", err)
 	}
 
-	grpcErr := make(chan error)
-
-	// Start gRPC server for liveness probe (at this point we're "live")
-	if w.args.GrpcHealthPort != 0 {
-		if err := w.startGrpcHealthServer(grpcErr); err != nil {
-			return fmt.Errorf("failed to start gRPC health server: %w", err)
-		}
-	}
-
 	for {
 		select {
-		case err := <-grpcErr:
-			return fmt.Errorf("error in serving gRPC: %w", err)
-
 		case info := <-w.eventSource:
 			klog.V(4).InfoS("event received, scanning...", "event", info.Event)
 			scanResponse, err := resScan.Scan()
@@ -257,9 +217,6 @@ func (w *nfdTopologyUpdater) Run() error {
 
 		case <-w.stop:
 			klog.InfoS("shutting down nfd-topology-updater")
-			if w.healthServer != nil {
-				w.healthServer.GracefulStop()
-			}
 			return nil
 		}
 	}
diff --git a/pkg/nfd-worker/nfd-worker.go b/pkg/nfd-worker/nfd-worker.go
index 357547912..e2348edf5 100644
--- a/pkg/nfd-worker/nfd-worker.go
+++ b/pkg/nfd-worker/nfd-worker.go
@@ -45,9 +45,9 @@ import (
 
 	apiequality "k8s.io/apimachinery/pkg/api/equality"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	nfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/features"
+	nfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
 	pb "sigs.k8s.io/node-feature-discovery/pkg/labeler"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/version"
@@ -146,7 +146,7 @@ func NewNfdWorker(args *Args) (NfdWorker, error) {
 		args:                *args,
 		config:              &NFDConfig{},
 		kubernetesNamespace: utils.GetKubernetesNamespace(),
-		stop:                make(chan struct{}),
+		stop:                make(chan struct{}, 1),
 	}
 
 	// Check TLS related args
@@ -290,7 +290,7 @@ func (w *nfdWorker) Run() error {
 		return nil
 	}
 
-	grpcErr := make(chan error)
+	grpcErr := make(chan error, 1)
 
 	// Start gRPC server for liveness probe (at this point we're "live")
 	if w.args.GrpcHealthPort != 0 {
diff --git a/pkg/utils/flags.go b/pkg/utils/flags.go
index fe7f5d2cd..1038b5156 100644
--- a/pkg/utils/flags.go
+++ b/pkg/utils/flags.go
@@ -42,6 +42,25 @@ func (a *RegexpVal) Set(val string) error {
 	return err
 }
 
+// UnmarshalJSON implements the Unmarshaler interface from "encoding/json"
+func (a *RegexpVal) UnmarshalJSON(data []byte) error {
+	var v interface{}
+	if err := json.Unmarshal(data, &v); err != nil {
+		return err
+	}
+	switch val := v.(type) {
+	case string:
+		if r, err := regexp.Compile(string(val)); err != nil {
+			return err
+		} else {
+			*a = RegexpVal{*r}
+		}
+	default:
+		return fmt.Errorf("invalid regexp %s", data)
+	}
+	return nil
+}
+
 // StringSetVal is a Value encapsulating a set of comma-separated strings
 type StringSetVal map[string]struct{}
 
diff --git a/source/cpu/cpu.go b/source/cpu/cpu.go
index 37187457e..5775d545a 100644
--- a/source/cpu/cpu.go
+++ b/source/cpu/cpu.go
@@ -27,7 +27,7 @@ import (
 
 	"github.com/klauspost/cpuid/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/source/custom/api/conversion.go b/source/custom/api/conversion.go
index b931b47b7..0034e49a6 100644
--- a/source/custom/api/conversion.go
+++ b/source/custom/api/conversion.go
@@ -17,7 +17,7 @@ limitations under the License.
 package api
 
 import (
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // convertFeaturematchertermToV1alpha1 converts the internal api type to nfdv1alpha1.
diff --git a/source/custom/custom.go b/source/custom/custom.go
index 0e422ec2e..2655279f0 100644
--- a/source/custom/custom.go
+++ b/source/custom/custom.go
@@ -22,8 +22,8 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
-	nodefeaturerule "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/nodefeaturerule"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nodefeaturerule "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1/nodefeaturerule"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 	api "sigs.k8s.io/node-feature-discovery/source/custom/api"
diff --git a/source/custom/directory_features.go b/source/custom/directory_features.go
index 8e4cb69c6..5fc92e845 100644
--- a/source/custom/directory_features.go
+++ b/source/custom/directory_features.go
@@ -22,7 +22,7 @@ import (
 	"strings"
 
 	"k8s.io/klog/v2"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	api "sigs.k8s.io/node-feature-discovery/source/custom/api"
 	"sigs.k8s.io/yaml"
 )
diff --git a/source/custom/static_features.go b/source/custom/static_features.go
index bcb91d3b8..45e77cd6c 100644
--- a/source/custom/static_features.go
+++ b/source/custom/static_features.go
@@ -17,7 +17,7 @@ limitations under the License.
 package custom
 
 import (
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // getStaticRules returns statically configured custom features to discover
diff --git a/source/fake/fake.go b/source/fake/fake.go
index 247e36bf2..92f205c0e 100644
--- a/source/fake/fake.go
+++ b/source/fake/fake.go
@@ -20,7 +20,7 @@ import (
 	"fmt"
 
 	"k8s.io/klog/v2"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/source/kernel/kernel.go b/source/kernel/kernel.go
index b753b302e..28cac6a8a 100644
--- a/source/kernel/kernel.go
+++ b/source/kernel/kernel.go
@@ -22,7 +22,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/source/local/local.go b/source/local/local.go
index dcb93d927..8acb0898a 100644
--- a/source/local/local.go
+++ b/source/local/local.go
@@ -27,7 +27,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/source/memory/memory.go b/source/memory/memory.go
index 5c4ce9f61..da8803fff 100644
--- a/source/memory/memory.go
+++ b/source/memory/memory.go
@@ -25,7 +25,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/source/network/network.go b/source/network/network.go
index 5f35d5064..a6cd71f1f 100644
--- a/source/network/network.go
+++ b/source/network/network.go
@@ -27,7 +27,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/source/pci/pci.go b/source/pci/pci.go
index 3e7870c03..39be031fc 100644
--- a/source/pci/pci.go
+++ b/source/pci/pci.go
@@ -23,7 +23,7 @@ import (
 	"golang.org/x/exp/maps"
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/source/pci/utils.go b/source/pci/utils.go
index 29c7afa11..f857baa28 100644
--- a/source/pci/utils.go
+++ b/source/pci/utils.go
@@ -24,7 +24,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 )
 
diff --git a/source/source.go b/source/source.go
index 06cdc5076..053ea69f0 100644
--- a/source/source.go
+++ b/source/source.go
@@ -21,7 +21,7 @@ package source
 import (
 	"fmt"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // Source is the base interface for all other source interfaces
diff --git a/source/storage/storage.go b/source/storage/storage.go
index 813df7013..b7534ae40 100644
--- a/source/storage/storage.go
+++ b/source/storage/storage.go
@@ -24,7 +24,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/source/system/system.go b/source/system/system.go
index 3f32d21c6..e1cfaff62 100644
--- a/source/system/system.go
+++ b/source/system/system.go
@@ -24,7 +24,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/source/usb/usb.go b/source/usb/usb.go
index f48fe5bcd..ab3febcb1 100644
--- a/source/usb/usb.go
+++ b/source/usb/usb.go
@@ -23,7 +23,7 @@ import (
 	"golang.org/x/exp/maps"
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/source/usb/utils.go b/source/usb/utils.go
index c5aa4d69a..2fbf80e50 100644
--- a/source/usb/utils.go
+++ b/source/usb/utils.go
@@ -25,7 +25,7 @@ import (
 
 	"k8s.io/klog/v2"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 )
 


Test patch:
diff --git a/pkg/apis/nfd/nodefeaturerule/expression-api_test.go b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression-api_test.go
similarity index 97%
rename from pkg/apis/nfd/nodefeaturerule/expression-api_test.go
rename to pkg/apis/nfd/v1alpha1/nodefeaturerule/expression-api_test.go
index d33ad355b..e9974d6df 100644
--- a/pkg/apis/nfd/nodefeaturerule/expression-api_test.go
+++ b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression-api_test.go
@@ -22,8 +22,8 @@ import (
 	"github.com/stretchr/testify/assert"
 	"sigs.k8s.io/yaml"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
-	api "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/nodefeaturerule"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	api "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1/nodefeaturerule"
 )
 
 type BoolAssertionFunc func(assert.TestingT, bool, ...interface{}) bool
diff --git a/pkg/apis/nfd/nodefeaturerule/expression_test.go b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression_test.go
similarity index 99%
rename from pkg/apis/nfd/nodefeaturerule/expression_test.go
rename to pkg/apis/nfd/v1alpha1/nodefeaturerule/expression_test.go
index 931cd72b9..3b51ca12e 100644
--- a/pkg/apis/nfd/nodefeaturerule/expression_test.go
+++ b/pkg/apis/nfd/v1alpha1/nodefeaturerule/expression_test.go
@@ -21,7 +21,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 type BoolAssertionFunc func(assert.TestingT, bool, ...interface{}) bool
diff --git a/pkg/apis/nfd/nodefeaturerule/rule_test.go b/pkg/apis/nfd/v1alpha1/nodefeaturerule/rule_test.go
similarity index 99%
rename from pkg/apis/nfd/nodefeaturerule/rule_test.go
rename to pkg/apis/nfd/v1alpha1/nodefeaturerule/rule_test.go
index ab52b7d24..a642404a7 100644
--- a/pkg/apis/nfd/nodefeaturerule/rule_test.go
+++ b/pkg/apis/nfd/v1alpha1/nodefeaturerule/rule_test.go
@@ -21,7 +21,7 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 // newMatchExpression returns a new MatchExpression instance.
diff --git a/pkg/apis/nfd/validate/validate_test.go b/pkg/apis/nfd/validate/validate_test.go
index 7786019a0..db49a805f 100644
--- a/pkg/apis/nfd/validate/validate_test.go
+++ b/pkg/apis/nfd/validate/validate_test.go
@@ -1,28 +1,10 @@
-/*
-Copyright 2023 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 validate
 
 import (
-	"fmt"
-	"sort"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
 )
 
 func TestAnnotation(t *testing.T) {
@@ -40,21 +22,15 @@ func TestAnnotation(t *testing.T) {
 		},
 		{
 			name:  "Invalid annotation key",
-			key:   "_invalid-key_",
-			value: "true",
-			want:  "invalid annotation key \"_invalid-key_\":",
-		},
-		{
-			name:  "Denied annotation key",
-			key:   "denied-key",
+			key:   "invalid-key",
 			value: "true",
 			want:  ErrUnprefixedKeysNotAllowed,
 		},
 		{
 			name:  "Invalid annotation value",
 			key:   "feature.node.kubernetes.io/feature",
-			value: string(make([]byte, 1100)),
-			want:  "invalid value: too long:",
+			value: "invalid value",
+			want:  "invalid value \"invalid value\": ",
 		},
 		{
 			name:  "Denied annotation key",
@@ -76,49 +52,6 @@ func TestAnnotation(t *testing.T) {
 	}
 }
 
-func TestAnnotations(t *testing.T) {
-	tests := []struct {
-		name        string
-		annotations map[string]string
-		want        []error
-	}{
-		{
-			name:        "Empty annotations",
-			annotations: map[string]string{},
-			want:        nil,
-		},
-		{
-			name: "Valid annotations",
-			annotations: map[string]string{
-				"feature.node.kubernetes.io/annotation": "true",
-				"vendor.io/annotation":                  "true",
-			},
-			want: nil,
-		},
-		{
-			name: "Invalid annotations",
-			annotations: map[string]string{
-				"invalid-key":              "true",
-				"kubernetes.io/annotation": "true",
-			},
-			want: []error{
-				ErrUnprefixedKeysNotAllowed,
-				ErrNSNotAllowed,
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errs := sortErrors(Annotations(tt.annotations))
-			assert.Equal(t, len(tt.want), len(errs))
-			for i := range errs {
-				assert.ErrorIs(t, errs[i], tt.want[i])
-			}
-		})
-	}
-}
-
 func TestTaint(t *testing.T) {
 	tests := []struct {
 		name  string
@@ -180,71 +113,6 @@ func TestTaint(t *testing.T) {
 	}
 }
 
-func TestTaints(t *testing.T) {
-	tests := []struct {
-		name   string
-		taints []corev1.Taint
-		want   []error
-	}{
-		{
-			name:   "Empty taints",
-			taints: []corev1.Taint{},
-			want:   nil,
-		},
-		{
-			name: "Valid taints",
-			taints: []corev1.Taint{
-				{
-					Key:    "feature.node.kubernetes.io/taint",
-					Value:  "true",
-					Effect: corev1.TaintEffectNoSchedule,
-				},
-				{
-					Key:    "vendor.io/taint",
-					Value:  "true",
-					Effect: corev1.TaintEffectNoExecute,
-				},
-			},
-			want: nil,
-		},
-		{
-			name: "Invalid taints",
-			taints: []corev1.Taint{
-				{
-					Key:    "invalid-key",
-					Value:  "true",
-					Effect: corev1.TaintEffectNoSchedule,
-				},
-				{
-					Key:    "feature.node.kubernetes.io/taint",
-					Value:  "true",
-					Effect: "",
-				},
-				{
-					Key:    "feature.node.kubernetes.io/taint",
-					Value:  "true",
-					Effect: "invalid-effect",
-				},
-			},
-			want: []error{
-				ErrUnprefixedKeysNotAllowed,
-				ErrEmptyTaintEffect,
-				ErrInvalidTaintEffect,
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errs := Taints(tt.taints)
-			assert.Equal(t, len(tt.want), len(errs))
-			for i := range errs {
-				assert.ErrorIs(t, errs[i], tt.want[i])
-			}
-		})
-	}
-}
-
 func TestLabel(t *testing.T) {
 	tests := []struct {
 		name  string
@@ -264,12 +132,6 @@ func TestLabel(t *testing.T) {
 			value: "true",
 			want:  nil,
 		},
-		{
-			name:  "Invalid label key",
-			key:   "invalid-key:",
-			value: "true",
-			want:  "invalid label key \"invalid-key:\": ",
-		},
 		{
 			name:  "Denied label with prefix",
 			key:   "kubernetes.io/label",
@@ -277,8 +139,8 @@ func TestLabel(t *testing.T) {
 			want:  ErrNSNotAllowed,
 		},
 		{
-			name:  "Denied label key unprefixed",
-			key:   "denied-key",
+			name:  "Invalid label key",
+			key:   "invalid-key",
 			value: "true",
 			want:  ErrUnprefixedKeysNotAllowed,
 		},
@@ -308,49 +170,6 @@ func TestLabel(t *testing.T) {
 	}
 }
 
-func TestLabels(t *testing.T) {
-	tests := []struct {
-		name   string
-		labels map[string]string
-		want   []error
-	}{
-		{
-			name:   "Empty labels",
-			labels: map[string]string{},
-			want:   nil,
-		},
-		{
-			name: "Valid labels",
-			labels: map[string]string{
-				"feature.node.kubernetes.io/label": "true",
-				"vendor.io/label":                  "true",
-			},
-			want: nil,
-		},
-		{
-			name: "Invalid labels",
-			labels: map[string]string{
-				"invalid-key":         "true",
-				"kubernetes.io/label": "true",
-			},
-			want: []error{
-				ErrUnprefixedKeysNotAllowed,
-				ErrNSNotAllowed,
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			err := sortErrors(Labels(tt.labels))
-			assert.Equal(t, len(tt.want), len(err))
-			for i := range err {
-				assert.ErrorIs(t, err[i], tt.want[i])
-			}
-		})
-	}
-}
-
 func TestExtendedResource(t *testing.T) {
 	tests := []struct {
 		name  string
@@ -365,14 +184,8 @@ func TestExtendedResource(t *testing.T) {
 			want:  nil,
 		},
 		{
-			name:  "Invalid extended resource name",
-			key:   "invalid-name~",
-			value: "123",
-			want:  "invalid name \"invalid-name~\": ",
-		},
-		{
-			name:  "Denied extended resource key",
-			key:   "denied-key",
+			name:  "Invalid extended resource key",
+			key:   "invalid-key",
 			value: "123",
 			want:  ErrUnprefixedKeysNotAllowed,
 		},
@@ -401,211 +214,3 @@ func TestExtendedResource(t *testing.T) {
 		})
 	}
 }
-
-func TestExtendedResources(t *testing.T) {
-	tests := []struct {
-		name              string
-		extendedResources map[string]string
-		want              []error
-	}{
-		{
-			name:              "Empty extended resources",
-			extendedResources: map[string]string{},
-			want:              nil,
-		},
-		{
-			name: "Valid extended resources",
-			extendedResources: map[string]string{
-				"feature.node.kubernetes.io/extended-resource": "123",
-				"vendor.io/extended-resource":                  "456",
-			},
-			want: nil,
-		},
-		{
-			name: "Invalid extended resources",
-			extendedResources: map[string]string{
-				"invalid-key":                     "456",
-				"kubernetes.io/extended-resource": "123",
-			},
-			want: []error{
-				ErrUnprefixedKeysNotAllowed,
-				ErrNSNotAllowed,
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errs := sortErrors(ExtendedResources(tt.extendedResources))
-			assert.Equal(t, len(tt.want), len(errs))
-			for i := range errs {
-				assert.ErrorIs(t, errs[i], tt.want[i])
-			}
-		})
-	}
-}
-
-func TestMatchFeatures(t *testing.T) {
-	tests := []struct {
-		name           string
-		matchFeature   nfdv1alpha1.FeatureMatcher
-		expectedErrors []error
-	}{
-		{
-			name:           "Empty matchFeature",
-			matchFeature:   nfdv1alpha1.FeatureMatcher{},
-			expectedErrors: nil,
-		},
-		{
-			name: "Valid matchFeature",
-			matchFeature: nfdv1alpha1.FeatureMatcher{
-				{
-					Feature: "domain1.feature1",
-				},
-				{
-					Feature: "domain2.feature2",
-				},
-			},
-			expectedErrors: nil,
-		},
-		{
-			name: "Invalid matchFeature",
-			matchFeature: nfdv1alpha1.FeatureMatcher{
-				{
-					Feature: "invalid-feature",
-				},
-				{
-					Feature: "domain3",
-				},
-				{
-					Feature: "prefix.domain.feature",
-				},
-			},
-			expectedErrors: []error{
-				fmt.Errorf("invalid feature name invalid-feature (not <domain>.<feature>), cannot be used for templating"),
-				fmt.Errorf("invalid feature name domain3 (not <domain>.<feature>), cannot be used for templating"),
-				fmt.Errorf("invalid feature name prefix.domain.feature (not <domain>.<feature>), cannot be used for templating"),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errors := MatchFeatures(tt.matchFeature)
-			assert.Equal(t, tt.expectedErrors, errors)
-		})
-	}
-}
-
-func TestMatchAny(t *testing.T) {
-	tests := []struct {
-		name           string
-		matchAny       []nfdv1alpha1.MatchAnyElem
-		expectedErrors []error
-	}{
-		{
-			name:           "Empty matchAny",
-			matchAny:       []nfdv1alpha1.MatchAnyElem{},
-			expectedErrors: nil,
-		},
-		{
-			name: "Valid matchAny",
-			matchAny: []nfdv1alpha1.MatchAnyElem{
-				{
-					MatchFeatures: nfdv1alpha1.FeatureMatcher{
-						{
-							Feature: "domain1.feature1",
-						},
-						{
-							Feature: "domain2.feature2",
-						},
-					},
-				},
-				{
-					MatchFeatures: nfdv1alpha1.FeatureMatcher{
-						{
-							Feature: "domain3.feature3",
-						},
-						{
-							Feature: "domain4.feature4",
-						},
-					},
-				},
-			},
-			expectedErrors: nil,
-		},
-		{
-			name: "Invalid matchAny",
-			matchAny: []nfdv1alpha1.MatchAnyElem{
-				{
-					MatchFeatures: nfdv1alpha1.FeatureMatcher{
-						{
-							Feature: "invalid-feature",
-						},
-						{
-							Feature: "domain3",
-						},
-					},
-				},
-				{
-					MatchFeatures: nfdv1alpha1.FeatureMatcher{
-						{
-							Feature: "domain5.feature5",
-						},
-						{
-							Feature: "invalid.domain.feature6",
-						},
-					},
-				},
-			},
-			expectedErrors: []error{
-				fmt.Errorf("invalid feature name invalid-feature (not <domain>.<feature>), cannot be used for templating"),
-				fmt.Errorf("invalid feature name domain3 (not <domain>.<feature>), cannot be used for templating"),
-				fmt.Errorf("invalid feature name invalid.domain.feature6 (not <domain>.<feature>), cannot be used for templating"),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errors := MatchAny(tt.matchAny)
-			assert.Equal(t, tt.expectedErrors, errors)
-		})
-	}
-}
-
-func TestTemplate(t *testing.T) {
-	tests := []struct {
-		name           string
-		labelsTemplate string
-		want           []error
-	}{
-		{
-			name:           "Valid template",
-			labelsTemplate: "key1=value1,key2=value2",
-			want:           nil,
-		},
-		{
-			name:           "Invalid template",
-			labelsTemplate: "{{.key1=value1,key2=value2}}",
-			want:           []error{fmt.Errorf("invalid template: template: :1: bad character U+003D '='")},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			errs := Template(tt.labelsTemplate)
-			assert.Equal(t, len(tt.want), len(errs))
-			for i := range errs {
-				assert.EqualError(t, errs[i], tt.want[i].Error())
-			}
-		})
-	}
-}
-
-func sortErrors(errs []error) []error {
-	sort.Slice(errs, func(i, j int) bool {
-		return errs[i].Error() < errs[j].Error()
-	})
-	return errs
-}
diff --git a/pkg/kubectl-nfd/test.go b/pkg/kubectl-nfd/test.go
index 6efb0e5bf..bafc83ab7 100644
--- a/pkg/kubectl-nfd/test.go
+++ b/pkg/kubectl-nfd/test.go
@@ -24,9 +24,9 @@ import (
 	k8sLabels "k8s.io/apimachinery/pkg/labels"
 	"k8s.io/client-go/tools/clientcmd"
 
-	nfdclientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdinformers "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	nfdinformers "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions"
 
 	"sigs.k8s.io/yaml"
 )
diff --git a/pkg/nfd-gc/nfd-gc_test.go b/pkg/nfd-gc/nfd-gc_test.go
index 02323aca8..0f4ed26d8 100644
--- a/pkg/nfd-gc/nfd-gc_test.go
+++ b/pkg/nfd-gc/nfd-gc_test.go
@@ -31,7 +31,7 @@ import (
 	"k8s.io/client-go/informers"
 	k8sclientset "k8s.io/client-go/kubernetes"
 	fakek8sclientset "k8s.io/client-go/kubernetes/fake"
-	fakenfdclientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/fake"
+	fakenfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/fake"
 
 	. "github.com/smartystreets/goconvey/convey"
 )
@@ -40,7 +40,7 @@ func TestNRTGC(t *testing.T) {
 	Convey("When theres is old NRT ", t, func() {
 		gc := newMockGC(nil, []string{"node1"})
 
-		errChan := make(chan error)
+		errChan := make(chan error, 1)
 		go func() { errChan <- gc.Run() }()
 
 		So(waitForNRT(gc.topoClient), ShouldBeTrue)
@@ -51,7 +51,7 @@ func TestNRTGC(t *testing.T) {
 	Convey("When theres is one old NRT and one up to date", t, func() {
 		gc := newMockGC([]string{"node1"}, []string{"node1", "node2"})
 
-		errChan := make(chan error)
+		errChan := make(chan error, 1)
 		go func() { errChan <- gc.Run() }()
 
 		So(waitForNRT(gc.topoClient, "node1"), ShouldBeTrue)
@@ -62,7 +62,7 @@ func TestNRTGC(t *testing.T) {
 	Convey("Should react to delete event", t, func() {
 		gc := newMockGC([]string{"node1", "node2"}, []string{"node1", "node2"})
 
-		errChan := make(chan error)
+		errChan := make(chan error, 1)
 		go func() { errChan <- gc.Run() }()
 
 		err := gc.k8sClient.CoreV1().Nodes().Delete(context.TODO(), "node1", metav1.DeleteOptions{})
@@ -81,7 +81,7 @@ func TestNRTGC(t *testing.T) {
 			},
 		}
 
-		errChan := make(chan error)
+		errChan := make(chan error, 1)
 		go func() { errChan <- gc.Run() }()
 
 		_, err := gc.topoClient.TopologyV1alpha2().NodeResourceTopologies().Create(context.TODO(), &nrt, metav1.CreateOptions{})
@@ -98,7 +98,7 @@ func newMockGC(nodes, nrts []string) *mockGC {
 			factory:    informers.NewSharedInformerFactory(k8sClient, 5*time.Minute),
 			nfdClient:  fakenfdclientset.NewSimpleClientset(),
 			topoClient: faketopologyv1alpha2.NewSimpleClientset(createFakeNRTs(nrts...)...),
-			stopChan:   make(chan struct{}),
+			stopChan:   make(chan struct{}, 1),
 			args: &Args{
 				GCPeriod: 10 * time.Minute,
 			},
diff --git a/pkg/nfd-master/nfd-api-controller_test.go b/pkg/nfd-master/nfd-api-controller_test.go
index 44153f41a..bffa9ffc2 100644
--- a/pkg/nfd-master/nfd-api-controller_test.go
+++ b/pkg/nfd-master/nfd-api-controller_test.go
@@ -22,7 +22,7 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 func TestGetNodeNameForObj(t *testing.T) {
diff --git a/pkg/nfd-master/nfd-master-internal_test.go b/pkg/nfd-master/nfd-master-internal_test.go
index 2c2e71e4c..29a3473de 100644
--- a/pkg/nfd-master/nfd-master-internal_test.go
+++ b/pkg/nfd-master/nfd-master-internal_test.go
@@ -22,6 +22,7 @@ import (
 	"maps"
 	"os"
 	"path/filepath"
+	"regexp"
 	"sort"
 	"strings"
 	"testing"
@@ -36,16 +37,18 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
+	k8sclient "k8s.io/client-go/kubernetes"
 	fakeclient "k8s.io/client-go/kubernetes/fake"
 	fakecorev1client "k8s.io/client-go/kubernetes/typed/core/v1/fake"
 	clienttesting "k8s.io/client-go/testing"
 	"k8s.io/client-go/tools/cache"
+	"k8s.io/klog/v2"
 
-	fakenfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/fake"
-	nfdscheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	nfdinformers "sigs.k8s.io/node-feature-discovery/api/generated/informers/externalversions"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/features"
+	fakenfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/fake"
+	nfdscheme "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
+	nfdinformers "sigs.k8s.io/node-feature-discovery/pkg/generated/informers/externalversions"
 	"sigs.k8s.io/node-feature-discovery/pkg/labeler"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
@@ -65,7 +68,7 @@ func newTestNode() *corev1.Node {
 
 func newFakeNfdAPIController(client *fakenfdclient.Clientset) *nfdController {
 	c := &nfdController{
-		stopChan:           make(chan struct{}),
+		stopChan:           make(chan struct{}, 1),
 		updateAllNodesChan: make(chan struct{}, 1),
 		updateOneNodeChan:  make(chan string),
 	}
@@ -102,25 +105,12 @@ func newFakeNfdAPIController(client *fakenfdclient.Clientset) *nfdController {
 	return c
 }
 
-func withNodeName(nodeName string) NfdMasterOption {
-	return &nfdMasterOpt{f: func(n *nfdMaster) { n.nodeName = nodeName }}
-}
-
-func withConfig(config *NFDConfig) NfdMasterOption {
-	return &nfdMasterOpt{f: func(n *nfdMaster) { n.config = config }}
-}
-
-func newFakeMaster(opts ...NfdMasterOption) *nfdMaster {
-	defaultOpts := []NfdMasterOption{
-		withNodeName(testNodeName),
-		withConfig(&NFDConfig{}),
-		WithKubernetesClient(fakeclient.NewSimpleClientset()),
-	}
-	m, err := NewNfdMaster(append(defaultOpts, opts...)...)
-	if err != nil {
-		panic(err)
+func newFakeMaster(cli k8sclient.Interface) *nfdMaster {
+	return &nfdMaster{
+		nodeName:  testNodeName,
+		config:    &NFDConfig{LabelWhiteList: utils.RegexpVal{Regexp: *regexp.MustCompile("")}},
+		k8sClient: cli,
 	}
-	return m.(*nfdMaster)
 }
 
 func TestUpdateNodeObject(t *testing.T) {
@@ -164,10 +154,10 @@ func TestUpdateNodeObject(t *testing.T) {
 
 		// Create fake api client and initialize NfdMaster instance
 		fakeCli := fakeclient.NewSimpleClientset(testNode)
-		fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+		fakeMaster := newFakeMaster(fakeCli)
 
 		Convey("When I successfully update the node with feature labels", func() {
-			err := fakeMaster.updateNodeObject(testNode, featureLabels, featureAnnotations, featureExtResources, nil)
+			err := fakeMaster.updateNodeObject(testNodeName, featureLabels, featureAnnotations, featureExtResources, nil)
 			Convey("Error is nil", func() {
 				So(err, ShouldBeNil)
 			})
@@ -195,11 +185,19 @@ func TestUpdateNodeObject(t *testing.T) {
 			})
 		})
 
+		Convey("When I fail to get a node while updating feature labels", func() {
+			err := fakeMaster.updateNodeObject("non-existent-node", featureLabels, featureAnnotations, featureExtResources, nil)
+
+			Convey("Error is produced", func() {
+				So(err, ShouldBeError)
+			})
+		})
+
 		Convey("When I fail to patch a node", func() {
 			fakeCli.CoreV1().(*fakecorev1client.FakeCoreV1).PrependReactor("patch", "nodes", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
 				return true, &v1.Node{}, errors.New("Fake error when patching node")
 			})
-			err := fakeMaster.updateNodeObject(testNode, nil, featureAnnotations, ExtendedResources{"": ""}, nil)
+			err := fakeMaster.updateNodeObject(testNodeName, nil, featureAnnotations, ExtendedResources{"": ""}, nil)
 
 			Convey("Error is produced", func() {
 				So(err, ShouldBeError)
@@ -216,7 +214,7 @@ func TestUpdateMasterNode(t *testing.T) {
 
 		Convey("When update operation succeeds", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 			err := fakeMaster.updateMasterNode()
 			Convey("No error should be returned", func() {
 				So(err, ShouldBeNil)
@@ -231,7 +229,7 @@ func TestUpdateMasterNode(t *testing.T) {
 
 		Convey("When getting API node object fails", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 			fakeMaster.nodeName = "does-not-exist"
 
 			err := fakeMaster.updateMasterNode()
@@ -246,7 +244,7 @@ func TestUpdateMasterNode(t *testing.T) {
 			fakeCli.CoreV1().(*fakecorev1client.FakeCoreV1).PrependReactor("patch", "nodes", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
 				return true, &v1.Node{}, fakeErr
 			})
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 
 			err := fakeMaster.updateMasterNode()
 			Convey("An error should be returned", func() {
@@ -258,7 +256,7 @@ func TestUpdateMasterNode(t *testing.T) {
 
 func TestAddingExtResources(t *testing.T) {
 	Convey("When adding extended resources", t, func() {
-		fakeMaster := newFakeMaster()
+		fakeMaster := newFakeMaster(nil)
 		Convey("When there are no matching labels", func() {
 			testNode := newTestNode()
 			resourceLabels := ExtendedResources{}
@@ -301,7 +299,7 @@ func TestAddingExtResources(t *testing.T) {
 
 func TestRemovingExtResources(t *testing.T) {
 	Convey("When removing extended resources", t, func() {
-		fakeMaster := newFakeMaster()
+		fakeMaster := newFakeMaster(nil)
 		Convey("When none are removed", func() {
 			testNode := newTestNode()
 			resourceLabels := ExtendedResources{nfdv1alpha1.FeatureLabelNs + "/feature-1": "1", nfdv1alpha1.FeatureLabelNs + "/feature-2": "2"}
@@ -350,7 +348,7 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("When node update succeeds", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 
 			_, err := fakeMaster.SetLabels(ctx, req)
 			Convey("No error should be returned", func() {
@@ -365,13 +363,11 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("When -resource-labels is specified", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(
-				WithKubernetesClient(fakeCli),
-				withConfig(&NFDConfig{
-					ResourceLabels: map[string]struct{}{
-						"feature.node.kubernetes.io/feature-3": {},
-						"feature-1":                            {}},
-				}))
+			fakeMaster := newFakeMaster(fakeCli)
+			fakeMaster.config.ResourceLabels = map[string]struct{}{
+				"feature.node.kubernetes.io/feature-3": {},
+				"feature-1":                            {},
+			}
 
 			_, err := fakeMaster.SetLabels(ctx, req)
 			Convey("Error is nil", func() {
@@ -387,7 +383,7 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("When node update fails", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 
 			fakeErr := errors.New("Fake error when patching node")
 			fakeCli.CoreV1().(*fakecorev1client.FakeCoreV1).PrependReactor("patch", "nodes", func(action clienttesting.Action) (handled bool, ret runtime.Object, err error) {
@@ -401,7 +397,7 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("With '-no-publish'", func() {
 			fakeCli := fakeclient.NewSimpleClientset(testNode)
-			fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+			fakeMaster := newFakeMaster(fakeCli)
 
 			fakeMaster.config.NoPublish = true
 			_, err := fakeMaster.SetLabels(ctx, req)
@@ -413,7 +409,7 @@ func TestSetLabels(t *testing.T) {
 }
 
 func TestFilterLabels(t *testing.T) {
-	fakeMaster := newFakeMaster()
+	fakeMaster := newFakeMaster(nil)
 	fakeMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
 	fakeMaster.deniedNs = deniedNs{
 		normal:   map[string]struct{}{"": struct{}{}, "kubernetes.io": struct{}{}, "denied.ns": struct{}{}},
@@ -584,7 +580,9 @@ func TestRemoveLabelsWithPrefix(t *testing.T) {
 
 func TestConfigParse(t *testing.T) {
 	Convey("When parsing configuration", t, func() {
-		master := newFakeMaster()
+		m, err := NewNfdMaster(&Args{})
+		So(err, ShouldBeNil)
+		master := m.(*nfdMaster)
 		overrides := `{"noPublish": true, "enableTaints": true, "extraLabelNs": ["added.ns.io","added.kubernetes.io"], "denyLabelNs": ["denied.ns.io","denied.kubernetes.io"], "resourceLabels": ["vendor-1.com/feature-1","vendor-2.io/feature-2"], "labelWhiteList": "foo"}`
 
 		Convey("and no core cmdline flags have been specified", func() {
@@ -664,10 +662,6 @@ leaderElection:
 
 func TestDynamicConfig(t *testing.T) {
 	Convey("When running nfd-master", t, func() {
-		// Add feature gates as running nfd-master depends on that
-		err := features.NFDMutableFeatureGate.Add(features.DefaultNFDFeatureGates)
-		So(err, ShouldBeNil)
-
 		tmpDir, err := os.MkdirTemp("", "*.nfd-test")
 		So(err, ShouldBeNil)
 		defer os.RemoveAll(tmpDir)
@@ -678,7 +672,7 @@ func TestDynamicConfig(t *testing.T) {
 		So(err, ShouldBeNil)
 
 		// Create config file
-		configFile := filepath.Clean(filepath.Join(configDir, "master.conf"))
+		configFile := filepath.Join(configDir, "master.conf")
 
 		writeConfig := func(data string) {
 			f, err := os.Create(configFile)
@@ -689,23 +683,28 @@ func TestDynamicConfig(t *testing.T) {
 			So(err, ShouldBeNil)
 		}
 		writeConfig(`
-klog:
-  v: "4"
 extraLabelNs: ["added.ns.io"]
 `)
 
-		master := newFakeMaster(
-			WithArgs(&Args{ConfigFile: configFile}),
-			WithKubernetesClient(fakeclient.NewSimpleClientset(newTestNode())))
+		noPublish := true
+		// Add FeatureGates flag
+		if err := features.NFDMutableFeatureGate.Add(features.DefaultNFDFeatureGates); err != nil {
+			klog.ErrorS(err, "failed to add default feature gates")
+			os.Exit(1)
+		}
+		_ = features.NFDMutableFeatureGate.OverrideDefault(features.NodeFeatureAPI, false)
+		m, err := NewNfdMaster(&Args{
+			ConfigFile: configFile,
+			Overrides: ConfigOverrideArgs{
+				NoPublish: &noPublish,
+			},
+		})
+		So(err, ShouldBeNil)
+		master := m.(*nfdMaster)
 
 		Convey("config file updates should take effect", func() {
-			go func() {
-				Convey("nfd-master should exit gracefully", t, func() {
-					err = master.Run()
-					So(err, ShouldBeNil)
-				})
-			}()
-			defer master.Stop()
+			go func() { _ = m.Run() }()
+			defer m.Stop()
 			// Check initial config
 			time.Sleep(10 * time.Second)
 			So(func() interface{} { return master.config.ExtraLabelNs },
@@ -769,7 +768,7 @@ func newTestNodeList() *corev1.NodeList {
 
 func BenchmarkNfdAPIUpdateAllNodes(b *testing.B) {
 	fakeCli := fakeclient.NewSimpleClientset(newTestNodeList())
-	fakeMaster := newFakeMaster(WithKubernetesClient(fakeCli))
+	fakeMaster := newFakeMaster(fakeCli)
 	fakeMaster.nfdController = newFakeNfdAPIController(fakenfdclient.NewSimpleClientset())
 
 	nodeUpdaterPool := newNodeUpdaterPool(fakeMaster)
diff --git a/pkg/nfd-master/nfd-master_test.go b/pkg/nfd-master/nfd-master_test.go
index 412a8a233..b15721601 100644
--- a/pkg/nfd-master/nfd-master_test.go
+++ b/pkg/nfd-master/nfd-master_test.go
@@ -20,16 +20,15 @@ import (
 	"testing"
 
 	. "github.com/smartystreets/goconvey/convey"
-	fakeclient "k8s.io/client-go/kubernetes/fake"
 	m "sigs.k8s.io/node-feature-discovery/pkg/nfd-master"
 )
 
 func TestNewNfdMaster(t *testing.T) {
 	Convey("When initializing new NfdMaster instance", t, func() {
 		Convey("When one of -cert-file, -key-file or -ca-file is missing", func() {
-			_, err := m.NewNfdMaster(m.WithArgs(&m.Args{CertFile: "crt", KeyFile: "key"}))
-			_, err2 := m.NewNfdMaster(m.WithArgs(&m.Args{KeyFile: "key", CaFile: "ca"}))
-			_, err3 := m.NewNfdMaster(m.WithArgs(&m.Args{CertFile: "crt", CaFile: "ca"}))
+			_, err := m.NewNfdMaster(&m.Args{CertFile: "crt", KeyFile: "key"})
+			_, err2 := m.NewNfdMaster(&m.Args{KeyFile: "key", CaFile: "ca"})
+			_, err3 := m.NewNfdMaster(&m.Args{CertFile: "crt", CaFile: "ca"})
 			Convey("An error should be returned", func() {
 				So(err, ShouldNotBeNil)
 				So(err2, ShouldNotBeNil)
@@ -37,13 +36,7 @@ func TestNewNfdMaster(t *testing.T) {
 			})
 		})
 		Convey("When -config is supplied", func() {
-			_, err := m.NewNfdMaster(
-				m.WithArgs(&m.Args{
-					CertFile:   "crt",
-					KeyFile:    "key",
-					CaFile:     "ca",
-					ConfigFile: "master-config.yaml"}),
-				m.WithKubernetesClient(fakeclient.NewSimpleClientset()))
+			_, err := m.NewNfdMaster(&m.Args{CertFile: "crt", KeyFile: "key", CaFile: "ca", ConfigFile: "master-config.yaml"})
 			Convey("An error should not be returned", func() {
 				So(err, ShouldBeNil)
 			})
diff --git a/pkg/nfd-master/node-updater-pool_test.go b/pkg/nfd-master/node-updater-pool_test.go
index a648ee435..5c6924bf6 100644
--- a/pkg/nfd-master/node-updater-pool_test.go
+++ b/pkg/nfd-master/node-updater-pool_test.go
@@ -23,7 +23,7 @@ import (
 
 	. "github.com/smartystreets/goconvey/convey"
 	fakek8sclient "k8s.io/client-go/kubernetes/fake"
-	fakenfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/fake"
+	fakenfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/fake"
 )
 
 func newFakeNodeUpdaterPool(nfdMaster *nfdMaster) *nodeUpdaterPool {
@@ -34,7 +34,7 @@ func newFakeNodeUpdaterPool(nfdMaster *nfdMaster) *nodeUpdaterPool {
 }
 
 func TestNodeUpdaterStart(t *testing.T) {
-	fakeMaster := newFakeMaster()
+	fakeMaster := newFakeMaster(nil)
 	nodeUpdaterPool := newFakeNodeUpdaterPool(fakeMaster)
 
 	Convey("When starting the node updater pool", t, func() {
@@ -53,7 +53,7 @@ func TestNodeUpdaterStart(t *testing.T) {
 }
 
 func TestNodeUpdaterStop(t *testing.T) {
-	fakeMaster := newFakeMaster()
+	fakeMaster := newFakeMaster(nil)
 	nodeUpdaterPool := newFakeNodeUpdaterPool(fakeMaster)
 
 	nodeUpdaterPool.start(10)
@@ -70,7 +70,7 @@ func TestNodeUpdaterStop(t *testing.T) {
 }
 
 func TestRunNodeUpdater(t *testing.T) {
-	fakeMaster := newFakeMaster(WithKubernetesClient(fakek8sclient.NewSimpleClientset()))
+	fakeMaster := newFakeMaster(fakek8sclient.NewSimpleClientset())
 	fakeMaster.nfdController = newFakeNfdAPIController(fakenfdclient.NewSimpleClientset())
 	nodeUpdaterPool := newFakeNodeUpdaterPool(fakeMaster)
 
diff --git a/pkg/nfd-worker/nfd-worker-internal_test.go b/pkg/nfd-worker/nfd-worker-internal_test.go
index 101177684..7747c1fc5 100644
--- a/pkg/nfd-worker/nfd-worker-internal_test.go
+++ b/pkg/nfd-worker/nfd-worker-internal_test.go
@@ -28,7 +28,7 @@ import (
 	"github.com/stretchr/testify/mock"
 	"github.com/vektra/errors"
 
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/labeler"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source"
diff --git a/pkg/nfd-worker/nfd-worker_test.go b/pkg/nfd-worker/nfd-worker_test.go
index 219098d0e..4df97e7db 100644
--- a/pkg/nfd-worker/nfd-worker_test.go
+++ b/pkg/nfd-worker/nfd-worker_test.go
@@ -24,7 +24,6 @@ import (
 	"time"
 
 	. "github.com/smartystreets/goconvey/convey"
-	fakeclient "k8s.io/client-go/kubernetes/fake"
 	"k8s.io/klog/v2"
 
 	"sigs.k8s.io/node-feature-discovery/pkg/features"
@@ -53,9 +52,7 @@ func setupTest(args *master.Args) testContext {
 		os.Exit(1)
 	}
 	_ = features.NFDMutableFeatureGate.OverrideDefault(features.NodeFeatureAPI, false)
-	m, err := master.NewNfdMaster(
-		master.WithArgs(args),
-		master.WithKubernetesClient(fakeclient.NewSimpleClientset()))
+	m, err := master.NewNfdMaster(args)
 	if err != nil {
 		fmt.Printf("Test setup failed: %v\n", err)
 		os.Exit(1)
diff --git a/scripts/test-infra/build-image-cross.sh b/scripts/test-infra/build-image-cross.sh
index 5ec84783d..444f7260d 100755
--- a/scripts/test-infra/build-image-cross.sh
+++ b/scripts/test-infra/build-image-cross.sh
@@ -1,4 +1,4 @@
 #!/bin/bash -e
 
 # cross build
-make image-all
+IMAGE_ALL_PLATFORMS=linux/amd64,linux/arm64 make image-all
diff --git a/source/custom/api/conversion_test.go b/source/custom/api/conversion_test.go
index f5c3707df..2d0e8a7f2 100644
--- a/source/custom/api/conversion_test.go
+++ b/source/custom/api/conversion_test.go
@@ -21,8 +21,8 @@ import (
 
 	"github.com/stretchr/testify/assert"
 
-	"sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 )
 
 func TestRuleConversion(t *testing.T) {
diff --git a/source/pci/pci_test.go b/source/pci/pci_test.go
index c5a7e40b5..8face6ce4 100644
--- a/source/pci/pci_test.go
+++ b/source/pci/pci_test.go
@@ -22,7 +22,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils/hostpath"
 	"sigs.k8s.io/node-feature-discovery/source"
 )
diff --git a/test/e2e/nfd_gc_test.go b/test/e2e/nfd_gc_test.go
index eab1f4de6..ac825d91b 100644
--- a/test/e2e/nfd_gc_test.go
+++ b/test/e2e/nfd_gc_test.go
@@ -29,8 +29,8 @@ import (
 	extclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/kubernetes/test/e2e/framework"
-	nfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	"sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	"sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
 
 	testutils "sigs.k8s.io/node-feature-discovery/test/e2e/utils"
 	testdeploy "sigs.k8s.io/node-feature-discovery/test/e2e/utils/deployment"
diff --git a/test/e2e/node_feature_discovery_test.go b/test/e2e/node_feature_discovery_test.go
index 12ad9a275..224dece28 100644
--- a/test/e2e/node_feature_discovery_test.go
+++ b/test/e2e/node_feature_discovery_test.go
@@ -42,8 +42,8 @@ import (
 	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 	admissionapi "k8s.io/pod-security-admission/api"
 
-	nfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
 	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 	"sigs.k8s.io/node-feature-discovery/source/custom"
 	testutils "sigs.k8s.io/node-feature-discovery/test/e2e/utils"
diff --git a/test/e2e/utils/crd.go b/test/e2e/utils/crd.go
index ef4821fd9..0f36f54ab 100644
--- a/test/e2e/utils/crd.go
+++ b/test/e2e/utils/crd.go
@@ -32,9 +32,9 @@ import (
 	apiruntime "k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/util/wait"
 	"k8s.io/client-go/kubernetes/scheme"
-	nfdclientset "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned"
-	nfdscheme "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/scheme"
-	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/api/nfd/v1alpha1"
+	nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
+	nfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
+	nfdscheme "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/scheme"
 )
 
 var packagePath string