@@ -2,11 +2,16 @@ package deschedule
22
33import  (
44	"context" 
5+ 	"errors" 
56	"fmt" 
7+ 	"reflect" 
8+ 	"strings" 
69	"testing" 
710	"time" 
811
9- 	"k8s.io/klog/v2" 
12+ 	"k8s.io/apimachinery/pkg/runtime" 
13+ 	"k8s.io/client-go/kubernetes/typed/core/v1/fake" 
14+ 	k8stesting "k8s.io/client-go/testing" 
1015
1116	"github.com/intel/platform-aware-scheduling/telemetry-aware-scheduling/pkg/cache" 
1217	"github.com/intel/platform-aware-scheduling/telemetry-aware-scheduling/pkg/metrics" 
@@ -19,81 +24,249 @@ import (
1924	_ "k8s.io/client-go/plugin/pkg/client/auth" 
2025)
2126
27+ var  errMockTest  =  errors .New ("error when calling list" )
28+ 
29+ type  expected  struct  {
30+ 	nodes         map [string ]map [string ]string  // node name: string -> labels: map[string]string 
31+ 	labeledNodes  map [string ]map [string ]string  // node name: string -> labels: map[string]string 
32+ }
33+ 
34+ type  CacheMetric  struct  {
35+ 	metricName   string 
36+ 	metricValue  int64 
37+ }
38+ 
39+ func  assertViolatingNodes (t  * testing.T , nodeList  * v1.NodeList , wantNodes  map [string ]map [string ]string ) {
40+ 	t .Helper ()
41+ 
42+ 	nodes  :=  nodeList .Items 
43+ 	// check lengths are equal 
44+ 	if  len (nodes ) !=  len (wantNodes ) {
45+ 		t .Errorf ("Number of violating nodes returned: %v not as expected: %v" , len (nodes ), len (wantNodes ))
46+ 	}
47+ 
48+ 	// check if the nodes are similar 
49+ 	for  _ , node  :=  range  nodes  {
50+ 		currentNodeName  :=  node .Name 
51+ 		currentNodeLabels  :=  node .Labels 
52+ 
53+ 		if  wantNodes [currentNodeName ] ==  nil  {
54+ 			t .Errorf ("Expected to find node %s in list of expected nodes, but wasn't there." , currentNodeName )
55+ 		}
56+ 
57+ 		expectedNodeLabels  :=  wantNodes [node .Name ]
58+ 		if  ! reflect .DeepEqual (expectedNodeLabels , currentNodeLabels ) {
59+ 			t .Errorf ("Labels for node were different, expected %v got: %v" , expectedNodeLabels , currentNodeLabels )
60+ 		}
61+ 	}
62+ }
63+ 
2264func  TestDescheduleStrategy_Enforce (t  * testing.T ) {
2365	type  args  struct  {
2466		enforcer  * strategy.MetricEnforcer 
2567		cache     cache.ReaderWriter 
2668	}
2769
28- 	type  expected  struct  {
29- 		nodeNames  []string 
30- 	}
70+ 	clientWithListNodeException  :=  testclient .NewSimpleClientset ()
71+ 	clientWithListNodeException .CoreV1 ().(* fake.FakeCoreV1 ).PrependReactor ("list" , "nodes" ,
72+ 		func (action  k8stesting.Action ) (handled  bool , ret  runtime.Object , err  error ) {
73+ 			return  true , & v1.NodeList {}, errMockTest 
74+ 		})
3175
3276	tests  :=  []struct  {
33- 		name     string 
34- 		d        * Strategy 
35- 		node     * v1.Node 
36- 		args     args 
37- 		wantErr  bool 
38- 		want     expected 
77+ 		name                 string 
78+ 		d                    * Strategy 
79+ 		nodes                []* v1.Node 
80+ 		args                 args 
81+ 		wantErr              bool 
82+ 		want                 expected 
83+ 		cacheMetrics         map [string ]CacheMetric  // node name: string -> metric : { metricName, metricValue } 
84+ 		wantErrMessageToken  string 
3985	}{
4086		{name : "node label test" ,
4187			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
4288				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1 },
4389				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
44- 			node : & v1.Node {ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "" }}},
90+ 			nodes : []* v1.Node {{ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "" , "node-1-label" : "test" }}},
91+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-2" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-2-label" : "test" }}},
92+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-3" , Labels : map [string ]string {"node-3-label" : "test" }}}},
93+ 			cacheMetrics : map [string ]CacheMetric {"node-2" : {"cpu" , 5 }, "node-3" : {"memory" , 100 }},
4594			args : args {enforcer : strategy .NewEnforcer (testclient .NewSimpleClientset ()),
4695				cache : cache .MockEmptySelfUpdatingCache ()},
47- 			want : expected {nodeNames : []string {"node-1" }}},
96+ 			want : expected {
97+ 				nodes : map [string ]map [string ]string {"node-1" : {"node-1-label" : "test" },
98+ 					"node-2" : {"deschedule-test" : "violating" , "node-2-label" : "test" },
99+ 					"node-3" : {"deschedule-test" : "violating" , "node-3-label" : "test" }},
100+ 				labeledNodes : map [string ]map [string ]string {"node-2" : {"deschedule-test" : "violating" , "node-2-label" : "test" },
101+ 					"node-3" : {"deschedule-test" : "violating" , "node-3-label" : "test" }},
102+ 			}},
48103		{name : "node unlabel test" ,
49104			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
50105				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1000 },
51106				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
52- 			node : & v1.Node {ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "violating" }}},
107+ 			nodes : []* v1.Node {{ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-1-label" : "test" }}},
108+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-2" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-2-label" : "test" }}},
109+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-3" , Labels : map [string ]string {"node-3-label" : "test" }}}},
110+ 			cacheMetrics : map [string ]CacheMetric {"node-2" : {"cpu" , 11 }, "node-3" : {"memory" , 100 }},
53111			args : args {enforcer : strategy .NewEnforcer (testclient .NewSimpleClientset ()),
54112				cache : cache .MockEmptySelfUpdatingCache ()},
55- 			want : expected {nodeNames : []string {}}},
113+ 			want : expected {
114+ 				nodes : map [string ]map [string ]string {"node-1" : {"node-1-label" : "test" },
115+ 					"node-2" : {"node-2-label" : "test" },
116+ 					"node-3" : {"node-3-label" : "test" }},
117+ 				labeledNodes : map [string ]map [string ]string {}}},
118+ 		{name : "list nodes with exception" ,
119+ 			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
120+ 				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1000 },
121+ 				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
122+ 			nodes : []* v1.Node {{ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-1-label" : "test" }}},
123+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-2" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-2-label" : "test" }}},
124+ 				{ObjectMeta : metav1.ObjectMeta {Name : "node-3" , Labels : map [string ]string {"node-3-label" : "test" }}}},
125+ 			cacheMetrics : map [string ]CacheMetric {"node-2" : {"cpu" , 11 }, "node-3" : {"memory" , 100 }},
126+ 			args : args {enforcer : strategy .NewEnforcer (clientWithListNodeException ),
127+ 				cache : cache .MockEmptySelfUpdatingCache ()},
128+ 			want :                expected {},
129+ 			wantErr :             true ,
130+ 			wantErrMessageToken : failNodeListEnforceMessage },
56131	}
57132	for  _ , tt  :=  range  tests  {
58133		tt  :=  tt 
59134
60- 		err  :=  tt .args .cache .WriteMetric ("memory" , metrics.NodeMetricsInfo {
61- 			"node-1" : {Timestamp : time .Now (), Window : 1 , Value : * resource .NewQuantity (100 , resource .DecimalSI )}})
62- 		if  err  !=  nil  {
63- 			t .Errorf ("Cannot write metric to mock cach for test: %v" , err )
135+ 		for  metricNodeName , metric  :=  range  tt .cacheMetrics  {
136+ 			err  :=  tt .args .cache .WriteMetric (metric .metricName , metrics.NodeMetricsInfo {
137+ 				metricNodeName : {Timestamp : time .Now (), Window : 1 , Value : * resource .NewQuantity (metric .metricValue , resource .DecimalSI )}})
138+ 			if  err  !=  nil  {
139+ 				t .Errorf ("Cannot write metric %s to mock cache for test: %v" , metricNodeName , err )
140+ 			}
64141		}
65142
66- 		_ , err  =  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().Create (context .TODO (), tt .node , metav1.CreateOptions {})
67- 		if  err  !=  nil  {
68- 			t .Errorf ("Cannot write metric to mock cach for test: %v" , err )
143+ 		// create nodes 
144+ 		for  _ , node  :=  range  tt .nodes  {
145+ 			_ , err  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().Create (context .TODO (), node , metav1.CreateOptions {})
146+ 			if  err  !=  nil  {
147+ 				t .Errorf ("Cannot create node %s : %v" , node .Name , err )
148+ 			}
69149		}
70150
71151		tt .args .enforcer .RegisterStrategyType (tt .d )
72152		tt .args .enforcer .AddStrategy (tt .d , tt .d .StrategyType ())
73153		t .Run (tt .name , func (t  * testing.T ) {
74- 			got  :=  []string {}
75154			if  _ , err  :=  tt .d .Enforce (tt .args .enforcer , tt .args .cache ); (err  !=  nil ) !=  tt .wantErr  {
76- 				t .Errorf ("Strategy.Enforce() error = %v, wantErr %v" , err , tt .wantErr )
155+ 				if  ! strings .Contains (err .Error (), tt .wantErrMessageToken ) {
156+ 					t .Errorf ("Expecting output to match wantErr %v, instead got %v" , tt .wantErrMessageToken , err )
157+ 
158+ 					return 
159+ 				}
160+ 				t .Errorf ("Unexpected exception while trying to call Enforce  %v" , err )
161+ 
162+ 				return 
77163			}
78164
79- 			labelledNodes , err  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {LabelSelector : "deschedule-test=violating" })
80- 			if  err  !=  nil  {
81- 				if  ! tt .wantErr  {
82- 					t .Errorf ("Strategy.Enforce() error = %v, wantErr %v" , err , tt .wantErr )
165+ 			if  ! tt .wantErr  {
166+ 				// violating nodes 
167+ 				labeledNodes , err  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {LabelSelector : "deschedule-test=violating" })
168+ 				if  err  !=  nil  &&  ! tt .wantErr  {
169+ 					t .Errorf ("Unexpected exception while trying to fetch violating nodes  %v" , err )
83170
84171					return 
85172				}
173+ 				assertViolatingNodes (t , labeledNodes , tt .want .labeledNodes )
174+ 				nodes , _  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {})
175+ 				assertViolatingNodes (t , nodes , tt .want .nodes )
176+ 			}
177+ 		})
178+ 	}
179+ }
180+ 
181+ func  TestDescheduleStrategy_Cleanup (t  * testing.T ) {
182+ 	type  args  struct  {
183+ 		enforcer  * strategy.MetricEnforcer 
184+ 		cache     cache.ReaderWriter 
185+ 	}
186+ 
187+ 	clientWithException  :=  testclient .NewSimpleClientset ()
188+ 	clientWithException .CoreV1 ().(* fake.FakeCoreV1 ).PrependReactor ("list" , "nodes" , func (action  k8stesting.Action ) (handled  bool , ret  runtime.Object , err  error ) {
189+ 		return  true , & v1.NodeList {}, errMockTest 
190+ 	})
191+ 
192+ 	tests  :=  []struct  {
193+ 		name                 string 
194+ 		d                    * Strategy 
195+ 		node                 * v1.Node 
196+ 		args                 args 
197+ 		wantErr              bool 
198+ 		wantErrMessageToken  string 
199+ 		want                 expected 
200+ 	}{
201+ 		{name : "node with violating label" ,
202+ 			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
203+ 				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1 },
204+ 				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
205+ 			node : & v1.Node {ObjectMeta : metav1.ObjectMeta {Name : "node-1" , Labels : map [string ]string {"deschedule-test" : "violating" , "node-1-label" : "test" }}},
206+ 			args : args {enforcer : strategy .NewEnforcer (testclient .NewSimpleClientset ()),
207+ 				cache : cache .MockEmptySelfUpdatingCache ()},
208+ 			want : expected {
209+ 				nodes :        map [string ]map [string ]string {"node-1" : {"node-1-label" : "test" }},
210+ 				labeledNodes : map [string ]map [string ]string {},
211+ 			}},
212+ 		{name : "node without violating label" ,
213+ 			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
214+ 				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1000 },
215+ 				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
216+ 			node : & v1.Node {ObjectMeta : metav1.ObjectMeta {Name : "node-2" , Labels : map [string ]string {"deschedule-test" : "" , "node-2-label" : "test" }}},
217+ 			args : args {enforcer : strategy .NewEnforcer (testclient .NewSimpleClientset ()),
218+ 				cache : cache .MockEmptySelfUpdatingCache ()},
219+ 			want : expected {
220+ 				nodes :        map [string ]map [string ]string {"node-2" : {"deschedule-test" : "" , "node-2-label" : "test" }},
221+ 				labeledNodes : map [string ]map [string ]string {},
222+ 			}},
223+ 		{name : "list nodes throws an error" ,
224+ 			d : & Strategy {PolicyName : "deschedule-test" , Rules : []telpol.TASPolicyRule {
225+ 				{Metricname : "memory" , Operator : "GreaterThan" , Target : 1000 },
226+ 				{Metricname : "cpu" , Operator : "LessThan" , Target : 10 }}},
227+ 			node : & v1.Node {ObjectMeta : metav1.ObjectMeta {Name : "node-2" , Labels : map [string ]string {"deschedule-test" : "" , "test" : "label" }}},
228+ 			args : args {enforcer : strategy .NewEnforcer (clientWithException ),
229+ 				cache : cache .MockEmptySelfUpdatingCache ()},
230+ 			wantErr :             true ,
231+ 			wantErrMessageToken : failNodeListCleanUpMessage ,
232+ 			want :                expected {}},
233+ 	}
234+ 
235+ 	for  _ , tt  :=  range  tests  {
236+ 		tt  :=  tt 
237+ 
238+ 		_ , err  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().Create (context .TODO (), tt .node , metav1.CreateOptions {})
239+ 		if  err  !=  nil  {
240+ 			t .Errorf ("Cannot write metric to mock cach for test: %v" , err )
241+ 		}
242+ 
243+ 		t .Run (tt .name , func (t  * testing.T ) {
244+ 			if  err  :=  tt .d .Cleanup (tt .args .enforcer , tt .d .PolicyName ); (err  !=  nil ) !=  tt .wantErr  {
245+ 				if  ! strings .Contains (fmt .Sprint (err .Error ()), tt .wantErrMessageToken ) {
246+ 					t .Errorf ("Expecting output to match wantErr %v, instead got %v" , tt .wantErrMessageToken , err )
247+ 
248+ 					return 
249+ 				}
250+ 				t .Errorf ("Strategy.Cleanup() unexpected error = %v, wantErr %v" , err , tt .wantErr )
86251
87252				return 
88253			}
89- 			for  _ , node  :=  range  labelledNodes .Items  {
90- 				got  =  append (got , node .Name )
91- 			}
92- 			nodys , _  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {})
93- 			msg  :=  fmt .Sprint (nodys .Items [0 ])
94- 			klog .InfoS (msg , "component" , "testing" )
95- 			if  len (tt .want .nodeNames ) !=  len (got ) {
96- 				t .Errorf ("Number of pods returned: %v not as expected: %v" , got , tt .want .nodeNames )
254+ 
255+ 			if  ! tt .wantErr  {
256+ 				labelledNodes , err  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {LabelSelector : "deschedule-test=violating" })
257+ 				if  err  !=  nil  {
258+ 					if  ! tt .wantErr  {
259+ 						t .Errorf ("Strategy.Enforce() error = %v, wantErr %v" , err , tt .wantErr )
260+ 
261+ 						return 
262+ 					}
263+ 					t .Errorf ("Unexpected error encountered while trying to filter for the deschedule-test=violating label..." )
264+ 
265+ 					return 
266+ 				}
267+ 				assertViolatingNodes (t , labelledNodes , tt .want .labeledNodes )
268+ 				nodes , _  :=  tt .args .enforcer .KubeClient .CoreV1 ().Nodes ().List (context .TODO (), metav1.ListOptions {})
269+ 				assertViolatingNodes (t , nodes , tt .want .nodes )
97270			}
98271		})
99272	}
0 commit comments