rest-ftp-daemon 0.72b → 0.85.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- YjI2MmE1NjJjY2NhZWViMGRlMGEzODYwNDFjZDMwYjViM2EwZWNiZQ==
5
- data.tar.gz: !binary |-
6
- ZDllYjExM2UwNzI1ZTk4OTFmZWRmMDZlZGQyNzU5Y2Q0NjEzNmFkOA==
2
+ SHA1:
3
+ metadata.gz: 1c6ed0547c0017afce76bb7b5ffc55e5ddb48981
4
+ data.tar.gz: 3a40535718c17d96e76a2f332d7e71eef4b3e2ce
7
5
  SHA512:
8
- metadata.gz: !binary |-
9
- OTA1MGZiMDY5Yjc0ZmViZTg3ZGYxNTZmYjViYmFiYjcwMzliMGNhYjZjZmJl
10
- OWI2MDNlYmY5YjcxMWZmNDQ4YzgzOWM0OTgxMmRlYjg0ZDkyYTY5Yjc5Y2Ix
11
- N2M0OTY3ZjNiZWM2MzEzYTg3OTQ0ZWIyMmE0MTIyYzgyNjk5YWQ=
12
- data.tar.gz: !binary |-
13
- ZTY1YmZmNmQyOWZiMjAzNmZkNTVlNDMwMWZkZTFkOTRiYmRiMjRkN2UwYjU4
14
- NmY3N2FiZjJkYWVjNDFjMjAyMDQxMDEyOTMzNmQ3NjAwYTNjNDhhNWQxNjhl
15
- YTA1NDJlNTI5YTllNDkxNzM5MWQyY2FiNDFiMGU0YmEyOTEwYzE=
6
+ metadata.gz: 77d115e07981f5bf41d5ef0240e50c813f2a908c5015aff532538a8c56fa9fdf4e2bbb06cbee60e2a51199923f1e38655adbee68d549f28ecdf8866ab2ab6d2b
7
+ data.tar.gz: 98e520e5b73394dd7f3cb187673b51650290fcd50fb9c19dd7badc89837814571f35550c08aef9e5c4a7de8534858f35e5b08390ea00f5ed0c29406b90f91662
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rest-ftp-daemon (0.63)
4
+ rest-ftp-daemon (0.84)
5
5
  double-bag-ftps
6
6
  facter
7
7
  grape
@@ -10,12 +10,13 @@ PATH
10
10
  settingslogic
11
11
  sys-cpu
12
12
  thin (~> 1.6)
13
+ timeout
13
14
 
14
15
  GEM
15
16
  remote: http://rubygems.org/
16
17
  specs:
17
18
  CFPropertyList (2.2.8)
18
- activesupport (4.1.6)
19
+ activesupport (4.1.7)
19
20
  i18n (~> 0.6, >= 0.6.9)
20
21
  json (~> 1.7, >= 1.7.7)
21
22
  minitest (~> 5.1)
@@ -34,9 +35,9 @@ GEM
34
35
  double-bag-ftps (0.1.2)
35
36
  equalizer (0.0.9)
36
37
  eventmachine (1.0.3)
37
- facter (2.2.0)
38
+ facter (2.3.0)
38
39
  CFPropertyList (~> 2.2.6)
39
- ffi (1.9.5)
40
+ ffi (1.9.6)
40
41
  grape (0.9.0)
41
42
  activesupport
42
43
  builder
@@ -71,6 +72,7 @@ GEM
71
72
  rack (~> 1.0)
72
73
  thread_safe (0.3.4)
73
74
  tilt (2.0.1)
75
+ timeout (0.0.0)
74
76
  tzinfo (1.2.2)
75
77
  thread_safe (~> 0.1)
76
78
  virtus (1.0.3)
data/README.md CHANGED
@@ -2,9 +2,15 @@ rest-ftp-daemon
2
2
  ====================================================================================
3
3
 
4
4
 
5
-
6
5
  This is a pretty simple FTP client daemon, controlled through a RESTfull API.
7
6
 
