rest-ftp-daemon 0.41 → 0.55

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MDQwNWFlNDE3ZGUwZGY2ZTc1N2FiMGE3OWNkODMxN2VhODdkNDNiMA==
4
+ YjFjZjg2NTk1Y2M5NzVmNGM4MWJmYjA1YTEwN2JhZjVjOTlkN2I2OQ==
5
5
  data.tar.gz: !binary |-
6
- MmM2YmRkMDkwMjM2OTM2YjZlOTcyNjU0NmNmODQwNmJhNjI0YzdlNQ==
6
+ OGViZWY5NzFjNjI2NDRlYzVkOTNhNjliY2UxMDg3MTRmMTFhMTVmMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- YzEzZjdmYzEyNWJhZTRkODc0NmY4NjRjNTMxNmZiZWFmMWIwOTg5YmM3YzE5
10
- NjIwYTNlMTgzNzhhYTljYTUzNTk2NTk3YzJmOTgzYmNjNzAwY2NiMjFlMDYz
11
- MjU3ZmZmNjBhYTA0ZWUzOWJkY2MyNWI1ZmYyM2M5ZDVmYWNiNTA=
9
+ ODExM2M0MjhhOTE2OGRjNTIyNmFlZjU5MzkxZTY2YzVkMjRmMDljYzhmZDRl
10
+ M2ZmZWVjZDE5NjYzNTM3NzJiMGVkOGQ3YTJkZjJkYzE3Yzc0ODk5M2M3MmRm
11
+ MWIzNTk3ZDc5ODc5YWJjZTY3MGRhNzhlMTNiYTA5NTRiYzhhNDQ=
12
12
  data.tar.gz: !binary |-
13
- ZTEyZmVkYjIwYzQ2NjVkZjEyNTUzOGFjY2NkNGJjMzkyZWIzZDQ4ZWJiOGNi
14
- OGUxMGY5Nzk4YzUxZWEzMjE5Mjg3NDhkMDI1MzFjMWYyNjYxMDk2NmYzZDY2
15
- ODQ0NzIwMWY1NGQwYWZjYTE1NjRiMjFhNGZiYzI1Y2U2YjEwMjI=
13
+ NWIxNTNlNzk5NDY3NGQ1ZjI4NzY1ZDlmMWI0NTRkYzU2NDRiMzg1YmZlODVi
14
+ NTlhNzVkNDk4Yjc5MzdiMjgwMTExNzA4MGQwNGZhZDdmZTA5ZGY4Njg2MTUz
15
+ OWMxNDA4NjQwMzUxZTYxNGFhM2FkYjRjY2U4NTFiZmQ0NGEzZGU=
data/.gitignore CHANGED
@@ -1,8 +1,6 @@
1
1
  pkg
2
- NOTES.txt
3
2
  .bundle
4
3
  .DS_Store
5
4
  *.log
6
5
  tmp/
7
-
8
-
6
+ work/
data/Gemfile.lock CHANGED
@@ -1,15 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rest-ftp-daemon (0.30.1)
4
+ rest-ftp-daemon (0.55)
5
+ facter
5
6
  grape
6
7
  json
8
+ settingslogic
7
9
  thin (~> 1.6)
8
10
 
9
11
  GEM
10
12
  remote: http://rubygems.org/
11
13
  specs:
12
- activesupport (4.1.5)
14
+ CFPropertyList (2.2.8)
15
+ activesupport (4.1.6)
13
16
  i18n (~> 0.6, >= 0.6.9)
14
17
  json (~> 1.7, >= 1.7.7)
15
18
  minitest (~> 5.1)
@@ -27,6 +30,8 @@ GEM
27
30
  thread_safe (~> 0.3, >= 0.3.1)
28
31
  equalizer (0.0.9)
29
32
  eventmachine (1.0.3)
33
+ facter (2.2.0)
34
+ CFPropertyList (~> 2.2.6)
30
35
  grape (0.9.0)
31
36
  activesupport
32
37
  builder
@@ -50,6 +55,7 @@ GEM
50
55
  rack-mount (0.8.3)
51
56
  rack (>= 1.0.0)
52
57
  rake (10.3.2)
58
+ settingslogic (2.0.9)
53
59
  thin (1.6.2)
54
60
  daemons (>= 1.0.9)
55
61
  eventmachine (>= 1.0.0)
data/README.md CHANGED
@@ -1,38 +1,36 @@
1
1
  rest-ftp-daemon
