-
Notifications
You must be signed in to change notification settings - Fork 259
Allow enabling versioning on buckets with a GCP location constraint #6266
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: development/9.5
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,23 +6,20 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders'); | |
| const metadata = require('../metadata/wrapper'); | ||
| const { standardMetadataValidateBucket } = require('../metadata/metadataUtils'); | ||
| const { pushMetric } = require('../utapi/utilities'); | ||
| const versioningNotImplBackends = | ||
| require('../../constants').versioningNotImplBackends; | ||
| const { versioningNotImplBackends, externalVersioningErrorMessage } = require('../../constants'); | ||
| const { config } = require('../Config'); | ||
| const monitoring = require('../utilities/monitoringHandler'); | ||
|
|
||
| const externalVersioningErrorMessage = 'We do not currently support putting ' + | ||
| 'a versioned object to a location-constraint of type Azure or GCP.'; | ||
| const replicationVersioningErrorMessage = | ||
| 'A replication configuration is ' + | ||
| 'present on this bucket, so you cannot change the versioning state. To ' + | ||
| 'change the versioning state, first delete the replication configuration.'; | ||
|
|
||
| const replicationVersioningErrorMessage = 'A replication configuration is ' + | ||
| 'present on this bucket, so you cannot change the versioning state. To ' + | ||
| 'change the versioning state, first delete the replication configuration.'; | ||
| const ingestionVersioningErrorMessage = | ||
| 'Versioning cannot be suspended for ' + 'buckets setup with Out of Band updates from a location'; | ||
|
|
||
| const ingestionVersioningErrorMessage = 'Versioning cannot be suspended for ' | ||
| + 'buckets setup with Out of Band updates from a location'; | ||
|
|
||
| const objectLockErrorMessage = 'An Object Lock configuration is present on ' + | ||
| 'this bucket, so the versioning state cannot be changed.'; | ||
| const objectLockErrorMessage = | ||
| 'An Object Lock configuration is present on ' + 'this bucket, so the versioning state cannot be changed.'; | ||
|
|
||
| /** | ||
| * Format of xml request: | ||
|
|
@@ -47,21 +44,17 @@ function _parseXML(request, log, cb) { | |
| return cb(errors.MalformedXML); | ||
| } | ||
| const versioningConf = result.VersioningConfiguration; | ||
| const status = versioningConf.Status ? | ||
| versioningConf.Status[0] : undefined; | ||
| const mfaDelete = versioningConf.MfaDelete ? | ||
| versioningConf.MfaDelete[0] : undefined; | ||
| const status = versioningConf.Status ? versioningConf.Status[0] : undefined; | ||
| const mfaDelete = versioningConf.MfaDelete ? versioningConf.MfaDelete[0] : undefined; | ||
| const validStatuses = ['Enabled', 'Suspended']; | ||
| const validMfaDeletes = [undefined, 'Enabled', 'Disabled']; | ||
| if (validStatuses.indexOf(status) < 0 || | ||
| validMfaDeletes.indexOf(mfaDelete) < 0) { | ||
| if (validStatuses.indexOf(status) < 0 || validMfaDeletes.indexOf(mfaDelete) < 0) { | ||
| log.debug('illegal versioning configuration'); | ||
| return cb(errors.IllegalVersioningConfigurationException); | ||
| } | ||
| if (versioningConf && mfaDelete === 'Enabled') { | ||
| log.debug('mfa deletion is not implemented'); | ||
| return cb(errorInstances.NotImplemented | ||
| .customizeDescription('MFA Deletion is not supported yet.')); | ||
| return cb(errorInstances.NotImplemented.customizeDescription('MFA Deletion is not supported yet.')); | ||
| } | ||
| return process.nextTick(() => cb(null)); | ||
| }); | ||
|
|
@@ -103,90 +96,89 @@ function bucketPutVersioning(authInfo, request, log, callback) { | |
| requestType: request.apiMethods || 'bucketPutVersioning', | ||
| request, | ||
| }; | ||
| return waterfall([ | ||
| next => _parseXML(request, log, next), | ||
| next => standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, | ||
| (err, bucket) => next(err, bucket)), // ignore extra null object, | ||
| (bucket, next) => parseString(request.post, (err, result) => { | ||
| // just for linting; there should not be any parsing error here | ||
| if (err) { | ||
| return next(err, bucket); | ||
| } | ||
| // prevent enabling versioning on an nfs exported bucket | ||
| if (bucket.isNFS()) { | ||
| const error = new Error(); | ||
| error.code = 'NFSBUCKET'; | ||
| return next(error); | ||
| } | ||
| // _checkBackendVersioningImplemented returns false if versioning | ||
| // is not implemented on the bucket backend | ||
| if (!_checkBackendVersioningImplemented(bucket)) { | ||
| log.debug(externalVersioningErrorMessage, | ||
| { method: 'bucketPutVersioning', | ||
| error: errors.NotImplemented }); | ||
| const error = errorInstances.NotImplemented.customizeDescription( | ||
| externalVersioningErrorMessage); | ||
| return next(error, bucket); | ||
| } | ||
| const versioningConfiguration = {}; | ||
| if (result.VersioningConfiguration.Status) { | ||
| versioningConfiguration.Status = | ||
| result.VersioningConfiguration.Status[0]; | ||
| } | ||
| if (result.VersioningConfiguration.MfaDelete) { | ||
| versioningConfiguration.MfaDelete = | ||
| result.VersioningConfiguration.MfaDelete[0]; | ||
| return waterfall( | ||
| [ | ||
| next => _parseXML(request, log, next), | ||
|
maeldonn marked this conversation as resolved.
Dismissed
|
||
| next => | ||
| standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => | ||
| next(err, bucket), | ||
| ), // ignore extra null object, | ||
|
maeldonn marked this conversation as resolved.
Dismissed
|
||
| (bucket, next) => | ||
| parseString(request.post, (err, result) => { | ||
| // just for linting; there should not be any parsing error here | ||
| if (err) { | ||
| return next(err, bucket); | ||
| } | ||
| // prevent enabling versioning on an nfs exported bucket | ||
| if (bucket.isNFS()) { | ||
| const error = new Error(); | ||
| error.code = 'NFSBUCKET'; | ||
| return next(error); | ||
| } | ||
| // _checkBackendVersioningImplemented returns false if versioning | ||
| // is not implemented on the bucket backend | ||
| if (!_checkBackendVersioningImplemented(bucket)) { | ||
| log.debug(externalVersioningErrorMessage, { | ||
| method: 'bucketPutVersioning', | ||
| error: errors.NotImplemented, | ||
| }); | ||
| const error = | ||
| errorInstances.NotImplemented.customizeDescription(externalVersioningErrorMessage); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Consider making the message generic when the rejection comes from the |
||
| return next(error, bucket); | ||
| } | ||
| const versioningConfiguration = {}; | ||
| if (result.VersioningConfiguration.Status) { | ||
| versioningConfiguration.Status = result.VersioningConfiguration.Status[0]; | ||
| } | ||
| if (result.VersioningConfiguration.MfaDelete) { | ||
| versioningConfiguration.MfaDelete = result.VersioningConfiguration.MfaDelete[0]; | ||
| } | ||
| // the configuration has been checked before | ||
| return next(null, bucket, versioningConfiguration); | ||
| }), | ||
|
maeldonn marked this conversation as resolved.
Dismissed
|
||
| (bucket, versioningConfiguration, next) => { | ||
| // check if replication is enabled if versioning is being suspended | ||
| const replicationConfig = bucket.getReplicationConfiguration(); | ||
| const isIngestionBucket = bucket.isIngestionBucket && bucket.isIngestionBucket(); | ||
| const invalidAction = | ||
| versioningConfiguration.Status === 'Suspended' && | ||
| (isIngestionBucket || replicationConfig?.rules?.some(r => r.enabled)); | ||
| if (invalidAction) { | ||
| const errorMsg = isIngestionBucket | ||
| ? ingestionVersioningErrorMessage | ||
| : replicationVersioningErrorMessage; | ||
| next(errorInstances.InvalidBucketState.customizeDescription(errorMsg)); | ||
| return; | ||
| } | ||
| const objectLockEnabled = bucket.isObjectLockEnabled(); | ||
| if (objectLockEnabled) { | ||
| next(errorInstances.InvalidBucketState.customizeDescription(objectLockErrorMessage)); | ||
| return; | ||
| } | ||
| bucket.setVersioningConfiguration(versioningConfiguration); | ||
| // TODO all metadata updates of bucket should be using CAS | ||
| metadata.updateBucket(bucket.getName(), bucket, log, err => next(err, bucket)); | ||
| }, | ||
|
maeldonn marked this conversation as resolved.
Dismissed
|
||
| ], | ||
| (err, bucket) => { | ||
| const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket); | ||
| if (err && err.code === 'NFSBUCKET') { | ||
| log.trace('skipping versioning for nfs exported bucket'); | ||
| return callback(null, corsHeaders); | ||
| } | ||
| // the configuration has been checked before | ||
| return next(null, bucket, versioningConfiguration); | ||
| }), | ||
| (bucket, versioningConfiguration, next) => { | ||
| // check if replication is enabled if versioning is being suspended | ||
| const replicationConfig = bucket.getReplicationConfiguration(); | ||
| const isIngestionBucket = bucket.isIngestionBucket && bucket.isIngestionBucket(); | ||
| const invalidAction = | ||
| versioningConfiguration.Status === 'Suspended' | ||
| && (isIngestionBucket || replicationConfig?.rules?.some(r => r.enabled)); | ||
| if (invalidAction) { | ||
| const errorMsg = isIngestionBucket ? | ||
| ingestionVersioningErrorMessage : replicationVersioningErrorMessage; | ||
| next(errorInstances.InvalidBucketState | ||
| .customizeDescription(errorMsg)); | ||
| return; | ||
| } | ||
| const objectLockEnabled = bucket.isObjectLockEnabled(); | ||
| if (objectLockEnabled) { | ||
| next(errorInstances.InvalidBucketState | ||
| .customizeDescription(objectLockErrorMessage)); | ||
| return; | ||
| if (err) { | ||
| log.trace('error processing request', { error: err, method: 'bucketPutVersioning' }); | ||
| monitoring.promMetrics('PUT', bucketName, err.code, 'putBucketVersioning'); | ||
| } else { | ||
| pushMetric('putBucketVersioning', log, { | ||
| authInfo, | ||
| bucket: bucketName, | ||
| }); | ||
| monitoring.promMetrics('PUT', bucketName, '200', 'putBucketVersioning'); | ||
| } | ||
| bucket.setVersioningConfiguration(versioningConfiguration); | ||
| // TODO all metadata updates of bucket should be using CAS | ||
| metadata.updateBucket(bucket.getName(), bucket, log, err => | ||
| next(err, bucket)); | ||
| return callback(err, corsHeaders); | ||
| }, | ||
| ], (err, bucket) => { | ||
| const corsHeaders = collectCorsHeaders(request.headers.origin, | ||
| request.method, bucket); | ||
| if (err && err.code === 'NFSBUCKET') { | ||
| log.trace('skipping versioning for nfs exported bucket'); | ||
| return callback(null, corsHeaders); | ||
| } | ||
| if (err) { | ||
| log.trace('error processing request', { error: err, | ||
| method: 'bucketPutVersioning' }); | ||
| monitoring.promMetrics( | ||
| 'PUT', bucketName, err.code, 'putBucketVersioning'); | ||
| } else { | ||
| pushMetric('putBucketVersioning', log, { | ||
| authInfo, | ||
| bucket: bucketName, | ||
| }); | ||
| monitoring.promMetrics( | ||
| 'PUT', bucketName, '200', 'putBucketVersioning'); | ||
| } | ||
| return callback(err, corsHeaders); | ||
| }); | ||
| ); | ||
| } | ||
|
|
||
| module.exports = bucketPutVersioning; | ||
Uh oh!
There was an error while loading. Please reload this page.