Skip to content

Commit

Permalink
Merge pull request noobaa#8219 from shirady/nsfs-iam-list-users-gap
Browse files Browse the repository at this point in the history
NSFS | NC | IAM Gap - Handle Concurrency When Reading Entries and They Deleted by Another Process
  • Loading branch information
shirady authored Jul 22, 2024
2 parents a6e8837 + 818d67c commit 7d78bac
Showing 1 changed file with 31 additions and 6 deletions.
37 changes: 31 additions & 6 deletions src/sdk/accountspace_fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const IamError = require('../endpoint/iam/iam_errors').IamError;
const cloud_utils = require('../util/cloud_utils');
const SensitiveString = require('../util/sensitive_string');
const { get_symlink_config_file_path, get_config_file_path, get_config_data,
generate_id } = require('../manage_nsfs/manage_nsfs_cli_utils');
get_config_data_if_exists, generate_id } = require('../manage_nsfs/manage_nsfs_cli_utils');
const nc_mkm = require('../manage_nsfs/nc_master_key_manager').get_instance();
const { account_cache } = require('./object_sdk');

Expand Down Expand Up @@ -494,6 +494,30 @@ class AccountSpaceFS {
return data;
}

/**
* _get_account_decrypted_data_optional_if_exists will read a config file and return its content
* if the config file was deleted (encounter ENOENT error) - continue (returns undefined)
*
* Notes: this function is important when dealing with concurrency.
* When we iterate files (for example for listing them) between the time we read the entries
* from the directory and the time we we are trying to read the config file,
* a file might be deleted (by another process), and we would not want to throw this error
* as a part of iterating the file, therefore we continue
* (not throwing this error and return undefined)
*
* @param {string} account_path
* @param {boolean} should_decrypt_secret_key
*/
async _get_account_decrypted_data_optional_if_exists(account_path, should_decrypt_secret_key) {
try {
const data = await this._get_account_decrypted_data_optional(account_path, should_decrypt_secret_key);
return data;
} catch (err) {
dbg.warn('_get_account_decrypted_data_optional_if_exists: with config_file_path', account_path, 'got an error', err);
if (err.code !== 'ENOENT') throw err;
}
}

_new_user_defaults(requesting_account, params, master_key_id) {
const distinguished_name = requesting_account.nsfs_account_config.distinguished_name;
const user_defaults = {
Expand Down Expand Up @@ -597,11 +621,11 @@ class AccountSpaceFS {
const entries = await nb_native().fs.readdir(this.fs_context, this.accounts_dir);
const should_filter_by_prefix = check_iam_path_was_set(iam_path_prefix);

// TODO - add silent get config to handle config deletion during list (concurrency)
const config_files_list = await P.map_with_concurrency(10, entries, async entry => {
if (entry.name.endsWith('.json')) {
const full_path = path.join(this.accounts_dir, entry.name);
const account_data = await this._get_account_decrypted_data_optional(full_path, false);
const account_data = await this._get_account_decrypted_data_optional_if_exists(full_path, false);
if (!account_data) return undefined;
if (entry.name.includes(config.NSFS_TEMP_CONF_DIR_NAME)) return undefined;
const is_root_account_owns_user = this._check_root_account_owns_user(requesting_account, account_data);
if ((!requesting_account.iam_operate_on_root_account && is_root_account_owns_user) ||
Expand Down Expand Up @@ -718,8 +742,8 @@ class AccountSpaceFS {
await P.map_with_concurrency(10, entries, async entry => {
if (entry.name.endsWith('.json')) {
const full_path = path.join(this.buckets_dir, entry.name);
const bucket_data = await get_config_data(this.config_root_backend, full_path, false);
if (bucket_data.bucket_owner === account_to_delete.name) {
const bucket_data = await get_config_data_if_exists(this.config_root_backend, full_path, false);
if (bucket_data && bucket_data.bucket_owner === account_to_delete.name) {
this._throw_error_delete_conflict(action, account_to_delete, resource_name);
}
return bucket_data;
Expand All @@ -735,7 +759,8 @@ class AccountSpaceFS {
await P.map_with_concurrency(10, entries, async entry => {
if (entry.name.endsWith('.json')) {
const full_path = path.join(this.accounts_dir, entry.name);
const account_data = await this._get_account_decrypted_data_optional(full_path, false);
const account_data = await this._get_account_decrypted_data_optional_if_exists(full_path, false);
if (!account_data) return undefined;
if (entry.name.includes(config.NSFS_TEMP_CONF_DIR_NAME)) return undefined;
const is_root_account_owns_user = this._check_root_account_owns_user(account_to_delete, account_data);
if ((!account_to_delete.iam_operate_on_root_account && is_root_account_owns_user) ||
Expand Down

0 comments on commit 7d78bac

Please sign in to comment.