Skip to content

Commit 0d02b9f

Browse files
committed
bucket notifications - add connection test to health (#8833)
Signed-off-by: Amit Prinz Setter <[email protected]>
1 parent 99e991f commit 0d02b9f

File tree

6 files changed

+148
-33
lines changed

6 files changed

+148
-33
lines changed

docs/NooBaaNonContainerized/Health.md

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ For more details about NooBaa RPM installation, see - [Getting Started](./Gettin
4242
- checks if there is ongoing upgrade
4343
- returns error if there is no ongoing upgrade, but the config directory phase is locked
4444
- returns message if there is no ongoing upgrade and the config directory is unlocked
45+
- `Bucket event notifications connections health`
46+
- Sends out a test notification for each connection.
4547

4648
* Health CLI requires root permissions.
4749

@@ -75,7 +77,12 @@ noobaa-cli diagnose health [--deployment_type][--https_port]
7577
- `all_bucket_details`
7678
- Type: Boolean
7779
- Default: false
78-
- Description: Indicates if health output should contain valid buckets.
80+
- Description: Indicates if health output should contain valid buckets.
81+
82+
- `all_connection_details`
83+
- Type: Boolean
84+
- Default: false
85+
- Description: Indicates if health output should contain connection test result.
7986

8087
- `config_root`
8188
- Type: String
@@ -143,6 +150,10 @@ The output of the Health CLI is a JSON object containing the following propertie
143150
2. The account doesn't have RW access to its `new_buckets_path` or having invalid config JSON file.
144151
3. The account has an invalid config JSON file.
145152

153+
- `invalid connections`:
154+
- Type: [{ "name": "connection_name", "config_path": "/connection/file/path", "code": String }]
155+
- Description: Array of connections that failed test notification.
156+
146157
- `valid_accounts`
147158
- Type: [{ "name": account_name, "storage_path": "/path/to/accounts/new_buckets_path" }]
148159
- Description: Array of all the valid accounts. If the all_account_details flag is set to true, valid_accounts will be included in the Health response.
@@ -151,6 +162,10 @@ The output of the Health CLI is a JSON object containing the following propertie
151162
- Type: [{ "name": bucket_name, "storage_path": "/path/to/bucket/path" }]
152163
- Description: Array of all the valid buckets. If the all_bucket_details flag is set to true, valid_buckets will be included in the Health response.
153164

165+
- `valid_connections`:
166+
- Type: [{ "name": "connection_name", "config_path": "/connection/file/path" }]
167+
- Description: Array of all connections to which test notification was send successfully.
168+
154169
- `error_type`
155170
- Type: String
156171
- Enum: 'PERSISTENT' | 'TEMPORARY'
@@ -239,6 +254,21 @@ Output:
239254
],
240255
"error_type": "PERSISTENT"
241256
},
257+
"connectoins_status": {
258+
"invalid_connections": [
259+
{
260+
"name": "notif_invalid",
261+
"config_path": "/etc/noobaa.conf.d/connections/notif_invalid.json",
262+
"code": "ECONNREFUSED"
263+
}
264+
],
265+
"valid_connections": [
266+
{
267+
"name": "notif_valid",
268+
"config_path": "/etc/noobaa.conf.d/connections/notif_valid.json"
269+
}
270+
]
271+
},
242272
"config_directory": {
243273
"phase": "CONFIG_DIR_UNLOCKED",
244274
"config_dir_version": "1.0.0",

src/manage_nsfs/health.js

Lines changed: 81 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { TYPES } = require('./manage_nsfs_constants');
1515
const { get_boolean_or_string_value, throw_cli_error, write_stdout_response, get_bucket_owner_account_by_id } = require('./manage_nsfs_cli_utils');
1616
const { ManageCLIResponse } = require('./manage_nsfs_cli_responses');
1717
const ManageCLIError = require('./manage_nsfs_cli_errors').ManageCLIError;
18+
const notifications_util = require('../util/notifications_util');
1819

1920

2021
const HOSTNAME = 'localhost';
@@ -101,6 +102,7 @@ class NSFSHealth {
101102
this.https_port = options.https_port;
102103
this.all_account_details = options.all_account_details;
103104
this.all_bucket_details = options.all_bucket_details;
105+
this.all_connection_details = options.all_connection_details;
104106
this.config_fs = options.config_fs;
105107
}
106108

@@ -128,12 +130,14 @@ class NSFSHealth {
128130

129131
let bucket_details;
130132
let account_details;
133+
let connection_details;
131134
const endpoint_response_code = (endpoint_state && endpoint_state.response?.response_code) || 'UNKNOWN_ERROR';
132135
const health_check_params = { service_status, pid, endpoint_response_code, config_directory_status };
133136
const service_health = this._calc_health_status(health_check_params);
134137
const error_code = this.get_error_code(health_check_params);
135138
if (this.all_bucket_details) bucket_details = await this.get_bucket_status();
136139
if (this.all_account_details) account_details = await this.get_account_status();
140+
if (this.all_connection_details) connection_details = await this.get_connection_status();
137141
const health = {
138142
service_name: NOOBAA_SERVICE,
139143
status: service_health,
@@ -155,11 +159,13 @@ class NSFSHealth {
155159
invalid_buckets: bucket_details === undefined ? undefined : bucket_details.invalid_storages,
156160
valid_buckets: bucket_details === undefined ? undefined : bucket_details.valid_storages,
157161
error_type: health_errors_tyes.PERSISTENT,
158-
}
162+
},
163+
connections_status: connection_details
159164
}
160165
};
161166
if (!this.all_account_details) delete health.checks.accounts_status;
162167
if (!this.all_bucket_details) delete health.checks.buckets_status;
168+
if (!this.all_connection_details) delete health.checks.connections_status;
163169
return health;
164170
}
165171

