rest-ftp-daemon 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +12 -0
- data/Gemfile.lock +89 -0
- data/LICENSE.txt +20 -0
- data/README.md +88 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bin/rest-ftp-daemon +16 -0
- data/lib/config.rb +2 -0
- data/lib/errors.rb +16 -0
- data/lib/rest-ftp-daemon.rb +242 -0
- data/lib/rest-ftp-daemon/config.ru +13 -0
- data/test/helper.rb +34 -0
- data/test/test_rest-ftp-daemon.rb +7 -0
- metadata +176 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
activesupport (4.1.4)
|
5
|
+
i18n (~> 0.6, >= 0.6.9)
|
6
|
+
json (~> 1.7, >= 1.7.7)
|
7
|
+
minitest (~> 5.1)
|
8
|
+
thread_safe (~> 0.1)
|
9
|
+
tzinfo (~> 1.1)
|
10
|
+
addressable (2.3.6)
|
11
|
+
builder (3.2.2)
|
12
|
+
descendants_tracker (0.0.4)
|
13
|
+
thread_safe (~> 0.3, >= 0.3.1)
|
14
|
+
docile (1.1.5)
|
15
|
+
faraday (0.9.0)
|
16
|
+
multipart-post (>= 1.2, < 3)
|
17
|
+
git (1.2.7)
|
18
|
+
github_api (0.12.0)
|
19
|
+
addressable (~> 2.3)
|
20
|
+
descendants_tracker (~> 0.0.4)
|
21
|
+
faraday (~> 0.8, < 0.10)
|
22
|
+
hashie (>= 3.2)
|
23
|
+
multi_json (>= 1.7.5, < 2.0)
|
24
|
+
nokogiri (~> 1.6.3)
|
25
|
+
oauth2
|
26
|
+
hashie (3.2.0)
|
27
|
+
highline (1.6.21)
|
28
|
+
i18n (0.6.11)
|
29
|
+
jeweler (2.0.1)
|
30
|
+
builder
|
31
|
+
bundler (>= 1.0)
|
32
|
+
git (>= 1.2.5)
|
33
|
+
github_api
|
34
|
+
highline (>= 1.6.15)
|
35
|
+
nokogiri (>= 1.5.10)
|
36
|
+
rake
|
37
|
+
rdoc
|
38
|
+
json (1.8.1)
|
39
|
+
jwt (1.0.0)
|
40
|
+
mini_portile (0.6.0)
|
41
|
+
minitest (5.4.0)
|
42
|
+
multi_json (1.10.1)
|
43
|
+
multi_xml (0.5.5)
|
44
|
+
multipart-post (2.0.0)
|
45
|
+
nokogiri (1.6.3.1)
|
46
|
+
mini_portile (= 0.6.0)
|
47
|
+
oauth2 (1.0.0)
|
48
|
+
faraday (>= 0.8, < 0.10)
|
49
|
+
jwt (~> 1.0)
|
50
|
+
multi_json (~> 1.3)
|
51
|
+
multi_xml (~> 0.5)
|
52
|
+
rack (~> 1.2)
|
53
|
+
rack (1.5.2)
|
54
|
+
rack-protection (1.5.3)
|
55
|
+
rack
|
56
|
+
rake (10.3.2)
|
57
|
+
rdoc (3.12.2)
|
58
|
+
json (~> 1.4)
|
59
|
+
shoulda (3.5.0)
|
60
|
+
shoulda-context (~> 1.0, >= 1.0.1)
|
61
|
+
shoulda-matchers (>= 1.4.1, < 3.0)
|
62
|
+
shoulda-context (1.2.1)
|
63
|
+
shoulda-matchers (2.6.2)
|
64
|
+
activesupport (>= 3.0.0)
|
65
|
+
simplecov (0.9.0)
|
66
|
+
docile (~> 1.1.0)
|
67
|
+
multi_json
|
68
|
+
simplecov-html (~> 0.8.0)
|
69
|
+
simplecov-html (0.8.0)
|
70
|
+
sinatra (1.4.5)
|
71
|
+
rack (~> 1.4)
|
72
|
+
rack-protection (~> 1.4)
|
73
|
+
tilt (~> 1.3, >= 1.3.4)
|
74
|
+
thread_safe (0.3.4)
|
75
|
+
tilt (1.4.1)
|
76
|
+
tzinfo (1.2.1)
|
77
|
+
thread_safe (~> 0.1)
|
78
|
+
|
79
|
+
PLATFORMS
|
80
|
+
ruby
|
81
|
+
|
82
|
+
DEPENDENCIES
|
83
|
+
bundler (~> 1.0)
|
84
|
+
jeweler (~> 2.0.1)
|
85
|
+
json
|
86
|
+
rdoc (~> 3.12)
|
87
|
+
shoulda
|
88
|
+
simplecov
|
89
|
+
sinatra
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2014 Bruno Medici Consultant
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
## rest-ftp-daemon ##
|
2
|
+
|
3
|
+
This is a fairly basic FTP client daemon, driven by REST-based webservice calls.
|
4
|
+
As of today, its main features are :
|
5
|
+
|
6
|
+
* Start a job through a basic structure posted with PUT /jobs
|
7
|
+
* Spawn a dedicated thread to handle this job in its own contexte
|
8
|
+
* Report transfer progress, error and activity for each job
|
9
|
+
* Gather jobs status into the main process to get a global view
|
10
|
+
* Report JSON status of workers on GET /jobs/ for automated monitoring
|
11
|
+
|
12
|
+
|
13
|
+
## Quick setup ##
|
14
|
+
|
15
|
+
This project requires ruby >= 1.9 and rubygems installed.
|
16
|
+
|
17
|
+
Furthermore, gems are required to run it:
|
18
|
+
|
19
|
+
``` gem install sinatra ```
|
20
|
+
|
21
|
+
Starting the daemon:
|
22
|
+
|
23
|
+
``` thin start ```
|
24
|
+
|
25
|
+
|
26
|
+
## Basic usage ##
|
27
|
+
|
28
|
+
Starting a job transferring file named "file.ova"
|
29
|
+
|
30
|
+
curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
31
|
+
'{"source":"~/file.ova","target":"ftp://anonymous@localhost/incoming/dest2.ova"}' "http://localhost:3000/jobs"
|
32
|
+
|
33
|
+
|
34
|
+
Starting a job transferring file named "dmg"
|
35
|
+
|
36
|
+
curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
|
37
|
+
'{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg"}' "http://localhost:3000/jobs"
|
38
|
+
|
39
|
+
|
40
|
+
Delete a specific job
|
41
|
+
|
42
|
+
curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
## Getting status ##
|
47
|
+
|
48
|
+
GET /jobs
|
49
|
+
|
50
|
+
Would return:
|
51
|
+
|
52
|
+
[
|
53
|
+
{
|
54
|
+
"id": "bob-49126-8",
|
55
|
+
"process": "sleep",
|
56
|
+
"status": "transferring",
|
57
|
+
"context": {
|
58
|
+
"source": "~\/file.ova",
|
59
|
+
"target": "ftp:\/\/anonymous@localhost\/incoming\/dest2.ova",
|
60
|
+
"code": -1,
|
61
|
+
"errmsg": "running",
|
62
|
+
"source_size": 1849036800,
|
63
|
+
"progress": 1.9,
|
64
|
+
"transferred": 34800000
|
65
|
+
}
|
66
|
+
},
|
67
|
+
{
|
68
|
+
"id": "bob-49126-9",
|
69
|
+
"process": "sleep",
|
70
|
+
"status": "transferring",
|
71
|
+
"context": {
|
72
|
+
"source": "~\/file.dmg",
|
73
|
+
"target": "ftp:\/\/anonymous@localhost\/incoming\/dest4.dmg",
|
74
|
+
"code": -1,
|
75
|
+
"errmsg": "running",
|
76
|
+
"source_size": 37109074,
|
77
|
+
"progress": 32.9,
|
78
|
+
"transferred": 12200000
|
79
|
+
}
|
80
|
+
}
|
81
|
+
]
|
82
|
+
|
83
|
+
|
84
|
+
### About ###
|
85
|
+
|
86
|
+
Bruno MEDICI
|
87
|
+
|
88
|
+
http://bmconseil.com/
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
17
|
+
gem.name = "rest-ftp-daemon"
|
18
|
+
gem.homepage = "http://github.com/bmedici/rest-ftp-daemon"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = "RESTful FTP client daemon"
|
21
|
+
gem.description = "A fairly basic FTP client daemon, driven by RESTful webservice calls"
|
22
|
+
gem.email = "rest-ftp-daemon@bmconseil.com"
|
23
|
+
gem.authors = ["Bruno"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
end
|
26
|
+
Jeweler::RubygemsDotOrgTasks.new
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
Rake::TestTask.new(:test) do |test|
|
30
|
+
test.libs << 'lib' << 'test'
|
31
|
+
test.pattern = 'test/**/test_*.rb'
|
32
|
+
test.verbose = true
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Code coverage detail"
|
36
|
+
task :simplecov do
|
37
|
+
ENV['COVERAGE'] = "true"
|
38
|
+
Rake::Task['test'].execute
|
39
|
+
end
|
40
|
+
|
41
|
+
task :default => :test
|
42
|
+
|
43
|
+
require 'rdoc/task'
|
44
|
+
Rake::RDocTask.new do |rdoc|
|
45
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
46
|
+
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = "rest-ftp-daemon #{version}"
|
49
|
+
rdoc.rdoc_files.include('README*')
|
50
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.0
|
data/bin/rest-ftp-daemon
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# Libs and init
|
4
|
+
require 'thin'
|
5
|
+
APP_ROOT = File.dirname(__FILE__) + '/../'
|
6
|
+
DEFAULT_PORT = 3000
|
7
|
+
|
8
|
+
# Prepare thin
|
9
|
+
rackup_file = File.expand_path "#{APP_ROOT}/lib/rest-ftp-daemon/config.ru"
|
10
|
+
argv = ARGV
|
11
|
+
argv << ["-R", rackup_file] unless ARGV.include?("-R")
|
12
|
+
argv << ["-p", DEFAULT_PORT.to_s] unless ARGV.include?("-p")
|
13
|
+
argv << ["-e", "production"] unless ARGV.include?("-e")
|
14
|
+
|
15
|
+
# Start thin
|
16
|
+
Thin::Runner.new(argv.flatten).run!
|
data/lib/config.rb
ADDED
data/lib/errors.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Error codes
|
2
|
+
# O: all ok
|
3
|
+
# 1x: request body errors
|
4
|
+
# 2x: transfer errors
|
5
|
+
|
6
|
+
ERR_OK = 0
|
7
|
+
ERR_BUSY = -1
|
8
|
+
|
9
|
+
ERR_REQ_SOURCE_MISSING = 11
|
10
|
+
ERR_REQ_TARGET_MISSING = 12
|
11
|
+
ERR_REQ_TARGET_SCHEME = 13
|
12
|
+
|
13
|
+
ERR_JOB_SOURCE_NOTFOUND = 21
|
14
|
+
ERR_JOB_TARGET_UNPARSEABLE = 22
|
15
|
+
ERR_JOB_PERMISSION = 24
|
16
|
+
|
@@ -0,0 +1,242 @@
|
|
1
|
+
class RestFtpDaemon < Sinatra::Base
|
2
|
+
helpers Sinatra::JSON
|
3
|
+
|
4
|
+
# General config
|
5
|
+
configure :development, :production do
|
6
|
+
|
7
|
+
# Create new thread group
|
8
|
+
@@threads = ThreadGroup.new
|
9
|
+
#set :dummy, true
|
10
|
+
# set :sessions, false
|
11
|
+
# set :logging, true
|
12
|
+
# set :root, APP_ROOT + '/lib/'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Server initialization
|
16
|
+
def initialize
|
17
|
+
# Setup logger
|
18
|
+
@logger = Logger.new(APP_ROOT + '/main.log','daily')
|
19
|
+
@logger.level = Logger::INFO
|
20
|
+
|
21
|
+
# Other stuff
|
22
|
+
@@last_worker_id = 0
|
23
|
+
@@hostname = `hostname`.chomp
|
24
|
+
|
25
|
+
super
|
26
|
+
end
|
27
|
+
|
28
|
+
# Server global status
|
29
|
+
get "/" do
|
30
|
+
content_type :json
|
31
|
+
json get_status
|
32
|
+
end
|
33
|
+
|
34
|
+
# List jobs
|
35
|
+
get "/jobs" do
|
36
|
+
# Build response
|
37
|
+
content_type :json
|
38
|
+
json get_jobs
|
39
|
+
#@@threads.count
|
40
|
+
end
|
41
|
+
|
42
|
+
# List jobs
|
43
|
+
delete "/jobs/:name" do
|
44
|
+
# Kill this job
|
45
|
+
ret = delete_job params[:name]
|
46
|
+
|
47
|
+
# Fail if no process has been killed
|
48
|
+
error 404 if ret<1
|
49
|
+
|
50
|
+
# Build response
|
51
|
+
content_type :json
|
52
|
+
json nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Spawn a new thread for this new job
|
56
|
+
post '/jobs' do
|
57
|
+
request.body.rewind
|
58
|
+
payload = JSON.parse request.body.read
|
59
|
+
info "POST / with #{payload.to_json}"
|
60
|
+
|
61
|
+
# Spawn a thread
|
62
|
+
#config = {}
|
63
|
+
result = new_job payload
|
64
|
+
|
65
|
+
# Build response
|
66
|
+
content_type :json
|
67
|
+
json result
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
|
72
|
+
def process_job
|
73
|
+
# Init
|
74
|
+
info "process_job: starting"
|
75
|
+
context = Thread.current[:context]
|
76
|
+
transferred = 0
|
77
|
+
|
78
|
+
# Check source
|
79
|
+
job_source = File.expand_path(context["source"])
|
80
|
+
if !(File.exists? job_source)
|
81
|
+
job_status ERR_JOB_SOURCE_NOTFOUND, :ERR_JOB_SOURCE_NOTFOUND
|
82
|
+
return
|
83
|
+
end
|
84
|
+
info "process_job: job_source: #{job_source}"
|
85
|
+
source_size = File.size job_source
|
86
|
+
|
87
|
+
# Check target
|
88
|
+
job_target = context["target"]
|
89
|
+
target = URI(job_target) rescue nil
|
90
|
+
if job_target.nil? || target.nil?
|
91
|
+
job_status ERR_JOB_TARGET_UNPARSEABLE, :ERR_JOB_TARGET_UNPARSEABLE
|
92
|
+
return
|
93
|
+
end
|
94
|
+
info "process_job: job_target: #{job_target}"
|
95
|
+
|
96
|
+
# Split URI
|
97
|
+
target_path = File.dirname target.path
|
98
|
+
target_name = File.basename target.path
|
99
|
+
info "ftp_transfer: job_target.host [#{target.host}]"
|
100
|
+
info "ftp_transfer: target_path [#{target_path}]"
|
101
|
+
info "ftp_transfer: target_name [#{target_name}]"
|
102
|
+
|
103
|
+
# Prepare FTP transfer
|
104
|
+
ftp = Net::FTP.new(target.host)
|
105
|
+
ftp.passive = true
|
106
|
+
ftp.login
|
107
|
+
ftp.chdir(target_path)
|
108
|
+
|
109
|
+
# Do transfer
|
110
|
+
info "source: starting stransfer"
|
111
|
+
Thread.current[:status] = :transferring
|
112
|
+
job_status ERR_BUSY, :running
|
113
|
+
job_set :source_size, source_size
|
114
|
+
|
115
|
+
begin
|
116
|
+
ftp.putbinaryfile(job_source, target_name, TRANSFER_CHUNK_SIZE) do |block|
|
117
|
+
# Update thread info
|
118
|
+
percent = (100.0 * transferred / source_size).round(1)
|
119
|
+
info "transferring [#{percent} %] of [#{target_name}]"
|
120
|
+
job_set :progress, percent
|
121
|
+
job_set :transferred, transferred
|
122
|
+
|
123
|
+
# Update counters
|
124
|
+
transferred += TRANSFER_CHUNK_SIZE
|
125
|
+
end
|
126
|
+
|
127
|
+
rescue Net::FTPPermError
|
128
|
+
Thread.current[:status] = :failed
|
129
|
+
job_status ERR_JOB_PERMISSION, :ERR_JOB_PERMISSION
|
130
|
+
info "source: FAILED: PERMISSIONS ERROR"
|
131
|
+
|
132
|
+
else
|
133
|
+
Thread.current[:status] = :finished
|
134
|
+
job_status ERR_OK, :finished
|
135
|
+
info "source: finished stransfer"
|
136
|
+
end
|
137
|
+
|
138
|
+
ftp.close
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_status
|
142
|
+
{
|
143
|
+
greeting: "REST FTP daemon: up and running",
|
144
|
+
jobs_count: @@threads.list.count,
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def get_jobs
|
149
|
+
output = []
|
150
|
+
@@threads.list.each do |thread|
|
151
|
+
output << {
|
152
|
+
:id => thread[:name],
|
153
|
+
:process => thread.status,
|
154
|
+
:status => thread[:status],
|
155
|
+
:context => thread[:context],
|
156
|
+
}
|
157
|
+
end
|
158
|
+
output
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
def delete_job name
|
163
|
+
count = 0
|
164
|
+
@@threads.list.collect do |thread|
|
165
|
+
next unless thread[:name] == name
|
166
|
+
Thread.kill(thread)
|
167
|
+
count += 1
|
168
|
+
end
|
169
|
+
count
|
170
|
+
end
|
171
|
+
|
172
|
+
def new_job context = {}
|
173
|
+
info "new_job: creating thread"
|
174
|
+
|
175
|
+
# Generate name
|
176
|
+
@@last_worker_id +=1
|
177
|
+
host = @@hostname.split('.')[0]
|
178
|
+
name = "#{host}-#{Process.pid.to_s}-#{@@last_worker_id}"
|
179
|
+
info "new_job: creating thread [#{name}]"
|
180
|
+
|
181
|
+
# Parse parameters
|
182
|
+
job_source = context["source"]
|
183
|
+
job_target = context["target"]
|
184
|
+
return { code: ERR_REQ_SOURCE_MISSING, errmsg: :ERR_REQ_SOURCE_MISSING} if job_source.nil?
|
185
|
+
return { code: ERR_REQ_TARGET_MISSING, errmsg: :ERR_REQ_TARGET_MISSING} if job_target.nil?
|
186
|
+
#return { code: ERR_TRX_SOURCE_FILE_NOT_FOUND, errmsg: "ERR_TRX_SOURCE_FILE_NOT_FOUND [#{job_source}]"} unless File.exists? job_source
|
187
|
+
|
188
|
+
# Parse dest URI
|
189
|
+
target = URI(job_target)
|
190
|
+
info target.scheme
|
191
|
+
return { code: ERR_REQ_TARGET_SCHEME, errmsg: :ERR_REQ_TARGET_SCHEME} unless target.scheme == "ftp"
|
192
|
+
|
193
|
+
# Create thread
|
194
|
+
job = Thread.new(name, job) do
|
195
|
+
# Initialize context
|
196
|
+
Thread.current[:name] = name
|
197
|
+
Thread.current[:created] = Time.now.to_f;
|
198
|
+
Thread.current[:status] = :thread_initializing
|
199
|
+
|
200
|
+
# Store job info
|
201
|
+
Thread.current[:context] = context
|
202
|
+
job_status ERR_BUSY, :thread_initializing
|
203
|
+
Thread.abort_on_exception = true
|
204
|
+
|
205
|
+
# Do the job
|
206
|
+
info "new_job: thread running"
|
207
|
+
process_job
|
208
|
+
|
209
|
+
# Sleep a few seconds before dying
|
210
|
+
Thread.current[:status] = :thread_ending
|
211
|
+
sleep THREAD_SLEEP_BEFORE_DIE
|
212
|
+
info "new_job: thread finished"
|
213
|
+
end
|
214
|
+
|
215
|
+
# Keep thread in thread group
|
216
|
+
info "new_job: attaching thread [#{name}] to group"
|
217
|
+
@@threads.add job
|
218
|
+
|
219
|
+
return { code: 0, errmsg: 'success', name: name, context: context }
|
220
|
+
end
|
221
|
+
|
222
|
+
def log level, msg=""
|
223
|
+
@logger.send(level.to_s, msg)
|
224
|
+
end
|
225
|
+
|
226
|
+
def info msg=""
|
227
|
+
log :info, msg
|
228
|
+
end
|
229
|
+
|
230
|
+
def job_status code, errmsg
|
231
|
+
Thread.current[:context] ||= {}
|
232
|
+
Thread.current[:context][:code] = code
|
233
|
+
Thread.current[:context][:errmsg] = errmsg
|
234
|
+
end
|
235
|
+
|
236
|
+
def job_set attribute, value
|
237
|
+
Thread.current[:context] ||= {}
|
238
|
+
Thread.current[:context][attribute] = value
|
239
|
+
end
|
240
|
+
|
241
|
+
|
242
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Main libs
|
2
|
+
require 'sinatra'
|
3
|
+
require 'sinatra/base'
|
4
|
+
require "sinatra/json"
|
5
|
+
require "sinatra/config_file"
|
6
|
+
require 'net/ftp'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
# My local libs
|
10
|
+
Dir[APP_ROOT+"/lib/*.rb"].each {|file| require File.expand_path file }
|
11
|
+
|
12
|
+
# Start application
|
13
|
+
run RestFtpDaemon
|
data/test/helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'simplecov'
|
2
|
+
|
3
|
+
module SimpleCov::Configuration
|
4
|
+
def clean_filters
|
5
|
+
@filters = []
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
SimpleCov.configure do
|
10
|
+
clean_filters
|
11
|
+
load_adapter 'test_frameworks'
|
12
|
+
end
|
13
|
+
|
14
|
+
ENV["COVERAGE"] && SimpleCov.start do
|
15
|
+
add_filter "/.rvm/"
|
16
|
+
end
|
17
|
+
require 'rubygems'
|
18
|
+
require 'bundler'
|
19
|
+
begin
|
20
|
+
Bundler.setup(:default, :development)
|
21
|
+
rescue Bundler::BundlerError => e
|
22
|
+
$stderr.puts e.message
|
23
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
24
|
+
exit e.status_code
|
25
|
+
end
|
26
|
+
require 'test/unit'
|
27
|
+
require 'shoulda'
|
28
|
+
|
29
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
30
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
31
|
+
require 'rest-ftp-daemon'
|
32
|
+
|
33
|
+
class Test::Unit::TestCase
|
34
|
+
end
|
metadata
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rest-ftp-daemon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Bruno
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-07-31 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: sinatra
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: json
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: shoulda
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rdoc
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '3.12'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '3.12'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: bundler
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '1.0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: jeweler
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 2.0.1
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 2.0.1
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: simplecov
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
description: A fairly basic FTP client daemon, driven by RESTful webservice calls
|
127
|
+
email: rest-ftp-daemon@bmconseil.com
|
128
|
+
executables:
|
129
|
+
- rest-ftp-daemon
|
130
|
+
extensions: []
|
131
|
+
extra_rdoc_files:
|
132
|
+
- LICENSE.txt
|
133
|
+
- README.md
|
134
|
+
files:
|
135
|
+
- Gemfile
|
136
|
+
- Gemfile.lock
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- VERSION
|
141
|
+
- bin/rest-ftp-daemon
|
142
|
+
- lib/config.rb
|
143
|
+
- lib/errors.rb
|
144
|
+
- lib/rest-ftp-daemon.rb
|
145
|
+
- lib/rest-ftp-daemon/config.ru
|
146
|
+
- test/helper.rb
|
147
|
+
- test/test_rest-ftp-daemon.rb
|
148
|
+
homepage: http://github.com/bmedici/rest-ftp-daemon
|
149
|
+
licenses:
|
150
|
+
- MIT
|
151
|
+
post_install_message:
|
152
|
+
rdoc_options: []
|
153
|
+
require_paths:
|
154
|
+
- lib
|
155
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
156
|
+
none: false
|
157
|
+
requirements:
|
158
|
+
- - ! '>='
|
159
|
+
- !ruby/object:Gem::Version
|
160
|
+
version: '0'
|
161
|
+
segments:
|
162
|
+
- 0
|
163
|
+
hash: 3830976928581563014
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
none: false
|
166
|
+
requirements:
|
167
|
+
- - ! '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 1.8.23
|
173
|
+
signing_key:
|
174
|
+
specification_version: 3
|
175
|
+
summary: RESTful FTP client daemon
|
176
|
+
test_files: []
|