rest-ftp-daemon 0.300.3 → 0.302.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ require "grape"
2
+
1
3
  module RestFtpDaemon
2
4
  module API
3
5
  class Jobs < Grape::API
@@ -1,3 +1,5 @@
1
+ require "grape"
2
+
1
3
  module RestFtpDaemon
2
4
  module API
3
5
  class Root < Grape::API
@@ -1,3 +1,4 @@
1
+ require "grape"
1
2
  require "get_process_mem"
2
3
 
3
4
  module RestFtpDaemon
@@ -6,7 +6,7 @@ DEFAULT_POOL = "default"
6
6
  DEFAULT_SFTP_TIMEOUT = 600 # 10mn
7
7
  DEFAULT_FTP_CHUNK = 1024 # 1 MB
8
8
  DEFAULT_PAGE_SIZE = 50 # 50 lines
9
- DEFAULT_RETRY_DELAY = 10 # 10s
9
+ DEFAULT_RETRY_AFTER = 10 # 10s
10
10
 
11
11
 
12
12
  # Internal job constants
@@ -21,7 +21,7 @@ LOG_ROTATION = "daily"
21
21
  LOG_FORMAT_PROGNAME = "%d\t%s"
22
22
 
23
23
  LOG_HEADER_TIME = "%Y-%m-%d %H:%M:%S"
24
- LOG_HEADER_FORMAT = "%s \t%d\t%-8s %-15s "
24
+ LOG_HEADER_FORMAT = "%s \t%d\t%-8s %-10s "
25
25
  LOG_MESSAGE_TRIM = 200
26
26
  LOG_MESSAGE_TEXT = "%s%s"
27
27
  LOG_MESSAGE_ARRAY = "%s - %s"
@@ -29,9 +29,9 @@ LOG_MESSAGE_HASH = "%s * %-20s %s"
29
29
 
30
30
  # Constants: logger app-specific prefix
31
31
  LOG_PREFIX_WID = 8
32
- LOG_PREFIX_JID = JOB_IDENT_LEN + 3 + 2
33
- LOG_PREFIX_ID = 6
34
- LOG_PREFIX_FORMAT = "%#{-LOG_PREFIX_WID.to_i}s %#{-LOG_PREFIX_JID.to_i}s %#{-LOG_PREFIX_ID.to_i}s"
32
+ LOG_PREFIX_JID = JOB_IDENT_LEN + 4
33
+ LOG_PREFIX_ID = 5
34
+ LOG_PREFIX_FORMAT = "%#{-LOG_PREFIX_WID.to_i}s %#{-LOG_PREFIX_JID.to_i}s %#{-LOG_PREFIX_ID.to_i}s "
35
35
 
36
36
 
37
37
  # Constants: logger to be cleaned up
@@ -24,6 +24,8 @@ module RestFtpDaemon
24
24
  attr_reader :infos
25
25
  attr_reader :pool
26
26
 
27
+ attr_accessor :config
28
+
27
29
  FIELDS.each do |name|
28
30
  attr_reader name
29
31
  end
@@ -43,8 +45,13 @@ module RestFtpDaemon
43
45
  @runs = 0
44
46
  @wid = nil
45
47
 
48
+ # Prepare configuration
49
+ @config = Conf[:transfer] || {}
50
+ @endpoints = Conf[:endpoints] || {}
51
+ @pools = Conf[:pools] || {}
52
+
46
53
  # Logger
47
- @logger = RestFtpDaemon::LoggerPool.instance.get :jobs
54
+ @logger = RestFtpDaemon::LoggerPool.instance.get :transfer
48
55
 
49
56
  # Protect with a mutex
50
57
  @mutex = Mutex.new
@@ -54,34 +61,24 @@ module RestFtpDaemon
54
61
  instance_variable_set "@#{name}", params[name]
55
62
  end
56
63
 
57
- # Set pool
58
- pools = (Conf[:pools] || {})
59
64
  # Check if pool name exists
