@@ -20,8 +20,367 @@ import (
2020 "testing"
2121
2222 v1 "k8s.io/api/core/v1"
23+
24+ "errors"
25+
26+ apps "k8s.io/api/apps/v1"
27+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28+ "k8s.io/apimachinery/pkg/runtime"
29+ "k8s.io/apimachinery/pkg/runtime/schema"
30+ "k8s.io/apimachinery/pkg/types"
31+ ctrl "sigs.k8s.io/controller-runtime"
32+ "sigs.k8s.io/controller-runtime/pkg/client"
2333)
2434
35+ type mockPlugin struct {
36+ client.Object
37+ }
38+
39+ func (m * mockPlugin ) GetNamespace () string {
40+ return "default"
41+ }
42+
43+ func (m * mockPlugin ) GetObjectKind () schema.ObjectKind {
44+ return & metav1.TypeMeta {
45+ Kind : "MockPlugin" ,
46+ APIVersion : "v1" ,
47+ }
48+ }
49+
50+ func (m * mockPlugin ) GetName () string {
51+ return "mock"
52+ }
53+
54+ func (m * mockPlugin ) GetUID () types.UID {
55+ return "mock-uid"
56+ }
57+
58+ type mockController struct {
59+ statusErr error
60+ updated bool
61+ upgrade bool
62+ }
63+
64+ func (m * mockController ) CreateEmptyObject () client.Object {
65+ return & mockPlugin {}
66+ }
67+
68+ func (m * mockController ) NewDaemonSet (rawObj client.Object ) * apps.DaemonSet {
69+ return & apps.DaemonSet {
70+ ObjectMeta : metav1.ObjectMeta {
71+ Name : "mock-ds" ,
72+ Namespace : "default" ,
73+ },
74+ Spec : apps.DaemonSetSpec {
75+ Selector : & metav1.LabelSelector {
76+ MatchLabels : map [string ]string {"app" : "mock" },
77+ },
78+ Template : v1.PodTemplateSpec {
79+ ObjectMeta : metav1.ObjectMeta {
80+ Labels : map [string ]string {"app" : "mock" },
81+ },
82+ Spec : v1.PodSpec {
83+ Containers : []v1.Container {
84+ {
85+ Name : "mock" ,
86+ Image : "intel/intel-mock-plugin:latest" ,
87+ },
88+ },
89+ },
90+ },
91+ },
92+ }
93+ }
94+
95+ func (m * mockController ) UpdateDaemonSet (rawObj client.Object , ds * apps.DaemonSet ) (updated bool ) {
96+ return m .updated
97+ }
98+
99+ func (m * mockController ) UpdateStatus (rawObj client.Object , ds * apps.DaemonSet , messages []string ) (updated bool , err error ) {
100+ if m .statusErr != nil {
101+ return false , m .statusErr
102+ }
103+
104+ return true , nil
105+ }
106+
107+ func (m * mockController ) Upgrade (ctx context.Context , obj client.Object ) bool {
108+ return m .upgrade
109+ }
110+
111+ type fakeStatusWriter struct {}
112+
113+ func (f * fakeStatusWriter ) Create (ctx context.Context , obj client.Object , obj2 client.Object , opts ... client.SubResourceCreateOption ) error {
114+ return nil
115+ }
116+ func (f * fakeStatusWriter ) Update (ctx context.Context , obj client.Object , opts ... client.SubResourceUpdateOption ) error {
117+ return nil
118+ }
119+ func (f * fakeStatusWriter ) Patch (ctx context.Context , obj client.Object , patch client.Patch , opts ... client.SubResourcePatchOption ) error {
120+ return nil
121+ }
122+ func (f * fakeStatusWriter ) Status () client.StatusWriter {
123+ return f
124+ }
125+
126+ type fakeClient struct {
127+ client.StatusWriter
128+ client.Client
129+ getErr error
130+ listErr error
131+ updateErr error
132+ createErr error
133+ statusErr error
134+ ds []* apps.DaemonSet
135+ pods []* v1.Pod
136+ createCalled bool
137+ }
138+
139+ func (f * fakeClient ) Get (ctx context.Context , key client.ObjectKey , obj client.Object , opts ... client.GetOption ) error {
140+ if f .getErr != nil {
141+ return f .getErr
142+ }
143+
144+ return nil
145+ }
146+ func (f * fakeClient ) List (ctx context.Context , list client.ObjectList , opts ... client.ListOption ) error {
147+ if f .listErr != nil {
148+ return f .listErr
149+ }
150+ switch l := list .(type ) {
151+ case * apps.DaemonSetList :
152+ l .Items = []apps.DaemonSet {}
153+
154+ for _ , ds := range f .ds {
155+ l .Items = append (l .Items , * ds )
156+ }
157+ case * v1.PodList :
158+ l .Items = []v1.Pod {}
159+
160+ for _ , pod := range f .pods {
161+ l .Items = append (l .Items , * pod )
162+ }
163+ }
164+ return nil
165+ }
166+ func (f * fakeClient ) Update (ctx context.Context , obj client.Object , opts ... client.UpdateOption ) error {
167+ return f .updateErr
168+ }
169+ func (f * fakeClient ) Create (ctx context.Context , obj client.Object , opts ... client.CreateOption ) error {
170+ f .createCalled = true
171+ return f .createErr
172+ }
173+ func (f * fakeClient ) Patch (ctx context.Context , obj client.Object , patch client.Patch , opts ... client.PatchOption ) error {
174+ return nil
175+ }
176+ func (f * fakeClient ) Delete (ctx context.Context , obj client.Object , opts ... client.DeleteOption ) error {
177+ return nil
178+ }
179+ func (f * fakeClient ) UpdateStatus (ctx context.Context , obj client.Object , opts ... client.UpdateOption ) error {
180+ return f .statusErr
181+ }
182+ func (f * fakeClient ) Status () client.StatusWriter {
183+ return f .StatusWriter
184+ }
185+ func (f * fakeClient ) Scheme () * runtime.Scheme {
186+ s := runtime .NewScheme ()
187+ s .AddKnownTypeWithName (schema.GroupVersionKind {
188+ Group : "deviceplugin.intel.com" ,
189+ Version : "v1" ,
190+ Kind : "MockPlugin" ,
191+ }, & mockPlugin {})
192+
193+ return s
194+ }
195+
196+ func fillDaemonSets () []* apps.DaemonSet {
197+ return []* apps.DaemonSet {
198+ {
199+ ObjectMeta : metav1.ObjectMeta {
200+ Name : "mock-ds" ,
201+ Namespace : "default" ,
202+ },
203+ Spec : apps.DaemonSetSpec {
204+ Selector : & metav1.LabelSelector {
205+ MatchLabels : map [string ]string {"app" : "mock" },
206+ },
207+ Template : v1.PodTemplateSpec {
208+ ObjectMeta : metav1.ObjectMeta {
209+ Labels : map [string ]string {"app" : "mock" },
210+ },
211+ Spec : v1.PodSpec {
212+ Containers : []v1.Container {
213+ {
214+ Name : "mock" ,
215+ Image : "intel/intel-mock-plugin:latest" ,
216+ },
217+ },
218+ },
219+ },
220+ },
221+ },
222+ }
223+ }
224+
225+ func fillPods () []* v1.Pod {
226+ return []* v1.Pod {
227+ {
228+ ObjectMeta : metav1.ObjectMeta {
229+ Name : "mock-pod" ,
230+ Namespace : "default" ,
231+ OwnerReferences : []metav1.OwnerReference {
232+ {
233+ APIVersion : "apps/v1" ,
234+ Kind : "DaemonSet" ,
235+ Name : "mock-ds" ,
236+ UID : "mock-uid" ,
237+ },
238+ },
239+ },
240+ Spec : v1.PodSpec {
241+ NodeName : "node1" ,
242+ Containers : []v1.Container {
243+ {
244+ Name : "mock" ,
245+ Image : "intel/intel-mock-plugin:latest" ,
246+ },
247+ },
248+ },
249+ },
250+ }
251+ }
252+
253+ func TestReconciler_Reconcile_CreateDaemonSet (t * testing.T ) {
254+ controller := & mockController {}
255+ c := & fakeClient {
256+ StatusWriter : & fakeStatusWriter {},
257+ ds : []* apps.DaemonSet {},
258+ }
259+ r := & reconciler {
260+ controller : controller ,
261+ Client : c ,
262+ scheme : c .Scheme (),
263+ pluginKind : "MockPlugin" ,
264+ ownerKey : "owner" ,
265+ }
266+ req := ctrl.Request {NamespacedName : types.NamespacedName {Name : "mock" , Namespace : "default" }}
267+
268+ res , err := r .Reconcile (context .Background (), req )
269+ if err != nil {
270+ t .Errorf ("expected no error, got %v" , err )
271+ }
272+ if res != (ctrl.Result {}) {
273+ t .Errorf ("expected empty result, got %v" , res )
274+ }
275+ if c .createCalled == false {
276+ t .Error ("expected create to be called, but it was not" )
277+ }
278+ }
279+
280+ func TestReconciler_Reconcile_UpdateDaemonSetAndStatus (t * testing.T ) {
281+ controller := & mockController {
282+ updated : true ,
283+ upgrade : false ,
284+ }
285+ c := & fakeClient {
286+ StatusWriter : & fakeStatusWriter {},
287+ ds : fillDaemonSets (),
288+ pods : fillPods (),
289+ }
290+ r := & reconciler {
291+ controller : controller ,
292+ Client : c ,
293+ scheme : c .Scheme (),
294+ pluginKind : "MockPlugin" ,
295+ ownerKey : "owner" ,
296+ }
297+ req := ctrl.Request {NamespacedName : client.ObjectKey {Name : "mock" , Namespace : "default" }}
298+
299+ res , err := r .Reconcile (context .Background (), req )
300+ if err != nil {
301+ t .Errorf ("expected no error, got %v" , err )
302+ }
303+ if res != (ctrl.Result {}) {
304+ t .Errorf ("expected empty result, got %v" , res )
305+ }
306+ }
307+
308+ type getError struct {
309+ error
310+ }
311+ type listError struct {
312+ error
313+ }
314+ type updatestatusError struct {
315+ error
316+ }
317+
318+ func TestReconciler_Reconcile_GetError (t * testing.T ) {
319+ controller := & mockController {}
320+ c := & fakeClient {
321+ getErr : getError {},
322+ StatusWriter : & fakeStatusWriter {},
323+ }
324+ r := & reconciler {
325+ controller : controller ,
326+ Client : c ,
327+ scheme : c .Scheme (),
328+ pluginKind : "MockPlugin" ,
329+ ownerKey : "owner" ,
330+ }
331+ req := ctrl.Request {NamespacedName : client.ObjectKey {Name : "mock" , Namespace : "default" }}
332+
333+ _ , err := r .Reconcile (context .Background (), req )
334+ if err == nil || errors .Is (err , c .getErr ) == false {
335+ t .Errorf ("expected get error, got %v" , err )
336+ }
337+ }
338+
339+ func TestReconciler_Reconcile_ListError (t * testing.T ) {
340+ controller := & mockController {}
341+ c := & fakeClient {
342+ listErr : listError {},
343+ StatusWriter : & fakeStatusWriter {},
344+ }
345+ r := & reconciler {
346+ controller : controller ,
347+ Client : c ,
348+ scheme : c .Scheme (),
349+ pluginKind : "MockPlugin" ,
350+ ownerKey : "owner" ,
351+ }
352+ req := ctrl.Request {NamespacedName : client.ObjectKey {Name : "mock" , Namespace : "default" }}
353+
354+ _ , err := r .Reconcile (context .Background (), req )
355+ if err == nil || errors .Is (err , c .listErr ) == false {
356+ t .Errorf ("expected list error, got %v" , err )
357+ }
358+ }
359+
360+ func TestReconciler_Reconcile_UpdateStatusError (t * testing.T ) {
361+ controller := & mockController {
362+ statusErr : updatestatusError {},
363+ }
364+ c := & fakeClient {
365+ StatusWriter : & fakeStatusWriter {},
366+ ds : fillDaemonSets (),
367+ pods : fillPods (),
368+ }
369+ r := & reconciler {
370+ controller : controller ,
371+ Client : c ,
372+ scheme : c .Scheme (),
373+ pluginKind : "MockPlugin" ,
374+ ownerKey : "owner" ,
375+ }
376+ req := ctrl.Request {NamespacedName : client.ObjectKey {Name : "mock" , Namespace : "default" }}
377+
378+ _ , err := r .Reconcile (context .Background (), req )
379+ if err == nil || errors .Is (err , controller .statusErr ) == false {
380+ t .Errorf ("expected status update error, got %v" , err )
381+ }
382+ }
383+
25384func TestUpgrade (test * testing.T ) {
26385 image := "intel/intel-dsa-plugin"
27386 initimage := "intel/intel-idxd-config-initcontainer"
0 commit comments