rest-ftp-daemon 0.41 → 0.55

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.
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