2
2
  ====================================================================================
3
3
 
4
+
5
+
4
6
  This is a pretty simple FTP client daemon, controlled through a RESTfull API.
5
7
 
6
8
  As of today, its main features are :
7
9
 
10
+ * Allow environment-specific configuration in a YAML file
8
11
  * Delegate a transfer job by ``POST```'ing a simple JSON structure
9
12
  * Spawn a dedicated thread to handle this job in its own context
10
13
  * Report transfer status, progress and errors for each job in realtime
11
14
  * Expose JSON status of workers on ```GET /jobs/``` for automated monitoring
12
15
  * Parralelize jobs as soon as they arrive
16
+ * Handle job queues and priority as an attribute of the job
17
+ * Allow dynamic evaluation of priorities, and change of any attribute until the job is picked
18
+ * Provide RESTful notifications to the requesting client
19
+ * Allow authentication in FTP target in a standard URI-format
20
+ * Allow configuration-based path templates to abstract local mounts or remote FTPs (endpoint tokens)
21
+
13
22
 
14
23
  Expected features in a short-time range :
15
24
 
16
- * Handle job queues
17
- * Handle job priorities
18
25
  * Allow change of priorities or other attributes after a job has been started
19
- * Provide RESTful notifications to the requesting client
20
26
  * Offer a basic dashboard directly within the daemon HTTP interface
21
27
  * Periodically send an update-notification with transfer status and progress
22
28
  * Allow fallback file source when first file path is unavailable (failover)
23
- * Some refactoring may be needed after thos steps
24
29
  * Provide swagger-style API documentation
25
30
  * Authenticate API clients
26
31
 
27
32
 
28
33
 
29
- Documentation TODO
30
- ------------------------------------------------------------------------------------
31
- overwrite: any non empty value allows overwriting
32
- todo: queues
33
-
34
-
35
-
36
34
  Installation
37
35
  ------------------------------------------------------------------------------------
38
36
 
@@ -51,51 +49,117 @@ Start the daemon:
51
49
  rest-ftp-daemon start
52
50
  ```
53
51
 
52
+ Start the daemon on a specific port :
53
+
54
+ ```
55
+ rest-ftp-daemon -p4000 start
56
+ ```
57
+
54
58
  Check that the daemon is running and giving status info
55
59
 
56
60
  ```
57
61
  http://localhost:3000/
58
62
  ```
59
63
 
60
- For now, daemon logs to ```APP_LOGTO``` defined in ```lib/config.rb```
64
+ Configuration
65
+ ------------------------------------------------------------------------------------
66
+ Most of the configuration options live in a YAML configuration file, containing two main sections:
61
67
 
68
+ * the ``defaults`` section should be left as-is and will be used is no other environment-specific value is provided.
69
+ * the ``production`` section can receive personnalized settings according to your environment-specific setup and paths.
62
70
 
63
- Usage examples
71
+ Configuration priority is defined as follows (from most important to last resort):
72
+
73
+ * command-line parameters
74
+ * config file defaults section
75
+ * config file environment section
76
+ * application internal defaults
77
+
78
+
79
+ As a starting point, ``rest-ftp-daemon.yml.sample`` is an exemple config file that can be copied into the expected location ``/etc/rest-ftp-daemon.yml``.
80
+
81
+
82
+ Logging
64
83
  ------------------------------------------------------------------------------------
65
84
 
66
- Requesting notifications is achieved by passing a "notify" key in the request, with a callback URL. This URL will be called at some points, ``POST```'ing a generic JSON structure with progress information.
85
+ The application will not log to any file by default, if not specified in its configuration.
86
+ 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``.
87
+
88
+
89
+ Usage examples
90
+ ------------------------------------------------------------------------------------
67
91
 
68
- Start a job to transfer a file named "file.iso" to a local FTP server
92
+ * Start a job to transfer a file named "file.iso" to a local FTP server
69
93
 
70
94
  ```
71
95
  curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
72
96
  '{"source":"~/file.iso","target":"ftp://anonymous@localhost/incoming/dest2.iso"}' "http://localhost:3000/jobs"
73
97
  ```
74
98
 
75
- Start a job to transfer a file, and request notifications ``POST```'ed on "http://requestb.in/1321axg1"
99
+ Requesting notifications is achieved by passing a "notify" key in the request, with a callback URL.
100
+ This URL will be called at some points, ``POST```'ing a generic JSON structure with progress information.
101
+
102
+
103
+ * Start a job requesting notifications ``POST```'ed on "http://requestb.in/1321axg1"
76
104
 
