Skip to content

Commit a53a446

Browse files
shiradyromayalon
authored andcommitted
NSFS | NC | Handle concurrency when reading entries and they deleted by another process
1. fix concurrency issue on delete account (when a bucket was deleted during deletion) 2. fix concurrency issue on list buckets (when a bucket was deleted during deletion) 3. fix concurrency issue on s3 api list buckets (when a bucket was deleted during deletion) 4. add TODO in accountspace_fs 5. add test (not related to the issue, was added during debug) Signed-off-by: shirady <[email protected]> (cherry picked from commit c42e64d)
1 parent 920e7e1 commit a53a446

File tree

5 files changed

+44
-6
lines changed

5 files changed

+44
-6
lines changed

src/cmd/manage_nsfs.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ const { print_usage } = require('../manage_nsfs/manage_nsfs_help_utils');
2020
const { TYPES, ACTIONS, LIST_ACCOUNT_FILTERS, LIST_BUCKET_FILTERS,
2121
GLACIER_ACTIONS } = require('../manage_nsfs/manage_nsfs_constants');
2222
const { throw_cli_error, write_stdout_response, get_config_file_path, get_symlink_config_file_path,
23-
get_config_data, get_boolean_or_string_value} = require('../manage_nsfs/manage_nsfs_cli_utils');
23+
get_config_data, get_boolean_or_string_value, get_config_data_if_exists } = require('../manage_nsfs/manage_nsfs_cli_utils');
2424
const { validate_input_types, validate_bucket_args, validate_account_args,
2525
verify_delete_account, validate_whitelist_arg, verify_whitelist_ips,
2626
_validate_access_keys } = require('../manage_nsfs/manage_nsfs_validations');
@@ -613,7 +613,8 @@ async function list_config_files(type, config_path, wide, show_secrets, filters)
613613
if (entry.name.endsWith('.json')) {
614614
if (wide || should_filter) {
615615
const full_path = path.join(config_path, entry.name);
616-
const data = await get_config_data(config_root_backend, full_path, show_secrets || should_filter);
616+
const data = await get_config_data_if_exists(config_root_backend, full_path, show_secrets || should_filter);
617+
if (!data) return undefined;
617618
// decryption causing mkm initalization
618619
// decrypt only if data has access_keys and show_secrets = true (no need to decrypt if show_secrets = false but should_filter = true)
619620
if (data.access_keys && show_secrets) data.access_keys = await nc_mkm.decrypt_access_keys(data);
@@ -626,6 +627,7 @@ async function list_config_files(type, config_path, wide, show_secrets, filters)
626627
}
627628
});
628629
// it inserts undefined for the entry '.noobaa-config-nsfs' and we wish to remove it
630+
// in case the entry was deleted during the list it also inserts undefined
629631
config_files_list = config_files_list.filter(item => item);
630632

631633
return config_files_list;

src/manage_nsfs/manage_nsfs_cli_utils.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Copyright (C) 2024 NooBaa */
22
'use strict';
33

4+
const dbg = require('../util/debug_module')(__filename);
45
const _ = require('lodash');
56
const path = require('path');
67
const nb_native = require('../util/nb_native');
@@ -53,6 +54,23 @@ async function get_config_data(config_root_backend, config_file_path, show_secre
5354
return config_data;
5455
}
5556

