From 33fdf75190c55279e9b1f8e402664b785dc6821e Mon Sep 17 00:00:00 2001 From: Markus Lehtonen Date: Tue, 15 Jun 2021 14:08:14 +0300 Subject: [PATCH] nfd-master: process labeling rules from CRs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable Custom Resource based label creation in nfd-master. This extends the previously implemented controller stub for watching NodeFeatureRule objects. NFD-master watches NodeFeatureRule objects in the cluster and processes the rules on every incoming labeling request from workers. The functionality relies on the "raw features" (identical to how nfd-worker handles custom rules) submitted by nfd-worker, making it independent of the label source configuration of the worker. This means that the labeling functions as expected even if all sources in the worker are disabled. NOTE: nfd-master is stateless and re-labeling only happens on the reception of SetLabelsRequest from the worker – i.e. on intervals specified by the core.sleepInterval configuration option (or -sleep-interval cmdline flag) of each nfd-worker instance. This means that modification/creation of NodeFeatureRule objects does not automatically update the node labels. Instead, the changes only come visible when workers send their labeling requests. --- pkg/nfd-master/nfd-master.go | 55 ++++++++++++++++++++++++++++++++++-- 1 file changed, 52 insertions(+), 3 deletions(-) diff --git a/pkg/nfd-master/nfd-master.go b/pkg/nfd-master/nfd-master.go index 7dba0d715..bffbcce2e 100644 --- a/pkg/nfd-master/nfd-master.go +++ b/pkg/nfd-master/nfd-master.go @@ -39,6 +39,7 @@ import ( api "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" restclient "k8s.io/client-go/rest" "k8s.io/klog/v2" @@ -179,6 +180,7 @@ func (m *nfdMaster) Run() error { if err != nil { return err } + klog.Info("starting nfd LabelRule controller") m.nfdController = newNfdController(kubeconfig) } @@ -408,15 +410,25 @@ func (m *nfdMaster) SetLabels(c context.Context, r *pb.SetLabelsRequest) (*pb.Se return &pb.SetLabelsReply{}, err } switch { - case klog.V(2).Enabled(): - utils.KlogDump(2, "REQUEST", " ", r) + case klog.V(4).Enabled(): + utils.KlogDump(3, "REQUEST", " ", r) case klog.V(1).Enabled(): klog.Infof("REQUEST Node: %q NFD-version: %q Labels: %s", r.NodeName, r.NfdVersion, r.Labels) default: klog.Infof("received labeling request for node %q", r.NodeName) } - labels, extendedResources := filterFeatureLabels(r.Labels, m.args.ExtraLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels) + // Mix in CR-originated labels + rawLabels := make(map[string]string) + if r.Labels != nil { + // NOTE: we effectively mangle the request struct by not creating a deep copy of the map + rawLabels = r.Labels + } + for k, v := range m.crLabels(r) { + rawLabels[k] = v + } + + labels, extendedResources := filterFeatureLabels(rawLabels, m.args.ExtraLabelNs, m.args.LabelWhiteList.Regexp, m.args.ResourceLabels) if !m.args.NoPublish { // Advertise NFD worker version as an annotation @@ -480,6 +492,43 @@ func (m *nfdMaster) UpdateNodeTopology(c context.Context, r *topologypb.NodeTopo return &topologypb.NodeTopologyResponse{}, nil } +func (m *nfdMaster) crLabels(r *pb.SetLabelsRequest) map[string]string { + if m.nfdController == nil { + return nil + } + + l := make(map[string]string) + ruleSpecs, err := m.nfdController.lister.List(labels.Everything()) + if err != nil { + klog.Errorf("failed to list LabelRule resources: %w", err) + return nil + } + + // Process all rule CRs + for _, spec := range ruleSpecs { + switch { + case klog.V(3).Enabled(): + h := fmt.Sprintf("executing LabelRule \"%s/%s\":", spec.ObjectMeta.Namespace, spec.ObjectMeta.Name) + utils.KlogDump(3, h, " ", spec.Spec) + case klog.V(1).Enabled(): + klog.Infof("executing LabelRule \"%s/%s\"", spec.ObjectMeta.Namespace, spec.ObjectMeta.Name) + } + for _, rule := range spec.Spec.Rules { + ruleOut, err := rule.Execute(r.Features) + if err != nil { + klog.Errorf("failed to process Rule %q: %w", rule.Name, err) + continue + } + for k, v := range ruleOut { + l[k] = v + } + utils.KlogDump(1, "", " ", ruleOut) + } + } + + return l +} + // updateNodeFeatures ensures the Kubernetes node object is up to date, // creating new labels and extended resources where necessary and removing // outdated ones. Also updates the corresponding annotations.