@@ -39,6 +39,8 @@ const { throw_cli_error, get_bucket_owner_account_by_name,
39
39
const manage_nsfs_validations = require ( '../manage_nsfs/manage_nsfs_validations' ) ;
40
40
const nc_mkm = require ( '../manage_nsfs/nc_master_key_manager' ) . get_instance ( ) ;
41
41
const notifications_util = require ( '../util/notifications_util' ) ;
42
+ const BucketSpaceFS = require ( '../sdk/bucketspace_fs' ) ;
43
+ const NoobaaEvent = require ( '../manage_nsfs/manage_nsfs_events_utils' ) . NoobaaEvent ;
42
44
43
45
///////////////
44
46
//// GENERAL //
@@ -135,7 +137,6 @@ async function fetch_bucket_data(action, user_input) {
135
137
force_md5_etag : user_input . force_md5_etag === undefined || user_input . force_md5_etag === '' ? user_input . force_md5_etag : get_boolean_or_string_value ( user_input . force_md5_etag ) ,
136
138
notifications : user_input . notifications
137
139
} ;
138
-
139
140
if ( user_input . bucket_policy !== undefined ) {
140
141
if ( typeof user_input . bucket_policy === 'string' ) {
141
142
// bucket_policy deletion specified with empty string ''
@@ -154,6 +155,27 @@ async function fetch_bucket_data(action, user_input) {
154
155
data = await merge_new_and_existing_config_data ( data ) ;
155
156
}
156
157
158
+ if ( ( action === ACTIONS . UPDATE && user_input . tag ) || ( action === ACTIONS . ADD ) ) {
159
+ const tags = JSON . parse ( user_input . tag || '[]' ) ;
160
+ data . tag = BucketSpaceFS . _merge_reserved_tags (
161
+ data . tag || BucketSpaceFS . _default_bucket_tags ( ) ,
162
+ tags ,
163
+ action === ACTIONS . ADD ? true : await _is_bucket_empty ( data ) ,
164
+ ) ;
165
+ }
166
+
167
+ if ( ( action === ACTIONS . UPDATE && user_input . merge_tag ) || ( action === ACTIONS . ADD ) ) {
168
+ const merge_tags = JSON . parse ( user_input . merge_tag || '[]' ) ;
169
+ data . tag = _ . merge (
170
+ data . tag ,
171
+ BucketSpaceFS . _merge_reserved_tags (
172
+ data . tag || BucketSpaceFS . _default_bucket_tags ( ) ,
173
+ merge_tags ,
174
+ action === ACTIONS . ADD ? true : await _is_bucket_empty ( data ) ,
175
+ )
176
+ ) ;
177
+ }
178
+
157
179
//if we're updating the owner, needs to override owner in file with the owner from user input.
158
180
//if we're adding a bucket, need to set its owner id field
159
181
if ( ( action === ACTIONS . UPDATE && user_input . owner ) || ( action === ACTIONS . ADD ) ) {
@@ -200,7 +222,24 @@ async function add_bucket(data) {
200
222
data . _id = mongo_utils . mongoObjectId ( ) ;
201
223
const parsed_bucket_data = await config_fs . create_bucket_config_file ( data ) ;
202
224
await set_bucker_owner ( parsed_bucket_data ) ;
203
- return { code : ManageCLIResponse . BucketCreated , detail : parsed_bucket_data , event_arg : { bucket : data . name } } ;
225
+
226
+ const reserved_tag_event_args = data . tag ?. reduce ( ( curr , tag ) => {
227
+ const tag_info = config . NSFS_GLACIER_RESERVED_BUCKET_TAGS [ tag . key ] ;
228
+
229
+ // If not a reserved tag - skip
230
+ if ( ! tag_info ) return curr ;
231
+
232
+ // If no event is requested - skip
233
+ if ( ! tag_info . event ) return curr ;
234
+
235
+ return Object . assign ( curr , { [ tag . key ] : tag . value } ) ;
236
+ } , { } ) ;
237
+
238
+ return {
239
+ code : ManageCLIResponse . BucketCreated ,
240
+ detail : parsed_bucket_data ,
241
+ event_arg : { ...( reserved_tag_event_args || { } ) , bucket : data . name , account : parsed_bucket_data . bucket_owner } ,
242
+ } ;
204
243
}
205
244
206
245
/**
@@ -256,25 +295,14 @@ async function update_bucket(data) {
256
295
*/
257
296
async function delete_bucket ( data , force ) {
258
297
try {
259
- const temp_dir_name = native_fs_utils . get_bucket_tmpdir_name ( data . _id ) ;
298
+ const bucket_empty = await _is_bucket_empty ( data ) ;
299
+ if ( ! bucket_empty && ! force ) {
300
+ throw_cli_error ( ManageCLIError . BucketDeleteForbiddenHasObjects , data . name ) ;
301
+ }
302
+
260
303
const bucket_temp_dir_path = native_fs_utils . get_bucket_tmpdir_full_path ( data . path , data . _id ) ;
261
- // fs_contexts for bucket temp dir (storage path)
262
304
const fs_context_fs_backend = native_fs_utils . get_process_fs_context ( data . fs_backend ) ;
263
- let entries ;
264
- try {
265
- entries = await nb_native ( ) . fs . readdir ( fs_context_fs_backend , data . path ) ;
266
- } catch ( err ) {
267
- dbg . warn ( `delete_bucket: bucket name ${ data . name } ,` +
268
- `got an error on readdir with path: ${ data . path } ` , err ) ;
269
- // if the bucket's path was deleted first (encounter ENOENT error) - continue deletion
270
- if ( err . code !== 'ENOENT' ) throw err ;
271
- }
272
- if ( entries ) {
273
- const object_entries = entries . filter ( element => ! element . name . endsWith ( temp_dir_name ) ) ;
274
- if ( object_entries . length > 0 && ! force ) {
275
- throw_cli_error ( ManageCLIError . BucketDeleteForbiddenHasObjects , data . name ) ;
276
- }
277
- }
305
+
278
306
await native_fs_utils . folder_delete ( bucket_temp_dir_path , fs_context_fs_backend , true ) ;
279
307
await config_fs . delete_bucket_config_file ( data . name ) ;
280
308
return { code : ManageCLIResponse . BucketDeleted , detail : { name : data . name } , event_arg : { bucket : data . name } } ;
@@ -340,6 +368,33 @@ async function list_bucket_config_files(wide, filters = {}) {
340
368
return config_files_list ;
341
369
}
342
370
371
+ /**
372
+ * _is_bucket_empty returns true if the given bucket is empty
373
+ *
374
+ * @param {* } data
375
+ * @returns {Promise<boolean> }
376
+ */
377
+ async function _is_bucket_empty ( data ) {
378
+ const temp_dir_name = native_fs_utils . get_bucket_tmpdir_name ( data . _id ) ;
379
+ // fs_contexts for bucket temp dir (storage path)
380
+ const fs_context_fs_backend = native_fs_utils . get_process_fs_context ( data . fs_backend ) ;
381
+ let entries ;
382
+ try {
383
+ entries = await nb_native ( ) . fs . readdir ( fs_context_fs_backend , data . path ) ;
384
+ } catch ( err ) {
385
+ dbg . warn ( `_is_bucket_empty: bucket name ${ data . name } ,` +
386
+ `got an error on readdir with path: ${ data . path } ` , err ) ;
387
+ // if the bucket's path was deleted first (encounter ENOENT error) - continue deletion
388
+ if ( err . code !== 'ENOENT' ) throw err ;
389
+ }
390
+ if ( entries ) {
391
+ const object_entries = entries . filter ( element => ! element . name . endsWith ( temp_dir_name ) ) ;
392
+ return object_entries . length === 0 ;
393
+ }
394
+
395
+ return true ;
396
+ }
397
+
343
398
/**
344
399
* bucket_management does the following -
345
400
* 1. fetches the bucket data if this is not a list operation
@@ -361,7 +416,37 @@ async function bucket_management(action, user_input) {
361
416
} else if ( action === ACTIONS . STATUS ) {
362
417
response = await get_bucket_status ( data ) ;
363
418
} else if ( action === ACTIONS . UPDATE ) {
364
- response = await update_bucket ( data ) ;
419
+ const bucket_path = config_fs . get_bucket_path_by_name ( user_input . name ) ;
420
+ const bucket_lock_file = `${ bucket_path } .lock` ;
421
+ await native_fs_utils . lock_and_run ( config_fs . fs_context , bucket_lock_file , async ( ) => {
422
+ const prev_bucket_info = await fetch_bucket_data ( action , _ . omit ( user_input , [ 'tag' , 'merge_tag' ] ) ) ;
423
+ const bucket_info = await fetch_bucket_data ( action , user_input ) ;
424
+
425
+ const tagging_object = prev_bucket_info . tag ?. reduce ( ( curr , tag ) => Object . assign ( curr , { [ tag . key ] : tag . value } ) , { } ) ;
426
+
427
+ let reserved_tag_modified = false ;
428
+ const reserved_tag_event_args = bucket_info . tag ?. reduce ( ( curr , tag ) => {
429
+ const tag_info = config . NSFS_GLACIER_RESERVED_BUCKET_TAGS [ tag . key ] ;
430
+
431
+ // If not a reserved tag - skip
432
+ if ( ! tag_info ) return curr ;
433
+
434
+ // If no event is requested - skip
435
+ if ( ! tag_info . event ) return curr ;
436
+
437
+ // If value didn't change - skip
438
+ if ( _ . isEqual ( tagging_object [ tag . key ] , tag . value ) ) return curr ;
439
+
440
+ reserved_tag_modified = true ;
441
+ return Object . assign ( curr , { [ tag . key ] : tag . value } ) ;
442
+ } , { } ) ;
443
+
444
+ response = await update_bucket ( bucket_info ) ;
445
+ if ( reserved_tag_modified ) {
446
+ new NoobaaEvent ( NoobaaEvent . BUCKET_RESERVED_TAG_MODIFIED )
447
+ . create_event ( undefined , { ...reserved_tag_event_args , bucket_name : user_input . name } ) ;
448
+ }
449
+ } ) ;
365
450
} else if ( action === ACTIONS . DELETE ) {
366
451
const force = get_boolean_or_string_value ( user_input . force ) ;
367
452
response = await delete_bucket ( data , force ) ;
@@ -729,6 +814,7 @@ async function set_bucker_owner(bucket_data) {
729
814
} catch ( err ) {
730
815
dbg . warn ( `set_bucker_owner.couldn't find bucket owner data by id ${ bucket_data . owner_account } ` ) ;
731
816
}
817
+ console . log ( account_data ) ;
732
818
bucket_data . bucket_owner = account_data ?. name ;
733
819
}
734
820
0 commit comments