-
Notifications
You must be signed in to change notification settings - Fork 255
Expand file tree
/
Copy pathobjectRestore.js
More file actions
172 lines (160 loc) · 7.4 KB
/
objectRestore.js
File metadata and controls
172 lines (160 loc) · 7.4 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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
const async = require('async');
const { errors, s3middleware } = require('arsenal');
const { allowedRestoreObjectRequestTierValues } = require('../../../../constants');
const coldStorage = require('./coldStorage');
const monitoring = require('../../../utilities/monitoringHandler');
const { pushMetric } = require('../../../utapi/utilities');
const { decodeVersionId } = require('./versioning');
const collectCorsHeaders = require('../../../utilities/collectCorsHeaders');
const { parseRestoreRequestXml } = s3middleware.objectRestore;
const { processBytesToWrite, validateQuotas } = require('../quotas/quotaUtils');
/**
* Check if tier is supported
* @param {object} restoreInfo - restore information
* @returns {ArsenalError|undefined} return NotImplemented error if tier not support
*/
function checkTierSupported(restoreInfo) {
if (!allowedRestoreObjectRequestTierValues.includes(restoreInfo.tier)) {
return errors.NotImplemented;
}
return undefined;
}
/**
* POST Object restore process
*
* @param {MetadataWrapper} metadata - metadata wrapper
* @param {object} mdUtils - utility object to treat metadata
* @param {AuthInfo} userInfo - Instance of AuthInfo class with requester's info
* @param {IncomingMessage} request - request info
* @param {object} log - Werelogs logger
* @param {function} callback callback function
* @return {undefined}
*/
function objectRestore(metadata, mdUtils, userInfo, request, log, callback) {
const METHOD = 'objectRestore';
const { bucketName, objectKey } = request;
log.debug('processing request', { method: METHOD });
const decodedVidResult = decodeVersionId(request.query);
if (decodedVidResult instanceof Error) {
log.trace('invalid versionId query',
{
method: METHOD,
versionId: request.query.versionId,
error: decodedVidResult,
});
return process.nextTick(() => callback(decodedVidResult));
}
let isObjectRestored = false;
const mdValueParams = {
authInfo: userInfo,
bucketName,
objectKey,
versionId: decodedVidResult,
requestType: request.apiMethods || 'restoreObject',
/**
* Restoring an object might not cause any impact on
* the storage, if the object is already restored: in
* this case, the duration is extended. We disable the
* quota evaluation and trigger it manually.
*/
checkQuota: false,
request,
};
return async.waterfall([
// get metadata of bucket and object
function validateBucketAndObject(next) {
return mdUtils.standardMetadataValidateBucketAndObj(mdValueParams, request.actionImplicitDenies,
log, (err, bucketMD, objectMD) => {
if (err) {
log.trace('request authorization failed', { method: METHOD, error: err });
return next(err);
}
// Call back error if object metadata could not be obtained
if (!objectMD) {
const err = decodedVidResult ? errors.NoSuchVersion : errors.NoSuchKey;
log.trace('error no object metadata found', { method: METHOD, error: err });
return next(err, bucketMD);
}
// If object metadata is delete marker,
// call back NoSuchKey or MethodNotAllowed depending on specifying versionId
if (objectMD.isDeleteMarker) {
let err = errors.NoSuchKey;
if (decodedVidResult) {
err = errors.MethodNotAllowed;
}
log.trace('version is a delete marker', { method: METHOD, error: err });
return next(err, bucketMD, objectMD);
}
log.debug('acquired the object metadata.', {
'method': METHOD,
});
return next(null, bucketMD, objectMD);
});
},
// generate restore param obj from xml of request body and check tier validity
function parseRequestXmlAndCheckTier(bucketMD, objectMD, next) {
log.trace('parsing object restore information');
return parseRestoreRequestXml(request.post, log, (err, restoreInfo) => {
if (err) {
return next(err, bucketMD, objectMD, restoreInfo);
}
log.debug('parsed xml of the request body.', { method: METHOD, value: restoreInfo });
const checkTierResult = checkTierSupported(restoreInfo);
if (checkTierResult instanceof Error) {
return next(checkTierResult);
}
return next(null, bucketMD, objectMD, restoreInfo);
});
},
// start restore process
function startRestore(bucketMD, objectMD, restoreInfo, next) {
return coldStorage.startRestore(objectMD, restoreInfo, log,
(err, _isObjectRestored) => {
isObjectRestored = _isObjectRestored;
return next(err, bucketMD, objectMD);
});
},
function evaluateQuotas(bucketMD, objectMD, next) {
if (isObjectRestored) {
return next(null, bucketMD, objectMD);
}
const actions = Array.isArray(mdValueParams.requestType) ?
mdValueParams.requestType : [mdValueParams.requestType];
const bytes = processBytesToWrite(request.apiMethod, bucketMD, mdValueParams.versionId, 0, objectMD);
return validateQuotas(request, bucketMD, request.accountQuotas, actions, request.apiMethod, bytes,
false, log, err => next(err, bucketMD, objectMD));
},
function updateObjectMD(bucketMD, objectMD, next) {
const params = objectMD.versionId ? { versionId: objectMD.versionId } : {};
metadata.putObjectMD(bucketMD.getName(), objectKey, objectMD, params,
log, err => next(err, bucketMD, objectMD));
},
],
(err, bucketMD) => {
// generate CORS response header
const responseHeaders = collectCorsHeaders(request.headers.origin, request.method, bucketMD);
if (err) {
log.trace('error processing request',
{
method: METHOD,
error: err,
});
monitoring.promMetrics(
'POST', bucketName, err.code, 'restoreObject');
return callback(err, err.code, responseHeaders);
}
pushMetric('restoreObject', log, {
userInfo,
bucket: bucketName,
});
if (isObjectRestored) {
monitoring.promMetrics(
'POST', bucketName, '200', 'restoreObject');
return callback(null, 200, responseHeaders);
}
monitoring.promMetrics(
'POST', bucketName, '202', 'restoreObject');
return callback(null, 202, responseHeaders);
});
}
module.exports = objectRestore;