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.
- checksums.yaml +4 -4
- data/Gemfile.lock +12 -12
- data/README.md +12 -3
- data/bin/rest-ftp-daemon +14 -15
- data/config.ru +2 -8
- data/defaults.yml +83 -59
- data/lib/rest-ftp-daemon.rb +4 -8
- data/lib/rest-ftp-daemon/api/config.rb +3 -1
- data/lib/rest-ftp-daemon/api/dashboard.rb +1 -0
- data/lib/rest-ftp-daemon/api/debug.rb +2 -0
- data/lib/rest-ftp-daemon/api/job_presenter.rb +2 -0
- data/lib/rest-ftp-daemon/api/jobs.rb +2 -0
- data/lib/rest-ftp-daemon/api/root.rb +2 -0
- data/lib/rest-ftp-daemon/api/status.rb +1 -0
- data/lib/rest-ftp-daemon/constants.rb +5 -5
- data/lib/rest-ftp-daemon/job.rb +37 -37
- data/lib/rest-ftp-daemon/logger_pool.rb +39 -16
- data/lib/rest-ftp-daemon/notification.rb +26 -19
- data/lib/rest-ftp-daemon/remote.rb +16 -15
- data/lib/rest-ftp-daemon/remote_ftp.rb +2 -7
- data/lib/rest-ftp-daemon/remote_sftp.rb +1 -4
- data/lib/rest-ftp-daemon/worker_pool.rb +5 -15
- data/lib/rest-ftp-daemon/{worker_conchita.rb → workers/conchita.rb} +20 -22
- data/lib/rest-ftp-daemon/{worker_reporter.rb → workers/reporter.rb} +23 -30
- data/lib/rest-ftp-daemon/{worker_job.rb → workers/transfer.rb} +25 -35
- data/lib/shared/conf.rb +47 -41
- data/lib/{rest-ftp-daemon/worker.rb → shared/worker_base.rb} +42 -28
- data/rest-ftp-daemon.gemspec +5 -3
- data/spec/rest-ftp-daemon/features/dashboard_spec.rb +5 -5
- data/spec/spec_helper.rb +2 -2
- metadata +36 -24
- data/rest-ftp-daemon.sample.yml +0 -71
- data/spec/support/config.yml +0 -25
@@ -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
|
-
|
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 %-
|
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 +
|
33
|
-
LOG_PREFIX_ID =
|
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
|
data/lib/rest-ftp-daemon/job.rb
CHANGED
@@ -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 :
|
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]
|
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
|
-
|
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
|
277
|
-
vectors =
|
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
|
460
|
+
def flag_prepare name, default
|
464
461
|
# build the flag instance var name
|
465
462
|
variable = "@#{name}"
|
466
463
|
|
467
|
-
|
468
|
-
|
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
|
-
|
471
|
-
|
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 "
|
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 (
|
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
|
19
|
-
|
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(
|
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
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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 =>
|
90
|
-
log_error "Net::OpenTimeout: #{
|
96
|
+
rescue Net::OpenTimeout => e
|
97
|
+
log_error "Net::OpenTimeout: #{e.message}"
|
91
98
|
|
92
|
-
rescue SocketError =>
|
93
|
-
log_error "SocketError: #{
|
99
|
+
rescue SocketError => e
|
100
|
+
log_error "SocketError: #{e.message}"
|
94
101
|
|
95
|
-
rescue StandardError =>
|
96
|
-
log_error "UNHANDLED EXCEPTION: #{
|
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 :
|
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
|
-
|
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
|
-
|
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 =
|
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
|