rest-ftp-daemon 0.60 → 0.70

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