mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Merge branch 'master' of https://github.com/sharelatex/filestore-sharelatex
This commit is contained in:
commit
158b640b59
6 changed files with 105 additions and 19 deletions
|
@ -1,13 +1,13 @@
|
||||||
_ = require("underscore")
|
_ = require("underscore")
|
||||||
metrics = require("metrics-sharelatex")
|
metrics = require("metrics-sharelatex")
|
||||||
logger = require("logger-sharelatex")
|
logger = require("logger-sharelatex")
|
||||||
exec = require('child_process').exec
|
safe_exec = require("./SafeExec")
|
||||||
approvedFormats = ["png"]
|
approvedFormats = ["png"]
|
||||||
|
|
||||||
fourtySeconds = 40 * 1000
|
fourtySeconds = 40 * 1000
|
||||||
|
|
||||||
childProcessOpts =
|
childProcessOpts =
|
||||||
killSignal: "SIGKILL"
|
killSignal: "SIGTERM"
|
||||||
timeout: fourtySeconds
|
timeout: fourtySeconds
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,8 +23,7 @@ module.exports =
|
||||||
return callback err
|
return callback err
|
||||||
width = "600x"
|
width = "600x"
|
||||||
args = "nice convert -define pdf:fit-page=#{width} -flatten -density 300 #{sourcePath} #{destPath}"
|
args = "nice convert -define pdf:fit-page=#{width} -flatten -density 300 #{sourcePath} #{destPath}"
|
||||||
console.log args
|
safe_exec args, childProcessOpts, (err, stdout, stderr)->
|
||||||
exec args, childProcessOpts, (err, stdout, stderr)->
|
|
||||||
timer.done()
|
timer.done()
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, stderr:stderr, sourcePath:sourcePath, requestedFormat:requestedFormat, destPath:destPath, "something went wrong converting file"
|
logger.err err:err, stderr:stderr, sourcePath:sourcePath, requestedFormat:requestedFormat, destPath:destPath, "something went wrong converting file"
|
||||||
|
@ -38,7 +37,7 @@ module.exports =
|
||||||
sourcePath = "#{sourcePath}[0]"
|
sourcePath = "#{sourcePath}[0]"
|
||||||
width = "260x"
|
width = "260x"
|
||||||
args = "nice convert -flatten -background white -density 300 -define pdf:fit-page=#{width} #{sourcePath} -resize #{width} #{destPath}"
|
args = "nice convert -flatten -background white -density 300 -define pdf:fit-page=#{width} #{sourcePath} -resize #{width} #{destPath}"
|
||||||
exec args, childProcessOpts, (err, stdout, stderr)->
|
safe_exec args, childProcessOpts, (err, stdout, stderr)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, stderr:stderr, sourcePath:sourcePath, "something went wrong converting file to thumbnail"
|
logger.err err:err, stderr:stderr, sourcePath:sourcePath, "something went wrong converting file to thumbnail"
|
||||||
else
|
else
|
||||||
|
@ -51,7 +50,7 @@ module.exports =
|
||||||
sourcePath = "#{sourcePath}[0]"
|
sourcePath = "#{sourcePath}[0]"
|
||||||
width = "548x"
|
width = "548x"
|
||||||
args = "nice convert -flatten -background white -density 300 -define pdf:fit-page=#{width} #{sourcePath} -resize #{width} #{destPath}"
|
args = "nice convert -flatten -background white -density 300 -define pdf:fit-page=#{width} #{sourcePath} -resize #{width} #{destPath}"
|
||||||
exec args, childProcessOpts, (err, stdout, stderr)->
|
safe_exec args, childProcessOpts, (err, stdout, stderr)->
|
||||||
if err?
|
if err?
|
||||||
logger.err err:err, stderr:stderr, sourcePath:sourcePath, destPath:destPath, "something went wrong converting file to preview"
|
logger.err err:err, stderr:stderr, sourcePath:sourcePath, destPath:destPath, "something went wrong converting file to preview"
|
||||||
else
|
else
|
||||||
|
|
|
@ -104,6 +104,9 @@ module.exports =
|
||||||
secret: settings.filestore.s3.secret
|
secret: settings.filestore.s3.secret
|
||||||
bucket: bucketName
|
bucket: bucketName
|
||||||
s3Client.list prefix:key, (err, data)->
|
s3Client.list prefix:key, (err, data)->
|
||||||
|
if err?
|
||||||
|
logger.err err:err, bucketName:bucketName, key:key, "something went wrong listing prefix in aws"
|
||||||
|
return callback(err)
|
||||||
keys = _.map data.Contents, (entry)->
|
keys = _.map data.Contents, (entry)->
|
||||||
return entry.Key
|
return entry.Key
|
||||||
s3Client.deleteMultiple keys, callback
|
s3Client.deleteMultiple keys, callback
|
||||||
|
|
43
services/filestore/app/coffee/SafeExec.coffee
Normal file
43
services/filestore/app/coffee/SafeExec.coffee
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
_ = require("underscore")
|
||||||
|
logger = require("logger-sharelatex")
|
||||||
|
child_process = require('child_process')
|
||||||
|
|
||||||
|
# execute a command in the same way as 'exec' but with a timeout that
|
||||||
|
# kills all child processes
|
||||||
|
#
|
||||||
|
# we spawn the command with 'detached:true' to make a new process
|
||||||
|
# group, then we can kill everything in that process group.
|
||||||
|
|
||||||
|
module.exports = (command, options, callback = (err, stdout, stderr) ->) ->
|
||||||
|
# options are {timeout: number-of-milliseconds, killSignal: signal-name}
|
||||||
|
[cmd, args...] = command.split(' ')
|
||||||
|
|
||||||
|
child = child_process.spawn cmd, args, {detached:true}
|
||||||
|
stdout = ""
|
||||||
|
stderr = ""
|
||||||
|
|
||||||
|
cleanup = _.once (err) ->
|
||||||
|
clearTimeout killTimer if killTimer?
|
||||||
|
callback err, stdout, stderr
|
||||||
|
|
||||||
|
if options.timeout?
|
||||||
|
killTimer = setTimeout () ->
|
||||||
|
try
|
||||||
|
# use negative process id to kill process group
|
||||||
|
process.kill -child.pid, options.killSignal || "SIGTERM"
|
||||||
|
catch error
|
||||||
|
logger.log process: child.pid, kill_error: error, "error killing process"
|
||||||
|
, options.timeout
|
||||||
|
|
||||||
|
child.on 'close', (code, signal) ->
|
||||||
|
err = if code then new Error("exit status #{code}") else signal
|
||||||
|
cleanup err
|
||||||
|
|
||||||
|
child.on 'error', (err) ->
|
||||||
|
cleanup err
|
||||||
|
|
||||||
|
child.stdout.on 'data', (chunk) ->
|
||||||
|
stdout += chunk
|
||||||
|
|
||||||
|
child.stderr.on 'data', (chunk) ->
|
||||||
|
stderr += chunk
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "filestore-sharelatex",
|
"name": "filestore-sharelatex",
|
||||||
"version": "0.1.3",
|
"version": "0.1.4",
|
||||||
"description": "An API for CRUD operations on binary files stored in S3",
|
"description": "An API for CRUD operations on binary files stored in S3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
@ -10,10 +10,9 @@ describe "FileConverter", ->
|
||||||
|
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
|
|
||||||
@child_process =
|
@safe_exec = sinon.stub()
|
||||||
exec : sinon.stub()
|
|
||||||
@converter = SandboxedModule.require modulePath, requires:
|
@converter = SandboxedModule.require modulePath, requires:
|
||||||
'child_process': @child_process
|
"./SafeExec": @safe_exec
|
||||||
"logger-sharelatex":
|
"logger-sharelatex":
|
||||||
log:->
|
log:->
|
||||||
err:->
|
err:->
|
||||||
|
@ -25,43 +24,43 @@ describe "FileConverter", ->
|
||||||
describe "convert", ->
|
describe "convert", ->
|
||||||
|
|
||||||
it "should convert the source to the requested format", (done)->
|
it "should convert the source to the requested format", (done)->
|
||||||
@child_process.exec.callsArgWith(2)
|
@safe_exec.callsArgWith(2)
|
||||||
@converter.convert @sourcePath, @format, (err)=>
|
@converter.convert @sourcePath, @format, (err)=>
|
||||||
args = @child_process.exec.args[0][0]
|
args = @safe_exec.args[0][0]
|
||||||
args.indexOf(@sourcePath).should.not.equal -1
|
args.indexOf(@sourcePath).should.not.equal -1
|
||||||
args.indexOf(@format).should.not.equal -1
|
args.indexOf(@format).should.not.equal -1
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should return the dest path", (done)->
|
it "should return the dest path", (done)->
|
||||||
@child_process.exec.callsArgWith(2)
|
@safe_exec.callsArgWith(2)
|
||||||
@converter.convert @sourcePath, @format, (err, destPath)=>
|
@converter.convert @sourcePath, @format, (err, destPath)=>
|
||||||
destPath.should.equal "#{@sourcePath}.#{@format}"
|
destPath.should.equal "#{@sourcePath}.#{@format}"
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should return the error from convert", (done)->
|
it "should return the error from convert", (done)->
|
||||||
@child_process.exec.callsArgWith(2, @error)
|
@safe_exec.callsArgWith(2, @error)
|
||||||
@converter.convert @sourcePath, @format, (err)=>
|
@converter.convert @sourcePath, @format, (err)=>
|
||||||
err.should.equal @error
|
err.should.equal @error
|
||||||
done()
|
done()
|
||||||
|
|
||||||
it "should not accapt an non aproved format", (done)->
|
it "should not accapt an non aproved format", (done)->
|
||||||
@child_process.exec.callsArgWith(2)
|
@safe_exec.callsArgWith(2)
|
||||||
@converter.convert @sourcePath, "ahhhhh", (err)=>
|
@converter.convert @sourcePath, "ahhhhh", (err)=>
|
||||||
expect(err).to.exist
|
expect(err).to.exist
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "thumbnail", ->
|
describe "thumbnail", ->
|
||||||
it "should call converter resize with args", (done)->
|
it "should call converter resize with args", (done)->
|
||||||
@child_process.exec.callsArgWith(2)
|
@safe_exec.callsArgWith(2)
|
||||||
@converter.thumbnail @sourcePath, (err)=>
|
@converter.thumbnail @sourcePath, (err)=>
|
||||||
args = @child_process.exec.args[0][0]
|
args = @safe_exec.args[0][0]
|
||||||
args.indexOf(@sourcePath).should.not.equal -1
|
args.indexOf(@sourcePath).should.not.equal -1
|
||||||
done()
|
done()
|
||||||
|
|
||||||
describe "preview", ->
|
describe "preview", ->
|
||||||
it "should call converter resize with args", (done)->
|
it "should call converter resize with args", (done)->
|
||||||
@child_process.exec.callsArgWith(2)
|
@safe_exec.callsArgWith(2)
|
||||||
@converter.preview @sourcePath, (err)=>
|
@converter.preview @sourcePath, (err)=>
|
||||||
args = @child_process.exec.args[0][0]
|
args = @safe_exec.args[0][0]
|
||||||
args.indexOf(@sourcePath).should.not.equal -1
|
args.indexOf(@sourcePath).should.not.equal -1
|
||||||
done()
|
done()
|
||||||
|
|
42
services/filestore/test/unit/coffee/SafeExec.coffee
Normal file
42
services/filestore/test/unit/coffee/SafeExec.coffee
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
assert = require("chai").assert
|
||||||
|
sinon = require('sinon')
|
||||||
|
chai = require('chai')
|
||||||
|
should = chai.should()
|
||||||
|
expect = chai.expect
|
||||||
|
modulePath = "../../../app/js/SafeExec.js"
|
||||||
|
SandboxedModule = require('sandboxed-module')
|
||||||
|
|
||||||
|
describe "SafeExec", ->
|
||||||
|
|
||||||
|
beforeEach ->
|
||||||
|
|
||||||
|
@safe_exec = SandboxedModule.require modulePath, requires:
|
||||||
|
"logger-sharelatex":
|
||||||
|
log:->
|
||||||
|
err:->
|
||||||
|
@options = {timeout: 10*1000, killSignal: "SIGTERM" }
|
||||||
|
|
||||||
|
describe "safe_exec", ->
|
||||||
|
|
||||||
|
it "should execute a valid command", (done) ->
|
||||||
|
@safe_exec "/bin/echo hello", @options, (err, stdout, stderr) =>
|
||||||
|
stdout.should.equal "hello\n"
|
||||||
|
should.not.exist(err)
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should execute a command with non-zero exit status", (done) ->
|
||||||
|
@safe_exec "/usr/bin/env false", @options, (err, stdout, stderr) =>
|
||||||
|
stdout.should.equal ""
|
||||||
|
stderr.should.equal ""
|
||||||
|
err.message.should.equal "exit status 1"
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should handle an invalid command", (done) ->
|
||||||
|
@safe_exec "/bin/foobar", @options, (err, stdout, stderr) =>
|
||||||
|
err.code.should.equal "ENOENT"
|
||||||
|
done()
|
||||||
|
|
||||||
|
it "should handle a command that runs too long", (done) ->
|
||||||
|
@safe_exec "/bin/sleep 10", {timeout: 500, killSignal: "SIGTERM"}, (err, stdout, stderr) =>
|
||||||
|
err.should.equal "SIGTERM"
|
||||||
|
done()
|
Loading…
Reference in a new issue