77
105
  ```
78
106
  curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
79
107
  '{"source":"~/file.dmg","target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
80
108
  ```
81
109
 
82
- Get status of a specific job based on its name
110
+ * Start a job with all the above plus a priority
111
+
112
+ ```
113
+ curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
114
+ '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
115
+ ```
116
+
117
+ * Start a job using endpoint tokens
118
+
119
+ First define ``nas`` ans ``ftp1`` in the configuration file :
83
120
 
84
121
  ```
85
- curl -H "Content-Type: application/json" -X GET -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
122
+ defaults: &defaults
123
+
124
+ development:
125
+ <<: *defaults
126
+
127
+ endpoints:
128
+ nas: "~/"
129
+ ftp1: "ftp://anonymous@localhost/incoming/"
86
130
  ```
87
131
 
88
- Delete a specific job based on its name
132
+ Thos tokens will be expanded when the job is ran :
89
133
 
90
134
  ```
91
- curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/bob-45320-1"
135
+ curl -H "Content-Type: application/json" -X POST -D /dev/stdout -d \
136
+ '{"source":"~/file.dmg","priority":"3", target":"ftp://anonymous@localhost/incoming/dest4.dmg","notify":"http://requestb.in/1321axg1"}' "http://localhost:3000/jobs"
137
+ ```
138
+
139
+ NB: a special token [RANDOM] helps to generate a random filename when needed
140
+
141
+ * Get status of a specific job based on its ID
142
+
143
+ ```
144
+ curl -H "Content-Type: application/json" -X GET -D /dev/stdout "http://localhost:3000/jobs/3"
145
+ ```
146
+
147
+
148
+ * Delete a specific job based on its ID
149
+
150
+ ```
151
+ curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localhost:3000/jobs/3"
92
152
  ```
93
153
 
94
154
 
95
155
  Getting status
96
156
  ------------------------------------------------------------------------------------
97
157
 
98
- The server exposes jobs list on ``` GET /jobs ```
158
+ * A global JSON status is provided on ``` GET /index.json ```
159
+
160
+ * A nice dashboard gives a global view of the daemon, jobs in queue, and system status, exposed on ``` GET /```
161
+
162
+ * The server exposes its jobs list on ``` GET /jobs ```
99
163
 
100
164
  ```
101
165
  http://localhost:3000/jobs
@@ -120,18 +184,19 @@ This query will return a job list :
120
184
  "transferred": 37100000
121
185
  },
122
186
  {
123
- "source": "~/file.ova",
124
- "target": "ftp://anonymous@localhost/incoming/dest2.ova",
125
- "worker_name": "bob-92439-2",
126
- "created": "2014-08-01 16:53:12 +0200",
127
- "id": 17,
128
- "runtime": 13.8,
129
- "status": "uploading",
130
- "source_size": 1849036800,
131
- "error": -1,
132
- "errmsg": "uploading",
133
- "progress": 36.1,
134
- "transferred": 668300000
187
+ source: "[nas]/file.dmg",
188
+ target: "[ftp2]/dest4.dmg",
189
+ notify: "http://requestb.in/1321axg1",
190
+ updated_at: "2014-09-17 22:56:14 +0200",
191
+ id: 2,
192
+ started_at: "2014-09-17 22:56:01 +0200",
193
+ status: "uploading",
194
+ error: 0,
195
+ debug_source: "/Users/bruno/file.dmg",
196
+ debug_target: "#<URI::FTP:0x007ffa9289e650 URL:ftp://uuuuuuuu:yyyyyyyyy@ftp.xxxxxx.fr/subdir/dest4.dmg>",
197
+ file_size: 32093208,
198
+ file_progress: 5.6,
199
+ file_sent: 1800000
135
200
  }
136
201
  ]
137
202
  ```
data/bin/rest-ftp-daemon CHANGED
@@ -2,29 +2,37 @@
2
2
 
3
3
  # Libs and init
4
4
  require "thin"
5
-
6
- # Initialize some local constants
7
5
  APP_ROOT = File.dirname(__FILE__) + '/../'
8
- rackup_file = File.expand_path "#{APP_ROOT}/config.ru"
9
-
10
- # Include config file
11
- require File.expand_path "#{APP_ROOT}/lib/rest-ftp-daemon/config.rb"
6
+ DEVELOPMENT = ARGV.include?("development")
12
7
 