60
- if (pools.keys.include? params[:pool])
65
+ if (@pools.keys.include? params[:pool])
61
66
  @pool = params[:pool].to_s
62
67
  else
63
68
  @pool = DEFAULT_POOL
64
69
  end
65
70
 
66
71
  # Set job queue, thus reset
67
- reset
68
-
69
- # Read source file size and parameters
70
- @notify_after_sec = Conf.at(:transfer, :notify_after_sec) rescue nil
72
+ # reset
71
73
  end
72
74
 
73
75
  def reset
74
- # Set super-default flags
75
- flag_default :mkdir, false
76
- flag_default :overwrite, false
77
- flag_default :tempfile, false
78
-
79
76
  # Flag current job
80
77
  @queued_at = Time.now
81
78
  @updated_at = Time.now
82
79
 
83
80
  # Send first notification
84
- log_info "Job.initialize notify[queued] notify_after_sec[#{@notify_after_sec}] interval[#{JOB_UPDATE_INTERVAL}]"
81
+ log_info "Job.initialize notify[queued]}]"
85
82
  client_notify :queued
86
83
 
87
84
  # Update job status
@@ -91,8 +88,7 @@ module RestFtpDaemon
91
88
  end
92
89
 
93
90
  def process
94
- # Update job's status
95
- log_info "Job.process"
91
+ log_info "Job.process update_interval[#{JOB_UPDATE_INTERVAL}]"
96
92
 
97
93
  # Prepare job
98
94
  begin
@@ -259,10 +255,6 @@ module RestFtpDaemon
259
255
 
260
256
  protected
261
257
 
262
- def expand_path path
263
- File.expand_path replace_tokens(path)
264
- end
265
-
266
258
  def expand_url path
267
259
  URI.parse replace_tokens(path)
268
260
  end
@@ -273,8 +265,8 @@ module RestFtpDaemon
273
265
 
274
266
  def replace_tokens path
275
267
  # Ensure endpoints are not a nil value
276
- return path unless Conf[:endpoints].is_a? Enumerable
277
- vectors = Conf[:endpoints].clone
268
+ return path unless @endpoints.is_a? Enumerable
269
+ vectors = @endpoints.clone
278
270
 
279
271
  # Stack RANDOM into tokens
280
272
  vectors["RANDOM"] = SecureRandom.hex(JOB_RANDOM_LEN)
@@ -294,16 +286,21 @@ module RestFtpDaemon
294
286
  end
295
287
 
296
288
  def prepare
289
+ # Init
290
+ @source_path = nil
291
+
292
+ # Prepare flags
293
+ flag_prepare :mkdir, false
294
+ flag_prepare :overwrite, false
295
+ flag_prepare :tempfile, true
296
+
297
297
  # Update job status
298
298
  set_status JOB_STATUS_PREPARING
299
299
  @runs += 1
300
300
 
301
- # Init
302
- @source_path = nil
303
-
304
301
  # Prepare source
305
302
  raise RestFtpDaemon::JobMissingAttribute unless @source
306
- @source_path = expand_path @source
303
+ @source_path = File.expand_path replace_tokens(@source)
307
304
  set_info :source, :path, @source_path
308
305
  set_info :source, :method, JOB_METHOD_FILE
309
306
 
@@ -324,21 +321,21 @@ module RestFtpDaemon
324
321
  # set_info :target, :method, :ftp
325
322
  set_info :target, :method, JOB_METHOD_FTP
326
323
  #@target_method = :ftp
327
- @remote = RemoteFTP.new target_uri, log_prefix
324
+ @remote = RemoteFTP.new target_uri, log_prefix, debug: @config[:debug_ftp]
328
325
 
329
326
  elsif (target_uri.is_a? URI::FTPES) || (target_uri.is_a? URI::FTPS)
330
327
  log_info "Job.prepare target_method FTPES"
331
328
  # set_info :target, :method, :ftpes
332
329
  set_info :target, :method, JOB_METHOD_FTPS
