-
Notifications
You must be signed in to change notification settings - Fork 254
Expand file tree
/
Copy pathbucketDeletion.js
More file actions
151 lines (144 loc) · 6.5 KB
/
bucketDeletion.js
File metadata and controls
151 lines (144 loc) · 6.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
const assert = require('assert');
const async = require('async');
const { errors } = require('arsenal');
const abortMultipartUpload = require('../object/abortMultipartUpload');
const { pushMetric } = require('../../../utapi/utilities');
const { splitter, oldSplitter, mpuBucketPrefix } =
require('../../../../constants');
const metadata = require('../../../metadata/wrapper');
const kms = require('../../../kms/wrapper');
const deleteUserBucketEntry = require('./deleteUserBucketEntry');
function _deleteMPUbucket(destinationBucketName, log, cb) {
const mpuBucketName =
`${mpuBucketPrefix}${destinationBucketName}`;
return metadata.deleteBucket(mpuBucketName, log, err => {
// If the mpu bucket does not exist, just move on
// TODO: move to `.is` once BKTCLT-9 is done and bumped in Cloudserver
if (err && err.NoSuchBucket) {
return cb();
}
return cb(err);
});
}
function _deleteOngoingMPUs(authInfo, bucketName, bucketMD, mpus, request, log, cb) {
async.mapLimit(mpus, 1, (mpu, next) => {
const splitterChar = mpu.key.includes(oldSplitter) ?
oldSplitter : splitter;
// `overview${splitter}${objectKey}${splitter}${uploadId}
const [, objectKey, uploadId] = mpu.key.split(splitterChar);
abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
(err, destBucket, partSizeSum) => {
pushMetric('abortMultipartUpload', log, {
authInfo,
canonicalID: bucketMD.getOwner(),
bucket: bucketName,
keys: [objectKey],
byteLength: partSizeSum,
});
next(err);
}, request);
}, cb);
}
/**
* deleteBucket - Delete bucket from namespace
* @param {object} authInfo - authentication info
* @param {object} bucketMD - bucket attributes/metadata
* @param {string} bucketName - bucket in which objectMetadata is stored
* @param {string} canonicalID - account canonicalID of requester
* @param {object} request - request object given by router
* including normalized headers
* @param {object} log - Werelogs logger
* @param {function} cb - callback from async.waterfall in bucketDelete
* @return {undefined}
*/
function deleteBucket(authInfo, bucketMD, bucketName, canonicalID, request, log, cb) {
log.trace('deleting bucket from metadata');
assert.strictEqual(typeof bucketName, 'string');
assert.strictEqual(typeof canonicalID, 'string');
return async.waterfall([
function checkForObjectsStep(next) {
const params = { maxKeys: 1, listingType: 'DelimiterVersions' };
// We list all the versions as we want to return BucketNotEmpty
// error if there are any versions or delete markers in the bucket.
// Works for non-versioned buckets as well since listing versions
// includes null (non-versioned) objects in the result.
return metadata.listObject(bucketName, params, log,
(err, list) => {
if (err) {
log.error('error from metadata', { error: err });
return next(err);
}
const length = (list.Versions ? list.Versions.length : 0) +
(list.DeleteMarkers ? list.DeleteMarkers.length : 0);
log.debug('listing result', { length });
if (length) {
log.debug('bucket delete failed',
{ error: errors.BucketNotEmpty });
return next(errors.BucketNotEmpty);
}
return next();
});
},
function deleteMPUbucketStep(next) {
const MPUBucketName = `${mpuBucketPrefix}${bucketName}`;
// check to see if there are any mpu overview objects (so ignore
// any orphaned part objects)
return metadata.listObject(MPUBucketName, { prefix: 'overview' },
log, (err, objectsListRes) => {
// If no shadow bucket ever created, no ongoing MPU's, so
// continue with deletion
if (err && err.is.NoSuchBucket) {
return next();
}
if (err) {
log.error('error from metadata', { error: err });
return next(err);
}
if (objectsListRes.Contents.length) {
return _deleteOngoingMPUs(authInfo, bucketName,
bucketMD, objectsListRes.Contents, request, log, err => {
if (err) {
return next(err);
}
log.trace('deleting shadow MPU bucket');
return _deleteMPUbucket(bucketName, log, next);
});
}
log.trace('deleting shadow MPU bucket');
return _deleteMPUbucket(bucketName, log, next);
});
},
function addDeleteFlagStep(next) {
log.trace('adding deleted attribute to bucket attributes');
// Remove transient flag if any so never have both transient
// and deleted flags.
bucketMD.removeTransientFlag();
bucketMD.addDeletedFlag();
return metadata.updateBucket(bucketName, bucketMD, log, next);
},
function deleteUserBucketEntryStep(next) {
log.trace('deleting bucket name from user bucket');
return deleteUserBucketEntry(bucketName, canonicalID, log, next);
},
],
// eslint-disable-next-line prefer-arrow-callback
function actualDeletionStep(err) {
if (err) {
return cb(err);
}
return metadata.deleteBucket(bucketName, log, err => {
log.trace('deleting bucket from metadata');
if (err) {
return cb(err);
}
const serverSideEncryption = bucketMD.getServerSideEncryption();
if (serverSideEncryption &&
serverSideEncryption.algorithm === 'AES256') {
const masterKeyId = serverSideEncryption.masterKeyId;
return kms.destroyBucketKey(masterKeyId, log, cb);
}
return cb();
});
});
}
module.exports = deleteBucket;