1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-14 20:56:42 +00:00

Merge pull request #548 from marquiz/devel/profile-ns

nfd-master: allow profile.node.kubernetes.io label ns
This commit is contained in:
Kubernetes Prow Robot 2021-08-27 07:24:04 -07:00 committed by GitHub
commit 189f86bec8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 51 deletions

View file

@ -175,8 +175,9 @@ nfd-master -label-whitelist='.*cpuid\.'
The `-extra-label-ns` flag specifies a comma-separated list of allowed feature
label namespaces. By default, nfd-master only allows creating labels in the
default `feature.node.kubernetes.io` label namespace and its sub-namespaces
(e.g. `vendor.feature.node.kubernetes.io`). This option can be used to allow
default `feature.node.kubernetes.io` and `profile.node.kubernetes.io` label
namespaces and their sub-namespaces (e.g. `vendor.feature.node.kubernetes.io`
and `sub.ns.profile.node.kubernetes.io`). This option can be used to allow
other vendor or application specific namespaces for custom labels from the
local and custom feature sources.

View file

@ -31,8 +31,9 @@ The published node labels encode a few pieces of information:
- all built-in labels use `feature.node.kubernetes.io`
- user-specified custom labels ([custom](#custom) and
[local](#local--user-specific-features) feature sources)
- `feature.node.kubernetes.io` and its sub-namespaces (e.g.
`vendor.profile.node.kubernetes.io`) are allowed by default
- `feature.node.kubernetes.io` and `profile.node.kubernetes.io` plus their
sub-namespaces (e.g. `vendor.profile.node.kubernetes.io` and
`sub.ns.profile.node.kubernetes.io`) are allowed by default
- additional namespaces may be enabled with the
[`--extra-label-ns`](../advanced/master-commandline-reference#-extra-label-ns)
command line flag of nfd-master
@ -376,7 +377,7 @@ custom:
matchOn:
- kConfig: ["GCC_VERSION=100101"]
loadedKMod: ["kmod1"]
- name: "my.datacenter"
- name: "profile.node.kubernetes.io/my-datacenter"
value: "datacenter-1"
matchOn:
- nodename: [ "node-datacenter1-rack.*-server.*" ]
@ -413,7 +414,7 @@ __In the example above:__
in-tree `kmod1` kernel module is loaded __AND__ it's built with
`GCC_VERSION=100101`.
- A node would contain the label:
`feature.node.kubernetes.io/my.datacenter=datacenter-1` if the node's name
`profile.node.kubernetes.io/my-datacenter=datacenter-1` if the node's name
matches the `node-datacenter1-rack.*-server.*` pattern, e.g.
`node-datacenter1-rack2-server42`
@ -577,7 +578,8 @@ e.g. for overriding labels created by other feature sources.
You can also override the default namespace of your labels using this format:
`<namespace>/<name>[=<value>]`. If using something else than
`[<sub-ns>.]feature.node.kubernetes.io`, you must whitelist your namespace
`[<sub-ns>.]feature.node.kubernetes.io` or
`[<sub-ns>.]profile.node.kubernetes.io`, you must whitelist your namespace
using the `-extra-label-ns` option on the master.
In this case, the name of the
file will not be added to the label name. For example, if you want to add the

View file

@ -63,19 +63,23 @@ func newMockMaster(apihelper apihelper.APIHelpers) *nfdMaster {
func TestUpdateNodeFeatures(t *testing.T) {
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{
FeatureLabelNs + "/source-feature.1": "1",
FeatureLabelNs + "/source-feature.2": "2",
FeatureLabelNs + "/source-feature.3": "val3",
ProfileLabelNs + "/profile-a": "val4"}
fakeAnnotations := map[string]string{"my-annotation": "my-val"}
fakeExtResources := ExtendedResources{LabelNs + "/source-feature.1": "1", LabelNs + "/source-feature.2": "2"}
fakeExtResources := ExtendedResources{FeatureLabelNs + "/source-feature.1": "1", FeatureLabelNs + "/source-feature.2": "2"}
fakeFeatureLabelNames := make([]string, 0, len(fakeFeatureLabels))
for k := range fakeFeatureLabels {
fakeFeatureLabelNames = append(fakeFeatureLabelNames, strings.TrimPrefix(k, LabelNs+"/"))
fakeFeatureLabelNames = append(fakeFeatureLabelNames, strings.TrimPrefix(k, FeatureLabelNs+"/"))
}
sort.Strings(fakeFeatureLabelNames)
fakeExtResourceNames := make([]string, 0, len(fakeExtResources))
for k := range fakeExtResources {
fakeExtResourceNames = append(fakeExtResourceNames, strings.TrimPrefix(k, LabelNs+"/"))
fakeExtResourceNames = append(fakeExtResourceNames, strings.TrimPrefix(k, FeatureLabelNs+"/"))
}
sort.Strings(fakeExtResourceNames)
@ -84,7 +88,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
mockClient := &k8sclient.Clientset{}
// Mock node with old features
mockNode := newMockNode()
mockNode.Labels[LabelNs+"/old-feature"] = "old-value"
mockNode.Labels[FeatureLabelNs+"/old-feature"] = "old-value"
mockNode.Annotations[AnnotationNsBase+"/feature-labels"] = "old-feature"
Convey("When I successfully update the node with feature labels", func() {
@ -92,7 +96,7 @@ func TestUpdateNodeFeatures(t *testing.T) {
metadataPatches := []apihelper.JsonPatch{
apihelper.NewJsonPatch("replace", "/metadata/annotations", AnnotationNsBase+"/feature-labels", strings.Join(fakeFeatureLabelNames, ",")),
apihelper.NewJsonPatch("add", "/metadata/annotations", AnnotationNsBase+"/extended-resources", strings.Join(fakeExtResourceNames, ",")),
apihelper.NewJsonPatch("remove", "/metadata/labels", LabelNs+"/old-feature", ""),
apihelper.NewJsonPatch("remove", "/metadata/labels", FeatureLabelNs+"/old-feature", ""),
}
for k, v := range fakeFeatureLabels {
metadataPatches = append(metadataPatches, apihelper.NewJsonPatch("add", "/metadata/labels", k, v))
@ -235,8 +239,8 @@ func TestAddingExtResources(t *testing.T) {
Convey("When the resource already exists", func() {
mockNode := newMockNode()
mockNode.Status.Capacity[api.ResourceName(LabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1"}
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
mockResourceLabels := ExtendedResources{FeatureLabelNs + "/feature-1": "1"}
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(len(patches), ShouldEqual, 0)
})
@ -260,27 +264,27 @@ func TestRemovingExtResources(t *testing.T) {
mockMaster := newMockMaster(nil)
Convey("When none are removed", func() {
mockNode := newMockNode()
mockResourceLabels := ExtendedResources{LabelNs + "/feature-1": "1", LabelNs + "/feature-2": "2"}
mockResourceLabels := ExtendedResources{FeatureLabelNs + "/feature-1": "1", FeatureLabelNs + "/feature-2": "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-2")] = *resource.NewQuantity(2, resource.BinarySI)
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(len(patches), ShouldEqual, 0)
})
Convey("When the related label is gone", func() {
mockNode := newMockNode()
mockResourceLabels := ExtendedResources{LabelNs + "/feature-4": "", LabelNs + "/feature-2": "2"}
mockResourceLabels := ExtendedResources{FeatureLabelNs + "/feature-4": "", FeatureLabelNs + "/feature-2": "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-2")] = *resource.NewQuantity(2, resource.BinarySI)
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-4")] = *resource.NewQuantity(4, resource.BinarySI)
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(len(patches), ShouldBeGreaterThan, 0)
})
Convey("When the extended resource is no longer wanted", func() {
mockNode := newMockNode()
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)
mockResourceLabels := ExtendedResources{LabelNs + "/feature-2": "2"}
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-1")] = *resource.NewQuantity(1, resource.BinarySI)
mockNode.Status.Capacity[api.ResourceName(FeatureLabelNs+"/feature-2")] = *resource.NewQuantity(2, resource.BinarySI)
mockResourceLabels := ExtendedResources{FeatureLabelNs + "/feature-2": "2"}
mockNode.Annotations[AnnotationNsBase+"/extended-resources"] = "feature-1,feature-2"
patches := mockMaster.createExtendedResourcePatches(mockNode, mockResourceLabels)
So(len(patches), ShouldBeGreaterThan, 0)
@ -319,7 +323,7 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/metadata/annotations", erAnnotation, ""),
}
for k, v := range mockLabels {
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/"+k, v))
expectedPatches = append(expectedPatches, apihelper.NewJsonPatch("add", "/metadata/labels", FeatureLabelNs+"/"+k, v))
}
mockHelper.On("GetClient").Return(mockClient, nil)
@ -337,7 +341,7 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/metadata/annotations", wvAnnotation, workerVer),
apihelper.NewJsonPatch("add", "/metadata/annotations", flAnnotation, "feature-2"),
apihelper.NewJsonPatch("add", "/metadata/annotations", erAnnotation, ""),
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-2", mockLabels["feature-2"]),
apihelper.NewJsonPatch("add", "/metadata/labels", FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
}
mockMaster.args.LabelWhiteList.Regexp = *regexp.MustCompile("^f.*2$")
@ -354,18 +358,23 @@ func TestSetLabels(t *testing.T) {
Convey("When --extra-label-ns and --instance are specified", func() {
// In the gRPC request the label names may omit the default ns
instance := "foo"
vendorLabel := "vendor." + LabelNs + "/feature-4"
vendorFeatureLabel := "vendor." + FeatureLabelNs + "/feature-4"
vendorProfileLabel := "vendor." + ProfileLabelNs + "/feature-5"
mockLabels := map[string]string{"feature-1": "val-1",
"valid.ns/feature-2": "val-2",
"invalid.ns/feature-3": "val-3",
vendorLabel: " val-4"}
vendorFeatureLabel: " val-4",
vendorProfileLabel: " val-5"}
expectedPatches := []apihelper.JsonPatch{
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+wvAnnotation, workerVer),
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+flAnnotation, "feature-1,valid.ns/feature-2,"+vendorLabel),
apihelper.NewJsonPatch("add", "/metadata/annotations",
instance+"."+flAnnotation,
"feature-1,valid.ns/feature-2,"+vendorFeatureLabel+","+vendorProfileLabel),
apihelper.NewJsonPatch("add", "/metadata/annotations", instance+"."+erAnnotation, ""),
apihelper.NewJsonPatch("add", "/metadata/labels", LabelNs+"/feature-1", mockLabels["feature-1"]),
apihelper.NewJsonPatch("add", "/metadata/labels", FeatureLabelNs+"/feature-1", mockLabels["feature-1"]),
apihelper.NewJsonPatch("add", "/metadata/labels", "valid.ns/feature-2", mockLabels["valid.ns/feature-2"]),
apihelper.NewJsonPatch("add", "/metadata/labels", vendorLabel, mockLabels[vendorLabel]),
apihelper.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
}
mockMaster.args.ExtraLabelNs = map[string]struct{}{"valid.ns": {}}
@ -387,11 +396,11 @@ func TestSetLabels(t *testing.T) {
apihelper.NewJsonPatch("add", "/metadata/annotations", wvAnnotation, workerVer),
apihelper.NewJsonPatch("add", "/metadata/annotations", flAnnotation, "feature-2"),
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", FeatureLabelNs+"/feature-2", mockLabels["feature-2"]),
}
expectedStatusPatches := []apihelper.JsonPatch{
apihelper.NewJsonPatch("add", "/status/capacity", LabelNs+"/feature-1", mockLabels["feature-1"]),
apihelper.NewJsonPatch("add", "/status/capacity", LabelNs+"/feature-3", mockLabels["feature-3"]),
apihelper.NewJsonPatch("add", "/status/capacity", FeatureLabelNs+"/feature-1", mockLabels["feature-1"]),
apihelper.NewJsonPatch("add", "/status/capacity", FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
}
mockMaster.args.ResourceLabels = map[string]struct{}{"feature-3": {}, "feature-1": {}}

View file

@ -45,11 +45,17 @@ import (
)
const (
// LabelNs defines the namespace for feature labels
LabelNs = "feature.node.kubernetes.io"
// FeatureLabelNs is the namespace for feature labels
FeatureLabelNs = "feature.node.kubernetes.io"
// LabelSubNsSuffix is the suffix for allowed label sub-namespaces
LabelSubNsSuffix = "." + LabelNs
// FeatureLabelSubNsSuffix is the suffix for allowed feature label sub-namespaces
FeatureLabelSubNsSuffix = "." + FeatureLabelNs
// ProfileLabelNs is the namespace for profile labels
ProfileLabelNs = "profile.node.kubernetes.io"
// ProfileLabelSubNsSuffix is the suffix for allowed profile label sub-namespaces
ProfileLabelSubNsSuffix = "." + ProfileLabelNs
// AnnotationNsBase namespace for all NFD-related annotations
AnnotationNsBase = "nfd.node.kubernetes.io"
@ -315,12 +321,13 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
for label, value := range labels {
// Add possibly missing default ns
label := addNs(label, LabelNs)
label := addNs(label, FeatureLabelNs)
ns, name := splitNs(label)
// Check label namespace, filter out if ns is not whitelisted
if ns != LabelNs && !strings.HasSuffix(ns, LabelSubNsSuffix) {
if ns != FeatureLabelNs && ns != ProfileLabelNs &&
!strings.HasSuffix(ns, FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, ProfileLabelSubNsSuffix) {
if _, ok := extraLabelNs[ns]; !ok {
klog.Errorf("Namespace %q is not allowed. Ignoring label %q\n", ns, label)
continue
@ -339,7 +346,7 @@ func filterFeatureLabels(labels Labels, extraLabelNs map[string]struct{}, labelW
extendedResources := ExtendedResources{}
for extendedResourceName := range extendedResourceNames {
// Add possibly missing default ns
extendedResourceName = addNs(extendedResourceName, LabelNs)
extendedResourceName = addNs(extendedResourceName, FeatureLabelNs)
if value, ok := outLabels[extendedResourceName]; ok {
if _, err := strconv.Atoi(value); err != nil {
klog.Errorf("bad label value (%s: %s) encountered for extended resource: %s", extendedResourceName, value, err.Error())
@ -433,7 +440,7 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
labelKeys := make([]string, 0, len(labels))
for key := range labels {
// Drop the ns part for labels in the default ns
labelKeys = append(labelKeys, strings.TrimPrefix(key, LabelNs+"/"))
labelKeys = append(labelKeys, strings.TrimPrefix(key, FeatureLabelNs+"/"))
}
sort.Strings(labelKeys)
annotations[m.annotationName(featureLabelAnnotation)] = strings.Join(labelKeys, ",")
@ -442,13 +449,13 @@ func (m *nfdMaster) updateNodeFeatures(nodeName string, labels Labels, annotatio
extendedResourceKeys := make([]string, 0, len(extendedResources))
for key := range extendedResources {
// Drop the ns part if in the default ns
extendedResourceKeys = append(extendedResourceKeys, strings.TrimPrefix(key, LabelNs+"/"))
extendedResourceKeys = append(extendedResourceKeys, strings.TrimPrefix(key, FeatureLabelNs+"/"))
}
sort.Strings(extendedResourceKeys)
annotations[m.annotationName(extendedResourceAnnotation)] = strings.Join(extendedResourceKeys, ",")
// Create JSON patches for changes in labels and annotations
oldLabels := stringToNsNames(node.Annotations[m.annotationName(featureLabelAnnotation)], LabelNs)
oldLabels := stringToNsNames(node.Annotations[m.annotationName(featureLabelAnnotation)], FeatureLabelNs)
patches := createPatches(oldLabels, node.Labels, labels, "/metadata/labels")
patches = append(patches, createPatches(nil, node.Annotations, annotations, "/metadata/annotations")...)
@ -522,7 +529,7 @@ func (m *nfdMaster) createExtendedResourcePatches(n *api.Node, extendedResources
patches := []apihelper.JsonPatch{}
// Form a list of namespaced resource names managed by us
oldResources := stringToNsNames(n.Annotations[m.annotationName(extendedResourceAnnotation)], LabelNs)
oldResources := stringToNsNames(n.Annotations[m.annotationName(extendedResourceAnnotation)], FeatureLabelNs)
// figure out which resources to remove
for _, resource := range oldResources {

View file

@ -426,7 +426,7 @@ func cleanupNode(cs clientset.Interface) {
update := false
// Remove labels
for key := range node.Labels {
if strings.HasPrefix(key, master.LabelNs) {
if strings.HasPrefix(key, master.FeatureLabelNs) {
delete(node.Labels, key)
update = true
}
@ -499,9 +499,9 @@ var _ = SIGDescribe("Node Feature Discovery", func() {
It("it should decorate the node with the fake feature labels", func() {
fakeFeatureLabels := map[string]string{
master.LabelNs + "/fake-fakefeature1": "true",
master.LabelNs + "/fake-fakefeature2": "true",
master.LabelNs + "/fake-fakefeature3": "true",
master.FeatureLabelNs + "/fake-fakefeature1": "true",
master.FeatureLabelNs + "/fake-fakefeature2": "true",
master.FeatureLabelNs + "/fake-fakefeature3": "true",
}
// Remove pre-existing stale annotations and labels
@ -528,7 +528,7 @@ var _ = SIGDescribe("Node Feature Discovery", func() {
// Check that there are no unexpected NFD labels
for k := range node.Labels {
if strings.HasPrefix(k, master.LabelNs) {
if strings.HasPrefix(k, master.FeatureLabelNs) {
Expect(fakeFeatureLabels).Should(HaveKey(k))
}
}
@ -593,7 +593,7 @@ var _ = SIGDescribe("Node Feature Discovery", func() {
Expect(node.Labels).To(HaveKey(k))
}
for k := range node.Labels {
if strings.HasPrefix(k, master.LabelNs) {
if strings.HasPrefix(k, master.FeatureLabelNs) {
if _, ok := nodeConf.ExpectedLabelValues[k]; ok {
continue
}