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
}
// 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.
func (c *FakeNodeFeatureRules) Delete(ctx context.Context, name string, opts v1.DeleteOptions) error {
_, err := c.Fake.

View file

@ -39,6 +39,8 @@ type NodeFeatureRulesGetter interface {
type NodeFeatureRuleInterface interface {
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)
// 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
DeleteCollection(ctx context.Context, opts v1.DeleteOptions, listOpts v1.ListOptions) 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.
// +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
@ -131,6 +132,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.
@ -139,6 +142,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

@ -544,6 +544,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
}
@ -621,6 +622,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
@ -709,3 +766,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

@ -703,8 +703,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

@ -703,8 +703,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

@ -39,6 +39,7 @@ type RuleOutput struct {
Annotations map[string]string
Vars map[string]string
Taints []corev1.Taint
Matched bool
}
// 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),
ExtendedResources: maps.Clone(r.ExtendedResources),
Taints: slices.Clone(r.Taints),
Matched: true,
}
klog.V(2).InfoS("rule matched", "ruleName", r.Name, "ruleOutput", utils.DelayedDumper(ret))
return ret, nil

View file

@ -634,6 +634,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
}
@ -878,7 +884,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)
@ -989,9 +995,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{}
@ -1005,12 +1011,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():
@ -1042,13 +1049,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,