Repository: kubernetes-sigs/node-feature-discovery
Pull request #1782 (2024-07-11 12:44:32)

Helm: Add revision history limit for master replica
Fixes: #1759 

Tested locally. Output:
```yaml
---
# Source: node-feature-discovery/templates/master.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name:  release-name-node-feature-discovery-master
  namespace: default
  labels:
    helm.sh/chart: node-feature-discovery-0.2.1
    app.kubernetes.io/name: node-feature-discovery
    app.kubernetes.io/instance: release-name
    app.kubernetes.io/version: "master"
    app.kubernetes.io/managed-by: Helm
    role: master
spec:
  replicas: 1
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app.kubernetes.io/name: node-feature-discovery
      app.kubernetes.io/instance: release-name
      role: master
  template:
    metadata:
      labels:
        app.kubernetes.io/name: node-feature-discovery
        app.kubernetes.io/instance: release-name
        role: master
    spec:
      serviceAccountName: release-name-node-feature-discovery
      enableServiceLinks: false
      securityContext:
        {}
      containers:
        - name: master
          securityContext:
            allowPrivilegeEscalation: false
            capabilities:
              drop:
              - ALL
            readOnlyRootFilesystem: true
            runAsNonRoot: true
          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
          ports:
          - containerPort: 8080
            name: grpc
          - containerPort: 8081
            name: metrics
          env:
          - name: NODE_NAME
            valueFrom:
              fieldRef:
                fieldPath: spec.nodeName
          command:
            - "nfd-master"
          resources:
            limits:
              memory: 4Gi
            requests:
              cpu: 100m
              memory: 128Mi
          args:
            ## By default, disable crd controller for other than the default instances
            - "-crd-controller=true"
            # Go over featureGates and add the feature-gate flag
            - "-feature-gates=NodeFeatureAPI=true"
            - "-feature-gates=NodeFeatureGroupAPI=false"
            - "-metrics=8081"
          volumeMounts:
            - name: nfd-master-conf
              mountPath: "/etc/kubernetes/node-feature-discovery"
              readOnly: true
      volumes:
        - name: nfd-master-conf
          configMap:
            name: release-name-node-feature-discovery-master-conf
            items:
              - key: nfd-master.conf
                path: nfd-master.conf
      affinity:
        nodeAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - preference:
              matchExpressions:
              - key: node-role.kubernetes.io/master
                operator: In
                values:
                - ""
            weight: 1
          - preference:
              matchExpressions:
              - key: node-role.kubernetes.io/control-plane
                operator: In
                values:
                - ""
            weight: 1
      tolerations:
        - effect: NoSchedule
          key: node-role.kubernetes.io/master
          operator: Equal
          value: ""
        - effect: NoSchedule
          key: node-role.kubernetes.io/control-plane
          operator: Equal
          value: ""
```

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/1782#" title="Author self-approved">omerap12</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"]} -->
k8s-ci-robot: @omerap12: The following test **failed**, say `/retest` to rerun all failed tests or `/retest-required` to rerun all mandatory failed tests:

