diff --git a/src/test/system_tests/test_utils.js b/src/test/system_tests/test_utils.js index 5fe9c0d561..b5a3b8929f 100644 --- a/src/test/system_tests/test_utils.js +++ b/src/test/system_tests/test_utils.js @@ -482,15 +482,15 @@ function get_new_buckets_path_by_test_env(new_buckets_full_path, new_buckets_dir * @param {import('../../sdk/config_fs').ConfigFS} config_fs * @param {Object} config_data * @param {String} [invalid_str] + * @param {{symlink_name?: Boolean, symlink_access_key?: Boolean}} [options] * @returns {Promise} */ -async function write_manual_config_file(type, config_fs, config_data, invalid_str = '') { +async function write_manual_config_file(type, config_fs, config_data, invalid_str = '', { symlink_name, symlink_access_key} = {symlink_name: true, symlink_access_key: true}) { const config_path = type === CONFIG_TYPES.BUCKET ? config_fs.get_bucket_path_by_name(config_data.name) : config_fs.get_identity_path_by_id(config_data._id); if (type === CONFIG_TYPES.ACCOUNT) { - const dir_path = config_fs.get_identity_dir_path_by_id(config_data._id); - await nb_native().fs.mkdir(config_fs.fs_context, dir_path, native_fs_utils.get_umasked_mode(config.BASE_MODE_DIR)); + await create_identity_dir_if_missing(config_fs, config_data); } await nb_native().fs.writeFile( config_fs.fs_context, @@ -500,22 +500,62 @@ async function write_manual_config_file(type, config_fs, config_data, invalid_st mode: native_fs_utils.get_umasked_mode(config.BASE_MODE_FILE) } ); + const id_relative_path = config_fs.get_account_relative_path_by_id(config_data._id); - if (type === CONFIG_TYPES.ACCOUNT) { - const id_relative_path = config_fs.get_account_relative_path_by_id(config_data._id); - const name_symlink_path = config_fs.get_account_or_user_path_by_name(config_data.name); - await nb_native().fs.symlink(config_fs.fs_context, id_relative_path, name_symlink_path); + if (type === CONFIG_TYPES.ACCOUNT && symlink_name) { + await symlink_account_name(config_fs, config_data.name, id_relative_path); + } + + if (type === CONFIG_TYPES.ACCOUNT && symlink_access_key && config_data.access_keys) { + await symlink_account_access_key(config_fs, config_data.access_keys[0].access_key, id_relative_path); } } +/** + * symlink_account_name symlinks the account's name path to the target link path + * @param {import('../../sdk/config_fs').ConfigFS} config_fs + * @param {String} account_name + * @param {String} link_target + */ +async function symlink_account_name(config_fs, account_name, link_target) { + const name_symlink_path = config_fs.get_account_or_user_path_by_name(account_name); + await nb_native().fs.symlink(config_fs.fs_context, link_target, name_symlink_path); +} + +/** + * symlink_account_access_key symlinks the account's access key path to the target link path + * @param {import('../../sdk/config_fs').ConfigFS} config_fs + * @param {String} access_key + * @param {String} link_target + */ +async function symlink_account_access_key(config_fs, access_key, link_target) { + const access_key_symlink_path = config_fs.get_account_or_user_path_by_access_key(access_key); + await nb_native().fs.symlink(config_fs.fs_context, link_target, access_key_symlink_path); +} + +/** + * create_identity_dir_if_missing created the identity directory if missing + * @param {import('../../sdk/config_fs').ConfigFS} config_fs + * @param {Object} config_data + * @returns {Promise} + */ +async function create_identity_dir_if_missing(config_fs, config_data) { + const dir_path = config_fs.get_identity_dir_path_by_id(config_data._id); + try { + await nb_native().fs.mkdir(config_fs.fs_context, dir_path, native_fs_utils.get_umasked_mode(config.BASE_MODE_DIR)); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } +} /** * write_manual_old_account_config_file writes account config file directly to the old file system account path without using config FS * @param {import('../../sdk/config_fs').ConfigFS} config_fs * @param {Object} config_data + * @param {{symlink_access_key?: Boolean}} [options] * @returns {Promise} */ -async function write_manual_old_account_config_file(config_fs, config_data) { +async function write_manual_old_account_config_file(config_fs, config_data, { symlink_access_key } = { symlink_access_key: false }) { const config_path = config_fs._get_old_account_path_by_name(config_data.name); await nb_native().fs.writeFile( config_fs.fs_context, @@ -525,6 +565,12 @@ async function write_manual_old_account_config_file(config_fs, config_data) { mode: native_fs_utils.get_umasked_mode(config.BASE_MODE_FILE) } ); + + const account_name_relative_path = config_fs.get_old_account_relative_path_by_name(config_data.name); + if (symlink_access_key) { + const access_key_symlink_path = config_fs.get_account_or_user_path_by_access_key(config_data.access_keys[0].access_key); + await nb_native().fs.symlink(config_fs.fs_context, account_name_relative_path, access_key_symlink_path); + } } /** @@ -556,9 +602,9 @@ async function delete_manual_config_file(type, config_fs, config_data) { /** * @param {any} test_name - * @param {Object} [config_fs] + * @param {import('../../sdk/config_fs').ConfigFS} [config_fs] */ -async function fail_test_if_default_config_dir_exists(test_name, config_fs = {}) { +async function fail_test_if_default_config_dir_exists(test_name, config_fs) { const fs_context = config_fs?.fs_context || native_fs_utils.get_process_fs_context(); const config_dir_exists = await native_fs_utils.is_path_exists(fs_context, config.NSFS_NC_DEFAULT_CONF_DIR); const msg = `${test_name} found an existing default config directory ${config.NSFS_NC_DEFAULT_CONF_DIR},` + @@ -583,6 +629,8 @@ async function create_config_dir(config_dir) { /** * clean_config_dir cleans the config directory + * @param {import('../../sdk/config_fs').ConfigFS} config_fs + * @param {String} [custom_config_dir_path] * @returns {Promise} */ async function clean_config_dir(config_fs, custom_config_dir_path) { @@ -593,17 +641,35 @@ async function clean_config_dir(config_fs, custom_config_dir_path) { const system_json = '/system.json'; for (const dir of [buckets_dir_name, identities_dir_name, access_keys_dir_name, accounts_by_name, config.NSFS_TEMP_CONF_DIR_NAME]) { const default_path = path.join(config.NSFS_NC_DEFAULT_CONF_DIR, dir); - await fs_utils.folder_delete(default_path); - const custom_path = path.join(custom_config_dir_path, dir); - await fs_utils.folder_delete(custom_path); - + await fs_utils.folder_delete_skip_enoent(default_path); + if (custom_config_dir_path) { + const custom_path = path.join(custom_config_dir_path, dir); + await fs_utils.folder_delete_skip_enoent(custom_path); + } } + await delete_redirect_file(config_fs); await fs_utils.file_delete(system_json); - await fs_utils.folder_delete(config.NSFS_NC_DEFAULT_CONF_DIR); - await fs_utils.folder_delete(custom_config_dir_path); + await fs_utils.folder_delete_skip_enoent(config.NSFS_NC_DEFAULT_CONF_DIR); + await fs_utils.folder_delete_skip_enoent(custom_config_dir_path); } +/** + * create_file creates a file in the file system + * @param {nb.NativeFSContext} fs_context + * @param {String} file_path + * @param {Object} file_data + */ +async function create_file(fs_context, file_path, file_data) { + await nb_native().fs.writeFile( + fs_context, + file_path, + Buffer.from(JSON.stringify(file_data)), + { + mode: native_fs_utils.get_umasked_mode(config.BASE_MODE_FILE) + } + ); +} exports.blocks_exist_on_cloud = blocks_exist_on_cloud; exports.create_hosts_pool = create_hosts_pool; @@ -629,6 +695,10 @@ exports.get_new_buckets_path_by_test_env = get_new_buckets_path_by_test_env; exports.write_manual_config_file = write_manual_config_file; exports.write_manual_old_account_config_file = write_manual_old_account_config_file; exports.delete_manual_config_file = delete_manual_config_file; +exports.create_identity_dir_if_missing = create_identity_dir_if_missing; +exports.symlink_account_name = symlink_account_name; +exports.symlink_account_access_key = symlink_account_access_key; +exports.create_file = create_file; exports.create_redirect_file = create_redirect_file; exports.delete_redirect_file = delete_redirect_file; exports.fail_test_if_default_config_dir_exists = fail_test_if_default_config_dir_exists; diff --git a/src/test/unit_tests/jest_tests/test_config_dir_restructure_upgrade_script.test.js b/src/test/unit_tests/jest_tests/test_config_dir_restructure_upgrade_script.test.js new file mode 100644 index 0000000000..91a89d8686 --- /dev/null +++ b/src/test/unit_tests/jest_tests/test_config_dir_restructure_upgrade_script.test.js @@ -0,0 +1,654 @@ +/* Copyright (C) 2024 NooBaa */ +'use strict'; + +const path = require('path'); +const P = require('../../../util/promise'); +const config = require('../../../../config'); +const nb_native = require('../../../util/nb_native'); +const { ConfigFS, CONFIG_TYPES } = require('../../../sdk/config_fs'); +const dbg = require('../../../util/debug_module')(__filename); +const { create_config_dir, create_identity_dir_if_missing, clean_config_dir, fail_test_if_default_config_dir_exists, + TEST_TIMEOUT, write_manual_config_file, write_manual_old_account_config_file, create_file, + symlink_account_name, symlink_account_access_key } = require('../../system_tests/test_utils'); +const { get_process_fs_context, is_path_exists } = require('../../../util/native_fs_utils'); +const { move_old_accounts_dir, create_account_access_keys_index_if_missing, create_account_name_index_if_missing, + create_identity_if_missing, prepare_account_upgrade_params, upgrade_account_config_file, upgrade_accounts_config_files, run } = require('../../../upgrade/nc_upgrade_scripts/1.0.0/config_dir_restructure'); +const { create_fresh_path } = require('../../../util/fs_utils'); +const native_fs_utils = require('../../../util/native_fs_utils'); + +const mock_access_key = 'Zzto3OwtGflQrqD41h3SEXAMPLE'; +const mock_old_version = '5.16'; +const config_root_backend = config.NSFS_NC_CONFIG_DIR_BACKEND; +const fs_context = get_process_fs_context(config_root_backend); +const DEFAULT_CONF_DIR_PATH = config.NSFS_NC_DEFAULT_CONF_DIR; +const default_config_fs = new ConfigFS(DEFAULT_CONF_DIR_PATH, config_root_backend, fs_context); +const hidden_old_accounts_path = path.join(default_config_fs.config_root, `.backup_accounts_dir_${mock_old_version}/`); +const hidden_access_keys_backup_path = path.join(default_config_fs.config_root, `.backup_access_keys_dir_${mock_old_version}/`); + + +// WARNING: +// The following test file will check the directory structure created using create_config_dirs_if_missing() +// which is called when using noobaa-cli, for having an accurate test, it'll be blocked from running on an +// env having an existing default config directory and the test executer will be asked to remove the default +// config directory before running the test again, do not run on production env or on any env where the existing config directory is being used +describe('move_old_accounts_dir', () => { + + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + }); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('move_old_accounts_dir() - accounts/ dir is missing', async () => { + await move_old_accounts_dir(default_config_fs, [], mock_old_version, dbg); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir({}); + }); + + it('move_old_accounts_dir() - accounts/ dir is empty', async () => { + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await move_old_accounts_dir(default_config_fs, [], mock_old_version, dbg); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir({}); + }); + + it('move_old_accounts_dir() - accounts/ dir contains a single account', async () => { + await create_fresh_path(default_config_fs.old_accounts_dir_path); + const account_name = 'old_account1'; + const new_account_data = { _id: '1', name: account_name, user: 'root' }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + await move_old_accounts_dir(default_config_fs, [account_name], mock_old_version, dbg); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir({ [account_name]: new_account_data}); + }); + + it('move_old_accounts_dir() - accounts/ dir contains 5000 accounts', async () => { + await create_fresh_path(default_config_fs.old_accounts_dir_path); + const account_ids = Array.from({ length: 5000 }, (_, i) => Number(i + 1)); + const account_names_obj = {}; + await P.map_with_concurrency(5000, account_ids, async account_id => { + const new_account_data = { _id: String(account_id), name: 'old_account' + account_id, user: 'root' }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + account_names_obj[new_account_data.name] = new_account_data; + }); + await move_old_accounts_dir(default_config_fs, Object.keys(account_names_obj), mock_old_version, dbg); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir(account_names_obj); + }, TEST_TIMEOUT); + + it('move_old_accounts_dir() - .backup_accounts_dir already exists and contains some of the accounts in accounts/ dir', async () => { + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(default_config_fs.identities_dir_path); + await create_fresh_path(default_config_fs.accounts_by_name_dir_path); + + const account_ids = Array.from({ length: 200 }, (_, i) => Number(i + 1)); + const account_names_obj = {}; + const symlink_options = { symlink_name: false, symlink_access_key: false }; + await P.map_with_concurrency(100, account_ids, async account_id => { + const new_account_data = { _id: String(account_id), name: 'old_account' + account_id, user: 'root' }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, symlink_options); + account_names_obj[new_account_data.name] = new_account_data; + if (account_id % 2 === 0) { + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, symlink_options); + } + }); + await move_old_accounts_dir(default_config_fs, Object.keys(account_names_obj), mock_old_version, dbg); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir(account_names_obj); + }); +}); + +describe('create_account_access_keys_index_if_missing', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(hidden_old_accounts_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + + it('create_account_access_keys_index_if_missing() - single regular account', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: true }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); + + it('create_account_access_keys_index_if_missing() - anonymous account', async () => { + const new_account_data = { _id: String(1), name: 'anonymous', user: 'root' }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); + + it('create_account_access_keys_index_if_missing() - new access key index already exists', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: true, symlink_name: false }); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); + + it('create_account_access_keys_index_if_missing() - new access key index already exists and points to a non existing location', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const mock_link_id_path = path.join(default_config_fs.identities_dir_path, 'mock_id_dir', 'identity.json'); + await symlink_account_access_key(default_config_fs, mock_access_key, mock_link_id_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); + + it('create_account_access_keys_index_if_missing() - new access key index already exists and points to a non wrong location', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const mock_id_dir = path.join(default_config_fs.identities_dir_path, 'mock_id_dir'); + const mock_link_id_path = path.join(mock_id_dir, 'identity.json'); + await create_fresh_path(mock_id_dir); + await create_file(default_config_fs.fs_context, mock_link_id_path, { mock_key: 'mock_value' }); + await symlink_account_access_key(default_config_fs, mock_access_key, mock_link_id_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); + + it('create_account_access_keys_index_if_missing() - old access key index already deleted', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, identity_path }; + await create_account_access_keys_index_if_missing(default_config_fs, account_upgrade_params, hidden_old_accounts_path, dbg); + await assert_access_key_index_is_updated(new_account_data); + }); +}); + +describe('create_account_name_index_if_missing', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(hidden_old_accounts_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + + it('create_account_name_index_if_missing() - single regular account', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); + + it('create_account_name_index_if_missing() - anonymous account', async () => { + const new_account_data = { _id: String(1), name: 'anonymous', user: 'root' }; + const symlink_options = { symlink_access_key: false }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, symlink_options); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, symlink_options); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); + + it('create_account_name_index_if_missing() - new name index already exists', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: true }); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); + + it('create_account_name_index_if_missing() - new name index already exists and points to a non existing location', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const mock_link_id_path = path.join(default_config_fs.identities_dir_path, 'mock_id_dir', 'identity.json'); + await symlink_account_name(default_config_fs, new_account_data.name, mock_link_id_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); + + it('create_account_name_index_if_missing123 - new name index already exists and points to wrong location', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const mock_id_dir = path.join(default_config_fs.identities_dir_path, 'mock_id_dir'); + const mock_link_id_path = path.join(mock_id_dir, 'identity.json'); + await create_fresh_path(mock_id_dir); + await create_file(default_config_fs.fs_context, mock_link_id_path, { mock_key: 'mock_value' }); + await symlink_account_name(default_config_fs, new_account_data.name, mock_link_id_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); + + it('create_account_name_index_if_missing() - old name index already deleted', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const account_upgrade_params = { ...new_account_data, account_name: new_account_data.name, identity_path }; + await create_account_name_index_if_missing(default_config_fs, account_upgrade_params, dbg); + await assert_name_index_is_updated(new_account_data); + }); +}); + +describe('create_identity_if_missing', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(hidden_old_accounts_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('create_identity_if_missing() - identity does not exist', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + const account_old_path = default_config_fs._get_old_account_path_by_name(new_account_data.name); + const account_old_path_stat = await nb_native().fs.stat(default_config_fs.fs_context, account_old_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const identity_dir_path = default_config_fs.get_identity_dir_path_by_id(new_account_data._id); + const account_upgrade_params = { + ...new_account_data, account_name: new_account_data.name, identity_path, + account_old_path, + account_old_path_stat, identity_dir_path + }; + await create_identity_if_missing(default_config_fs.fs_context, account_upgrade_params, dbg); + await assert_identity_created(new_account_data); + }); + + it('create_identity_if_missing() - identity already exists', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined); + const account_old_path = default_config_fs._get_old_account_path_by_name(new_account_data.name); + const account_old_path_stat = await nb_native().fs.stat(default_config_fs.fs_context, account_old_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const identity_dir_path = default_config_fs.get_identity_dir_path_by_id(new_account_data._id); + const account_upgrade_params = { + ...new_account_data, account_name: new_account_data.name, identity_path, + account_old_path, + account_old_path_stat, identity_dir_path + }; + await create_identity_if_missing(default_config_fs.fs_context, account_upgrade_params, dbg); + await assert_identity_created(new_account_data); + }); + + it('create_identity_if_missing() - only dir exists but file doesn’t', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + await create_identity_dir_if_missing(default_config_fs, new_account_data); + const account_old_path = default_config_fs._get_old_account_path_by_name(new_account_data.name); + const account_old_path_stat = await nb_native().fs.stat(default_config_fs.fs_context, account_old_path); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const identity_dir_path = default_config_fs.get_identity_dir_path_by_id(new_account_data._id); + const account_upgrade_params = { + ...new_account_data, account_name: new_account_data.name, identity_path, + account_old_path, + account_old_path_stat, identity_dir_path + }; + await create_identity_if_missing(default_config_fs.fs_context, account_upgrade_params, dbg); + await assert_identity_created(new_account_data); + }); +}); + +describe('prepare_account_upgrade_params', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(hidden_old_accounts_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('prepare_account_upgrade_params() - account name exists', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data); + const account_old_path = default_config_fs._get_old_account_path_by_name(new_account_data.name); + const identity_path = default_config_fs.get_identity_path_by_id(new_account_data._id); + const identity_dir_path = default_config_fs.get_identity_dir_path_by_id(new_account_data._id); + let src_file; + let actual_upgrade_params; + try { + src_file = await native_fs_utils.open_file(fs_context, undefined, account_old_path, 'r'); + const account_old_path_stat = await src_file.stat(default_config_fs.fs_context); + const expected_account_upgrade_params = { + _id: new_account_data._id, + access_keys: new_account_data.access_keys, + account_name: new_account_data.name, + identity_path, + account_old_path, + account_old_path_stat, + identity_dir_path, + src_file, + gpfs_options: { src_file, dst_file: undefined } + }; + actual_upgrade_params = await prepare_account_upgrade_params(default_config_fs, new_account_data.name); + assert_upgrade_params(actual_upgrade_params, expected_account_upgrade_params); + } catch (err) { + fail(err); + } finally { + if (src_file) await src_file.close(default_config_fs.fs_context); + if (actual_upgrade_params.src_file) await actual_upgrade_params.src_file.close(default_config_fs.fs_context); + } + }); + + it('prepare_account_upgrade_params() - account name exists', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await expect(prepare_account_upgrade_params(default_config_fs, new_account_data.name)).rejects.toThrow('No such file or directory'); + }); +}); + +describe('upgrade_account_config_file', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(hidden_old_accounts_path); + await create_fresh_path(hidden_access_keys_backup_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('upgrade_account_config_file() - account exists in accounts/', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: true }); + await upgrade_account_config_file(default_config_fs, new_account_data.name, hidden_access_keys_backup_path, dbg); + await assert_account_config_file_upgraded({[new_account_data.name]: new_account_data}); + }); + + it('upgrade_account_config_file() - account was already upgraded', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + await upgrade_account_config_file(default_config_fs, new_account_data.name, hidden_access_keys_backup_path, dbg); + await assert_account_config_file_upgraded({[new_account_data.name]: new_account_data}); + }); + + it('upgrade_account_config_file() - identity exists but indexes are not', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: false }); + await upgrade_account_config_file(default_config_fs, new_account_data.name, hidden_access_keys_backup_path, dbg); + await assert_account_config_file_upgraded({[new_account_data.name]: new_account_data}); + }); + + it('upgrade_account_config_file() - identity exists, name index exist, access keys index does not', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: false, symlink_name: true }); + await upgrade_account_config_file(default_config_fs, new_account_data.name, hidden_access_keys_backup_path, dbg); + await assert_account_config_file_upgraded({[new_account_data.name]: new_account_data}); + }); + + it('upgrade_account_config_file() - identity exists, access keys index exist, name index does not', async () => { + const new_account_data = { _id: String(1), name: 'old_account' + 1, user: 'root', access_keys: [{ access_key: mock_access_key }] }; + await write_manual_old_account_config_file(default_config_fs, new_account_data, { symlink_access_key: false }); + await write_manual_config_file(CONFIG_TYPES.ACCOUNT, default_config_fs, new_account_data, undefined, + { symlink_access_key: true, symlink_name: false }); + await upgrade_account_config_file(default_config_fs, new_account_data.name, hidden_access_keys_backup_path, dbg); + await assert_account_config_file_upgraded({[new_account_data.name]: new_account_data}); + }); +}); + +describe('upgrade_accounts_config_files', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await default_config_fs.create_config_dirs_if_missing(); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(default_config_fs.access_keys_dir_path); + await create_fresh_path(hidden_old_accounts_path); + await create_fresh_path(hidden_access_keys_backup_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('upgrade_accounts_config_files() - empty accounts/ dir', async () => { + await upgrade_accounts_config_files(default_config_fs, [], mock_old_version, dbg); + await assert_dir_is_empty(default_config_fs.identities_dir_path); + await assert_dir_is_empty(default_config_fs.accounts_by_name_dir_path); + await assert_dir_is_empty(default_config_fs.access_keys_dir_path); + }); + + it('upgrade_accounts_config_files() - accounts/ dir contains 5000 accounts', async () => { + const account_ids = Array.from({ length: 5000 }, (_, i) => Number(i + 1)); + const account_names_obj = {}; + await P.map_with_concurrency(100, account_ids, async account_id => { + const account_data = { _id: String(account_id), name: 'old_account' + account_id, user: 'root', access_keys: [{ access_key: mock_access_key + String(account_id) }] }; + await write_manual_old_account_config_file(default_config_fs, account_data, { symlink_access_key: true }); + account_names_obj[account_data.name] = account_data; + }); + await upgrade_accounts_config_files(default_config_fs, Object.keys(account_names_obj), mock_old_version, dbg); + await assert_account_config_file_upgraded(account_names_obj); + }, TEST_TIMEOUT); +}); + +describe('run', () => { + beforeAll(async () => { + await fail_test_if_default_config_dir_exists('test_config_dir_restructure', default_config_fs); + }); + + beforeEach(async () => { + await create_config_dir(config.NSFS_NC_DEFAULT_CONF_DIR); + await create_fresh_path(default_config_fs.old_accounts_dir_path); + await create_fresh_path(default_config_fs.access_keys_dir_path); + }, TEST_TIMEOUT); + + afterEach(async () => { + await clean_config_dir(default_config_fs); + }, TEST_TIMEOUT); + + it('run() - empty accounts/ dir', async () => { + await run({ dbg, from_version: mock_old_version }); + await assert_dir_is_empty(default_config_fs.identities_dir_path); + await assert_dir_is_empty(default_config_fs.accounts_by_name_dir_path); + await assert_dir_is_empty(default_config_fs.access_keys_dir_path); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir({}); + }); + + it('run() - accounts/ dir contains 5000 accounts', async () => { + const account_ids = Array.from({ length: 5000 }, (_, i) => Number(i + 1)); + const account_names_obj = {}; + await P.map_with_concurrency(100, account_ids, async account_id => { + const account_data = { _id: String(account_id), name: 'old_account' + account_id, user: 'root', access_keys: [{ access_key: mock_access_key + String(account_id) }] }; + await write_manual_old_account_config_file(default_config_fs, account_data, { symlink_access_key: true }); + account_names_obj[account_data.name] = account_data; + }); + await run({ dbg, from_version: mock_old_version }); + await assert_account_config_file_upgraded(account_names_obj); + await assert_old_account_dir_was_deleted(); + await assert_backup_dir(account_names_obj); + }, TEST_TIMEOUT); +}); + +/** + * assert_old_account_dir_was_deleted asserts old accounts/ dir was deleted + * @returns {Promise} + */ +async function assert_old_account_dir_was_deleted() { + const exists = await is_path_exists(default_config_fs.fs_context, default_config_fs.old_accounts_dir_path); + expect(exists).toBe(false); +} + +/** + * assert_backup_dir was created and contains the given accounts + * @param {Object} [expected_accounts] + * @returns {Promise} + */ +async function assert_backup_dir(expected_accounts = {}) { + const exists = await is_path_exists(default_config_fs.fs_context, hidden_old_accounts_path); + expect(exists).toBe(true); + for (const account_name of Object.keys(expected_accounts)) { + const account_path = path.join(hidden_old_accounts_path, default_config_fs.json(account_name)); + const account_exists = await is_path_exists(default_config_fs.fs_context, account_path); + expect(account_exists).toBe(true); + } +} + +/** + * assert_access_key_index_is_updated to point to the account's identity.json file + * @param {Object} account_data + * @returns {Promise} + */ +async function assert_access_key_index_is_updated(account_data) { + const { _id, name, access_keys } = account_data; + if (name === 'anonymous' && !access_keys?.[0]?.access_key) return; + const access_key_path = default_config_fs.get_account_or_user_path_by_access_key(access_keys[0].access_key); + const identity_path = default_config_fs.get_identity_path_by_id(_id); + const exists = await is_path_exists(default_config_fs.fs_context, access_key_path); + expect(exists).toBe(true); + const access_key_linked_to_identity = await default_config_fs._is_symlink_pointing_to_identity(access_key_path, identity_path); + expect(access_key_linked_to_identity).toBe(true); +} + +/** + * assert_name_index_is_updated to point to the account's identity.json file + * @param {Object} account_data + * @returns {Promise} + */ +async function assert_name_index_is_updated(account_data) { + const { _id, name } = account_data; + const account_by_name_path = default_config_fs.get_account_or_user_path_by_name(name); + const identity_path = default_config_fs.get_identity_path_by_id(_id); + const exists = await is_path_exists(default_config_fs.fs_context, account_by_name_path); + expect(exists).toBe(true); + const name_linked_to_identity = await default_config_fs._is_symlink_pointing_to_identity(account_by_name_path, identity_path); + expect(name_linked_to_identity).toBe(true); +} + +/** + * assert_identity_created asserts that - + * 1. the identity dir was created + * 2. the identity.json file was created + * @param {Object} account_data + * @returns {Promise} + */ +async function assert_identity_created(account_data) { + const { _id } = account_data; + const identity_dir_path = default_config_fs.get_identity_path_by_id(_id); + const dir_exists = await is_path_exists(default_config_fs.fs_context, identity_dir_path); + expect(dir_exists).toBe(true); + const identity_data = await default_config_fs.get_identity_by_id(_id, CONFIG_TYPES.ACCOUNT, { show_secrets: true }); + expect(identity_data).toStrictEqual(account_data); +} + +/** + * assert_upgrade_params asserts that the actual upgrade params are as expected + * @param {Object} actual_upgrade_params + * @param {Object} expected_account_upgrade_params + * @returns {Void} + */ +function assert_upgrade_params(actual_upgrade_params, expected_account_upgrade_params) { + expect(actual_upgrade_params).toStrictEqual(expected_account_upgrade_params); +} + +/** + * assert_account_config_file_upgraded asserts that the account config file was upgraded as expected + */ +async function assert_account_config_file_upgraded(expected_accounts) { + for (const account_name of Object.keys(expected_accounts)) { + const account_data = expected_accounts[account_name]; + await assert_access_key_index_is_updated(account_data); + await assert_name_index_is_updated(account_data); + await assert_identity_created(account_data); + } +} + +// Jest has builtin function fail that based on Jasmine +// in case Jasmine would get removed from jest, created this one +// based on this: https://stackoverflow.com/a/55526098/16571658 +function fail(reason) { + throw new Error(reason); +} + +/** + * assert_dir_is_empty readdir and checks that the length is 0 + * @param {String} dir_path + */ +async function assert_dir_is_empty(dir_path) { + const entries = await nb_native().fs.readdir(default_config_fs.fs_context, default_config_fs.identities_dir_path); + expect(entries.length).toBe(0); +} diff --git a/src/test/unit_tests/jest_tests/test_config_dir_structure.test.js b/src/test/unit_tests/jest_tests/test_config_dir_structure.test.js index 68b8242ffb..2abc14edc0 100644 --- a/src/test/unit_tests/jest_tests/test_config_dir_structure.test.js +++ b/src/test/unit_tests/jest_tests/test_config_dir_structure.test.js @@ -8,7 +8,7 @@ const { TMP_PATH, create_redirect_file, create_config_dir, clean_config_dir, fail_test_if_default_config_dir_exists, TEST_TIMEOUT } = require('../../system_tests/test_utils'); const { get_process_fs_context, is_path_exists } = require('../../../util/native_fs_utils'); -const tmp_fs_path = path.join(TMP_PATH, 'test_config_fs'); +const tmp_fs_path = path.join(TMP_PATH, 'test_config_dir_restructure'); const config_root = path.join(tmp_fs_path, 'config_root'); const config_root_backend = config.NSFS_NC_CONFIG_DIR_BACKEND; const fs_context = get_process_fs_context(config_root_backend); diff --git a/src/upgrade/nc_upgrade_scripts/1.0.0/config_dir_restructure.js b/src/upgrade/nc_upgrade_scripts/1.0.0/config_dir_restructure.js index 1deefadcf3..05cecf332f 100644 --- a/src/upgrade/nc_upgrade_scripts/1.0.0/config_dir_restructure.js +++ b/src/upgrade/nc_upgrade_scripts/1.0.0/config_dir_restructure.js @@ -34,7 +34,6 @@ const nb_native = require('../../../util/nb_native'); async function run({ dbg, from_version }) { try { const config_fs = new ConfigFS(config.NSFS_NC_CONF_DIR, config.NSFS_NC_CONFIG_DIR_BACKEND); - const fs_context = config_fs.fs_context; await config_fs.create_dir_if_missing(config_fs.identities_dir_path); await config_fs.create_dir_if_missing(config_fs.accounts_by_name_dir_path); @@ -43,7 +42,7 @@ async function run({ dbg, from_version }) { const failed_accounts = await upgrade_accounts_config_files(config_fs, old_account_names, from_version, dbg); if (failed_accounts.length > 0) throw new Error('NC upgrade process failed, failed_accounts array length is bigger than 0' + util.inspect(failed_accounts)); - await move_old_accounts_dir(fs_context, config_fs, old_account_names, from_version, dbg); + await move_old_accounts_dir(config_fs, old_account_names, from_version, dbg); } catch (err) { dbg.error('NC upgrade process failed due to - ', err); throw err; @@ -74,7 +73,7 @@ async function upgrade_accounts_config_files(config_fs, old_account_names, from_ break; } catch (err) { retries -= 1; - dbg.warn(`upgrade account config failed ${account_name}, err ${err} retries left ${retries}`); + dbg.warn(`upgrade accounts config failed ${account_name}, err ${err} retries left ${retries}`); if (retries <= 0) { failed_accounts.push({ account_name, err }); break; @@ -187,10 +186,15 @@ async function create_account_name_index_if_missing(config_fs, account_upgrade_p const { account_name, _id, identity_path } = account_upgrade_params; try { const account_name_path = config_fs.get_account_path_by_name(account_name); - const is_account_symlink_exists = await native_fs_utils.is_path_exists(config_fs.fs_context, account_name_path); - const account_name_already_linked = is_account_symlink_exists && + const is_account_symlink_exists = await native_fs_utils.is_path_exists(config_fs.fs_context, account_name_path, true); + const is_account_symlink_target_exists = await native_fs_utils.is_path_exists(config_fs.fs_context, account_name_path); + const account_name_already_linked_to_identity = is_account_symlink_target_exists && await config_fs._is_symlink_pointing_to_identity(account_name_path, identity_path); - if (!account_name_already_linked) await config_fs.link_account_name_index(_id, account_name); + if (!account_name_already_linked_to_identity) { + // in case it was linked to a wrong location or the target does not exist, delete it + if (is_account_symlink_exists) await native_fs_utils.unlink_ignore_enoent(config_fs.fs_context, account_name_path); + await config_fs.link_account_name_index(_id, account_name); + } } catch (err) { if (err.code !== 'EEXIST') throw err; dbg.warn(`account name was already linked on a previous run of the upgrade script, skipping ${account_name}, ${_id}`); @@ -248,21 +252,21 @@ async function create_account_access_keys_index_if_missing(config_fs, account_up * 2. iterates all old accounts to a hidden directory * 3. deletes the accounts/ directory * // TODO - consider removing the accounts in the future, currently we decide to not delete old accounts - * @param {nb.NativeFSContext} fs_context * @param {import('../../../sdk/config_fs').ConfigFS} config_fs * @param {String[]} old_account_names * @param {String} from_version * @param {*} dbg * @returns {Promise} */ -async function move_old_accounts_dir(fs_context, config_fs, old_account_names, from_version, dbg) { +async function move_old_accounts_dir(config_fs, old_account_names, from_version, dbg) { + const fs_context = config_fs.fs_context; const old_account_tmp_dir_path = path.join(config_fs.old_accounts_dir_path, native_fs_utils.get_config_files_tmpdir()); const hidden_old_accounts_path = path.join(config_fs.config_root, `.backup_accounts_dir_${from_version}/`); try { await nb_native().fs.mkdir(fs_context, hidden_old_accounts_path); } catch (err) { if (err.code !== 'EEXIST') throw err; - dbg.warn(`config_dir_restructure.move_old_accounts_dir backup dir for old accounts allready exists ${hidden_old_accounts_path}, skipping`); + dbg.warn(`config_dir_restructure.move_old_accounts_dir backup dir for old accounts already exists ${hidden_old_accounts_path}, skipping`); } for (const account_name of old_account_names) { const old_account_path = path.join(config_fs.old_accounts_dir_path, config_fs.json(account_name)); @@ -276,8 +280,8 @@ async function move_old_accounts_dir(fs_context, config_fs, old_account_names, f dbg.warn(`config_dir_restructure.move_old_accounts_dir old account file does not exist ${old_account_path}, skipping`); } } - await native_fs_utils.folder_delete(old_account_tmp_dir_path, fs_context, true); try { + await native_fs_utils.folder_delete(old_account_tmp_dir_path, fs_context, true); await nb_native().fs.rmdir(fs_context, config_fs.old_accounts_dir_path); } catch (err) { if (err.code !== 'ENOENT') throw err; @@ -289,3 +293,11 @@ module.exports = { run, description: 'Config directory resturcture' }; + +module.exports.move_old_accounts_dir = move_old_accounts_dir; +module.exports.create_identity_if_missing = create_identity_if_missing; +module.exports.upgrade_account_config_file = upgrade_account_config_file; +module.exports.prepare_account_upgrade_params = prepare_account_upgrade_params; +module.exports.create_account_name_index_if_missing = create_account_name_index_if_missing; +module.exports.create_account_access_keys_index_if_missing = create_account_access_keys_index_if_missing; +module.exports.upgrade_accounts_config_files = upgrade_accounts_config_files; diff --git a/src/util/fs_utils.js b/src/util/fs_utils.js index ba5fdce8f2..37801ab97d 100644 --- a/src/util/fs_utils.js +++ b/src/util/fs_utils.js @@ -191,6 +191,15 @@ function folder_delete(dir) { return rimraf(dir); } +async function folder_delete_skip_enoent(dir) { + if (!dir) return; + try { + return rimraf(dir); + } catch (err) { + if (err.code !== 'ENOENT') throw err; + } +} + async function file_delete(file_name) { try { await fs.promises.unlink(file_name); @@ -304,6 +313,7 @@ exports.full_dir_copy = full_dir_copy; exports.file_copy = file_copy; exports.file_delete = file_delete; exports.folder_delete = folder_delete; +exports.folder_delete_skip_enoent = folder_delete_skip_enoent; exports.tar_pack = tar_pack; exports.write_file_from_stream = write_file_from_stream; exports.replace_file = replace_file;