Repository: kubernetes-sigs/node-feature-discovery
Pull request #1568 (2024-01-25 15:25:29)

pkg/utils: move JsonPatch from pkg/apihelper
Part of the effort of dropping apihelper package (#1561)


Patch:
diff --git a/pkg/apihelper/apihelpers.go b/pkg/apihelper/apihelpers.go
index b067e539b..c458d67fc 100644
--- a/pkg/apihelper/apihelpers.go
+++ b/pkg/apihelper/apihelpers.go
@@ -22,6 +22,7 @@ import (
 	topologyclientset "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned"
 	corev1 "k8s.io/api/core/v1"
 	k8sclient "k8s.io/client-go/kubernetes"
+	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
 
 // APIHelpers represents a set of API helpers for Kubernetes
@@ -39,10 +40,10 @@ type APIHelpers interface {
 	UpdateNode(*k8sclient.Clientset, *corev1.Node) error
 
 	// PatchNode updates the node object via the API server using a client.
-	PatchNode(*k8sclient.Clientset, string, []JsonPatch) error
+	PatchNode(*k8sclient.Clientset, string, []utils.JsonPatch) error
 
 	// PatchNodeStatus updates the node status via the API server using a client.
-	PatchNodeStatus(*k8sclient.Clientset, string, []JsonPatch) error
+	PatchNodeStatus(*k8sclient.Clientset, string, []utils.JsonPatch) error
 
 	// GetTopologyClient returns a topologyclientset
 	GetTopologyClient() (*topologyclientset.Clientset, error)
diff --git a/pkg/apihelper/k8shelpers.go b/pkg/apihelper/k8shelpers.go
index 4943f0fb9..8d8f494e0 100644
--- a/pkg/apihelper/k8shelpers.go
+++ b/pkg/apihelper/k8shelpers.go
@@ -26,6 +26,7 @@ import (
 	"k8s.io/apimachinery/pkg/types"
 	k8sclient "k8s.io/client-go/kubernetes"
 	restclient "k8s.io/client-go/rest"
+	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
 
 // K8sHelpers implements APIHelpers
@@ -77,7 +78,7 @@ func (h K8sHelpers) UpdateNode(c *k8sclient.Clientset, n *corev1.Node) error {
 	return nil
 }
 
-func (h K8sHelpers) PatchNode(c *k8sclient.Clientset, nodeName string, patches []JsonPatch) error {
+func (h K8sHelpers) PatchNode(c *k8sclient.Clientset, nodeName string, patches []utils.JsonPatch) error {
 	if len(patches) > 0 {
 		data, err := json.Marshal(patches)
 		if err == nil {
@@ -88,7 +89,7 @@ func (h K8sHelpers) PatchNode(c *k8sclient.Clientset, nodeName string, patches [
 	return nil
 }
 
-func (h K8sHelpers) PatchNodeStatus(c *k8sclient.Clientset, nodeName string, patches []JsonPatch) error {
+func (h K8sHelpers) PatchNodeStatus(c *k8sclient.Clientset, nodeName string, patches []utils.JsonPatch) error {
 	if len(patches) > 0 {
 		data, err := json.Marshal(patches)
 		if err == nil {
diff --git a/pkg/apihelper/mock_APIHelpers.go b/pkg/apihelper/mock_APIHelpers.go
index 60b349d4e..46341ae2d 100644
--- a/pkg/apihelper/mock_APIHelpers.go
+++ b/pkg/apihelper/mock_APIHelpers.go
@@ -6,6 +6,8 @@ import (
 	mock "github.com/stretchr/testify/mock"
 	kubernetes "k8s.io/client-go/kubernetes"
 
+	utils "sigs.k8s.io/node-feature-discovery/pkg/utils"
+
 	v1 "k8s.io/api/core/v1"
 
 	versioned "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned"
@@ -147,11 +149,11 @@ func (_m *MockAPIHelpers) GetTopologyClient() (*versioned.Clientset, error) {
 }
 
 // PatchNode provides a mock function with given fields: _a0, _a1, _a2
-func (_m *MockAPIHelpers) PatchNode(_a0 *kubernetes.Clientset, _a1 string, _a2 []JsonPatch) error {
+func (_m *MockAPIHelpers) PatchNode(_a0 *kubernetes.Clientset, _a1 string, _a2 []utils.JsonPatch) error {
 	ret := _m.Called(_a0, _a1, _a2)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*kubernetes.Clientset, string, []JsonPatch) error); ok {
+	if rf, ok := ret.Get(0).(func(*kubernetes.Clientset, string, []utils.JsonPatch) error); ok {
 		r0 = rf(_a0, _a1, _a2)
 	} else {
 		r0 = ret.Error(0)
@@ -161,11 +163,11 @@ func (_m *MockAPIHelpers) PatchNode(_a0 *kubernetes.Clientset, _a1 string, _a2 [
 }
 
 // PatchNodeStatus provides a mock function with given fields: _a0, _a1, _a2
-func (_m *MockAPIHelpers) PatchNodeStatus(_a0 *kubernetes.Clientset, _a1 string, _a2 []JsonPatch) error {
+func (_m *MockAPIHelpers) PatchNodeStatus(_a0 *kubernetes.Clientset, _a1 string, _a2 []utils.JsonPatch) error {
 	ret := _m.Called(_a0, _a1, _a2)
 
 	var r0 error
-	if rf, ok := ret.Get(0).(func(*kubernetes.Clientset, string, []JsonPatch) error); ok {
+	if rf, ok := ret.Get(0).(func(*kubernetes.Clientset, string, []utils.JsonPatch) error); ok {
 		r0 = rf(_a0, _a1, _a2)
 	} else {
 		r0 = ret.Error(0)
diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go
index 168867d2b..46a790eb2 100644
--- a/pkg/nfd-master/nfd-master.go
+++ b/pkg/nfd-master/nfd-master.go
@@ -1146,14 +1146,14 @@ func (m *nfdMaster) getKubeconfig() (*restclient.Config, error) {
 }
 
 // createPatches is a generic helper that returns json patch operations to perform
-func createPatches(removeKeys []string, oldItems map[string]string, newItems map[string]string, jsonPath string) []apihelper.JsonPatch {
-	patches := []apihelper.JsonPatch{}
+func createPatches(removeKeys []string, oldItems map[string]string, newItems map[string]string, jsonPath string) []utils.JsonPatch {
+	patches := []utils.JsonPatch{}
 
 	// Determine items to remove
 	for _, key := range removeKeys {
 		if _, ok := oldItems[key]; ok {
 			if _, ok := newItems[key]; !ok {
-				patches = append(patches, apihelper.NewJsonPatch("remove", jsonPath, key, ""))
+				patches = append(patches, utils.NewJsonPatch("remove", jsonPath, key, ""))
 			}
 		}
 	}
@@ -1162,10 +1162,10 @@ func createPatches(removeKeys []string, oldItems map[string]string, newItems map
 	for key, newVal := range newItems {
 		if oldVal, ok := oldItems[key]; ok {
 			if newVal != oldVal {
-				patches = append(patches, apihelper.NewJsonPatch("replace", jsonPath, key, newVal))
+				patches = append(patches, utils.NewJsonPatch("replace", jsonPath, key, newVal))
 			}
 		} else {
-			patches = append(patches, apihelper.NewJsonPatch("add", jsonPath, key, newVal))
+			patches = append(patches, utils.NewJsonPatch("add", jsonPath, key, newVal))
 		}
 	}
 
@@ -1174,8 +1174,8 @@ func createPatches(removeKeys []string, oldItems map[string]string, newItems map
 
 // createExtendedResourcePatches returns a slice of operations to perform on
 // the node status
-func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResources ExtendedResources) []apihelper.JsonPatch {
-	patches := []apihelper.JsonPatch{}
+func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResources ExtendedResources) []utils.JsonPatch {
+	patches := []utils.JsonPatch{}
 
 	// Form a list of namespaced resource names managed by us
 	oldResources := stringToNsNames(n.Annotations[m.instanceAnnotation(nfdv1alpha1.ExtendedResourceAnnotation)], nfdv1alpha1.FeatureLabelNs)
@@ -1185,8 +1185,8 @@ func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResour
 		if _, ok := n.Status.Capacity[corev1.ResourceName(resource)]; ok {
 			// check if the ext resource is still needed
 			if _, extResNeeded := extendedResources[resource]; !extResNeeded {
-				patches = append(patches, apihelper.NewJsonPatch("remove", "/status/capacity", resource, ""))
-				patches = append(patches, apihelper.NewJsonPatch("remove", "/status/allocatable", resource, ""))
+				patches = append(patches, utils.NewJsonPatch("remove", "/status/capacity", resource, ""))
+				patches = append(patches, utils.NewJsonPatch("remove", "/status/allocatable", resource, ""))
 			}
 		}
 	}
@@ -1197,11 +1197,11 @@ func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResour
 		if quantity, ok := n.Status.Capacity[corev1.ResourceName(resource)]; ok {
 			val, _ := quantity.AsInt64()
 			if strconv.FormatInt(val, 10) != value {
-				patches = append(patches, apihelper.NewJsonPatch("replace", "/status/capacity", resource, value))
-				patches = append(patches, apihelper.NewJsonPatch("replace", "/status/allocatable", resource, value))
+				patches = append(patches, utils.NewJsonPatch("replace", "/status/capacity", resource, value))
+				patches = append(patches, utils.NewJsonPatch("replace", "/status/allocatable", resource, value))
 			}
 		} else {
-			patches = append(patches, apihelper.NewJsonPatch("add", "/status/capacity", resource, value))
+			patches = append(patches, utils.NewJsonPatch("add", "/status/capacity", resource, value))
 			// "allocatable" gets added implicitly after adding to capacity
 		}
 	}
diff --git a/pkg/apihelper/jsonpatch.go b/pkg/utils/jsonpatch.go
similarity index 98%
rename from pkg/apihelper/jsonpatch.go
rename to pkg/utils/jsonpatch.go
index acb9aba8e..bd89302ba 100644
--- a/pkg/apihelper/jsonpatch.go
+++ b/pkg/utils/jsonpatch.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package apihelper
+package utils
 
 import (
 	"path"


Test patch:
diff --git a/pkg/nfd-master/nfd-master-internal_test.go b/pkg/nfd-master/nfd-master-internal_test.go
index 0183dd6f8..20c6d6841 100644
--- a/pkg/nfd-master/nfd-master-internal_test.go
+++ b/pkg/nfd-master/nfd-master-internal_test.go
@@ -151,9 +151,9 @@ func TestUpdateNodeObject(t *testing.T) {
 		sort.Strings(fakeExtResourceNames)
 
 		// Create a list of expected node status patches
-		statusPatches := []apihelper.JsonPatch{}
+		statusPatches := []utils.JsonPatch{}
 		for k, v := range fakeExtResources {
-			statusPatches = append(statusPatches, apihelper.NewJsonPatch("add", "/status/capacity", k, v))
+			statusPatches = append(statusPatches, utils.NewJsonPatch("add", "/status/capacity", k, v))
 		}
 
 		mockAPIHelper := new(apihelper.MockAPIHelpers)
@@ -166,17 +166,17 @@ func TestUpdateNodeObject(t *testing.T) {
 
 		Convey("When I successfully update the node with feature labels", func() {
 			// Create a list of expected node metadata patches
-			metadataPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("replace", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureAnnotationsTrackingAnnotation, strings.Join(fakeFeatureAnnotationsNames, ",")),
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
-				apihelper.NewJsonPatch("remove", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/old-feature", ""),
+			metadataPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("replace", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureAnnotationsTrackingAnnotation, strings.Join(fakeFeatureAnnotationsNames, ",")),
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
+				utils.NewJsonPatch("remove", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/old-feature", ""),
 			}
 			for k, v := range fakeFeatureLabels {
-				metadataPatches = append(metadataPatches, apihelper.NewJsonPatch("add", "/metadata/labels", k, v))
+				metadataPatches = append(metadataPatches, utils.NewJsonPatch("add", "/metadata/labels", k, v))
 			}
 			for k, v := range fakeAnnotations {
-				metadataPatches = append(metadataPatches, apihelper.NewJsonPatch("add", "/metadata/annotations", k, v))
+				metadataPatches = append(metadataPatches, utils.NewJsonPatch("add", "/metadata/annotations", k, v))
 			}
 
 			mockAPIHelper.On("GetClient").Return(mockClient, nil)
@@ -244,7 +244,7 @@ func TestUpdateMasterNode(t *testing.T) {
 		mockClient := &k8sclient.Clientset{}
 		mockNode := newMockNode()
 		Convey("When update operation succeeds", func() {
-			expectedPatches := []apihelper.JsonPatch{}
+			expectedPatches := []utils.JsonPatch{}
 			mockHelper.On("GetClient").Return(mockClient, nil)
 			mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil)
 			mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(nil)
@@ -297,9 +297,9 @@ func TestAddingExtResources(t *testing.T) {
 		Convey("When there are matching labels", func() {
 			mockNode := newMockNode()
 			mockResourceLabels := ExtendedResources{"feature-1": "1", "feature-2": "2"}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/status/capacity", "feature-1", "1"),
-				apihelper.NewJsonPatch("add", "/status/capacity", "feature-2", "2"),
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/status/capacity", "feature-1", "1"),
+				utils.NewJsonPatch("add", "/status/capacity", "feature-2", "2"),
 			}
 			patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
 			So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
@@ -317,9 +317,9 @@ func TestAddingExtResources(t *testing.T) {
 			mockNode := newMockNode()
 			mockNode.Status.Capacity[corev1.ResourceName("feature-1")] = *resource.NewQuantity(2, resource.BinarySI)
 			mockResourceLabels := ExtendedResources{"feature-1": "1"}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"),
-				apihelper.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"),
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"),
+				utils.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"),
 			}
 			patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
 			So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
@@ -379,15 +379,15 @@ func TestSetLabels(t *testing.T) {
 		}
 		sort.Strings(mockLabelNames)
 
-		expectedStatusPatches := []apihelper.JsonPatch{}
+		expectedStatusPatches := []utils.JsonPatch{}
 
 		Convey("When node update succeeds", func() {
 			mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
 			}
 			for k, v := range mockLabels {
-				expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", k, v))
+				expectedPatches = append(expectedPatches, utils.NewJsonPatch("add", "/metadata/labels", k, v))
 			}
 
 			mockHelper.On("GetClient").Return(mockClient, nil)
@@ -402,9 +402,9 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("When -label-whitelist is specified", func() {
 			mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
-				apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
+				utils.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
 			}
 
 			mockMaster.config.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
@@ -434,14 +434,14 @@ func TestSetLabels(t *testing.T) {
 				vendorProfileLabel:                     "val-7",
 				"--invalid-name--":                     "valid-val",
 				"valid-name":                           "--invalid-val--"}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/metadata/annotations",
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/metadata/annotations",
 					instance+"."+nfdv1alpha1.FeatureLabelsAnnotation,
 					"feature-1,valid.ns/feature-2,"+vendorFeatureLabel+","+vendorProfileLabel),
-				apihelper.NewJsonPatch("add", "/metadata/labels", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
-				apihelper.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
-				apihelper.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
-				apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
+				utils.NewJsonPatch("add", "/metadata/labels", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
+				utils.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
+				utils.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
+				utils.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
 			}
 
 			mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}}
@@ -461,14 +461,14 @@ func TestSetLabels(t *testing.T) {
 
 		Convey("When -resource-labels is specified", func() {
 			mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
-			expectedPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
-				apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
-				apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
+			expectedPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
+				utils.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
+				utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
 			}
-			expectedStatusPatches := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
-				apihelper.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-3", mockLabels["feature.node.kubernetes.io/feature-3"]),
+			expectedStatusPatches := []utils.JsonPatch{
+				utils.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/feature-1"]),
+				utils.NewJsonPatch("add", "/status/capacity", "feature.node.kubernetes.io/feature-3", mockLabels["feature.node.kubernetes.io/feature-3"]),
 			}
 
 			mockMaster.config.ResourceLabels = map[string]struct{}{"feature.node.kubernetes.io/feature-3": {}, "feature-1": {}}
@@ -605,9 +605,9 @@ func TestCreatePatches(t *testing.T) {
 
 		Convey("When when there are itmes to remoe but none to add or update", func() {
 			p := createPatches([]string{"key-2", "key-3", "foo"}, existingItems, map[string]string{}, jsonPath)
-			expected := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("remove", jsonPath, "key-2", ""),
-				apihelper.NewJsonPatch("remove", jsonPath, "key-3", ""),
+			expected := []utils.JsonPatch{
+				utils.NewJsonPatch("remove", jsonPath, "key-2", ""),
+				utils.NewJsonPatch("remove", jsonPath, "key-3", ""),
 			}
 			So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected))
 		})
@@ -615,9 +615,9 @@ func TestCreatePatches(t *testing.T) {
 		Convey("When when there are no itmes to remove but new items to add", func() {
 			newItems := map[string]string{"new-key": "new-val", "key-1": "new-1"}
 			p := createPatches([]string{"key-1"}, existingItems, newItems, jsonPath)
-			expected := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
-				apihelper.NewJsonPatch("replace", jsonPath, "key-1", newItems["key-1"]),
+			expected := []utils.JsonPatch{
+				utils.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
+				utils.NewJsonPatch("replace", jsonPath, "key-1", newItems["key-1"]),
 			}
 			So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected))
 		})
@@ -625,12 +625,12 @@ func TestCreatePatches(t *testing.T) {
 		Convey("When when there are items to remove add and update", func() {
 			newItems := map[string]string{"new-key": "new-val", "key-2": "new-2", "key-4": "val-4"}
 			p := createPatches([]string{"key-1", "key-2", "key-3", "foo"}, existingItems, newItems, jsonPath)
-			expected := []apihelper.JsonPatch{
-				apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
-				apihelper.NewJsonPatch("add", jsonPath, "key-4", newItems["key-4"]),
-				apihelper.NewJsonPatch("replace", jsonPath, "key-2", newItems["key-2"]),
-				apihelper.NewJsonPatch("remove", jsonPath, "key-1", ""),
-				apihelper.NewJsonPatch("remove", jsonPath, "key-3", ""),
+			expected := []utils.JsonPatch{
+				utils.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
+				utils.NewJsonPatch("add", jsonPath, "key-4", newItems["key-4"]),
+				utils.NewJsonPatch("replace", jsonPath, "key-2", newItems["key-2"]),
+				utils.NewJsonPatch("remove", jsonPath, "key-1", ""),
+				utils.NewJsonPatch("remove", jsonPath, "key-3", ""),
 			}
 			So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected))
 		})
@@ -651,14 +651,14 @@ func TestRemoveLabelsWithPrefix(t *testing.T) {
 
 		Convey("a unique label should be removed", func() {
 			p := removeLabelsWithPrefix(n, "single")
-			So(p, ShouldResemble, []apihelper.JsonPatch{apihelper.NewJsonPatch("remove", "/metadata/labels", "single-label", "")})
+			So(p, ShouldResemble, []utils.JsonPatch{utils.NewJsonPatch("remove", "/metadata/labels", "single-label", "")})
 		})
 
 		Convey("a non-unique search string should remove all matching keys", func() {
 			p := removeLabelsWithPrefix(n, "multiple")
-			So(sortJsonPatches(p), ShouldResemble, sortJsonPatches([]apihelper.JsonPatch{
-				apihelper.NewJsonPatch("remove", "/metadata/labels", "multiple_A", ""),
-				apihelper.NewJsonPatch("remove", "/metadata/labels", "multiple_B", ""),
+			So(sortJsonPatches(p), ShouldResemble, sortJsonPatches([]utils.JsonPatch{
+				utils.NewJsonPatch("remove", "/metadata/labels", "multiple_A", ""),
+				utils.NewJsonPatch("remove", "/metadata/labels", "multiple_B", ""),
 			}))
 		})
 
@@ -851,8 +851,8 @@ func BenchmarkNfdAPIUpdateAllNodes(b *testing.B) {
 
 	mockClient := &k8sclient.Clientset{}
 
-	statusPatches := []apihelper.JsonPatch{}
-	metadataPatches := []apihelper.JsonPatch{
+	statusPatches := []utils.JsonPatch{}
+	metadataPatches := []utils.JsonPatch{
 		{Op: "add", Path: "/metadata/annotations/nfd.node.kubernetes.io~1feature-labels", Value: ""}, {Op: "add", Path: "/metadata/annotations/nfd.node.kubernetes.io~1extended-resources", Value: ""},
 	}
 
@@ -909,8 +909,8 @@ func withTimeout(actual interface{}, expected ...interface{}) string {
 	}
 }
 
-func jsonPatchMatcher(expected []apihelper.JsonPatch) func([]apihelper.JsonPatch) bool {
-	return func(actual []apihelper.JsonPatch) bool {
+func jsonPatchMatcher(expected []utils.JsonPatch) func([]utils.JsonPatch) bool {
+	return func(actual []utils.JsonPatch) bool {
 		// We don't care about modifying the original slices
 		ok, msg := assertions.So(sortJsonPatches(actual), ShouldResemble, sortJsonPatches(expected))
 		if !ok {
@@ -926,18 +926,18 @@ func jsonPatchMatcher(expected []apihelper.JsonPatch) func([]apihelper.JsonPatch
 	}
 }
 
-func sortJsonPatches(p []apihelper.JsonPatch) []apihelper.JsonPatch {
+func sortJsonPatches(p []utils.JsonPatch) []utils.JsonPatch {
 	sort.Slice(p, func(i, j int) bool { return p[i].Path < p[j].Path })
 	return p
 }
 
 // Remove any labels having the given prefix
-func removeLabelsWithPrefix(n *corev1.Node, search string) []apihelper.JsonPatch {
-	var p []apihelper.JsonPatch
+func removeLabelsWithPrefix(n *corev1.Node, search string) []utils.JsonPatch {
+	var p []utils.JsonPatch
 
 	for k := range n.Labels {
 		if strings.HasPrefix(k, search) {
-			p = append(p, apihelper.NewJsonPatch("remove", "/metadata/labels", k, ""))
+			p = append(p, utils.NewJsonPatch("remove", "/metadata/labels", k, ""))
 		}
 	}
 
diff --git a/pkg/nfd-master/node-updater-pool_test.go b/pkg/nfd-master/node-updater-pool_test.go
index 882d995b6..4e6ae1816 100644
--- a/pkg/nfd-master/node-updater-pool_test.go
+++ b/pkg/nfd-master/node-updater-pool_test.go
@@ -26,6 +26,7 @@ import (
 	k8sclient "k8s.io/client-go/kubernetes"
 	"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
 	"sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/fake"
+	"sigs.k8s.io/node-feature-discovery/pkg/utils"
 )
 
 func newMockNodeUpdaterPool(nfdMaster *nfdMaster) *nodeUpdaterPool {
@@ -80,8 +81,8 @@ func TestRunNodeUpdater(t *testing.T) {
 	mockClient := &k8sclient.Clientset{}
 	mockNode := newMockNode()
 	mockNodeUpdaterPool := newMockNodeUpdaterPool(mockMaster)
-	statusPatches := []apihelper.JsonPatch{}
-	metadataPatches := []apihelper.JsonPatch{}
+	statusPatches := []utils.JsonPatch{}
+	metadataPatches := []utils.JsonPatch{}
 
 	mockAPIHelper.On("GetClient").Return(mockClient, nil)
 	mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil)
diff --git a/test/e2e/node_feature_discovery_test.go b/test/e2e/node_feature_discovery_test.go
index 151af4b8a..d1d51f72f 100644
--- a/test/e2e/node_feature_discovery_test.go
+++ b/test/e2e/node_feature_discovery_test.go
@@ -42,9 +42,9 @@ import (
 	e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
 	admissionapi "k8s.io/pod-security-admission/api"
 
-	"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
 	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"
 	testds "sigs.k8s.io/node-feature-discovery/test/e2e/utils/daemonset"
@@ -974,8 +974,8 @@ resyncPeriod: "1s"
 					eventuallyNonControlPlaneNodes(ctx, f.ClientSet).Should(MatchLabels(expectedLabels, nodes))
 
 					patches, err := json.Marshal(
-						[]apihelper.JsonPatch{
-							apihelper.NewJsonPatch(
+						[]utils.JsonPatch{
+							utils.NewJsonPatch(
 								"replace",
 								"/metadata/labels",
 								nfdv1alpha1.FeatureLabelNs+"/e2e-nodefeature-test-1",