@@ -5,6 +5,7 @@ package deployable
55
66import (
77 "context"
8+ "errors"
89 "testing"
910
1011 kaiv1 "github.com/NVIDIA/KAI-scheduler/pkg/apis/kai/v1"
@@ -24,6 +25,7 @@ import (
2425 "k8s.io/utils/ptr"
2526 "sigs.k8s.io/controller-runtime/pkg/client"
2627 "sigs.k8s.io/controller-runtime/pkg/client/fake"
28+ "sigs.k8s.io/controller-runtime/pkg/client/interceptor"
2729)
2830
2931func TestDeployable (t * testing.T ) {
@@ -62,92 +64,154 @@ var _ = Describe("Deployable", func() {
6264 deployable * DeployableOperands
6365 fakeClient client.Client
6466 )
65- BeforeEach (func () {
66- operand := & fakeOperand {}
67+ Context ("object creation successfull" , func () {
6768
68- deployable = New ([]operands.Operand {operand }, known_types .KAIConfigRegisteredCollectible )
69- fakeClient = getFakeClient (fakeClientBuilder , known_types .KAIConfigRegisteredCollectible )
70- })
71- It ("should deploy operands desired state" , func () {
72- Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
73- podsList := & v1.PodList {}
74- err := fakeClient .List (context .TODO (), podsList )
75- Expect (err ).ToNot (HaveOccurred ())
76- Expect (len (podsList .Items )).To (Equal (1 ))
69+ BeforeEach (func () {
70+ operand := & fakeOperand {}
7771
78- configMapList := & v1.ConfigMapList {}
79- err = fakeClient .List (context .TODO (), configMapList )
80- Expect (err ).ToNot (HaveOccurred ())
81- Expect (len (configMapList .Items )).To (Equal (1 ))
82- })
83- It ("should not deploy operands on custom field inheritor" , func () {
84- desiredWithAnnotation := & v1.ConfigMap {
85- TypeMeta : metav1.TypeMeta {
86- Kind : "ConfigMap" ,
87- APIVersion : "v1" ,
88- },
89- ObjectMeta : metav1.ObjectMeta {
90- Name : "foo" ,
91- Namespace : "bar" ,
92- Annotations : map [string ]string {"A" : "a" },
93- OwnerReferences : []metav1.OwnerReference {
94- {
95- APIVersion : kaiConfig .GetObjectKind ().GroupVersionKind ().GroupVersion ().String (),
96- Kind : kaiConfig .GetObjectKind ().GroupVersionKind ().Kind ,
97- Name : kaiConfig .GetName (),
98- UID : kaiConfig .GetUID (),
99- Controller : ptr .To (true ),
72+ deployable = New ([]operands.Operand {operand }, known_types .KAIConfigRegisteredCollectible )
73+ fakeClient = getFakeClient (fakeClientBuilder , known_types .KAIConfigRegisteredCollectible )
74+ })
75+ It ("should deploy operands desired state" , func () {
76+ Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
77+ podsList := & v1.PodList {}
78+ err := fakeClient .List (context .TODO (), podsList )
79+ Expect (err ).ToNot (HaveOccurred ())
80+ Expect (len (podsList .Items )).To (Equal (1 ))
81+
82+ configMapList := & v1.ConfigMapList {}
83+ err = fakeClient .List (context .TODO (), configMapList )
84+ Expect (err ).ToNot (HaveOccurred ())
85+ Expect (len (configMapList .Items )).To (Equal (1 ))
86+ })
87+ It ("should not deploy operands on custom field inheritor" , func () {
88+ desiredWithAnnotation := & v1.ConfigMap {
89+ TypeMeta : metav1.TypeMeta {
90+ Kind : "ConfigMap" ,
91+ APIVersion : "v1" ,
92+ },
93+ ObjectMeta : metav1.ObjectMeta {
94+ Name : "foo" ,
95+ Namespace : "bar" ,
96+ Annotations : map [string ]string {"A" : "a" },
97+ OwnerReferences : []metav1.OwnerReference {
98+ {
99+ APIVersion : kaiConfig .GetObjectKind ().GroupVersionKind ().GroupVersion ().String (),
100+ Kind : kaiConfig .GetObjectKind ().GroupVersionKind ().Kind ,
101+ Name : kaiConfig .GetName (),
102+ UID : kaiConfig .GetUID (),
103+ Controller : ptr .To (true ),
104+ },
100105 },
101106 },
102- },
103- }
104- Expect (fakeClient .Create (context .TODO (), desiredWithAnnotation )).To (Succeed ())
105-
106- deployable .RegisterFieldsInheritFromClusterObjects (& v1.ConfigMap {}, func (current , desired client.Object ) {
107- currentCm := current .(* v1.ConfigMap )
108- desiredCm := desired .(* v1.ConfigMap )
109- if desiredCm .Annotations == nil {
110- desiredCm .Annotations = map [string ]string {}
111107 }
112- maps .Copy (desiredCm .Annotations , currentCm .Annotations )
108+ Expect (fakeClient .Create (context .TODO (), desiredWithAnnotation )).To (Succeed ())
109+
110+ deployable .RegisterFieldsInheritFromClusterObjects (& v1.ConfigMap {}, func (current , desired client.Object ) {
111+ currentCm := current .(* v1.ConfigMap )
112+ desiredCm := desired .(* v1.ConfigMap )
113+ if desiredCm .Annotations == nil {
114+ desiredCm .Annotations = map [string ]string {}
115+ }
116+ maps .Copy (desiredCm .Annotations , currentCm .Annotations )
117+ })
118+ Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
119+
120+ cmList := & v1.ConfigMapList {}
121+ err := fakeClient .List (context .TODO (), cmList )
122+ Expect (err ).ToNot (HaveOccurred ())
123+ Expect (len (cmList .Items )).To (Equal (1 ))
124+ Expect (len (cmList .Items [0 ].Annotations )).To (Equal (1 ))
113125 })
114- Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
115126
116- cmList := & v1.ConfigMapList {}
117- err := fakeClient .List (context .TODO (), cmList )
118- Expect (err ).ToNot (HaveOccurred ())
119- Expect (len (cmList .Items )).To (Equal (1 ))
120- Expect (len (cmList .Items [0 ].Annotations )).To (Equal (1 ))
127+ It ("should delete other resources" , func () {
128+ otherConfigMap := & v1.ConfigMap {
129+ ObjectMeta : metav1.ObjectMeta {
130+ Name : "foo2" ,
131+ Namespace : "bar2" ,
132+ OwnerReferences : []metav1.OwnerReference {
133+ {
134+ APIVersion : kaiConfig .GetObjectKind ().GroupVersionKind ().GroupVersion ().String (),
135+ Kind : kaiConfig .GetObjectKind ().GroupVersionKind ().Kind ,
136+ Name : kaiConfig .GetName (),
137+ UID : kaiConfig .GetUID (),
138+ Controller : ptr .To (true ),
139+ },
140+ },
141+ },
142+ }
143+ Expect (fakeClient .Create (context .TODO (), otherConfigMap )).To (Succeed ())
144+
145+ Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
146+
147+ configMapList := & v1.ConfigMapList {}
148+ err := fakeClient .List (context .TODO (), configMapList )
149+ Expect (err ).ToNot (HaveOccurred ())
150+ Expect (len (configMapList .Items )).To (Equal (1 ))
151+
152+ for _ , item := range configMapList .Items {
153+ Expect (item .Name ).ToNot (Equal (otherConfigMap .Name ))
154+ }
155+ })
121156 })
122157
123- It ("should delete other resources" , func () {
124- otherConfigMap := & v1.ConfigMap {
125- ObjectMeta : metav1.ObjectMeta {
126- Name : "foo2" ,
127- Namespace : "bar2" ,
128- OwnerReferences : []metav1.OwnerReference {
129- {
130- APIVersion : kaiConfig .GetObjectKind ().GroupVersionKind ().GroupVersion ().String (),
131- Kind : kaiConfig .GetObjectKind ().GroupVersionKind ().Kind ,
132- Name : kaiConfig .GetName (),
133- UID : kaiConfig .GetUID (),
134- Controller : ptr .To (true ),
158+ Context ("Object creation fails" , func () {
159+ var (
160+ fakeClient client.Client
161+ createCalls int
162+ updateCalls int
163+ createError error
164+ updateError error
165+ )
166+ BeforeEach (func () {
167+ createCalls = 0
168+ updateCalls = 0
169+ createError = nil
170+ updateError = nil
171+
172+ operand := & fakeOperand {}
173+ deployable = New ([]operands.Operand {operand }, known_types .KAIConfigRegisteredCollectible )
174+
175+ fakeClient = getFakeClient (fakeClientBuilder .
176+ WithInterceptorFuncs (interceptor.Funcs {
177+ Create : func (ctx context.Context , client client.WithWatch , obj client.Object , opts ... client.CreateOption ) error {
178+ createCalls ++
179+ return createError
135180 },
136- },
137- },
138- }
139- Expect (fakeClient .Create (context .TODO (), otherConfigMap )).To (Succeed ())
181+ Update : func (ctx context.Context , client client.WithWatch , obj client.Object , opts ... client.UpdateOption ) error {
182+ updateCalls ++
183+ return updateError
184+ },
185+ }), known_types .KAIConfigRegisteredCollectible )
186+ })
140187
141- Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
188+ It ("should create object successfully" , func () {
189+ Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
190+ Expect (createCalls ).To (Equal (2 )) // Pod and ConfigMap creates
191+ Expect (updateCalls ).To (Equal (0 ))
192+ })
142193
143- configMapList := & v1.ConfigMapList {}
144- err := fakeClient .List (context .TODO (), configMapList )
145- Expect (err ).ToNot (HaveOccurred ())
146- Expect (len (configMapList .Items )).To (Equal (1 ))
194+ It ("should update object if create fails due to existing resource" , func () {
195+ createError = errors .New ("already exists" )
196+ defer func () { createError = nil }()
197+ Expect (deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )).To (Succeed ())
198+ Expect (createCalls ).To (Equal (2 ))
199+ Expect (updateCalls ).To (Equal (2 ))
200+ })
147201
148- for _ , item := range configMapList .Items {
149- Expect (item .Name ).ToNot (Equal (otherConfigMap .Name ))
150- }
202+ It ("should fail if both create and update fail" , func () {
203+ createError = errors .New ("already exists" )
204+ updateError = errors .New ("update failed" )
205+ defer func () {
206+ createError = nil
207+ updateError = nil
208+ }()
209+
210+ err := deployable .Deploy (context .TODO (), fakeClient , kaiConfig , kaiConfig )
211+ Expect (err ).To (HaveOccurred ())
212+ Expect (createCalls ).To (Equal (1 ))
213+ Expect (updateCalls ).To (Equal (1 ))
214+ })
151215 })
152216 })
153217
@@ -198,6 +262,64 @@ var _ = Describe("Deployable", func() {
198262 })
199263 })
200264
265+ Describe ("Monitor" , func () {
266+ var (
267+ deployable * DeployableOperands
268+ fakeClient client.Client
269+ )
270+ BeforeEach (func () {
271+ operand := & fakeOperand {}
272+ deployable = New ([]operands.Operand {operand }, known_types .KAIConfigRegisteredCollectible )
273+ fakeClient = getFakeClient (fakeClientBuilder , known_types .KAIConfigRegisteredCollectible )
274+ })
275+
276+ It ("should not return error when monitoring" , func () {
277+ Expect (deployable .Monitor (context .TODO (), fakeClient , kaiConfig )).To (Succeed ())
278+ })
279+
280+ It ("should return error if operand's Monitor fails" , func () {
281+ errOperand := & fakeOperandWithMonitorError {monitorErr : errors .New ("monitor failed" )}
282+ deployable = New ([]operands.Operand {errOperand }, known_types .KAIConfigRegisteredCollectible )
283+ Expect (deployable .Monitor (context .TODO (), fakeClient , kaiConfig )).To (MatchError (ContainSubstring ("monitor failed" )))
284+ Expect (errOperand .monitorErr ).To (HaveOccurred ())
285+ })
286+ })
287+
288+ Describe ("HasMissingDependencies" , func () {
289+ var (
290+ deployable * DeployableOperands
291+ fakeClient client.Reader
292+ )
293+ BeforeEach (func () {
294+ operand := & fakeOperand {}
295+ deployable = New ([]operands.Operand {operand }, known_types .KAIConfigRegisteredCollectible )
296+ fakeClient = getFakeClient (fakeClientBuilder , known_types .KAIConfigRegisteredCollectible )
297+ })
298+
299+ It ("should return no missing dependencies if all operands report none" , func () {
300+ missing , err := deployable .HasMissingDependencies (context .TODO (), fakeClient , kaiConfig )
301+ Expect (missing ).To (BeEmpty ())
302+ Expect (err ).ToNot (HaveOccurred ())
303+ })
304+
305+ It ("should aggregate missing dependencies from operands" , func () {
306+ operand1 := & fakeOperandWithDeps {missingDeps : "dep1" , name : "operand1" }
307+ operand2 := & fakeOperandWithDeps {missingDeps : "dep2" , name : "operand2" }
308+ deployable = New ([]operands.Operand {operand1 , operand2 }, known_types .KAIConfigRegisteredCollectible )
309+ missing , err := deployable .HasMissingDependencies (context .TODO (), fakeClient , kaiConfig )
310+ Expect (missing ).To (ContainSubstring ("operand1 is missing dep1" ))
311+ Expect (missing ).To (ContainSubstring ("operand2 is missing dep2" ))
312+ Expect (err ).ToNot (HaveOccurred ())
313+ })
314+
315+ It ("should handle error from operand's HasMissingDependencies" , func () {
316+ errOperand := & fakeOperandWithDeps {hasDepErr : errors .New ("dependency check failed" ), name : "errOperand" }
317+ deployable = New ([]operands.Operand {errOperand }, known_types .KAIConfigRegisteredCollectible )
318+ _ , err := deployable .HasMissingDependencies (context .TODO (), fakeClient , kaiConfig )
319+ Expect (err ).To (MatchError (ContainSubstring ("dependency check failed" )))
320+ })
321+ })
322+
201323 Describe ("SortObjectByCreationOrder" , func () {
202324 var (
203325 orderDefinition []string
@@ -375,3 +497,48 @@ func getFakeClient(builder *fake.ClientBuilder, collectables []*known_types.Coll
375497 }
376498 return builder .Build ()
377499}
500+
501+ type fakeOperandWithMonitorError struct {
502+ monitorErr error
503+ }
504+
505+ func (f * fakeOperandWithMonitorError ) Monitor (_ context.Context , _ client.Reader , _ * kaiv1.Config ) error {
506+ return f .monitorErr
507+ }
508+
509+ func (f * fakeOperandWithMonitorError ) Name () string { return "fakeOperandWithMonitorError" }
510+ func (f * fakeOperandWithMonitorError ) DesiredState (_ context.Context , _ client.Reader , _ * kaiv1.Config ) ([]client.Object , error ) {
511+ return nil , nil
512+ }
513+ func (f * fakeOperandWithMonitorError ) IsDeployed (_ context.Context , _ client.Reader ) (bool , error ) {
514+ return true , nil
515+ }
516+ func (f * fakeOperandWithMonitorError ) IsAvailable (_ context.Context , _ client.Reader ) (bool , error ) {
517+ return true , nil
518+ }
519+ func (f * fakeOperandWithMonitorError ) HasMissingDependencies (context.Context , client.Reader , * kaiv1.Config ) (string , error ) {
520+ return "" , nil
521+ }
522+
523+ type fakeOperandWithDeps struct {
524+ missingDeps string
525+ hasDepErr error
526+ name string
527+ }
528+
529+ func (f * fakeOperandWithDeps ) HasMissingDependencies (_ context.Context , _ client.Reader , _ * kaiv1.Config ) (string , error ) {
530+ return f .missingDeps , f .hasDepErr
531+ }
532+ func (f * fakeOperandWithDeps ) Name () string { return f .name }
533+ func (f * fakeOperandWithDeps ) DesiredState (_ context.Context , _ client.Reader , _ * kaiv1.Config ) ([]client.Object , error ) {
534+ return nil , nil
535+ }
536+ func (f * fakeOperandWithDeps ) IsDeployed (_ context.Context , _ client.Reader ) (bool , error ) {
537+ return true , nil
538+ }
539+ func (f * fakeOperandWithDeps ) IsAvailable (_ context.Context , _ client.Reader ) (bool , error ) {
540+ return true , nil
541+ }
542+ func (f * fakeOperandWithDeps ) Monitor (_ context.Context , _ client.Reader , _ * kaiv1.Config ) error {
543+ return nil
544+ }
0 commit comments