Skip to content

Commit bdf218b

Browse files
committed
bucket notifications - add connection test to health (#8833)
Signed-off-by: Amit Prinz Setter <alphaprinz@gmail.com>
1 parent b1a724b commit bdf218b

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
@@ -16,6 +16,7 @@ const { get_boolean_or_string_value, throw_cli_error, write_stdout_response,
1616
get_bucket_owner_account_by_id, get_service_status, NOOBAA_SERVICE_NAME } = require('./manage_nsfs_cli_utils');
1717
const { ManageCLIResponse } = require('./manage_nsfs_cli_responses');
1818
const ManageCLIError = require('./manage_nsfs_cli_errors').ManageCLIError;
19+
const notifications_util = require('../util/notifications_util');
1920

2021

2122
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_NAME,
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

@@ -323,6 +329,17 @@ class NSFSHealth {
323329
};
324330
}
325331

332+
async validate_config_dir_exists(path, type) {
333+
const config_root_type_exists = await this.config_fs.validate_config_dir_exists(path);
334+
if (!config_root_type_exists) {
335+
dbg.log1(`Config directory type - ${type} is missing, ${path}`);
336+
return {
337+
invalid_storages: [],
338+
valid_storages: []
339+
};
340+
}
341+
}
342+
326343
async get_bucket_status() {
327344
const bucket_details = await this.get_storage_status(TYPES.BUCKET, this.all_bucket_details);
328345
return bucket_details;
@@ -333,34 +350,38 @@ class NSFSHealth {
333350
return account_details;
334351
}
335352

353+
async get_connection_status() {
354+
const connection_details = await this.get_storage_status(TYPES.CONNECTION, this.all_connection_details);
355+
//if there were in failed test notifications, mark error as temp
356+
if (connection_details.invalid_storages && connection_details.invalid_storages.length > 0) {
357+
connection_details.error_type = health_errors_tyes.TEMPORARY;
358+
}
359+
return connection_details;
360+
}
361+
336362
async get_storage_status(type, all_details) {
337363
const invalid_storages = [];
338364
const valid_storages = [];
339365
//check for account and buckets dir paths
340-
let config_root_type_exists;
341366
let config_dir_path;
342367
if (type === TYPES.BUCKET) {
343368
config_dir_path = this.config_fs.buckets_dir_path;
344-
config_root_type_exists = await this.config_fs.validate_config_dir_exists(config_dir_path);
345369
} else if (type === TYPES.ACCOUNT) {
346370
// TODO - handle iam accounts when directory structure changes - read_account_by_id
347371
config_dir_path = this.config_fs.accounts_by_name_dir_path;
348-
config_root_type_exists = await this.config_fs.validate_config_dir_exists(config_dir_path);
349-
}
350-
// TODO - this is not a good handling for that - we need to take it to an upper level
351-
if (!config_root_type_exists) {
352-
dbg.log1(`Config directory type - ${type} is missing, ${config_dir_path}`);
353-
return {
354-
invalid_storages: invalid_storages,
355-
valid_storages: valid_storages
356-
};
372+
} else {
373+
config_dir_path = this.config_fs.connections_dir_path;
357374
}
375+
const missing = await this.validate_config_dir_exists(config_dir_path, type);
376+
if (missing) return missing;
358377

359378
let config_files_names;
360379
if (type === TYPES.BUCKET) {
361380
config_files_names = await this.config_fs.list_buckets();
362-
} else {
381+
} else if (type === TYPES.ACCOUNT) {
363382
config_files_names = await this.config_fs.list_accounts();
383+
} else {
384+
config_files_names = await this.config_fs.list_connections();
364385
}
365386
for (const config_file_name of config_files_names) {
366387
// config_file get data or push error
@@ -376,13 +397,24 @@ class NSFSHealth {
376397
let res;
377398
const storage_path = type === TYPES.BUCKET ?
378399
config_data.path :
379-
config_data.nsfs_account_config.new_buckets_path;
400+
config_data.nsfs_account_config?.new_buckets_path;
380401

381402
if (type === TYPES.ACCOUNT) {
382403
const config_file_path = this.config_fs.get_account_path_by_name(config_file_name);
383404
res = await is_new_buckets_path_valid(config_file_path, config_data, storage_path);
384405
} else if (type === TYPES.BUCKET) {
385406
res = await is_bucket_storage_and_owner_exists(this.config_fs, config_data, storage_path);
407+
} else {
408+
const connection_file_path = this.config_fs.get_connection_path_by_name(config_file_name);
409+
const test_notif_err = await notifications_util.test_notifications([{
410+
name: config_data.name,
411+
topic: [this.config_fs.json(config_file_name)]
412+
}], this.config_fs.config_root);
413+
if (test_notif_err) {
414+
res = get_invalid_object(config_data.name, connection_file_path, undefined, test_notif_err.code);
415+
} else {
416+
res = get_valid_object(config_data.name, connection_file_path, undefined);
417+
}
386418
}
387419
if (all_details && res.valid_storage) {
388420
valid_storages.push(res.valid_storage);
@@ -406,16 +438,41 @@ class NSFSHealth {
406438
let config_data;
407439
let err_obj;
408440
try {
409-
config_data = type === TYPES.BUCKET ?
410-
await this.config_fs.get_bucket_by_name(config_file_name) :
411-
// TODO - should be changed to id when moving to new structure for supporting iam accounts
412-
await this.config_fs.get_account_by_name(config_file_name);
441+
switch (type) {
442+
case TYPES.BUCKET: {
443+
config_data = await this.config_fs.get_bucket_by_name(config_file_name);
444+
break;
445+
}
446+
case TYPES.ACCOUNT: {
447+
// TODO - should be changed to id when moving to new structure for supporting iam accounts
448+
config_data = await this.config_fs.get_account_by_name(config_file_name);
449+
break;
450+
}
451+
case TYPES.CONNECTION: {
452+
config_data = await this.config_fs.get_connection_by_name(config_file_name);
453+
break;
454+
}
455+
default: //shouldn't happen, for js-lint
456+
}
413457
} catch (err) {
414458
let err_code;
415-
const config_file_path = type === TYPES.BUCKET ?
416-
this.config_fs.get_bucket_path_by_name(config_file_name) :
417-
// TODO - should be changed to id when moving to new structure for supporting iam accounts
418-
this.config_fs.get_account_path_by_name(config_file_name);
459+
let config_file_path;
460+
switch (type) {
461+
case TYPES.BUCKET: {
462+
config_file_path = await this.config_fs.get_bucket_path_by_name(config_file_name);
463+
break;
464+
}
465+
case TYPES.ACCOUNT: {
466+
// TODO - should be changed to id when moving to new structure for supporting iam accounts
467+
config_file_path = await this.config_fs.get_account_path_by_name(config_file_name);
468+
break;
469+
}
470+
case TYPES.CONNECTION: {
471+
config_file_path = await this.config_fs.get_connection_path_by_name(config_file_name);
472+
break;
473+
}
474+
default: //shouldn't happen, for js-lint
475+
}
419476

420477
if (err.code === 'ENOENT') {
421478
dbg.log1(`Error: Config file path should be a valid path`, config_file_path, err);
@@ -536,9 +593,10 @@ async function get_health_status(argv, config_fs) {
536593
const deployment_type = argv.deployment_type || 'nc';
537594
const all_account_details = get_boolean_or_string_value(argv.all_account_details);
538595
const all_bucket_details = get_boolean_or_string_value(argv.all_bucket_details);
596+
const all_connection_details = get_boolean_or_string_value(argv.all_connection_details);
539597

540598
if (deployment_type === 'nc') {
541-
const health = new NSFSHealth({ https_port, all_account_details, all_bucket_details, config_fs });
599+
const health = new NSFSHealth({ https_port, all_account_details, all_bucket_details, all_connection_details, config_fs });
542600
const health_status = await health.nc_nsfs_health();
543601
write_stdout_response(ManageCLIResponse.HealthStatus, health_status);
544602
} else {

src/manage_nsfs/manage_nsfs_constants.js

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

7777
const VALID_OPTIONS_DIAGNOSE = {
78-
'health': new Set([ 'https_port', 'deployment_type', 'all_account_details', 'all_bucket_details', ...CLI_MUTUAL_OPTIONS]),
78+
'health': new Set([ 'https_port', 'deployment_type', 'all_account_details', 'all_bucket_details', 'all_connection_details', ...CLI_MUTUAL_OPTIONS]),
7979
'gather-logs': new Set([ CONFIG_ROOT_FLAG]),
8080
'metrics': new Set([CONFIG_ROOT_FLAG])
8181
};
@@ -146,6 +146,7 @@ const OPTION_TYPE = {
146146
deployment_type: 'string',
147147
all_account_details: 'boolean',
148148
all_bucket_details: 'boolean',
149+
all_connection_details: 'boolean',
149150
https_port: 'number',
150151
debug: 'number',
151152
// 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
@@ -352,10 +352,12 @@ async function test_notifications(notifs, nc_config_dir) {
352352
return;
353353
}
354354
let connect_files_dir = config.NOTIFICATION_CONNECT_DIR;
355+
let config_fs;
355356
if (nc_config_dir) {
356-
connect_files_dir = new ConfigFS(nc_config_dir).connections_dir_path;
357+
config_fs = new ConfigFS(nc_config_dir);
358+
connect_files_dir = config_fs.connections_dir_path;
357359
}
358-
const notificator = new Notificator({connect_files_dir});
360+
const notificator = new Notificator({connect_files_dir, nc_config_fs: config_fs});
359361
for (const notif of notifs) {
360362
let connect;
361363
let connection;

0 commit comments

Comments
 (0)