Comparing version 0.8.5 to 0.9.0
@@ -13,3 +13,3 @@ 'use strict'; | ||
process(req, res, containerName) { | ||
storageManager.deleteContainer(containerName) | ||
storageManager.deleteContainer(containerName, { leaseId: req.headers['x-ms-lease-id'] }) | ||
.then((result) => { | ||
@@ -16,0 +16,0 @@ res.set(new ResponseHeader()); |
@@ -15,7 +15,7 @@ 'use strict'; | ||
process(req, res, containerName) { | ||
storageManager.getContainerAcl(containerName) | ||
storageManager.getContainerAcl(containerName, { leaseId: req.headers['x-ms-lease-id'] }) | ||
.then((result) => { | ||
res.set(new ResponseHeader(result.props)); | ||
let xml = js2xmlparser.parse('SignedIdentifiers', result.signedIdentifiers) | ||
xml = xml.replace(`<?xml version='1.0'?>`, `<?xml version="1.0" encoding="utf-8"?>`); | ||
xml = xml.replace(`<?xml version='1.0'?>`, `<?xml version="1.0" encoding="utf-8"?>`); | ||
res.status(200).send(xml); | ||
@@ -22,0 +22,0 @@ }) |
@@ -11,3 +11,3 @@ 'use strict'; | ||
process(req, res, containerName) { | ||
return storageManager.getContainerMetadata(containerName) | ||
return storageManager.getContainerMetadata(containerName, { leaseId: req.headers['x-ms-lease-id'] }) | ||
.then((result) => { | ||
@@ -14,0 +14,0 @@ res.set(new ResponseHeader(result.httpProps, result.metaProps)); |
@@ -11,10 +11,13 @@ 'use strict'; | ||
process(req, res, containerName) { | ||
return storageManager.getContainerProperties(containerName) | ||
return storageManager.getContainerProperties(containerName, { leaseId: req.headers['x-ms-lease-id'] }) | ||
.then((result) => { | ||
res.set(new ResponseHeader(result.httpProps, | ||
result.metaProps, | ||
{ | ||
'x-ms-lease-status': 'unlocked', | ||
'x-ms-lease-state': 'available' | ||
})); | ||
const leaseProps = {}; | ||
leaseProps['x-ms-lease-status'] = (['available', 'broken', 'expired'].includes(result.container.leaseState)) ? 'unlocked' : 'locked'; | ||
leaseProps['x-ms-lease-state'] = result.container.leaseState; | ||
if (leaseProps['x-ms-lease-state'] === 'leased') { | ||
leaseProps['x-ms-lease-duration'] = (result.container.leaseDuration === -1) ? 'infinite' : 'fixed'; | ||
} | ||
res.set(new ResponseHeader(result.httpProps, | ||
result.metaProps, | ||
leaseProps)); | ||
res.status(200).send(); | ||
@@ -21,0 +24,0 @@ }) |
@@ -19,3 +19,3 @@ 'use strict'; | ||
.then((containers) => { | ||
res.set(new ResponseHeader({'Content-Type': 'application/xml'})); | ||
res.set(new ResponseHeader({ 'Content-Type': 'application/xml' })); | ||
let transformedModel = this._transformContainerList(containers, includeMetadata, prefix, maxresults, marker); | ||
@@ -52,2 +52,9 @@ let xmlDoc = js2xmlparser.parse('EnumerationResults', transformedModel); | ||
modelContainer.Properties.ETag = container.httpProps.ETag; | ||
modelContainer.Properties.LeaseStatus = (['available', 'broken', 'expired'].includes(container.leaseState)) ? 'unlocked' : 'locked'; | ||
modelContainer.Properties.LeaseState = container.leaseState; | ||
if (container.leaseState === 'leased') { | ||
modelContainer.Properties.LeaseDuration = (container.leaseDuration === -1) ? 'infinite' : 'fixed'; | ||
} else { | ||
delete modelContainer.Properties.LeaseDuration; | ||
} | ||
} | ||
@@ -54,0 +61,0 @@ return xmlContainerListModel; |
@@ -22,3 +22,3 @@ 'use strict'; | ||
const container = new Container(containerName, req.headers); | ||
return storageManager.setContainerAcl(container, signedIdentifiers); | ||
return storageManager.setContainerAcl(container, signedIdentifiers, { leaseId: req['x-ms-lease-id'] }); | ||
}) | ||
@@ -25,0 +25,0 @@ .then((result) => { |
@@ -22,2 +22,3 @@ 'use strict'; | ||
MissingContentLengthHeader: new ErrorCode('MissingContentLengthHeader', 411, 'The Content-Length header was not specified.'), | ||
MissingRequiredHeader: new ErrorCode('MissingRequiredHeader', 400, 'A required HTTP header was not specified.'), | ||
Md5Mismatch: new ErrorCode('Md5Mismatch', 400, 'The MD5 value specified in the request did not match the MD5 value calculated by the server.'), | ||
@@ -32,3 +33,12 @@ PreconditionFailed: new ErrorCode('PreconditionFailed', 412, 'One of the XML nodes specified in the request body is not supported.'), | ||
UnsupportedBlobType: new ErrorCode('UnsupportedBlobType', 400, 'The blob type is invalid for this operation.'), | ||
SnapshotsPresent: new ErrorCode('SnapshotsPresent', 409, 'This operation is not permitted while the blob has snapshots.') | ||
SnapshotsPresent: new ErrorCode('SnapshotsPresent', 409, 'This operation is not permitted while the blob has snapshots.'), | ||
LeaseNotPresentWithLeaseOperation: new ErrorCode('LeaseNotPresentWithLeaseOperation', 409, 'There is currently no lease on the blob/container.'), | ||
LeaseIdMismatchWithLeaseOperation: new ErrorCode('LeaseIdMismatchWithLeaseOperation', 409, 'The lease ID specified did not match the lease ID for the blob/container.'), | ||
LeaseAlreadyPresent: new ErrorCode('LeaseAlreadyPresent', 409, 'There is already a lease present.'), | ||
LeaseIsBreakingAndCannotBeChanged: new ErrorCode('LeaseIsBreakingAndCannotBeChanged', 409, 'The lease ID matched, but the lease is currently in breaking state and cannot be changed.'), | ||
LeaseIsBreakingAndCannotBeAcquired: new ErrorCode('LeaseIsBreakingAndCannotBeAcquired', 409, 'The lease ID matched, but the lease is currently in breaking state and cannot be acquired until it is broken.'), | ||
LeaseIsBrokenAndCannotBeRenewed: new ErrorCode('LeaseIsBrokenAndCannotBeRenewed', 409, 'The lease ID matched, but the lease has been broken explicitly and cannot be renewed.'), | ||
LeaseNotPresentWithContainerOperation: new ErrorCode('LeaseNotPresentWithContainerOperation', 412, 'There is currently no lease on the container.'), | ||
LeaseIdMissing: new ErrorCode('LeaseIdMissing', 412, 'There is currently a lease on the blob/container and no lease ID was specified in the request.'), | ||
LeaseIdMismatchWithContainerOperation: new ErrorCode('LeaseIdMismatchWithContainerOperation', 412, 'The lease ID specified did not match the lease ID for the container.') | ||
} |
@@ -51,2 +51,7 @@ 'use strict'; | ||
this.httpProps['x-ms-delete-snapshots'] = httpHeader['x-ms-delete-snapshots'] || undefined; | ||
this.httpProps['x-ms-lease-id'] = httpHeader['x-ms-lease-id'] || undefined; | ||
this.httpProps['x-ms-lease-action'] = httpHeader['x-ms-lease-action'] || undefined; | ||
this.httpProps['x-ms-lease-duration'] = httpHeader['x-ms-lease-duration'] || undefined; | ||
this.httpProps['x-ms-lease-break-period'] = httpHeader['x-ms-lease-break-period'] || undefined; | ||
this.httpProps['x-ms-proposed-lease-id'] = httpHeader['x-ms-proposed-lease-id'] || undefined; | ||
@@ -53,0 +58,0 @@ Object.keys(this.httpProps).forEach((key) => { |
@@ -11,2 +11,3 @@ 'use strict'; | ||
getContainerAclHandler = require('./../api/GetContainerAcl'), | ||
leaseContainerHandler = require('./../api/LeaseContainer'), | ||
env = require('./../env'); | ||
@@ -53,2 +54,5 @@ | ||
} | ||
else if (req.query.restype === 'container' && req.query.comp === 'lease') { | ||
leaseContainerHandler.process(req, res, req.params.container); | ||
} | ||
else if (req.query.restype === 'container') { | ||
@@ -55,0 +59,0 @@ createContainerHandler.process(req, res, req.params.container); |
@@ -15,2 +15,3 @@ 'use strict'; | ||
SnapshotTimeManager = require('./SnapshotTimeManager'), | ||
uuidv4 = require('uuid/v4'), | ||
// Validation | ||
@@ -35,4 +36,9 @@ RootValidator = require('./validation/RootValidator'), | ||
PageBlobHeaderSanityVal = require('./validation/PageBlobHeaderSanity'), | ||
AssociatedSnapshotDeletion = require('./validation/AssociatedSnapshotsDeletion'); | ||
AssociatedSnapshotDeletion = require('./validation/AssociatedSnapshotsDeletion'), | ||
LeaseActionsValidation = require('./validation/LeaseActions'), | ||
LeaseDurationValidation = require('./validation/LeaseDuration'), | ||
LeaseIdValidation = require('./validation/LeaseId'), | ||
ContainerLeaseUsageValidation = require('./validation/ContainerLeaseUsage'); | ||
class StorageManager { | ||
@@ -88,3 +94,3 @@ constructor() { | ||
coll.insert({ name: model.name, httpProps: model.httpProps, metaProps: model.metaProps, access: model.access }); | ||
coll.insert({ name: model.name, httpProps: model.httpProps, metaProps: model.metaProps, access: model.access, leaseState: 'available' }); | ||
this.db.addCollection(model.name); | ||
@@ -95,7 +101,7 @@ return fs.mkdirAsync(containerPath) | ||
deleteContainer(name) { | ||
deleteContainer(name, options) { | ||
return BbPromise.try(() => { | ||
let container = path.join(env.localStoragePath, name); | ||
let containerPath = path.join(env.localStoragePath, name); | ||
let coll = this.db.getCollection(StorageTables.Containers); | ||
new RootValidator({ | ||
const validationContext = new RootValidator({ | ||
collection: coll, | ||
@@ -106,6 +112,15 @@ containerName: name | ||
const container = coll.chain().find({ 'name': { '$eq': name } }).data()[0]; | ||
container.leaseState = this._updateLeaseState(container.leaseState, container.leaseExpiredAt, container.leaseBrokenAt) | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: container, | ||
leaseId: options.leaseId, | ||
usage: 'delete' | ||
}); | ||
coll.chain().find({ 'name': { '$eq': name } }).remove(); | ||
this.db.removeCollection(name); | ||
// TODO: Remove Blocks in Committed Directory and Committed Blocks in DB | ||
return fs.remove(container) | ||
return fs.remove(containerPath) | ||
}); | ||
@@ -592,18 +607,27 @@ } | ||
setContainerMetadata(container) { | ||
setContainerMetadata(containerRequest) { | ||
return BbPromise.try(() => { | ||
new RootValidator() | ||
const validationContext = new RootValidator() | ||
.run(ContainerExistsVal, { | ||
containerName: container.name, | ||
containerName: containerRequest.name, | ||
collection: this.db.getCollection(StorageTables.Containers) | ||
}); | ||
const res = this._getCollectionAndContainer(container.name), | ||
containerToUpdate = res.container, | ||
const res = this._getCollectionAndContainer(containerRequest.name), | ||
container = res.container, | ||
coll = res.coll; | ||
containerToUpdate.metaProps = container.metaProps; | ||
containerToUpdate.httpProps['LastModified'] = container.httpProps['Last-Modified']; | ||
coll.update(containerToUpdate); | ||
container.leaseState = this._updateLeaseState(container.leaseState, container.leaseExpiredAt, container.leaseBrokenAt) | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: container, | ||
leaseId: containerRequest.httpProps['x-ms-lease-id'], | ||
usage: 'other' | ||
}); | ||
container.metaProps = containerRequest.metaProps; | ||
container.httpProps['LastModified'] = containerRequest.httpProps['Last-Modified']; | ||
coll.update(container); | ||
return { | ||
'Last-Modified': containerToUpdate.httpProps['Last-Modified'], | ||
ETag: containerToUpdate.meta.revision | ||
'Last-Modified': container.httpProps['Last-Modified'], | ||
ETag: container.meta.revision | ||
} | ||
@@ -613,5 +637,5 @@ }); | ||
getContainerMetadata(containerName) { | ||
getContainerMetadata(containerName, options) { | ||
return BbPromise.try(() => { | ||
new RootValidator() | ||
const validationContext = new RootValidator() | ||
.run(ContainerExistsVal, { | ||
@@ -624,2 +648,10 @@ containerName: containerName, | ||
metaProps = res.container.metaProps; | ||
res.container.leaseState = this._updateLeaseState(res.container.leaseState, res.container.leaseExpiredAt, res.container.leaseBrokenAt); | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: res.container, | ||
leaseId: options.leaseId, | ||
usage: 'other' | ||
}); | ||
httpProps.ETag = res.container.meta.revision; | ||
@@ -633,5 +665,5 @@ return { | ||
getContainerProperties(containerName) { | ||
getContainerProperties(containerName, options) { | ||
return BbPromise.try(() => { | ||
new RootValidator() | ||
const validationContext = new RootValidator() | ||
.run(ContainerExistsVal, { | ||
@@ -644,2 +676,10 @@ containerName: containerName, | ||
metaProps = res.container.metaProps; | ||
res.container.leaseState = this._updateLeaseState(res.container.leaseState, res.container.leaseExpiredAt, res.container.leaseBrokenAt); | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: res.container, | ||
leaseId: options.leaseId, | ||
usage: 'other' | ||
}); | ||
httpProps.ETag = res.container.meta.revision; | ||
@@ -651,3 +691,4 @@ if (res.container.access !== 'private') { | ||
httpProps: httpProps, | ||
metaProps: metaProps | ||
metaProps: metaProps, | ||
container: res.container | ||
}; | ||
@@ -782,6 +823,6 @@ }); | ||
setContainerAcl(container, signedIdentifiers) { | ||
setContainerAcl(containerRequest, signedIdentifiers, options) { | ||
return BbPromise.try(() => { | ||
new RootValidator({ | ||
containerName: container.name, | ||
const validationContext = new RootValidator({ | ||
containerName: containerRequest.name, | ||
collection: this.db.getCollection(StorageTables.Containers), | ||
@@ -793,12 +834,20 @@ model: signedIdentifiers | ||
const res = this._getCollectionAndContainer(container.name), | ||
const res = this._getCollectionAndContainer(containerRequest.name), | ||
coll = res.coll, | ||
updateContainer = res.container; | ||
updateContainer.httpProps = container.httpProps; | ||
updateContainer.signedIdentifiers = signedIdentifiers; | ||
updateContainer.access = container.access; | ||
coll.update(updateContainer); | ||
container = res.container; | ||
container.leaseState = this._updateLeaseState(container.leaseState, container.leaseExpiredAt, container.leaseBrokenAt); | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: container, | ||
leaseId: containerRequest.httpProps['x-ms-lease-id'], | ||
usage: 'other' | ||
}); | ||
container.httpProps = containerRequest.httpProps; | ||
container.signedIdentifiers = signedIdentifiers; | ||
container.access = containerRequest.access; | ||
coll.update(container); | ||
return { | ||
ETag: updateContainer.meta.revision, | ||
'Last-Modified': updateContainer.httpProps['Last-Modified'] | ||
ETag: container.meta.revision, | ||
'Last-Modified': container.httpProps['Last-Modified'] | ||
} | ||
@@ -808,5 +857,5 @@ }); | ||
getContainerAcl(containerName) { | ||
getContainerAcl(containerName, options) { | ||
return BbPromise.try(() => { | ||
new RootValidator({ | ||
const validationContext = new RootValidator({ | ||
containerName: containerName, | ||
@@ -819,2 +868,10 @@ collection: this.db.getCollection(StorageTables.Containers) | ||
container = res.container; | ||
container.leaseState = this._updateLeaseState(res.container.leaseState, res.container.leaseExpiredAt, res.container.leaseBrokenAt); | ||
validationContext.run(ContainerLeaseUsageValidation, { | ||
container: container, | ||
leaseId: options.leaseId, | ||
usage: 'other' | ||
}); | ||
const props = { | ||
@@ -880,2 +937,98 @@ ETag: container.meta.revision, | ||
leaseContainer(containerRequest) { | ||
return BbPromise.try(() => { | ||
const leaseAction = containerRequest.httpProps['x-ms-lease-action'], | ||
proposedLeaseId = containerRequest.httpProps['x-ms-proposed-lease-id'], | ||
leaseId = containerRequest.httpProps['x-ms-lease-id'], | ||
leaseBreakPeriod = (containerRequest.httpProps['x-ms-lease-break-period']) ? parseInt(containerRequest.httpProps['x-ms-lease-break-period']) : undefined, | ||
leaseDuration = (containerRequest.httpProps['x-ms-lease-duration']) ? parseInt(containerRequest.httpProps['x-ms-lease-duration']) : undefined; | ||
const coll = this.db.getCollection(StorageTables.Containers); | ||
const validationContext = new RootValidator({ | ||
collection: coll, | ||
containerName: containerRequest.name, | ||
leaseAction: leaseAction, | ||
leaseId: leaseId, | ||
proposedLeaseId: proposedLeaseId, | ||
leaseBreakPeriod: leaseBreakPeriod, | ||
leaseDuration: leaseDuration | ||
}) | ||
.run(ContainerExistsVal); | ||
const container = coll.chain().find({ 'name': { '$eq': containerRequest.name } }).data()[0]; | ||
container.leaseState = this._updateLeaseState(container.leaseState, container.leaseExpiredAt, container.leaseBrokenAt) | ||
validationContext.run(LeaseActionsValidation, { | ||
container: container | ||
}) | ||
.run(LeaseDurationValidation) | ||
.run(LeaseIdValidation); | ||
const result = { props: {} }; | ||
const now = Date.now(); | ||
switch (leaseAction) { | ||
case 'acquire': | ||
container.leaseId = proposedLeaseId || uuidv4(); | ||
container.leaseExpiredAt = (leaseDuration === -1) ? -1 : now + leaseDuration * 1000; | ||
container.leaseDuration = leaseDuration; | ||
container.leaseState = 'leased'; | ||
result.statusCode = 201; | ||
break; | ||
case 'renew': | ||
container.leaseExpiredAt = (container.leaseDuration === -1) ? -1 : now + container.leaseDuration * 1000; | ||
result.statusCode = 200; | ||
break; | ||
case 'change': | ||
container.leaseId = proposedLeaseId; | ||
result.statusCode = 200; | ||
break; | ||
case 'release': | ||
container.leaseState = 'available'; | ||
result.statusCode = 200; | ||
break; | ||
case 'break': | ||
if (leaseBreakPeriod === undefined) { | ||
container.leaseBrokenAt = (container.leaseExpiredAt === -1) ? now : container.leaseExpiredAt; | ||
} else if (container.leaseExpiredAt === -1) { | ||
container.leaseBrokenAt = now + leaseBreakPeriod * 1000; | ||
} else { | ||
const span = container.leaseExpiredAt - now; | ||
container.leaseBrokenAt = (span > leaseBreakPeriod * 1000) | ||
? container.leaseBrokenAt = now + leaseBreakPeriod * 1000 | ||
: container.leaseBrokenAt = container.leaseExpiredAt; | ||
} | ||
container.leaseState = 'breaking'; | ||
const leaseTimeRemaining = Math.floor((container.leaseBrokenAt - now) / 1000); | ||
result.props['x-ms-lease-time'] = (leaseTimeRemaining > 0) ? leaseTimeRemaining : 0; | ||
result.statusCode = 202; | ||
break; | ||
default: | ||
// This should never happen due to preceding validation! | ||
throw new Error(`leaseContainer: Invalid Lease Action "${leaseAction}"`); | ||
} | ||
result.props['x-ms-lease-id'] = container.leaseId; | ||
result.props.ETag = container.meta.revision; | ||
result.props['Last-Modified'] = container.httpProps['Last-Modified']; | ||
return result; | ||
}); | ||
} | ||
_updateLeaseState(leaseState, leaseExpiredAt, leaseBrokenAt) { | ||
const now = Date.now(); | ||
switch (leaseState) { | ||
// Has breaking period expired? | ||
case 'breaking': | ||
return (leaseBrokenAt <= now) ? 'broken' : 'breaking'; | ||
// Has lease expired? | ||
case 'leased': | ||
// Infinite Lease | ||
if (leaseExpiredAt === -1) { | ||
return 'leased'; | ||
} | ||
return (leaseExpiredAt <= now) ? 'expired' : 'leased'; | ||
default: | ||
return leaseState; | ||
} | ||
} | ||
_updatePageRanges(collPages, pageRanges, startByte, endByte, name) { | ||
@@ -882,0 +1035,0 @@ const startAlignment = startByte / 512, |
{ | ||
"name": "azurite", | ||
"version": "0.8.5", | ||
"version": "0.9.0", | ||
"description": "A lightweight server clone of Azure Blob Storage that simulates most of the commands supported by it with minimal dependencies.", | ||
@@ -12,3 +12,3 @@ "scripts": { | ||
"engines": { | ||
"node": ">=4.0" | ||
"node": ">=6.0" | ||
}, | ||
@@ -51,2 +51,3 @@ "main": "lib/Azurite.js", | ||
"uuid": "^3.0.1", | ||
"validator": "^8.0.0", | ||
"xml2js": "^0.4.17" | ||
@@ -53,0 +54,0 @@ }, |
@@ -9,3 +9,3 @@ # Azurite | ||
# Installation and Usage | ||
In order to run Azurite you need [Node.js](https://nodejs.org/) >= 4.0 installed on your system. Azurite works cross-platform on Windows, Linux, and OS X. | ||
In order to run Azurite you need [Node.js](https://nodejs.org/) >= 6.0 installed on your system. Azurite works cross-platform on Windows, Linux, and OS X. | ||
@@ -130,3 +130,3 @@ After installation you can install Azurite simply with `npm` which is Node.js package management tool and which is included with every Node.js installation. | ||
- Lease Container [TODO] | ||
- Lease Container [IN-PROGRESS] | ||
Establishes and manages a lock on a container for delete operations. | ||
@@ -133,0 +133,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
217199
81
3873
14
+ Addedvalidator@^8.0.0
+ Addedvalidator@8.2.0(transitive)