rest-ftp-daemon 0.60 → 0.70

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
- NWFjZTBkMGEwMWVhNjEzMmFhZDFhY2IyZmE0OThmYzk0YzdlNzAxMQ==
4
+ MDZkM2FiNjVjM2U0ZWYzOWFiOThhMGVjM2YzMjc2NzFmYWVkN2QyYQ==
5
5
  data.tar.gz: !binary |-
6
- OTFlYWZiODY4MjY4OGFlOWY2NzI4Yjk4YmE4OWFhZjFiZmIxNzg1MQ==
6
+ YmVlMjAzMjEzYjg3NTkzNDQzYmM3NTM5MmE3OGQxODM3MTcwOTEzNA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- OGIwMWE4NTQxM2IzOGRkM2E5MWY3YzdlYjI4N2EyMjdmNTYzYmJjOGY1NDUw
10
- MGQ4MzAyN2ZiODY1N2FhOWZkOWZiMmMzMjQ2MmZjMGM1MDY0MjEyOWE1N2Ew
11
- NGEwNjkyN2NjMGFjNWJkYzI4ODdjZjkwNGM1YzkzMjhkNjg4NTA=
9
+ MjYxNGE4N2YwNTI4YzhkMmJiODczMTYyZWYyOTI5YmVhNWJhZDkzOWUwOTg2
10
+ YWI4MTBkZjM1ZWVhODgwZWViZGFkNzMyMjg3YWVjNWI1NjhiMGMwNWJhMmMy
11
+ ZGE5NTNmMzM1YmY3OGI4NzdlMjU3MjYyNzlhMThlOWFlZWE0NDg=
12
12
  data.tar.gz: !binary |-
13
- OTA1NDU1ZGU4NjNmNTE2Y2EzODM4MDE5OGU2M2RhOWRhNjM5ZDdiNzg4ZDIz
14
- M2E4ZGJmNzYxZTk5MWQ3NDJmZGUyZTMwMDI3ODY4MTRmOWM0N2JmMDcyZjY1
15
- ZmFiYTI5NTQ3OTY0OTBhZjE0YWM3NDBiNjI0MGNkM2FiYjhhMzc=
13
+ NjUwOWNlZDRiOWY3Mjk0OTI5N2MwODA0NmY5YTYxZmRiMTViZWE3ZDNjZGZi
14
+ YmViNGQ0M2FlNTI4M2EzZTE1OTIzZmZiMmJkNTFiMzUyYzg0MjYxNDQ0N2Y2
15
+ NWI0M2YxOTUyNjM3N2MwODRkNDI0ZjBmZWRiZGU2ODM3OWQ3NzI=
data/Gemfile.lock CHANGED
@@ -1,11 +1,14 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rest-ftp-daemon (0.55)
4
+ rest-ftp-daemon (0.63)
5
+ double-bag-ftps
5
6
  facter
6
7
  grape
8
+ haml
7
9
  json
8
10
  settingslogic
11
+ sys-cpu
9
12
  thin (~> 1.6)
10
13
 
11
14
  GEM
@@ -28,10 +31,12 @@ GEM
28
31
  daemons (1.1.9)
29
32
  descendants_tracker (0.0.4)
30
33
  thread_safe (~> 0.3, >= 0.3.1)
34
+ double-bag-ftps (0.1.2)
31
35
  equalizer (0.0.9)
32
36
  eventmachine (1.0.3)
33
37
  facter (2.2.0)
34
38
  CFPropertyList (~> 2.2.6)
39
+ ffi (1.9.5)
35
40
  grape (0.9.0)
36
41
  activesupport
37
42
  builder
@@ -42,11 +47,13 @@ GEM
42
47
  rack-accept
43
48
  rack-mount
44
49
  virtus (>= 1.0.0)
50
+ haml (4.0.5)
51
+ tilt
45
52
  hashie (3.3.1)
46
53
  i18n (0.6.11)
47
54
  ice_nine (0.11.0)
48
55
  json (1.8.1)
49
- minitest (5.4.1)
56
+ minitest (5.4.2)
50
57
  multi_json (1.10.1)
51
58
  multi_xml (0.5.5)
52
59
  rack (1.5.2)
@@ -56,11 +63,14 @@ GEM
56
63
  rack (>= 1.0.0)
57
64
  rake (10.3.2)
58
65
  settingslogic (2.0.9)
59
- thin (1.6.2)
60
- daemons (>= 1.0.9)
61
- eventmachine (>= 1.0.0)
62
- rack (>= 1.0.0)
66
+ sys-cpu (0.7.1)
67
+ ffi (>= 1.0.0)
68
+ thin (1.6.3)
69
+ daemons (~> 1.0, >= 1.0.9)
70
+ eventmachine (~> 1.0)
71
+ rack (~> 1.0)
63
72
  thread_safe (0.3.4)
73
+ tilt (2.0.1)
64
74
  tzinfo (1.2.2)
