Skip to content

Commit a14183c

Browse files
authored
bucket notifications - validate notifications conf on change (gh issue 8649) (#8667)
Signed-off-by: Amit Prinz Setter <[email protected]>
1 parent 0394c0d commit a14183c

File tree

4 files changed

+77
-18
lines changed

4 files changed

+77
-18
lines changed

src/cmd/manage_nsfs.js

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ async function get_bucket_status(data) {
203203
* @param {Object} data
204204
* @returns { Promise<{ code: typeof ManageCLIResponse.BucketUpdated, detail: Object }>}
205205
*/
206-
async function update_bucket(data, user_input) {
206+
async function update_bucket(data) {
207207
const cur_name = data.name;
208208
const new_name = data.new_name;
209209
const name_update = is_name_update(data);
@@ -212,13 +212,6 @@ async function update_bucket(data, user_input) {
212212

213213
let parsed_bucket_data;
214214

215-
if (user_input.notifications) {
216-
//notifications are tested before they can be updated
217-
const test_notif_err = await notifications_util.test_notifications(data, config_fs.connect_dir_path);
218-
if (test_notif_err) {
219-
throw_cli_error(ManageCLIError.InvalidArgument, "Failed to update notifications", test_notif_err);
220-
}
221-
}
222215
if (name_update) {
223216
parsed_bucket_data = await config_fs.create_bucket_config_file({ ...data, name: new_name });
224217
await config_fs.delete_bucket_config_file(cur_name);
@@ -279,14 +272,15 @@ async function delete_bucket(data, force) {
279272
async function bucket_management(action, user_input) {
280273
const data = action === ACTIONS.LIST ? undefined : await fetch_bucket_data(action, user_input);
281274
await manage_nsfs_validations.validate_bucket_args(config_fs, data, action);
275+
await manage_nsfs_validations.validate_bucket_notifications(config_fs, user_input.notifications);
282276

283277
let response = {};
284278
if (action === ACTIONS.ADD) {
285279
response = await add_bucket(data);
286280
} else if (action === ACTIONS.STATUS) {
287281
response = await get_bucket_status(data);
288282
} else if (action === ACTIONS.UPDATE) {
289-
response = await update_bucket(data, user_input);
283+
response = await update_bucket(data);
290284
} else if (action === ACTIONS.DELETE) {
291285
const force = get_boolean_or_string_value(user_input.force);
292286
response = await delete_bucket(data, force);

src/endpoint/s3/ops/s3_put_bucket_notification.js

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
/* Copyright (C) 2016 NooBaa */
22
'use strict';
33

4+
const S3Error = require('../s3_errors').S3Error;
5+
const notif_util = require('../../../util/notifications_util');
6+
47
/**
58
* http://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketPUTnotification.html
69
*/
@@ -22,6 +25,19 @@ async function put_bucket_notification(req) {
2225
topic_configuration = [];
2326
}
2427

28+
//test new notifications.
29+
//if test notification fails, fail the put op
30+
const err = await notif_util.test_notifications(topic_configuration,
31+
req.object_sdk.nsfs_config_root);
32+
if (err) {
33+
throw new S3Error({
34+
code: 'InvalidArgument',
35+
message: JSON.stringify(err.message),
36+
http_code: 400,
37+
detail: err.toString()
38+
});
39+
}
40+
2541
const reply = await req.object_sdk.put_bucket_notification({
2642
bucket_name: req.params.bucket,
2743
notifications: topic_configuration

src/manage_nsfs/manage_nsfs_validations.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const { TYPES, ACTIONS, VALID_OPTIONS, OPTION_TYPE, FROM_FILE, BOOLEAN_STRING_VA
1515
GLACIER_ACTIONS, LIST_UNSETABLE_OPTIONS, ANONYMOUS, DIAGNOSE_ACTIONS, UPGRADE_ACTIONS } = require('../manage_nsfs/manage_nsfs_constants');
1616
const { check_root_account_owns_user } = require('../nc/nc_utils');
1717
const { validate_username } = require('../util/validation_utils');
18+
const notifications_util = require('../util/notifications_util');
1819

1920
/////////////////////////////
2021
//// GENERAL VALIDATIONS ////
@@ -447,6 +448,24 @@ async function validate_bucket_args(config_fs, data, action) {
447448
}
448449
}
449450

451+
/**
452+
* When setting notifications, we are supposed to send a test notification.
453+
* If this test fails, we fail the user's request.
454+
* @param {object} config_fs
455+
* @param {object} notifications bucket's notification conf
456+
* @returns {Promise} Error encountered during notification test, if any
457+
*/
458+
async function validate_bucket_notifications(config_fs, notifications) {
459+
if (!notifications) {
460+
return;
461+
}
462+
//test_notification returns an error if notification fails for the given config
463+
const test_notif_err = await notifications_util.test_notifications(notifications, config_fs.config_root);
464+
if (test_notif_err) {
465+
throw_cli_error(ManageCLIError.InvalidArgument, "Failed to update notifications", test_notif_err);
466+
}
467+
}
468+
450469
/////////////////////////////
451470
//// ACCOUNT VALIDATIONS ////
452471
/////////////////////////////
@@ -682,6 +701,7 @@ function validate_connection_args(user_input, action) {
682701
// EXPORTS
683702
exports.validate_input_types = validate_input_types;
684703
exports.validate_bucket_args = validate_bucket_args;
704+
exports.validate_bucket_notifications = validate_bucket_notifications;
685705
exports.validate_account_args = validate_account_args;
686706
exports._validate_access_keys = _validate_access_keys;
687707
exports.validate_root_accounts_manager_update = validate_root_accounts_manager_update;

src/util/notifications_util.js

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const nb_native = require('../util/nb_native');
1515
const http_utils = require('../util/http_utils');
1616
const nc_mkm = require('../manage_nsfs/nc_master_key_manager').get_instance();
1717
const NoobaaEvent = require('../manage_nsfs/manage_nsfs_events_utils').NoobaaEvent;
18+
const {ConfigFS} = require('../sdk/config_fs');
1819

1920
const OP_TO_EVENT = Object.freeze({
2021
put_object: { name: 'ObjectCreated' },
@@ -338,23 +339,51 @@ function get_connection(connect) {
338339
}
339340
}
340341

342+
/**
343+
* Try to send a test notification for each of the notification configuration.
344+
* If there's a failure, return it.
345+
* @param {*} notifs notificatoin config to test
346+
* @param {*} nc_config_dir path to NC conf dir (if system is NC)
347+
* @returns {Promise} Error while testing, if any.
348+
*/
341349

342-
async function test_notifications(bucket, connect_files_dir) {
343-
if (!bucket.notifications) {
350+
async function test_notifications(notifs, nc_config_dir) {
351+
//notifs can be empty in case we're removing the notification from the bucket
352+
if (!notifs) {
344353
return;
345354
}
355+
let connect_files_dir = DEFAULT_CONNECT_FILES_DIR;
356+
if (nc_config_dir) {
357+
connect_files_dir = new ConfigFS(nc_config_dir).connections_dir_path;
358+
}
346359
const notificator = new Notificator({connect_files_dir});
347-
for (const notif of bucket.notifications) {
348-
const connect = await notificator.parse_connect_file(notif.connect, true);
349-
dbg.log1("testing notif", notif);
360+
for (const notif of notifs) {
361+
let connect;
362+
let connection;
363+
let failure = false;
364+
let notif_failure;
350365
try {
351-
const connection = get_connection(connect);
366+
connect = await notificator.parse_connect_file(notif.topic[0]);
367+
connection = get_connection(connect);
352368
await connection.connect();
353-
await connection.promise_notify({notif: "test notification"}, async err => err);
354-
connection.destroy();
369+
await connection.promise_notify({notif: "test notification"}, async (notif_cb, err_cb) => {
370+
failure = true;
371+
notif_failure = err_cb;
372+
});
373+
if (failure) {
374+
if (notif_failure) {
375+
throw notif_failure;
376+
}
377+
//no error was thrown during notify, throw generic error
378+
throw new Error();
379+
}
355380
} catch (err) {
356-
dbg.error("Connection failed for", connect);
381+
dbg.error("Connection failed for", notif, ", connect =", connect, ", err = ", err);
357382
return err;
383+
} finally {
384+
if (connection) {
385+
connection.destroy();
386+
}
358387
}
359388
}
360389
}

0 commit comments

Comments
 (0)