7
+ API documentation is [maintained on Apiary](http://docs.restftpdaemon.apiary.io/)
8
+
9
+
10
+
11
+ Features
12
+ ------------------------------------------------------------------------------------
13
+
8
14
  As of today, its main features are :
9
15
 
10
16
  * Allow environment-specific configuration in a YAML file
@@ -19,6 +25,10 @@ As of today, its main features are :
19
25
  * Allow authentication in FTP target in a standard URI-format
20
26
  * Allow configuration-based path templates to abstract local mounts or remote FTPs (endpoint tokens)
21
27
  * Remote supported protocols: FTP and FTPs
28
+ * Allow main file transfer protocols: sFTP, FTPs / FTPes
29
+ * Automatically clean-up jobs after a configurable amount of time (failed, finished)
30
+ * Current bitrate on the last blocks chunk updated in the job attributes
31
+ * Global bitrate on the whole file transfer is re-computed after the transfer finishes
22
32
 
23
33
  Expected features in a short-time range :
24
34
 
@@ -29,34 +39,60 @@ Expected features in a short-time range :
29
39
  * Provide swagger-style API documentation
30
40
  * Authenticate API clients
31
41
  * Allow to specify random remote/local source/target
32
- * Allow more remote protocols: sFTP, FTPs, HTTP POST, etc
42
+ * Allow more transfer protocols (sFTP, HTTP POST etc)
43
+
33
44
 
34
45
 
35
46
  Installation
36
47
  ------------------------------------------------------------------------------------
37
48
 
38
- This project is available as a rubygem, requires on ruby >= 1.9.3 and rubygems installed.
49
+ This project is available as a rubygem, requires Ruby 2.1 and rubygems installed.
39
50
 
40
- Get and install the gem from rubygems.org:
51
+ You may use ```rbenv``` and ```ruby-build``` to get the right Ruby version. If this is your case, ensure that ruby-build definitions are up-to-date and include ruby-2.1.0
41
52
 
42
53
  ```
43
- # apt-get install ruby1.9.3 ruby1.9.1-dev ruby-dev rubygems gcc g++
44
- gem install rest-ftp-daemon --no-ri --no-rdoc
54
+ # apt-get install ruby-build rbenv
55
+ # ruby-build --definitions | grep '2.1'
56
+
45
57
  ```
58
+ Otherwise, you way have to update ruby-build to include Ruby 2.1.0 definitions.
59
+ On Debian, 2.1.0 is not included in Wheezy and appears in Jessie's version of the package.
46
60
 
47
- Start the daemon:
61
+ Use a dedicated user for the daemon, switch to this user and enable rbenv
48
62
 
49
63
  ```
50
- rest-ftp-daemon start
64
+ # adduser --disabled-password --gecos "" rftpd
65
+ # su rftpd -l
66
+ # echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
67
+ # echo 'eval "$(rbenv init -)"' >> ~/.bashrc
51
68
  ```
52
69
 
53
- Start the daemon on a specific port :
70
+
71
+ Install the right ruby version and activate it
54
72
 
55
73
  ```
56
- rest-ftp-daemon -p4000 start
74
+ # rbenv install 2.1.0
75
+ # rbenv local 2.1.0
76
+ # rbenv rehash
57
77
  ```
58
78
 
59
- Check that the daemon is running and providing its status info
79
+ Update RubyGems and install the gem from rubygems.org
80
+
81
+ ```
82
+ # gem update --system
83
+ # gem install rest-ftp-daemon --no-ri --no-rdoc
84
+ # rbenv rehash
85
+ # rest-ftp-daemon start
86
+ ```
87
+
88
+ Finally start the daemon on the standart port, or on a specific port using ```-p```
89
+
90
+ ```
91
+ # rest-ftp-daemon -p 4000 start
92
+ ```
93
+
94
+ Check that the daemon is running and providing its status info.
95
+ If the daemon seems to exit as soon as it's launched, this may be due to logfiles that cannot be written on (check permissions or owner).
60
96
 
61
97
  ```
62
98
  http://localhost:3200/
@@ -64,6 +100,7 @@ http://localhost:3200/
64
100
 
65
101
  Configuration
66
102
  ------------------------------------------------------------------------------------
103
+
67
104
  Most of the configuration options live in a YAML configuration file, containing two main sections:
68
105
 
69
106
  * the ``defaults`` section should be left as-is and will be used is no other environment-specific value is provided.
@@ -80,6 +117,7 @@ As a starting point, ``rest-ftp-daemon.yml.sample`` is an exemple config file th
80
117
 
81
118
  Default administrator credentials are admin/admin. Please change the password in this configuration file before starting any kind of production.
82
119
 
120
+
83
121
  Logging
84
122
  ------------------------------------------------------------------------------------
85
123
 
@@ -87,32 +125,25 @@ The application will not log to any file by default, if not specified in its con
87
125
  Otherwise separate logging paths can be provided for the Thin webserver, API related messages, and workers related messages. Providing and empty value will simply activate logging to ``STDOUT``.
88
126
 
89
127
 
90
- Usage examples
128
+ Job cleanup
91
129
  ------------------------------------------------------------------------------------
92
130
 
93
- * Start a job to transfer a file named "file.iso" to a local FTP server
94
-
95
- ```
96
- curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
97
- '{"source":"~/file.iso","target":"ftp://anonymous@localhost/incoming/dest2.iso"}' "http://localhost:3000/jobs"
98
- ```
131
+ Job can be cleanup up after a certain amount of time, when they reach on of these status:
99
132
 
100
- Requesting notifications is achieved by passing a "notify" key in the request, with a callback URL.
101
- This URL will be called at some points, ``POST```'ing a generic JSON structure with progress information.
133
+ - failed, after conchita.clean_failed seconds
134
+ - finished, after conchita.clean_finished seconds
102
135
 
136
+ Cleanup is done on a regular basis, every few seconds (conchita.timer)
103
137
 
104
- * Start a job requesting notifications ``POST```'ed on "http://requestb.in/1321axg1"
105
138
 
106
- ```
107
- curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
108
- '{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
109
- ```
139
+ Usage examples
140
+ ------------------------------------------------------------------------------------
110
141
 
111
- * Start a job with all the above plus a priority
142
+ * Start a job to transfer a file named "file.iso" to a local FTP server
112
143
 
113
144
  ```
114
145
  curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
115
- '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
146
+ '{"source":"~/file.iso","target":"ftp://anonymous@localhost/incoming/dest2.iso"}' "http://localhost:3000/jobs"
116
147
  ```
117
148
 
118
149
  * Start a job using endpoint tokens
@@ -137,72 +168,6 @@ curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
137
168
  '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
138
169
  ```
139
170
 
140
- NB: a special token [RANDOM] helps to generate a random filename when needed
141
-
142
- * Get status of a specific job based on its ID
143
-
144
- ```
145
- curl -H "Content-Type: application/json" -X GET -D /dev/stdout "http://localhost:3000/jobs/3"
146
- ```
147
-
148
-
149
- * Delete a specific job based on its ID
150
-
151
- ```
152
- curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/3"
153
- ```
154
-
155
-
156
- Getting status
157
- ------------------------------------------------------------------------------------
158
-
159
- * A global JSON status is provided on ``` GET /status ```
160
-
161
- * A nice dashboard gives a global view of the daemon, jobs in queue, and system status, exposed on ``` GET /```
162
-
163
- * The server exposes its jobs list on ``` GET /jobs ```
164
-
165
- ```
166
- http://localhost:3000/jobs
167
- ```
168
-
169
- This query will return a job list :
170
-
171
- ```
172
- [
173
- {
174
- "source": "~/file.dmg",
175
- "target": "ftp://anonymous@localhost/incoming/dest2.dmg",
176
- "worker_name": "bob-92439-1",
177
- "created": "2014-08-01 16:53:08 +0200",
178
- "id": 16,
179
- "runtime": 17.4,
180
- "status": "graceful_ending",
181
- "source_size": 37109074,
182
- "error": 0,
183
- "errmsg": "finished",
184
- "progress": 100.0,
185
- "transferred": 37100000
186
- },
187
- {
188
- source: "[nas]/file.dmg",
189
- target: "[ftp2]/dest4.dmg",
190
- notify: "http://requestb.in/1321axg1",
191
- updated_at: "2014-09-17 22:56:14 +0200",
192
- id: 2,
193
- started_at: "2014-09-17 22:56:01 +0200",
194
- status: "uploading",
195
- error: 0,
196
- debug_source: "/Users/bruno/file.dmg",
197
- debug_target: "#<URI::FTP:0x007ffa9289e650 URL:ftp://uuuuuuuu:yyyyyyyyy@ftp.xxxxxx.fr/subdir/dest4.dmg>",
198
- file_size: 32093208,
199
- file_progress: 5.6,
200
- file_sent: 1800000
201
- }
202
- ]
203
- ```
204
-
205
-
206
171
  About
207
172
  ------------------------------------------------------------------------------------
208
173
 
data/bin/rest-ftp-daemon CHANGED
@@ -1,38 +1,110 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Include libs and config file
4
- require "thin"
5
- app_root = File.expand_path(File.dirname(__FILE__) + '/../')
6
- require File.expand_path("#{app_root}/lib/rest-ftp-daemon/config.rb")
7
-
8
- # Build rackup context
9
- settings_daemonize = [nil, true, 1, "1"].include?(Settings.daemonize)
10
- argv = ARGV
11
- argv << ["-e", "development"] unless ARGV.include?("-e")
12
- argv << ["-p", Settings[:port].to_s] unless ARGV.include?("-p") || Settings[:port].nil?
13
- argv << ["-l", Settings[:logs][:thin]] unless ARGV.include?("-l") unless Settings[:logs][:thin].nil? unless Settings[:logs].nil?
14
- argv << ["--daemonize"] if settings_daemonize unless (ARGV.include?("-d") || ARGV.include?("--daemonize"))
15
- argv << ["-R", File.expand_path("#{app_root}/config.ru")] unless ARGV.include?("-R")
3
+ # Try to load libs
4
+ begin
5
+ require "thin"
6
+ require 'optparse'
7
+ require 'socket'
8
+ require 'timeout'
9
+ rescue LoadError
10
+ raise "EXITING: some of basic libs were not found: thin, optparse, socket, timeout"
11
+ end
12
+ puts
13
+
14
+ # Detect environment from options
15
+ begin
16
+ OptionParser.new do |opts|
17
+ opts.on("-e", "--environment ENV") { |env| APP_ENV = env }
18
+ opts.on("", "--dev") { APP_ENV = "development" }
19
+ end.parse(ARGV)
20
+ rescue OptionParser::InvalidOption => e
21
+ end
22
+
23
+ # Load remaining libs and config subsystem
24
+ app_root = File.dirname(__FILE__) + '/../'
25
+ [:constants, :helpers, :config].each do |lib|
26
+ require File.expand_path("#{app_root}/lib/rest-ftp-daemon/#{lib.to_s}.rb")
27
+ end
28
+
29
+ # Setup options and import current ARGV
30
+ options = {}
31
+ parser = OptionParser.new do |opts|
32
+ opts.banner = "Usage: #{File.basename $0} [options] start|stop|restart"
33
+ opts.on("-e", "--environment ENV", "Environment (#{APP_ENV if defined? APP_ENV})")
34
+ opts.on("--dev", "Force development environment")
35
+ opts.on("-p", "--port PORT", "use PORT (#{Settings.port})") { |port| options["port"] = port.to_i }
36
+ opts.on("-w", "--workers COUNT", "Use COUNT worker threads (#{Settings['workers']})") { |count| options["workers"] = count.to_i }
37
+ opts.on("-d", "--daemonize", "Run daemonized in the background") { |bool| options["daemonize"] = bool || true }
38
+ opts.on("-P", "--pid FILE", "File to store PID") { |file| options["pidfile"] = file }
39
+ opts.on("-u", "--user NAME", "User to run daemon as (use with -g)") { |user| options["user"] = user }
40
+ opts.on("-g", "--group NAME", "Group to run daemon as (use with -u)"){ |group| options["group"] = group }
41
+ opts.on_tail("-h", "--help", "Show this message") { puts opts; exit }
42
+ opts.on_tail('-v', '--version', "Show version") { puts Settings['app_ver']; exit }
43
+ end
44
+ begin
45
+ parser.order!(ARGV)
46
+ command = ARGV.shift
47
+ unless ["start", "stop", "restart"].include? command
48
+ puts parser
49
+ exit 1
50
+ end
51
+ rescue OptionParser::InvalidOption => e
52
+ puts "EXITING: option parser: #{e.message}"
53
+ exit 1
54
+ end
55
+ Settings.merge!(options)
16
56
 
17
57
  # Display compiled configuration
18
- puts "---"
19
- puts "Version \t #{Settings.app_ver}"
20
- puts "Environment \t #{Settings.namespace}"
21
- puts "Config file \t #{Settings.source}"
22
- puts "Parameters \t #{argv.flatten}"
58
+ puts "--- #{APP_NAME} #{APP_VER}"
59
+ puts "Config file \t #{APP_CONF}"
60
+ puts "PID file \t #{Settings.pidfile}"
61
+ puts "Namespace \t #{Settings.namespace}"
62
+ puts "Network port \t #{Settings['port']}"
63
+ puts "Daemonize \t #{Settings['daemonize']}"
64
+ puts "User:group \t #{Settings['user']}:#{Settings['group']}"
65
+ puts
23
66
  puts Settings.to_hash.to_yaml( :Indent => 4, :UseHeader => true, :UseVersion => false )
67
+ puts
24
68
 
25
- # Start Thin with this rackup configuration
26
- puts "---"
69
+ # Validate network configuration
70
+ if ["start", "restart"].include? command
71
+ if Settings['port'].nil?
72
+ puts "ABORTING: Network port is missing"
73
+ exit 1
74
+ elsif RestFtpDaemon::Helpers.local_port_used?(Settings['port'])
75
+ puts "ABORTING: Network port #{Settings['port']} is already in use"
76
+ exit 1
77
+ end
78
+ end
79
+
80
+ # Build final ARGV
81
+ argv = []
82
+ argv << ["-e", Settings.namespace]
83
+ argv << ["-p", Settings["port"].to_s] unless Settings["port"].nil?
84
+ argv << ["--pid", Settings.pidfile]
85
+ argv << ["--log", Settings["logs"]["thin"].to_s] unless Settings["logs"]["thin"].nil? if Settings["logs"].is_a? Enumerable
86
+ argv << ["--daemonize"] if [1, true].include? Settings["daemonize"]
87
+ # User / group
88
+ if Settings["user"] && Settings["group"]
89
+ argv << ["--user", Settings["user"]]
90
+ argv << ["--group", Settings["group"]]
91
+ end
92
+ argv << command unless command.nil? rescue nil
93
+ puts "--- Thin ARGV"
94
+ puts argv.flatten.join(' ')
27
95
  puts
96
+
97
+ # Start Thin with this rackup configuration
28
98
  begin
29
99
  Thin::Runner.new(argv.flatten).run!
100
+ rescue RuntimeError => e
101
+ puts "FAILED: RuntimeError: #{e.message}"
30
102
  rescue Thin::PidFileExist
31
- puts "ERROR: daemon was already running (Thin::PidFileExist)"
103
+ puts "FAILED: daemon was already running (Thin::PidFileExist)"
32
104
  rescue Thin::PidFileNotFound
33
- puts "ERROR: daemon was not running (Thin::PidFileNotFound)"
105
+ puts "FAILED: daemon was not running (Thin::PidFileNotFound)"
34
106
  rescue SystemExit
35
- puts "EXITING: daemon in the background (SystemExit)"
107
+ # puts "EXITING: daemon in the background (SystemExit)"
36
108
  else
37
- puts "EXITING: process ending"
109
+ # puts "FAILED: process ending"
38
110
  end
data/config.ru CHANGED
@@ -1,24 +1,27 @@
1
1
  # Load gem files
2
- APP_LIBS = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
3
- $LOAD_PATH.unshift(APP_LIBS) unless $LOAD_PATH.include?(APP_LIBS)
2
+ load_path_libs = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
3
+ $LOAD_PATH.unshift(load_path_libs) unless $LOAD_PATH.include?(load_path_libs)
4
4
  require 'rest-ftp-daemon'
5
5
 
6
6
  # Create queue and worker pool
7
7
  $queue = RestFtpDaemon::JobQueue.new
8
- $pool = RestFtpDaemon::WorkerPool.new(Settings.workers.to_i)
8
+ $pool = RestFtpDaemon::WorkerPool.new(Settings[:workers] || DEFAULT_WORKERS)
9
9
 
10
- # Rack middleware
11
- # use Rack::Etag # Add an ETag
12
- # use Rack::Reloader, 0
10
+ # Rack reloader
11
+ unless Settings.namespace == "production"
12
+ use Rack::Reloader, 0
13
+ end
14
+
15
+ # Rack authent
13
16
  unless Settings.adminpwd.nil?
14
17
  use Rack::Auth::Basic, "Restricted Area" do |username, password|
15
18
  [username, password] == ['admin', Settings.adminpwd]
16
19
  end
17
20
  end
18
- #use Rack::Deflator # Compress
19
21
 
20
22
  # Serve static assets
21
- use Rack::Static, :urls => ["/css", "/images"], :root => "#{Settings.app_lib}/static/"
23
+ use Rack::Static, :urls => ["/css", "/images"], :root => "#{APP_LIBS}/static/"
22
24
 
23
25
  # Launch the main daemon
24
- run Rack::Cascade.new [RestFtpDaemon::API::Root]
26
+ run RestFtpDaemon::API::Root
27
+ #run Rack::Cascade.new [RestFtpDaemon::API::Root]