mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2024-12-14 11:57:51 +00:00
nfd-gc: support garbage collection of NodeFeatures
Hook into the same logic already exercised for NodeResourceTopology objects: GC watches for node delete events and immediately drops stale objects (NRT and now also NF). In addition there is a periodic resync to catch any missed node deletes, once every hour by default.
This commit is contained in:
parent
01c08d67b6
commit
e3415ec484
2 changed files with 56 additions and 12 deletions
|
@ -33,6 +33,8 @@ import (
|
|||
"k8s.io/klog/v2"
|
||||
|
||||
"sigs.k8s.io/node-feature-discovery/pkg/apihelper"
|
||||
nfdv1alpha1 "sigs.k8s.io/node-feature-discovery/pkg/apis/nfd/v1alpha1"
|
||||
nfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned"
|
||||
)
|
||||
|
||||
// Args are the command line arguments
|
||||
|
@ -49,6 +51,7 @@ type NfdGarbageCollector interface {
|
|||
|
||||
type nfdGarbageCollector struct {
|
||||
stopChan chan struct{}
|
||||
nfdClient nfdclientset.Interface
|
||||
topoClient topologyclientset.Interface
|
||||
gcPeriod time.Duration
|
||||
factory informers.SharedInformerFactory
|
||||
|
@ -77,12 +80,26 @@ func newNfdGarbageCollector(config *restclient.Config, stop chan struct{}, gcPer
|
|||
|
||||
return &nfdGarbageCollector{
|
||||
topoClient: cli,
|
||||
nfdClient: nfdclientset.NewForConfigOrDie(config),
|
||||
stopChan: stop,
|
||||
gcPeriod: gcPeriod,
|
||||
factory: factory,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (n *nfdGarbageCollector) deleteNodeFeature(namespace, name string) {
|
||||
if err := n.nfdClient.NfdV1alpha1().NodeFeatures(namespace).Delete(context.TODO(), name, metav1.DeleteOptions{}); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(2).InfoS("NodeFeature not found, omitting deletion", "nodefeature", klog.KRef(namespace, name))
|
||||
return
|
||||
} else {
|
||||
klog.ErrorS(err, "failed to delete NodeFeature object", "nodefeature", klog.KRef(namespace, name))
|
||||
return
|
||||
}
|
||||
}
|
||||
klog.InfoS("NodeFeature object has been deleted", "nodefeature", klog.KRef(namespace, name))
|
||||
}
|
||||
|
||||
func (n *nfdGarbageCollector) deleteNRT(nodeName string) {
|
||||
if err := n.topoClient.TopologyV1alpha2().NodeResourceTopologies().Delete(context.TODO(), nodeName, metav1.DeleteOptions{}); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
|
@ -111,6 +128,16 @@ func (n *nfdGarbageCollector) deleteNodeHandler(object interface{}) {
|
|||
}
|
||||
|
||||
n.deleteNRT(node.GetName())
|
||||
|
||||
// Delete all NodeFeature objects (from all namespaces) targeting the deleted node
|
||||
nfListOptions := metav1.ListOptions{LabelSelector: nfdv1alpha1.NodeFeatureObjNodeNameLabel + "=" + node.GetName()}
|
||||
if nfs, err := n.nfdClient.NfdV1alpha1().NodeFeatures("").List(context.TODO(), nfListOptions); err != nil {
|
||||
klog.ErrorS(err, "failed to list NodeFeature objects")
|
||||
} else {
|
||||
for _, nf := range nfs.Items {
|
||||
n.deleteNodeFeature(nf.Namespace, nf.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// garbageCollect removes all stale API objects
|
||||
|
@ -126,20 +153,35 @@ func (n *nfdGarbageCollector) garbageCollect() {
|
|||
nodeNames.Insert(node.Name)
|
||||
}
|
||||
|
||||
nrts, err := n.topoClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to list NodeResourceTopology objects")
|
||||
return
|
||||
// Handle NodeFeature objects
|
||||
nfs, err := n.nfdClient.NfdV1alpha1().NodeFeatures("").List(context.TODO(), metav1.ListOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(2).InfoS("NodeFeature CRD does not exist")
|
||||
} else if err != nil {
|
||||
klog.ErrorS(err, "failed to list NodeFeature objects")
|
||||
} else {
|
||||
for _, nf := range nfs.Items {
|
||||
nodeName, ok := nf.GetLabels()[nfdv1alpha1.NodeFeatureObjNodeNameLabel]
|
||||
if !ok {
|
||||
klog.InfoS("node name label missing from NodeFeature object", "nodefeature", klog.KObj(&nf))
|
||||
}
|
||||
if !nodeNames.Has(nodeName) {
|
||||
n.deleteNodeFeature(nf.Namespace, nf.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle NodeResourceTopology objects
|
||||
nrts, err := n.topoClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
||||
if errors.IsNotFound(err) {
|
||||
klog.V(2).InfoS("NodeResourceTopology CRD does not exist")
|
||||
} else if err != nil {
|
||||
klog.ErrorS(err, "failed to list NodeResourceTopology objects")
|
||||
} else {
|
||||
for _, nrt := range nrts.Items {
|
||||
key, err := cache.MetaNamespaceKeyFunc(&nrt)
|
||||
if err != nil {
|
||||
klog.ErrorS(err, "failed to create key", "noderesourcetopology", klog.KObj(&nrt))
|
||||
continue
|
||||
if !nodeNames.Has(nrt.Name) {
|
||||
n.deleteNRT(nrt.Name)
|
||||
}
|
||||
if !nodeNames.Has(key) {
|
||||
n.deleteNRT(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import (
|
|||
"k8s.io/client-go/informers"
|
||||
k8sclientset "k8s.io/client-go/kubernetes"
|
||||
fakek8sclientset "k8s.io/client-go/kubernetes/fake"
|
||||
fakenfdclientset "sigs.k8s.io/node-feature-discovery/pkg/generated/clientset/versioned/fake"
|
||||
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
@ -95,6 +96,7 @@ func newMockGC(nodes, nrts []string) *mockGC {
|
|||
return &mockGC{
|
||||
nfdGarbageCollector: nfdGarbageCollector{
|
||||
factory: informers.NewSharedInformerFactory(k8sClient, 5*time.Minute),
|
||||
nfdClient: fakenfdclientset.NewSimpleClientset(),
|
||||
topoClient: faketopologyv1alpha2.NewSimpleClientset(createFakeNRTs(nrts...)...),
|
||||
stopChan: make(chan struct{}, 1),
|
||||
gcPeriod: 10 * time.Minute,
|
||||
|
|
Loading…
Reference in a new issue