mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
nfd-master: predictable handling of unprefixed names
Make the handling of unprefixed names (of labels, annotations and extended resources) well-defined and predictable. Previously the resulting output was not predictable in case the same name was coming in both the unprefixed and prefixed form, say unprefixed "foo=bar" coming from one source (be it nfd-worker or NodeFeature(Rule)) and "feature.node.kubernetes.io/foo=baz" from a NodeFeature(Rule). Previously the output value was randomly either "bar" or "baz". This patch adds prefixes to all names early in the processing "pipeline", preventing random name clashes later on.
This commit is contained in:
parent
678d7e89cb
commit
dc5af8be04
3 changed files with 54 additions and 32 deletions
|
@ -422,20 +422,21 @@ func TestSetLabels(t *testing.T) {
|
|||
vendorFeatureLabel := "vendor." + nfdv1alpha1.FeatureLabelNs + "/feature-4"
|
||||
vendorProfileLabel := "vendor." + nfdv1alpha1.ProfileLabelNs + "/feature-5"
|
||||
mockLabels := map[string]string{
|
||||
"feature-1": "val-1",
|
||||
"valid.ns/feature-2": "val-2",
|
||||
"random.denied.ns/feature-3": "val-3",
|
||||
"kubernetes.io/feature-4": "val-4",
|
||||
"sub.ns.kubernetes.io/feature-5": "val-5",
|
||||
vendorFeatureLabel: "val-6",
|
||||
vendorProfileLabel: "val-7",
|
||||
"--invalid-name--": "valid-val",
|
||||
"valid-name": "--invalid-val--"}
|
||||
"feature-1": "val-0",
|
||||
"feature.node.kubernetes.io/feature-1": "val-1",
|
||||
"valid.ns/feature-2": "val-2",
|
||||
"random.denied.ns/feature-3": "val-3",
|
||||
"kubernetes.io/feature-4": "val-4",
|
||||
"sub.ns.kubernetes.io/feature-5": "val-5",
|
||||
vendorFeatureLabel: "val-6",
|
||||
vendorProfileLabel: "val-7",
|
||||
"--invalid-name--": "valid-val",
|
||||
"valid-name": "--invalid-val--"}
|
||||
expectedPatches := []apihelper.JsonPatch{
|
||||
apihelper.NewJsonPatch("add", "/metadata/annotations",
|
||||
instance+"."+nfdv1alpha1.FeatureLabelsAnnotation,
|
||||
"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", vendorFeatureLabel, mockLabels[vendorFeatureLabel]),
|
||||
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"]),
|
||||
}
|
||||
|
||||
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("GetNode", mockClient, workerName).Return(mockNode, nil)
|
||||
mockHelper.On("PatchNodeStatus", mockClient, mockNodeName, mock.MatchedBy(jsonPatchMatcher(expectedStatusPatches))).Return(nil)
|
||||
|
@ -503,7 +504,7 @@ func TestFilterLabels(t *testing.T) {
|
|||
mockMaster := newMockMaster(mockHelper)
|
||||
|
||||
Convey("When using dynamic values", t, func() {
|
||||
labelName := "testLabel"
|
||||
labelName := "ns/testLabel"
|
||||
labelValue := "@test.feature.LSM"
|
||||
features := nfdv1alpha1.Features{
|
||||
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) {
|
||||
outLabels := 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 {
|
||||
klog.ErrorS(err, "ignoring label", "labelKey", name, "labelValue", value)
|
||||
nodeLabelsRejected.Inc()
|
||||
|
@ -537,8 +534,7 @@ func (m *nfdMaster) filterFeatureLabels(labels Labels, features *nfdv1alpha1.Fea
|
|||
// Remove labels which are intended to be extended resources
|
||||
extendedResources := ExtendedResources{}
|
||||
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 _, err := strconv.Atoi(value); err != nil {
|
||||
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
|
||||
ns, base := splitNs(name)
|
||||
if ns == "" {
|
||||
return "", fmt.Errorf("labels without namespace (prefix/) not allowed")
|
||||
}
|
||||
if ns != nfdv1alpha1.FeatureLabelNs && ns != nfdv1alpha1.ProfileLabelNs &&
|
||||
!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
|
||||
|
@ -764,8 +763,11 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
|||
// NOTE: changing the rule api to support handle multiple objects instead
|
||||
// of merging would probably perform better with lot less data to copy.
|
||||
features = objs[0].Spec.DeepCopy()
|
||||
features.Labels = addNsToMapKeys(features.Labels, nfdv1alpha1.FeatureLabelNs)
|
||||
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))
|
||||
|
@ -790,9 +792,6 @@ func (m *nfdMaster) nfdAPIUpdateOneNode(nodeName string) error {
|
|||
func filterExtendedResources(features *nfdv1alpha1.Features, extendedResources ExtendedResources) ExtendedResources {
|
||||
outExtendedResources := ExtendedResources{}
|
||||
for name, value := range extendedResources {
|
||||
// Add possibly missing default ns
|
||||
name = addNs(name, nfdv1alpha1.ExtendedResourceNs)
|
||||
|
||||
capacity, err := filterExtendedResource(name, value, features)
|
||||
if err != nil {
|
||||
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) {
|
||||
// Check if given NS is allowed
|
||||
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 == "kubernetes.io" || strings.HasSuffix(ns, ".kubernetes.io") {
|
||||
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 {
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels = addNsToMapKeys(labels, nfdv1alpha1.FeatureLabelNs)
|
||||
|
||||
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(nodeName, features)
|
||||
|
||||
|
@ -1011,13 +1011,13 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
|
|||
continue
|
||||
}
|
||||
taints = append(taints, ruleOut.Taints...)
|
||||
for k, v := range ruleOut.Labels {
|
||||
for k, v := range addNsToMapKeys(ruleOut.Labels, nfdv1alpha1.FeatureLabelNs) {
|
||||
labels[k] = v
|
||||
}
|
||||
for k, v := range ruleOut.ExtendedResources {
|
||||
for k, v := range addNsToMapKeys(ruleOut.ExtendedResources, nfdv1alpha1.ExtendedResourceNs) {
|
||||
extendedResources[k] = v
|
||||
}
|
||||
for k, v := range ruleOut.Annotations {
|
||||
for k, v := range addNsToMapKeys(ruleOut.Annotations, nfdv1alpha1.FeatureAnnotationNs) {
|
||||
annotations[k] = v
|
||||
}
|
||||
|
||||
|
@ -1285,6 +1285,25 @@ func (m *nfdMaster) configure(filepath string, overrides string) error {
|
|||
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
|
||||
func addNs(src string, nsToAdd string) string {
|
||||
if strings.Contains(src, "/") {
|
||||
|
@ -1404,11 +1423,11 @@ func (m *nfdMaster) filterFeatureAnnotations(annotations map[string]string) map[
|
|||
outAnnotations := make(map[string]string)
|
||||
|
||||
for annotation, value := range annotations {
|
||||
// Add possibly missing default ns
|
||||
annotation := addNs(annotation, nfdv1alpha1.FeatureAnnotationNs)
|
||||
|
||||
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
|
||||
if ns != nfdv1alpha1.FeatureAnnotationNs && !strings.HasSuffix(ns, nfdv1alpha1.FeatureAnnotationSubNsSuffix) {
|
||||
// If the namespace is denied, and not present in the extraLabelNs, label will be ignored
|
||||
|
|
|
@ -25,7 +25,9 @@ spec:
|
|||
attr_2: "9"
|
||||
# Labels to be created
|
||||
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"
|
||||
# Override feature from nfd-worker
|
||||
fake-fakefeature3: "overridden"
|
||||
|
|
Loading…
Reference in a new issue