333
330
  # @target_method = :ftpes
334
- @remote = RemoteFTP.new target_uri, log_prefix, ftpes: true
331
+ @remote = RemoteFTP.new target_uri, log_prefix, debug: @config[:debug_sftp], ftpes: true
335
332
 
336
333
  elsif target_uri.is_a? URI::SFTP
337
334
  log_info "Job.prepare target_method SFTP"
338
335
  # set_info :target, :method, :sftp
339
336
  set_info :target, :method, JOB_METHOD_SFTP
340
337
  # @target_method = :sftp
341
- @remote = RemoteSFTP.new target_uri, log_prefix
338
+ @remote = RemoteSFTP.new target_uri, log_prefix, debug: @config[:debug_sftp]
342
339
 
343
340
  else
344
341
  log_info "Job.prepare unknown scheme [#{target_uri.scheme}]"
@@ -460,15 +457,17 @@ module RestFtpDaemon
460
457
  touch_job
461
458
  end
462
459
 
463
- def flag_default name, default
460
+ def flag_prepare name, default
464
461
  # build the flag instance var name
465
462
  variable = "@#{name}"
466
463
 
467
- # If it's true or false, that's ok
468
- return if [true, false].include? instance_variable_get(variable)
464
+ [config[name], default].each do |alt_value|
465
+ # If it's already true or false, that's ok
466
+ return if [true, false].include? instance_variable_get(variable)
469
467
 
470
- # Otherwise, set it to the default value
471
- instance_variable_set variable, default
468
+ # Otherwise, set it to the new alt_value
469
+ instance_variable_set variable, alt_value
470
+ end
472
471
  end
473
472
 
474
473
  def finalize
@@ -539,6 +538,7 @@ module RestFtpDaemon
539
538
  def progress transferred, name = ""
540
539
  # What's current time ?
541
540
  now = Time.now
541
+ notify_after = @config[:notify_after]
542
542
 
543
543
  # Update counters
544
544
  @transfer_sent += transferred
@@ -561,7 +561,7 @@ module RestFtpDaemon
561
561
  stack << (Helpers.format_bytes @transfer_total, "B")
562
562
  stack << (Helpers.format_bytes @current_bitrate.round(0), "bps")
563
563
  stack2 = stack.map { |txt| ("%#{LOG_PIPE_LEN.to_i}s" % txt) }.join("\t")
564
- log_debug "Job.progress #{stack2} \t#{name}"
564
+ log_debug "progress #{stack2} \t#{name}"
565
565
 
566
566
  # Remember when we last did it
567
567
  @progress_at = now
@@ -569,13 +569,13 @@ module RestFtpDaemon
569
569
 
570
570
  # Notify if requested
571
571
  notified_ago = (now.to_f - @notified_at.to_f)
572
- if (!@notify_after_sec.nil?) && (notified_ago > @notify_after_sec)
572
+ if (!notify_after.nil?) && (notified_ago > notify_after)
573
573
  # Prepare and send notification
574
574
  notif_status = {
575
575
  progress: percent0,
576
576
  transfer_sent: @transfer_sent,
577
577
  transfer_total: @transfer_total,
578
- transfer_bitrate: @current_bitrate,
578
+ transfer_bitrate: @current_bitrate.round(0),
579
579
  }
580
580
  client_notify :progress, status: notif_status
581
581
 
@@ -1,37 +1,60 @@
1
1
  require "logger"
2
+ require "singleton"
2
3
 
4
+ # Logger interface class to access logger though symbolic names
3
5
  module RestFtpDaemon
4
-
5
- # Logger interface class to access logger though symbolic names
6
6
  class LoggerPool
7
7
  include Singleton
8
8
 
9
- def initialize
10
- @loggers = {}
11
- end
12
-
13
9
  def get pipe
10
+ @loggers ||= {}
14
11
  @loggers[pipe] ||= create(pipe)
15
12
  end
16
13
 
