mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-05 16:27:05 +00:00
Merge pull request #1471 from marquiz/devel/default-ns-fix
nfd-master: predictable handling of unprefixed names
This commit is contained in:
commit
4e7f8b10be
3 changed files with 54 additions and 32 deletions
|
@ -422,20 +422,21 @@ func TestSetLabels(t *testing.T) {
|
||||||
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
|
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
|
||||||
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
|
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
|
||||||
mockLabels := map[string]string{
|
mockLabels := map[string]string{
|
||||||
"feature-1": "val-1",
|
"feature-1": "val-0",
|
||||||
"valid.ns/feature-2": "val-2",
|
"feature.node.kubernetes.io/feature-1": "val-1",
|
||||||
"random.denied.ns/feature-3": "val-3",
|
"valid.ns/feature-2": "val-2",
|
||||||
"kubernetes.io/feature-4": "val-4",
|
"random.denied.ns/feature-3": "val-3",
|
||||||
"sub.ns.kubernetes.io/feature-5": "val-5",
|
"kubernetes.io/feature-4": "val-4",
|
||||||
vendorFeatureLabel: "val-6",
|
"sub.ns.kubernetes.io/feature-5": "val-5",
|
||||||
vendorProfileLabel: "val-7",
|
vendorFeatureLabel: "val-6",
|
||||||
"--invalid-name--": "valid-val",
|
vendorProfileLabel: "val-7",
|
||||||
"valid-name": "--invalid-val--"}
|
"--invalid-name--": "valid-val",
|
||||||
|
"valid-name": "--invalid-val--"}
|
||||||
expectedPatches := []apihelper.JsonPatch{
|
expectedPatches := []apihelper.JsonPatch{
|
||||||
apihelper.NewJsonPatch("add", "/metadata/annotations",
|
apihelper.NewJsonPatch("add", "/metadata/annotations",
|
||||||
instance+"."+nfdv1alpha1.FeatureLabelsAnnotation,
|
instance+"."+nfdv1alpha1.FeatureLabelsAnnotation,
|
||||||
"feature-1,valid.ns/feature-2,"+vendorFeatureLabel+","+vendorProfileLabel),
|
"feature-1,valid.ns/feature-2,"+vendorFeatureLabel+","+vendorProfileLabel),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", nfdv1alpha1.FeatureLabelNs+"/feature-1", mockLabels["feature-1"]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", "feature.node.kubernetes.io/feature-1", mockLabels["feature.node.kubernetes.io/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"]),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
|
||||||
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
|
apihelper.NewJsonPatch("add", "/metadata/labels", vendorProfileLabel, mockLabels[vendorProfileLabel]),
|
||||||
|
@ -468,7 +469,7 @@ func TestSetLabels(t *testing.T) {
|
||||||
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
|
apihelper.NewJsonPatch("add", "/status/capacity", nfdv1alpha1.FeatureLabelNs+"/feature-3", mockLabels["feature-3"]),
|
||||||
}
|
}
|
||||||
|
|
||||||
mockMaster.config.ResourceLabels = map[string]struct{}{"feature-3": {}, "feature-1": {}}
|
mockMaster.config.ResourceLabels = map[string]struct{}{"feature.node.kubernetes.io/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("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||||
|
@ -503,7 +504,7 @@ func TestFilterLabels(t *testing.T) {
|
||||||
mockMaster := newMockMaster(mockHelper)
|
mockMaster := newMockMaster(mockHelper)
|
||||||
|
|
||||||
Convey("When using dynamic values", t, func() {
|
Convey("When using dynamic values", t, func() {
|
||||||
labelName := "testLabel"
|
labelName := "ns/testLabel"
|
||||||
labelValue := "@test.feature.LSM"
|
labelValue := "@test.feature.LSM"
|
||||||
features := nfdv1alpha1.Features{
|
features := nfdv1alpha1.Features{
|
||||||
Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{
|
Attributes: map[string]nfdv1alpha1.AttributeFeatureSet{
|
||||||
|
|
|
@ -523,9 +523,6 @@ func (m *nfdMaster) updateMasterNode() error {
|
||||||
func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Features) (Labels, ExtendedResources) {
|
func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Features) (Labels, ExtendedResources) {
|
||||||
outLabels := Labels{}
|
outLabels := Labels{}
|
||||||
for name, value := range labels {
|
for name, value := range labels {
|
||||||
// Add possibly missing default ns
|
|
||||||
name := addNs(name, nfdv1alpha1.FeatureLabelNs)
|
|
||||||
|
|
||||||
if value, err := m.filterFeatureLabel(name, value, features); err != nil {
|
if value, err := m.filterFeatureLabel(name, value, features); err != nil {
|
||||||
klog.ErrorS(err, "ignoring label", "labelKey", name, "labelValue", value)
|
klog.ErrorS(err, "ignoring label", "labelKey", name, "labelValue", value)
|
||||||
nodeLabelsRejected.Inc()
|
nodeLabelsRejected.Inc()
|
||||||
|
@ -537,8 +534,7 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Fea
|
||||||
// Remove labels which are intended to be extended resources
|
// Remove labels which are intended to be extended resources
|
||||||
extendedResources := ExtendedResources{}
|
extendedResources := ExtendedResources{}
|
||||||
for extendedResourceName := range m.config.ResourceLabels {
|
for extendedResourceName := range m.config.ResourceLabels {
|
||||||
// Add possibly missing default ns
|
extendedResourceName := addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
||||||
extendedResourceName = addNs(extendedResourceName, nfdv1alpha1.FeatureLabelNs)
|
|
||||||
if value, ok := outLabels[extendedResourceName]; ok {
|
if value, ok := outLabels[extendedResourceName]; ok {
|
||||||
if _, err := strconv.Atoi(value); err != nil {
|
if _, err := strconv.Atoi(value); err != nil {
|
||||||
klog.ErrorS(err, "bad label value encountered for extended resource", "labelKey", extendedResourceName, "labelValue", value)
|
klog.ErrorS(err, "bad label value encountered for extended resource", "labelKey", extendedResourceName, "labelValue", value)
|
||||||
|
@ -563,6 +559,9 @@ func (m *nfdMaster) filterFeatureLabel(name, value string, features *nfdv1alpha1
|
||||||
|
|
||||||
// Check label namespace, filter out if ns is not whitelisted
|
// Check label namespace, filter out if ns is not whitelisted
|
||||||
ns, base := splitNs(name)
|
ns, base := splitNs(name)
|
||||||
|
if ns == "" {
|
||||||
|
return "", fmt.Errorf("labels without namespace (prefix/) not allowed")
|
||||||
|
}
|
||||||
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
||||||
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
!strings.HasSuffix(ns, nfdv1alpha1.FeatureLabelSubNsSuffix) && !strings.HasSuffix(ns, nfdv1alpha1.ProfileLabelSubNsSuffix) {
|
||||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||||
|
@ -764,8 +763,11 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
||||||
// NOTE: changing the rule api to support handle multiple objects instead
|
// NOTE: changing the rule api to support handle multiple objects instead
|
||||||
// of merging would probably perform better with lot less data to copy.
|
// of merging would probably perform better with lot less data to copy.
|
||||||
features = objs[0].Spec.DeepCopy()
|
features = objs[0].Spec.DeepCopy()
|
||||||
|
features.Labels = addNsToMapKeys(features.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
for _, o := range objs[1:] {
|
for _, o := range objs[1:] {
|
||||||
o.Spec.MergeInto(features)
|
s := o.Spec.DeepCopy()
|
||||||
|
s.Labels = addNsToMapKeys(s.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
|
s.MergeInto(features)
|
||||||
}
|
}
|
||||||
|
|
||||||
klog.V(4).InfoS("merged nodeFeatureSpecs", "newNodeFeatureSpec", utils.DelayedDumper(features))
|
klog.V(4).InfoS("merged nodeFeatureSpecs", "newNodeFeatureSpec", utils.DelayedDumper(features))
|
||||||
|
@ -790,9 +792,6 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
||||||
func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources {
|
func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources {
|
||||||
outExtendedResources := ExtendedResources{}
|
outExtendedResources := ExtendedResources{}
|
||||||
for name, value := range extendedResources {
|
for name, value := range extendedResources {
|
||||||
// Add possibly missing default ns
|
|
||||||
name = addNs(name, nfdv1alpha1.ExtendedResourceNs)
|
|
||||||
|
|
||||||
capacity, err := filterExtendedResource(name, value, features)
|
capacity, err := filterExtendedResource(name, value, features)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
klog.ErrorS(err, "failed to create extended resources", "extendedResourceName", name, "extendedResourceValue", value)
|
klog.ErrorS(err, "failed to create extended resources", "extendedResourceName", name, "extendedResourceValue", value)
|
||||||
|
@ -807,6 +806,9 @@ func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources E
|
||||||
func filterExtendedResource(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
func filterExtendedResource(name, value string, features *nfdv1alpha1.Features) (string, error) {
|
||||||
// Check if given NS is allowed
|
// Check if given NS is allowed
|
||||||
ns, _ := splitNs(name)
|
ns, _ := splitNs(name)
|
||||||
|
if ns == "" {
|
||||||
|
return "", fmt.Errorf("extended resource without namespace (prefix/) not allowed")
|
||||||
|
}
|
||||||
if ns != nfdv1alpha1.ExtendedResourceNs && !strings.HasSuffix(ns, nfdv1alpha1.ExtendedResourceSubNsSuffix) {
|
if ns != nfdv1alpha1.ExtendedResourceNs && !strings.HasSuffix(ns, nfdv1alpha1.ExtendedResourceSubNsSuffix) {
|
||||||
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
if ns == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||||
return "", fmt.Errorf("namespace %q is not allowed", ns)
|
return "", fmt.Errorf("namespace %q is not allowed", ns)
|
||||||
|
@ -834,9 +836,7 @@ func filterExtendedResource(name, value string, features *nfdv1alpha1.Features)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
|
func (m *nfdMaster) refreshNodeFeatures(cli *kubernetes.Clientset, nodeName string, labels map[string]string, features *nfdv1alpha1.Features) error {
|
||||||
if labels == nil {
|
labels = addNsToMapKeys(labels, nfdv1alpha1.FeatureLabelNs)
|
||||||
labels = make(map[string]string)
|
|
||||||
}
|
|
||||||
|
|
||||||
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
|
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
|
||||||
|
|
||||||
|
@ -1011,13 +1011,13 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
taints = append(taints, ruleOut.Taints...)
|
taints = append(taints, ruleOut.Taints...)
|
||||||
for k, v := range ruleOut.Labels {
|
for k, v := range addNsToMapKeys(ruleOut.Labels, nfdv1alpha1.FeatureLabelNs) {
|
||||||
labels[k] = v
|
labels[k] = v
|
||||||
}
|
}
|
||||||
for k, v := range ruleOut.ExtendedResources {
|
for k, v := range addNsToMapKeys(ruleOut.ExtendedResources, nfdv1alpha1.ExtendedResourceNs) {
|
||||||
extendedResources[k] = v
|
extendedResources[k] = v
|
||||||
}
|
}
|
||||||
for k, v := range ruleOut.Annotations {
|
for k, v := range addNsToMapKeys(ruleOut.Annotations, nfdv1alpha1.FeatureAnnotationNs) {
|
||||||
annotations[k] = v
|
annotations[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1285,6 +1285,25 @@ func (m *nfdMaster) configure(filepath string, overrides string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// addNsToMapKeys creates a copy of a map with the namespace (prefix) added to
|
||||||
|
// unprefixed keys. Prefixed keys in the input map will take presedence, i.e.
|
||||||
|
// if the input contains both prefixed (say "prefix/name") and unprefixed
|
||||||
|
// ("name") name the unprefixed key will be ignored.
|
||||||
|
func addNsToMapKeys(in map[string]string, nsToAdd string) map[string]string {
|
||||||
|
out := make(map[string]string, len(in))
|
||||||
|
for k, v := range in {
|
||||||
|
if strings.Contains(k, "/") {
|
||||||
|
out[k] = v
|
||||||
|
} else {
|
||||||
|
fqn := path.Join(nsToAdd, k)
|
||||||
|
if _, ok := in[fqn]; !ok {
|
||||||
|
out[fqn] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
// addNs adds a namespace if one isn't already found from src string
|
// addNs adds a namespace if one isn't already found from src string
|
||||||
func addNs(src string, nsToAdd string) string {
|
func addNs(src string, nsToAdd string) string {
|
||||||
if strings.Contains(src, "/") {
|
if strings.Contains(src, "/") {
|
||||||
|
@ -1404,11 +1423,11 @@ func (m *nfdMaster) filterFeatureAnnotations(annotations map[string]string) map[
|
||||||
outAnnotations := make(map[string]string)
|
outAnnotations := make(map[string]string)
|
||||||
|
|
||||||
for annotation, value := range annotations {
|
for annotation, value := range annotations {
|
||||||
// Add possibly missing default ns
|
|
||||||
annotation := addNs(annotation, nfdv1alpha1.FeatureAnnotationNs)
|
|
||||||
|
|
||||||
ns, _ := splitNs(annotation)
|
ns, _ := splitNs(annotation)
|
||||||
|
if ns == "" {
|
||||||
|
klog.ErrorS(fmt.Errorf("annotations without namespace (prefix/) not allowed"), fmt.Sprintf("Ignoring annotation %s", annotation))
|
||||||
|
continue
|
||||||
|
}
|
||||||
// Check annotation namespace, filter out if ns is not whitelisted
|
// Check annotation namespace, filter out if ns is not whitelisted
|
||||||
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
||||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||||
|
|
|
@ -25,7 +25,9 @@ spec:
|
||||||
attr_2: "9"
|
attr_2: "9"
|
||||||
# Labels to be created
|
# Labels to be created
|
||||||
labels:
|
labels:
|
||||||
e2e-nodefeature-test-1: "obj-1"
|
e2e-nodefeature-test-1: "foo"
|
||||||
|
# The prefixed name should take precedence over the non-prefixed name above
|
||||||
|
feature.node.kubernetes.io/e2e-nodefeature-test-1: "obj-1"
|
||||||
e2e-nodefeature-test-2: "obj-1"
|
e2e-nodefeature-test-2: "obj-1"
|
||||||
# Override feature from nfd-worker
|
# Override feature from nfd-worker
|
||||||
fake-fakefeature3: "overridden"
|
fake-fakefeature3: "overridden"
|
||||||
|
|
Loading…
Add table
Reference in a new issue