Skip to content
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

force delete prefix and object support #1076

Merged
merged 2 commits into from
Jan 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions examples/remove-object.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,11 @@ s3Client.removeObject('my-bucketname', 'my-objectname', {versionId:"my-versionId
}
console.log("Success")
})

// force delete object/prefix
s3Client.removeObject('force-del-test', 'test/', {forceDelete:true}, function(e) {
if (e) {
return console.log(e)
}
console.log("Success")
})
6 changes: 5 additions & 1 deletion src/main/minio.js
Original file line number Diff line number Diff line change
Expand Up @@ -1669,7 +1669,7 @@ export class Client {
// __Arguments__
// * `bucketName` _string_: name of the bucket
// * `objectName` _string_: name of the object
// * `removeOpts` _object_: Version of the object in the form `{versionId:'my-uuid', governanceBypass:true|false}`. Default is `{}`. (optional)
// * `removeOpts` _object_: Version of the object in the form `{versionId:'my-uuid', governanceBypass:true|false, forceDelete:true|false}`. Default is `{}`. (optional)
// * `callback(err)` _function_: callback function is called with non `null` value in case of error
removeObject(bucketName, objectName, removeOpts={} , cb) {
if (!isValidBucketName(bucketName)) {
Expand Down Expand Up @@ -1700,6 +1700,9 @@ export class Client {
if(removeOpts.governanceBypass){
headers["X-Amz-Bypass-Governance-Retention"]=true
}
if(removeOpts.forceDelete){
headers["x-minio-force-delete"]=true
}

const query = querystring.stringify( queryParams )

Expand Down Expand Up @@ -3710,3 +3713,4 @@ export class PostPolicy {
}

export * from './notification'
export * from './helpers'
218 changes: 218 additions & 0 deletions src/test/functional/functional-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3452,4 +3452,222 @@ describe('functional tests', function () {
})
})


describe('Force Deletion of objects with versions', function () {
// Isolate the bucket/object for easy debugging and tracking.
const fdWithVerBucket = "minio-js-fd-version-" + uuid.v4()
const fdObjectName = 'datafile-100-kB'
const fdObject = dataDir ? fs.readFileSync(dataDir + '/' + fdObjectName) : Buffer.alloc(100 * 1024, 0)

before((done) => client.makeBucket(fdWithVerBucket, '', done))
after((done) => client.removeBucket(fdWithVerBucket, done))

describe('Test for force removal of multiple versions', function () {
let isVersioningSupported = false
const objVersionList = []
step(`setBucketVersioning(bucketName, versionConfig):_bucketName:${fdWithVerBucket},versionConfig:{Status:"Enabled"} `, (done) => {
client.setBucketVersioning(fdWithVerBucket, {Status: "Enabled"}, (err) => {
if (err && err.code === 'NotImplemented') return done()
if (err) return done(err)
isVersioningSupported = true
done()
})
})

step(`putObject(bucketName, objectName, stream)_bucketName:${fdWithVerBucket}, objectName:${fdObjectName}, stream:100Kib_`, done => {
if (isVersioningSupported) {
client.putObject(fdWithVerBucket, fdObjectName, fdObject)
.then(() => done())
.catch(done)
} else {
done()
}

})
// Put two versions of the same object.
step(`putObject(bucketName, objectName, stream)_bucketName:${fdWithVerBucket}, objectName:${fdObjectName}, stream:100Kib_`, done => {
if (isVersioningSupported) {
client.putObject(fdWithVerBucket, fdObjectName, fdObject)
.then(() => done())
.catch(done)
} else {
done()
}
})

step(`removeObject(bucketName, objectList, removeOpts)_bucketName:${fdWithVerBucket}_Remove ${objVersionList.length} objects`, done => {
if (isVersioningSupported) {
client.removeObject(fdWithVerBucket, fdObjectName,{forceDelete:true}, () => {
done()
})
} else {
done()
}
})

step(`listObjects(bucketName, prefix, recursive)_bucketName:${fdWithVerBucket}, prefix: '', recursive:true_`, done => {
if (isVersioningSupported) {
client.listObjects(fdWithVerBucket, '', true, {IncludeVersion: true})
.on('error', done)
.on('end', () => {
if (_.isEqual(0, objVersionList.length)) return done()
return done(new Error(`listObjects lists ${objVersionList.length} objects, expected 0`))
})
.on('data', data => {
objVersionList.push(data)
})
} else {
done()
}
})
})
})

describe('Force Deletion of prefix with versions', function () {
// Isolate the bucket/object for easy debugging and tracking.
const fdPrefixBucketName = "minio-js-fd-version-" + uuid.v4()
const fdPrefixObjName = 'my-prefix/datafile-100-kB'
const fdPrefixObject = dataDir ? fs.readFileSync(dataDir + '/' + fdPrefixObjName) : Buffer.alloc(100 * 1024, 0)

before((done) => client.makeBucket(fdPrefixBucketName, '', done))
after((done) => client.removeBucket(fdPrefixBucketName, done))


describe('Test for removal of multiple versions', function () {
let isVersioningSupported = false
const objVersionList = []
step(`setBucketVersioning(bucketName, versionConfig):_bucketName:${fdPrefixBucketName},versionConfig:{Status:"Enabled"} `, (done) => {
client.setBucketVersioning(fdPrefixBucketName, {Status: "Enabled"}, (err) => {
if (err && err.code === 'NotImplemented') return done()
if (err) return done(err)
isVersioningSupported = true
done()
})
})

step(`putObject(bucketName, objectName, stream)_bucketName:${fdPrefixBucketName}, objectName:${fdPrefixObjName}, stream:100Kib_`, done => {
if (isVersioningSupported) {
client.putObject(fdPrefixBucketName, fdPrefixObjName, fdPrefixObject)
.then(() => done())
.catch(done)
} else {
done()
}

})
// Put two versions of the same object.
step(`putObject(bucketName, objectName, stream)_bucketName:${fdPrefixBucketName}, objectName:${fdPrefixObjName}, stream:100Kib_`, done => {
if (isVersioningSupported) {
client.putObject(fdPrefixBucketName, fdPrefixObjName, fdPrefixObject)
.then(() => done())
.catch(done)
} else {
done()
}
})

step(`removeObject(bucketName, objectList, removeOpts)_bucketName:${fdPrefixBucketName}_Remove ${objVersionList.length} objects`, done => {
if (isVersioningSupported) {
client.removeObject(fdPrefixBucketName, "my-prefix/",{forceDelete:true}, () => {
done()
})
} else {
done()
}
})

step(`listObjects(bucketName, prefix, recursive)_bucketName:${fdPrefixBucketName}, prefix: '', recursive:true_`, done => {
if (isVersioningSupported) {
client.listObjects(fdPrefixBucketName, '/my-prefix', true, {IncludeVersion: true})
.on('error', done)
.on('end', () => {
if (_.isEqual(0, objVersionList.length)) return done()
return done(new Error(`listObjects lists ${objVersionList.length} objects, expected 0`))
})
.on('data', data => {
objVersionList.push(data)
})
} else {
done()
}
})
})
})

describe('Force Deletion of objects without versions', function () {
// Isolate the bucket/object for easy debugging and tracking.
const versionedBucketName = "minio-js-fd-nv-" + uuid.v4()
const versioned_100kbObjectName = 'datafile-100-kB'
const versioned_100kb_Object = dataDir ? fs.readFileSync(dataDir + '/' + versioned_100kbObjectName) : Buffer.alloc(100 * 1024, 0)

before((done) => client.makeBucket(versionedBucketName, '', done))
after((done) => client.removeBucket(versionedBucketName, done))


describe('Test force removal of an object', function () {
step(`putObject(bucketName, objectName, stream)_bucketName:${versionedBucketName}, objectName:${versioned_100kbObjectName}, stream:100Kib_`, done => {
client.putObject(versionedBucketName, versioned_100kbObjectName, versioned_100kb_Object)
.then(() => done())
.catch(done)
})

step(`removeObject(bucketName, objectList, removeOpts)_bucketName:${versionedBucketName}_Remove 1 object`, done => {
client.removeObject(versionedBucketName, versioned_100kbObjectName,{forceDelete:true}, () => {
done()
})
})

step(`listObjects(bucketName, prefix, recursive)_bucketName:${versionedBucketName}, prefix: '', recursive:true_`, done => {
let objVersionList=[]
client.listObjects(versionedBucketName, '', true, {})
.on('error', done)
.on('end', () => {
if (_.isEqual(0, objVersionList.length)) return done()
return done(new Error(`listObjects lists ${objVersionList.length} objects, expected 0`))
})
.on('data', data => {
objVersionList.push(data)
})
})
})
})

describe('Force Deletion of prefix', function () {
// Isolate the bucket/object for easy debugging and tracking.
const fdPrefixBucket = "minio-js-fd-nv-" + uuid.v4()
const fdObjectName = 'my-prefix/datafile-100-kB'
const fdObject = dataDir ? fs.readFileSync(dataDir + '/' + fdObjectName) : Buffer.alloc(100 * 1024, 0)

before((done) => client.makeBucket(fdPrefixBucket, '', done))
after((done) => client.removeBucket(fdPrefixBucket, done))


describe('Test force removal of a prefix', function () {
step(`putObject(bucketName, objectName, stream)_bucketName:${fdPrefixBucket}, objectName:${fdObjectName}, stream:100Kib_`, done => {
client.putObject(fdPrefixBucket, fdObjectName, fdObject)
.then(() => done())
.catch(done)
})

step(`removeObject(bucketName, objectList, removeOpts)_bucketName:${fdPrefixBucket}_Remove 1 object`, done => {
client.removeObject(fdPrefixBucket, "/my-prefix",{forceDelete:true}, () => {
done()
})
})

step(`listObjects(bucketName, prefix, recursive)_bucketName:${fdPrefixBucket}, prefix: 'my-prefix', recursive:true_`, done => {
let objList=[]
client.listObjects(fdPrefixBucket, 'my-prefix', true, {})
.on('error', done)
.on('end', () => {
if (_.isEqual(0, objList.length)) return done()
return done(new Error(`listObjects lists ${objList.length} objects, expected 0`))
})
.on('data', data => {
objList.push(data)
})
})
})
})

})