@@ -333,6 +339,17 @@ class NSFSHealth {
333339
};
334340
}
335341

342+
async validate_config_dir_exists(path, type) {
343+
const config_root_type_exists = await this.config_fs.validate_config_dir_exists(path);
344+
if (!config_root_type_exists) {
345+
dbg.log1(`Config directory type - ${type} is missing, ${path}`);
346+
return {
347+
invalid_storages: [],
348+
valid_storages: []
349+
};
350+
}
351+
}
352+
336353
async get_bucket_status() {
337354
const bucket_details = await this.get_storage_status(TYPES.BUCKET, this.all_bucket_details);
338355
return bucket_details;
@@ -343,34 +360,38 @@ class NSFSHealth {
343360
return account_details;
344361
}
345362

363+
async get_connection_status() {
364+
const connection_details = await this.get_storage_status(TYPES.CONNECTION, this.all_connection_details);
365+
//if there were in failed test notifications, mark error as temp
366+
if (connection_details.invalid_storages && connection_details.invalid_storages.length > 0) {
367+
connection_details.error_type = health_errors_tyes.TEMPORARY;
368+
}
369+
return connection_details;
370+
}
371+
346372
async get_storage_status(type, all_details) {
347373
const invalid_storages = [];
348374
const valid_storages = [];
349375
//check for account and buckets dir paths
350-
let config_root_type_exists;
351376
let config_dir_path;
352377
if (type === TYPES.BUCKET) {
353378
config_dir_path = this.config_fs.buckets_dir_path;
354-
config_root_type_exists = await this.config_fs.validate_config_dir_exists(config_dir_path);
355379
} else if (type === TYPES.ACCOUNT) {
356380
// TODO - handle iam accounts when directory structure changes - read_account_by_id
357381
config_dir_path = this.config_fs.accounts_by_name_dir_path;
358-
config_root_type_exists = await this.config_fs.validate_config_dir_exists(config_dir_path);
359-
}
360-
// TODO - this is not a good handling for that - we need to take it to an upper level
361-
if (!config_root_type_exists) {
362-
dbg.log1(`Config directory type - ${type} is missing, ${config_dir_path}`);
363-
return {
364-
invalid_storages: invalid_storages,
365-
valid_storages: valid_storages
366-
};
382+
} else {
383+
config_dir_path = this.config_fs.connections_dir_path;
367384
}
385+
const missing = await this.validate_config_dir_exists(config_dir_path, type);
386+
if (missing) return missing;
368387

369388
let config_files_names;
370389
if (type === TYPES.BUCKET) {
371390
config_files_names = await this.config_fs.list_buckets();
372-
} else {
391+
} else if (type === TYPES.ACCOUNT) {
373392
config_files_names = await this.config_fs.list_accounts();
393+
} else {
394+
config_files_names = await this.config_fs.list_connections();
374395
}
375396
for (const config_file_name of config_files_names) {
376397
// config_file get data or push error
@@ -386,13 +407,24 @@ class NSFSHealth {
386407
let res;
387408
const storage_path = type === TYPES.BUCKET ?
388409
config_data.path :
389-
config_data.nsfs_account_config.new_buckets_path;
410+
config_data.nsfs_account_config?.new_buckets_path;
390411

391412
if (type === TYPES.ACCOUNT) {
392413
const config_file_path = this.config_fs.get_account_path_by_name(config_file_name);
393414
res = await is_new_buckets_path_valid(config_file_path, config_data, storage_path);
394415
} else if (type === TYPES.BUCKET) {
395416
res = await is_bucket_storage_and_owner_exists(this.config_fs, config_data, storage_path);
417+
} else {
418+
const connection_file_path = this.config_fs.get_connection_path_by_name(config_file_name);
419+
const test_notif_err = await notifications_util.test_notifications([{
420+
name: config_data.name,
421+
topic: [this.config_fs.json(config_file_name)]
422+
}], this.config_fs.config_root);
423+
if (test_notif_err) {
424+
res = get_invalid_object(config_data.name, connection_file_path, undefined, test_notif_err.code);
425+
} else {
426+
res = get_valid_object(config_data.name, connection_file_path, undefined);
427+
}
396428
}
397429
if (all_details && res.valid_storage) {
398430
valid_storages.push(res.valid_storage);
@@ -416,16 +448,41 @@ class NSFSHealth {
416448
let config_data;
417449
let err_obj;
418450
try {
419-
config_data = type === TYPES.BUCKET ?
420-
await this.config_fs.get_bucket_by_name(config_file_name) :
421-
// TODO - should be changed to id when moving to new structure for supporting iam accounts
422-
await this.config_fs.get_account_by_name(config_file_name);
451+
switch (type) {
452+
case TYPES.BUCKET: {
453+
config_data = await this.config_fs.get_bucket_by_name(config_file_name);
454+
break;
455+
}
456+
case TYPES.ACCOUNT: {
457+
// TODO - should be changed to id when moving to new structure for supporting iam accounts
458+
config_data = await this.config_fs.get_account_by_name(config_file_name);
459+
break;
460+
}
461+
case TYPES.CONNECTION: {
462+
config_data = await this.config_fs.get_connection_by_name(config_file_name);
463+
break;
464+
}
465+
default: //shouldn't happen, for js-lint
466+
}
423467
} catch (err) {
424468
let err_code;
425-
const config_file_path = type === TYPES.BUCKET ?
426-
this.config_fs.get_bucket_path_by_name(config_file_name) :
427-
// TODO - should be changed to id when moving to new structure for supporting iam accounts
428-
this.config_fs.get_account_path_by_name(config_file_name);
469+
let config_file_path;
470+
switch (type) {
471+
case TYPES.BUCKET: {
472+
config_file_path = await this.config_fs.get_bucket_path_by_name(config_file_name);
473+
break;
474+
}
475+
case TYPES.ACCOUNT: {
476+
// TODO - should be changed to id when moving to new structure for supporting iam accounts
477+
config_file_path = await this.config_fs.get_account_path_by_name(config_file_name);
478+
break;
479+
}
480+
case TYPES.CONNECTION: {
481+
config_file_path = await this.config_fs.get_connection_path_by_name(config_file_name);
482+
break;
483+
}
484+
default: //shouldn't happen, for js-lint
485+
}
429486

430487
if (err.code === 'ENOENT') {
431488
dbg.log1(`Error: Config file path should be a valid path`, config_file_path, err);
@@ -546,9 +603,10 @@ async function get_health_status(argv, config_fs) {
546603
const deployment_type = argv.deployment_type || 'nc';
547604
const all_account_details = get_boolean_or_string_value(argv.all_account_details);
548605
const all_bucket_details = get_boolean_or_string_value(argv.all_bucket_details);
606+
const all_connection_details = get_boolean_or_string_value(argv.all_connection_details);
549607

550608
if (deployment_type === 'nc') {
551-
const health = new NSFSHealth({ https_port, all_account_details, all_bucket_details, config_fs });
609+
const health = new NSFSHealth({ https_port, all_account_details, all_bucket_details, all_connection_details, config_fs });
552610
const health_status = await health.nc_nsfs_health();
553611
write_stdout_response(ManageCLIResponse.HealthStatus, health_status);
554612
} else {

src/manage_nsfs/manage_nsfs_constants.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ const VALID_OPTIONS_GLACIER = {
7474
};
7575

7676
const VALID_OPTIONS_DIAGNOSE = {
77-
'health': new Set([ 'https_port', 'deployment_type', 'all_account_details', 'all_bucket_details', ...CLI_MUTUAL_OPTIONS]),
77+
'health': new Set([ 'https_port', 'deployment_type', 'all_account_details', 'all_bucket_details', 'all_connection_details', ...CLI_MUTUAL_OPTIONS]),
7878
'gather-logs': new Set([ CONFIG_ROOT_FLAG]),
7979
'metrics': new Set([CONFIG_ROOT_FLAG])
8080
};
@@ -143,6 +143,7 @@ const OPTION_TYPE = {
143143
deployment_type: 'string',
144144
all_account_details: 'boolean',
145145
all_bucket_details: 'boolean',
146+
all_connection_details: 'boolean',
146147
https_port: 'number',
147148
debug: 'number',
148149
// upgrade options

src/manage_nsfs/manage_nsfs_help_utils.js

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,13 @@ Usage:
366366
367367
Flags:
368368
369-
--deployment_type <string> (optional) Set the nsfs type for heath check.(default nc; Non Containerized)
370-
--https_port <number> (optional) Set the S3 endpoint listening HTTPS port to serve. (default config.ENDPOINT_SSL_PORT)
371-
--all_account_details <boolean> (optional) Set a flag for returning all account details.
372-
--all_bucket_details <boolean> (optional) Set a flag for returning all bucket details.
373-
--debug <number> (optional) Use for increasing the log verbosity of health cli commands.
374-
--config_root <string> (optional) Set Configuration files path for Noobaa standalon NSFS. (default config.NSFS_NC_DEFAULT_CONF_DIR)
369+
--deployment_type <string> (optional) Set the nsfs type for heath check.(default nc; Non Containerized)
370+
--https_port <number> (optional) Set the S3 endpoint listening HTTPS port to serve. (default config.ENDPOINT_SSL_PORT)
371+
--all_account_details <boolean> (optional) Set a flag for returning all account details.
372+
--all_bucket_details <boolean> (optional) Set a flag for returning all bucket details.
373+
--all_connection_details <boolean> (optional) Set a flag for returning all connection details.
374+
--debug <number> (optional) Use for increasing the log verbosity of health cli commands.
375+
--config_root <string> (optional) Set Configuration files path for Noobaa standalon NSFS. (default config.NSFS_NC_DEFAULT_CONF_DIR)
375376
376377
`;
377378

src/test/unit_tests/test_nc_health.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const sinon = require('sinon');
99
const assert = require('assert');
1010
const config = require('../../../config');
1111
const pkg = require('../../../package.json');
12+
const fs = require('fs');
1213
const fs_utils = require('../../util/fs_utils');
1314
const nb_native = require('../../util/nb_native');
1415
const { ConfigFS } = require('../../sdk/config_fs');
@@ -44,6 +45,17 @@ const get_system_config_mock_default_response = [{
4445
} }];
4546
const default_mock_upgrade_status = { message: 'there is no in-progress upgrade' };
4647

48+
const connection_from_file_path = path.join(tmp_fs_path, 'connection_from_file');
49+
const connection_from_file = {
50+
agent_request_object: {
51+
host: "localhost",
52+
port: 9999,
53+
timeout: 100
54+
},
55+
notification_protocol: "http",
56+
name: "http_notif",
57+
};
58+
4759
mocha.describe('nsfs nc health', function() {
4860

4961
const config_root = path.join(tmp_fs_path, 'config_root_nsfs_health');
@@ -210,6 +222,17 @@ mocha.describe('nsfs nc health', function() {
210222
assert_config_dir_status(health_status, valid_system_json.config_directory);
211223
});
212224

225+
mocha.it('health should report on invalid connections', async function() {
226+
fs.writeFileSync(connection_from_file_path, JSON.stringify(connection_from_file));
227+
await exec_manage_cli(TYPES.CONNECTION, ACTIONS.ADD, { config_root, from_file: connection_from_file_path });
228+
229+
Health.all_connection_details = true;
230+
const health_status = await Health.nc_nsfs_health();
231+
Health.all_connection_details = false;
232+
233+
assert.strictEqual(health_status.checks.connections_status.invalid_storages[0].name, connection_from_file.name);
234+
});
235+
213236
mocha.it('NooBaa service is inactive', async function() {
214237
set_mock_functions(Health, {
215238
get_service_state: [{ service_status: 'inactive', pid: 0 }],

src/util/notifications_util.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -351,10 +351,12 @@ async function test_notifications(notifs, nc_config_dir) {
351351
return;
352352
}
353353
let connect_files_dir = config.NOTIFICATION_CONNECT_DIR;
354+
let config_fs;
354355
if (nc_config_dir) {
355-
connect_files_dir = new ConfigFS(nc_config_dir).connections_dir_path;
356+
config_fs = new ConfigFS(nc_config_dir);
357+
connect_files_dir = config_fs.connections_dir_path;
356358
}
357-
const notificator = new Notificator({connect_files_dir});
359+
const notificator = new Notificator({connect_files_dir, nc_config_fs: config_fs});
358360
for (const notif of notifs) {
359361
let connect;
360362
let connection;

0 commit comments

Comments
 (0)