@@ -40,6 +40,8 @@ const { throw_cli_error, get_bucket_owner_account_by_name,
40
40
const manage_nsfs_validations = require ( '../manage_nsfs/manage_nsfs_validations' ) ;
41
41
const nc_mkm = require ( '../manage_nsfs/nc_master_key_manager' ) . get_instance ( ) ;
42
42
const notifications_util = require ( '../util/notifications_util' ) ;
43
+ const BucketSpaceFS = require ( '../sdk/bucketspace_fs' ) ;
44
+ const NoobaaEvent = require ( '../manage_nsfs/manage_nsfs_events_utils' ) . NoobaaEvent ;
43
45
44
46
let config_fs ;
45
47
@@ -123,7 +125,6 @@ async function fetch_bucket_data(action, user_input) {
123
125
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 ) ,
124
126
notifications : user_input . notifications
125
127
} ;
126
-
127
128
if ( user_input . bucket_policy !== undefined ) {
128
129
if ( typeof user_input . bucket_policy === 'string' ) {
129
130
// bucket_policy deletion specified with empty string ''
@@ -142,6 +143,27 @@ async function fetch_bucket_data(action, user_input) {
142
143
data = await merge_new_and_existing_config_data ( data ) ;
143
144
}
144
145
146
+ if ( ( action === ACTIONS . UPDATE && user_input . tag ) || ( action === ACTIONS . ADD ) ) {
147
+ const tags = JSON . parse ( user_input . tag || '[]' ) ;
148
+ data . tag = BucketSpaceFS . _merge_reserved_tags (
149
+ data . tag || BucketSpaceFS . _default_bucket_tags ( ) ,
150
+ tags ,
151
+ action === ACTIONS . ADD ? true : await _is_bucket_empty ( data ) ,
152
+ ) ;
153
+ }
154
+
155
+ if ( ( action === ACTIONS . UPDATE && user_input . merge_tag ) || ( action === ACTIONS . ADD ) ) {
156
+ const merge_tags = JSON . parse ( user_input . merge_tag || '[]' ) ;
157
+ data . tag = _ . merge (
158
+ data . tag ,
159
+ BucketSpaceFS . _merge_reserved_tags (
160
+ data . tag || BucketSpaceFS . _default_bucket_tags ( ) ,
161
+ merge_tags ,
162
+ action === ACTIONS . ADD ? true : await _is_bucket_empty ( data ) ,
163
+ )
164
+ ) ;
165
+ }
166
+
145
167
//if we're updating the owner, needs to override owner in file with the owner from user input.
146
168
//if we're adding a bucket, need to set its owner id field
147
169
if ( ( action === ACTIONS . UPDATE && user_input . owner ) || ( action === ACTIONS . ADD ) ) {
@@ -189,7 +211,14 @@ async function add_bucket(data) {
189
211
data . _id = mongo_utils . mongoObjectId ( ) ;
190
212
const parsed_bucket_data = await config_fs . create_bucket_config_file ( data ) ;
191
213
await set_bucker_owner ( parsed_bucket_data ) ;
192
- return { code : ManageCLIResponse . BucketCreated , detail : parsed_bucket_data , event_arg : { bucket : data . name } } ;
214
+
215
+ const [ reserved_tag_event_args ] = BucketSpaceFS . _generate_reserved_tag_event_args ( { } , data . tag ) ;
216
+
217
+ return {
218
+ code : ManageCLIResponse . BucketCreated ,
219
+ detail : parsed_bucket_data ,
220
+ event_arg : { ...( reserved_tag_event_args || { } ) , bucket : data . name , account : parsed_bucket_data . bucket_owner } ,
221
+ } ;
193
222
}
194
223
195
224
/**
@@ -245,25 +274,14 @@ async function update_bucket(data) {
245
274
*/
246
275
async function delete_bucket ( data , force ) {
247
276
try {
248
- const temp_dir_name = native_fs_utils . get_bucket_tmpdir_name ( data . _id ) ;
277
+ const bucket_empty = await _is_bucket_empty ( data ) ;
278
+ if ( ! bucket_empty && ! force ) {
279
+ throw_cli_error ( ManageCLIError . BucketDeleteForbiddenHasObjects , data . name ) ;
280
+ }
281
+
249
282
const bucket_temp_dir_path = native_fs_utils . get_bucket_tmpdir_full_path ( data . path , data . _id ) ;
250
- // fs_contexts for bucket temp dir (storage path)
251
283
const fs_context_fs_backend = native_fs_utils . get_process_fs_context ( data . fs_backend ) ;
252
- let entries ;
253
- try {
254
- entries = await nb_native ( ) . fs . readdir ( fs_context_fs_backend , data . path ) ;
255
- } catch ( err ) {
256
- dbg . warn ( `delete_bucket: bucket name ${ data . name } ,` +
257
- `got an error on readdir with path: ${ data . path } ` , err ) ;
258
- // if the bucket's path was deleted first (encounter ENOENT error) - continue deletion
259
- if ( err . code !== 'ENOENT' ) throw err ;
260
- }
261
- if ( entries ) {
262
- const object_entries = entries . filter ( element => ! element . name . endsWith ( temp_dir_name ) ) ;
263
- if ( object_entries . length > 0 && ! force ) {
264
- throw_cli_error ( ManageCLIError . BucketDeleteForbiddenHasObjects , data . name ) ;
265
- }
266
- }
284
+
267
285
await native_fs_utils . folder_delete ( bucket_temp_dir_path , fs_context_fs_backend , true ) ;
268
286
await config_fs . delete_bucket_config_file ( data . name ) ;
269
287
return { code : ManageCLIResponse . BucketDeleted , detail : { name : data . name } , event_arg : { bucket : data . name } } ;
@@ -273,6 +291,33 @@ async function delete_bucket(data, force) {
273
291
}
274
292
}
275
293
294
+ /**
295
+ * _is_bucket_empty returns true if the given bucket is empty
296
+ *
297
+ * @param {* } data
298
+ * @returns {Promise<boolean> }
299
+ */
300
+ async function _is_bucket_empty ( data ) {
301
+ const temp_dir_name = native_fs_utils . get_bucket_tmpdir_name ( data . _id ) ;
302
+ // fs_contexts for bucket temp dir (storage path)
303
+ const fs_context_fs_backend = native_fs_utils . get_process_fs_context ( data . fs_backend ) ;
304
+ let entries ;
305
+ try {
306
+ entries = await nb_native ( ) . fs . readdir ( fs_context_fs_backend , data . path ) ;
307
+ } catch ( err ) {
308
+ dbg . warn ( `_is_bucket_empty: bucket name ${ data . name } ,` +
309
+ `got an error on readdir with path: ${ data . path } ` , err ) ;
310
+ // if the bucket's path was deleted first (encounter ENOENT error) - continue deletion
311
+ if ( err . code !== 'ENOENT' ) throw err ;
312
+ }
313
+ if ( entries ) {
314
+ const object_entries = entries . filter ( element => ! element . name . endsWith ( temp_dir_name ) ) ;
315
+ return object_entries . length === 0 ;
316
+ }
317
+
318
+ return true ;
319
+ }
320
+
276
321
/**
277
322
* bucket_management does the following -
278
323
* 1. fetches the bucket data if this is not a list operation
@@ -294,7 +339,24 @@ async function bucket_management(action, user_input) {
294
339
} else if ( action === ACTIONS . STATUS ) {
295
340
response = await get_bucket_status ( data ) ;
296
341
} else if ( action === ACTIONS . UPDATE ) {
297
- response = await update_bucket ( data ) ;
342
+ const bucket_path = config_fs . get_bucket_path_by_name ( user_input . name ) ;
343
+ const bucket_lock_file = `${ bucket_path } .lock` ;
344
+ await native_fs_utils . lock_and_run ( config_fs . fs_context , bucket_lock_file , async ( ) => {
345
+ const prev_bucket_info = await fetch_bucket_data ( action , _ . omit ( user_input , [ 'tag' , 'merge_tag' ] ) ) ;
346
+ const bucket_info = await fetch_bucket_data ( action , user_input ) ;
347
+
348
+ const tagging_object = BucketSpaceFS . _objectify_tagging_arr ( prev_bucket_info . tag ) ;
349
+ const [
350
+ reserved_tag_event_args ,
351
+ reserved_tag_modified ,
352
+ ] = BucketSpaceFS . _generate_reserved_tag_event_args ( tagging_object , bucket_info . tag ) ;
353
+
354
+ response = await update_bucket ( bucket_info ) ;
355
+ if ( reserved_tag_modified ) {
356
+ new NoobaaEvent ( NoobaaEvent . BUCKET_RESERVED_TAG_MODIFIED )
357
+ . create_event ( undefined , { ...reserved_tag_event_args , bucket_name : user_input . name } ) ;
358
+ }
359
+ } ) ;
298
360
} else if ( action === ACTIONS . DELETE ) {
299
361
const force = get_boolean_or_string_value ( user_input . force ) ;
300
362
response = await delete_bucket ( data , force ) ;
0 commit comments