17
14
  def create pipe
18
- # Compute file path / STDERR
19
- logfile = Conf[:logs][pipe] if Conf[:logs].is_a? Hash
20
- logfile ||= STDERR
21
-
22
- # Check if we can write to than file
23
- unless File.writable?(logfile)
24
- puts "LoggerPool [#{pipe}] logging to hyperspace (cannot write to #{logfile})"
25
- logfile = nil
26
- end
15
+ # Compute logfile or STDERR, and declare what we're doing
16
+ filename = logfile(pipe)
27
17
 
28
18
  # Create the logger and return it
29
- logger = Logger.new(logfile, LOG_ROTATION) #, 10, 1024000)
19
+ logger = Logger.new(filename, LOG_ROTATION) #, 10, 1024000)
30
20
  logger.progname = pipe.to_s.downcase
31
21
  logger.formatter = Shared::LoggerFormatter
32
22
 
33
23
  # Finally return this logger
34
24
  logger
25
+
26
+ rescue Errno::EACCES
27
+ puts "LoggerPool [#{pipe}] failed: access error"
28
+ end
29
+
30
+ protected
31
+
32
+ def logfile pipe
33
+ # Disabled if no valid config
34
+ return nil unless Conf[:logs].is_a?(Hash)
35
+
36
+ # Compute logfile and check if we can write there
37
+ logfile = File.expand_path(Conf[:logs][pipe], Conf[:logs][:path])
38
+
39
+ # Check that we'll be able to create logfiles
40
+ if File.exists?(logfile)
41
+ # File is there, is it writable ?
42
+ unless File.writable?(logfile)
43
+ puts "LoggerPool [#{pipe}] disabled: file not writable [#{logfile}]"
44
+ return nil
45
+ end
46
+ else
47
+ # No file here, can we create it ?
48
+ logdir = File.dirname(logfile)
49
+ unless File.writable?(logdir)
50
+ puts "LoggerPool [#{pipe}] disabled: directory not writable [#{logdir}]"
51
+ return nil
52
+ end
53
+ end
54
+
55
+ # OK, return a clean file path
56
+ puts "LoggerPool [#{pipe}] logging to [#{logfile}]"
57
+ return logfile
35
58
  end
36
59
 
37
60
  end
@@ -1,3 +1,6 @@
1
+ require 'api_auth'
2
+ require 'rest_client'
3
+
1
4
  module RestFtpDaemon
2
5
 
3
6
  # Handles a notification POST using a dedicated thread
@@ -61,20 +64,24 @@ module RestFtpDaemon
61
64
 
62
65
  def send flags
63
66
  # Prepare query
64
- headers = {
65
- "Content-Type" => "application/json",
66
- "Accept" => "application/json",
67
- "User-Agent" => "#{Conf.app_name}/v#{Conf.app_ver}"
68
- }
69
- data = flags.to_json
70
-
71
- # Send notification through HTTP
72
67
  uri = URI @url
73
- http = Net::HTTP.new uri.host, uri.port
74
-
75
- # Post notification, handle server response / multi-lines
76
- log_info "sending #{data}"
77
- response = http.post uri.path, data, headers
68
+ # uri = URI(rule[:relay])
69
+ #http = Net::HTTP.new uri.host, uri.port
70
+
71
+ # Prepare request
72
+ request = RestClient::Request.new url: uri.to_s,
73
+ method: :post,
74
+ payload: JSON.pretty_generate(flags),
75
+ headers: {
76
+ content_type: :json,
77
+ accept: :json,
78
+ user_agent: Conf.generate(:user_agent),
79
+ }
80
+
81
+ # Execure request
82
+ log_info "posting #{flags.to_json}"
83
+ # response = http.post uri.path, data, headers
84
+ response = request.execute
78
85
 
79
86
  # Log reponse body
80
87
  response_lines = response.body.lines
@@ -86,14 +93,14 @@ module RestFtpDaemon
86
93
  end
87
94
 