Test name | Commit | Details | Required | Rerun command
--- | --- | --- | --- | ---
pull-node-feature-discovery-verify-docs-master | bda18be8079ef7d37feddb754fdf74382034f609 | [link](https://prow.k8s.io/view/gs/kubernetes-jenkins/pr-logs/pull/kubernetes-sigs_node-feature-discovery/1782/pull-node-feature-discovery-verify-docs-master/1811395622138286080) | true | `/test pull-node-feature-discovery-verify-docs-master`

[Full PR test history](https://prow.k8s.io/pr-history?org=kubernetes-sigs&repo=node-feature-discovery&pr=1782). [Your PR dashboard](https://prow.k8s.io/pr?query=is%3Apr%20state%3Aopen%20author%3Aomerap12). Please help us cut down on flakes by [linking to](https://git.k8s.io/community/contributors/devel/sig-testing/flaky-tests.md#filing-issues-for-flaky-tests) an [open issue](https://github.com/kubernetes-sigs/node-feature-discovery/issues?q=is:issue+is:open) when you hit one in your PR.

<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-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository. I understand the commands that are listed [here](https://go.k8s.io/bot-commands).
</details>
<!-- test report -->
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/1782#" title="Author self-approved">omerap12</a>*, *<a href="https://github.com/kubernetes-sigs/node-feature-discovery/pull/1782#pullrequestreview-2174094350" title="Approved">TessaIO</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"]} -->

Patch:
diff --git a/README.md b/README.md
index 5e5c1b98c..52d318d5c 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.16.2
+$ kubectl apply -k https://github.com/kubernetes-sigs/node-feature-discovery/deployment/overlays/default?ref=v0.16.1
   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/cmd/nfd-master/main.go b/cmd/nfd-master/main.go
index 08a35324f..a51309b4a 100644
--- a/cmd/nfd-master/main.go
+++ b/cmd/nfd-master/main.go
@@ -81,6 +81,8 @@ func main() {
 			args.Overrides.ResyncPeriod = overrides.ResyncPeriod
 		case "nfd-api-parallelism":
 			args.Overrides.NfdApiParallelism = overrides.NfdApiParallelism
+		case "enable-nodefeature-api":
+			klog.InfoS("-enable-nodefeature-api is deprecated and will be removed in the next release, use -feature-gate NodeFeatureAPI instead")
 		case "ca-file":
 			klog.InfoS("-ca-file is deprecated, will be removed in a future release along with the deprecated gRPC API")
 		case "cert-file":
@@ -139,6 +141,9 @@ func initFlags(flagset *flag.FlagSet) (*master.Args, *master.ConfigOverrideArgs)
 		"Config file to use.")
 	flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
 		"Kubeconfig to use")
+	flagset.BoolVar(&args.EnableNodeFeatureApi, "enable-nodefeature-api", true,
+		"Enable the NodeFeature CRD API for receiving node features. This will automatically disable the gRPC communication."+
+			" DEPRECATED: will be removed in NFD v0.17. Use -feature-gate NodeFeatureAPI instead.")
 	flagset.BoolVar(&args.CrdController, "featurerules-controller", true,
 		"Enable NFD CRD API controller. DEPRECATED: use -crd-controller instead")
 	flagset.BoolVar(&args.CrdController, "crd-controller", true,
diff --git a/cmd/nfd-worker/main.go b/cmd/nfd-worker/main.go
index 5c204aea4..20a259381 100644
--- a/cmd/nfd-worker/main.go
+++ b/cmd/nfd-worker/main.go
@@ -69,6 +69,8 @@ func main() {
 			klog.InfoS("-cert-file is deprecated, will be removed in a future release along with the deprecated gRPC API")
 		case "key-file":
 			klog.InfoS("-key-file is deprecated, will be removed in a future release along with the deprecated gRPC API")
+		case "enable-nodefeature-api":
+			klog.InfoS("-enable-nodefeature-api is deprecated and will be removed in the next release, use -feature-gate NodeFeatureAPI instead")
 		case "server":
 			klog.InfoS("-server is deprecated, will be removed in a future release along with the deprecated gRPC API")
 		case "server-name-override":
@@ -132,6 +134,9 @@ func initFlags(flagset *flag.FlagSet) (*worker.Args, *worker.ConfigOverrideArgs)
 	flagset.StringVar(&args.KeyFile, "key-file", "",
 		"Private key matching -cert-file."+
 			" DEPRECATED: will be removed in a future release along with the deprecated gRPC API.")
+	flagset.BoolVar(&args.EnableNodeFeatureApi, "enable-nodefeature-api", true,
+		"Enable the NodeFeature CRD API for communicating with nfd-master. This will automatically disable the gRPC communication."+
+			" DEPRECATED: will be removed in NFD v0.17. Use -feature-gate NodeFeatureAPI instead.")
 	flagset.StringVar(&args.Kubeconfig, "kubeconfig", "",
 		"Kubeconfig to use")
 	flagset.BoolVar(&args.Oneshot, "oneshot", false,
diff --git a/deployment/helm/node-feature-discovery/templates/clusterrole.yaml b/deployment/helm/node-feature-discovery/templates/clusterrole.yaml
index 958f043fe..f935cfe41 100644
--- a/deployment/helm/node-feature-discovery/templates/clusterrole.yaml
+++ b/deployment/helm/node-feature-discovery/templates/clusterrole.yaml
@@ -94,7 +94,7 @@ rules:
   - update
 {{- end }}
 
-{{- if and .Values.gc.enable .Values.gc.rbac.create (or .Values.featureGates.NodeFeatureAPI .Values.topologyUpdater.enable) }}
+{{- if and .Values.gc.enable .Values.gc.rbac.create (or (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi) .Values.topologyUpdater.enable) }}
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRole
diff --git a/deployment/helm/node-feature-discovery/templates/clusterrolebinding.yaml b/deployment/helm/node-feature-discovery/templates/clusterrolebinding.yaml
index 8623de606..3f717988b 100644
--- a/deployment/helm/node-feature-discovery/templates/clusterrolebinding.yaml
+++ b/deployment/helm/node-feature-discovery/templates/clusterrolebinding.yaml
@@ -33,7 +33,7 @@ subjects:
   namespace: {{ include "node-feature-discovery.namespace" .  }}
 {{- end }}
 
-{{- if and .Values.gc.enable .Values.gc.rbac.create (or .Values.featureGates.NodeFeatureAPI  .Values.topologyUpdater.enable) }}
+{{- if and .Values.gc.enable .Values.gc.rbac.create (or (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi) .Values.topologyUpdater.enable) }}
 ---
 apiVersion: rbac.authorization.k8s.io/v1
 kind: ClusterRoleBinding
diff --git a/deployment/helm/node-feature-discovery/templates/master.yaml b/deployment/helm/node-feature-discovery/templates/master.yaml
index a15467c83..a61ce9874 100644
--- a/deployment/helm/node-feature-discovery/templates/master.yaml
+++ b/deployment/helm/node-feature-discovery/templates/master.yaml
@@ -13,6 +13,7 @@ metadata:
   {{- end }}
 spec:
   replicas: {{ .Values.master.replicaCount }}
+  revisionHistoryLimit: {{ .Values.master.revisionHistoryLimit }}
   selector:
     matchLabels:
       {{- include "node-feature-discovery.selectorLabels" . | nindent 6 }}
@@ -73,7 +74,7 @@ spec:
             {{- if .Values.master.instance | empty | not }}
             - "-instance={{ .Values.master.instance }}"
             {{- end }}
-            {{- if not .Values.featureGates.NodeFeatureAPI }}
+            {{- if not (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi) }}
             - "-port={{ .Values.master.port | default "8080" }}"
             {{- else if gt (int .Values.master.replicaCount) 1 }}
             - "-enable-leader-election"
diff --git a/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml b/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
index e23a25ad0..182e51aa8 100644
--- a/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
+++ b/deployment/helm/node-feature-discovery/templates/nfd-gc.yaml
@@ -1,4 +1,4 @@
-{{- if and .Values.gc.enable (or .Values.featureGates.NodeFeatureAPI .Values.topologyUpdater.enable) -}}
+{{- if and .Values.gc.enable (or (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi) .Values.topologyUpdater.enable) -}}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -13,6 +13,7 @@ metadata:
   {{- end }}
 spec:
   replicas: {{ .Values.gc.replicaCount | default 1 }}
+  revisionHistoryLimit: {{ .Values.gc.revisionHistoryLimit }}
   selector:
     matchLabels:
       {{- include "node-feature-discovery.selectorLabels" . | nindent 6 }}
diff --git a/deployment/helm/node-feature-discovery/templates/service.yaml b/deployment/helm/node-feature-discovery/templates/service.yaml
index a6e7678fb..7191dca70 100644
--- a/deployment/helm/node-feature-discovery/templates/service.yaml
+++ b/deployment/helm/node-feature-discovery/templates/service.yaml
@@ -1,4 +1,4 @@
-{{- if and (not .Values.featureGates.NodeFeatureAPI) .Values.master.enable }}
+{{- if and (not (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi)) .Values.master.enable }}
 apiVersion: v1
 kind: Service
 metadata:
diff --git a/deployment/helm/node-feature-discovery/templates/serviceaccount.yaml b/deployment/helm/node-feature-discovery/templates/serviceaccount.yaml
index 740160284..59edc5e6c 100644
--- a/deployment/helm/node-feature-discovery/templates/serviceaccount.yaml
+++ b/deployment/helm/node-feature-discovery/templates/serviceaccount.yaml
@@ -27,7 +27,7 @@ metadata:
   {{- end }}
 {{- end }}
 
-{{- if and .Values.gc.enable .Values.gc.serviceAccount.create (or .Values.featureGates.NodeFeatureAPI .Values.topologyUpdater.enable) }}
+{{- if and .Values.gc.enable .Values.gc.serviceAccount.create (or (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi) .Values.topologyUpdater.enable) }}
 ---
 apiVersion: v1
 kind: ServiceAccount
diff --git a/deployment/helm/node-feature-discovery/templates/worker.yaml b/deployment/helm/node-feature-discovery/templates/worker.yaml
index 684681324..d775debca 100644
--- a/deployment/helm/node-feature-discovery/templates/worker.yaml
+++ b/deployment/helm/node-feature-discovery/templates/worker.yaml
@@ -72,7 +72,7 @@ spec:
         command:
         - "nfd-worker"
         args:
-{{- if not .Values.featureGates.NodeFeatureAPI }}
+{{- if not (and .Values.featureGates.NodeFeatureAPI .Values.enableNodeFeatureApi)   }}
         - "-server={{ include "node-feature-discovery.fullname" . }}-master:{{ .Values.master.service.port }}"
 {{- end }}
 {{- if .Values.tls.enable }}
diff --git a/deployment/helm/node-feature-discovery/values.yaml b/deployment/helm/node-feature-discovery/values.yaml
index a5f94df37..b313f20e1 100644
--- a/deployment/helm/node-feature-discovery/values.yaml
+++ b/deployment/helm/node-feature-discovery/values.yaml
@@ -10,6 +10,8 @@ nameOverride: ""
 fullnameOverride: ""
 namespaceOverride: ""
 
+enableNodeFeatureApi: true
+
 featureGates:
   NodeFeatureAPI: true
   NodeFeatureGroupAPI: false
@@ -88,6 +90,9 @@ master:
     # The name of the service account to use.
     # If not set and create is true, a name is generated using the fullname template
     name:
+  
+  # specify how many old ReplicaSets for the Deployment to retain.
+  revisionHistoryLimit: 
 
   rbac:
     create: true
@@ -542,6 +547,9 @@ gc:
   deploymentAnnotations: {}
   affinity: {}
 
+  # specify how many old ReplicaSets for the Deployment to retain.
+  revisionHistoryLimit: 
+
 # Optionally use encryption for worker <--> master comms
 # TODO: verify hostname is not yet supported
 #
diff --git a/docs/deployment/helm.md b/docs/deployment/helm.md
index de727eae4..ed5e15dd7 100644
--- a/docs/deployment/helm.md
+++ b/docs/deployment/helm.md
@@ -101,6 +101,7 @@ 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. |
 | `featureGates.NodeFeatureGroupAPI` | bool | false                        | Enable the [NodeFeatureGroup](../usage/custom-resources.md#nodefeaturegroup) CRD API. |
 | `featureGates.DisableAutoPrefix`   | bool | false                        | Enable [DisableAutoPrefix](../reference/feature-gates.md#disableautoprefix) feature gate. Disables automatic prefixing of unprefixed labels, annotations and extended resources. |
+| `enableNodeFeatureApi`| bool   | true                                    | Enable the [NodeFeature](../usage/custom-resources.md#nodefeature) CRD API for communicating node features. This will automatically disable the gRPC communication. **DEPRECATED**: will be removed in NFD v0.17, use `featureGates.NodeFeatureAPI` instead.    |
 | `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                                                        |
@@ -143,6 +144,7 @@ API's you need to install the prometheus operator in your cluster.
 | `master.nfdApiParallelism` | integer | 10                                      | Specifies the maximum number of concurrent node updates. |
 | `master.config`            | dict    |                                         | NFD master [configuration](../reference/master-configuration-reference) |
 | `master.args`              | array   | []                                      | Additional [command line arguments](../reference/master-commandline-reference.md) to pass to nfd-master |
+| `master.revisionHistoryLimit` | integer |                                      | Specify how many old ReplicaSets for this Deployment you want to retain. [revisionHistoryLimit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#revision-history-limit) |
 
 ### Worker pod parameters
 
@@ -216,9 +218,10 @@ API's you need to install the prometheus operator in your cluster.
 | `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/)              |
 | `gc.annotations`                      | dict   | {}      | Garbage collector pod [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)                  |
-| `gc.deploymentAnnotations`            | dict   | {}      | Garbage collector deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)          |
+| `gc.deploymentAnnotations`            | dict   | {}      | Garbage collector deployment [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)           |
 | `gc.affinity`                         | dict   | {}      | Garbage collector pod [affinity](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes-using-node-affinity/)    |
-| `gc.args`                             | array   | []     | Additional [command line arguments](../reference/gc-commandline-reference.md) to pass to nfd-gc |
+| `gc.args`                             | array  | []      | Additional [command line arguments](../reference/gc-commandline-reference.md) to pass to nfd-gc |
+| `gc.revisionHistoryLimit`             | integer |        | Specify how many old ReplicaSets for this Deployment you want to retain. [revisionHistoryLimit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#revision-history-limit) |
 
 <!-- Links -->
 [rbac]: https://kubernetes.io/docs/reference/access-authn-authz/rbac/
diff --git a/docs/reference/master-commandline-reference.md b/docs/reference/master-commandline-reference.md
index ffdd07e90..a64670e3a 100644
--- a/docs/reference/master-commandline-reference.md
+++ b/docs/reference/master-commandline-reference.md
@@ -175,6 +175,27 @@ nfd-master -verify-node-name -ca-file=/opt/nfd/ca.crt \
     -cert-file=/opt/nfd/master.crt -key-file=/opt/nfd/master.key
 ```
 
+### -enable-nodefeature-api
+
+**DEPRECATED**: will be removed in NFD v0.17. Use'
+[`-feature-gates`](#-feature-gates)
+[NodeFeatureAPI](feature-gates.md#nodefeatureapi) instead.
+
+> **NOTE** the gRPC API is deprecated and will be removed in a future release.
+
+The `-enable-nodefeature-api` flag enables/disables the
+[NodeFeature](../usage/custom-resources.md#nodefeature) CRD API for receiving
+feature requests. This will also automatically disable/enable the gRPC
+interface.
+
+Default: true
+
+Example:
+
+```bash
+nfd-master -enable-nodefeature-api=false
+```
+
 ### -enable-leader-election
 
 The `-enable-leader-election` flag enables leader election for NFD-Master.
diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go
index e34bb3e5f..b9a1de156 100644
--- a/pkg/nfd-master/nfd-master.go
+++ b/pkg/nfd-master/nfd-master.go
@@ -111,15 +111,16 @@ type ConfigOverrideArgs struct {
 
 // Args holds command line arguments
 type Args struct {
-	CaFile        string
-	CertFile      string
-	ConfigFile    string
-	Instance      string
-	KeyFile       string
-	Klog          map[string]*utils.KlogFlagVal
-	Kubeconfig    string
-	CrdController bool
-	Port          int
+	CaFile               string
+	CertFile             string
+	ConfigFile           string
+	Instance             string
+	KeyFile              string
+	Klog                 map[string]*utils.KlogFlagVal
+	Kubeconfig           string
+	CrdController        bool
+	EnableNodeFeatureApi bool
+	Port                 int
 	// GrpcHealthPort is only needed to avoid races between tests (by skipping the health server).
 	// Could be removed when gRPC labler service is dropped (when nfd-worker tests stop running nfd-master).
 	GrpcHealthPort       int
@@ -336,7 +337,7 @@ func (m *nfdMaster) Run() error {
 	grpcErr := make(chan error)
 	// If the NodeFeature API is enabled, don'tregister the labeler API
 	// server. Otherwise, register the labeler server.
-	if !nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) {
+	if !nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) || !m.args.EnableNodeFeatureApi {
 		go m.runGrpcServer(grpcErr)
 	}
 
@@ -391,7 +392,7 @@ func (m *nfdMaster) Run() error {
 			m.updaterPool.start(m.config.NfdApiParallelism)
 
 			// Update all nodes when the configuration changes
-			if m.nfdController != nil && nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) {
+			if m.nfdController != nil && nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) && m.args.EnableNodeFeatureApi {
 				m.nfdController.updateAllNodesChan <- struct{}{}
 			}
 
@@ -489,7 +490,7 @@ func (m *nfdMaster) runGrpcServer(errChan chan<- error) {
 func (m *nfdMaster) nfdAPIUpdateHandler() {
 	// We want to unconditionally update all nodes at startup if gRPC is
 	// disabled (i.e. NodeFeature API is enabled)
-	updateAll := nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI)
+	updateAll := nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) && m.args.EnableNodeFeatureApi
 	updateNodes := make(map[string]struct{})
 	nodeFeatureGroup := make(map[string]struct{})
 	updateAllNodeFeatureGroups := false
@@ -1511,7 +1512,7 @@ func (m *nfdMaster) startNfdApiController() error {
 	}
 	klog.InfoS("starting the nfd api controller")
 	m.nfdController, err = newNfdController(kubeconfig, nfdApiControllerOptions{
-		DisableNodeFeature: !nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI),
+		DisableNodeFeature: !nfdfeatures.NFDFeatureGate.Enabled(nfdfeatures.NodeFeatureAPI) || !m.args.EnableNodeFeatureApi,
 		ResyncPeriod:       m.config.ResyncPeriod.Duration,
 	})
 	if err != nil {
diff --git a/pkg/nfd-worker/nfd-worker.go b/pkg/nfd-worker/nfd-worker.go
index f0ed390bc..b9bb87f60 100644
--- a/pkg/nfd-worker/nfd-worker.go
+++ b/pkg/nfd-worker/nfd-worker.go
@@ -97,18 +97,19 @@ type Labels map[string]string
 
 // Args are the command line arguments of NfdWorker.
 type Args struct {
-	CaFile             string
-	CertFile           string
-	ConfigFile         string
-	KeyFile            string
-	Klog               map[string]*utils.KlogFlagVal
-	Kubeconfig         string
-	Oneshot            bool
-	Options            string
-	Server             string
-	ServerNameOverride string
-	MetricsPort        int
-	GrpcHealthPort     int
+	CaFile               string
+	CertFile             string
+	ConfigFile           string
+	EnableNodeFeatureApi bool
+	KeyFile              string
+	Klog                 map[string]*utils.KlogFlagVal
+	Kubeconfig           string
+	Oneshot              bool
+	Options              string
+	Server               string
+	ServerNameOverride   string
+	MetricsPort          int
+	GrpcHealthPort       int
 
 	Overrides ConfigOverrideArgs
 }
@@ -131,7 +132,7 @@ type nfdWorker struct {
 	grpcClient          pb.LabelerClient
 	healthServer        *grpc.Server
 	k8sClient           k8sclient.Interface
-	nfdClient           nfdclient.Interface
+	nfdClient           *nfdclient.Clientset
 	stop                chan struct{} // channel for signaling stop
 	featureSources      []source.FeatureSource
 	labelSources        []source.LabelSource
@@ -150,26 +151,20 @@ type NfdWorkerOption interface {
 
 // WithArgs is used for passing settings from command line arguments.
 func WithArgs(args *Args) NfdWorkerOption {
-	return &nfdWorkerOpt{f: func(n *nfdWorker) { n.args = *args }}
+	return &nfdMWorkerOpt{f: func(n *nfdWorker) { n.args = *args }}
 }
 
 // WithKuberneteClient forces to use the given kubernetes client, without
 // initializing one from kubeconfig.
 func WithKubernetesClient(cli k8sclient.Interface) NfdWorkerOption {
-	return &nfdWorkerOpt{f: func(n *nfdWorker) { n.k8sClient = cli }}
+	return &nfdMWorkerOpt{f: func(n *nfdWorker) { n.k8sClient = cli }}
 }
 
-// WithNFDClient forces to use the given client for the NFD API, without
-// initializing one from kubeconfig.
-func WithNFDClient(cli nfdclient.Interface) NfdWorkerOption {
-	return &nfdWorkerOpt{f: func(n *nfdWorker) { n.nfdClient = cli }}
-}
-
-type nfdWorkerOpt struct {
+type nfdMWorkerOpt struct {
 	f func(*nfdWorker)
 }
 
-func (f *nfdWorkerOpt) apply(n *nfdWorker) {
+func (f *nfdMWorkerOpt) apply(n *nfdWorker) {
 	f.f(n)
 }
 
@@ -396,7 +391,7 @@ func (w *nfdWorker) Run() error {
 				return err
 			}
 			// Manage connection to master
-			if w.config.Core.NoPublish || !features.NFDFeatureGate.Enabled(features.NodeFeatureAPI) {
+			if w.config.Core.NoPublish || !features.NFDFeatureGate.Enabled(features.NodeFeatureAPI) || !w.args.EnableNodeFeatureApi {
 				w.grpcDisconnect()
 			}
 
@@ -740,7 +735,7 @@ func getFeatureLabels(source source.LabelSource, labelWhiteList regexp.Regexp) (
 
 // advertiseFeatures advertises the features of a Kubernetes node
 func (w *nfdWorker) advertiseFeatures(labels Labels) error {
-	if features.NFDFeatureGate.Enabled(features.NodeFeatureAPI) {
+	if features.NFDFeatureGate.Enabled(features.NodeFeatureAPI) && w.args.EnableNodeFeatureApi {
 		// Create/update NodeFeature CR object
 		if err := w.updateNodeFeatureObject(labels); err != nil {
 			return fmt.Errorf("failed to advertise features (via CRD API): %w", err)
@@ -842,7 +837,7 @@ func (m *nfdWorker) updateNodeFeatureObject(labels Labels) error {
 }
 
 // getNfdClient returns the clientset for using the nfd CRD api
-func (m *nfdWorker) getNfdClient() (nfdclient.Interface, error) {
+func (m *nfdWorker) getNfdClient() (*nfdclient.Clientset, error) {
 	if m.nfdClient != nil {
 		return m.nfdClient, nil
 	}
diff --git a/source/memory/memory.go b/source/memory/memory.go
index ab9bc02ed..5c4ce9f61 100644
--- a/source/memory/memory.go
+++ b/source/memory/memory.go
@@ -131,7 +131,7 @@ func (s *memorySource) GetFeatures() *nfdv1alpha1.Features {
 // detectSwap detects Swap node information
 func detectSwap() (map[string]string, error) {
 	procBasePath := hostpath.ProcDir.Path("swaps")
-	lines, err := getNumberOfNonEmptyLinesFromFile(procBasePath)
+	lines, err := getNumberOfLinesFromFile(procBasePath)
 	if err != nil {
 		return nil, fmt.Errorf("failed to read swaps file: %w", err)
 	}
@@ -195,18 +195,12 @@ func readNdDeviceInfo(path string) nfdv1alpha1.InstanceFeature {
 	return *nfdv1alpha1.NewInstanceFeature(attrs)
 }
 
-func getNumberOfNonEmptyLinesFromFile(path string) (int, error) {
+func getNumberOfLinesFromFile(path string) (int, error) {
 	data, err := os.ReadFile(path)
 	if err != nil {
 		return 0, err
 	}
-	length := 0
-	for _, line := range strings.Split(string(data), "\n") {
-		if strings.TrimSpace(line) != "" {
-			length++
-		}
-	}
-	return length, nil
+	return len(strings.Split(string(data), "\n")), nil
 }
 
 func init() {


Test patch:
diff --git a/pkg/nfd-worker/nfd-worker_test.go b/pkg/nfd-worker/nfd-worker_test.go
index 6f9220568..42fe8b71b 100644
--- a/pkg/nfd-worker/nfd-worker_test.go
+++ b/pkg/nfd-worker/nfd-worker_test.go
@@ -24,14 +24,9 @@ import (
 	"time"
 
 	. "github.com/smartystreets/goconvey/convey"
-	"golang.org/x/net/context"
-	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	fakeclient "k8s.io/client-go/kubernetes/fake"
 	"k8s.io/klog/v2"
 
-	fakenfdclient "sigs.k8s.io/node-feature-discovery/api/generated/clientset/versioned/fake"
-	"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/features"
 	master "sigs.k8s.io/node-feature-discovery/pkg/nfd-master"
 	worker "sigs.k8s.io/node-feature-discovery/pkg/nfd-worker"
@@ -44,13 +39,6 @@ type testContext struct {
 	errs   chan error
 }
 
-func initializeFeatureGates() {
-	if err := features.NFDMutableFeatureGate.Add(features.DefaultNFDFeatureGates); err != nil {
-		klog.ErrorS(err, "failed to add default feature gates")
-		os.Exit(1)
-	}
-}
-
 func setupTest(args *master.Args) testContext {
 	// Fixed port and no-publish, for convenience
 	publish := true
@@ -60,7 +48,10 @@ func setupTest(args *master.Args) testContext {
 	}
 	args.Port = 8192
 	// Add FeatureGates flag
-	initializeFeatureGates()
+	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)
 	_ = features.NFDMutableFeatureGate.OverrideDefault(features.NodeFeatureGroupAPI, false)
 	m, err := master.NewNfdMaster(
@@ -115,95 +106,21 @@ func TestNewNfdWorker(t *testing.T) {
 }
 
 func TestRun(t *testing.T) {
-	nfdCli := fakenfdclient.NewSimpleClientset()
-	initializeFeatureGates()
-	Convey("When running nfd-worker", t, func() {
+	ctx := setupTest(&master.Args{})
+	defer teardownTest(ctx)
+	Convey("When running nfd-worker against nfd-master", t, func() {
 		Convey("When publishing features from fake source", func() {
-			os.Setenv("NODE_NAME", "fake-node")
-			os.Setenv("KUBERNETES_NAMESPACE", "fake-ns")
 			args := &worker.Args{
-				Oneshot: true,
-				Overrides: worker.ConfigOverrideArgs{
-					FeatureSources: &utils.StringSliceVal{"fake"},
-					LabelSources:   &utils.StringSliceVal{"fake"},
-				},
+				Server:    "localhost:8192",
+				Oneshot:   true,
+				Overrides: worker.ConfigOverrideArgs{LabelSources: &utils.StringSliceVal{"fake"}},
 			}
-			w, _ := worker.NewNfdWorker(
-				worker.WithArgs(args),
-				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()),
-				worker.WithNFDClient(nfdCli),
-			)
-			err := w.Run()
+			fooasdf, _ := worker.NewNfdWorker(worker.WithArgs(args),
+				worker.WithKubernetesClient(fakeclient.NewSimpleClientset()))
+			err := fooasdf.Run()
 			Convey("No error should be returned", func() {
 				So(err, ShouldBeNil)
 			})
-			Convey("NodeFeture object should be created", func() {
-				nf, err := nfdCli.NfdV1alpha1().NodeFeatures("fake-ns").Get(context.TODO(), "fake-node", metav1.GetOptions{})
-				So(err, ShouldBeNil)
-
-				nfExpected := &nfdv1alpha1.NodeFeature{
-					ObjectMeta: metav1.ObjectMeta{
-						Name:      "fake-node",
-						Namespace: "fake-ns",
-						Labels: map[string]string{
-							"nfd.node.kubernetes.io/node-name": "fake-node",
-						},
-						Annotations: map[string]string{
-							"nfd.node.kubernetes.io/worker.version": "undefined",
-						},
-						OwnerReferences: []metav1.OwnerReference{},
-					},
-					Spec: nfdv1alpha1.NodeFeatureSpec{
-						Labels: map[string]string{
-							"feature.node.kubernetes.io/fake-fakefeature1": "true",
-							"feature.node.kubernetes.io/fake-fakefeature2": "true",
-							"feature.node.kubernetes.io/fake-fakefeature3": "true",
-						},
-						Features: v1alpha1.Features{
-							Flags: map[string]v1alpha1.FlagFeatureSet{
-								"fake.flag": {
-									Elements: map[string]v1alpha1.Nil{
-										"flag_1": {},
-										"flag_2": {},
-										"flag_3": {}},
-								},
-							},
-							Attributes: map[string]v1alpha1.AttributeFeatureSet{
-								"fake.attribute": {
-									Elements: map[string]string{
-										"attr_1": "true",
-										"attr_2": "false",
-										"attr_3": "10",
-									},
-								},
-							},
-							Instances: map[string]v1alpha1.InstanceFeatureSet{
-								"fake.instance": {
-									Elements: []v1alpha1.InstanceFeature{
-										{Attributes: map[string]string{
-											"name":   "instance_1",
-											"attr_1": "true",
-											"attr_2": "false",
-											"attr_3": "10",
-											"attr_4": "foobar",
-										}},
-										{Attributes: map[string]string{
-											"name":   "instance_2",
-											"attr_1": "true",
-											"attr_2": "true",
-											"attr_3": "100",
-										}},
-										{Attributes: map[string]string{
-											"name": "instance_3",
-										}},
-									},
-								},
-							},
-						},
-					},
-				}
-				So(nf, ShouldResemble, nfExpected)
-			})
 		})
 	})
 }
diff --git a/source/memory/memory_test.go b/source/memory/memory_test.go
index 535decb03..c5edc1b36 100644
--- a/source/memory/memory_test.go
+++ b/source/memory/memory_test.go
@@ -54,7 +54,7 @@ func TestGetNumberofLinesFromFile(t *testing.T) {
 		},
 	}
 	for _, tc := range tc {
-		actual, err := getNumberOfNonEmptyLinesFromFile(tc.path)
+		actual, err := getNumberOfLinesFromFile(tc.path)
 		if tc.expectErr {
 			assert.NotNil(t, err, "should get an error")
 		}
diff --git a/source/memory/testdata/noswap b/source/memory/testdata/noswap
index 613dce24e..37a6cc05c 100644
--- a/source/memory/testdata/noswap
+++ b/source/memory/testdata/noswap
@@ -1,1 +1,1 @@
-Filename                          Type        Size     Used    Priority
+Filename                          Type        Size     Used    Priority
\ No newline at end of file
diff --git a/source/memory/testdata/swap b/source/memory/testdata/swap
index 8eb673444..6ffbd4070 100644
--- a/source/memory/testdata/swap
+++ b/source/memory/testdata/swap
@@ -1,2 +1,2 @@
 Filename                          Type        Size     Used    Priority
-dummyfile   partition   65555   0       -1
+dummyfile   partition   65555   0       -1
\ No newline at end of file