mirror of
https://github.com/kubernetes-sigs/node-feature-discovery.git
synced 2025-03-15 04:57:56 +00:00
topology-gc: refactor unit tests
Remove a lot of boilerplate code by defining reusable functions. Also, test the Run() method instead of the functions callees of Run() as it is the top level functionality that was tested in practice (we don't have separate unit tests for the callee functions).
This commit is contained in:
parent
4674bce27d
commit
0b5e51bd35
1 changed files with 87 additions and 169 deletions
|
@ -22,10 +22,14 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha2"
|
"github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/apis/topology/v1alpha2"
|
||||||
|
topologyclientset "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned"
|
||||||
faketopologyv1alpha2 "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned/fake"
|
faketopologyv1alpha2 "github.com/k8stopologyawareschedwg/noderesourcetopology-api/pkg/generated/clientset/versioned/fake"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
"k8s.io/client-go/informers"
|
"k8s.io/client-go/informers"
|
||||||
|
k8sclientset "k8s.io/client-go/kubernetes"
|
||||||
fakek8sclientset "k8s.io/client-go/kubernetes/fake"
|
fakek8sclientset "k8s.io/client-go/kubernetes/fake"
|
||||||
|
|
||||||
. "github.com/smartystreets/goconvey/convey"
|
. "github.com/smartystreets/goconvey/convey"
|
||||||
|
@ -33,177 +37,42 @@ import (
|
||||||
|
|
||||||
func TestNRTGC(t *testing.T) {
|
func TestNRTGC(t *testing.T) {
|
||||||
Convey("When theres is old NRT ", t, func() {
|
Convey("When theres is old NRT ", t, func() {
|
||||||
k8sClient := fakek8sclientset.NewSimpleClientset()
|
gc := newMockGC(nil, []string{"node1"})
|
||||||
|
|
||||||
fakeClient := faketopologyv1alpha2.NewSimpleClientset(&v1alpha2.NodeResourceTopology{
|
errChan := make(chan error, 1)
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
go func() { errChan <- gc.Run() }()
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
factory := informers.NewSharedInformerFactory(k8sClient, 5*time.Minute)
|
|
||||||
|
|
||||||
stopChan := make(chan struct{}, 1)
|
So(waitForNRT(gc.topoClient), ShouldBeTrue)
|
||||||
|
|
||||||
gc := &topologyGC{
|
|
||||||
factory: factory,
|
|
||||||
topoClient: fakeClient,
|
|
||||||
stopChan: stopChan,
|
|
||||||
gcPeriod: 10 * time.Minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := gc.startNodeInformer()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(nrts.Items, ShouldHaveLength, 0)
|
|
||||||
|
|
||||||
gc.Stop()
|
gc.Stop()
|
||||||
|
So(<-errChan, ShouldBeNil)
|
||||||
})
|
})
|
||||||
Convey("When theres is one old NRT and one up to date", t, func() {
|
Convey("When theres is one old NRT and one up to date", t, func() {
|
||||||
k8sClient := fakek8sclientset.NewSimpleClientset(&corev1.Node{
|
gc := newMockGC([]string{"node1"}, []string{"node1", "node2"})
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
fakeClient := faketopologyv1alpha2.NewSimpleClientset(&v1alpha2.NodeResourceTopology{
|
errChan := make(chan error, 1)
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
go func() { errChan <- gc.Run() }()
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&v1alpha2.NodeResourceTopology{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
stopChan := make(chan struct{}, 1)
|
So(waitForNRT(gc.topoClient, "node1"), ShouldBeTrue)
|
||||||
|
|
||||||
factory := informers.NewSharedInformerFactory(k8sClient, 5*time.Minute)
|
|
||||||
|
|
||||||
gc := &topologyGC{
|
|
||||||
factory: factory,
|
|
||||||
topoClient: fakeClient,
|
|
||||||
stopChan: stopChan,
|
|
||||||
gcPeriod: 10 * time.Minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := gc.startNodeInformer()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
So(nrts.Items, ShouldHaveLength, 1)
|
|
||||||
So(nrts.Items[0].GetName(), ShouldEqual, "node1")
|
|
||||||
|
|
||||||
|
gc.Stop()
|
||||||
|
So(<-errChan, ShouldBeNil)
|
||||||
})
|
})
|
||||||
Convey("Should react to delete event", t, func() {
|
Convey("Should react to delete event", t, func() {
|
||||||
k8sClient := fakek8sclientset.NewSimpleClientset(
|
gc := newMockGC([]string{"node1", "node2"}, []string{"node1", "node2"})
|
||||||
&corev1.Node{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&corev1.Node{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
fakeClient := faketopologyv1alpha2.NewSimpleClientset(
|
errChan := make(chan error, 1)
|
||||||
&v1alpha2.NodeResourceTopology{
|
go func() { errChan <- gc.Run() }()
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&v1alpha2.NodeResourceTopology{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
stopChan := make(chan struct{}, 1)
|
err := gc.k8sClient.CoreV1().Nodes().Delete(context.TODO(), "node1", metav1.DeleteOptions{})
|
||||||
|
|
||||||
factory := informers.NewSharedInformerFactory(k8sClient, 5*time.Minute)
|
|
||||||
gc := &topologyGC{
|
|
||||||
factory: factory,
|
|
||||||
topoClient: fakeClient,
|
|
||||||
stopChan: stopChan,
|
|
||||||
gcPeriod: 10 * time.Minute,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := gc.startNodeInformer()
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
So(waitForNRT(gc.topoClient, "node2"), ShouldBeTrue)
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
So(nrts.Items, ShouldHaveLength, 2)
|
|
||||||
|
|
||||||
err = k8sClient.CoreV1().Nodes().Delete(context.TODO(), "node1", metav1.DeleteOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
// simple sleep with retry loop to make sure indexer will pick up event and trigger deleteNode Function
|
|
||||||
deleted := false
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
if len(nrts.Items) == 1 {
|
|
||||||
deleted = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(time.Second)
|
|
||||||
}
|
|
||||||
So(deleted, ShouldBeTrue)
|
|
||||||
})
|
})
|
||||||
Convey("periodic GC should remove obsolete NRT", t, func() {
|
Convey("periodic GC should remove obsolete NRT", t, func() {
|
||||||
k8sClient := fakek8sclientset.NewSimpleClientset(
|
gc := newMockGC([]string{"node1", "node2"}, []string{"node1", "node2"})
|
||||||
&corev1.Node{
|
// Override period to run fast
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
gc.gcPeriod = 100 * time.Millisecond
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&corev1.Node{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
fakeClient := faketopologyv1alpha2.NewSimpleClientset(
|
|
||||||
&v1alpha2.NodeResourceTopology{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node1",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&v1alpha2.NodeResourceTopology{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: "node2",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
stopChan := make(chan struct{}, 1)
|
|
||||||
|
|
||||||
factory := informers.NewSharedInformerFactory(k8sClient, 5*time.Minute)
|
|
||||||
gc := &topologyGC{
|
|
||||||
factory: factory,
|
|
||||||
topoClient: fakeClient,
|
|
||||||
stopChan: stopChan,
|
|
||||||
gcPeriod: time.Second,
|
|
||||||
}
|
|
||||||
|
|
||||||
err := gc.startNodeInformer()
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
|
||||||
|
|
||||||
So(nrts.Items, ShouldHaveLength, 2)
|
|
||||||
|
|
||||||
nrt := v1alpha2.NodeResourceTopology{
|
nrt := v1alpha2.NodeResourceTopology{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
@ -211,23 +80,72 @@ func TestNRTGC(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
go gc.periodicGC(time.Second)
|
errChan := make(chan error, 1)
|
||||||
|
go func() { errChan <- gc.Run() }()
|
||||||
|
|
||||||
_, err = fakeClient.TopologyV1alpha2().NodeResourceTopologies().Create(context.TODO(), &nrt, metav1.CreateOptions{})
|
_, err := gc.topoClient.TopologyV1alpha2().NodeResourceTopologies().Create(context.TODO(), &nrt, metav1.CreateOptions{})
|
||||||
So(err, ShouldBeNil)
|
|
||||||
// simple sleep with retry loop to make sure GC was triggered
|
|
||||||
deleted := false
|
|
||||||
for i := 0; i < 5; i++ {
|
|
||||||
nrts, err := fakeClient.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
|
||||||
So(err, ShouldBeNil)
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
if len(nrts.Items) == 2 {
|
So(waitForNRT(gc.topoClient, "node1", "node2"), ShouldBeTrue)
|
||||||
deleted = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
time.Sleep(2 * time.Second)
|
|
||||||
}
|
|
||||||
So(deleted, ShouldBeTrue)
|
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMockGC(nodes, nrts []string) *mockGC {
|
||||||
|
k8sClient := fakek8sclientset.NewSimpleClientset(createFakeNodes(nodes...)...)
|
||||||
|
return &mockGC{
|
||||||
|
topologyGC: topologyGC{
|
||||||
|
factory: informers.NewSharedInformerFactory(k8sClient, 5*time.Minute),
|
||||||
|
topoClient: faketopologyv1alpha2.NewSimpleClientset(createFakeNRTs(nrts...)...),
|
||||||
|
stopChan: make(chan struct{}, 1),
|
||||||
|
gcPeriod: 10 * time.Minute,
|
||||||
|
},
|
||||||
|
k8sClient: k8sClient,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFakeNodes(names ...string) []runtime.Object {
|
||||||
|
nodes := make([]runtime.Object, len(names))
|
||||||
|
for i, n := range names {
|
||||||
|
nodes[i] = &corev1.Node{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: n,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
return nodes
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFakeNRTs(names ...string) []runtime.Object {
|
||||||
|
nrts := make([]runtime.Object, len(names))
|
||||||
|
for i, n := range names {
|
||||||
|
nrts[i] = &v1alpha2.NodeResourceTopology{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: n,
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
return nrts
|
||||||
|
}
|
||||||
|
|
||||||
|
type mockGC struct {
|
||||||
|
topologyGC
|
||||||
|
|
||||||
|
k8sClient k8sclientset.Interface
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitForNRT(cli topologyclientset.Interface, names ...string) bool {
|
||||||
|
nameSet := sets.NewString(names...)
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
nrts, err := cli.TopologyV1alpha2().NodeResourceTopologies().List(context.TODO(), metav1.ListOptions{})
|
||||||
|
So(err, ShouldBeNil)
|
||||||
|
|
||||||
|
nrtNames := sets.NewString()
|
||||||
|
for _, nrt := range nrts.Items {
|
||||||
|
nrtNames.Insert(nrt.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if nrtNames.Equal(nameSet) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue