mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-15 17:50:49 +00:00
nfd-master: implement --instance flag
This can be used to help running multiple parallel NFD deployments in the same cluster. The flag changes the node annotation namespace to <instance>.nfd.node.kubernetes.io allowing different nfd-master intances to store metadata in separate annotations.
This commit is contained in:
parent
705687192d
commit
e52ec3480f
6 changed files with 119 additions and 62 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
|
||||||
|
|
|
@ -81,11 +81,15 @@ 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"
|
||||||
|
@ -53,6 +54,7 @@ func newMockNode() *api.Node {
|
||||||
func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
|
func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
|
||||||
return &nfdMaster{
|
return &nfdMaster{
|
||||||
nodeName: mockNodeName,
|
nodeName: mockNodeName,
|
||||||
|
annotationNs: AnnotationNsBase,
|
||||||
args: Args{LabelWhiteList: regexp.MustCompile("")},
|
args: Args{LabelWhiteList: regexp.MustCompile("")},
|
||||||
apihelper: apihelper,
|
apihelper: apihelper,
|
||||||
}
|
}
|
||||||
|
@ -82,13 +84,13 @@ func TestUpdateNodeFeatures(t *testing.T) {
|
||||||
// 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 {
|
||||||
|
@ -169,7 +171,7 @@ func TestUpdateMasterNode(t *testing.T) {
|
||||||
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)
|
||||||
|
@ -211,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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -225,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))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -233,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)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -245,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))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -253,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() {
|
||||||
|
@ -276,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)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -304,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))
|
||||||
|
@ -326,9 +333,9 @@ 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"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,20 +350,22 @@ func TestSetLabels(t *testing.T) {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
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"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.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)
|
||||||
|
@ -366,14 +375,14 @@ func TestSetLabels(t *testing.T) {
|
||||||
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{
|
||||||
|
|
|
@ -45,14 +45,14 @@ 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
|
||||||
|
@ -75,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
|
||||||
|
@ -94,6 +95,7 @@ type NfdMaster interface {
|
||||||
type nfdMaster struct {
|
type nfdMaster struct {
|
||||||
args Args
|
args Args
|
||||||
nodeName string
|
nodeName string
|
||||||
|
annotationNs string
|
||||||
server *grpc.Server
|
server *grpc.Server
|
||||||
ready chan bool
|
ready chan bool
|
||||||
apihelper apihelper.APIHelpers
|
apihelper apihelper.APIHelpers
|
||||||
|
@ -103,7 +105,14 @@ type nfdMaster struct {
|
||||||
func NewNfdMaster(args Args) (NfdMaster, error) {
|
func NewNfdMaster(args Args) (NfdMaster, error) {
|
||||||
nfd := &nfdMaster{args: args,
|
nfd := &nfdMaster{args: args,
|
||||||
nodeName: os.Getenv("NODE_NAME"),
|
nodeName: os.Getenv("NODE_NAME"),
|
||||||
ready: make(chan bool, 1)}
|
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 != "" {
|
||||||
|
@ -128,6 +137,9 @@ 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())
|
||||||
|
if m.args.Instance != "" {
|
||||||
|
stdoutLogger.Printf("Master instance: '%s'", m.args.Instance)
|
||||||
|
}
|
||||||
stdoutLogger.Printf("NodeName: '%s'", m.nodeName)
|
stdoutLogger.Printf("NodeName: '%s'", m.nodeName)
|
||||||
|
|
||||||
if m.args.Prune {
|
if m.args.Prune {
|
||||||
|
@ -229,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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -254,7 +266,10 @@ func (m *nfdMaster) updateMasterNode() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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,
|
||||||
|
node.Annotations,
|
||||||
|
Annotations{m.annotationName(masterVersionAnnotation): version.Get()},
|
||||||
|
"/metadata/annotations")
|
||||||
err = m.apihelper.PatchNode(cli, node.Name, p)
|
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)
|
||||||
|
@ -343,7 +358,7 @@ func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.Se
|
||||||
|
|
||||||
if !m.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 := m.updateNodeFeatures(r.NodeName, labels, annotations, extendedResources)
|
err := m.updateNodeFeatures(r.NodeName, labels, annotations, extendedResources)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -376,7 +391,7 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
|
||||||
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))
|
||||||
|
@ -385,10 +400,10 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
|
||||||
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")...)
|
||||||
|
|
||||||
|
@ -404,7 +419,7 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
|
||||||
}
|
}
|
||||||
|
|
||||||
// patch node status with extended resource changes
|
// patch node status with extended resource changes
|
||||||
patches = createExtendedResourcePatches(node, extendedResources)
|
patches = m.createExtendedResourcePatches(node, extendedResources)
|
||||||
err = m.apihelper.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())
|
||||||
|
@ -414,6 +429,10 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
|
||||||
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
|
||||||
|
@ -456,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…
Reference in a new issue