65
75
  thread_safe (~> 0.1)
66
76
  virtus (1.0.3)
data/README.md CHANGED
@@ -18,7 +18,7 @@ As of today, its main features are :
18
18
  * Provide RESTful notifications to the requesting client
19
19
  * Allow authentication in FTP target in a standard URI-format
20
20
  * Allow configuration-based path templates to abstract local mounts or remote FTPs (endpoint tokens)
21
-
21
+ * Remote supported protocols: FTP and FTPs
22
22
 
23
23
  Expected features in a short-time range :
24
24
 
@@ -28,7 +28,8 @@ Expected features in a short-time range :
28
28
  * Allow fallback file source when first file path is unavailable (failover)
29
29
  * Provide swagger-style API documentation
30
30
  * Authenticate API clients
31
-
31
+ * Allow to specify random remote/local source/target
32
+ * Allow more remote protocols: sFTP, FTPs, HTTP POST, etc
32
33
 
33
34
 
34
35
  Installation
@@ -39,7 +40,7 @@ This project is available as a rubygem, requires on ruby >= 1.9.3 and rubygems i
39
40
  Get and install the gem from rubygems.org:
40
41
 
41
42
  ```
42
- # apt-get install ruby1.9.3 ruby-dev rubygems gcc g++
43
+ # apt-get install ruby1.9.3 ruby1.9.1-dev ruby-dev rubygems gcc g++
43
44
  gem install rest-ftp-daemon --no-ri --no-rdoc
44
45
  ```
45
46
 
@@ -55,10 +56,10 @@ Start the daemon on a specific port :
55
56
  rest-ftp-daemon -p4000 start
56
57
  ```
57
58
 
58
- Check that the daemon is running and giving status info
59
+ Check that the daemon is running and providing its status info
59
60
 
60
61
  ```
