mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-15 21:08:23 +00:00
Merge pull request #431 from marquiz/devel/master-instance-flag
nfd-master: implement --instance flag
This commit is contained in:
commit
85bde7f749
6 changed files with 167 additions and 108 deletions
|
@ -66,7 +66,7 @@ func argsParse(argv []string) (master.Args, error) {
|
||||||
%s [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
|
%s [--prune] [--no-publish] [--label-whitelist=<pattern>] [--port=<port>]
|
||||||
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
[--ca-file=<path>] [--cert-file=<path>] [--key-file=<path>]
|
||||||
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
[--verify-node-name] [--extra-label-ns=<list>] [--resource-labels=<list>]
|
||||||
[--kubeconfig=<path>]
|
[--kubeconfig=<path>] [--instance=<name>]
|
||||||
%s -h | --help
|
%s -h | --help
|
||||||
%s --version
|
%s --version
|
||||||
|
|
||||||
|
@ -75,6 +75,9 @@ func argsParse(argv []string) (master.Args, error) {
|
||||||
--version Output version and exit.
|
--version Output version and exit.
|
||||||
--prune Prune all NFD related attributes from all nodes
|
--prune Prune all NFD related attributes from all nodes
|
||||||
of the cluster and exit.
|
of the cluster and exit.
|
||||||
|
--instance=<name> Instance name. Used to separate annotation
|
||||||
|
namespaces for multiple parallel deployments.
|
||||||
|
[Default: ]
|
||||||
--kubeconfig=<path> Kubeconfig to use [Default: ]
|
--kubeconfig=<path> Kubeconfig to use [Default: ]
|
||||||
of the cluster and exit.
|
of the cluster and exit.
|
||||||
--port=<port> Port on which to listen for connections.
|
--port=<port> Port on which to listen for connections.
|
||||||
|
@ -109,6 +112,12 @@ func argsParse(argv []string) (master.Args, error) {
|
||||||
|
|
||||||
// Parse argument values as usable types.
|
// Parse argument values as usable types.
|
||||||
var err error
|
var err error
|
||||||
|
args.Instance = arguments["--instance"].(string)
|
||||||
|
if ok, _ := regexp.MatchString(`^([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9]$`, args.Instance); args.Instance != "" && !ok {
|
||||||
|
return args, fmt.Errorf("invalid --instance %q: instance name "+
|
||||||
|
"must start and end with an alphanumeric character and may only contain "+
|
||||||
|
"alphanumerics, `-`, `_` or `.`", args.Instance)
|
||||||
|
}
|
||||||
args.CaFile = arguments["--ca-file"].(string)
|
args.CaFile = arguments["--ca-file"].(string)
|
||||||
args.CertFile = arguments["--cert-file"].(string)
|
args.CertFile = arguments["--cert-file"].(string)
|
||||||
args.KeyFile = arguments["--key-file"].(string)
|
args.KeyFile = arguments["--key-file"].(string)
|
||||||
|
|
|
@ -48,6 +48,22 @@ Example:
|
||||||
nfd-master --port=443
|
nfd-master --port=443
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### --instance
|
||||||
|
|
||||||
|
The `--instance` flag makes it possible to run multiple NFD deployments in
|
||||||
|
parallel. In practice, it separates the node annotations between deployments so
|
||||||
|
that each of them can store metadata independently. The instance name must
|
||||||
|
start and end with an alphanumeric character and may only contain alphanumeric
|
||||||
|
characters, `-`, `_` or `.`.
|
||||||
|
|
||||||
|
Default: *empty*
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nfd-master --instance=network
|
||||||
|
```
|
||||||
|
|
||||||
### --ca-file
|
### --ca-file
|
||||||
|
|
||||||
The `--ca-file` is one of the three flags (together with `--cert-file` and
|
The `--ca-file` is one of the three flags (together with `--cert-file` and
|
||||||
|
|
|
@ -80,12 +80,16 @@ An overview of the default feature labels:
|
||||||
|
|
||||||
NFD also annotates nodes it is running on:
|
NFD also annotates nodes it is running on:
|
||||||
|
|
||||||
| Annotation | Description
|
| Annotation | Description
|
||||||
| ----------------------------------------- | -----------
|
| ------------------------------------------------------------ | -----------
|
||||||
| nfd.node.kubernetes.io/master.version | Version of the nfd-master instance running on the node. Informative use only.
|
| [<instance>.]nfd.node.kubernetes.io/master.version | Version of the nfd-master instance running on the node. Informative use only.
|
||||||
| nfd.node.kubernetes.io/worker.version | Version of the nfd-worker instance running on the node. Informative use only.
|
| [<instance>.]nfd.node.kubernetes.io/worker.version | Version of the nfd-worker instance running on the node. Informative use only.
|
||||||
| nfd.node.kubernetes.io/feature-labels | Comma-separated list of node labels managed by NFD. NFD uses this internally so must not be edited by users.
|
| [<instance>.]nfd.node.kubernetes.io/feature-labels | Comma-separated list of node labels managed by NFD. NFD uses this internally so must not be edited by users.
|
||||||
| nfd.node.kubernetes.io/extended-resources | Comma-separated list of node extended resources managed by NFD. NFD uses this internally so must not be edited by users.
|
| [<instance>.]nfd.node.kubernetes.io/extended-resources | Comma-separated list of node extended resources managed by NFD. NFD uses this internally so must not be edited by users.
|
||||||
|
|
||||||
|
NOTE: the [`--instance`](../advanced/master-commandline-reference.md#instance)
|
||||||
|
command line flag affects the annotation names
|
||||||
|
|
||||||
Unapplicable annotations are not created, i.e. for example master.version is only created on nodes running nfd-master.
|
Unapplicable annotations are not created, i.e. for example master.version is only created on nodes running nfd-master.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
||||||
package nfdmaster
|
package nfdmaster
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"path"
|
||||||
"regexp"
|
"regexp"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -41,10 +42,6 @@ const (
|
||||||
mockNodeName = "mock-node"
|
mockNodeName = "mock-node"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
nodeName = mockNodeName
|
|
||||||
}
|
|
||||||
|
|
||||||
func newMockNode() *api.Node {
|
func newMockNode() *api.Node {
|
||||||
n := api.Node{}
|
n := api.Node{}
|
||||||
n.Name = mockNodeName
|
n.Name = mockNodeName
|
||||||
|
@ -54,6 +51,15 @@ func newMockNode() *api.Node {
|
||||||
return &n
|
return &n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
|
||||||
|
return &nfdMaster{
|
||||||
|
nodeName: mockNodeName,
|
||||||
|
annotationNs: AnnotationNsBase,
|
||||||
|
args: Args{LabelWhiteList: regexp.MustCompile("")},
|
||||||
|
apihelper: apihelper,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdateNodeFeatures(t *testing.T) {
|
func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
Convey("When I update the node using fake client", t, func() {
|
Convey("When I update the node using fake client", t, func() {
|
||||||
fakeFeatureLabels := map[string]string{LabelNs + "/source-feature.1": "1", LabelNs + "/source-feature.2": "2", LabelNs + "/source-feature.3": "val3"}
|
fakeFeatureLabels := map[string]string{LabelNs + "/source-feature.1": "1", LabelNs + "/source-feature.2": "2", LabelNs + "/source-feature.3": "val3"}
|
||||||
|
@ -73,17 +79,18 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
sort.Strings(fakeExtResourceNames)
|
sort.Strings(fakeExtResourceNames)
|
||||||
|
|
||||||
mockAPIHelper := new(apihelper.MockAPIHelpers)
|
mockAPIHelper := new(apihelper.MockAPIHelpers)
|
||||||
|
mockMaster := newMockMaster(mockAPIHelper)
|
||||||
mockClient := &k8sclient.Clientset{}
|
mockClient := &k8sclient.Clientset{}
|
||||||
// Mock node with old features
|
// Mock node with old features
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockNode.Labels[LabelNs+"/old-feature"] = "old-value"
|
mockNode.Labels[LabelNs+"/old-feature"] = "old-value"
|
||||||
mockNode.Annotations[AnnotationNs+"/feature-labels"] = "old-feature"
|
mockNode.Annotations[AnnotationNsBase+"/feature-labels"] = "old-feature"
|
||||||
|
|
||||||
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 := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("replace", "/metadata/annotations", AnnotationNs+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
|
apihelper.NewJsonPatch("replace", "/metadata/annotations", AnnotationNsBase+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", AnnotationNs+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", AnnotationNsBase+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
|
||||||
apihelper.NewJsonPatch("remove", "/metadata/labels", LabelNs+"/old-feature", ""),
|
apihelper.NewJsonPatch("remove", "/metadata/labels", LabelNs+"/old-feature", ""),
|
||||||
}
|
}
|
||||||
for k, v := range fakeFeatureLabels {
|
for k, v := range fakeFeatureLabels {
|
||||||
|
@ -103,7 +110,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once()
|
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once()
|
||||||
mockAPIHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(metadataPatches))).Return(nil)
|
mockAPIHelper.On("PatchNode", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(metadataPatches))).Return(nil)
|
||||||
mockAPIHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(statusPatches))).Return(nil)
|
mockAPIHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(statusPatches))).Return(nil)
|
||||||
err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
err := mockMaster.updateNodeFeatures(mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
||||||
|
|
||||||
Convey("Error is nil", func() {
|
Convey("Error is nil", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
@ -113,7 +120,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
Convey("When I fail to update the node with feature labels", func() {
|
Convey("When I fail to update the node with feature labels", func() {
|
||||||
expectedError := errors.New("fake error")
|
expectedError := errors.New("fake error")
|
||||||
mockAPIHelper.On("GetClient").Return(nil, expectedError)
|
mockAPIHelper.On("GetClient").Return(nil, expectedError)
|
||||||
err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
err := mockMaster.updateNodeFeatures(mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
||||||
|
|
||||||
Convey("Error is produced", func() {
|
Convey("Error is produced", func() {
|
||||||
So(err, ShouldEqual, expectedError)
|
So(err, ShouldEqual, expectedError)
|
||||||
|
@ -123,7 +130,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
Convey("When I fail to get a mock client while updating feature labels", func() {
|
Convey("When I fail to get a mock client while updating feature labels", func() {
|
||||||
expectedError := errors.New("fake error")
|
expectedError := errors.New("fake error")
|
||||||
mockAPIHelper.On("GetClient").Return(nil, expectedError)
|
mockAPIHelper.On("GetClient").Return(nil, expectedError)
|
||||||
err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
err := mockMaster.updateNodeFeatures(mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
||||||
|
|
||||||
Convey("Error is produced", func() {
|
Convey("Error is produced", func() {
|
||||||
So(err, ShouldEqual, expectedError)
|
So(err, ShouldEqual, expectedError)
|
||||||
|
@ -134,7 +141,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
expectedError := errors.New("fake error")
|
expectedError := errors.New("fake error")
|
||||||
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(nil, expectedError).Once()
|
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(nil, expectedError).Once()
|
||||||
err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
err := mockMaster.updateNodeFeatures(mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
||||||
|
|
||||||
Convey("Error is produced", func() {
|
Convey("Error is produced", func() {
|
||||||
So(err, ShouldEqual, expectedError)
|
So(err, ShouldEqual, expectedError)
|
||||||
|
@ -146,7 +153,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
mockAPIHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once()
|
mockAPIHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, nil).Once()
|
||||||
mockAPIHelper.On("PatchNode", mockClient, mockNodeName, mock.Anything).Return(expectedError).Once()
|
mockAPIHelper.On("PatchNode", mockClient, mockNodeName, mock.Anything).Return(expectedError).Once()
|
||||||
err := updateNodeFeatures(mockAPIHelper, mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
err := mockMaster.updateNodeFeatures(mockNodeName, fakeFeatureLabels, fakeAnnotations, fakeExtResources)
|
||||||
|
|
||||||
Convey("Error is produced", func() {
|
Convey("Error is produced", func() {
|
||||||
So(err, ShouldEqual, expectedError)
|
So(err, ShouldEqual, expectedError)
|
||||||
|
@ -159,15 +166,16 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
func TestUpdateMasterNode(t *testing.T) {
|
func TestUpdateMasterNode(t *testing.T) {
|
||||||
Convey("When updating the nfd-master node", t, func() {
|
Convey("When updating the nfd-master node", t, func() {
|
||||||
mockHelper := &apihelper.MockAPIHelpers{}
|
mockHelper := &apihelper.MockAPIHelpers{}
|
||||||
|
mockMaster := newMockMaster(mockHelper)
|
||||||
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 := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", AnnotationNs+"/master.version", version.Get())}
|
apihelper.NewJsonPatch("add", "/metadata/annotations", AnnotationNsBase+"/master.version", version.Get())}
|
||||||
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)
|
||||||
err := updateMasterNode(mockHelper)
|
err := mockMaster.updateMasterNode()
|
||||||
Convey("No error should be returned", func() {
|
Convey("No error should be returned", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
@ -176,7 +184,7 @@ func TestUpdateMasterNode(t *testing.T) {
|
||||||
mockErr := errors.New("mock-error")
|
mockErr := errors.New("mock-error")
|
||||||
Convey("When getting API client fails", func() {
|
Convey("When getting API client fails", func() {
|
||||||
mockHelper.On("GetClient").Return(mockClient, mockErr)
|
mockHelper.On("GetClient").Return(mockClient, mockErr)
|
||||||
err := updateMasterNode(mockHelper)
|
err := mockMaster.updateMasterNode()
|
||||||
Convey("An error should be returned", func() {
|
Convey("An error should be returned", func() {
|
||||||
So(err, ShouldEqual, mockErr)
|
So(err, ShouldEqual, mockErr)
|
||||||
})
|
})
|
||||||
|
@ -185,7 +193,7 @@ func TestUpdateMasterNode(t *testing.T) {
|
||||||
Convey("When getting API node object fails", func() {
|
Convey("When getting API node object fails", func() {
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, mockErr)
|
mockHelper.On("GetNode", mockClient, mockNodeName).Return(mockNode, mockErr)
|
||||||
err := updateMasterNode(mockHelper)
|
err := mockMaster.updateMasterNode()
|
||||||
Convey("An error should be returned", func() {
|
Convey("An error should be returned", func() {
|
||||||
So(err, ShouldEqual, mockErr)
|
So(err, ShouldEqual, mockErr)
|
||||||
})
|
})
|
||||||
|
@ -195,7 +203,7 @@ func TestUpdateMasterNode(t *testing.T) {
|
||||||
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.Anything).Return(mockErr)
|
mockHelper.On("PatchNode", mockClient, mockNodeName, mock.Anything).Return(mockErr)
|
||||||
err := updateMasterNode(mockHelper)
|
err := mockMaster.updateMasterNode()
|
||||||
Convey("An error should be returned", func() {
|
Convey("An error should be returned", func() {
|
||||||
So(err, ShouldEqual, mockErr)
|
So(err, ShouldEqual, mockErr)
|
||||||
})
|
})
|
||||||
|
@ -205,10 +213,11 @@ func TestUpdateMasterNode(t *testing.T) {
|
||||||
|
|
||||||
func TestAddingExtResources(t *testing.T) {
|
func TestAddingExtResources(t *testing.T) {
|
||||||
Convey("When adding extended resources", t, func() {
|
Convey("When adding extended resources", t, func() {
|
||||||
|
mockMaster := newMockMaster(nil)
|
||||||
Convey("When there are no matching labels", func() {
|
Convey("When there are no matching labels", func() {
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockResourceLabels := ExtendedResources{}
|
mockResourceLabels := ExtendedResources{}
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(len(patches), ShouldEqual, 0)
|
So(len(patches), ShouldEqual, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -219,7 +228,7 @@ func TestAddingExtResources(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", "feature-1", "1"),
|
apihelper.NewJsonPatch("add", "/status/capacity", "feature-1", "1"),
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", "feature-2", "2"),
|
apihelper.NewJsonPatch("add", "/status/capacity", "feature-2", "2"),
|
||||||
}
|
}
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
|
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -227,7 +236,7 @@ func TestAddingExtResources(t *testing.T) {
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
||||||
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1"}
|
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1"}
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(len(patches), ShouldEqual, 0)
|
So(len(patches), ShouldEqual, 0)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -239,7 +248,7 @@ func TestAddingExtResources(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"),
|
apihelper.NewJsonPatch("replace", "/status/capacity", "feature-1", "1"),
|
||||||
apihelper.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"),
|
apihelper.NewJsonPatch("replace", "/status/allocatable", "feature-1", "1"),
|
||||||
}
|
}
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
|
So(sortJsonPatches(patches), ShouldResemble, sortJsonPatches(expectedPatches))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -247,22 +256,23 @@ func TestAddingExtResources(t *testing.T) {
|
||||||
|
|
||||||
func TestRemovingExtResources(t *testing.T) {
|
func TestRemovingExtResources(t *testing.T) {
|
||||||
Convey("When removing extended resources", t, func() {
|
Convey("When removing extended resources", t, func() {
|
||||||
|
mockMaster := newMockMaster(nil)
|
||||||
Convey("When none are removed", func() {
|
Convey("When none are removed", func() {
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1", LabelNs + "/feature-2": "2"}
|
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1", LabelNs + "/feature-2": "2"}
|
||||||
mockNode.Annotations[AnnotationNs+"/extended-resources"] = "feature-1,feature-2"
|
mockNode.Annotations[AnnotationNsBase+"/extended-resources"] = "feature-1,feature-2"
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(len(patches), ShouldEqual, 0)
|
So(len(patches), ShouldEqual, 0)
|
||||||
})
|
})
|
||||||
Convey("When the related label is gone", func() {
|
Convey("When the related label is gone", func() {
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockResourceLabels := ExtendedResources{LabelNs + "/feature-4": "", LabelNs + "/feature-2": "2"}
|
mockResourceLabels := ExtendedResources{LabelNs + "/feature-4": "", LabelNs + "/feature-2": "2"}
|
||||||
mockNode.Annotations[AnnotationNs+"/extended-resources"] = "feature-4,feature-2"
|
mockNode.Annotations[AnnotationNsBase+"/extended-resources"] = "feature-4,feature-2"
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-4")] = *resource.NewQuantity(4, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-4")] = *resource.NewQuantity(4, resource.BinarySI)
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(len(patches), ShouldBeGreaterThan, 0)
|
So(len(patches), ShouldBeGreaterThan, 0)
|
||||||
})
|
})
|
||||||
Convey("When the extended resource is no longer wanted", func() {
|
Convey("When the extended resource is no longer wanted", func() {
|
||||||
|
@ -270,8 +280,8 @@ func TestRemovingExtResources(t *testing.T) {
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
|
||||||
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
|
||||||
mockResourceLabels := ExtendedResources{LabelNs + "/feature-2": "2"}
|
mockResourceLabels := ExtendedResources{LabelNs + "/feature-2": "2"}
|
||||||
mockNode.Annotations[AnnotationNs+"/extended-resources"] = "feature-1,feature-2"
|
mockNode.Annotations[AnnotationNsBase+"/extended-resources"] = "feature-1,feature-2"
|
||||||
patches := createExtendedResourcePatches(mockNode, mockResourceLabels)
|
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
|
||||||
So(len(patches), ShouldBeGreaterThan, 0)
|
So(len(patches), ShouldBeGreaterThan, 0)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -282,9 +292,9 @@ func TestSetLabels(t *testing.T) {
|
||||||
const workerName = "mock-worker"
|
const workerName = "mock-worker"
|
||||||
const workerVer = "0.1-test"
|
const workerVer = "0.1-test"
|
||||||
mockHelper := &apihelper.MockAPIHelpers{}
|
mockHelper := &apihelper.MockAPIHelpers{}
|
||||||
|
mockMaster := newMockMaster(mockHelper)
|
||||||
mockClient := &k8sclient.Clientset{}
|
mockClient := &k8sclient.Clientset{}
|
||||||
mockNode := newMockNode()
|
mockNode := newMockNode()
|
||||||
mockServer := labelerServer{args: Args{LabelWhiteList: regexp.MustCompile("")}, apiHelper: mockHelper}
|
|
||||||
mockCtx := context.Background()
|
mockCtx := context.Background()
|
||||||
// In the gRPC request the label names may omit the default ns
|
// In the gRPC request the label names may omit the default ns
|
||||||
mockLabels := map[string]string{"feature-1": "1", "feature-2": "val-2", "feature-3": "3"}
|
mockLabels := map[string]string{"feature-1": "1", "feature-2": "val-2", "feature-3": "3"}
|
||||||
|
@ -298,11 +308,14 @@ func TestSetLabels(t *testing.T) {
|
||||||
|
|
||||||
expectedStatusPatches := []apihelper.JsonPatch{}
|
expectedStatusPatches := []apihelper.JsonPatch{}
|
||||||
|
|
||||||
|
wvAnnotation := path.Join(AnnotationNsBase, workerVersionAnnotation)
|
||||||
|
flAnnotation := path.Join(AnnotationNsBase, featureLabelAnnotation)
|
||||||
|
erAnnotation := path.Join(AnnotationNsBase, extendedResourceAnnotation)
|
||||||
Convey("When node update succeeds", func() {
|
Convey("When node update succeeds", func() {
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", workerVersionAnnotation, workerVer),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", wvAnnotation, workerVer),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", featureLabelAnnotation, strings.Join(mockLabelNames, ",")),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", flAnnotation, strings.Join(mockLabelNames, ",")),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", extendedResourceAnnotation, ""),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", erAnnotation, ""),
|
||||||
}
|
}
|
||||||
for k, v := range mockLabels {
|
for k, v := range mockLabels {
|
||||||
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/"+k, v))
|
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/"+k, v))
|
||||||
|
@ -312,7 +325,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).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)
|
||||||
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("No error should be returned", func() {
|
Convey("No error should be returned", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
@ -320,54 +333,56 @@ func TestSetLabels(t *testing.T) {
|
||||||
|
|
||||||
Convey("When --label-whitelist is specified", func() {
|
Convey("When --label-whitelist is specified", func() {
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", workerVersionAnnotation, workerVer),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", wvAnnotation, workerVer),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", featureLabelAnnotation, "feature-2"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", flAnnotation, "feature-2"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", extendedResourceAnnotation, ""),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", erAnnotation, ""),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockServer.args.LabelWhiteList = regexp.MustCompile("^f.*2$")
|
mockMaster.args.LabelWhiteList = regexp.MustCompile("^f.*2$")
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).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)
|
||||||
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("Error is nil", func() {
|
Convey("Error is nil", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When --extra-label-ns is specified", func() {
|
Convey("When --extra-label-ns and --instance are specified", func() {
|
||||||
// In the gRPC request the label names may omit the default ns
|
// In the gRPC request the label names may omit the default ns
|
||||||
|
instance := "foo"
|
||||||
mockLabels := map[string]string{"feature-1": "val-1",
|
mockLabels := map[string]string{"feature-1": "val-1",
|
||||||
"valid.ns/feature-2": "val-2",
|
"valid.ns/feature-2": "val-2",
|
||||||
"invalid.ns/feature-3": "val-3"}
|
"invalid.ns/feature-3": "val-3"}
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", workerVersionAnnotation, workerVer),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+wvAnnotation, workerVer),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", featureLabelAnnotation, "feature-1,valid.ns/feature-2"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+flAnnotation, "feature-1,valid.ns/feature-2"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", extendedResourceAnnotation, ""),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+erAnnotation, ""),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-1", mockLabels["feature-1"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-1", mockLabels["feature-1"]),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockServer.args.ExtraLabelNs = map[string]struct{}{"valid.ns": struct{}{}}
|
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": struct{}{}}
|
||||||
|
mockMaster.annotationNs = instance + "." + AnnotationNsBase
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).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)
|
||||||
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||||
mockReq := &labeler.SetLabelsRequest{NodeName: workerName, NfdVersion: workerVer, Labels: mockLabels}
|
mockReq := &labeler.SetLabelsRequest{NodeName: workerName, NfdVersion: workerVer, Labels: mockLabels}
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("Error is nil", func() {
|
Convey("Error is nil", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
mockMaster.annotationNs = AnnotationNsBase
|
||||||
})
|
})
|
||||||
|
|
||||||
Convey("When --resource-labels is specified", func() {
|
Convey("When --resource-labels is specified", func() {
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", workerVersionAnnotation, workerVer),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", wvAnnotation, workerVer),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", featureLabelAnnotation, "feature-2"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", flAnnotation, "feature-2"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations", extendedResourceAnnotation, "feature-1,feature-3"),
|
apihelper.NewJsonPatch("add", "/metadata/annotations", erAnnotation, "feature-1,feature-3"),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
|
||||||
}
|
}
|
||||||
expectedStatusPatches := []apihelper.JsonPatch{
|
expectedStatusPatches := []apihelper.JsonPatch{
|
||||||
|
@ -375,12 +390,12 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", LabelNs+"/feature-3", mockLabels["feature-3"]),
|
apihelper.NewJsonPatch("add", "/status/capacity", LabelNs+"/feature-3", mockLabels["feature-3"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockServer.args.ResourceLabels = []string{"feature-3", "feature-1"}
|
mockMaster.args.ResourceLabels = []string{"feature-3", "feature-1"}
|
||||||
mockHelper.On("GetClient").Return(mockClient, nil)
|
mockHelper.On("GetClient").Return(mockClient, nil)
|
||||||
mockHelper.On("GetNode", mockClient, workerName).Return(mockNode, nil)
|
mockHelper.On("GetNode", mockClient, workerName).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)
|
||||||
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("Error is nil", func() {
|
Convey("Error is nil", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
@ -389,15 +404,15 @@ func TestSetLabels(t *testing.T) {
|
||||||
mockErr := errors.New("mock-error")
|
mockErr := errors.New("mock-error")
|
||||||
Convey("When node update fails", func() {
|
Convey("When node update fails", func() {
|
||||||
mockHelper.On("GetClient").Return(mockClient, mockErr)
|
mockHelper.On("GetClient").Return(mockClient, mockErr)
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("An error should be returned", func() {
|
Convey("An error should be returned", func() {
|
||||||
So(err, ShouldEqual, mockErr)
|
So(err, ShouldEqual, mockErr)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
mockServer.args.NoPublish = true
|
mockMaster.args.NoPublish = true
|
||||||
Convey("With '--no-publish'", func() {
|
Convey("With '--no-publish'", func() {
|
||||||
_, err := mockServer.SetLabels(mockCtx, mockReq)
|
_, err := mockMaster.SetLabels(mockCtx, mockReq)
|
||||||
Convey("Operation should succeed", func() {
|
Convey("Operation should succeed", func() {
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
})
|
})
|
||||||
|
|
|
@ -45,21 +45,20 @@ const (
|
||||||
// Namespace for feature labels
|
// Namespace for feature labels
|
||||||
LabelNs = "feature.node.kubernetes.io"
|
LabelNs = "feature.node.kubernetes.io"
|
||||||
|
|
||||||
// Namespace for all NFD-related annotations
|
// Base namespace for all NFD-related annotations
|
||||||
AnnotationNs = "nfd.node.kubernetes.io"
|
AnnotationNsBase = "nfd.node.kubernetes.io"
|
||||||
|
|
||||||
// NFD Annotations
|
// NFD Annotations
|
||||||
extendedResourceAnnotation = AnnotationNs + "/extended-resources"
|
extendedResourceAnnotation = "extended-resources"
|
||||||
featureLabelAnnotation = AnnotationNs + "/feature-labels"
|
featureLabelAnnotation = "feature-labels"
|
||||||
masterVersionAnnotation = AnnotationNs + "/master.version"
|
masterVersionAnnotation = "master.version"
|
||||||
workerVersionAnnotation = AnnotationNs + "/worker.version"
|
workerVersionAnnotation = "worker.version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// package loggers
|
// package loggers
|
||||||
var (
|
var (
|
||||||
stdoutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
stdoutLogger = log.New(os.Stdout, "", log.LstdFlags)
|
||||||
stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
|
stderrLogger = log.New(os.Stderr, "", log.LstdFlags)
|
||||||
nodeName = os.Getenv("NODE_NAME")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Labels are a Kubernetes representation of discovered features.
|
// Labels are a Kubernetes representation of discovered features.
|
||||||
|
@ -76,6 +75,7 @@ type Args struct {
|
||||||
CaFile string
|
CaFile string
|
||||||
CertFile string
|
CertFile string
|
||||||
ExtraLabelNs map[string]struct{}
|
ExtraLabelNs map[string]struct{}
|
||||||
|
Instance string
|
||||||
KeyFile string
|
KeyFile string
|
||||||
Kubeconfig string
|
Kubeconfig string
|
||||||
LabelWhiteList *regexp.Regexp
|
LabelWhiteList *regexp.Regexp
|
||||||
|
@ -93,15 +93,26 @@ type NfdMaster interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type nfdMaster struct {
|
type nfdMaster struct {
|
||||||
args Args
|
args Args
|
||||||
server *grpc.Server
|
nodeName string
|
||||||
ready chan bool
|
annotationNs string
|
||||||
apihelper apihelper.APIHelpers
|
server *grpc.Server
|
||||||
|
ready chan bool
|
||||||
|
apihelper apihelper.APIHelpers
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create new NfdMaster server instance.
|
// Create new NfdMaster server instance.
|
||||||
func NewNfdMaster(args Args) (NfdMaster, error) {
|
func NewNfdMaster(args Args) (NfdMaster, error) {
|
||||||
nfd := &nfdMaster{args: args, ready: make(chan bool, 1)}
|
nfd := &nfdMaster{args: args,
|
||||||
|
nodeName: os.Getenv("NODE_NAME"),
|
||||||
|
ready: make(chan bool, 1),
|
||||||
|
}
|
||||||
|
|
||||||
|
if args.Instance == "" {
|
||||||
|
nfd.annotationNs = AnnotationNsBase
|
||||||
|
} else {
|
||||||
|
nfd.annotationNs = args.Instance + "." + AnnotationNsBase
|
||||||
|
}
|
||||||
|
|
||||||
// Check TLS related args
|
// Check TLS related args
|
||||||
if args.CertFile != "" || args.KeyFile != "" || args.CaFile != "" {
|
if args.CertFile != "" || args.KeyFile != "" || args.CaFile != "" {
|
||||||
|
@ -126,14 +137,17 @@ func NewNfdMaster(args Args) (NfdMaster, error) {
|
||||||
// is called.
|
// is called.
|
||||||
func (m *nfdMaster) Run() error {
|
func (m *nfdMaster) Run() error {
|
||||||
stdoutLogger.Printf("Node Feature Discovery Master %s", version.Get())
|
stdoutLogger.Printf("Node Feature Discovery Master %s", version.Get())
|
||||||
stdoutLogger.Printf("NodeName: '%s'", nodeName)
|
if m.args.Instance != "" {
|
||||||
|
stdoutLogger.Printf("Master instance: '%s'", m.args.Instance)
|
||||||
|
}
|
||||||
|
stdoutLogger.Printf("NodeName: '%s'", m.nodeName)
|
||||||
|
|
||||||
if m.args.Prune {
|
if m.args.Prune {
|
||||||
return m.prune()
|
return m.prune()
|
||||||
}
|
}
|
||||||
|
|
||||||
if !m.args.NoPublish {
|
if !m.args.NoPublish {
|
||||||
err := updateMasterNode(m.apihelper)
|
err := m.updateMasterNode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to update master node: %v", err)
|
return fmt.Errorf("failed to update master node: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -175,7 +189,7 @@ func (m *nfdMaster) Run() error {
|
||||||
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConfig)))
|
||||||
}
|
}
|
||||||
m.server = grpc.NewServer(serverOpts...)
|
m.server = grpc.NewServer(serverOpts...)
|
||||||
pb.RegisterLabelerServer(m.server, &labelerServer{args: m.args, apiHelper: m.apihelper})
|
pb.RegisterLabelerServer(m.server, m)
|
||||||
stdoutLogger.Printf("gRPC server serving on port: %d", m.args.Port)
|
stdoutLogger.Printf("gRPC server serving on port: %d", m.args.Port)
|
||||||
return m.server.Serve(lis)
|
return m.server.Serve(lis)
|
||||||
}
|
}
|
||||||
|
@ -216,7 +230,7 @@ func (m *nfdMaster) prune() error {
|
||||||
stdoutLogger.Printf("pruning node %q...", node.Name)
|
stdoutLogger.Printf("pruning node %q...", node.Name)
|
||||||
|
|
||||||
// Prune labels and extended resources
|
// Prune labels and extended resources
|
||||||
err := updateNodeFeatures(m.apihelper, node.Name, Labels{}, Annotations{}, ExtendedResources{})
|
err := m.updateNodeFeatures(node.Name, Labels{}, Annotations{}, ExtendedResources{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to prune labels from node %q: %v", node.Name, err)
|
return fmt.Errorf("failed to prune labels from node %q: %v", node.Name, err)
|
||||||
}
|
}
|
||||||
|
@ -227,7 +241,7 @@ func (m *nfdMaster) prune() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for a := range node.Annotations {
|
for a := range node.Annotations {
|
||||||
if strings.HasPrefix(a, AnnotationNs) {
|
if strings.HasPrefix(a, m.annotationNs) {
|
||||||
delete(node.Annotations, a)
|
delete(node.Annotations, a)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -241,19 +255,22 @@ func (m *nfdMaster) prune() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advertise NFD master information
|
// Advertise NFD master information
|
||||||
func updateMasterNode(helper apihelper.APIHelpers) error {
|
func (m *nfdMaster) updateMasterNode() error {
|
||||||
cli, err := helper.GetClient()
|
cli, err := m.apihelper.GetClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
node, err := helper.GetNode(cli, nodeName)
|
node, err := m.apihelper.GetNode(cli, m.nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Advertise NFD version as an annotation
|
// Advertise NFD version as an annotation
|
||||||
p := createPatches(nil, node.Annotations, Annotations{masterVersionAnnotation: version.Get()}, "/metadata/annotations")
|
p := createPatches(nil,
|
||||||
err = helper.PatchNode(cli, node.Name, p)
|
node.Annotations,
|
||||||
|
Annotations{m.annotationName(masterVersionAnnotation): version.Get()},
|
||||||
|
"/metadata/annotations")
|
||||||
|
err = m.apihelper.PatchNode(cli, node.Name, p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderrLogger.Printf("failed to patch node annotations: %v", err)
|
stderrLogger.Printf("failed to patch node annotations: %v", err)
|
||||||
return err
|
return err
|
||||||
|
@ -310,15 +327,9 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
|
||||||
return outLabels, extendedResources
|
return outLabels, extendedResources
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implement LabelerServer
|
// SetLabels implements LabelerServer
|
||||||
type labelerServer struct {
|
func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.SetLabelsReply, error) {
|
||||||
args Args
|
if m.args.VerifyNodeName {
|
||||||
apiHelper apihelper.APIHelpers
|
|
||||||
}
|
|
||||||
|
|
||||||
// Service SetLabels
|
|
||||||
func (s *labelerServer) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.SetLabelsReply, error) {
|
|
||||||
if s.args.VerifyNodeName {
|
|
||||||
// Client authorization.
|
// Client authorization.
|
||||||
// Check that the node name matches the CN from the TLS cert
|
// Check that the node name matches the CN from the TLS cert
|
||||||
client, ok := peer.FromContext(c)
|
client, ok := peer.FromContext(c)
|
||||||
|
@ -343,13 +354,13 @@ func (s *labelerServer) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*p
|
||||||
}
|
}
|
||||||
stdoutLogger.Printf("REQUEST Node: %s NFD-version: %s Labels: %s", r.NodeName, r.NfdVersion, r.Labels)
|
stdoutLogger.Printf("REQUEST Node: %s NFD-version: %s Labels: %s", r.NodeName, r.NfdVersion, r.Labels)
|
||||||
|
|
||||||
labels, extendedResources := filterFeatureLabels(r.Labels, s.args.ExtraLabelNs, s.args.LabelWhiteList, s.args.ResourceLabels)
|
labels, extendedResources := filterFeatureLabels(r.Labels, m.args.ExtraLabelNs, m.args.LabelWhiteList, m.args.ResourceLabels)
|
||||||
|
|
||||||
if !s.args.NoPublish {
|
if !m.args.NoPublish {
|
||||||
// Advertise NFD worker version as an annotation
|
// Advertise NFD worker version as an annotation
|
||||||
annotations := Annotations{workerVersionAnnotation: r.NfdVersion}
|
annotations := Annotations{m.annotationName(workerVersionAnnotation): r.NfdVersion}
|
||||||
|
|
||||||
err := updateNodeFeatures(s.apiHelper, r.NodeName, labels, annotations, extendedResources)
|
err := m.updateNodeFeatures(r.NodeName, labels, annotations, extendedResources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderrLogger.Printf("failed to advertise labels: %s", err.Error())
|
stderrLogger.Printf("failed to advertise labels: %s", err.Error())
|
||||||
return &pb.SetLabelsReply{}, err
|
return &pb.SetLabelsReply{}, err
|
||||||
|
@ -361,14 +372,14 @@ func (s *labelerServer) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*p
|
||||||
// updateNodeFeatures ensures the Kubernetes node object is up to date,
|
// updateNodeFeatures ensures the Kubernetes node object is up to date,
|
||||||
// creating new labels and extended resources where necessary and removing
|
// creating new labels and extended resources where necessary and removing
|
||||||
// outdated ones. Also updates the corresponding annotations.
|
// outdated ones. Also updates the corresponding annotations.
|
||||||
func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Labels, annotations Annotations, extendedResources ExtendedResources) error {
|
func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotations Annotations, extendedResources ExtendedResources) error {
|
||||||
cli, err := helper.GetClient()
|
cli, err := m.apihelper.GetClient()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the worker node object
|
// Get the worker node object
|
||||||
node, err := helper.GetNode(cli, nodeName)
|
node, err := m.apihelper.GetNode(cli, nodeName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -380,7 +391,7 @@ func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Lab
|
||||||
labelKeys = append(labelKeys, strings.TrimPrefix(key, LabelNs+"/"))
|
labelKeys = append(labelKeys, strings.TrimPrefix(key, LabelNs+"/"))
|
||||||
}
|
}
|
||||||
sort.Strings(labelKeys)
|
sort.Strings(labelKeys)
|
||||||
annotations[featureLabelAnnotation] = strings.Join(labelKeys, ",")
|
annotations[m.annotationName(featureLabelAnnotation)] = strings.Join(labelKeys, ",")
|
||||||
|
|
||||||
// Store names of extended resources in an annotation
|
// Store names of extended resources in an annotation
|
||||||
extendedResourceKeys := make([]string, 0, len(extendedResources))
|
extendedResourceKeys := make([]string, 0, len(extendedResources))
|
||||||
|
@ -389,10 +400,10 @@ func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Lab
|
||||||
extendedResourceKeys = append(extendedResourceKeys, strings.TrimPrefix(key, LabelNs+"/"))
|
extendedResourceKeys = append(extendedResourceKeys, strings.TrimPrefix(key, LabelNs+"/"))
|
||||||
}
|
}
|
||||||
sort.Strings(extendedResourceKeys)
|
sort.Strings(extendedResourceKeys)
|
||||||
annotations[extendedResourceAnnotation] = strings.Join(extendedResourceKeys, ",")
|
annotations[m.annotationName(extendedResourceAnnotation)] = strings.Join(extendedResourceKeys, ",")
|
||||||
|
|
||||||
// Create JSON patches for changes in labels and annotations
|
// Create JSON patches for changes in labels and annotations
|
||||||
oldLabels := stringToNsNames(node.Annotations[featureLabelAnnotation], LabelNs)
|
oldLabels := stringToNsNames(node.Annotations[m.annotationName(featureLabelAnnotation)], LabelNs)
|
||||||
patches := createPatches(oldLabels, node.Labels, labels, "/metadata/labels")
|
patches := createPatches(oldLabels, node.Labels, labels, "/metadata/labels")
|
||||||
patches = append(patches, createPatches(nil, node.Annotations, annotations, "/metadata/annotations")...)
|
patches = append(patches, createPatches(nil, node.Annotations, annotations, "/metadata/annotations")...)
|
||||||
|
|
||||||
|
@ -401,15 +412,15 @@ func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Lab
|
||||||
patches = append(patches, removeLabelsWithPrefix(node, "node.alpha.kubernetes-incubator.io/node-feature-discovery")...)
|
patches = append(patches, removeLabelsWithPrefix(node, "node.alpha.kubernetes-incubator.io/node-feature-discovery")...)
|
||||||
|
|
||||||
// Patch the node object in the apiserver
|
// Patch the node object in the apiserver
|
||||||
err = helper.PatchNode(cli, node.Name, patches)
|
err = m.apihelper.PatchNode(cli, node.Name, patches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderrLogger.Printf("error while patching node object: %s", err.Error())
|
stderrLogger.Printf("error while patching node object: %s", err.Error())
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// patch node status with extended resource changes
|
// patch node status with extended resource changes
|
||||||
patches = createExtendedResourcePatches(node, extendedResources)
|
patches = m.createExtendedResourcePatches(node, extendedResources)
|
||||||
err = helper.PatchNodeStatus(cli, node.Name, patches)
|
err = m.apihelper.PatchNodeStatus(cli, node.Name, patches)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
stderrLogger.Printf("error while patching extended resources: %s", err.Error())
|
stderrLogger.Printf("error while patching extended resources: %s", err.Error())
|
||||||
return err
|
return err
|
||||||
|
@ -418,6 +429,10 @@ func updateNodeFeatures(helper apihelper.APIHelpers, nodeName string, labels Lab
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *nfdMaster) annotationName(name string) string {
|
||||||
|
return path.Join(m.annotationNs, name)
|
||||||
|
}
|
||||||
|
|
||||||
// Remove any labels having the given prefix
|
// Remove any labels having the given prefix
|
||||||
func removeLabelsWithPrefix(n *api.Node, search string) []apihelper.JsonPatch {
|
func removeLabelsWithPrefix(n *api.Node, search string) []apihelper.JsonPatch {
|
||||||
var p []apihelper.JsonPatch
|
var p []apihelper.JsonPatch
|
||||||
|
@ -460,11 +475,11 @@ 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 createExtendedResourcePatches(n *api.Node, extendedResources ExtendedResources) []apihelper.JsonPatch {
|
func (m *nfdMaster) createExtendedResourcePatches(n *api.Node, extendedResources ExtendedResources) []apihelper.JsonPatch {
|
||||||
patches := []apihelper.JsonPatch{}
|
patches := []apihelper.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[extendedResourceAnnotation], LabelNs)
|
oldResources := stringToNsNames(n.Annotations[m.annotationName(extendedResourceAnnotation)], LabelNs)
|
||||||
|
|
||||||
// figure out which resources to remove
|
// figure out which resources to remove
|
||||||
for _, resource := range oldResources {
|
for _, resource := range oldResources {
|
||||||
|
|
|
@ -393,7 +393,7 @@ func cleanupNode(cs clientset.Interface) {
|
||||||
|
|
||||||
// Remove annotations
|
// Remove annotations
|
||||||
for key := range node.Annotations {
|
for key := range node.Annotations {
|
||||||
if strings.HasPrefix(key, master.AnnotationNs) {
|
if strings.HasPrefix(key, master.AnnotationNsBase) {
|
||||||
delete(node.Annotations, key)
|
delete(node.Annotations, key)
|
||||||
update = true
|
update = true
|
||||||
}
|
}
|
||||||
|
@ -573,7 +573,7 @@ var _ = framework.KubeDescribe("[NFD] Node Feature Discovery", func() {
|
||||||
gomega.Expect(node.Annotations).To(gomega.HaveKey(k))
|
gomega.Expect(node.Annotations).To(gomega.HaveKey(k))
|
||||||
}
|
}
|
||||||
for k := range node.Annotations {
|
for k := range node.Annotations {
|
||||||
if strings.HasPrefix(k, master.AnnotationNs) {
|
if strings.HasPrefix(k, master.AnnotationNsBase) {
|
||||||
if _, ok := nodeConf.ExpectedAnnotationValues[k]; ok {
|
if _, ok := nodeConf.ExpectedAnnotationValues[k]; ok {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
@ -587,7 +587,7 @@ var _ = framework.KubeDescribe("[NFD] Node Feature Discovery", func() {
|
||||||
|
|
||||||
// Node running nfd-master should have master version annotation
|
// Node running nfd-master should have master version annotation
|
||||||
if node.Name == masterPod.Spec.NodeName {
|
if node.Name == masterPod.Spec.NodeName {
|
||||||
gomega.Expect(node.Annotations).To(gomega.HaveKey(master.AnnotationNs + "master.version"))
|
gomega.Expect(node.Annotations).To(gomega.HaveKey(master.AnnotationNsBase + "master.version"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue