1
0
Fork 0
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:
Markus Lehtonen 2021-02-03 19:49:02 +02:00
parent 705687192d
commit e52ec3480f
6 changed files with 119 additions and 62 deletions

View file

@ -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)

View file

@ -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

View file

@ -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. | [&lt;instance&gt;.]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. | [&lt;instance&gt;.]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. | [&lt;instance&gt;.]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. | [&lt;instance&gt;.]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.

View file

@ -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{

View file

@ -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 {

View file

@ -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"))
} }
} }