@@ -18,6 +18,7 @@ package command
18
18
19
19
import (
20
20
"fmt"
21
+ "strings"
21
22
22
23
"github.com/pkg/errors"
23
24
apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -57,6 +58,11 @@ type Reconciliation struct {
57
58
// client to invoke CRUD operations against k8s Job
58
59
jobClient * clientset.ResourceClient
59
60
61
+ // getChildJob will hold function to fetch the child object
62
+ // from k8s cluster
63
+ // NOTE: This is helpful to mocking
64
+ getChildJob func () (* unstructured.Unstructured , bool , error )
65
+
60
66
// is Command resource supposed to run Once
61
67
isRunOnce bool
62
68
@@ -98,6 +104,10 @@ type Reconciliation struct {
98
104
}
99
105
100
106
func (r * Reconciliation ) initChildJobDetails () {
107
+ var got * unstructured.Unstructured
108
+ var found bool
109
+ var err error
110
+
101
111
if r .childJob == nil || r .childJob .Object == nil {
102
112
return
103
113
}
@@ -115,7 +125,12 @@ func (r *Reconciliation) initChildJobDetails() {
115
125
)
116
126
return
117
127
}
118
- got , found , err := r .isChildJobAvailable ()
128
+
129
+ if r .getChildJob != nil {
130
+ got , found , err = r .getChildJob ()
131
+ } else {
132
+ got , found , err = r .isChildJobAvailable ()
133
+ }
119
134
if err != nil {
120
135
r .err = err
121
136
return
@@ -162,16 +177,17 @@ func (r *Reconciliation) initChildJobDetails() {
162
177
return
163
178
}
164
179
165
- // Extract status.active of this Job
166
- activeCount , found , err := unstructured .NestedInt64 (
180
+ // Extract status.conditions of this Job to know whether
181
+ // job has completed its execution
182
+ jobConditions , found , err := unstructured .NestedSlice (
167
183
got .Object ,
168
184
"status" ,
169
- "active " ,
185
+ "conditions " ,
170
186
)
171
187
if err != nil {
172
188
r .err = errors .Wrapf (
173
189
err ,
174
- "Failed to get Job status.active : Kind %q: Job %q / %q" ,
190
+ "Failed to get Job status.conditions : Kind %q: Job %q / %q" ,
175
191
r .childJob .GetKind (),
176
192
r .childJob .GetNamespace (),
177
193
r .childJob .GetName (),
@@ -180,21 +196,45 @@ func (r *Reconciliation) initChildJobDetails() {
180
196
}
181
197
if ! found {
182
198
klog .V (1 ).Infof (
183
- "Job status.active is not set: Kind %q: Job %q / %q" ,
199
+ "Job status.conditions is not set: Kind %q: Job %q / %q" ,
184
200
r .childJob .GetKind (),
185
201
r .childJob .GetNamespace (),
186
202
r .childJob .GetName (),
187
203
)
188
- // Job's status.active is not set
204
+ // Job's status.conditions is not set
189
205
//
190
206
// Nothing to do
191
207
// Wait for next reconcile
192
208
return
193
209
}
194
-
195
- if activeCount > 0 {
196
- r .isChildJobCompleted = true
210
+ // Look for condition type complete
211
+ // if found then mark isChildJobCompleted as true
212
+ for _ , value := range jobConditions {
213
+ condition , ok := value .(map [string ]interface {})
214
+ if ! ok {
215
+ r .err = errors .Errorf (
216
+ "Job status.condition is not map[string]interface{} got %T: " +
217
+ "kind %q: Job %q / %q" ,
218
+ value ,
219
+ r .childJob .GetKind (),
220
+ r .childJob .GetNamespace (),
221
+ r .childJob .GetName (),
222
+ )
223
+ return
224
+ }
225
+ condType := condition ["type" ].(string )
226
+ if condType == types .JobPhaseCompleted {
227
+ condStatus := condition ["status" ].(string )
228
+ if strings .ToLower (condStatus ) == "true" {
229
+ r .isChildJobCompleted = true
230
+ }
231
+ }
197
232
}
233
+
234
+ // If there is no condtion with complete type then
235
+ // nothing to do
236
+
237
+ // wait for next reconciliation
198
238
}
199
239
200
240
func (r * Reconciliation ) initCommandDetails () {
@@ -356,13 +396,17 @@ func (r *Reconciliation) isChildJobAvailable() (*unstructured.Unstructured, bool
356
396
}
357
397
358
398
func (r * Reconciliation ) deleteChildJob () (types.CommandStatus , error ) {
399
+ // If propagationPolicy is set to background then the garbage collector will
400
+ // delete dependents in the background
401
+ propagationPolicy := v1 .DeletePropagationBackground
359
402
err := r .jobClient .
360
403
Namespace (r .childJob .GetNamespace ()).
361
404
Delete (
362
405
r .childJob .GetName (),
363
406
& v1.DeleteOptions {
364
407
// Delete immediately
365
408
GracePeriodSeconds : pointer .Int64 (0 ),
409
+ PropagationPolicy : & propagationPolicy ,
366
410
},
367
411
)
368
412
if err != nil && ! apierrors .IsNotFound (err ) {
@@ -445,12 +489,52 @@ func (r *Reconciliation) reconcileRunAlwaysCommand() (types.CommandStatus, error
445
489
return r .createChildJob ()
446
490
}
447
491
if r .isStatusSet && r .isChildJobCompleted {
492
+ // Since this is for run always we are performing below steps
493
+ // 1. Delete Job and wait til it gets deleted from etcd
494
+ // 2. Create Job in the same reconciliation
448
495
klog .V (1 ).Infof (
449
496
"Will delete command job: Command %q / %q" ,
450
497
r .command .GetNamespace (),
451
498
r .command .GetName (),
452
499
)
453
- return r .deleteChildJob ()
500
+ _ , err := r .deleteChildJob ()
501
+ if err != nil {
502
+ return types.CommandStatus {}, err
503
+ }
504
+
505
+ // Logic to wait for Job resource deletion from etcd
506
+ var message = fmt .Sprintf (
507
+ "Waiting for command job: %q / %q deletion" ,
508
+ r .childJob .GetNamespace (),
509
+ r .childJob .GetName (),
510
+ )
511
+ err = r .Retry .Waitf (
512
+ func () (bool , error ) {
513
+ _ , err := r .jobClient .
514
+ Namespace (r .childJob .GetNamespace ()).
515
+ Get (r .childJob .GetName (), v1.GetOptions {})
516
+ if err != nil {
517
+ if apierrors .IsNotFound (err ) {
518
+ return true , nil
519
+ }
520
+ return false , err
521
+ }
522
+ return false , nil
523
+ },
524
+ message ,
525
+ )
526
+
527
+ klog .V (1 ).Infof ("Deleted command job: Command %q / %q successfully" ,
528
+ r .command .GetNamespace (),
529
+ r .command .GetName (),
530
+ )
531
+
532
+ klog .V (1 ).Infof (
533
+ "Will create command job: Command %q / %q" ,
534
+ r .command .GetNamespace (),
535
+ r .command .GetName (),
536
+ )
537
+ return r .createChildJob ()
454
538
}
455
539
return types.CommandStatus {
456
540
Phase : types .CommandPhaseInProgress ,
0 commit comments