61
- http://localhost:3000/
62
+ http://localhost:3200/
62
63
  ```
63
64
 
64
65
  Configuration
@@ -155,7 +156,7 @@ curl -H "Content-Type: application/json" -X DELETE -D /dev/stdout "http://localh
155
156
  Getting status
156
157
  ------------------------------------------------------------------------------------
157
158
 
158
- * A global JSON status is provided on ``` GET /index.json ```
159
+ * A global JSON status is provided on ``` GET /status ```
159
160
 
160
161
  * A nice dashboard gives a global view of the daemon, jobs in queue, and system status, exposed on ``` GET /```
161
162
 
data/bin/rest-ftp-daemon CHANGED
@@ -2,41 +2,40 @@
2
2
 
3
3
  # Libs and init
4
4
  require "thin"
5
- DEVELOPMENT=ARGV.include?("development")
6
- HERE=File.expand_path(File.dirname(__FILE__) + '/../')
7
- #APP_NAME2="rest-ftp-daemon"
8
5
 
9
6
  # Include config file and build rack commandline
10
- require File.expand_path("#{HERE}/lib/rest-ftp-daemon/config.rb")
7
+ app_root = File.expand_path(File.dirname(__FILE__) + '/../')
8
+ require File.expand_path("lib/rest-ftp-daemon/config.rb")
11
9
 
10
+ # Build rackup context
11
+ settings_daemonize = [nil, true, 1, "1"].include?(Settings.daemonize)
12
12
  argv = ARGV
13
- argv << ["-p", Settings.port.to_s] unless ARGV.include?("-p") || Settings.port.nil?
14
- argv << ["-e", Settings.namespace] unless ARGV.include?("-e") || Settings.namespace.nil?
15
- argv << ["-l", Settings.logs.thin] unless ARGV.include?("-l") unless Settings.logs.thin.nil? unless Settings.logs.nil?
16
- argv << ["--daemonize"] if (Settings.daemonize==1 || Settings.daemonize.nil?) unless ARGV.include?("-d")
17
-
18
- # Rackup file
19
- argv << ["-R", File.expand_path("#{HERE}/config.ru")] unless ARGV.include?("-R")
13
+ argv << ["-e", "development"] unless ARGV.include?("-e")
14
+ argv << ["-p", Settings[:port].to_s] unless ARGV.include?("-p") || Settings[:port].nil?
15
+ argv << ["-l", Settings[:logs][:thin]] unless ARGV.include?("-l") unless Settings[:logs][:thin].nil? unless Settings[:logs].nil?
16
+ argv << ["--daemonize"] if settings_daemonize unless (ARGV.include?("-d") || ARGV.include?("--daemonize"))
17
+ argv << ["-R", File.expand_path("config.ru")] unless ARGV.include?("-R")
20
18
 
21
19
  # Display compiled configuration
22
- puts
23
- puts "Daemon name \t #{Settings.name}"
24
- puts "Version \t #{Settings.version}"
20
+ puts "---"
21
+ puts "Daemon name \t #{Settings.app_name}"
22
+ puts "Version \t #{Settings.app_ver}"
25
23
  puts "Environment \t #{Settings.namespace}"
26
24
  puts "Config file \t #{Settings.source}"
27
25
  puts "Parameters \t #{argv.flatten}"
28
26
  puts Settings.to_hash.to_yaml( :Indent => 4, :UseHeader => true, :UseVersion => false )
29
27
 
30
28
  # Start Thin with this rackup configuration
29
+ puts "---"
31
30
  puts
32
31
  begin
33
32
  Thin::Runner.new(argv.flatten).run!
34
- rescue Thin::PidFileExist
35
- puts "EXITING: daemon was already running (Thin::PidFileExist)"
36
- rescue Thin::PidFileNotFound
37
- puts "EXITING: daemon was not running (Thin::PidFileNotFound)"
38
- else
39
- puts "PROCESS ENDING"
33
+ rescue Thin::PidFileExist
34
+ puts "ERROR: daemon was already running (Thin::PidFileExist)"
35
+ rescue Thin::PidFileNotFound
36
+ puts "ERROR: daemon was not running (Thin::PidFileNotFound)"
37
+ rescue SystemExit
38
+ puts "EXITING: daemon in the background (SystemExit)"
39
+ else
40
+ puts "EXITING: process ending"
40
41
  end
41
-
42
-
data/config.ru CHANGED
@@ -1,14 +1,23 @@
1
1
  # Load gem files
2
- $LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
2
+ APP_LIBS = File.expand_path(File.join(File.dirname(__FILE__), 'lib'))
3
+ $LOAD_PATH.unshift(APP_LIBS) unless $LOAD_PATH.include?(APP_LIBS)
3
4
  require 'rest-ftp-daemon'
4
5
 
5
- # Some extra constants
6
- APP_STARTED = Time.now
7
-
8
- # Create worker pool
6
+ # Create queue and worker pool
9
7
  $queue = RestFtpDaemon::JobQueue.new
10
8
  $pool = RestFtpDaemon::WorkerPool.new(Settings.workers.to_i)
11
9
 
12
- # Start REST FTP Daemon
13
- use Rack::Static, :urls => ["/css", "/images"], :root => "lib/rest-ftp-daemon/static/"
10
+ # Rack middleware
11
+ # use Rack::Etag # Add an ETag
12
+ # use Rack::Reloader, 0
13
+ # Rack::Auth::Basic
14
+ # use Rack::Auth::Basic, "Restricted Area" do |username, password|
15
+ # [username, password] == ['admin', 'admin']
16
+ # end
17
+ #use Rack::Deflator # Compress
18
+
19
+ # Serve static assets
20
+ use Rack::Static, :urls => ["/css", "/images"], :root => "lib/#{Settings.app_name}/static/"
21
+
22
+ # Launch the main daemon
14
23
  run Rack::Cascade.new [RestFtpDaemon::API::Root]
@@ -8,6 +8,7 @@ require 'securerandom'
8
8
  require 'rest-ftp-daemon/config'
9
9
  require 'rest-ftp-daemon/exceptions'
10
10
  require 'rest-ftp-daemon/common'
11
+ require 'rest-ftp-daemon/uri'
11
12
  require 'rest-ftp-daemon/job_queue'
12
13
  require 'rest-ftp-daemon/worker_pool'
13
14
  require 'rest-ftp-daemon/logger'
@@ -45,7 +45,6 @@ module RestFtpDaemon
45
45
  # end
46
46
 
47
47
  helpers do
48
-
49
48
  def format_nice_bytes( number )
50
49
  return "Ø" if number.nil? || number.zero?
51
50
  index = ( Math.log( number ) / Math.log( 2 ) ).to_i / 10
@@ -63,7 +62,7 @@ module RestFtpDaemon
63
62
  end
64
63
 
65
64
  def render name, values={}
66
- template = File.read("#{APP_ROOT}/lib/#{APP_NAME}/views/#{name.to_s}.haml")
65
+ template = File.read("lib/#{Settings.app_name}/views/#{name.to_s}.haml")
67
66
  haml_engine = Haml::Engine.new(template)
68
67
  haml_engine.render(binding, values)
69
68
  end
@@ -2,13 +2,33 @@ module RestFtpDaemon
2
2
  module API
3
3
 
4
4
  class Jobs < Grape::API
5
- include RestFtpDaemon::API::Defaults
6
- logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
5
+
6
+
7
+ ####### CLASS CONFIG
8
+
9
+ #include RestFtpDaemon::API::Defaults
10
+ #logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
7
11
 
8
12
  params do
9
13
  optional :overwrite, type: Integer, default: false
10
14
  end
11
15
 
16
+
17
+ ####### INITIALIZATION
18
+
19
+ def initialize
20
+ $last_worker_id = 0
21
+
22
+ # Check that Queue and Pool are available
23
+ raise RestFtpDaemon::MissingQueue unless defined? $queue
24
+ raise RestFtpDaemon::MissingQueue unless defined? $pool
25
+
26
+ super
27
+ end
28
+
29
+
30
+ ####### HELPERS
31
+
12
32
  helpers do
13
33
 
14
34
  def threads_with_id job_id
@@ -42,6 +62,9 @@ module RestFtpDaemon
42
62
 
43
63
  end
44
64
 
65
+
66
+ ####### API DEFINITION
67
+
45
68
  desc "Get information about a specific job"
46
69
  params do
47
70
  requires :id, type: Integer, desc: "job id"
@@ -7,13 +7,19 @@ module RestFtpDaemon
7
7
  module API
8
8
 
9
9
  class Root < Grape::API
10
+
11
+
12
+ ####### CLASS CONFIG
10
13
  include RestFtpDaemon::API::Defaults
11
- logger ActiveSupport::Logger.new Settings.logs.api, 'daily' unless Settings.logs.api.nil?
14
+ logger ActiveSupport::Logger.new(Settings.logs.api, 'daily') unless Settings.logs.api.nil?
12
15
  #add_swagger_documentation
13
16
 
14
17
  mount RestFtpDaemon::API::Jobs => '/jobs'
15
18
  # mount RestFtpDaemon::API::Workers => '/workers'
16
19
 
20
+
21
+ ####### HELPERS
22
+
17
23
  helpers do
18
24
  def info message, level = 0
19
25
  Root.logger.add(Logger::INFO, "#{' '*level} #{message}", "API::Root")
@@ -32,19 +38,21 @@ module RestFtpDaemon
32
38
 
33
39
  end
34
40
 
35
- ######################################################################
36
- ####### INIT
37
- ######################################################################
41
+
42
+ ####### INITIALIZATION
43
+
38
44
  def initialize
39
45
  $last_worker_id = 0
46
+
47
+ # Check that Queue and Pool are available
48
+ raise RestFtpDaemon::MissingQueue unless defined? $queue
49
+ raise RestFtpDaemon::MissingQueue unless defined? $pool
50
+
40
51
  super
41
52
  end
42
53
 
43
54
 
44
-
45
- ######################################################################
46
- ####### API DEFINITION
47
- ######################################################################
55
+ ####### API DEFINITION
48
56
 
49
57
  # Server global status
50
58
  get '/' do
@@ -90,15 +98,15 @@ module RestFtpDaemon
90
98
  end
91
99
 
92
100
  # Server global status
93
- get '/index.json' do
101
+ get '/status' do
94
102
  info "GET /"
95
103
  status 200
96
104
  return {
97
105
  hostname: `hostname`.chomp,
98
- version: Settings.version,
106
+ version: Settings.app_ver,
99
107
  config: Settings.to_hash,
100
- started: APP_STARTED,
101
- uptime: (Time.now - APP_STARTED).round(1),
108
+ started: Settings.app_started,
109
+ uptime: (Time.now - Settings.app_started).round(1),
102
110
  status: job_list_by_status,
103
111
  queue_size: $queue.all_size,
104
112
  jobs_queued: $queue.queued.collect(&:id),
@@ -18,7 +18,7 @@ module RestFtpDaemon
18
18
  def info message, level = 0
19
19
  # progname = "Job [#{id}]" unless id.nil?
20
20
  # progname = "Worker [#{id}]" unless worker_id.nil?
21
- @logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname)
21
+ @logger.add(Logger::INFO, "#{' '*(level+1)} #{message}", progname) unless @logger.nil?
22
22
  end
23
23
 
24
24
  def notify signal, error = 0, status = {}
@@ -1,24 +1,24 @@
1
1
  require 'settingslogic'
2
- DEVELOPMENT = false unless defined? DEVELOPMENT
3
- APP_NAME="rest-ftp-daemon"
4
2
 
5
- class Settings < Settingslogic
6
- namespace DEVELOPMENT ? "development" : "production"
7
- suppress_errors namespace!="development"
8
- end
3
+ # Terrific assertions
4
+ #raise "config.rb: APP_ROOT is not defined" unless defined? APP_ROOT
5
+ APP_NAME = "rest-ftp-daemon"
6
+ APP_CONF = "/etc/#{APP_NAME}.yml"
7
+ APP_DEV = ARGV.include?("development") ? true : false
9
8
 
10
- # Fix application defaults and load config file if found
11
- app_config_file = "/etc/#{APP_NAME}.yml"
12
- if File.exists? app_config_file
13
- Settings.source app_config_file
14
- else
15
- Settings.source Hash.new
16
- end
9
+ class Settings < Settingslogic
10
+ # Read configuration
11
+ source (File.exists? APP_CONF) ? APP_CONF : Hash.new
12
+ namespace (APP_DEV ? "development" : "production")
13
+ suppress_errors true
17
14
 
18
- # Forced shared settings
19
- Settings[:name] = APP_NAME
20
- Settings[:version] = "0.60"
15
+ # Some constants
16
+ self[:dev] = APP_DEV
17
+ self[:app_name] = APP_NAME
18
+ self[:app_ver] = "0.70"
19
+ self[:app_started] = Time.now
21
20
 
22
- # Forced fixed settings
23
- Settings[:default_trim_progname] = "18"
24
- Settings[:default_chunk_size] = "1000000"
21
+ # Some defaults
22
+ self[:app_trim_progname] = "18"
23
+ self[:app_chunk_size] = "1000000"
24
+ end
@@ -4,6 +4,10 @@ module RestFtpDaemon
4
4
 
5
5
  class DummyException < RestFtpDaemonException; end
6
6
 
7
+ class MissingQueue < RestFtpDaemonException; end
8
+ class MissingPool < RestFtpDaemonException; end
9
+
10
+
7
11
  class RequestSourceMissing < RestFtpDaemonException; end
8
12
  class RequestSourceNotFound < RestFtpDaemonException; end
9
13
  class RequestTargetMissing < RestFtpDaemonException; end
@@ -15,9 +19,12 @@ module RestFtpDaemon
15
19
  class JobSourceMissing < RestFtpDaemonException; end
16
20
  class JobSourceNotFound < RestFtpDaemonException; end
17
21
  class JobTargetMissing < RestFtpDaemonException; end
22
+ class JobTargetUnsupported < RestFtpDaemonException; end
18
23
  class JobTargetUnparseable < RestFtpDaemonException; end
19
24
  #class JobTargetPermission < RestFtpDaemonException; end
20
25
  class JobTargetFileExists < RestFtpDaemonException; end
26
+ class JobPrerequisitesNotMet < RestFtpDaemonException; end
27
+
21
28
 
22
29
  class NotificationMissingUrl < RestFtpDaemonException; end
23
30
  class NotificationMissingSignal < RestFtpDaemonException; end
@@ -1,5 +1,8 @@
1
- require 'net/ftp'
1
+ #require 'net/ftptls'
2
2
 
3
+ require 'uri'
4
+ require 'net/ftp'
5
+ require 'double_bag_ftps'
3
6
 
4
7
  module RestFtpDaemon
5
8
  class Job < RestFtpDaemon::Common
@@ -10,8 +13,6 @@ module RestFtpDaemon
10
13
 
11
14
  # Grab params
12
15
  @params = params
13
- @target = nil
14
- @source = nil
15
16
 
16
17
  # Init context
17
18
  set :id, id
@@ -57,22 +58,17 @@ module RestFtpDaemon
57
58
  set :error, exception.class
58
59
 
59
60
  rescue RestFtpDaemonException => exception
60
- info "Job.process failed [RestFtpDaemonException::#{exception.class}]"
61
+ info "Job.process failed [::#{exception.class}]"
61
62
  set :status, :failed
62
63
  set :error, exception.class
63
64
 
64
- # rescue Exception => exception
65
- # set :status, :crashed
66
- # set :error, exception.class
65
+ rescue Exception => exception
66
+ info "Job.process exception [#{exception.class}] #{exception.backtrace.inspect}"
67
+ set :status, :crashed
68
+ set :error, exception.class
67
69
 
68
70
  else
69
71
  info "Job.process finished"
70
- # set :error, 0
71
- #set :status, :wandering
72
-
73
- # Wait for a few seconds before marking the job as finished
74
- # info "#{prefix} wander for #{RestFtpDaemon::THREAD_SLEEP_BEFORE_DIE} sec"
75
- # wander RestFtpDaemon::THREAD_SLEEP_BEFORE_DIE
76
72
  set :status, :finished
77
73
  end
78
74
 
@@ -80,8 +76,8 @@ module RestFtpDaemon
80
76
 
81
77
  def describe
82
78
  # Update realtime info
83
- w = wandering_time
84
- set :wandering, w.round(2) unless w.nil?
79
+ #w = wandering_time
80
+ #set :wandering, w.round(2) unless w.nil?
85
81
 
86
82
  # Update realtime info
87
83
  u = up_time
@@ -131,15 +127,15 @@ module RestFtpDaemon
131
127
  @params[attribute.to_s] = value
132
128
  end
133
129
 
134
- def expand_path_from path
135
- File.expand_path replace_token_in_path(path)
130
+ def expand_path path
131
+ File.expand_path replace_token(path)
136
132
  end
137
133
 
138
- def expand_url_from path
139
- URI replace_token_in_path(path) rescue nil
134
+ def expand_url path
135
+ URI::parse replace_token(path) rescue nil
140
136
  end
141
137
 
142
- def replace_token_in_path path
138
+ def replace_token path
143
139
  # Ensure endpoints are not a nil value
144
140
  return path unless Settings.endpoints.is_a? Enumerable
145
141
  newpath = path.clone
@@ -157,21 +153,36 @@ module RestFtpDaemon
157
153
 
158
154
  def prepare
159
155
  # Init
156
+ info "Job.prepare"
160
157
  set :status, :preparing
158
+ @source_method = :file
159
+ @target_method = nil
160
+ @source_path = nil
161
+ @target_url = nil
161
162
 
162
163
  # Check source
163
164
  raise JobSourceMissing unless @params["source"]
164
- @source = expand_path_from @params["source"]
165
- set :debug_source, @source
165
+ #@source = expand_path @params["source"]
166
+ @source_path = expand_path @params["source"]
167
+ set :source_path, @source_path
168
+ set :source_method, :file
166
169
 
167
170
  # Check target
168
171
  raise JobTargetMissing unless @params["target"]
169
- @target = expand_url_from @params["target"]
170
- set :debug_target, @target.inspect
172
+ @target_url = expand_url @params["target"]
173
+ set :target_url, @target_url.inspect
174
+
175
+ if @target_url.kind_of? URI::FTP
176
+ @target_method = :ftp
177
+ elsif @target_url.kind_of? URI::FTPS
178
+ @target_method = :ftps
179
+ end
180
+ set :target_method, @target_method
171
181
 
172
182
  # Check compliance
173
- raise JobTargetUnparseable if @target.nil?
174
- raise JobSourceNotFound unless File.exists? @source
183
+ raise JobTargetUnparseable if @target_url.nil?
184
+ raise JobTargetUnsupported if @target_method.nil?
185
+ raise JobSourceNotFound unless File.exists? @source_path
175
186
 
176
187
  end
177
188
 
@@ -187,44 +198,90 @@ module RestFtpDaemon
187
198
  end
188
199
 
189
200
  def transfer
201
+ # Init
202
+ info "Job.transfer"
203
+
190
204
  # Send first notification
191
205
  transferred = 0
192
206
  notify "rftpd.started"
193
207
 
194
208
  # Ensure @source and @target are there
209
+ info "Job.transfer checking_source"
195
210
  set :status, :checking_source
196
- raise JobPrerequisitesNotMet unless @source
197
- raise JobPrerequisitesNotMet unless @source
198
- target_path = File.dirname @target.path
199
- target_name = File.basename @target.path
211
+ raise RestFtpDaemon::JobPrerequisitesNotMet unless @source_path
212
+ raise RestFtpDaemon::JobPrerequisitesNotMet unless @target_url
213
+ target_path = File.dirname @target_url.path
214
+ target_name = File.basename @target_url.path
200
215
 
201
216
  # Read source file size
202
- source_size = File.size @source
217
+ source_size = File.size @source_path
203
218
  set :file_size, source_size
204
219
 
205
220
  # Prepare FTP transfer
206
- set :status, :checking_target
207
- ftp = Net::FTP.new(@target.host)
221
+ info "Job.transfer preparing"
222
+
223
+ # Scheme-aware config
224
+ case @target_method
225
+ when :ftp
226
+ info "Job.transfer scheme FTP"
227
+ ftp = Net::FTP.new
228
+ when :ftps
229
+ info "Job.transfer scheme FTPS"
230
+ ftp = DoubleBagFTPS.new
231
+ ftp.ssl_context = DoubleBagFTPS.create_ssl_context(:verify_mode => OpenSSL::SSL::VERIFY_NONE)
232
+ ftp.ftps_mode = DoubleBagFTPS::EXPLICIT
233
+ else
234
+ info "Job.transfer scheme other: [#{@target_url.scheme}]"
235
+ end
236
+
237
+ # Connect remote server
238
+ info "Job.transfer connecting"
239
+ set :status, :connecting
240
+ ftp.connect(@target_url.host)
208
241
  ftp.passive = true
209
- ftp.login @target.user, @target.password
242
+
243
+ # Logging in
244
+ info "Job.transfer login"
245
+ begin
246
+ u = ftp.login @target_url.user, @target_url.password
247
+ rescue Exception => exception
248
+ info "Job.process login failed [#{exception.class}] #{u.inspect}"
249
+ set :status, :login_failed
250
+ set :error, exception.class
251
+ end
252
+
253
+ # Changing to directory
254
+ info "Job.transfer chdir"
255
+ set :status, :chdir
210
256
  ftp.chdir(target_path)
211
257
 
212
258
  # Check for target file presence
213
259
  if get(:overwrite).nil?
214
- results = ftp.list(target_name)
215
- #results = ftp.list()
216
-
217
- unless results.count.zero?
260
+ info "Job.transfer remote_check (#{target_name})"
261
+ set :status, :remote_check
262
+
263
+ # Get file list, sometimes the response can be an empty value
264
+ results = ftp.list(target_name) rescue nil
265
+
266
+ # Result can be nil or a list of files
267
+ if results.nil? || results.count.zero?
268
+ info "Job.transfer remote_absent"
269
+ set :status, :remote_absent
270
+ else
271
+ info "Job.transfer remote_present"
272
+ set :status, :remote_present
218
273
  ftp.close
219
274
  notify "rftpd.ended", RestFtpDaemon::JobTargetFileExists
220
275
  raise RestFtpDaemon::JobTargetFileExists
221
276
  end
277
+
222
278
  end
223
279
 
224
280
  # Do transfer
281
+ info "Job.transfer uploading"
225
282
  set :status, :uploading
226
- chunk_size = Settings.transfer.chunk_size || Settings[:default_chunk_size]
227
- ftp.putbinaryfile(@source, target_name, chunk_size) do |block|
283
+ chunk_size = Settings.transfer.chunk_size || Settings[:app_chunk_size]
284
+ ftp.putbinaryfile(@source_path, target_name, chunk_size) do |block|
228
285
  # Update counters
229
286
  transferred += block.bytesize
230
287
 
@@ -235,6 +292,8 @@ module RestFtpDaemon
235
292
  end
236
293
 
237
294
  # Close FTP connexion
295
+ info "Job.transfer closing"
296
+ set :status, :disconnecting
238
297
  notify "rftpd.ended"
239
298
  set :progress, nil
240
299
  ftp.close
@@ -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 = "%-#{Settings[:default_trim_progname]}s" % progname
4
+ progname = "%-#{Settings[:app_trim_progname]}s" % progname
5
5
  "#{stamp} #{severity} #{progname} #{msg}\n"
6
6
  end
7
7
  end
@@ -0,0 +1,6 @@
1
+ module URI
2
+ class FTPS < Generic
3
+ DEFAULT_PORT = 990
4
+ end
5
+ @@schemes['FTPS'] = FTPS
6
+ end
@@ -30,9 +30,9 @@
30
30
 
31
31
  .navbar-header
32
32
  %h1
33
- = Settings.name
33
+ = Settings.app_name
34
34
  %small
35
- = "v#{Settings.version}"
35
+ = "v#{Settings.app_ver}"
36
36
  = "[#{Settings.namespace}]"
37
37
 
38
38
 
@@ -70,17 +70,7 @@
70
70
 
71
71
  = render :dashboard_jobs, {jobs: @jobs_all}
72
72
 
73
- =# render :dashboard_jobs, {kind: "queued", jobs: @jobs_queued}
74
-
75
-
76
- %h2 Endpoint tokens
77
- - endpoints = Settings.endpoints || {}
78
- - endpoints.each do |token, value|
79
- .btn-group.btn-group-sm
80
- .btn.btn-default.btn-danger= token
81
- .btn.btn-default= value
82
-
83
-
73
+ = render :dashboard_tokens, {tokens: Settings.endpoints || {}}
84
74
 
85
75
  %br
86
76
  %br
@@ -35,6 +35,7 @@
35
35
  %th= "priority"
36
36
  %th= "source"
37
37
  %th= "target"
38
+ %th= "mode"
38
39
  %th= "size"
39
40
  %th= "status"
40
41
  %th= "error"
@@ -60,8 +61,13 @@
60
61
 
61
62
  %td= job.id
62
63
  %td= job.get :priority
63
- %td= job.get :source
64
- %td= job.get :target
64
+ %td{title: job.get(:source_path)}
65
+ = job.get :source
66
+ %td{title: job.get(:target_url)}
67
+ = job.get :target
68
+ %td
69
+ .label.label-info= job.get :source_method
70
+ .label.label-primary= job.get :target_method
65
71
  %td= format_nice_bytes(size)
66
72
  %td= status
67
73
  %td= error
@@ -0,0 +1,9 @@
1
+ %h2 Endpoint tokens
2
+
3
+ %table.table.table-striped.table-hover.table-condensed
4
+ - tokens.each do |token, value|
5
+ %tr
6
+ %td
7
+ %b= token
8
+ %td
9
+ = value
@@ -12,9 +12,11 @@ module RestFtpDaemon
12
12
  super()
13
13
 
14
14
  # Create worker threads
15
+ info "WorkerPool initializing with #{number_threads} workers"
15
16
  number_threads.times do
16
17
  Thread.new { run }
17
18
  end
19
+
18
20
  end
19
21
 
20
22
  def wait
@@ -35,7 +37,7 @@ module RestFtpDaemon
35
37
 
36
38
  begin
37
39
  loop do
38
- info "worker [#{@wid}] waiting for a job... "
40
+ info "worker [#{@wid}] ready, waiting for a job"
39
41
 
40
42
  # Wait for a job to come into the queue
41
43
  job = $queue.pop
@@ -1,12 +1,13 @@
1
1
  # coding: utf-8
2
2
 
3
3
  # Libs
4
- lib = File.expand_path('../lib', __FILE__)
5
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ # APP_ROOT = File.expand_path(File.dirname(__FILE__) + '/../')
5
+ APP_LIBS = File.expand_path('../lib', __FILE__)
6
+ $LOAD_PATH.unshift(APP_LIBS) unless $LOAD_PATH.include?(APP_LIBS)
6
7
  require 'rest-ftp-daemon/config'
7
8
 
8
9
  Gem::Specification.new do |spec|
9
- spec.name = Settings[:name]
10
+ spec.name = Settings[:app_name]
10
11
  spec.date = Time.now.strftime("%Y-%m-%d")
11
12
  spec.authors = ["Bruno MEDICI"]
12
13
  spec.email = "rest-ftp-daemon@bmconseil.com"
@@ -18,7 +19,7 @@ Gem::Specification.new do |spec|
18
19
  spec.files = `git ls-files -z`.split("\x0")
19
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
21
  spec.require_paths = ["lib"]
21
- spec.version = Settings[:version]
22
+ spec.version = Settings[:app_ver]
22
23
 
23
24
  spec.required_ruby_version = '>= 1.9.3'
24
25
 
@@ -30,6 +31,7 @@ Gem::Specification.new do |spec|
30
31
  spec.add_runtime_dependency "settingslogic"
31
32
  spec.add_runtime_dependency "haml"
32
33
  spec.add_runtime_dependency "json"
34
+ spec.add_runtime_dependency "double-bag-ftps"
33
35
  spec.add_runtime_dependency "facter"
34
36
  spec.add_runtime_dependency "sys-cpu"
35
37
 
@@ -1,6 +1,6 @@
1
1
  defaults: &defaults
2
2
  daemonize: 1
3
- port: 3000
3
+ port: 3200
4
4
  workers: 1
5
5
 
6
6
  transfer:
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.60'
4
+ version: '0.70'
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-23 00:00:00.000000000 Z
11
+ date: 2014-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - ! '>='
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: double-bag-ftps
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: facter
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -165,9 +179,10 @@ files:
165
179
  - lib/rest-ftp-daemon/logger.rb
166
180
  - lib/rest-ftp-daemon/notification.rb
167
181
  - lib/rest-ftp-daemon/static/css/bootstrap.min.css
182
+ - lib/rest-ftp-daemon/uri.rb
168
183
  - lib/rest-ftp-daemon/views/dashboard.haml
169
184
  - lib/rest-ftp-daemon/views/dashboard_jobs.haml
170
- - lib/rest-ftp-daemon/views/index.haml
185
+ - lib/rest-ftp-daemon/views/dashboard_tokens.haml
171
186
  - lib/rest-ftp-daemon/worker_pool.rb
172
187
  - rest-ftp-daemon.gemspec
173
188
  - rest-ftp-daemon.yml.sample
@@ -1,116 +0,0 @@
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
37
- .btn.btn-default= @info_load.round(1)
38
-
39
- .btn-group.btn-group-sm
40
- .btn.btn-default.btn-warning CPU
41
- .btn.btn-default= "#{@info_norm} %"
42
-
43
- .btn-group.btn-group-sm
44
- .btn.btn-default.btn-warning Free
45
- .btn.btn-default= @info_memfree
46
-
47
- .btn-group.btn-group-sm
48
- .btn.btn-default.btn-success IP
49
- .btn.btn-default= @info_ipaddr
50
-
51
- .btn-group.btn-group-sm
52
- .btn.btn-default.btn-success Workers
53
- .btn.btn-default= Settings.workers
54
-
55
- .btn-group.btn-group-sm
56
- .btn.btn-default.btn-info Transferred
57
- .btn.btn-default= format_nice_bytes(@info_total_transferred)
58
-
59
-
60
- %h2
61
- Jobs on this system (#{@jobs_all})
62
- %small
63
- queued (#{@jobs_queued}) / popped (#{@jobs_popped})
64
-
65
- %table.table.table-striped.table-hover.table-condensed
66
- %tr
67
- %th= "ID"
68
- %th= "priority"
69
- %th= "source"
70
- %th= "target"
71
- %th= "size"
72
- %th= "status"
73
- %th= "error"
74
- %th= "progress"
75
- %th
76
-
77
- - @jobs.each do |job|
78
- - error = job.get :error
79
- - status = job.get :status
80
- - size = job.get :file_size
81
- - progress = job.get :progress
82
-
83
- - if error!=0 && !error.nil?
84
- - trclass = "danger"
85
- - elsif status == :uploading
86
- - trclass = "info"
87
- - elsif status == :finished
88
- - trclass = "success"
89
- - else
90
- - trclass = "warning"
91
-
92
- %tr{class: trclass}
93
-
94
- %td= job.id
95
- %td= job.get :priority
96
- %td= job.get :source
97
- %td= job.get :target
98
- %td= format_nice_bytes(size)
99
- %td= status
100
- %td= error
101
- %td
102
- - unless progress.nil?
103
- .progress
104
- .progress-bar{style:"width: #{progress}%;"}
105
- = format_nice_bytes job.get(:file_sent)
106
- %td
107
- - unless progress.nil?
108
- = "#{progress} %"
109
-
110
-
111
- %h2 Endpoint tokens
112
- - endpoints = Settings.endpoints || {}
113
- - endpoints.each do |token, value|
114
- .btn-group.btn-group-sm
115
- .btn.btn-default.btn-danger= token
116
- .btn.btn-default= value