Skip to content

Commit 2a2c184

Browse files
authored
Merge pull request #8919 from alphaprinz/notif_backports
[Backport into 5.18] Notif backports
2 parents ea9f9b9 + e742a5a commit 2a2c184

File tree

8 files changed

+106
-21
lines changed

8 files changed

+106
-21
lines changed

src/api/object_api.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,9 +1166,23 @@ module.exports = {
11661166
deleted_objects: {
11671167
type: 'array',
11681168
items: {
1169-
$ref: '#/definitions/object_info'
1169+
oneOf: [
1170+
{
1171+
$ref: '#/definitions/object_info',
1172+
},
1173+
{
1174+
type: 'object',
1175+
properties: {
1176+
err_code: {
1177+
type: 'string',
1178+
enum: ['AccessDenied', 'InternalError']
1179+
},
1180+
err_message: { type: 'string' }
1181+
}
1182+
}
1183+
]
11701184
}
1171-
}
1185+
},
11721186
}
11731187
},
11741188
auth: { system: 'admin' }

src/endpoint/s3/ops/s3_post_object_uploadId.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ async function post_object_uploadId(req, res) {
4343
res.setHeader('x-amz-version-id', reply.version_id);
4444
}
4545

46+
res.size_for_notif = reply.size;
47+
4648
return {
4749
CompleteMultipartUploadResult: {
4850
Bucket: req.params.bucket,

src/endpoint/s3/s3_bucket_logging.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const { Buffer } = require('node:buffer');
88
const config = require('../../../config');
99
const {compose_notification_req, check_notif_relevant, check_free_space_if_needed} = require('../../util/notifications_util');
1010

11-
async function send_bucket_op_logs(req, res) {
11+
async function send_bucket_op_logs(req, res, reply) {
1212
if (req.params && req.params.bucket &&
1313
!(req.op_name === 'put_bucket' ||
1414
req.op_name === 'delete_bucket' ||
@@ -36,8 +36,10 @@ async function send_bucket_op_logs(req, res) {
3636

3737
if (req.notification_logger && bucket_info.notifications) {
3838
for (const notif_conf of bucket_info.notifications) {
39-
if (check_notif_relevant(notif_conf, req)) {
40-
const notif = compose_notification_req(req, res, bucket_info, notif_conf);
39+
//write the notification log only if request is successful (ie res.statusCode < 300)
40+
//and event is actually relevant to the notification conf
41+
if (res.statusCode < 300 && check_notif_relevant(notif_conf, req)) {
42+
const notif = compose_notification_req(req, res, bucket_info, notif_conf, reply);
4143
dbg.log1("logging notif ", notif_conf, ", notif = ", notif);
4244
writes_aggregate.push({
4345
file: req.notification_logger,

src/endpoint/s3/s3_rest.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ async function handle_request(req, res) {
175175
http_utils.send_reply(req, res, reply, options);
176176
collect_bucket_usage(op, req, res);
177177
try {
178-
await s3_logging.send_bucket_op_logs(req, res); // logging again with result
178+
await s3_logging.send_bucket_op_logs(req, res, reply); // logging again with result
179179
} catch (err) {
180180
dbg.error(`Could not log bucket operation (after operation ${req.op_name}):`, err);
181181
}

src/server/bg_services/lifecycle.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ async function handle_bucket_rule(system, rule, j, bucket) {
6868
})
6969
});
7070

71-
//dbg.log0("LIFECYCLE PROCESSING res =", res);
72-
7371
if (res.deleted_objects) {
7472

7573
const writes = [];
7674

7775
for (const deleted_obj of res.deleted_objects) {
76+
//if deletion has failed, don't send a notification
77+
if (deleted_obj.err_code) continue;
7878
for (const notif of bucket.notifications) {
7979
if (check_notif_relevant(notif, {
8080
op_name: 'lifecycle_delete',

src/server/object_services/object_server.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -964,7 +964,7 @@ async function delete_multiple_objects_by_filter(req) {
964964

965965
const { objects } = await MDStore.instance().find_objects(query);
966966

967-
await delete_multiple_objects(_.assign(req, {
967+
const delete_results = await delete_multiple_objects(_.assign(req, {
968968
rpc_params: {
969969
bucket: req.bucket.name,
970970
objects: _.map(objects, obj => ({
@@ -978,8 +978,19 @@ async function delete_multiple_objects_by_filter(req) {
978978
if (reply_objects) {
979979
//reply needs to include deleted objects
980980
//(this is used for LifecycleExpiratoin event notifications)
981-
//so map the md into (api friendly) object info
982-
reply.deleted_objects = _.map(objects, get_object_info);
981+
//so map the md into (api friendly) object info,
982+
//or incude the error if deletion failed
983+
reply.deleted_objects = [];
984+
for (let i = 0; i < objects.length; ++i) {
985+
if (delete_results[i].err_code) {
986+
reply.deleted_objects[i] = {
987+
err_code: delete_results[i].err_code,
988+
err_message: delete_results[i].err_message
989+
};
990+
} else {
991+
reply.deleted_objects[i] = get_object_info(objects[i]);
992+
}
993+
}
983994
}
984995

985996
return reply;

src/test/unit_tests/test_notifications.js

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ let http_server = null;
5252
let server_done = false;
5353
let expected_bucket;
5454
let expected_event_name;
55+
let expected_key;
56+
let expected_eTag;
5557

5658
// eslint-disable-next-line max-lines-per-function
5759
mocha.describe('notifications', function() {
@@ -91,9 +93,10 @@ mocha.describe('notifications', function() {
9193
if (notif !== "test notification") {
9294
assert.strictEqual(notif.Records[0].s3.bucket.name, expected_bucket, 'wrong bucket name in notification');
9395
assert.strictEqual(notif.Records[0].eventName, expected_event_name, 'wrong event name in notification');
94-
96+
assert.strictEqual(notif.Records[0].s3.object.key, expected_key, 'wrong key in notification');
97+
const expected_eTag_trimmed = expected_eTag && expected_eTag.substring(1, expected_eTag.length - 1);
98+
assert.strictEqual(notif.Records[0].s3.object.eTag, expected_eTag_trimmed, 'wrong eTag in notification');
9599
}
96-
97100
res.writeHead(200, {'Content-Type': 'text/plain'});
98101
res.end();
99102
server_done = true;
@@ -120,13 +123,18 @@ mocha.describe('notifications', function() {
120123
});
121124

122125
mocha.it('simple notif put', async () => {
123-
await s3.putObject({
126+
const res = await s3.putObject({
124127
Bucket: bucket,
125128
Key: 'f1',
126129
Body: 'this is the body',
127130
});
128131

129-
await notify_await_result({bucket_name: bucket, event_name: 'ObjectCreated:Put'});
132+
await notify_await_result({
133+
bucket_name: bucket,
134+
event_name: 'ObjectCreated:Put',
135+
key: "f1",
136+
etag: res.ETag
137+
});
130138
});
131139

132140

@@ -136,7 +144,12 @@ mocha.describe('notifications', function() {
136144
Key: 'f1',
137145
});
138146

139-
await notify_await_result({bucket_name: bucket, event_name: 'ObjectRemoved:Delete'});
147+
await notify_await_result({
148+
bucket_name: bucket,
149+
event_name: 'ObjectRemoved:Delete',
150+
key: "f1",
151+
etag: undefined
152+
});
140153
});
141154

142155

@@ -155,13 +168,18 @@ mocha.describe('notifications', function() {
155168

156169
assert.strictEqual(set.$metadata.httpStatusCode, 200);
157170

158-
await s3.putObject({
171+
const res = await s3.putObject({
159172
Bucket: bucket,
160173
Key: 'f2',
161174
Body: 'this is the body',
162175
});
163176

164-
await notify_await_result({bucket_name: bucket, event_name: 'ObjectCreated:Put'});
177+
await notify_await_result({
178+
bucket_name: bucket,
179+
event_name: 'ObjectCreated:Put',
180+
key: "f2",
181+
etag: res.ETag
182+
});
165183

166184
await s3.deleteObject({
167185
Bucket: bucket,
@@ -171,16 +189,53 @@ mocha.describe('notifications', function() {
171189
//there shouldn't be a notification for the delete, wait 2 seconds to validate this
172190
await notify_await_result({timeout: 2000});
173191
});
192+
193+
mocha.it('multipart', async () => {
194+
const res_create = await s3.createMultipartUpload({
195+
Bucket: bucket,
196+
Key: 'mp1'
197+
});
198+
199+
const res_upload = await s3.uploadPart({
200+
Bucket: bucket,
201+
Key: 'mp1',
202+
UploadId: res_create.UploadId,
203+
PartNumber: 1,
204+
Body: 'this is the body'
205+
});
206+
207+
const res_complete = await s3.completeMultipartUpload({
208+
Bucket: bucket,
209+
Key: 'mp1',
210+
UploadId: res_create.UploadId,
211+
MultipartUpload: {
212+
Parts: [{
213+
ETag: res_upload.ETag,
214+
PartNumber: 1
215+
}]
216+
}
217+
});
218+
219+
await notify_await_result({
220+
bucket_name: bucket,
221+
event_name: 'ObjectCreated:CompleteMultipartUpload',
222+
key: "mp1",
223+
etag: res_complete.ETag
224+
});
225+
});
226+
174227
});
175228

176229
});
177230

178231
const step_wait = 100;
179-
async function notify_await_result({bucket_name, event_name, timeout}) {
232+
async function notify_await_result({bucket_name, event_name, etag, key, timeout = undefined}) {
180233

181234
//remember expected result here so server could compare it to actual result later
182235
expected_bucket = bucket_name;
183236
expected_event_name = event_name;
237+
expected_eTag = etag;
238+
expected_key = key;
184239
server_done = false;
185240

186241
//busy-sync wait for server

src/util/notifications_util.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,9 @@ function compose_notification_base(notif_conf, bucket, req) {
418418
}
419419

420420
//see https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-content-structure.html
421-
function compose_notification_req(req, res, bucket, notif_conf) {
422-
let eTag = res.getHeader('ETag');
421+
function compose_notification_req(req, res, bucket, notif_conf, reply) {
422+
//most s3 ops put etag in header. CompleteMultipartUploadResult is an exception.
423+
let eTag = res.getHeader('ETag') || reply?.CompleteMultipartUploadResult?.ETag;
423424
//eslint-disable-next-line
424425
if (eTag && eTag.startsWith('\"') && eTag.endsWith('\"')) {
425426
eTag = eTag.substring(1, eTag.length - 1);

0 commit comments

Comments
 (0)