@@ -22,6 +22,7 @@ const OP_TO_EVENT = Object.freeze({
22
22
put_object_acl : { name : 'ObjectAcl' } ,
23
23
put_object_tagging : { name : 'ObjectTagging' } ,
24
24
delete_object_tagging : { name : 'ObjectTagging' } ,
25
+ lifecycle_delete : { name : 'LifecycleExpiration' } ,
25
26
} ) ;
26
27
27
28
class Notificator {
@@ -86,7 +87,7 @@ class Notificator {
86
87
seen_nodes . add ( node_namespace ) ;
87
88
}
88
89
dbg . log1 ( "process_notification_files node_namespace =" , node_namespace , ", file =" , entry . name ) ;
89
- const log = new PersistentLogger ( config . NOTIFICATION_LOG_DIR , node_namespace , { locking : 'EXCLUSIVE' } ) ;
90
+ const log = get_notification_logger ( 'EXCLUSIVE' , node_namespace ) ;
90
91
try {
91
92
await log . process ( async ( file , failure_append ) => await this . _notify ( this . fs_context , file , failure_append ) ) ;
92
93
} catch ( err ) {
@@ -310,52 +311,66 @@ async function test_notifications(bucket) {
310
311
}
311
312
}
312
313
313
- //see https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-content-structure.html
314
- function compose_notification ( req , res , bucket , notif_conf ) {
315
- let eTag = res . getHeader ( 'ETag' ) ;
316
- //eslint-disable-next-line
317
- if ( eTag && eTag . startsWith ( '\"' ) && eTag . endsWith ( '\"' ) ) {
318
- eTag = eTag . substring ( 2 , eTag . length - 2 ) ;
319
- }
314
+ function compose_notification_base ( notif_conf , bucket , req ) {
320
315
321
- const event = OP_TO_EVENT [ req . op_name ] ;
322
- const http_verb_capitalized = req . method . charAt ( 0 ) . toUpperCase ( ) + req . method . slice ( 1 ) . toLowerCase ( ) ;
323
316
const event_time = new Date ( ) ;
324
317
325
318
const notif = {
326
319
eventVersion : '2.3' ,
327
320
eventSource : _get_system_name ( req ) + ':s3' ,
328
321
eventTime : event_time . toISOString ( ) ,
329
- eventName : event . name + ':' + ( event . method || req . s3_event_method || http_verb_capitalized ) ,
330
- userIdentity : {
331
- principalId : req . object_sdk . requesting_account . name ,
332
- } ,
333
- requestParameters : {
334
- sourceIPAddress : http_utils . parse_client_ip ( req ) ,
335
- } ,
336
- responseElements : {
337
- "x-amz-request-id" : req . request_id ,
338
- "x-amz-id-2" : req . request_id ,
339
- } ,
322
+
340
323
s3 : {
341
324
s3SchemaVersion : "1.0" ,
342
325
configurationId : notif_conf . name ,
326
+ object : {
327
+ //default for sequencer, overriden in compose_notification_req for noobaa ns
328
+ sequencer : event_time . getTime ( ) . toString ( 16 ) ,
329
+ } ,
343
330
bucket : {
344
331
name : bucket . name ,
345
332
ownerIdentity : {
346
- principalId : bucket . bucket_owner . unwrap ( ) ,
333
+ //buckets from s3 reqs are sdk-style, from lifcycle are "raw" system store object
334
+ principalId : bucket . bucket_owner ? bucket . bucket_owner . unwrap ( ) : bucket . owner_account . name . unwrap ( ) ,
347
335
} ,
348
336
arn : "arn:aws:s3:::" + bucket . name ,
349
337
} ,
350
- object : {
351
- key : req . params . key ,
352
- size : res . getHeader ( 'content-length' ) ,
353
- eTag,
354
- versionId : res . getHeader ( 'x-amz-version-id' ) ,
355
- } ,
356
338
}
357
339
} ;
358
340
341
+ return notif ;
342
+
343
+ }
344
+
345
+ //see https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-content-structure.html
346
+ function compose_notification_req ( req , res , bucket , notif_conf ) {
347
+ let eTag = res . getHeader ( 'ETag' ) ;
348
+ //eslint-disable-next-line
349
+ if ( eTag && eTag . startsWith ( '\"' ) && eTag . endsWith ( '\"' ) ) {
350
+ eTag = eTag . substring ( 2 , eTag . length - 2 ) ;
351
+ }
352
+
353
+ const event = OP_TO_EVENT [ req . op_name ] ;
354
+ const http_verb_capitalized = req . method . charAt ( 0 ) . toUpperCase ( ) + req . method . slice ( 1 ) . toLowerCase ( ) ;
355
+
356
+ const notif = compose_notification_base ( notif_conf , bucket , req ) ;
357
+
358
+ notif . eventName = event . name + ':' + ( event . method || req . s3_event_method || http_verb_capitalized ) ;
359
+ notif . userIdentity = {
360
+ principalId : req . object_sdk . requesting_account . name ,
361
+ } ;
362
+ notif . requestParameters = {
363
+ sourceIPAddress : http_utils . parse_client_ip ( req ) ,
364
+ } ;
365
+ notif . responseElements = {
366
+ "x-amz-request-id" : req . request_id ,
367
+ "x-amz-id-2" : req . request_id ,
368
+ } ;
369
+ notif . s3 . object . key = req . params . key ;
370
+ notif . s3 . object . size = res . getHeader ( 'content-length' ) ;
371
+ notif . s3 . object . eTag = eTag ;
372
+ notif . s3 . object . versionId = res . getHeader ( 'x-amz-version-id' ) ;
373
+
359
374
//handle glacierEventData
360
375
if ( res . restore_object_result ) {
361
376
notif . glacierEventData = {
@@ -370,21 +385,41 @@ function compose_notification(req, res, bucket, notif_conf) {
370
385
if ( res . seq ) {
371
386
//in noobaa-ns we have a sequence from db
372
387
notif . s3 . object . sequencer = res . seq ;
373
- } else {
374
- //fallback to time-based sequence
375
- notif . s3 . object . sequencer = event_time . getTime ( ) . toString ( 16 ) ;
376
388
}
377
389
378
- const records = [ notif ] ;
379
-
380
- return { Records : records } ;
390
+ return compose_meta ( notif , notif_conf ) ;
381
391
}
382
392
393
+ function compose_notification_lifecycle ( deleted_obj , notif_conf , bucket ) {
383
394
395
+ const notif = compose_notification_base ( notif_conf , bucket ) ;
396
+
397
+ notif . eventName = OP_TO_EVENT . lifecycle_delete . name + ':' +
398
+ ( deleted_obj . created_delete_marker ? 'DeleteMarkerCreated' : 'Delete' ) ;
399
+ notif . s3 . object . key = deleted_obj . key ;
400
+ notif . s3 . object . size = deleted_obj . size ;
401
+ notif . s3 . object . eTag = deleted_obj . etag ;
402
+ notif . s3 . object . versionId = deleted_obj . version_id ;
403
+
404
+ return compose_meta ( notif , notif_conf ) ;
405
+
406
+ }
407
+
408
+ function compose_meta ( record , notif_conf ) {
409
+ return {
410
+ meta : {
411
+ connect : notif_conf . Connect ,
412
+ name : notif_conf . Id
413
+ } ,
414
+ notif : {
415
+ Records : [ record ] ,
416
+ }
417
+ } ;
418
+ }
384
419
385
420
function _get_system_name ( req ) {
386
421
387
- if ( req . object_sdk . nsfs_config_root ) {
422
+ if ( req && req . object_sdk && req . object_sdk . nsfs_system ) {
388
423
const name = Object . keys ( req . object_sdk . nsfs_system ) [ 0 ] ;
389
424
return name ;
390
425
} else {
@@ -427,7 +462,26 @@ function check_notif_relevant(notif, req) {
427
462
return false ;
428
463
}
429
464
465
+ /**
466
+ *
467
+ * @param {"SHARED" | "EXCLUSIVE" } locking counterintuitively, either 'SHARED' for writing or 'EXCLUSIVE' for reading
468
+ */
469
+ function get_notification_logger ( locking , namespace , poll_interval ) {
470
+ if ( ! namespace ) {
471
+ const node_name = process . env . NODE_NAME || os . hostname ( ) ;
472
+ namespace = node_name + '_' + config . NOTIFICATION_LOG_NS ;
473
+ }
474
+
475
+ return new PersistentLogger ( config . NOTIFICATION_LOG_DIR , namespace , {
476
+ locking,
477
+ poll_interval,
478
+ } ) ;
479
+ }
480
+
430
481
exports . Notificator = Notificator ;
431
482
exports . test_notifications = test_notifications ;
432
- exports . compose_notification = compose_notification ;
483
+ exports . compose_notification_req = compose_notification_req ;
484
+ exports . compose_notification_lifecycle = compose_notification_lifecycle ;
433
485
exports . check_notif_relevant = check_notif_relevant ;
486
+ exports . get_notification_logger = get_notification_logger ;
487
+ exports . OP_TO_EVENT = OP_TO_EVENT ;
0 commit comments