1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2024-12-14 11:57:51 +00:00

nfd-master: Add status for NodeFeatureRule CRD

Signed-off-by: Oleg Zhurakivskyy <oleg.zhurakivskyy@intel.com>
This commit is contained in:
Oleg Zhurakivskyy 2024-10-04 14:37:47 +03:00
parent 0beafc6aa7
commit 28c40db0a6
8 changed files with 253 additions and 5 deletions

View file

@ -99,6 +99,18 @@ func (c *FakeNodeFeatureRules) Update(ctx context.Context, nodeFeatureRule *v1al
return obj.(*v1alpha1.NodeFeatureRule), err return obj.(*v1alpha1.NodeFeatureRule), err
} }
// UpdateStatus was generated because the type contains a Status member.
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
func (c *FakeNodeFeatureRules) UpdateStatus(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (result *v1alpha1.NodeFeatureRule, err error) {
emptyResult := &v1alpha1.NodeFeatureRule{}
obj, err := c.Fake.
Invokes(testing.NewRootUpdateSubresourceActionWithOptions(nodefeaturerulesResource, "status", nodeFeatureRule, opts), emptyResult)
if obj == nil {
return emptyResult, err
}
return obj.(*v1alpha1.NodeFeatureRule), err
}
// Delete takes name of the nodeFeatureRule and deletes it. Returns an error if one occurs. // Delete takes name of the nodeFeatureRule and deletes it. Returns an error if one occurs.
func (c *FakeNodeFeatureRules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error { func (c *FakeNodeFeatureRules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake. _, err := c.Fake.

View file

@ -39,6 +39,8 @@ type NodeFeatureRulesGetter interface {
type NodeFeatureRuleInterface interface { type NodeFeatureRuleInterface interface {
Create(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.CreateOptions) (*v1alpha1.NodeFeatureRule, error) Create(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.CreateOptions) (*v1alpha1.NodeFeatureRule, error)
Update(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureRule, error) Update(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureRule, error)
// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus().
UpdateStatus(ctx context.Context, nodeFeatureRule *v1alpha1.NodeFeatureRule, opts v1.UpdateOptions) (*v1alpha1.NodeFeatureRule, error)
Delete(ctx context.Context, name string, opts v1.DeleteOptions) error Delete(ctx context.Context, name string, opts v1.DeleteOptions) error
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) error
Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeFeatureRule, error) Get(ctx context.Context, name string, opts v1.GetOptions) (*v1alpha1.NodeFeatureRule, error)

View file

@ -122,6 +122,7 @@ type NodeFeatureRuleList struct {
// customization of node objects, such as node labeling. // customization of node objects, such as node labeling.
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,shortName=nfr // +kubebuilder:resource:scope=Cluster,shortName=nfr
// +kubebuilder:subresource:status
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient // +genclient
// +genclient:nonNamespaced // +genclient:nonNamespaced
@ -131,6 +132,8 @@ type NodeFeatureRule struct {
// Spec defines the rules to be evaluated. // Spec defines the rules to be evaluated.
Spec NodeFeatureRuleSpec `json:"spec"` Spec NodeFeatureRuleSpec `json:"spec"`
// +optional
Status NodeFeatureRuleStatus `json:"status,omitempty"`
} }
// NodeFeatureRuleSpec describes a NodeFeatureRule. // NodeFeatureRuleSpec describes a NodeFeatureRule.
@ -139,6 +142,28 @@ type NodeFeatureRuleSpec struct {
Rules []Rule `json:"rules"` Rules []Rule `json:"rules"`
} }
// NodeFeatureRuleStatus represents the status of a NodeFeatureRule
type NodeFeatureRuleStatus struct {
// +optional
Rules []RuleStatus `json:"rules,omitempty"`
}
// RuleStatus contains information on matched rules and nodes
type RuleStatus struct {
Name string `json:"name"`
MatchedNodes []string `json:"matchedNodes"`
}
// NodeFeatureRuleStatusList contains a list of NodeFeatureRuleStatus objects.
// +kubebuilder:object:root=true
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
type NodeFeatureRuleStatusList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata"`
Items []NodeFeatureRuleStatus `json:"items"`
}
// NodeFeatureGroup resource holds Node pools by featureGroup // NodeFeatureGroup resource holds Node pools by featureGroup
// +kubebuilder:object:root=true // +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced,shortName=nfg // +kubebuilder:resource:scope=Namespaced,shortName=nfg

View file

@ -544,6 +544,7 @@ func (in *NodeFeatureRule) DeepCopyInto(out *NodeFeatureRule) {
out.TypeMeta = in.TypeMeta out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec) in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return return
} }
@ -621,6 +622,62 @@ func (in *NodeFeatureRuleSpec) DeepCopy() *NodeFeatureRuleSpec {
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeFeatureRuleStatus) DeepCopyInto(out *NodeFeatureRuleStatus) {
*out = *in
if in.Rules != nil {
in, out := &in.Rules, &out.Rules
*out = make([]RuleStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleStatus.
func (in *NodeFeatureRuleStatus) DeepCopy() *NodeFeatureRuleStatus {
if in == nil {
return nil
}
out := new(NodeFeatureRuleStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeFeatureRuleStatusList) DeepCopyInto(out *NodeFeatureRuleStatusList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]NodeFeatureRuleStatus, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NodeFeatureRuleStatusList.
func (in *NodeFeatureRuleStatusList) DeepCopy() *NodeFeatureRuleStatusList {
if in == nil {
return nil
}
out := new(NodeFeatureRuleStatusList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NodeFeatureRuleStatusList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NodeFeatureSpec) DeepCopyInto(out *NodeFeatureSpec) { func (in *NodeFeatureSpec) DeepCopyInto(out *NodeFeatureSpec) {
*out = *in *out = *in
@ -709,3 +766,24 @@ func (in *Rule) DeepCopy() *Rule {
in.DeepCopyInto(out) in.DeepCopyInto(out)
return out return out
} }
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *RuleStatus) DeepCopyInto(out *RuleStatus) {
*out = *in
if in.MatchedNodes != nil {
in, out := &in.MatchedNodes, &out.MatchedNodes
*out = make([]string, len(*in))
copy(*out, *in)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RuleStatus.
func (in *RuleStatus) DeepCopy() *RuleStatus {
if in == nil {
return nil
}
out := new(RuleStatus)
in.DeepCopyInto(out)
return out
}

View file

@ -703,8 +703,30 @@ spec:
required: required:
- rules - rules
type: object type: object
status:
description: NodeFeatureRuleStatus represents the status of a NodeFeatureRule
properties:
rules:
items:
description: RuleStatus contains information on matched rules and
nodes
properties:
matchedNodes:
items:
type: string
type: array
name:
type: string
required:
- matchedNodes
- name
type: object
type: array
type: object
required: required:
- spec - spec
type: object type: object
served: true served: true
storage: true storage: true
subresources:
status: {}

View file

@ -703,8 +703,30 @@ spec:
required: required:
- rules - rules
type: object type: object
status:
description: NodeFeatureRuleStatus represents the status of a NodeFeatureRule
properties:
rules:
items:
description: RuleStatus contains information on matched rules and
nodes
properties:
matchedNodes:
items:
type: string
type: array
name:
type: string
required:
- matchedNodes
- name
type: object
type: array
type: object
required: required:
- spec - spec
type: object type: object
served: true served: true
storage: true storage: true
subresources:
status: {}

View file

@ -39,6 +39,7 @@ type RuleOutput struct {
Annotations map[string]string Annotations map[string]string
Vars map[string]string Vars map[string]string
Taints []corev1.Taint Taints []corev1.Taint
Matched bool
} }
// Execute the rule against a set of input features. // Execute the rule against a set of input features.
@ -103,6 +104,7 @@ func Execute(r *nfdv1alpha1.Rule, features *nfdv1alpha1.Features) (RuleOutput, e
Annotations: maps.Clone(r.Annotations), Annotations: maps.Clone(r.Annotations),
ExtendedResources: maps.Clone(r.ExtendedResources), ExtendedResources: maps.Clone(r.ExtendedResources),
Taints: slices.Clone(r.Taints), Taints: slices.Clone(r.Taints),
Matched: true,
} }
klog.V(2).InfoS("rule matched", "ruleName", r.Name, "ruleOutput", utils.DelayedDumper(ret)) klog.V(2).InfoS("rule matched", "ruleName", r.Name, "ruleOutput", utils.DelayedDumper(ret))
return ret, nil return ret, nil

View file

@ -634,6 +634,12 @@ func (m *nfdMaster) nfdAPIUpdateAllNodes() error {
m.updaterPool.addNode(node.Name) m.updaterPool.addNode(node.Name)
} }
err = m.updateRuleStatus()
if err != nil {
klog.ErrorS(err, "failed to update rule status")
return err
}
return nil return nil
} }
@ -878,7 +884,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli k8sclient.Interface, node *corev1.No
labels = make(map[string]string) labels = make(map[string]string)
} }
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(node.Name, features) crLabels, crAnnotations, crExtendedResources, crTaints, _ := m.processNodeFeatureRule(node.Name, features)
// Labels // Labels
maps.Copy(labels, crLabels) maps.Copy(labels, crLabels)
@ -989,9 +995,9 @@ func (m *nfdMaster) setTaints(cli k8sclient.Interface, taints []corev1.Taint, no
return nil return nil
} }
func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha1.Features) (Labels, Annotations, ExtendedResources, []corev1.Taint) { func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha1.Features) (Labels, Annotations, ExtendedResources, []corev1.Taint, []*nfdv1alpha1.NodeFeatureRule) {
if m.nfdController == nil { if m.nfdController == nil {
return nil, nil, nil, nil return nil, nil, nil, nil, nil
} }
extendedResources := ExtendedResources{} extendedResources := ExtendedResources{}
@ -1005,12 +1011,13 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
if err != nil { if err != nil {
klog.ErrorS(err, "failed to list NodeFeatureRule resources") klog.ErrorS(err, "failed to list NodeFeatureRule resources")
return nil, nil, nil, nil return nil, nil, nil, nil, nil
} }
// Process all rule CRs // Process all rule CRs
processStart := time.Now() processStart := time.Now()
for _, spec := range ruleSpecs { for _, spec := range ruleSpecs {
spec.Status.Rules = []nfdv1alpha1.RuleStatus{}
t := time.Now() t := time.Now()
switch { switch {
case klog.V(3).Enabled(): case klog.V(3).Enabled():
@ -1042,13 +1049,91 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
// Feed back rule output to features map for subsequent rules to match // Feed back rule output to features map for subsequent rules to match
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Labels)
features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars) features.InsertAttributeFeatures(nfdv1alpha1.RuleBackrefDomain, nfdv1alpha1.RuleBackrefFeature, ruleOut.Vars)
if ruleOut.Matched {
r := nfdv1alpha1.RuleStatus{
Name: rule.Name,
MatchedNodes: []string{
nodeName,
},
}
spec.Status.Rules = append(spec.Status.Rules, r)
}
} }
nfrProcessingTime.WithLabelValues(spec.Name, nodeName).Observe(time.Since(t).Seconds()) nfrProcessingTime.WithLabelValues(spec.Name, nodeName).Observe(time.Since(t).Seconds())
} }
processingTime := time.Since(processStart) processingTime := time.Since(processStart)
klog.V(2).InfoS("processed NodeFeatureRule objects", "nodeName", nodeName, "objectCount", len(ruleSpecs), "duration", processingTime) klog.V(2).InfoS("processed NodeFeatureRule objects", "nodeName", nodeName, "objectCount", len(ruleSpecs), "duration", processingTime)
return labels, annotations, extendedResources, taints return labels, annotations, extendedResources, taints, ruleSpecs
}
func findRuleByName(ruleSpecs []*nfdv1alpha1.NodeFeatureRule, name string) *nfdv1alpha1.NodeFeatureRule {
var spec *nfdv1alpha1.NodeFeatureRule
for _, r := range ruleSpecs {
if r.Name == name {
spec = r
break
}
}
return spec
}
func findStatusRuleByName(status *[]nfdv1alpha1.RuleStatus, name string) *nfdv1alpha1.RuleStatus {
var rule *nfdv1alpha1.RuleStatus
for _, r := range *status {
if r.Name == name {
rule = &r
break
}
}
return rule
}
func (m *nfdMaster) updateRuleStatus() error {
nodes, err := getNodes(m.k8sClient)
if err != nil {
return err
}
var ruleSpecs []*nfdv1alpha1.NodeFeatureRule
var outSpecs []*nfdv1alpha1.NodeFeatureRule
for _, node := range nodes.Items {
nodeFeatures, err := m.getAndMergeNodeFeatures(node.Name)
if err != nil {
return fmt.Errorf("failed to merge NodeFeature objects for node %q: %w", node.Name, err)
}
_, _, _, _, ruleSpecs = m.processNodeFeatureRule(node.Name, &nodeFeatures.Spec.Features)
}
for _, spec := range ruleSpecs {
if len(spec.Status.Rules) > 0 {
s := findRuleByName(outSpecs, spec.Name)
if s != nil {
for _, r := range spec.Status.Rules {
s.Status.Rules = append(s.Status.Rules, r)
sr := findStatusRuleByName(&s.Status.Rules, r.Name)
if sr != nil {
sr.MatchedNodes = append(sr.MatchedNodes, r.MatchedNodes...)
}
}
} else {
outSpecs = append(outSpecs, spec)
}
}
}
for _, spec := range outSpecs {
_, err = m.nfdClient.NfdV1alpha1().NodeFeatureRules().Update(context.TODO(), spec, metav1.UpdateOptions{})
if err != nil {
klog.ErrorS(err, "failed to update rule status", "nodefeaturerule", klog.KObj(spec))
}
}
return nil
} }
// updateNodeObject ensures the Kubernetes node object is up to date, // updateNodeObject ensures the Kubernetes node object is up to date,