57+
/**
58+
* get_config_data_if_exists will read a config file and return its content
59+
* while omitting secrets if show_secrets flag was not provided
60+
* if the config file was deleted (encounter ENOENT error) - continue (returns undefined)
61+
* @param {string} config_file_path
62+
* @param {boolean} [show_secrets]
63+
*/
64+
async function get_config_data_if_exists(config_root_backend, config_file_path, show_secrets = false) {
65+
try {
66+
const config_data = await get_config_data(config_root_backend, config_file_path, show_secrets);
67+
return config_data;
68+
} catch (err) {
69+
dbg.warn('get_config_data_if_exists: with config_file_path', config_file_path, 'got an error', err);
70+
if (err.code !== 'ENOENT') throw err;
71+
}
72+
}
73+
5674
/**
5775
* get_bucket_owner_account will return the account of the bucket_owner
5876
* otherwise it would throw an error
@@ -118,3 +136,5 @@ exports.get_boolean_or_string_value = get_boolean_or_string_value;
118136
exports.get_config_data = get_config_data;
119137
exports.get_bucket_owner_account = get_bucket_owner_account;
120138
exports.get_options_from_file = get_options_from_file;
139+
exports.get_config_data_if_exists = get_config_data_if_exists;
140+

src/manage_nsfs/manage_nsfs_validations.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const native_fs_utils = require('../util/native_fs_utils');
1212
const ManageCLIError = require('../manage_nsfs/manage_nsfs_cli_errors').ManageCLIError;
1313
const bucket_policy_utils = require('../endpoint/s3/s3_bucket_policy_utils');
1414
const { throw_cli_error, get_config_file_path, get_bucket_owner_account,
15-
get_config_data, get_options_from_file } = require('../manage_nsfs/manage_nsfs_cli_utils');
15+
get_options_from_file, get_config_data_if_exists } = require('../manage_nsfs/manage_nsfs_cli_utils');
1616
const { TYPES, ACTIONS, VALID_OPTIONS, OPTION_TYPE, FROM_FILE, BOOLEAN_STRING_VALUES,
1717
GLACIER_ACTIONS, LIST_UNSETABLE_OPTIONS } = require('../manage_nsfs/manage_nsfs_constants');
1818

@@ -335,11 +335,12 @@ function _validate_access_keys(access_key, secret_key) {
335335
async function verify_delete_account(config_root_backend, buckets_dir_path, account_name) {
336336
const fs_context = native_fs_utils.get_process_fs_context(config_root_backend);
337337
const entries = await nb_native().fs.readdir(fs_context, buckets_dir_path);
338+
let data;
338339
await P.map_with_concurrency(10, entries, async entry => {
339340
if (entry.name.endsWith('.json')) {
340341
const full_path = path.join(buckets_dir_path, entry.name);
341-
const data = await get_config_data(config_root_backend, full_path);
342-
if (data.bucket_owner === account_name) {
342+
data = await get_config_data_if_exists(config_root_backend, full_path);
343+
if (data && data.bucket_owner === account_name) {
343344
const detail_msg = `Account ${account_name} has bucket ${data.name}`;
344345
throw_cli_error(ManageCLIError.AccountDeleteForbiddenHasBuckets, detail_msg);
345346
}

src/sdk/bucketspace_fs.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,15 @@ class BucketSpaceFS extends BucketSpaceSimpleFS {
228228
return;
229229
}
230230
const bucket_name = this.get_bucket_name(entry.name);
231-
const bucket = await object_sdk.read_bucket_sdk_config_info(bucket_name);
231+
let bucket;
232+
try {
233+
bucket = await object_sdk.read_bucket_sdk_config_info(bucket_name);
234+
} catch (err) {
235+
dbg.warn('list_buckets: read_bucket_sdk_config_info of bucket', bucket_name, 'got an error', err);
236+
// in case the config file was deleted during the bucket list - we will continue
237+
if (err.rpc_code !== 'NO_SUCH_BUCKET') throw err;
238+
}
239+
if (!bucket) return;
232240
const bucket_policy_accessible = await this.has_bucket_action_permission(bucket, account, 's3:ListBucket');
233241
if (!bucket_policy_accessible) return;
234242
const fs_accessible = await this.validate_fs_bucket_access(bucket, object_sdk);

src/test/unit_tests/jest_tests/test_nc_nsfs_account_cli.test.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -925,6 +925,13 @@ describe('manage nsfs cli account flow', () => {
925925
expect(JSON.parse(res.stdout).error.code).toBe(ManageCLIError.MissingIdentifier.code);
926926
});
927927

928+
it('cli list filter by access key (non existing) and name (of account3) - (none)', async () => {
929+
const account_options = { config_root, name: 'account3', access_key: 'non-existing-access-key' };
930+
const action = ACTIONS.LIST;
931+
const res = await exec_manage_cli(TYPES.ACCOUNT, action, account_options);
932+
expect(JSON.parse(res).response.reply.map(item => item.name))
933+
.toEqual([]);
934+
});
928935
});
929936

930937
describe('cli delete account', () => {

0 commit comments

Comments
 (0)