13
- # Prepare thin
8
+ # Include config file and build rack commandline
9
+ require File.expand_path("#{APP_ROOT}/lib/rest-ftp-daemon/config.rb")
14
10
  argv = ARGV
15
- argv << ["-R", rackup_file] unless ARGV.include?("-R")
16
- argv << ["-l", "/tmp/thin.log"] unless ARGV.include?("-l")
17
- argv << ["--daemonize"] unless ARGV.include?("--daemonize")
18
- argv << ["-e", "production"] unless ARGV.include?("-e")
19
- argv << ["-p", (RestFtpDaemon::PORT).to_s] unless ARGV.include?("-p")
20
- #argv << ["--stats", "/stats"] unless ARGV.include?("--stats")
11
+ argv << ["-p", Settings.port.to_s] unless ARGV.include?("-p") || Settings.port.nil?
12
+ argv << ["-e", Settings.namespace] unless ARGV.include?("-e") || Settings.namespace.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==1 || Settings.daemonize.nil?) unless ARGV.include?("-d")
15
+
16
+ # Rackup file
17
+ argv << ["-R", File.expand_path("#{APP_ROOT}/config.ru")] unless ARGV.include?("-R")
18
+
19
+ # Display compiled configuration
20
+ puts
21
+ puts "Daemon name \t #{Settings.name}"
22
+ puts "Version \t #{Settings.version}"
23
+ puts "Environment \t #{Settings.namespace}"
24
+ puts "Config file \t #{Settings.source}"
25
+ puts "Parameters \t #{argv.flatten}"
26
+ puts Settings.to_hash.to_yaml( :Indent => 4, :UseHeader => true, :UseVersion => false )
21
27
 
22
28
  # Start Thin with this rackup configuration
23
- puts "#{RestFtpDaemon::NAME}: using [#{argv.join (' ')}]"
29
+ puts
24
30
  begin
25
31
  Thin::Runner.new(argv.flatten).run!
32
+ rescue Thin::PidFileExist
33
+ puts "EXITING: daemon was already running (Thin::PidFileExist)"
26
34
  rescue Thin::PidFileNotFound
27
- puts "#{RestFtpDaemon::NAME}: not running (Thin::PidFileNotFound)"
35
+ puts "EXITING: daemon was not running (Thin::PidFileNotFound)"
28
36
  end
29
37
 
30
38
 
data/config.ru CHANGED
@@ -7,8 +7,9 @@ APP_STARTED = Time.now
7
7
 
8
8
  # Create worker pool
9
9
  $queue = RestFtpDaemon::JobQueue.new
10
- $pool = RestFtpDaemon::WorkerPool.new(1)
10
+ $pool = RestFtpDaemon::WorkerPool.new(Settings.workers.to_i)
11
11
 
12
12
  # Start REST FTP Daemon
13
13
  #run Rack::Cascade.new [Rack::File.new("/public"), RestFtpDaemon::API::Root]
14
+ #run Rack::URLMap.new("/" => Frontend.new, "/api" => Api.new)
14
15
  run Rack::Cascade.new [RestFtpDaemon::API::Root]
@@ -1,8 +1,6 @@
1
1
  # Global libs
2
+ require 'rubygems'
2
3
  require 'json'
3
- require 'grape'
4
- require 'net/ftp'
5
- require 'net/http'
6
4
  require 'securerandom'
7
5
  # require 'celluloid/autostart'
8
6
 
@@ -18,5 +16,4 @@ require 'rest-ftp-daemon/notification'
18
16
  require 'rest-ftp-daemon/api/defaults'
19
17
  require 'rest-ftp-daemon/api/jobs'
20
18
  require 'rest-ftp-daemon/api/root'
21
- require 'rest-ftp-daemon/www'
22
19
 
@@ -1,3 +1,5 @@
1
+ require 'grape'
2
+
1
3
  module RestFtpDaemon
2
4
  module API
3
5
  module Defaults
