Skip to content

Commit a3bc7dd

Browse files
authored
Merge pull request #8747 from shirady/nsfs-nc-list-separate
NC | NSFS | CLI | Separate Bucket and Account List Functions
2 parents b89f7bc + b11aa5d commit a3bc7dd

File tree

2 files changed

+176
-99
lines changed

2 files changed

+176
-99
lines changed

src/cmd/manage_nsfs.js

Lines changed: 146 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ const manage_nsfs_validations = require('../manage_nsfs/manage_nsfs_validations'
2929
const nc_mkm = require('../manage_nsfs/nc_master_key_manager').get_instance();
3030
const notifications_util = require('../util/notifications_util');
3131

32+
///////////////
33+
//// GENERAL //
34+
///////////////
35+
3236
let config_fs;
3337

3438
async function main(argv = minimist(process.argv.slice(2))) {
@@ -92,6 +96,10 @@ async function main(argv = minimist(process.argv.slice(2))) {
9296
}
9397
}
9498

99+
///////////////
100+
//// BUCKETS //
101+
///////////////
102+
95103
// in name and new_name we allow type number, hence convert it to string
96104
async function fetch_bucket_data(action, user_input) {
97105
let data = {
@@ -146,7 +154,6 @@ async function fetch_bucket_data(action, user_input) {
146154
return data;
147155
}
148156

149-
150157
/**
151158
* merge_new_and_existing_config_data returns the merged object of the existing bucket data and the user data
152159
* @param {Object} user_input_bucket_data
@@ -259,6 +266,62 @@ async function delete_bucket(data, force) {
259266
}
260267
}
261268

269+
270+
/**
271+
* filter_bucket will return true or false based on whether a bucket meets the criteria defined by the supported flags.
272+
* @param {object} bucket
273+
* @param {object} [filters]
274+
*/
275+
function filter_bucket(bucket, filters) {
276+
for (const [key, val] of Object.entries(filters)) {
277+
if (bucket[key] !== val) { // We will never reach here if we will not add an appropriate field to the filter
278+
return false;
279+
}
280+
}
281+
return true;
282+
}
283+
284+
/**
285+
* list_bucket_config_files will list all the bucket config files (json) in a given config directory
286+
* @param {boolean} [wide]
287+
* @param {object} [filters]
288+
*/
289+
async function list_bucket_config_files(wide, filters = {}) {
290+
let entry_names = [];
291+
const should_filter = Object.keys(filters).length > 0;
292+
const is_filter_by_name = filters.name !== undefined;
293+
294+
const options = {
295+
silent_if_missing: true
296+
};
297+
298+
// in case we have a filter by name, we don't need to read all the entries and iterate them
299+
// instead we "mock" the entries array to have one entry and it is the name by the filter (we add it for performance)
300+
if (is_filter_by_name) {
301+
entry_names = [filters.name];
302+
} else {
303+
entry_names = await config_fs.list_buckets();
304+
}
305+
306+
let config_files_list = await P.map_with_concurrency(10, entry_names, async entry_name => {
307+
if (wide || should_filter) {
308+
const data = await config_fs.get_bucket_by_name(entry_name, options);
309+
if (!data) return undefined;
310+
if (should_filter && !filter_bucket(data, filters)) return undefined;
311+
if (!wide) return { name: entry_name };
312+
await set_bucker_owner(data);
313+
return data;
314+
} else {
315+
return { name: entry_name };
316+
}
317+
});
318+
// it inserts undefined for the entry '.noobaa-config-nsfs' and we wish to remove it
319+
// in case the entry was deleted during the list it also inserts undefined
320+
config_files_list = config_files_list.filter(item => item);
321+
322+
return config_files_list;
323+
}
324+
262325
/**
263326
* bucket_management does the following -
264327
* 1. fetches the bucket data if this is not a list operation
@@ -287,14 +350,18 @@ async function bucket_management(action, user_input) {
287350
} else if (action === ACTIONS.LIST) {
288351
const bucket_filters = _.pick(user_input, LIST_BUCKET_FILTERS);
289352
const wide = get_boolean_or_string_value(user_input.wide);
290-
const buckets = await list_config_files(TYPES.BUCKET, wide, undefined, bucket_filters);
353+
const buckets = await list_bucket_config_files(wide, bucket_filters);
291354
response = { code: ManageCLIResponse.BucketList, detail: buckets };
292355
} else {
293356
throw_cli_error(ManageCLIError.InvalidAction);
294357
}
295358
write_stdout_response(response.code, response.detail, response.event_arg);
296359
}
297360

361+
////////////////
362+
//// ACCOUNTS //
363+
////////////////
364+
298365
/**
299366
* set_access_keys will set the access keys either given or generated.
300367
* @param {string} access_key
@@ -488,65 +555,10 @@ async function get_account_status(data, show_secrets) {
488555
}
489556
}
490557

491-
/**
492-
* account_management does the following -
493-
* 1. sets variables by the user input options
494-
* 2. iniates nc_master_key_manager on UPDATE/ADD/show_secrets
495-
* 2. validates account args - TODO - we should split it to validate_account_args
496-
* and validations of the merged account (user_input + existing account config)
497-
* 3. call account operation based on the action argument
498-
* 4. write output to stdout
499-
* @param {'add'|'update'|'delete'|'status'|'list'} action
500-
* @param {Object} user_input
501-
*/
502-
async function account_management(action, user_input) {
503-
const show_secrets = get_boolean_or_string_value(user_input.show_secrets);
504-
const is_flag_iam_operate_on_root_account = get_boolean_or_string_value(user_input.iam_operate_on_root_account);
505-
const account_filters = _.pick(user_input, LIST_ACCOUNT_FILTERS);
506-
const wide = get_boolean_or_string_value(user_input.wide);
507-
if (get_boolean_or_string_value(user_input.anonymous)) {
508-
user_input.name = config.ANONYMOUS_ACCOUNT_NAME;
509-
user_input.email = config.ANONYMOUS_ACCOUNT_NAME;
510-
}
511-
// init nc_mkm here to avoid concurrent initializations
512-
// init if actions is add/update (require encryption) or show_secrets = true (require decryption)
513-
if ([ACTIONS.ADD, ACTIONS.UPDATE].includes(action) || show_secrets) await nc_mkm.init();
514-
const data = action === ACTIONS.LIST ? undefined : await fetch_account_data(action, user_input);
515-
await manage_nsfs_validations.validate_account_args(config_fs, data, action, is_flag_iam_operate_on_root_account);
516-
517-
let response = {};
518-
if (action === ACTIONS.ADD) {
519-
response = await add_account(data);
520-
} else if (action === ACTIONS.STATUS) {
521-
response = await get_account_status(data, show_secrets);
522-
} else if (action === ACTIONS.UPDATE) {
523-
response = await update_account(data);
524-
} else if (action === ACTIONS.DELETE) {
525-
response = await delete_account(data);
526-
} else if (action === ACTIONS.LIST) {
527-
const accounts = await list_config_files(TYPES.ACCOUNT, wide, show_secrets, account_filters);
528-
response = { code: ManageCLIResponse.AccountList, detail: accounts };
529-
} else {
530-
throw_cli_error(ManageCLIError.InvalidAction);
531-
}
532-
write_stdout_response(response.code, response.detail, response.event_arg);
533-
534-
}
535-
536-
/**
537-
* filter_list_item will return an answer of filter_account() or filter_bucket() based on the entity type
538-
* @param {string} type
539-
* @param {object} entity
540-
* @param {string[]} [filters]
541-
*/
542-
function filter_list_item(type, entity, filters) {
543-
return type === TYPES.ACCOUNT ? filter_account(entity, filters) : filter_bucket(entity, filters);
544-
}
545-
546558
/**
547559
* filter_account will return true or false based on whether an account meets the criteria defined by the supported flags.
548560
* @param {object} account
549-
* @param {string[]} [filters]
561+
* @param {object} [filters]
550562
*/
551563
function filter_account(account, filters) {
552564
for (const [key, val] of Object.entries(filters)) {
@@ -570,32 +582,17 @@ function filter_account(account, filters) {
570582
}
571583

572584
/**
573-
* filter_bucket will return true or false based on whether a bucket meets the criteria defined by the supported flags.
574-
* currently not implemented
575-
* @param {object} bucket
576-
* @param {string[]} [filters]
577-
*/
578-
function filter_bucket(bucket, filters) {
579-
for (const [key, val] of Object.entries(filters)) {
580-
if (bucket[key] !== val) { // We will never reach here if we will not add an appropriate field to the filter
581-
return false;
582-
}
583-
}
584-
return true;
585-
}
586-
/**
587-
* list_config_files will list all the config files (json) in a given config directory
588-
* @param {string} type
585+
* list_account_config_files will list all the account config files (json) in a given config directory
589586
* @param {boolean} [wide]
590587
* @param {boolean} [show_secrets]
591588
* @param {object} [filters]
592589
*/
593-
async function list_config_files(type, wide, show_secrets, filters = {}) {
590+
async function list_account_config_files(wide, show_secrets, filters = {}) {
594591
let entry_names = [];
595592
const should_filter = Object.keys(filters).length > 0;
596593
const is_filter_by_name = filters.name !== undefined;
597594

598-
// decryption causing mkm initalization
595+
// decryption causing mkm initialization
599596
// decrypt only if data has access_keys and show_secrets = true (no need to decrypt if show_secrets = false but should_filter = true)
600597
const options = {
601598
show_secrets: show_secrets || should_filter,
@@ -607,26 +604,18 @@ async function list_config_files(type, wide, show_secrets, filters = {}) {
607604
// instead we "mock" the entries array to have one entry and it is the name by the filter (we add it for performance)
608605
if (is_filter_by_name) {
609606
entry_names = [filters.name];
610-
} else if (type === TYPES.ACCOUNT) {
607+
} else {
611608
entry_names = await config_fs.list_accounts();
612-
} else if (type === TYPES.BUCKET) {
613-
entry_names = await config_fs.list_buckets();
614609
}
615610

616611
let config_files_list = await P.map_with_concurrency(10, entry_names, async entry_name => {
617612
if (wide || should_filter) {
618-
const data = type === TYPES.ACCOUNT ?
619-
await config_fs.get_account_by_name(entry_name, options) :
620-
await config_fs.get_bucket_by_name(entry_name, options);
613+
const data = await config_fs.get_account_by_name(entry_name, options);
621614
if (!data) return undefined;
622-
if (should_filter && !filter_list_item(type, data, filters)) return undefined;
615+
if (should_filter && !filter_account(data, filters)) return undefined;
623616
// remove secrets on !show_secrets && should filter
624617
if (!wide) return { name: entry_name };
625-
if (type === TYPES.ACCOUNT) return _.omit(data, show_secrets ? [] : ['access_keys']);
626-
if (type === TYPES.BUCKET) {
627-
await set_bucker_owner(data);
628-
return data;
629-
}
618+
return _.omit(data, show_secrets ? [] : ['access_keys']);
630619
} else {
631620
return { name: entry_name };
632621
}
@@ -639,16 +628,48 @@ async function list_config_files(type, wide, show_secrets, filters = {}) {
639628
}
640629

641630
/**
642-
* list_connections
643-
* @returns An array with names of all connection files.
631+
* account_management does the following -
632+
* 1. sets variables by the user input options
633+
* 2. iniates nc_master_key_manager on UPDATE/ADD/show_secrets
634+
* 2. validates account args - TODO - we should split it to validate_account_args
635+
* and validations of the merged account (user_input + existing account config)
636+
* 3. call account operation based on the action argument
637+
* 4. write output to stdout
638+
* @param {'add'|'update'|'delete'|'status'|'list'} action
639+
* @param {Object} user_input
644640
*/
645-
async function list_connections() {
646-
let conns = await config_fs.list_connections();
647-
// it inserts undefined for the entry '.noobaa-config-nsfs' and we wish to remove it
648-
// in case the entry was deleted during the list it also inserts undefined
649-
conns = conns.filter(item => item);
641+
async function account_management(action, user_input) {
642+
const show_secrets = get_boolean_or_string_value(user_input.show_secrets);
643+
const is_flag_iam_operate_on_root_account = get_boolean_or_string_value(user_input.iam_operate_on_root_account);
644+
const account_filters = _.pick(user_input, LIST_ACCOUNT_FILTERS);
645+
const wide = get_boolean_or_string_value(user_input.wide);
646+
if (get_boolean_or_string_value(user_input.anonymous)) {
647+
user_input.name = config.ANONYMOUS_ACCOUNT_NAME;
648+
user_input.email = config.ANONYMOUS_ACCOUNT_NAME;
649+
}
650+
// init nc_mkm here to avoid concurrent initializations
651+
// init if actions is add/update (require encryption) or show_secrets = true (require decryption)
652+
if ([ACTIONS.ADD, ACTIONS.UPDATE].includes(action) || show_secrets) await nc_mkm.init();
653+
const data = action === ACTIONS.LIST ? undefined : await fetch_account_data(action, user_input);
654+
await manage_nsfs_validations.validate_account_args(config_fs, data, action, is_flag_iam_operate_on_root_account);
655+
656+
let response = {};
657+
if (action === ACTIONS.ADD) {
658+
response = await add_account(data);
659+
} else if (action === ACTIONS.STATUS) {
660+
response = await get_account_status(data, show_secrets);
661+
} else if (action === ACTIONS.UPDATE) {
662+
response = await update_account(data);
663+
} else if (action === ACTIONS.DELETE) {
664+
response = await delete_account(data);
665+
} else if (action === ACTIONS.LIST) {
666+
const accounts = await list_account_config_files(wide, show_secrets, account_filters);
667+
response = { code: ManageCLIResponse.AccountList, detail: accounts };
668+
} else {
669+
throw_cli_error(ManageCLIError.InvalidAction);
670+
}
671+
write_stdout_response(response.code, response.detail, response.event_arg);
650672

651-
return conns;
652673
}
653674

654675
/**
@@ -689,6 +710,10 @@ async function set_bucker_owner(bucket_data) {
689710
bucket_data.bucket_owner = account_data?.name;
690711
}
691712

713+
////////////////////
714+
//// IP WHITELIST //
715+
////////////////////
716+
692717
async function whitelist_ips_management(args) {
693718
const ips = args.ips;
694719
manage_nsfs_validations.validate_whitelist_arg(ips);
@@ -708,6 +733,10 @@ async function whitelist_ips_management(args) {
708733
write_stdout_response(ManageCLIResponse.WhiteListIPUpdated, ips);
709734
}
710735

736+
///////////////
737+
//// GLACIER //
738+
///////////////
739+
711740
async function glacier_management(argv) {
712741
const action = argv._[1] || '';
713742
await manage_glacier_operations(action, argv);
@@ -729,10 +758,18 @@ async function manage_glacier_operations(action, argv) {
729758
}
730759
}
731760

761+
//////////////////////
762+
//// BUCKET LOGGING //
763+
//////////////////////
764+
732765
async function logging_management() {
733766
await manage_nsfs_logging.export_bucket_logging(config_fs);
734767
}
735768

769+
/////////////////////
770+
//// NOTIFICATIONS //
771+
////////////////////
772+
736773
async function notification_management() {
737774
await new notifications_util.Notificator({
738775
fs_context: config_fs.fs_context,
@@ -780,5 +817,18 @@ async function connection_management(action, user_input) {
780817
write_stdout_response(response.code, response.detail, response.event_arg);
781818
}
782819

820+
/**
821+
* list_connections
822+
* @returns An array with names of all connection files.
823+
*/
824+
async function list_connections() {
825+
let conns = await config_fs.list_connections();
826+
// it inserts undefined for the entry '.noobaa-config-nsfs' and we wish to remove it
827+
// in case the entry was deleted during the list it also inserts undefined
828+
conns = conns.filter(item => item);
829+
830+
return conns;
831+
}
832+
783833
exports.main = main;
784834
if (require.main === module) main();

0 commit comments

Comments
 (0)