1
0
Fork 0
mirror of https://github.com/kubernetes-sigs/node-feature-discovery.git synced 2025-03-31 04:04:51 +00:00
This commit is contained in:
Oleg Zhurakivskyy 2025-03-28 05:12:19 -07:00 committed by GitHub
commit 57fcfba861
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 241 additions and 5 deletions

View file

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

View file

@ -111,6 +111,7 @@ type NodeFeatureRuleList struct {
// customization of node objects, such as node labeling.
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster,shortName=nfr
// +kubebuilder:subresource:status
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// +genclient
// +genclient:nonNamespaced
@ -120,6 +121,8 @@ type NodeFeatureRule struct {
// Spec defines the rules to be evaluated.
Spec NodeFeatureRuleSpec `json:"spec"`
// +optional
Status NodeFeatureRuleStatus `json:"status,omitempty"`
}
// NodeFeatureRuleSpec describes a NodeFeatureRule.
@ -128,6 +131,28 @@ type NodeFeatureRuleSpec struct {
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
// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Namespaced,shortName=nfg

View file

@ -551,6 +551,7 @@ func (in *NodeFeatureRule) DeepCopyInto(out *NodeFeatureRule) {
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
return
}
@ -628,6 +629,62 @@ func (in *NodeFeatureRuleSpec) DeepCopy() *NodeFeatureRuleSpec {
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.
func (in *NodeFeatureSpec) DeepCopyInto(out *NodeFeatureSpec) {
*out = *in
@ -716,3 +773,24 @@ func (in *Rule) DeepCopy() *Rule {
in.DeepCopyInto(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

@ -717,8 +717,30 @@ spec:
required:
- rules
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:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

@ -717,8 +717,30 @@ spec:
required:
- rules
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:
- spec
type: object
served: true
storage: true
subresources:
status: {}

View file

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

View file

@ -575,6 +575,12 @@ func (m *nfdMaster) nfdAPIUpdateAllNodes() error {
m.updaterPool.addNode(node.Name)
}
err = m.updateRuleStatus()
if err != nil {
klog.ErrorS(err, "failed to update rule status")
return err
}
return nil
}
@ -821,7 +827,7 @@ func (m *nfdMaster) refreshNodeFeatures(cli k8sclient.Interface, node *corev1.No
labels = make(map[string]string)
}
crLabels, crAnnotations, crExtendedResources, crTaints := m.processNodeFeatureRule(node.Name, features)
crLabels, crAnnotations, crExtendedResources, crTaints, _ := m.processNodeFeatureRule(node.Name, features)
// Labels
maps.Copy(labels, crLabels)
@ -932,9 +938,9 @@ func (m *nfdMaster) setTaints(cli k8sclient.Interface, taints []corev1.Taint, no
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 {
return nil, nil, nil, nil
return nil, nil, nil, nil, nil
}
extendedResources := ExtendedResources{}
@ -948,12 +954,13 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
if err != nil {
klog.ErrorS(err, "failed to list NodeFeatureRule resources")
return nil, nil, nil, nil
return nil, nil, nil, nil, nil
}
// Process all rule CRs
processStart := time.Now()
for _, spec := range ruleSpecs {
spec.Status.Rules = []nfdv1alpha1.RuleStatus{}
t := time.Now()
switch {
case klog.V(3).Enabled():
@ -985,13 +992,91 @@ func (m *nfdMaster) processNodeFeatureRule(nodeName string, features *nfdv1alpha
// 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.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())
}
processingTime := time.Since(processStart)
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,