@@ -0,0 +1,105 @@
1
+ !!! 5
2
+ %html{:lang => "en"}
3
+ %head
4
+ %meta{:charset => "utf-8"}/
5
+ %link{ href:"//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css" , rel: "stylesheet"}
6
+
7
+ %title="#{Settings.name} dashboard"
8
+
9
+ :css
10
+
11
+ #footer {
12
+ padding: 15px 0;
13
+ _border-top: 1px solid silver;
14
+ }
15
+
16
+ .progress {
17
+ margin-bottom: 0;
18
+ }
19
+
20
+ %body
21
+
22
+ .container
23
+ %h1
24
+ = Settings.name
25
+ %small
26
+ = "v#{Settings.version}"
27
+ = "[#{Settings.namespace}]"
28
+
29
+
30
+ %h2 System status
31
+ .btn-group.btn-group-sm
32
+ .btn.btn-default.btn-warning Processors
33
+ .btn.btn-default= @info_procs
34
+
35
+ .btn-group.btn-group-sm
36
+ .btn.btn-default.btn-warning Load average
37
+ .btn.btn-default= @info_load.round(1)
38
+
39
+ .btn-group.btn-group-sm
40
+ .btn.btn-default.btn-warning Normalized load
41
+ .btn.btn-default= "#{@info_norm} %"
42
+
43
+ .btn-group.btn-group-sm
44
+ .btn.btn-default.btn-success IP Address
45
+ .btn.btn-default= @info_ipaddr
46
+
47
+ .btn-group.btn-group-sm
48
+ .btn.btn-default.btn-success Memory free
49
+ .btn.btn-default= @info_memfree
50
+
51
+ .btn-group.btn-group-sm
52
+ .btn.btn-default.btn-info Settings.workers
53
+ .btn.btn-default= Settings.workers
54
+
55
+
56
+ %h2
57
+ Jobs on this system (#{@jobs_all})
58
+ %small
59
+ queued (#{@jobs_queued}) / popped (#{@jobs_popped})
60
+
61
+ %table.table.table-striped.table-hover.table-condensed
62
+ %tr
63
+ %th= "ID"
64
+ %th= "priority"
65
+ %th= "source"
66
+ %th= "target"
67
+ %th= "status"
68
+ %th= "error"
69
+ %th= "progress"
70
+
71
+ - @jobs.each do |job|
72
+ - error = job.get :error
73
+ - status = job.get :status
74
+ - progress = job.get :progress
75
+
76
+ - if error!=0 && !error.nil?
77
+ - trclass = "danger"
78
+ - elsif status == :uploading
79
+ - trclass = "info"
80
+ - elsif status == :finished
81
+ - trclass = "success"
82
+ - else
83
+ - trclass = "warning"
84
+
85
+ %tr{class: trclass}
86
+
87
+ %td= job.id
88
+ %td= job.get :priority
89
+ %td= job.get :source
90
+ %td= job.get :target
91
+ %td= status
92
+ %td= error
93
+ %td
94
+ - unless progress.nil?
95
+ .progress
96
+ .progress-bar{style:"width: #{progress}%;"}
97
+ = "#{progress} %"
98
+
99
+
100
+ %h2 Endpoint tokens
101
+ - endpoints = Settings.endpoints || {}
102
+ - endpoints.each do |token, value|
103
+ .btn-group.btn-group-sm
104
+ .btn.btn-default.btn-danger= token
105
+ .btn.btn-default= value
@@ -3,7 +3,7 @@ module RestFtpDaemon
3
3
 
4
4
  class Jobs < Grape::API
5
5
  include RestFtpDaemon::API::Defaults
6
- logger ActiveSupport::Logger.new APP_LOGTO, 'daily'
6
+ logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
7
7
 
8
8
  params do
9
9
  optional :overwrite, type: Integer, default: false
@@ -1,9 +1,14 @@
1
+ require 'haml'
2
+ require "facter"
3
+ require "sys/cpu"
4
+
5
+
1
6
  module RestFtpDaemon
2
7
  module API
3
8
 
4
9
  class Root < Grape::API
5
10
  include RestFtpDaemon::API::Defaults
6
- logger ActiveSupport::Logger.new APP_LOGTO, 'daily'
11
+ logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
7
12
  #add_swagger_documentation
8
13
 
9
14
  mount RestFtpDaemon::API::Jobs => '/jobs'
@@ -41,12 +46,54 @@ module RestFtpDaemon
41
46
 
42
47
  # Server global status
43
48
  get '/' do
49
+ # Prepare data
50
+ @jobs = $queue.all
51
+ @jobs_all = $queue.all_size
52
+ @jobs_queued = $queue.queued_size
53
+ @jobs_popped = $queue.popped_size
54
+
55
+ # Initialize UsageWatch
56
+ Facter.loadfacts
57
+ @info_load = Sys::CPU.load_avg.first.to_f
58
+ @info_procs = (Facter.value :processorcount).to_i
59
+ @info_ipaddr = Facter.value :ipaddress
60
+ @info_memfree = Facter.value :memoryfree
61
+
62
+ # Compute normalized load
63
+ if @info_procs.zero?
64
+ @info_norm = "N/A"
65
+ else
66
+ @info_norm = (100 * @info_load / @info_procs).round(1)
67
+ end
68
+
69
+ # Compile haml template
70
+ @name = "Test"
71
+ template = File.read("#{File.dirname(__FILE__)}/index.haml")
72
+ haml_engine = Haml::Engine.new(template)
73
+ output = haml_engine.render(binding)
74
+ #[200, {'Content-Type' => 'text/html'}, [output]]
75
+
76
+ # Generate output
77
+ # env['api.format'] = :binary
78
+ # env['api.format'] = :txt
79
+
80
+ # Send response
81
+ env['api.format'] = :html
82
+ format "html"
83
+ status 200
84
+ content_type "text/html"
85
+ body output
86
+
87
+ end
88
+
89
+ # Server global status
90
+ get '/index.json' do
44
91
  info "GET /"
45
92
  status 200
46
93
  return {
47
- name: RestFtpDaemon::NAME,
48
94
  hostname: `hostname`.chomp,
49
- version: RestFtpDaemon::VERSION,
95
+ version: Settings.version,
96
+ config: Settings.to_hash,
50
97
  started: APP_STARTED,
51
98
  uptime: (Time.now - APP_STARTED).round(1),
52
99
  status: job_list_by_status,
@@ -0,0 +1,49 @@
1
+ # module RestFtpDaemon
2
+ # module API
3
+
4
+ # class Workers < Grape::API
5
+ # include RestFtpDaemon::API::Defaults
6
+ # logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
7
+
8
+ # helpers do
9
+ # def info message, level = 0
10
+ # Jobs.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Workers")
11
+ # end
12
+
13
+ # def worker_list
14
+ # return {
15
+ # busy: $pool.busy_size,
16
+ # idle: $pool.idle_size,
17
+ # to_s: $pool.to_s,
18
+ # }
19
+ # #return $workers.list.size
20
+ # $workers.list.map do |thread|
21
+ # #next unless thread[:job].is_a? Worker
22
+ # "worker"
23
+ # #thread[:worker].inspect
24
+ # end
25
+ # end
26
+
27
+ # end
28
+
29
+ # # List jobs
30
+ # desc "Get a list of workers"
31
+ # get do
32
+ # info "GET /workers"
33
+ # begin
34
+ # response = worker_list
35
+ # rescue RestFtpDaemonException => exception
36
+ # status 501
37
+ # api_error exception
38
+ # rescue Exception => exception
39
+ # status 501
40
+ # api_error exception
41
+ # else
42
+ # status 200
43
+ # response
44
+ # end
45
+ # end
46
+
47
+ # end
48
+ # end
49
+ # end
@@ -6,7 +6,7 @@ module RestFtpDaemon
6
6
 
7
7
  def initialize
8
8
  # Logger
9
- @logger = ActiveSupport::Logger.new APP_LOGTO, 'daily'
9
+ @logger = ActiveSupport::Logger.new Settings.logs.workers, 'daily' unless Settings.logs.workers.nil?
10
10
  end
11
11
 
12
12
  def id
@@ -1,15 +1,24 @@
1
- module RestFtpDaemon
2
- # Global config
3
- NAME = 'rest-ftp-daemon'
4
- VERSION = "0.41"
5
- PORT = 3000
1
+ require 'settingslogic'
2
+ DEVELOPMENT = false unless defined? DEVELOPMENT
3
+ CONFIG_FILE = "/etc/rest-ftp-daemon.yml"
6
4
 
7
- # Transfer config
8
- TRANSFER_CHUNK_SIZE = 100000
9
- THREAD_SLEEP_BEFORE_DIE = 10
10
-
11
- # Logging
12
- APP_LOGTO = "/tmp/#{NAME}.log"
13
- LOG_TRIM_PROGNAME = 18
5
+ class Settings < Settingslogic
6
+ namespace DEVELOPMENT ? "development" : "production"
7
+ suppress_errors namespace!="development"
8
+ end
14
9
 
10
+ # Fix application defaults and load config file if found
11
+ if File.exists? CONFIG_FILE
12
+ Settings.source CONFIG_FILE
13
+ else
14
+ Settings.source Hash.new
15
15
  end
16
+
17
+ # Forced shared settings
18
+ Settings[:name] = "rest-ftp-daemon"
19
+ Settings[:version] = 0.55
20
+
21
+ # Forced fixed settings
22
+ Settings[:default_trim_progname] = "18"
23
+ Settings[:default_chunk_size] = "1000000"
24
+
@@ -1,3 +1,6 @@
1
+ require 'net/ftp'
2
+
3
+
1
4
  module RestFtpDaemon
2
5
  class Job < RestFtpDaemon::Common
3
6
 
@@ -10,22 +13,15 @@ module RestFtpDaemon
10
13
  @target = nil
11
14
  @source = nil
12
15
 
13
- # Logger
14
- #@logger = ActiveSupport::Logger.new APP_LOGTO, 'daily'
15
-
16
16
  # Init context
17
17
  set :id, id
18
18
  set :started_at, Time.now
19
- set :status, :initialized
19
+ set :status, :created
20
20
 
21
21
  # Send first notification
22
22
  notify "rftpd.queued"
23
23
  end
24
24
 
25
- # def job_id
26
- # get :id
27
- # end
28
-
29
25
  def progname
30
26
  job_id = get(:id)
31
27
  "JOB #{job_id}"
@@ -99,6 +95,11 @@ module RestFtpDaemon
99
95
  @status = text
100
96
  end
101
97
 
98
+ def get attribute
99
+ return nil unless @params.is_a? Enumerable
100
+ @params[attribute.to_s]
101
+ end
102
+
102
103
  protected
103
104
 
104
105
  def up_time
@@ -130,9 +131,28 @@ module RestFtpDaemon
130
131
  @params[attribute.to_s] = value
131
132
  end
132
133
 
133
- def get attribute
134
- return nil unless @params.is_a? Enumerable
135
- @params[attribute.to_s]
134
+ def expand_path_from path
135
+ File.expand_path replace_token_in_path(path)
136
+ end
137
+
138
+ def expand_url_from path
139
+ URI replace_token_in_path(path) rescue nil
140
+ end
141
+
142
+ def replace_token_in_path path
143
+ # Ensure endpoints are not a nil value
144
+ return path unless Settings.endpoints.is_a? Enumerable
145
+ newpath = path.clone
146
+
147
+ # Replace endpoints defined in config
148
+ Settings.endpoints.each do |from, to|
149
+ newpath.gsub! "[#{from}]", to
150
+ end
151
+
152
+ # Replace with the special RAND token
153
+ newpath.gsub! "[RANDOM]", SecureRandom.hex(8)
154
+
155
+ return newpath
136
156
  end
137
157
 
138
158
  def prepare
@@ -141,15 +161,18 @@ module RestFtpDaemon
141
161
 
142
162
  # Check source
143
163
  raise JobSourceMissing unless @params["source"]
144
- @source = File.expand_path(@params["source"])
164
+ @source = expand_path_from @params["source"]
145
165
  set :debug_source, @source
146
- raise JobSourceNotFound unless File.exists? @source
147
166
 
148
167
  # Check target
149
168
  raise JobTargetMissing unless @params["target"]
150
- @target = URI(@params["target"]) rescue nil
169
+ @target = expand_url_from @params["target"]
151
170
  set :debug_target, @target.inspect
171
+
172
+ # Check compliance
152
173
  raise JobTargetUnparseable if @target.nil?
174
+ raise JobSourceNotFound unless File.exists? @source
175
+
153
176
  end
154
177
 
155
178
  def transfer_fake
@@ -200,20 +223,24 @@ module RestFtpDaemon
200
223
 
201
224
  # Do transfer
202
225
  set :status, :uploading
203
- ftp.putbinaryfile(@source, target_name, TRANSFER_CHUNK_SIZE) do |block|
226
+ chunk_size = Settings.transfer.chunk_size || Settings[:default_chunk_size]
227
+ ftp.putbinaryfile(@source, target_name, chunk_size) do |block|
204
228
  # Update counters
205
229
  transferred += block.bytesize
206
230
 
207
231
  # Update job info
208
232
  percent = (100.0 * transferred / source_size).round(1)
209
- set :file_progress, percent
233
+ set :progress, percent
210
234
  set :file_sent, transferred
211
235
  end
212
236
 
213
237
  # Close FTP connexion
214
238
  notify "rftpd.ended"
239
+ set :progress, nil
215
240
  ftp.close
216
241
  end
217
242
 
243
+ private
244
+
218
245
  end
219
246
  end
@@ -1,7 +1,7 @@
1
1
  class Logger
2
2
  def format_message(severity, timestamp, progname, msg)
3
3
  stamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
4
- progname = "%-#{RestFtpDaemon::LOG_TRIM_PROGNAME}s" % progname
4
+ progname = "%-#{Settings[:default_trim_progname]}s" % progname
5
5
  "#{stamp} #{severity} #{progname} #{msg}\n"
6
6
  end
7
7
  end
@@ -1,3 +1,5 @@
1
+ require 'net/http'
2
+
1
3
  module RestFtpDaemon
2
4
  class Notification < RestFtpDaemon::Common
3
5
  attr_accessor :job_id
@@ -4,7 +4,8 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'rest-ftp-daemon/config'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = RestFtpDaemon::NAME
7
+ #spec.name = RestFtpDaemon::NAME
8
+ spec.name = Settings[:name]
8
9
  spec.date = Time.now.strftime("%Y-%m-%d")
9
10
  spec.authors = ["Bruno MEDICI"]
10
11
  spec.email = "rest-ftp-daemon@bmconseil.com"
@@ -16,7 +17,7 @@ Gem::Specification.new do |spec|
16
17
  spec.files = `git ls-files -z`.split("\x0")
17
18
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
19
  spec.require_paths = ["lib"]
19
- spec.version = RestFtpDaemon::VERSION
20
+ spec.version = Settings[:version]
20
21
 
21
22
  spec.required_ruby_version = '>= 1.9.3'
22
23
 
@@ -25,6 +26,8 @@ Gem::Specification.new do |spec|
25
26
 
26
27
  spec.add_runtime_dependency "thin", "~> 1.6"
27
28
  spec.add_runtime_dependency "grape"
29
+ spec.add_runtime_dependency "facter"
30
+ spec.add_runtime_dependency "settingslogic"
28
31
  spec.add_runtime_dependency "json"
29
32
 
30
33
  end
@@ -0,0 +1,15 @@
1
+ defaults: &defaults
2
+ daemonize: 1
3
+ port: 3000
4
+ workers: 1
5
+
6
+ transfer:
7
+ chunk_size: 100000
8
+
9
+ logs:
10
+ thin:
11
+ api:
12
+ workers:
13
+
14
+ production:
15
+ <<: *defaults
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest-ftp-daemon
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.41'
4
+ version: '0.55'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bruno MEDICI
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-14 00:00:00.000000000 Z
11
+ date: 2014-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,34 @@ dependencies:
66
66
  - - ! '>='
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: facter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: settingslogic
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
69
97
  - !ruby/object:Gem::Dependency
70
98
  name: json
71
99
  requirement: !ruby/object:Gem::Requirement
@@ -98,8 +126,10 @@ files:
98
126
  - config.ru
99
127
  - lib/rest-ftp-daemon.rb
100
128
  - lib/rest-ftp-daemon/api/defaults.rb
129
+ - lib/rest-ftp-daemon/api/index.haml
101
130
  - lib/rest-ftp-daemon/api/jobs.rb
102
131
  - lib/rest-ftp-daemon/api/root.rb
132
+ - lib/rest-ftp-daemon/api/workers.rb
103
133
  - lib/rest-ftp-daemon/common.rb
104
134
  - lib/rest-ftp-daemon/config.rb
105
135
  - lib/rest-ftp-daemon/exceptions.rb
@@ -108,8 +138,8 @@ files:
108
138
  - lib/rest-ftp-daemon/logger.rb
109
139
  - lib/rest-ftp-daemon/notification.rb
110
140
  - lib/rest-ftp-daemon/worker_pool.rb
111
- - lib/rest-ftp-daemon/www.rb
112
141
  - rest-ftp-daemon.gemspec
142
+ - rest-ftp-daemon.yml.sample
113
143
  - test/helper.rb
114
144
  - test/test_rest-ftp-daemon.rb
115
145
  homepage: http://github.com/bmedici/rest-ftp-daemon
@@ -1,6 +0,0 @@
1
- module RestFtpDaemon
2
-
3
- class WWW
4
- end
5
-
6
- end