88
95
  # Handle exceptions
89
- rescue Net::OpenTimeout => ex
90
- log_error "Net::OpenTimeout: #{ex.inspect}"
96
+ rescue Net::OpenTimeout => e
97
+ log_error "Net::OpenTimeout: #{e.message}"
91
98
 
92
- rescue SocketError => ex
93
- log_error "SocketError: #{ex.inspect}"
99
+ rescue SocketError => e
100
+ log_error "SocketError: #{e.message}"
94
101
 
95
- rescue StandardError => ex
96
- log_error "UNHANDLED EXCEPTION: #{ex.inspect}"
102
+ rescue StandardError => e
103
+ log_error "UNHANDLED EXCEPTION: #{e.message}", e.backtrace
97
104
  end
98
105
 
99
106
  def log_prefix
@@ -7,9 +7,12 @@ module RestFtpDaemon
7
7
  attr_reader :log_prefix
8
8
 
9
9
  def initialize url, log_prefix, options = {}
10
+ # Options
11
+ @debug = !!options[:debug]
12
+
10
13
  # Logger
11
14
  @log_prefix = log_prefix || {}
12
- @logger = RestFtpDaemon::LoggerPool.instance.get :jobs
15
+ @logger = RestFtpDaemon::LoggerPool.instance.get :transfer
13
16
 
14
17
  # Extract URL parts
15
18
  @url = url
@@ -21,12 +24,22 @@ module RestFtpDaemon
21
24
 
22
25
  def connect
23
26
  # Debug mode ?
24
- debug_header if @debug
27
+ return unless @debug
28
+ puts
29
+ puts "-------------------- SESSION STARTING -------------------------"
30
+ puts "class\t #{myname}"
31
+ puts "host\t #{@url.host}"
32
+ puts "user\t #{@url.user}"
33
+ puts "port\t #{@url.port}"
34
+ puts "options\t #{@options.inspect}"
35
+ puts "---------------------------------------------------------------"
36
+
25
37
  end
26
38
 
27
39
  def close
28
40
  # Debug mode ?
29
- puts "-------------------- SESSION CLOSING --------------------------" if @debug
41
+ return unless @debug
42
+ puts "-------------------- SESSION CLOSING --------------------------"
30
43
  end
31
44
 
32
45
  private
@@ -35,17 +48,5 @@ module RestFtpDaemon
35
48
  self.class.to_s
36
49
  end
37
50
 
38
- def debug_header
39
- # Output header to STDOUT
40
- puts
41
- puts "-------------------- SESSION STARTING -------------------------"
42
- puts "class\t #{myname}"
43
- puts "host\t #{@url.host}"
44
- puts "user\t #{@url.user}"
45
- puts "port\t #{@url.port}"
46
- puts "options\t #{@options.inspect}"
47
- puts "---------------------------------------------------------------"
48
- end
49
-
50
51
  end
51
52
  end
@@ -10,9 +10,6 @@ module RestFtpDaemon
10
10
  # Call super
11
11
  super
12
12
 
13
- # Use debug ?
14
- @debug = (Conf.at :debug, :ftp) == true
15
-
16
13
  # Create FTP object
17
14
  if options[:ftpes]
18
15
  prepare_ftpes
@@ -20,7 +17,7 @@ module RestFtpDaemon
20
17
  prepare_ftp
21
18
  end
22
19
  @ftp.passive = true
23
- @ftp.debug_mode = !!@debug
20
+ @ftp.debug_mode = @debug
24
21
 
25
22
  # Config
26
23
  @chunk_size = DEFAULT_FTP_CHUNK.to_i * 1024
@@ -30,10 +27,8 @@ module RestFtpDaemon
30
27
  end
31
28
 
32
29
  def connect
33
- # Connect init
34
- super
35
-
36
30
  # Connect remote server
31
+ super
37
32
  @ftp.connect @url.host, @url.port
38
33
  @ftp.login @url.user, @url.password
39
34
  end