diff --git a/Dockerfile b/Dockerfile index 114907bd5..cfe7d6eac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN go install \ ./cmd/* RUN install -D -m644 nfd-worker.conf.example /etc/kubernetes/node-feature-discovery/nfd-worker.conf -#RUN go test . +RUN make test # Create production image for running node feature discovery diff --git a/Makefile b/Makefile index adc3cc011..fad2fe868 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: all +.PHONY: all test IMAGE_BUILD_CMD := docker build @@ -18,4 +18,8 @@ image: mock: mockery --name=FeatureSource --dir=source --inpkg --note="Re-generate by running 'make mock'" - mockery --name=APIHelpers --inpkg --note="Re-generate by running 'make mock'" + mockery --name=APIHelpers --dir=pkg/apihelper --inpkg --note="Re-generate by running 'make mock'" + mockery --name=LabelerClient --dir=pkg/labeler --inpkg --note="Re-generate by running 'make mock'" + +test: + go test ./cmd/... ./pkg/... diff --git a/cmd/nfd-master/main_test.go b/cmd/nfd-master/main_test.go new file mode 100644 index 000000000..2e8b0aeab --- /dev/null +++ b/cmd/nfd-master/main_test.go @@ -0,0 +1,241 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "os" + "strings" + "testing" + + . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/mock" + "github.com/vektra/errors" + "golang.org/x/net/context" + api "k8s.io/api/core/v1" + k8sclient "k8s.io/client-go/kubernetes" + "sigs.k8s.io/node-feature-discovery/pkg/apihelper" + "sigs.k8s.io/node-feature-discovery/pkg/labeler" + "sigs.k8s.io/node-feature-discovery/pkg/version" +) + +const ( + mockNodeName = "mock-node" +) + +func init() { + os.Setenv(NodeNameEnv, mockNodeName) +} + +func TestUpdateNodeFeatures(t *testing.T) { + Convey("When I update the node using fake client", t, func() { + fakeFeatureLabels := map[string]string{"source-feature.1": "val1", "source-feature.2": "val2", "source-feature.3": "val3"} + fakeAnnotations := map[string]string{"version": version.Get()} + fakeFeatureLabelNames := make([]string, 0, len(fakeFeatureLabels)) + for k, _ := range fakeFeatureLabels { + fakeFeatureLabelNames = append(fakeFeatureLabelNames, k) + } + fakeAnnotations["feature-labels"] = strings.Join(fakeFeatureLabelNames, ",") + + mockAPIHelper := new(apihelper.MockAPIHelpers) + mockNode := &api.Node{} + mockClient := &k8sclient.Clientset{} + + Convey("When I successfully update the node with feature labels", func() { + mockAPIHelper.On("GetClient").Return(mockClient, nil) + mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once() + mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, labelNs).Return().Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/nfd").Return().Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/node-feature-discovery").Return().Once() + mockAPIHelper.On("AddAnnotations", mockNode, fakeAnnotations).Return().Once() + mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(nil).Once() + err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations) + + Convey("Error is nil", func() { + So(err, ShouldBeNil) + }) + }) + + Convey("When I fail to update the node with feature labels", func() { + expectedError := errors.New("fake error") + mockAPIHelper.On("GetClient").Return(nil, expectedError) + err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations) + + Convey("Error is produced", func() { + So(err, ShouldEqual, expectedError) + }) + }) + + Convey("When I fail to get a mock client while updating feature labels", func() { + expectedError := errors.New("fake error") + mockAPIHelper.On("GetClient").Return(nil, expectedError) + err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations) + + Convey("Error is produced", func() { + So(err, ShouldEqual, expectedError) + }) + }) + + Convey("When I fail to get a mock node while updating feature labels", func() { + expectedError := errors.New("fake error") + mockAPIHelper.On("GetClient").Return(mockClient, nil) + mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(nil, expectedError).Once() + err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations) + + Convey("Error is produced", func() { + So(err, ShouldEqual, expectedError) + }) + }) + + Convey("When I fail to update a mock node while updating feature labels", func() { + expectedError := errors.New("fake error") + mockAPIHelper.On("GetClient").Return(mockClient, nil) + mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, labelNs).Return().Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/nfd").Return().Once() + mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/node-feature-discovery").Return().Once() + mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once() + mockAPIHelper.On("AddAnnotations", mockNode, fakeAnnotations).Return().Once() + mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(expectedError).Once() + err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations) + + Convey("Error is produced", func() { + So(err, ShouldEqual, expectedError) + }) + }) + + }) +} + +func TestUpdateMasterNode(t *testing.T) { + Convey("When updating the nfd-master node", t, func() { + mockHelper := &apihelper.MockAPIHelpers{} + mockClient := &k8sclient.Clientset{} + mockNode := &api.Node{} + Convey("When update operation succeeds", func() { + mockHelper.On("GetClient").Return(mockClient, nil) + mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil) + mockHelper.On("AddAnnotations", mockNode, map[string]string{"master.version": version.Get()}) + mockHelper.On("UpdateNode", mockClient, mockNode).Return(nil) + err := updateMasterNode(mockHelper) + Convey("No error should be returned", func() { + So(err, ShouldBeNil) + }) + }) + + mockErr := errors.New("mock-error") + Convey("When getting API client fails", func() { + mockHelper.On("GetClient").Return(mockClient, mockErr) + err := updateMasterNode(mockHelper) + Convey("An error should be returned", func() { + So(err, ShouldEqual, mockErr) + }) + }) + + Convey("When getting API node object fails", func() { + mockHelper.On("GetClient").Return(mockClient, nil) + mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, mockErr) + err := updateMasterNode(mockHelper) + Convey("An error should be returned", func() { + So(err, ShouldEqual, mockErr) + }) + }) + + Convey("When updating node object fails", func() { + mockHelper.On("GetClient").Return(mockClient, nil) + mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil) + mockHelper.On("AddAnnotations", mock.Anything, mock.Anything) + mockHelper.On("UpdateNode", mockClient, mockNode).Return(mockErr) + err := updateMasterNode(mockHelper) + Convey("An error should be returned", func() { + So(err, ShouldEqual, mockErr) + }) + }) + }) +} + +func TestSetLabels(t *testing.T) { + Convey("When servicing SetLabels request", t, func() { + const workerName = "mock-worker" + const workerVer = "0.1-test" + mockHelper := &apihelper.MockAPIHelpers{} + mockClient := &k8sclient.Clientset{} + mockNode := &api.Node{} + mockServer := labelerServer{args: Args{}, apiHelper: mockHelper} + mockCtx := context.Background() + mockReq := &labeler.SetLabelsRequest{NodeName: workerName, NfdVersion: workerVer, Labels: map[string]string{"feature-1": "val-1"}} + + Convey("When node update succeeds", func() { + mockHelper.On("GetClient").Return(mockClient, nil) + mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil) + mockHelper.On("RemoveLabelsWithPrefix", mockNode, mock.Anything).Return() + mockHelper.On("AddLabels", mockNode, mock.Anything).Return() + mockHelper.On("AddAnnotations", mockNode, map[string]string{"worker.version": workerVer, "feature-labels": "feature-1"}) + mockHelper.On("UpdateNode", mockClient, mockNode).Return(nil) + _, err := mockServer.SetLabels(mockCtx, mockReq) + Convey("No error should be returned", func() { + So(err, ShouldBeNil) + }) + }) + + mockErr := errors.New("mock-error") + Convey("When node update fails", func() { + mockHelper.On("GetClient").Return(mockClient, mockErr) + _, err := mockServer.SetLabels(mockCtx, mockReq) + Convey("An error should be returned", func() { + So(err, ShouldEqual, mockErr) + }) + }) + + mockServer.args.noPublish = true + Convey("With '--no-publish'", func() { + _, err := mockServer.SetLabels(mockCtx, mockReq) + Convey("Operation should succeed", func() { + So(err, ShouldBeNil) + }) + }) + }) +} + +func TestArgsParse(t *testing.T) { + Convey("When parsing command line arguments", t, func() { + Convey("When --no-publish and --oneshot flags are passed", func() { + args, err := argsParse([]string{"--no-publish"}) + Convey("noPublish is set and args.sources is set to the default value", func() { + So(args.noPublish, ShouldBeTrue) + So(len(args.labelWhiteList.String()), ShouldEqual, 0) + So(err, ShouldBeNil) + }) + }) + + Convey("When valid args are specified", func() { + args, err := argsParse([]string{"--label-whitelist=.*rdt.*", "--port=1234"}) + Convey("Argument parsing should succeed and args set to correct values", func() { + So(args.noPublish, ShouldBeFalse) + So(args.port, ShouldEqual, 1234) + So(args.labelWhiteList.String(), ShouldResemble, ".*rdt.*") + So(err, ShouldBeNil) + }) + }) + Convey("When invalid --port is defined", func() { + _, err := argsParse([]string{"--port=123a"}) + Convey("argsParse should fail", func() { + So(err, ShouldNotBeNil) + }) + }) + }) +} diff --git a/main_test.go b/cmd/nfd-worker/main_test.go similarity index 60% rename from main_test.go rename to cmd/nfd-worker/main_test.go index cf4a11901..404e36a1d 100644 --- a/main_test.go +++ b/cmd/nfd-worker/main_test.go @@ -1,3 +1,19 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + package main import ( @@ -5,19 +21,16 @@ import ( "io/ioutil" "os" "regexp" - "strings" "testing" "time" . "github.com/smartystreets/goconvey/convey" + "github.com/stretchr/testify/mock" "github.com/vektra/errors" - api "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - k8sclient "k8s.io/client-go/kubernetes" + "sigs.k8s.io/node-feature-discovery/pkg/labeler" "sigs.k8s.io/node-feature-discovery/source" "sigs.k8s.io/node-feature-discovery/source/fake" "sigs.k8s.io/node-feature-discovery/source/panic_fake" - "sigs.k8s.io/node-feature-discovery/pkg/version" ) func TestDiscoveryWithMockSources(t *testing.T) { @@ -27,8 +40,6 @@ func TestDiscoveryWithMockSources(t *testing.T) { fakeFeatureNames := []string{"testfeature1", "testfeature2", "testfeature3"} fakeFeatures := source.Features{} fakeFeatureLabels := Labels{} - fakeAnnotations := Annotations{"version": version.Get(), - "feature-labels": "testSource-testfeature1,testSource-testfeature2,testSource-testfeature3"} fakeFeatureLabelNames := make([]string, 0, len(fakeFeatureNames)) for _, f := range fakeFeatureNames { fakeFeatures[f] = true @@ -36,7 +47,6 @@ func TestDiscoveryWithMockSources(t *testing.T) { fakeFeatureLabels[labelName] = "true" fakeFeatureLabelNames = append(fakeFeatureLabelNames, labelName) } - fakeAnnotations["feature-labels"] = strings.Join(fakeFeatureLabelNames, ",") fakeFeatureSource := source.FeatureSource(mockFeatureSource) Convey("When I successfully get the labels from the mock source", func() { @@ -64,90 +74,13 @@ func TestDiscoveryWithMockSources(t *testing.T) { So(err, ShouldEqual, expectedError) }) }) - - mockAPIHelper := new(MockAPIHelpers) - testHelper := APIHelpers(mockAPIHelper) - mockNode := &api.Node{} - var mockClient *k8sclient.Clientset - - Convey("When I successfully update the node with feature labels", func() { - mockAPIHelper.On("GetClient").Return(mockClient, nil) - mockAPIHelper.On("GetNode", mockClient).Return(mockNode, nil).Once() - mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, labelNs).Return().Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/nfd").Return().Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/node-feature-discovery").Return().Once() - mockAPIHelper.On("AddAnnotations", mockNode, fakeAnnotations).Return().Once() - mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(nil).Once() - noPublish := false - err := updateNodeWithFeatureLabels(testHelper, noPublish, fakeFeatureLabels) - - Convey("Error is nil", func() { - So(err, ShouldBeNil) - }) - }) - - Convey("When I fail to update the node with feature labels", func() { - expectedError := errors.New("fake error") - mockAPIHelper.On("GetClient").Return(nil, expectedError) - noPublish := false - err := updateNodeWithFeatureLabels(testHelper, noPublish, fakeFeatureLabels) - - Convey("Error is produced", func() { - So(err, ShouldEqual, expectedError) - }) - }) - - Convey("When I fail to get a mock client while advertising feature labels", func() { - expectedError := errors.New("fake error") - mockAPIHelper.On("GetClient").Return(nil, expectedError) - err := advertiseFeatureLabels(testHelper, fakeFeatureLabels, fakeAnnotations) - - Convey("Error is produced", func() { - So(err, ShouldEqual, expectedError) - }) - }) - - Convey("When I fail to get a mock node while advertising feature labels", func() { - expectedError := errors.New("fake error") - mockAPIHelper.On("GetClient").Return(mockClient, nil) - mockAPIHelper.On("GetNode", mockClient).Return(nil, expectedError).Once() - err := advertiseFeatureLabels(testHelper, fakeFeatureLabels, fakeAnnotations) - - Convey("Error is produced", func() { - So(err, ShouldEqual, expectedError) - }) - }) - - Convey("When I fail to update a mock node while advertising feature labels", func() { - expectedError := errors.New("fake error") - mockAPIHelper.On("GetClient").Return(mockClient, nil) - mockAPIHelper.On("GetNode", mockClient).Return(mockNode, nil).Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, labelNs).Return().Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/nfd").Return().Once() - mockAPIHelper.On("RemoveLabelsWithPrefix", mockNode, "node.alpha.kubernetes-incubator.io/node-feature-discovery").Return().Once() - mockAPIHelper.On("AddLabels", mockNode, fakeFeatureLabels).Return().Once() - mockAPIHelper.On("AddAnnotations", mockNode, fakeAnnotations).Return().Once() - mockAPIHelper.On("UpdateNode", mockClient, mockNode).Return(expectedError).Once() - err := advertiseFeatureLabels(testHelper, fakeFeatureLabels, fakeAnnotations) - - Convey("Error is produced", func() { - So(err, ShouldEqual, expectedError) - }) - }) - }) } func TestArgsParse(t *testing.T) { Convey("When parsing command line arguments", t, func() { - argv1 := []string{"--no-publish", "--oneshot"} - argv2 := []string{"--sources=fake1,fake2,fake3", "--sleep-interval=30s"} - argv3 := []string{"--label-whitelist=.*rdt.*"} - argv4 := []string{"--no-publish", "--sources=fake1,fake2,fake3"} - Convey("When --no-publish and --oneshot flags are passed", func() { - args := argsParse(argv1) + args, err := argsParse([]string{"--no-publish", "--oneshot"}) Convey("noPublish is set and args.sources is set to the default value", func() { So(args.sleepInterval, ShouldEqual, 60*time.Second) @@ -155,11 +88,12 @@ func TestArgsParse(t *testing.T) { So(args.oneshot, ShouldBeTrue) So(args.sources, ShouldResemble, []string{"cpu", "cpuid", "iommu", "kernel", "local", "memory", "network", "pci", "pstate", "rdt", "storage", "system"}) So(len(args.labelWhiteList), ShouldEqual, 0) + So(err, ShouldBeNil) }) }) Convey("When --sources flag is passed and set to some values, --sleep-inteval is specified", func() { - args := argsParse(argv2) + args, err := argsParse([]string{"--sources=fake1,fake2,fake3", "--sleep-interval=30s"}) Convey("args.sources is set to appropriate values", func() { So(args.sleepInterval, ShouldEqual, 30*time.Second) @@ -167,26 +101,29 @@ func TestArgsParse(t *testing.T) { So(args.oneshot, ShouldBeFalse) So(args.sources, ShouldResemble, []string{"fake1", "fake2", "fake3"}) So(len(args.labelWhiteList), ShouldEqual, 0) + So(err, ShouldBeNil) }) }) Convey("When --label-whitelist flag is passed and set to some value", func() { - args := argsParse(argv3) + args, err := argsParse([]string{"--label-whitelist=.*rdt.*"}) Convey("args.labelWhiteList is set to appropriate value and args.sources is set to default value", func() { So(args.noPublish, ShouldBeFalse) So(args.sources, ShouldResemble, []string{"cpu", "cpuid", "iommu", "kernel", "local", "memory", "network", "pci", "pstate", "rdt", "storage", "system"}) So(args.labelWhiteList, ShouldResemble, ".*rdt.*") + So(err, ShouldBeNil) }) }) Convey("When --no-publish and --sources flag are passed and --sources flag is set to some value", func() { - args := argsParse(argv4) + args, err := argsParse([]string{"--no-publish", "--sources=fake1,fake2,fake3"}) Convey("--no-publish is set and args.sources is set to appropriate values", func() { So(args.noPublish, ShouldBeTrue) So(args.sources, ShouldResemble, []string{"fake1", "fake2", "fake3"}) So(len(args.labelWhiteList), ShouldEqual, 0) + So(err, ShouldBeNil) }) }) }) @@ -322,69 +259,6 @@ func TestCreateFeatureLabels(t *testing.T) { }) } -func TestAddLabels(t *testing.T) { - Convey("When adding labels", t, func() { - helper := k8sHelpers{} - labels := Labels{} - n := &api.Node{ - ObjectMeta: meta_v1.ObjectMeta{ - Labels: map[string]string{}, - }, - } - - Convey("If no labels are passed", func() { - helper.AddLabels(n, labels) - - Convey("None should be added", func() { - So(len(n.Labels), ShouldEqual, 0) - }) - }) - - Convey("They should be added to the node.Labels", func() { - test1 := "test1" - labels[test1] = "true" - helper.AddLabels(n, labels) - So(n.Labels, ShouldContainKey, labelNs+test1) - }) - }) -} - -func TestRemoveLabelsWithPrefix(t *testing.T) { - Convey("When removing labels", t, func() { - helper := k8sHelpers{} - n := &api.Node{ - ObjectMeta: meta_v1.ObjectMeta{ - Labels: map[string]string{ - "single": "123", - "multiple_A": "a", - "multiple_B": "b", - }, - }, - } - - Convey("a unique label should be removed", func() { - helper.RemoveLabelsWithPrefix(n, "single") - So(len(n.Labels), ShouldEqual, 2) - So(n.Labels, ShouldNotContainKey, "single") - }) - - Convey("a non-unique search string should remove all matching keys", func() { - helper.RemoveLabelsWithPrefix(n, "multiple") - So(len(n.Labels), ShouldEqual, 1) - So(n.Labels, ShouldNotContainKey, "multiple_A") - So(n.Labels, ShouldNotContainKey, "multiple_B") - }) - - Convey("a search string with no matches should not alter labels", func() { - helper.RemoveLabelsWithPrefix(n, "unique") - So(n.Labels, ShouldContainKey, "single") - So(n.Labels, ShouldContainKey, "multiple_A") - So(n.Labels, ShouldContainKey, "multiple_B") - So(len(n.Labels), ShouldEqual, 3) - }) - }) -} - func TestGetFeatureLabels(t *testing.T) { Convey("When I get feature labels and panic occurs during discovery of a feature source", t, func() { fakePanicFeatureSource := source.FeatureSource(new(panic_fake.Source)) @@ -399,3 +273,26 @@ func TestGetFeatureLabels(t *testing.T) { }) } + +func TestAdvertiseFeatureLabels(t *testing.T) { + Convey("When advertising labels", t, func() { + mockClient := &labeler.MockLabelerClient{} + labels := map[string]string{"feature-1": "value-1"} + + Convey("Correct labeling request is sent", func() { + mockClient.On("SetLabels", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*labeler.SetLabelsRequest")).Return(&labeler.SetLabelsReply{}, nil) + err := advertiseFeatureLabels(mockClient, labels) + Convey("There should be no error", func() { + So(err, ShouldBeNil) + }) + }) + Convey("Labeling request fails", func() { + mockErr := errors.New("mock-error") + mockClient.On("SetLabels", mock.AnythingOfType("*context.timerCtx"), mock.AnythingOfType("*labeler.SetLabelsRequest")).Return(&labeler.SetLabelsReply{}, mockErr) + err := advertiseFeatureLabels(mockClient, labels) + Convey("An error should be returned", func() { + So(err, ShouldEqual, mockErr) + }) + }) + }) +} diff --git a/nfd-master.yaml.template b/nfd-master.yaml.template index 86901c1ce..cb3bff46a 100644 --- a/nfd-master.yaml.template +++ b/nfd-master.yaml.template @@ -46,7 +46,7 @@ spec: labels: app: nfd-master spec: - serviceAccount: node-feature-discovery + serviceAccount: nfd-master nodeSelector: node-role.kubernetes.io/master: "" tolerations: diff --git a/pkg/apihelper/k8shelpers_test.go b/pkg/apihelper/k8shelpers_test.go new file mode 100644 index 000000000..945bfad69 --- /dev/null +++ b/pkg/apihelper/k8shelpers_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2019 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package apihelper_test + +import ( + "testing" + + . "github.com/smartystreets/goconvey/convey" + api "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/node-feature-discovery/pkg/apihelper" +) + +func TestAddLabels(t *testing.T) { + Convey("When adding labels", t, func() { + labelNs := "test.nfd/" + helper := apihelper.K8sHelpers{LabelNs: labelNs} + labels := map[string]string{} + n := &api.Node{ + ObjectMeta: meta_v1.ObjectMeta{ + Labels: map[string]string{}, + }, + } + + Convey("If no labels are passed", func() { + helper.AddLabels(n, labels) + + Convey("None should be added", func() { + So(len(n.Labels), ShouldEqual, 0) + }) + }) + + Convey("They should be added to the node.Labels", func() { + test1 := "test1" + labels[test1] = "true" + helper.AddLabels(n, labels) + So(n.Labels, ShouldContainKey, labelNs+test1) + }) + }) +} + +func TestRemoveLabelsWithPrefix(t *testing.T) { + Convey("When removing labels", t, func() { + helper := apihelper.K8sHelpers{} + n := &api.Node{ + ObjectMeta: meta_v1.ObjectMeta{ + Labels: map[string]string{ + "single": "123", + "multiple_A": "a", + "multiple_B": "b", + }, + }, + } + + Convey("a unique label should be removed", func() { + helper.RemoveLabelsWithPrefix(n, "single") + So(len(n.Labels), ShouldEqual, 2) + So(n.Labels, ShouldNotContainKey, "single") + }) + + Convey("a non-unique search string should remove all matching keys", func() { + helper.RemoveLabelsWithPrefix(n, "multiple") + So(len(n.Labels), ShouldEqual, 1) + So(n.Labels, ShouldNotContainKey, "multiple_A") + So(n.Labels, ShouldNotContainKey, "multiple_B") + }) + + Convey("a search string with no matches should not alter labels", func() { + helper.RemoveLabelsWithPrefix(n, "unique") + So(n.Labels, ShouldContainKey, "single") + So(n.Labels, ShouldContainKey, "multiple_A") + So(n.Labels, ShouldContainKey, "multiple_B") + So(len(n.Labels), ShouldEqual, 3) + }) + }) +} diff --git a/mock_APIHelpers.go b/pkg/apihelper/mock_APIHelpers.go similarity index 76% rename from mock_APIHelpers.go rename to pkg/apihelper/mock_APIHelpers.go index 800303565..f2abbbe36 100644 --- a/mock_APIHelpers.go +++ b/pkg/apihelper/mock_APIHelpers.go @@ -2,7 +2,7 @@ // Re-generate by running 'make mock' -package main +package apihelper import kubernetes "k8s.io/client-go/kubernetes" import mock "github.com/stretchr/testify/mock" @@ -14,12 +14,12 @@ type MockAPIHelpers struct { } // AddAnnotations provides a mock function with given fields: _a0, _a1 -func (_m *MockAPIHelpers) AddAnnotations(_a0 *v1.Node, _a1 Annotations) { +func (_m *MockAPIHelpers) AddAnnotations(_a0 *v1.Node, _a1 map[string]string) { _m.Called(_a0, _a1) } // AddLabels provides a mock function with given fields: _a0, _a1 -func (_m *MockAPIHelpers) AddLabels(_a0 *v1.Node, _a1 Labels) { +func (_m *MockAPIHelpers) AddLabels(_a0 *v1.Node, _a1 map[string]string) { _m.Called(_a0, _a1) } @@ -46,13 +46,13 @@ func (_m *MockAPIHelpers) GetClient() (*kubernetes.Clientset, error) { return r0, r1 } -// GetNode provides a mock function with given fields: _a0 -func (_m *MockAPIHelpers) GetNode(_a0 *kubernetes.Clientset) (*v1.Node, error) { - ret := _m.Called(_a0) +// GetNode provides a mock function with given fields: _a0, _a1 +func (_m *MockAPIHelpers) GetNode(_a0 *kubernetes.Clientset, _a1 string) (*v1.Node, error) { + ret := _m.Called(_a0, _a1) var r0 *v1.Node - if rf, ok := ret.Get(0).(func(*kubernetes.Clientset) *v1.Node); ok { - r0 = rf(_a0) + if rf, ok := ret.Get(0).(func(*kubernetes.Clientset, string) *v1.Node); ok { + r0 = rf(_a0, _a1) } else { if ret.Get(0) != nil { r0 = ret.Get(0).(*v1.Node) @@ -60,8 +60,8 @@ func (_m *MockAPIHelpers) GetNode(_a0 *kubernetes.Clientset) (*v1.Node, error) { } var r1 error - if rf, ok := ret.Get(1).(func(*kubernetes.Clientset) error); ok { - r1 = rf(_a0) + if rf, ok := ret.Get(1).(func(*kubernetes.Clientset, string) error); ok { + r1 = rf(_a0, _a1) } else { r1 = ret.Error(1) } diff --git a/pkg/labeler/mock_LabelerClient.go b/pkg/labeler/mock_LabelerClient.go new file mode 100644 index 000000000..145bc1403 --- /dev/null +++ b/pkg/labeler/mock_LabelerClient.go @@ -0,0 +1,44 @@ +// Code generated by mockery v1.0.0. DO NOT EDIT. + +// Re-generate by running 'make mock' + +package labeler + +import context "context" +import grpc "google.golang.org/grpc" +import mock "github.com/stretchr/testify/mock" + +// MockLabelerClient is an autogenerated mock type for the LabelerClient type +type MockLabelerClient struct { + mock.Mock +} + +// SetLabels provides a mock function with given fields: ctx, in, opts +func (_m *MockLabelerClient) SetLabels(ctx context.Context, in *SetLabelsRequest, opts ...grpc.CallOption) (*SetLabelsReply, error) { + _va := make([]interface{}, len(opts)) + for _i := range opts { + _va[_i] = opts[_i] + } + var _ca []interface{} + _ca = append(_ca, ctx, in) + _ca = append(_ca, _va...) + ret := _m.Called(_ca...) + + var r0 *SetLabelsReply + if rf, ok := ret.Get(0).(func(context.Context, *SetLabelsRequest, ...grpc.CallOption) *SetLabelsReply); ok { + r0 = rf(ctx, in, opts...) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*SetLabelsReply) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, *SetLabelsRequest, ...grpc.CallOption) error); ok { + r1 = rf(ctx, in, opts...) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +}