2015-08-24 11:13:21 -07:00
'use strict' ;
exports = module . exports = {
2017-09-17 18:50:29 -07:00
upload : upload ,
download : download ,
2017-09-23 14:27:35 -07:00
downloadDir : downloadDir ,
2017-09-17 18:50:29 -07:00
copy : copy ,
2017-09-23 11:09:36 -07:00
remove : remove ,
2017-09-27 17:34:49 -07:00
removeDir : removeDir ,
2016-10-11 11:36:25 +02:00
2017-01-04 16:22:58 -08:00
backupDone : backupDone ,
2017-04-18 19:15:56 +02:00
testConfig : testConfig ,
// Used to mock AWS
_mockInject : mockInject ,
_mockRestore : mockRestore
2015-08-24 11:13:21 -07:00
} ;
2017-04-20 15:35:52 +02:00
var assert = require ( 'assert' ) ,
2017-09-23 11:09:36 -07:00
async = require ( 'async' ) ,
2016-09-19 15:03:38 +02:00
AWS = require ( 'aws-sdk' ) ,
2017-04-18 15:33:06 +02:00
BackupsError = require ( '../backups.js' ) . BackupsError ,
2017-10-05 10:01:09 -07:00
chunk = require ( 'lodash.chunk' ) ,
2017-09-26 12:28:33 -07:00
config = require ( '../config.js' ) ,
2017-04-18 15:33:06 +02:00
debug = require ( 'debug' ) ( 'box:storage/s3' ) ,
2017-10-04 11:00:30 -07:00
EventEmitter = require ( 'events' ) ,
2017-09-23 14:27:35 -07:00
fs = require ( 'fs' ) ,
2017-10-05 10:01:09 -07:00
https = require ( 'https' ) ,
2017-09-23 14:27:35 -07:00
mkdirp = require ( 'mkdirp' ) ,
2017-04-21 15:28:25 -07:00
PassThrough = require ( 'stream' ) . PassThrough ,
2017-04-18 15:33:06 +02:00
path = require ( 'path' ) ,
2017-09-26 12:28:33 -07:00
S3BlockReadStream = require ( 's3-block-read-stream' ) ,
2017-10-10 10:47:41 -07:00
safe = require ( 'safetydance' ) ,
2017-09-26 12:28:33 -07:00
superagent = require ( 'superagent' ) ;
2015-08-24 11:13:21 -07:00
2017-04-18 19:15:56 +02:00
// test only
var originalAWS ;
function mockInject ( mock ) {
originalAWS = AWS ;
AWS = mock ;
}
function mockRestore ( ) {
AWS = originalAWS ;
}
2017-10-04 11:49:38 -07:00
var gCachedCaasCredentials = { issueDate : null , credentials : null } ;
2017-09-26 12:28:33 -07:00
function getCaasCredentials ( apiConfig , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
assert ( apiConfig . token ) ;
2017-10-04 11:49:38 -07:00
if ( ( new Date ( ) - gCachedCaasCredentials . issueDate ) <= ( 1.75 * 60 * 60 * 1000 ) ) { // caas gives tokens with 2 hour limit
2017-10-04 19:08:07 -07:00
return callback ( null , gCachedCaasCredentials . credentials ) ;
2017-10-04 11:49:38 -07:00
}
2017-10-04 19:08:07 -07:00
debug ( 'getCaasCredentials: getting new credentials' ) ;
2017-09-26 12:28:33 -07:00
var url = config . apiServerOrigin ( ) + '/api/v1/boxes/' + config . fqdn ( ) + '/awscredentials' ;
superagent . post ( url ) . query ( { token : apiConfig . token } ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( error ) ;
if ( result . statusCode !== 201 ) return callback ( new Error ( result . text ) ) ;
if ( ! result . body || ! result . body . credentials ) return callback ( new Error ( 'Unexpected response: ' + JSON . stringify ( result . headers ) ) ) ;
var credentials = {
signatureVersion : 'v4' ,
accessKeyId : result . body . credentials . AccessKeyId ,
secretAccessKey : result . body . credentials . SecretAccessKey ,
sessionToken : result . body . credentials . SessionToken ,
region : apiConfig . region || 'us-east-1'
} ;
if ( apiConfig . endpoint ) credentials . endpoint = new AWS . Endpoint ( apiConfig . endpoint ) ;
2017-10-04 11:49:38 -07:00
gCachedCaasCredentials = {
issueDate : new Date ( ) ,
credentials : credentials
} ;
2017-09-26 12:28:33 -07:00
callback ( null , credentials ) ;
} ) ;
}
2016-03-31 09:48:01 -07:00
function getBackupCredentials ( apiConfig , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2015-08-24 11:13:21 -07:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-26 12:28:33 -07:00
if ( apiConfig . provider === 'caas' ) return getCaasCredentials ( apiConfig , callback ) ;
2015-11-06 18:22:29 -08:00
var credentials = {
2017-04-20 19:56:06 -07:00
signatureVersion : apiConfig . signatureVersion || 'v4' ,
2017-10-09 18:46:14 -07:00
s3ForcePathStyle : true , // Force use path-style url (http://endpoint/bucket/path) instead of host-style (http://bucket.endpoint/path)
2016-03-31 09:48:01 -07:00
accessKeyId : apiConfig . accessKeyId ,
secretAccessKey : apiConfig . secretAccessKey ,
2016-03-31 09:48:38 -07:00
region : apiConfig . region || 'us-east-1'
2015-11-06 18:22:29 -08:00
} ;
2015-09-09 11:43:50 -07:00
2016-12-07 10:47:06 +01:00
if ( apiConfig . endpoint ) credentials . endpoint = apiConfig . endpoint ;
2015-09-09 11:43:50 -07:00
2017-10-05 10:01:09 -07:00
if ( apiConfig . acceptSelfSignedCerts === true ) {
credentials . httpOptions = {
agent : new https . Agent ( { rejectUnauthorized : false } )
} ;
}
2015-11-06 18:22:29 -08:00
callback ( null , credentials ) ;
2015-08-24 11:13:21 -07:00
}
2015-08-25 10:01:04 -07:00
2017-04-18 15:33:06 +02:00
// storage api
2017-09-20 09:57:16 -07:00
function upload ( apiConfig , backupFilePath , sourceStream , callback ) {
2016-09-16 11:21:08 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof backupFilePath , 'string' ) ;
2017-09-20 09:57:16 -07:00
assert . strictEqual ( typeof sourceStream , 'object' ) ;
2016-09-16 11:21:08 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-10-10 10:47:41 -07:00
function done ( error ) {
if ( error ) {
debug ( '[%s] upload: s3 upload error.' , backupFilePath , error ) ;
return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , ` Error uploading ${ backupFilePath } . Message: ${ error . message } HTTP Code: ${ error . code } ` ) ) ;
}
callback ( null ) ;
}
2016-03-31 09:48:01 -07:00
getBackupCredentials ( apiConfig , function ( error , credentials ) {
2015-08-25 10:01:04 -07:00
if ( error ) return callback ( error ) ;
var params = {
2016-04-04 11:44:24 -07:00
Bucket : apiConfig . bucket ,
2017-04-18 15:33:06 +02:00
Key : backupFilePath ,
2017-09-20 09:57:16 -07:00
Body : sourceStream
2015-08-25 10:01:04 -07:00
} ;
2017-04-18 15:33:06 +02:00
var s3 = new AWS . S3 ( credentials ) ;
2017-10-10 10:47:41 -07:00
// exoscale does not like multi-part uploads. so avoid them for filesystem streams < 5GB
if ( apiConfig . provider === 'exoscale-sos' && typeof sourceStream . path === 'string' ) {
var stat = safe . fs . statSync ( sourceStream . path ) ;
if ( ! stat ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , ` Error detecting size ${ sourceStream . path } . Message: ${ safe . error . message } ` ) ) ;
if ( stat . size <= 5 * 1024 * 1024 * 1024 ) return s3 . putObject ( params , done ) ;
}
2017-04-27 11:40:18 -07:00
// s3.upload automatically does a multi-part upload. we set queueSize to 1 to reduce memory usage
2017-10-10 10:47:41 -07:00
// uploader will buffer at most queueSize * partSize bytes into memory at any given time.
return s3 . upload ( params , { partSize : 10 * 1024 * 1024 , queueSize : 1 } , done ) ;
2015-08-25 10:01:04 -07:00
} ) ;
}
2015-08-26 16:14:51 -07:00
2017-09-20 09:57:16 -07:00
function download ( apiConfig , backupFilePath , callback ) {
2016-09-19 15:03:38 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof backupFilePath , 'string' ) ;
2016-09-19 15:03:38 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-04-18 15:33:06 +02:00
getBackupCredentials ( apiConfig , function ( error , credentials ) {
2016-09-19 15:03:38 +02:00
if ( error ) return callback ( error ) ;
2017-04-21 15:28:25 -07:00
var params = {
Bucket : apiConfig . bucket ,
Key : backupFilePath
} ;
2017-04-18 15:33:06 +02:00
2017-04-21 15:28:25 -07:00
var s3 = new AWS . S3 ( credentials ) ;
2017-04-18 16:44:49 +02:00
2017-09-20 09:57:16 -07:00
var ps = new PassThrough ( ) ;
2017-10-04 12:32:12 -07:00
var multipartDownload = new S3BlockReadStream ( s3 , params , { blockSize : 64 * 1024 * 1024 /*, logCallback: debug */ } ) ;
2017-04-21 15:06:54 -07:00
2017-04-27 11:50:16 -07:00
multipartDownload . on ( 'error' , function ( error ) {
2017-09-20 09:57:16 -07:00
if ( error . code === 'NoSuchKey' || error . code === 'ENOENT' ) {
ps . emit ( 'error' , new BackupsError ( BackupsError . NOT _FOUND ) ) ;
} else {
debug ( '[%s] download: s3 stream error.' , backupFilePath , error ) ;
ps . emit ( 'error' , new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
}
2017-04-18 15:33:06 +02:00
} ) ;
2017-04-21 15:28:25 -07:00
2017-09-20 09:57:16 -07:00
multipartDownload . pipe ( ps ) ;
callback ( null , ps ) ;
2017-04-18 15:33:06 +02:00
} ) ;
2016-09-16 18:14:36 +02:00
}
2017-09-27 21:46:24 -07:00
function listDir ( apiConfig , backupFilePath , options , iteratorCallback , callback ) {
2017-09-23 14:27:35 -07:00
getBackupCredentials ( apiConfig , function ( error , credentials ) {
if ( error ) return callback ( error ) ;
var s3 = new AWS . S3 ( credentials ) ;
var listParams = {
Bucket : apiConfig . bucket ,
Prefix : backupFilePath
} ;
2017-09-27 21:46:24 -07:00
var total = 0 ;
2017-09-23 14:27:35 -07:00
async . forever ( function listAndDownload ( foreverCallback ) {
2017-10-05 12:02:12 -07:00
s3 . listObjects ( listParams , function ( error , listData ) {
2017-09-23 14:27:35 -07:00
if ( error ) {
debug ( 'remove: Failed to list %s. Not fatal.' , error ) ;
return foreverCallback ( error ) ;
}
2017-10-05 12:02:12 -07:00
debug ( 'listDir: processing %s files (processed %s so far). From "%s"' , listData . Contents . length , total , listParams . Marker || '' ) ;
2017-09-27 21:46:24 -07:00
var arr = options . batchSize === 1 ? listData . Contents : chunk ( listData . Contents , options . batchSize ) ;
2017-09-28 12:20:11 -07:00
async . eachLimit ( arr , 10 , iteratorCallback . bind ( null , s3 ) , function iteratorDone ( error ) {
2017-09-23 14:27:35 -07:00
if ( error ) return foreverCallback ( error ) ;
2017-10-05 11:36:54 -07:00
total += listData . Contents . length ;
2017-09-27 21:46:24 -07:00
2017-09-28 10:36:56 -07:00
if ( ! listData . IsTruncated ) return foreverCallback ( new Error ( 'Done' ) ) ;
2017-09-27 21:46:24 -07:00
2017-10-05 12:02:12 -07:00
listParams . Marker = listData . Contents [ listData . Contents . length - 1 ] . Key ; // NextMarker is returned only with delimiter
2017-09-23 14:27:35 -07:00
2017-09-28 10:36:56 -07:00
foreverCallback ( ) ;
2017-09-23 14:27:35 -07:00
} ) ;
} ) ;
} , function ( error ) {
2017-10-02 20:08:00 -07:00
if ( error . message === 'Done' ) return callback ( null ) ;
2017-09-27 21:46:24 -07:00
2017-09-23 14:27:35 -07:00
callback ( error ) ;
} ) ;
} ) ;
}
2017-09-27 21:46:24 -07:00
function downloadDir ( apiConfig , backupFilePath , destDir , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof backupFilePath , 'string' ) ;
assert . strictEqual ( typeof destDir , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-28 12:20:11 -07:00
listDir ( apiConfig , backupFilePath , { batchSize : 1 } , function downloadFile ( s3 , content , iteratorCallback ) {
2017-09-27 21:46:24 -07:00
var relativePath = path . relative ( backupFilePath , content . Key ) ;
2017-10-01 17:35:44 -07:00
2017-09-27 21:46:24 -07:00
mkdirp ( path . dirname ( path . join ( destDir , relativePath ) ) , function ( error ) {
if ( error ) return iteratorCallback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2017-09-28 14:26:39 -07:00
download ( apiConfig , content . Key , function ( error , sourceStream ) {
if ( error ) return iteratorCallback ( error ) ;
var destStream = fs . createWriteStream ( path . join ( destDir , relativePath ) ) ;
destStream . on ( 'open' , function ( ) {
sourceStream . pipe ( destStream ) ;
} ) ;
destStream . on ( 'error' , function ( error ) {
return iteratorCallback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
} ) ;
destStream . on ( 'finish' , iteratorCallback ) ;
2017-09-27 21:46:24 -07:00
} ) ;
} ) ;
} , callback ) ;
}
2017-10-04 11:00:30 -07:00
function copy ( apiConfig , oldFilePath , newFilePath ) {
2016-03-31 09:48:01 -07:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-19 20:40:38 -07:00
assert . strictEqual ( typeof oldFilePath , 'string' ) ;
assert . strictEqual ( typeof newFilePath , 'string' ) ;
2017-10-04 11:00:30 -07:00
var events = new EventEmitter ( ) ;
2015-09-21 14:02:00 -07:00
2017-09-28 12:20:11 -07:00
listDir ( apiConfig , oldFilePath , { batchSize : 1 } , function copyFile ( s3 , content , iteratorCallback ) {
2017-09-27 21:46:24 -07:00
var relativePath = path . relative ( oldFilePath , content . Key ) ;
2015-09-21 14:02:00 -07:00
2017-10-04 16:54:56 +02:00
function done ( error ) {
2017-10-03 15:40:01 -07:00
if ( error && error . code === 'NoSuchKey' ) return iteratorCallback ( new BackupsError ( BackupsError . NOT _FOUND , ` Old backup not found: ${ content . Key } ` ) ) ;
2017-09-27 21:46:24 -07:00
if ( error ) {
2017-10-09 11:50:22 -07:00
debug ( 'copy: s3 copy error when copying %s' , content . Key , error ) ;
2017-10-03 14:46:59 -07:00
return iteratorCallback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , ` Error copying ${ content . Key } : ${ error . message } ` ) ) ;
2017-09-27 21:46:24 -07:00
}
2017-09-22 14:40:37 -07:00
2017-10-04 11:00:30 -07:00
iteratorCallback ( null ) ;
2017-10-04 16:54:56 +02:00
}
var copyParams = {
Bucket : apiConfig . bucket ,
Key : path . join ( newFilePath , relativePath )
} ;
// S3 copyObject has a file size limit of 5GB so if we have larger files, we do a multipart copy
if ( content . Size < 5 * 1024 * 1024 * 1024 ) {
2017-10-04 11:00:30 -07:00
events . emit ( 'progress' , 'Copying ' + content . Key . slice ( oldFilePath . length + 1 ) ) ;
2017-10-09 11:50:22 -07:00
// for exoscale, '/' should not be encoded
copyParams . CopySource = path . join ( apiConfig . bucket , encodeURIComponent ( content . Key ) ) ; // See aws-sdk-js/issues/1302
2017-10-04 16:54:56 +02:00
return s3 . copyObject ( copyParams , done ) ;
}
2017-10-04 11:00:30 -07:00
events . emit ( 'progress' , 'Copying (multipart) ' + content . Key . slice ( oldFilePath . length + 1 ) ) ;
2017-10-04 16:54:56 +02:00
s3 . createMultipartUpload ( copyParams , function ( error , result ) {
if ( error ) return done ( error ) ;
const CHUNK _SIZE = 1024 * 1024 * 1024 ; // 1GB - rather random size
var uploadId = result . UploadId ;
var uploadedParts = [ ] ;
var partNumber = 1 ;
var startBytes = 0 ;
var endBytes = 0 ;
var size = content . Size - 1 ;
function copyNextChunk ( ) {
endBytes = startBytes + CHUNK _SIZE ;
if ( endBytes > size ) endBytes = size ;
var params = {
Bucket : apiConfig . bucket ,
Key : path . join ( newFilePath , relativePath ) ,
2017-10-09 11:50:22 -07:00
CopySource : path . join ( apiConfig . bucket , encodeURIComponent ( content . Key ) ) , // See aws-sdk-js/issues/1302
2017-10-04 16:54:56 +02:00
CopySourceRange : 'bytes=' + startBytes + '-' + endBytes ,
PartNumber : partNumber ,
UploadId : uploadId
} ;
s3 . uploadPartCopy ( params , function ( error , result ) {
if ( error ) return done ( error ) ;
uploadedParts . push ( { ETag : result . CopyPartResult . ETag , PartNumber : partNumber } ) ;
if ( endBytes < size ) {
startBytes = endBytes + 1 ;
partNumber ++ ;
return copyNextChunk ( ) ;
}
var params = {
Bucket : apiConfig . bucket ,
Key : path . join ( newFilePath , relativePath ) ,
MultipartUpload : { Parts : uploadedParts } ,
UploadId : uploadId
} ;
s3 . completeMultipartUpload ( params , done ) ;
} ) ;
}
copyNextChunk ( ) ;
2017-04-18 15:33:06 +02:00
} ) ;
2017-10-04 11:00:30 -07:00
} , function ( error ) {
events . emit ( 'done' , error ) ;
} ) ;
return events ;
2015-09-21 14:02:00 -07:00
}
2016-10-10 15:04:28 +02:00
2017-09-27 17:34:49 -07:00
function remove ( apiConfig , filename , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof filename , 'string' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
getBackupCredentials ( apiConfig , function ( error , credentials ) {
if ( error ) return callback ( error ) ;
var s3 = new AWS . S3 ( credentials ) ;
var deleteParams = {
Bucket : apiConfig . bucket ,
Delete : {
Objects : [ { Key : filename } ]
}
} ;
s3 . deleteObjects ( deleteParams , function ( error ) {
if ( error ) debug ( 'remove: Unable to remove %s. Not fatal.' , deleteParams . Key , error ) ;
callback ( null ) ;
} ) ;
} ) ;
}
function removeDir ( apiConfig , pathPrefix , callback ) {
2016-10-10 15:04:28 +02:00
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-09-23 11:09:36 -07:00
assert . strictEqual ( typeof pathPrefix , 'string' ) ;
2016-10-10 15:04:28 +02:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-28 12:20:11 -07:00
listDir ( apiConfig , pathPrefix , { batchSize : 1000 } , function deleteFiles ( s3 , contents , iteratorCallback ) {
2017-09-27 21:46:24 -07:00
var deleteParams = {
2017-04-18 17:33:59 +02:00
Bucket : apiConfig . bucket ,
2017-09-27 21:46:24 -07:00
Delete : {
Objects : contents . map ( function ( c ) { return { Key : c . Key } ; } )
}
2017-04-18 17:33:59 +02:00
} ;
2016-10-10 15:04:28 +02:00
2017-10-03 01:22:37 -07:00
s3 . deleteObjects ( deleteParams , function ( error /*, deleteData */ ) {
2017-09-27 21:46:24 -07:00
if ( error ) {
debug ( 'removeDir: Unable to remove %s. Not fatal.' , deleteParams . Key , error ) ;
return iteratorCallback ( error ) ;
}
2017-10-03 01:22:37 -07:00
// debug('removeDir: Deleted: %j Errors: %j', deleteData.Deleted, deleteData.Errors);
2017-09-23 11:09:36 -07:00
2017-09-27 21:46:24 -07:00
iteratorCallback ( ) ;
2017-04-18 17:33:59 +02:00
} ) ;
2017-09-27 21:46:24 -07:00
} , callback ) ;
2016-10-10 15:04:28 +02:00
}
2016-10-11 11:36:25 +02:00
function testConfig ( apiConfig , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-27 10:25:36 -07:00
if ( apiConfig . provider === 'caas' ) {
2017-09-27 17:34:49 -07:00
if ( typeof apiConfig . token !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'token must be a string' ) ) ;
2017-09-27 10:25:36 -07:00
} else {
if ( typeof apiConfig . accessKeyId !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'accessKeyId must be a string' ) ) ;
if ( typeof apiConfig . secretAccessKey !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'secretAccessKey must be a string' ) ) ;
}
2017-04-20 17:23:31 -07:00
if ( typeof apiConfig . bucket !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'bucket must be a string' ) ) ;
if ( typeof apiConfig . prefix !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'prefix must be a string' ) ) ;
2017-09-27 10:25:36 -07:00
if ( 'signatureVersion' in apiConfig && typeof apiConfig . signatureVersion !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'signatureVersion must be a string' ) ) ;
if ( 'endpoint' in apiConfig && typeof apiConfig . endpoint !== 'string' ) return callback ( new BackupsError ( BackupsError . BAD _FIELD , 'endpoint must be a string' ) ) ;
2016-10-11 11:36:25 +02:00
2016-10-11 11:46:28 +02:00
// attempt to upload and delete a file with new credentials
getBackupCredentials ( apiConfig , function ( error , credentials ) {
if ( error ) return callback ( error ) ;
var params = {
Bucket : apiConfig . bucket ,
2017-07-19 14:20:35 +02:00
Key : path . join ( apiConfig . prefix , 'cloudron-testfile' ) ,
2016-10-11 11:46:28 +02:00
Body : 'testcontent'
} ;
var s3 = new AWS . S3 ( credentials ) ;
s3 . putObject ( params , function ( error ) {
2017-04-20 17:23:31 -07:00
if ( error ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2016-10-11 11:46:28 +02:00
var params = {
Bucket : apiConfig . bucket ,
2017-07-19 14:20:35 +02:00
Key : path . join ( apiConfig . prefix , 'cloudron-testfile' )
2016-10-11 11:46:28 +02:00
} ;
s3 . deleteObject ( params , function ( error ) {
2017-04-20 17:23:31 -07:00
if ( error ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error . message ) ) ;
2016-10-11 11:46:28 +02:00
2017-04-18 16:51:54 +02:00
callback ( ) ;
2016-10-11 11:46:28 +02:00
} ) ;
} ) ;
} ) ;
2016-10-11 11:36:25 +02:00
}
2017-01-04 16:22:58 -08:00
2017-09-26 12:28:33 -07:00
function backupDone ( apiConfig , backupId , appBackupIds , callback ) {
assert . strictEqual ( typeof apiConfig , 'object' ) ;
2017-04-21 10:31:43 +02:00
assert . strictEqual ( typeof backupId , 'string' ) ;
assert ( Array . isArray ( appBackupIds ) ) ;
2017-01-04 16:22:58 -08:00
assert . strictEqual ( typeof callback , 'function' ) ;
2017-09-26 12:28:33 -07:00
if ( apiConfig . provider !== 'caas' ) return callback ( ) ;
// CaaS expects filenames instead of backupIds, this means no prefix but a file type extension
var FILE _TYPE = '.tar.gz.enc' ;
var boxBackupFilename = backupId + FILE _TYPE ;
var appBackupFilenames = appBackupIds . map ( function ( id ) { return id + FILE _TYPE ; } ) ;
debug ( '[%s] backupDone: %s apps %j' , backupId , boxBackupFilename , appBackupFilenames ) ;
var url = config . apiServerOrigin ( ) + '/api/v1/boxes/' + config . fqdn ( ) + '/backupDone' ;
var data = {
boxVersion : config . version ( ) ,
restoreKey : boxBackupFilename ,
appId : null , // now unused
appVersion : null , // now unused
appBackupIds : appBackupFilenames
} ;
superagent . post ( url ) . send ( data ) . query ( { token : config . token ( ) } ) . timeout ( 30 * 1000 ) . end ( function ( error , result ) {
if ( error && ! error . response ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , error ) ) ;
if ( result . statusCode !== 200 ) return callback ( new BackupsError ( BackupsError . EXTERNAL _ERROR , result . text ) ) ;
return callback ( null ) ;
} ) ;
2017-01-04 16:22:58 -08:00
}