1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-16 13:28:18 +00:00

pkg/utils: move JsonPatch from pkg/apihelper

This commit is contained in:
Markus Lehtonen 2024-01-22 15:32:02 +02:00
parent 5d30be1013
commit 53003cbf69
8 changed files with 91 additions and 86 deletions

View file

@ -22,6 +22,7 @@ import (
topologyclientset "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned" topologyclientset "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
k8sclient "k8s.io/client-go/kubernetes" k8sclient "k8s.io/client-go/kubernetes"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
) )
// APIHelpers represents a set of API helpers for Kubernetes // APIHelpers represents a set of API helpers for Kubernetes
@ -39,10 +40,10 @@ type APIHelpers interface {
UpdateNode(*k8sclient.Clientset, *corev1.Node) error UpdateNode(*k8sclient.Clientset, *corev1.Node) error
// PatchNode updates the node object via the API server using a client. // 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 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 returns a topologyclientset
GetTopologyClient() (*topologyclientset.Clientset, error) GetTopologyClient() (*topologyclientset.Clientset, error)

View file

@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
k8sclient "k8s.io/client-go/kubernetes" k8sclient "k8s.io/client-go/kubernetes"
restclient "k8s.io/client-go/rest" restclient "k8s.io/client-go/rest"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
) )
// K8sHelpers implements APIHelpers // K8sHelpers implements APIHelpers
@ -77,7 +78,7 @@ func (h K8sHelpers) UpdateNode(c *k8sclient.Clientset, n *corev1.Node) error {
return nil 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 { if len(patches) > 0 {
data, err := json.Marshal(patches) data, err := json.Marshal(patches)
if err == nil { if err == nil {
@ -88,7 +89,7 @@ func (h K8sHelpers) PatchNode(c *k8sclient.Clientset, nodeName string, patches [
return nil 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 { if len(patches) > 0 {
data, err := json.Marshal(patches) data, err := json.Marshal(patches)
if err == nil { if err == nil {

View file

@ -6,6 +6,8 @@ import (
mock "github.com/stretchr/testify/mock" mock "github.com/stretchr/testify/mock"
kubernetes "k8s.io/client-go/kubernetes" kubernetes "k8s.io/client-go/kubernetes"
utils "sigs.k8s.io/node-feature-discovery/pkg/utils"
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
versioned "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned" 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 // 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) ret := _m.Called(_a0, _a1, _a2)
var r0 error 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) r0 = rf(_a0, _a1, _a2)
} else { } else {
r0 = ret.Error(0) 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 // 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) ret := _m.Called(_a0, _a1, _a2)
var r0 error 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) r0 = rf(_a0, _a1, _a2)
} else { } else {
r0 = ret.Error(0) r0 = ret.Error(0)

View file

@ -151,9 +151,9 @@ func TestUpdateNodeObject(t *testing.T) {
sort.Strings(fakeExtResourceNames) sort.Strings(fakeExtResourceNames)
// Create a list of expected node status patches // Create a list of expected node status patches
statusPatches := []apihelper.JsonPatch{} statusPatches := []utils.JsonPatch{}
for k, v := range fakeExtResources { 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) mockAPIHelper := new(apihelper.MockAPIHelpers)
@ -166,17 +166,17 @@ func TestUpdateNodeObject(t *testing.T) {
Convey("When I successfully update the node with feature labels", func() { Convey("When I successfully update the node with feature labels", func() {
// Create a list of expected node metadata patches // Create a list of expected node metadata patches
metadataPatches := []apihelper.JsonPatch{ metadataPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("replace", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")), utils.NewJsonPatch("replace", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureAnnotationsTrackingAnnotation, strings.Join(fakeFeatureAnnotationsNames, ",")), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureAnnotationsTrackingAnnotation, strings.Join(fakeFeatureAnnotationsNames, ",")),
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/extended-resources", strings.Join(fakeExtResourceNames, ",")), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.AnnotationNs+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
apihelper.NewJsonPatch("remove", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/old-feature", ""), utils.NewJsonPatch("remove", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/old-feature", ""),
} }
for k, v := range fakeFeatureLabels { 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 { 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) mockAPIHelper.On("GetClient").Return(mockClient, nil)
@ -244,7 +244,7 @@ func TestUpdateMasterNode(t *testing.T) {
mockClient := &k8sclient.Clientset{} mockClient := &k8sclient.Clientset{}
mockNode := newMockNode() mockNode := newMockNode()
Convey("When update operation succeeds", func() { Convey("When update operation succeeds", func() {
expectedPatches := []apihelper.JsonPatch{} expectedPatches := []utils.JsonPatch{}
mockHelper.On("GetClient").Return(mockClient, nil) mockHelper.On("GetClient").Return(mockClient, nil)
mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil) mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil)
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedPatches))).Return(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() { Convey("When there are matching labels", func() {
mockNode := newMockNode() mockNode := newMockNode()
mockResourceLabels := ExtendedResources{"feature-1": "1", "feature-2": "2"} mockResourceLabels := ExtendedResources{"feature-1": "1", "feature-2": "2"}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("add", "/status/capacity", "feature-1", "1"), utils.NewJsonPatch("add", "/status/capacity", "feature-1", "1"),
apihelper.NewJsonPatch("add", "/status/capacity", "feature-2", "2"), utils.NewJsonPatch("add", "/status/capacity", "feature-2", "2"),
} }
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels) patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches)) So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
@ -317,9 +317,9 @@ func TestAddingExtResources(t *testing.T) {
mockNode := newMockNode() mockNode := newMockNode()
mockNode.Status.Capacity[corev1.ResourceName("feature-1")] = *resource.NewQuantity(2, resource.BinarySI) mockNode.Status.Capacity[corev1.ResourceName("feature-1")] = *resource.NewQuantity(2, resource.BinarySI)
mockResourceLabels := ExtendedResources{"feature-1": "1"} mockResourceLabels := ExtendedResources{"feature-1": "1"}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"), utils.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"),
apihelper.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"), utils.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"),
} }
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels) patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches)) So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
@ -379,15 +379,15 @@ func TestSetLabels(t *testing.T) {
} }
sort.Strings(mockLabelNames) sort.Strings(mockLabelNames)
expectedStatusPatches := []apihelper.JsonPatch{} expectedStatusPatches := []utils.JsonPatch{}
Convey("When node update succeeds", func() { Convey("When node update succeeds", func() {
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}} mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, strings.Join(mockLabelNames, ",")),
} }
for k, v := range mockLabels { 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) mockHelper.On("GetClient").Return(mockClient, nil)
@ -402,9 +402,9 @@ func TestSetLabels(t *testing.T) {
Convey("When -label-whitelist is specified", func() { Convey("When -label-whitelist is specified", func() {
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}} mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["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$") mockMaster.config.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
@ -434,14 +434,14 @@ func TestSetLabels(t *testing.T) {
vendorProfileLabel: "val-7", vendorProfileLabel: "val-7",
"--invalid-name--": "valid-val", "--invalid-name--": "valid-val",
"valid-name": "--invalid-val--"} "valid-name": "--invalid-val--"}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", utils.NewJsonPatch("add", "/metadata/annotations",
instance+"."+nfdv1alpha1.FeatureLabelsAnnotation, instance+"."+nfdv1alpha1.FeatureLabelsAnnotation,
"feature-1,valid.ns/feature-2,"+vendorFeatureLabel+","+vendorProfileLabel), "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"]), utils.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"]), utils.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
apihelper.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]), utils.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]), utils.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
} }
mockMaster.deniedNs.normal = map[string]struct{}{"random.denied.ns": {}} 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() { Convey("When -resource-labels is specified", func() {
mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}} mockMaster.config.ExtraLabelNs = map[string]struct{}{"example.io": {}}
expectedPatches := []apihelper.JsonPatch{ expectedPatches := []utils.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.FeatureLabelsAnnotation, "example.io/feature-2"),
apihelper.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]), utils.NewJsonPatch("add", "/metadata/labels", "example.io/feature-2", mockLabels["example.io/feature-2"]),
apihelper.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"), utils.NewJsonPatch("add", "/metadata/annotations", nfdv1alpha1.ExtendedResourceAnnotation, "feature-1,feature-3"),
} }
expectedStatusPatches := []apihelper.JsonPatch{ expectedStatusPatches := []utils.JsonPatch{
apihelper.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-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"]), 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": {}} 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() { 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) p := createPatches([]string{"key-2", "key-3", "foo"}, existingItems, map[string]string{}, jsonPath)
expected := []apihelper.JsonPatch{ expected := []utils.JsonPatch{
apihelper.NewJsonPatch("remove", jsonPath, "key-2", ""), utils.NewJsonPatch("remove", jsonPath, "key-2", ""),
apihelper.NewJsonPatch("remove", jsonPath, "key-3", ""), utils.NewJsonPatch("remove", jsonPath, "key-3", ""),
} }
So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected)) 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() { 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"} newItems := map[string]string{"new-key": "new-val", "key-1": "new-1"}
p := createPatches([]string{"key-1"}, existingItems, newItems, jsonPath) p := createPatches([]string{"key-1"}, existingItems, newItems, jsonPath)
expected := []apihelper.JsonPatch{ expected := []utils.JsonPatch{
apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]), utils.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
apihelper.NewJsonPatch("replace", jsonPath, "key-1", newItems["key-1"]), utils.NewJsonPatch("replace", jsonPath, "key-1", newItems["key-1"]),
} }
So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected)) 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() { 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"} 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) p := createPatches([]string{"key-1", "key-2", "key-3", "foo"}, existingItems, newItems, jsonPath)
expected := []apihelper.JsonPatch{ expected := []utils.JsonPatch{
apihelper.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]), utils.NewJsonPatch("add", jsonPath, "new-key", newItems["new-key"]),
apihelper.NewJsonPatch("add", jsonPath, "key-4", newItems["key-4"]), utils.NewJsonPatch("add", jsonPath, "key-4", newItems["key-4"]),
apihelper.NewJsonPatch("replace", jsonPath, "key-2", newItems["key-2"]), utils.NewJsonPatch("replace", jsonPath, "key-2", newItems["key-2"]),
apihelper.NewJsonPatch("remove", jsonPath, "key-1", ""), utils.NewJsonPatch("remove", jsonPath, "key-1", ""),
apihelper.NewJsonPatch("remove", jsonPath, "key-3", ""), utils.NewJsonPatch("remove", jsonPath, "key-3", ""),
} }
So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected)) So(sortJsonPatches(p), ShouldResemble, sortJsonPatches(expected))
}) })
@ -651,14 +651,14 @@ func TestRemoveLabelsWithPrefix(t *testing.T) {
Convey("a unique label should be removed", func() { Convey("a unique label should be removed", func() {
p := removeLabelsWithPrefix(n, "single") 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() { Convey("a non-unique search string should remove all matching keys", func() {
p := removeLabelsWithPrefix(n, "multiple") p := removeLabelsWithPrefix(n, "multiple")
So(sortJsonPatches(p), ShouldResemble, sortJsonPatches([]apihelper.JsonPatch{ So(sortJsonPatches(p), ShouldResemble, sortJsonPatches([]utils.JsonPatch{
apihelper.NewJsonPatch("remove", "/metadata/labels", "multiple_A", ""), utils.NewJsonPatch("remove", "/metadata/labels", "multiple_A", ""),
apihelper.NewJsonPatch("remove", "/metadata/labels", "multiple_B", ""), utils.NewJsonPatch("remove", "/metadata/labels", "multiple_B", ""),
})) }))
}) })
@ -851,8 +851,8 @@ func BenchmarkNfdAPIUpdateAllNodes(b *testing.B) {
mockClient := &k8sclient.Clientset{} mockClient := &k8sclient.Clientset{}
statusPatches := []apihelper.JsonPatch{} statusPatches := []utils.JsonPatch{}
metadataPatches := []apihelper.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: ""}, {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 { func jsonPatchMatcher(expected []utils.JsonPatch) func([]utils.JsonPatch) bool {
return func(actual []apihelper.JsonPatch) bool { return func(actual []utils.JsonPatch) bool {
// We don't care about modifying the original slices // We don't care about modifying the original slices
ok, msg := assertions.So(sortJsonPatches(actual), ShouldResemble, sortJsonPatches(expected)) ok, msg := assertions.So(sortJsonPatches(actual), ShouldResemble, sortJsonPatches(expected))
if !ok { 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 }) sort.Slice(p, func(i, j int) bool { return p[i].Path < p[j].Path })
return p return p
} }
// Remove any labels having the given prefix // Remove any labels having the given prefix
func removeLabelsWithPrefix(n *corev1.Node, search string) []apihelper.JsonPatch { func removeLabelsWithPrefix(n *corev1.Node, search string) []utils.JsonPatch {
var p []apihelper.JsonPatch var p []utils.JsonPatch
for k := range n.Labels { for k := range n.Labels {
if strings.HasPrefix(k, search) { if strings.HasPrefix(k, search) {
p = append(p, apihelper.NewJsonPatch("remove", "/metadata/labels", k, "")) p = append(p, utils.NewJsonPatch("remove", "/metadata/labels", k, ""))
} }
} }

View file

@ -1146,14 +1146,14 @@ func (m *nfdMaster) getKubeconfig() (*restclient.Config, error) {
} }
// createPatches is a generic helper that returns json patch operations to perform // 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 { func createPatches(removeKeys []string, oldItems map[string]string, newItems map[string]string, jsonPath string) []utils.JsonPatch {
patches := []apihelper.JsonPatch{} patches := []utils.JsonPatch{}
// Determine items to remove // Determine items to remove
for _, key := range removeKeys { for _, key := range removeKeys {
if _, ok := oldItems[key]; ok { if _, ok := oldItems[key]; ok {
if _, ok := newItems[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 { for key, newVal := range newItems {
if oldVal, ok := oldItems[key]; ok { if oldVal, ok := oldItems[key]; ok {
if newVal != oldVal { if newVal != oldVal {
patches = append(patches, apihelper.NewJsonPatch("replace", jsonPath, key, newVal)) patches = append(patches, utils.NewJsonPatch("replace", jsonPath, key, newVal))
} }
} else { } 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 // createExtendedResourcePatches returns a slice of operations to perform on
// the node status // the node status
func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResources ExtendedResources) []apihelper.JsonPatch { func (m *nfdMaster) createExtendedResourcePatches(n *corev1.Node, extendedResources ExtendedResources) []utils.JsonPatch {
patches := []apihelper.JsonPatch{} patches := []utils.JsonPatch{}
// Form a list of namespaced resource names managed by us // Form a list of namespaced resource names managed by us
oldResources := stringToNsNames(n.Annotations[m.instanceAnnotation(nfdv1alpha1.ExtendedResourceAnnotation)], nfdv1alpha1.FeatureLabelNs) 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 { if _, ok := n.Status.Capacity[corev1.ResourceName(resource)]; ok {
// check if the ext resource is still needed // check if the ext resource is still needed
if _, extResNeeded := extendedResources[resource]; !extResNeeded { if _, extResNeeded := extendedResources[resource]; !extResNeeded {
patches = append(patches, apihelper.NewJsonPatch("remove", "/status/capacity", resource, "")) patches = append(patches, utils.NewJsonPatch("remove", "/status/capacity", resource, ""))
patches = append(patches, apihelper.NewJsonPatch("remove", "/status/allocatable", 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 { if quantity, ok := n.Status.Capacity[corev1.ResourceName(resource)]; ok {
val, _ := quantity.AsInt64() val, _ := quantity.AsInt64()
if strconv.FormatInt(val, 10) != value { if strconv.FormatInt(val, 10) != value {
patches = append(patches, apihelper.NewJsonPatch("replace", "/status/capacity", resource, value)) patches = append(patches, utils.NewJsonPatch("replace", "/status/capacity", resource, value))
patches = append(patches, apihelper.NewJsonPatch("replace", "/status/allocatable", resource, value)) patches = append(patches, utils.NewJsonPatch("replace", "/status/allocatable", resource, value))
} }
} else { } 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 // "allocatable" gets added implicitly after adding to capacity
} }
} }

View file

@ -26,6 +26,7 @@ import (
k8sclient "k8s.io/client-go/kubernetes" k8sclient "k8s.io/client-go/kubernetes"
"sigs.k8s.io/node-feature-discovery/pkg/apihelper" "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/generated/clientset/versioned/fake"
"sigs.k8s.io/node-feature-discovery/pkg/utils"
) )
func newMockNodeUpdaterPool(nfdMaster *nfdMaster) *nodeUpdaterPool { func newMockNodeUpdaterPool(nfdMaster *nfdMaster) *nodeUpdaterPool {
@ -80,8 +81,8 @@ func TestRunNodeUpdater(t *testing.T) {
mockClient := &k8sclient.Clientset{} mockClient := &k8sclient.Clientset{}
mockNode := newMockNode() mockNode := newMockNode()
mockNodeUpdaterPool := newMockNodeUpdaterPool(mockMaster) mockNodeUpdaterPool := newMockNodeUpdaterPool(mockMaster)
statusPatches := []apihelper.JsonPatch{} statusPatches := []utils.JsonPatch{}
metadataPatches := []apihelper.JsonPatch{} metadataPatches := []utils.JsonPatch{}
mockAPIHelper.On("GetClient").Return(mockClient, nil) mockAPIHelper.On("GetClient").Return(mockClient, nil)
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil) mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil)

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package apihelper package utils
import ( import (
"path" "path"

View file

@ -42,9 +42,9 @@ import (
e2epod "k8s.io/kubernetes/test/e2e/framework/pod" e2epod "k8s.io/kubernetes/test/e2e/framework/pod"
admissionapi "k8s.io/pod-security-admission/api" 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" nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
nfdclient "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned" 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" "sigs.k8s.io/node-feature-discovery/source/custom"
testutils "sigs.k8s.io/node-feature-discovery/test/e2e/utils" testutils "sigs.k8s.io/node-feature-discovery/test/e2e/utils"
testds "sigs.k8s.io/node-feature-discovery/test/e2e/utils/daemonset" 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)) eventuallyNonControlPlaneNodes(ctx, f.ClientSet).Should(MatchLabels(expectedLabels, nodes))
patches, err := json.Marshal( patches, err := json.Marshal(
[]apihelper.JsonPatch{ []utils.JsonPatch{
apihelper.NewJsonPatch( utils.NewJsonPatch(
"replace", "replace",
"/metadata/labels", "/metadata/labels",
nfdv1alpha1.FeatureLabelNs+"/e2e-nodefeature-test-1", nfdv1alpha1.FeatureLabelNs+"/e2e-nodefeature-test-1",