@@ -19,14 +19,15 @@ package scheduledsparkapplication
19
19
import (
20
20
"fmt"
21
21
"reflect"
22
+ "sort"
22
23
"time"
23
24
24
25
"github.com/golang/glog"
25
26
"github.com/robfig/cron"
26
27
27
28
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
28
- "k8s.io/apimachinery/pkg/api/errors"
29
29
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30
+ "k8s.io/apimachinery/pkg/labels"
30
31
"k8s.io/apimachinery/pkg/util/clock"
31
32
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
32
33
"k8s.io/apimachinery/pkg/util/wait"
@@ -41,6 +42,7 @@ import (
41
42
crdscheme "github.com/GoogleCloudPlatform/spark-on-k8s-operator/pkg/client/clientset/versioned/scheme"
42
43
crdinformers "github.com/GoogleCloudPlatform/spark-on-k8s-operator/pkg/client/informers/externalversions"
43
44
crdlisters "github.com/GoogleCloudPlatform/spark-on-k8s-operator/pkg/client/listers/sparkoperator.k8s.io/v1alpha1"
45
+ "github.com/GoogleCloudPlatform/spark-on-k8s-operator/pkg/config"
44
46
)
45
47
46
48
var (
@@ -176,38 +178,26 @@ func (c *Controller) syncScheduledSparkApplication(key string) error {
176
178
status .NextRun = metav1 .NewTime (nextRunTime )
177
179
}
178
180
if nextRunTime .Before (now ) {
179
- // The next run is due. Check if this is the first run of the application.
180
- if status .LastRunName == "" {
181
- // This is the first run of the application.
182
- if err = c .startNextRun (app , status , schedule ); err != nil {
183
- return err
184
- }
185
- } else {
186
- // Check if the condition for starting the next run is satisfied.
187
- ok , err := c .shouldStartNextRun (app )
188
- if err != nil {
189
- return err
190
- }
191
- if ok {
192
- // Start the next run if the condition is satisfied.
193
- if err = c .startNextRun (app , status , schedule ); err != nil {
194
- return err
195
- }
196
- } else {
197
- // Otherwise, check and update past runs.
198
- if err = c .checkAndUpdatePastRuns (app , status ); err != nil {
199
- return err
200
- }
201
- }
181
+ // Check if the condition for starting the next run is satisfied.
182
+ ok , err := c .shouldStartNextRun (app )
183
+ if err != nil {
184
+ return err
202
185
}
203
- } else {
204
- // The next run is not due yet, check and update past runs.
205
- if status . LastRunName != "" {
206
- if err = c . checkAndUpdatePastRuns ( app , status ); err != nil {
186
+ if ok {
187
+ glog . Infof ( "Next run of ScheduledSparkApplication %s/%s is due, creating a new SparkApplication instance" , app . Namespace , app . Name )
188
+ name , err := c . startNextRun ( app , now )
189
+ if err != nil {
207
190
return err
208
191
}
192
+ status .LastRun = metav1 .NewTime (now )
193
+ status .NextRun = metav1 .NewTime (schedule .Next (status .LastRun .Time ))
194
+ status .LastRunName = name
209
195
}
210
196
}
197
+
198
+ if err = c .checkAndUpdatePastRuns (app , status ); err != nil {
199
+ return err
200
+ }
211
201
}
212
202
213
203
return c .updateScheduledSparkApplicationStatus (app , status )
@@ -257,7 +247,11 @@ func (c *Controller) createSparkApplication(
257
247
Name : scheduledApp .Name ,
258
248
UID : scheduledApp .UID ,
259
249
})
260
- app .ObjectMeta .SetLabels (scheduledApp .GetLabels ())
250
+ app .ObjectMeta .Labels = make (map [string ]string )
251
+ for key , value := range scheduledApp .Labels {
252
+ app .ObjectMeta .Labels [key ] = value
253
+ }
254
+ app .ObjectMeta .Labels [config .ScheduledSparkAppNameLabel ] = scheduledApp .Name
261
255
_ , err := c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (scheduledApp .Namespace ).Create (app )
262
256
if err != nil {
263
257
return "" , err
@@ -266,69 +260,52 @@ func (c *Controller) createSparkApplication(
266
260
}
267
261
268
262
func (c * Controller ) shouldStartNextRun (app * v1alpha1.ScheduledSparkApplication ) (bool , error ) {
263
+ sortedApps , err := c .listSparkApplications (app )
264
+ if err != nil {
265
+ return false , err
266
+ }
267
+ if len (sortedApps ) == 0 {
268
+ return true , nil
269
+ }
270
+
271
+ // The last run (most recently started) is the first one in the sorted slice.
272
+ lastRun := sortedApps [0 ]
269
273
switch app .Spec .ConcurrencyPolicy {
270
274
case v1alpha1 .ConcurrencyAllow :
271
275
return true , nil
272
276
case v1alpha1 .ConcurrencyForbid :
273
- finished , _ , err := c .hasLastRunFinished (app .Namespace , app .Status .LastRunName )
274
- if err != nil {
275
- return false , err
276
- }
277
- return finished , nil
277
+ return c .hasLastRunFinished (lastRun ), nil
278
278
case v1alpha1 .ConcurrencyReplace :
279
- if err := c .killLastRunIfNotFinished (app . Namespace , app . Status . LastRunName ); err != nil {
279
+ if err := c .killLastRunIfNotFinished (lastRun ); err != nil {
280
280
return false , err
281
281
}
282
282
return true , nil
283
283
}
284
284
return true , nil
285
285
}
286
286
287
- func (c * Controller ) startNextRun (
288
- app * v1alpha1.ScheduledSparkApplication ,
289
- status * v1alpha1.ScheduledSparkApplicationStatus ,
290
- schedule cron.Schedule ) error {
291
- glog .Infof ("Next run of ScheduledSparkApplication %s/%s is due, creating a new SparkApplication instance" , app .Namespace , app .Name )
292
- now := c .clock .Now ()
287
+ func (c * Controller ) startNextRun (app * v1alpha1.ScheduledSparkApplication , now time.Time ) (string , error ) {
293
288
name , err := c .createSparkApplication (app , now )
294
289
if err != nil {
295
290
glog .Errorf ("failed to create a SparkApplication instance for ScheduledSparkApplication %s/%s: %v" , app .Namespace , app .Name , err )
296
- return err
291
+ return "" , err
297
292
}
298
-
299
- status .LastRun = metav1 .NewTime (now )
300
- status .NextRun = metav1 .NewTime (schedule .Next (status .LastRun .Time ))
301
- status .LastRunName = name
302
- return nil
293
+ return name , nil
303
294
}
304
295
305
- func (c * Controller ) hasLastRunFinished (
306
- namespace string ,
307
- lastRunName string ) (bool , * v1alpha1.SparkApplication , error ) {
308
- app , err := c .saLister .SparkApplications (namespace ).Get (lastRunName )
309
- if err != nil {
310
- // The SparkApplication of the last run may have been deleted already (e.g., manually by the user).
311
- if errors .IsNotFound (err ) {
312
- return true , nil , nil
313
- }
314
- return false , nil , err
315
- }
316
-
296
+ func (c * Controller ) hasLastRunFinished (app * v1alpha1.SparkApplication ) bool {
317
297
return app .Status .AppState .State == v1alpha1 .CompletedState ||
318
- app .Status .AppState .State == v1alpha1 .FailedState , app , nil
298
+ app .Status .AppState .State == v1alpha1 .FailedState
319
299
}
320
300
321
- func (c * Controller ) killLastRunIfNotFinished (namespace string , lastRunName string ) error {
322
- finished , app , err := c .hasLastRunFinished (namespace , lastRunName )
323
- if err != nil {
324
- return err
325
- }
326
- if app == nil || finished {
301
+ func (c * Controller ) killLastRunIfNotFinished (app * v1alpha1.SparkApplication ) error {
302
+ finished := c .hasLastRunFinished (app )
303
+ if finished {
327
304
return nil
328
305
}
329
306
330
307
// Delete the SparkApplication object of the last run.
331
- if err = c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (namespace ).Delete (lastRunName ,
308
+ if err : = c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (app . Namespace ).Delete (app . Name ,
332
309
metav1 .NewDeleteOptions (0 )); err != nil {
333
310
return err
334
311
}
@@ -339,34 +316,29 @@ func (c *Controller) killLastRunIfNotFinished(namespace string, lastRunName stri
339
316
func (c * Controller ) checkAndUpdatePastRuns (
340
317
app * v1alpha1.ScheduledSparkApplication ,
341
318
status * v1alpha1.ScheduledSparkApplicationStatus ) error {
342
- lastRunName := status .LastRunName
343
- lastRunApp , err := c .saLister .SparkApplications (app .Namespace ).Get (lastRunName )
319
+ sortedApps , err := c .listSparkApplications (app )
344
320
if err != nil {
345
- // The SparkApplication of the last run may have been deleted already (e.g., manually by the user).
346
- if errors .IsNotFound (err ) {
347
- return nil
348
- }
349
321
return err
350
322
}
351
323
352
- var toDelete []string
353
- if lastRunApp .Status .AppState .State == v1alpha1 .CompletedState {
354
- limit := 1
355
- if app .Spec .SuccessfulRunHistoryLimit != nil {
356
- limit = int (* app .Spec .SuccessfulRunHistoryLimit )
357
- }
358
- status .PastSuccessfulRunNames , toDelete = recordPastRuns (status .PastSuccessfulRunNames , lastRunName , limit )
359
- } else if lastRunApp .Status .AppState .State == v1alpha1 .FailedState {
360
- limit := 1
361
- if app .Spec .FailedRunHistoryLimit != nil {
362
- limit = int (* app .Spec .FailedRunHistoryLimit )
324
+ var completedRuns []string
325
+ var failedRuns []string
326
+ for _ , a := range sortedApps {
327
+ if a .Status .AppState .State == v1alpha1 .CompletedState {
328
+ completedRuns = append (completedRuns , a .Name )
329
+ } else if a .Status .AppState .State == v1alpha1 .FailedState {
330
+ failedRuns = append (failedRuns , a .Name )
363
331
}
364
- status .PastFailedRunNames , toDelete = recordPastRuns (status .PastFailedRunNames , lastRunName , limit )
365
332
}
366
333
334
+ var toDelete []string
335
+ status .PastSuccessfulRunNames , toDelete = bookkeepPastRuns (completedRuns , app .Spec .SuccessfulRunHistoryLimit )
367
336
for _ , name := range toDelete {
368
- c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (app .Namespace ).Delete (name ,
369
- metav1 .NewDeleteOptions (0 ))
337
+ c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (app .Namespace ).Delete (name , metav1 .NewDeleteOptions (0 ))
338
+ }
339
+ status .PastFailedRunNames , toDelete = bookkeepPastRuns (failedRuns , app .Spec .FailedRunHistoryLimit )
340
+ for _ , name := range toDelete {
341
+ c .crdClient .SparkoperatorV1alpha1 ().SparkApplications (app .Namespace ).Delete (name , metav1 .NewDeleteOptions (0 ))
370
342
}
371
343
372
344
return nil
@@ -400,19 +372,28 @@ func (c *Controller) updateScheduledSparkApplicationStatus(
400
372
})
401
373
}
402
374
403
- func recordPastRuns (names []string , newName string , limit int ) (updatedNames []string , toDelete []string ) {
404
- if len (names ) > 0 && names [0 ] == newName {
405
- // The name has already been recorded.
406
- return names , nil
375
+ func (c * Controller ) listSparkApplications (app * v1alpha1.ScheduledSparkApplication ) (sparkApps , error ) {
376
+ set := labels.Set {config .ScheduledSparkAppNameLabel : app .Name }
377
+ apps , err := c .saLister .SparkApplications (app .Namespace ).List (set .AsSelector ())
378
+ if err != nil {
379
+ return nil , fmt .Errorf ("failed to list SparkApplications: %v" , err )
407
380
}
381
+ sortedApps := sparkApps (apps )
382
+ sort .Sort (sortedApps )
383
+ return sortedApps , nil
384
+ }
408
385
409
- rest := names
410
- if len (names ) >= limit {
411
- rest = names [:limit - 1 ]
412
- toDelete = names [limit - 1 :]
386
+ func bookkeepPastRuns (names []string , runLimit * int32 ) (toKeep []string , toDelete []string ) {
387
+ limit := 1
388
+ if runLimit != nil {
389
+ limit = int (* runLimit )
390
+ }
391
+
392
+ if len (names ) <= limit {
393
+ return names , nil
413
394
}
414
- // Pre-append the name of the latest run.
415
- updatedNames = append ([] string { newName }, rest ... )
395
+ toKeep = names [: limit ]
396
+ toDelete = names [ limit :]
416
397
return
417
398
}
418
399
0 commit comments