@@ -18,7 +18,7 @@ const { write_stdout_response, throw_cli_error, get_service_status, NOOBAA_SERVI
18
18
is_desired_time, record_current_time } = require ( './manage_nsfs_cli_utils' ) ;
19
19
20
20
// TODO:
21
- // implement
21
+ // implement
22
22
// 1. notifications
23
23
// 2. POSIX scanning and filtering per rule
24
24
// 3. GPFS ILM policy and apply for scanning and filtering optimization
@@ -49,7 +49,7 @@ const TIMED_OPS = Object.freeze({
49
49
/**
50
50
* run_lifecycle_under_lock runs the lifecycle workflow under a file system lock
51
51
* lifecycle workflow is being locked to prevent multiple instances from running the lifecycle workflow
52
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
52
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
53
53
* @param {{disable_service_validation?: boolean, disable_runtime_validation?: boolean, short?: boolean} } flags
54
54
*/
55
55
async function run_lifecycle_under_lock ( config_fs , flags ) {
@@ -83,9 +83,9 @@ async function run_lifecycle_under_lock(config_fs, flags) {
83
83
}
84
84
85
85
/**
86
- * run_lifecycle_or_timeout runs the lifecycle workflow or times out while calculating
86
+ * run_lifecycle_or_timeout runs the lifecycle workflow or times out while calculating
87
87
* and saving times and stats of the run on the global lifecycle status
88
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
88
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
89
89
* @param {boolean } disable_service_validation
90
90
* @returns {Promise<Void> }
91
91
*/
@@ -104,7 +104,7 @@ async function run_lifecycle_or_timeout(config_fs, disable_service_validation) {
104
104
105
105
/**
106
106
* run_lifecycle runs the lifecycle workflow
107
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
107
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
108
108
* @param {boolean } disable_service_validation
109
109
* @returns {Promise<Void> }
110
110
*/
@@ -125,13 +125,13 @@ async function run_lifecycle(config_fs, disable_service_validation) {
125
125
126
126
/**
127
127
* process_buckets iterates over buckets and handles their rules
128
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
129
- * @param {String[] } bucket_names
128
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
129
+ * @param {String[] } bucket_names
130
130
* @param {Object } system_json
131
131
* @returns {Promise<Void> }
132
132
*/
133
133
async function process_buckets ( config_fs , bucket_names , system_json ) {
134
- const buckets_concurrency = 10 ; // TODO - think about it
134
+ const buckets_concurrency = 10 ; // TODO - think about it
135
135
await P . map_with_concurrency ( buckets_concurrency , bucket_names , async bucket_name =>
136
136
await _call_op_and_update_status ( {
137
137
bucket_name,
@@ -182,10 +182,10 @@ async function process_rules(config_fs, bucket_json, object_sdk) {
182
182
/**
183
183
* process_rule processes the lifecycle rule for a bucket
184
184
* TODO - implement notifications for the deleted objects (check if needed for abort mpus as well)
185
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
186
- * @param {Object } lifecycle_rule
187
- * @param {number } index
188
- * @param {Object } bucket_json
185
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
186
+ * @param {Object } lifecycle_rule
187
+ * @param {number } index
188
+ * @param {Object } bucket_json
189
189
* @param {nb.ObjectSDK } object_sdk
190
190
* @returns {Promise<Void> }
191
191
*/
@@ -233,14 +233,14 @@ async function process_rule(config_fs, lifecycle_rule, index, bucket_json, objec
233
233
234
234
/**
235
235
* abort_mpus iterates over the abort mpu candidates and calls abort_object_upload
236
- * since abort_object_upload is not returning anything, we catch it in case of an error and assign err_code
236
+ * since abort_object_upload is not returning anything, we catch it in case of an error and assign err_code
237
237
* so it can be translated to error on stats
238
238
* @param {* } candidates
239
- * @param {nb.ObjectSDK } object_sdk
239
+ * @param {nb.ObjectSDK } object_sdk
240
240
* @returns {Promise<Object[]> }
241
241
*/
242
242
async function abort_mpus ( candidates , object_sdk ) {
243
- const aborts_concurrency = 10 ; // TODO - think about it
243
+ const aborts_concurrency = 10 ; // TODO - think about it
244
244
const abort_mpus_reply = await P . map_with_concurrency ( aborts_concurrency , candidates . abort_mpu_candidates ,
245
245
async candidate => {
246
246
const candidate_info = { key : candidate . key , upload_id : candidate . obj_id } ;
@@ -261,13 +261,13 @@ async function abort_mpus(candidates, object_sdk) {
261
261
/////////////////////////////////
262
262
263
263
/**
264
- * _should_lifecycle_run checks if lifecycle worker should run based on the followings -
265
- * 1. lifecycle workrer can be disabled
264
+ * _should_lifecycle_run checks if lifecycle worker should run based on the followings -
265
+ * 1. lifecycle workrer can be disabled
266
266
* 2. lifecycle worker might run at time that does not match config.NC_LIFECYCLE_RUN_TIME
267
267
* 3. previous run was in the delay time frame
268
- * @param {nb.NativeFSContext } fs_context
269
- * @param {String } lifecycle_timestamp_file_path
270
- * @param {Boolean } disable_runtime_validation
268
+ * @param {nb.NativeFSContext } fs_context
269
+ * @param {String } lifecycle_timestamp_file_path
270
+ * @param {Boolean } disable_runtime_validation
271
271
* @returns {Promise<Boolean> }
272
272
*/
273
273
async function _should_lifecycle_run ( fs_context , lifecycle_timestamp_file_path , disable_runtime_validation ) {
@@ -284,7 +284,7 @@ async function _should_lifecycle_run(fs_context, lifecycle_timestamp_file_path,
284
284
285
285
/**
286
286
* throw_if_noobaa_not_active checks if system.json exists and the noobaa service is active
287
- * @param {import('../sdk/config_fs').ConfigFS } config_fs
287
+ * @param {import('../sdk/config_fs').ConfigFS } config_fs
288
288
* @param {Object } system_json
289
289
*/
290
290
async function throw_if_noobaa_not_active ( config_fs , system_json ) {
@@ -308,7 +308,7 @@ async function throw_if_noobaa_not_active(config_fs, system_json) {
308
308
async function get_candidates ( bucket_json , lifecycle_rule , object_sdk , fs_context ) {
309
309
const candidates = { abort_mpu_candidates : [ ] , delete_candidates : [ ] } ;
310
310
if ( lifecycle_rule . expiration ) {
311
- candidates . delete_candidates = await get_candidates_by_expiration_rule ( lifecycle_rule , bucket_json ) ;
311
+ candidates . delete_candidates = await get_candidates_by_expiration_rule ( lifecycle_rule , bucket_json , object_sdk ) ;
312
312
if ( lifecycle_rule . expiration . days || lifecycle_rule . expiration . expired_object_delete_marker ) {
313
313
const dm_candidates = await get_candidates_by_expiration_delete_marker_rule ( lifecycle_rule , bucket_json ) ;
314
314
candidates . delete_candidates = candidates . delete_candidates . concat ( dm_candidates ) ;
@@ -325,10 +325,11 @@ async function get_candidates(bucket_json, lifecycle_rule, object_sdk, fs_contex
325
325
return candidates ;
326
326
}
327
327
328
+
328
329
/**
329
330
* validate_rule_enabled checks if the rule is enabled and should be processed
330
- * @param {* } rule
331
- * @param {Object } bucket
331
+ * @param {* } rule
332
+ * @param {Object } bucket
332
333
* @returns {boolean }
333
334
*/
334
335
function validate_rule_enabled ( rule , bucket ) {
@@ -348,25 +349,37 @@ function validate_rule_enabled(rule, bucket) {
348
349
//////// EXPIRATION HELPERS ////////
349
350
////////////////////////////////////
350
351
352
+ /**
353
+ * @param {Object } entry list object entry
354
+ */
355
+ function _get_lifecycle_object_info_from_list_object_entry ( entry ) {
356
+ return {
357
+ key : entry . key ,
358
+ age : _get_file_age_days ( entry . create_time ) ,
359
+ size : entry . size ,
360
+ tags : entry . tagging ,
361
+ } ;
362
+ }
363
+
351
364
/**
352
365
* get_candidates_by_expiration_rule processes the expiration rule
353
- * @param {* } lifecycle_rule
354
- * @param {Object } bucket_json
366
+ * @param {* } lifecycle_rule
367
+ * @param {Object } bucket_json
355
368
* @returns {Promise<Object[]> }
356
369
*/
357
- async function get_candidates_by_expiration_rule ( lifecycle_rule , bucket_json ) {
370
+ async function get_candidates_by_expiration_rule ( lifecycle_rule , bucket_json , object_sdk ) {
358
371
const is_gpfs = nb_native ( ) . fs . gpfs ;
359
372
if ( is_gpfs ) {
360
373
return get_candidates_by_expiration_rule_gpfs ( lifecycle_rule , bucket_json ) ;
361
374
} else {
362
- return get_candidates_by_expiration_rule_posix ( lifecycle_rule , bucket_json ) ;
375
+ return get_candidates_by_expiration_rule_posix ( lifecycle_rule , bucket_json , object_sdk ) ;
363
376
}
364
377
}
365
378
366
379
/**
367
- *
368
- * @param {* } lifecycle_rule
369
- * @param {Object } bucket_json
380
+ *
381
+ * @param {* } lifecycle_rule
382
+ * @param {Object } bucket_json
370
383
* @returns {Promise<Object[]> }
371
384
*/
372
385
async function get_candidates_by_expiration_rule_gpfs ( lifecycle_rule , bucket_json ) {
@@ -375,20 +388,34 @@ async function get_candidates_by_expiration_rule_gpfs(lifecycle_rule, bucket_jso
375
388
}
376
389
377
390
/**
378
- *
379
- * @param {* } lifecycle_rule
391
+ *
392
+ * @param {* } lifecycle_rule
380
393
* @param {Object } bucket_json
381
- * @returns {Promise<Object[]> }
394
+ * @returns {Promise<Object[]> }
382
395
*/
383
- async function get_candidates_by_expiration_rule_posix ( lifecycle_rule , bucket_json ) {
384
- // TODO - implement
385
- return [ ] ;
396
+ async function get_candidates_by_expiration_rule_posix ( lifecycle_rule , bucket_json , object_sdk ) {
397
+ const expiration = _get_expiration_time ( lifecycle_rule . expiration ) ;
398
+ if ( expiration < 0 ) return [ ] ;
399
+ const filter_func = _build_lifecycle_filter ( { filter : lifecycle_rule . filter , expiration} ) ;
400
+
401
+ const filtered_objects = [ ] ;
402
+ // TODO list_objects does not accept a filter and works in batch sizes of 1000. should handle batching
403
+ // also should maybe create a helper function or add argument for a filter in list object
404
+ const objects_list = await object_sdk . list_objects ( { bucket : bucket_json . name , prefix : lifecycle_rule . filter ?. prefix } ) ;
405
+ objects_list . objects . forEach ( obj => {
406
+ const object_info = _get_lifecycle_object_info_from_list_object_entry ( obj ) ;
407
+ if ( filter_func ( object_info ) ) {
408
+ filtered_objects . push ( { key : object_info . key } ) ;
409
+ }
410
+ } ) ;
411
+ return filtered_objects ;
412
+
386
413
}
387
414
388
415
/**
389
416
* get_candidates_by_expiration_delete_marker_rule processes the expiration delete marker rule
390
- * @param {* } lifecycle_rule
391
- * @param {Object } bucket_json
417
+ * @param {* } lifecycle_rule
418
+ * @param {Object } bucket_json
392
419
* @returns {Promise<Object[]> }
393
420
*/
394
421
async function get_candidates_by_expiration_delete_marker_rule ( lifecycle_rule , bucket_json ) {
@@ -462,13 +489,13 @@ async function get_candidates_by_abort_incomplete_multipart_upload_rule(lifecycl
462
489
}
463
490
464
491
/**
465
- * @param {* } create_params_parsed
492
+ * @param {Object } create_params_parsed
466
493
* @param {nb.NativeFSStats } stat
467
494
*/
468
495
function _get_lifecycle_object_info_for_mpu ( create_params_parsed , stat ) {
469
496
return {
470
497
key : create_params_parsed . key ,
471
- age : _get_file_age_days ( stat ) ,
498
+ age : _get_file_age_days ( stat . mtime . getTime ( ) ) ,
472
499
tags : create_params_parsed . tagging ,
473
500
} ;
474
501
}
@@ -502,13 +529,32 @@ function _build_lifecycle_filter(params) {
502
529
503
530
/**
504
531
* get file time since last modified in days
505
- * @param {nb.NativeFSStats } stat
532
+ * @param {Number } mtime
533
+ * @returns {Number } days since object was last modified
506
534
*/
507
- function _get_file_age_days ( stat ) {
508
- //TODO how much do we care about rounding errors? (it is by days after all)
509
- return ( Date . now ( ) - Number ( stat . mtimeNsBigint ) / 1e6 ) / 24 / 60 / 60 / 1000 ;
535
+ function _get_file_age_days ( mtime ) {
536
+ return Math . floor ( ( Date . now ( ) - mtime ) / 24 / 60 / 60 / 1000 ) ;
510
537
}
511
538
539
+ /**
540
+ * get the expiration time in days of an object
541
+ * if rule is set with date, then rule is applied for all objects after that date
542
+ * return -1 to indicate that the date hasn't arrived, so rule should not be applied
543
+ * return 0 in case date has arrived so expiration is true for all elements
544
+ * return days in case days was defined and not date
545
+ * @param {Object } expiration_rule
546
+ * @returns {Number }
547
+ */
548
+ function _get_expiration_time ( expiration_rule ) {
549
+ if ( expiration_rule . date ) {
550
+ const expiration_date = new Date ( expiration_rule . date ) . getTime ( ) ;
551
+ if ( Date . now ( ) < expiration_date ) return - 1 ;
552
+ return 0 ;
553
+ }
554
+ return expiration_rule . days ;
555
+ }
556
+
557
+
512
558
/**
513
559
* checks if tag query_tag is in the list tag_set
514
560
* @param {Object } query_tag
@@ -545,7 +591,7 @@ function _file_contain_tags(object_info, filter_tags) {
545
591
* update_lifecycle_rules_last_sync updates the last sync time of the lifecycle rule
546
592
* @param {import('../sdk/config_fs').ConfigFS } config_fs
547
593
* @param {Object } bucket_json
548
- * @param {String } rule_id
594
+ * @param {String } rule_id
549
595
* @param {number } index
550
596
* @returns {Promise<Void> }
551
597
*/
@@ -601,14 +647,14 @@ async function _call_op_and_update_status({ bucket_name = undefined, rule_id = u
601
647
* 2. update times
602
648
* 3. update errors
603
649
* 4. update stats if the op is at rule level
604
- * @param {{
650
+ * @param {{
605
651
* op_name: string,
606
652
* bucket_name?: string,
607
- * rule_id?: string,
653
+ * rule_id?: string,
608
654
* op_times: { start_time?: number, end_time?: number, took_ms?: number },
609
655
* reply?: Object[],
610
656
* error?: Error}
611
- * } params
657
+ * } params
612
658
* @returns {Void }
613
659
*/
614
660
function update_status ( { bucket_name, rule_id, op_name, op_times, reply = [ ] , error = undefined } ) {
@@ -629,7 +675,7 @@ function update_status({ bucket_name, rule_id, op_name, op_times, reply = [], er
629
675
630
676
/**
631
677
* _calc_stats accumulates stats for global/bucket stats
632
- * @param {Object } stats_acc
678
+ * @param {Object } stats_acc
633
679
* @param {Object } [cur_op_stats]
634
680
* @returns {Object }
635
681
*/
@@ -649,17 +695,17 @@ function _acc_stats(stats_acc, cur_op_stats = {}) {
649
695
650
696
/**
651
697
* update_stats_on_status updates stats on rule context status and adds the rule status to the summarized bucket/global context stats
652
- * @param {{
653
- * op_name: string,
654
- * bucket_name: string,
655
- * rule_id: string,
656
- * op_times: {
657
- * start_time?: number,
658
- * end_time?: number,
659
- * took_ms?: number
698
+ * @param {{
699
+ * op_name: string,
700
+ * bucket_name: string,
701
+ * rule_id: string,
702
+ * op_times: {
703
+ * start_time?: number,
704
+ * end_time?: number,
705
+ * took_ms?: number
660
706
* },
661
707
* reply?: Object[],
662
- * }} params
708
+ * }} params
663
709
* @returns {Void }
664
710
*/
665
711
function update_stats_on_status ( { bucket_name, rule_id, op_name, op_times, reply = [ ] } ) {
@@ -689,9 +735,9 @@ function update_stats_on_status({ bucket_name, rule_id, op_name, op_times, reply
689
735
690
736
/**
691
737
* _update_times_on_status updates start/end & took times in lifecycle status
692
- * @param {{op_name: String, op_times: {start_time?: number, end_time?: number, took_ms?: number },
738
+ * @param {{op_name: String, op_times: {start_time?: number, end_time?: number, took_ms?: number },
693
739
* bucket_name?: String, rule_id?: String }} params
694
- * @returns
740
+ * @returns
695
741
*/
696
742
function _update_times_on_status ( { op_name, op_times, bucket_name = undefined , rule_id = undefined } ) {
697
743
for ( const [ key , value ] of Object . entries ( op_times ) ) {
@@ -709,7 +755,7 @@ function _update_times_on_status({ op_name, op_times, bucket_name = undefined, r
709
755
/**
710
756
* _update_error_on_status updates an error occured in lifecycle status
711
757
* @param {{error: Error, bucket_name?: string, rule_id?: string} } params
712
- * @returns
758
+ * @returns
713
759
*/
714
760
function _update_error_on_status ( { error, bucket_name = undefined , rule_id = undefined } ) {
715
761
if ( ! error ) return ;
0 commit comments