rest-ftp-daemon 0.400.0 → 0.410.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +26 -6
- data/Rakefile +5 -3
- data/bin/rest-ftp-daemon +2 -1
- data/config.ru +8 -2
- data/lib/rest-ftp-daemon.rb +10 -2
- data/lib/rest-ftp-daemon/api/debug.rb +17 -5
- data/lib/rest-ftp-daemon/api/{job_presenter.rb → entities/job.rb} +25 -2
- data/lib/rest-ftp-daemon/api/entities/options.rb +15 -0
- data/lib/rest-ftp-daemon/api/jobs.rb +46 -13
- data/lib/rest-ftp-daemon/api/root-real.rb +80 -0
- data/lib/rest-ftp-daemon/api/root-test.rb +69 -0
- data/lib/rest-ftp-daemon/api/root.rb +21 -5
- data/lib/rest-ftp-daemon/constants.rb +18 -1
- data/lib/rest-ftp-daemon/counters.rb +0 -3
- data/lib/rest-ftp-daemon/exceptions.rb +16 -12
- data/lib/rest-ftp-daemon/helpers/api.rb +3 -3
- data/lib/rest-ftp-daemon/helpers/common.rb +12 -5
- data/lib/rest-ftp-daemon/helpers/views.rb +41 -11
- data/lib/rest-ftp-daemon/job.rb +118 -420
- data/lib/rest-ftp-daemon/job_queue.rb +34 -5
- data/lib/rest-ftp-daemon/jobs/dummy.rb +20 -0
- data/lib/rest-ftp-daemon/jobs/transfer.rb +319 -0
- data/lib/rest-ftp-daemon/jobs/video.rb +107 -0
- data/lib/rest-ftp-daemon/location.rb +125 -0
- data/lib/rest-ftp-daemon/path.rb +3 -0
- data/lib/rest-ftp-daemon/remote_ftp.rb +2 -2
- data/lib/rest-ftp-daemon/remote_sftp.rb +2 -2
- data/lib/rest-ftp-daemon/static/config.json +411 -0
- data/lib/rest-ftp-daemon/static/css/bootstrap.min.css +14 -0
- data/lib/rest-ftp-daemon/static/css/{bootstrap.css → bootstrap.old.css} +0 -0
- data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.eot +0 -0
- data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.svg +288 -0
- data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.ttf +0 -0
- data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.woff +0 -0
- data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.woff2 +0 -0
- data/lib/rest-ftp-daemon/static/swagger.html +83 -0
- data/lib/rest-ftp-daemon/static/swagger/css/print.css +1362 -0
- data/lib/rest-ftp-daemon/static/swagger/css/reset.css +125 -0
- data/lib/rest-ftp-daemon/static/swagger/css/screen.css +1489 -0
- data/lib/rest-ftp-daemon/static/swagger/css/style.css +250 -0
- data/lib/rest-ftp-daemon/static/swagger/css/typography.css +14 -0
- data/lib/rest-ftp-daemon/static/swagger/fonts/DroidSans-Bold.ttf +0 -0
- data/lib/rest-ftp-daemon/static/swagger/fonts/DroidSans.ttf +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/collapse.gif +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/expand.gif +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/explorer_icons.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/favicon-16x16.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/favicon-32x32.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/favicon.ico +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/logo_small.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/pet_store_api.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/throbber.gif +0 -0
- data/lib/rest-ftp-daemon/static/swagger/images/wordnik_api.png +0 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/backbone-min.js +15 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/es5-shim.js +2065 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/handlebars-4.0.5.js +4608 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/highlight.9.1.0.pack.js +2 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/highlight.9.1.0.pack_extended.js +34 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/jquery-1.8.0.min.js +2 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/jquery.ba-bbq.min.js +18 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/jquery.slideto.min.js +1 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/jquery.wiggle.min.js +8 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/js-yaml.min.js +3 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/jsoneditor.min.js +11 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/lodash.min.js +102 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/marked.js +1272 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/object-assign-pollyfill.js +23 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/swagger-oauth.js +347 -0
- data/lib/rest-ftp-daemon/static/swagger/lib/swagger-ui.min.js +10 -0
- data/lib/rest-ftp-daemon/uri.rb +9 -1
- data/lib/rest-ftp-daemon/views/dashboard.haml +1 -1
- data/lib/rest-ftp-daemon/views/dashboard_footer.haml +6 -3
- data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +3 -3
- data/lib/rest-ftp-daemon/views/dashboard_table.haml +25 -20
- data/lib/rest-ftp-daemon/workers/transfer.rb +14 -0
- data/rest-ftp-daemon.gemspec +14 -4
- data/spec/rest-ftp-daemon/features/swagger_spec.rb +24 -0
- data/spec/spec_helper.rb +4 -4
- metadata +119 -14
@@ -0,0 +1,69 @@
|
|
1
|
+
require "grape"
|
2
|
+
require 'grape-swagger'
|
3
|
+
|
4
|
+
module RestFtpDaemon
|
5
|
+
module API
|
6
|
+
class Root < Grape::API
|
7
|
+
|
8
|
+
### API Documentation
|
9
|
+
add_swagger_documentation hide_documentation_path: true,
|
10
|
+
api_version: Conf.app_ver,
|
11
|
+
doc_version: Conf.app_ver,
|
12
|
+
mount_path: MOUNT_SWAGGER_JSON,
|
13
|
+
info: {
|
14
|
+
title: Conf.app_name,
|
15
|
+
version: Conf.app_ver,
|
16
|
+
description: "API description for #{Conf.app_name} #{Conf.app_ver}",
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
namespace :hudson do
|
21
|
+
desc 'Document root'
|
22
|
+
get '/' do
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
namespace :hudson do
|
27
|
+
desc 'This gets something.',
|
28
|
+
notes: '_test_'
|
29
|
+
|
30
|
+
get '/simple' do
|
31
|
+
{ bla: 'something' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
namespace :colorado do
|
36
|
+
desc 'This gets something for URL using - separator.',
|
37
|
+
notes: '_test_'
|
38
|
+
|
39
|
+
get '/simple-test' do
|
40
|
+
{ bla: 'something' }
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
desc "List all Jobs", http_codes: [
|
45
|
+
{ code: 200, message: "Here are the jobs you requested" },
|
46
|
+
],
|
47
|
+
is_array: true
|
48
|
+
get "/" do
|
49
|
+
begin
|
50
|
+
# Get jobs to display
|
51
|
+
jobs = RestFtpDaemon::JobQueue.instance.jobs
|
52
|
+
|
53
|
+
rescue StandardError => exception
|
54
|
+
log_error "Exception: #{exception.message}"
|
55
|
+
error!({ error: :api_exception, message: exception.message }, 500)
|
56
|
+
|
57
|
+
else
|
58
|
+
status 200
|
59
|
+
present jobs, with: RestFtpDaemon::API::Entities::Job
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -1,4 +1,7 @@
|
|
1
1
|
require "grape"
|
2
|
+
require 'grape-swagger'
|
3
|
+
# require 'grape-swagger/entity'
|
4
|
+
# require 'grape-swagger/representable'
|
2
5
|
|
3
6
|
module RestFtpDaemon
|
4
7
|
module API
|
@@ -34,11 +37,24 @@ module RestFtpDaemon
|
|
34
37
|
|
35
38
|
|
36
39
|
### MOUNTPOINTS
|
37
|
-
mount RestFtpDaemon::API::Status
|
38
|
-
mount RestFtpDaemon::API::Jobs
|
39
|
-
mount RestFtpDaemon::API::Dashbaord
|
40
|
-
mount RestFtpDaemon::API::Config
|
41
|
-
mount RestFtpDaemon::API::Debug
|
40
|
+
mount RestFtpDaemon::API::Status => MOUNT_STATUS
|
41
|
+
mount RestFtpDaemon::API::Jobs => MOUNT_JOBS
|
42
|
+
mount RestFtpDaemon::API::Dashbaord => MOUNT_BOARD
|
43
|
+
mount RestFtpDaemon::API::Config => MOUNT_CONFIG
|
44
|
+
mount RestFtpDaemon::API::Debug => MOUNT_DEBUG
|
45
|
+
|
46
|
+
|
47
|
+
### API Documentation
|
48
|
+
add_swagger_documentation hide_documentation_path: true,
|
49
|
+
api_version: Conf.app_ver,
|
50
|
+
doc_version: Conf.app_ver,
|
51
|
+
mount_path: MOUNT_SWAGGER_JSON,
|
52
|
+
info: {
|
53
|
+
title: Conf.app_name,
|
54
|
+
version: Conf.app_ver,
|
55
|
+
description: "API description for #{Conf.app_name} #{Conf.app_ver}",
|
56
|
+
}
|
57
|
+
|
42
58
|
|
43
59
|
|
44
60
|
### INITIALIZATION
|
@@ -5,6 +5,8 @@ DEFAULT_FTP_CHUNK = 1024 # 1 MB
|
|
5
5
|
DEFAULT_PAGE_SIZE = 50 # 50 lines
|
6
6
|
DEFAULT_RETRY_AFTER = 10 # 10s
|
7
7
|
|
8
|
+
TARGET_BLANK = "_blank"
|
9
|
+
|
8
10
|
|
9
11
|
# Internal job constants
|
10
12
|
JOB_RANDOM_LEN = 8
|
@@ -38,7 +40,10 @@ LOG_INDENT = "\t"
|
|
38
40
|
|
39
41
|
# Jobs statuses
|
40
42
|
JOB_STATUS_PREPARING = "preparing"
|
41
|
-
|
43
|
+
JOB_STATUS_WORKING = "working"
|
44
|
+
|
45
|
+
JOB_STATUS_TRANSFORMING = "transforming"
|
46
|
+
|
42
47
|
JOB_STATUS_CHECKING_SRC = "checking_source"
|
43
48
|
JOB_STATUS_CONNECTING = "remote_connect"
|
44
49
|
JOB_STATUS_CHDIR = "remote_chdir"
|
@@ -47,12 +52,17 @@ JOB_STATUS_RENAMING = "renaming"
|
|
47
52
|
JOB_STATUS_PREPARED = "prepared"
|
48
53
|
JOB_STATUS_DISCONNECTING= "remote_disconnect"
|
49
54
|
JOB_STATUS_FINISHED = "finished"
|
55
|
+
|
50
56
|
JOB_STATUS_FAILED = "failed"
|
51
57
|
JOB_STATUS_QUEUED = "queued"
|
58
|
+
|
52
59
|
JOB_STYLES = {
|
53
60
|
JOB_STATUS_QUEUED => :active,
|
54
61
|
JOB_STATUS_FAILED => :warning,
|
55
62
|
JOB_STATUS_FINISHED => :success,
|
63
|
+
|
64
|
+
JOB_STATUS_TRANSFORMING => :info,
|
65
|
+
|
56
66
|
JOB_STATUS_UPLOADING => :info,
|
57
67
|
JOB_STATUS_RENAMING => :info,
|
58
68
|
}
|
@@ -64,6 +74,11 @@ JOB_METHOD_FTPS = "ftps"
|
|
64
74
|
JOB_METHOD_SFTP = "sftp"
|
65
75
|
JOB_METHOD_FILE = "file"
|
66
76
|
|
77
|
+
# Jobs types
|
78
|
+
JOB_TYPE_TRANSFER = "transfer"
|
79
|
+
JOB_TYPE_VIDEO = "video"
|
80
|
+
JOB_TYPE_DUMMY = "dummy"
|
81
|
+
JOB_TYPES = [JOB_TYPE_TRANSFER, JOB_TYPE_VIDEO, JOB_TYPE_DUMMY]
|
67
82
|
|
68
83
|
# Worker statuses
|
69
84
|
WORKER_STATUS_STARTING = "starting"
|
@@ -83,6 +98,8 @@ WORKER_STYLES = {
|
|
83
98
|
|
84
99
|
|
85
100
|
# API mountpoints
|
101
|
+
MOUNT_SWAGGER_JSON = "/swagger.json"
|
102
|
+
MOUNT_SWAGGER_UI = "/swagger.html"
|
86
103
|
MOUNT_JOBS = "/jobs"
|
87
104
|
MOUNT_BOARD = "/board"
|
88
105
|
MOUNT_STATUS = "/status"
|
@@ -6,20 +6,24 @@ module RestFtpDaemon
|
|
6
6
|
|
7
7
|
class MissingPool < RestFtpDaemonException; end
|
8
8
|
class InvalidWorkerNumber < RestFtpDaemonException; end
|
9
|
-
|
9
|
+
class QueueCantCreateJob < RestFtpDaemonException; end
|
10
10
|
class JobException < RestFtpDaemonException; end
|
11
11
|
class JobTimeout < RestFtpDaemonException; end
|
12
12
|
class JobNotFound < RestFtpDaemonException; end
|
13
|
-
|
14
|
-
|
15
|
-
class
|
16
|
-
class
|
17
|
-
class
|
18
|
-
class
|
19
|
-
class
|
20
|
-
|
21
|
-
class
|
22
|
-
class
|
23
|
-
|
13
|
+
|
14
|
+
|
15
|
+
class AttributeMissing < RestFtpDaemonException; end
|
16
|
+
class AssertionFailed < RestFtpDaemonException; end
|
17
|
+
class UnresolvedTokens < RestFtpDaemonException; end
|
18
|
+
class LocationParseError < RestFtpDaemonException; end
|
19
|
+
class UnsupportedScheme < RestFtpDaemonException; end
|
20
|
+
|
21
|
+
class SourceNotSupported < RestFtpDaemonException; end
|
22
|
+
class SourceNotFound < RestFtpDaemonException; end
|
23
|
+
|
24
|
+
class TargetNotSupported < RestFtpDaemonException; end
|
25
|
+
class TargetFileExists < RestFtpDaemonException; end
|
26
|
+
class TargetDirectoryError < RestFtpDaemonException; end
|
27
|
+
class TargetPermissionError < RestFtpDaemonException; end
|
24
28
|
|
25
29
|
end
|
@@ -14,9 +14,9 @@ module RestFtpDaemon
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def get_censored_config
|
17
|
-
config
|
18
|
-
config[:users]
|
19
|
-
config[:endpoints]
|
17
|
+
config = Conf.to_hash
|
18
|
+
config[:users] = Conf[:users].keys if Conf[:users]
|
19
|
+
config[:endpoints] = Conf[:endpoints].keys if Conf[:endpoints]
|
20
20
|
config
|
21
21
|
end
|
22
22
|
|
@@ -17,15 +17,22 @@ module RestFtpDaemon
|
|
17
17
|
rand(36**len).to_s(36)
|
18
18
|
end
|
19
19
|
|
20
|
-
def tokenize item
|
21
|
-
return unless item.is_a? String
|
22
|
-
"[#{item}]"
|
23
|
-
end
|
24
|
-
|
25
20
|
def dashboard_url filter = ''
|
26
21
|
"#{MOUNT_BOARD}/#{filter}"
|
27
22
|
end
|
28
23
|
|
24
|
+
def underscore camel_cased_word
|
25
|
+
camel_cased_word.to_s.gsub(/::/, '/').
|
26
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
27
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
28
|
+
tr("-", "_").
|
29
|
+
downcase
|
30
|
+
end
|
31
|
+
|
32
|
+
def exception_to_error exception
|
33
|
+
underscore exception.class.name.split('::').last
|
34
|
+
end
|
35
|
+
|
29
36
|
# Dates and times: date with time generator
|
30
37
|
# def datetime_full datetime
|
31
38
|
# return "-" if datetime.nil?
|
@@ -30,19 +30,49 @@ module RestFtpDaemon
|
|
30
30
|
return "label-danger" if runs > 2
|
31
31
|
end
|
32
32
|
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
33
|
+
def location_style uri
|
34
|
+
case uri
|
35
|
+
when URI::FILE
|
36
|
+
"primary"
|
37
|
+
when URI::FTP
|
38
|
+
"warning"
|
39
|
+
when URI::FTPS
|
40
|
+
"success"
|
41
|
+
when URI::SFTP
|
42
|
+
"success"
|
42
43
|
else
|
43
|
-
"
|
44
|
+
"default"
|
44
45
|
end
|
45
|
-
|
46
|
+
end
|
47
|
+
|
48
|
+
def job_style job
|
49
|
+
case job.type
|
50
|
+
when JOB_TYPE_TRANSFER
|
51
|
+
icon_klass = "transfer"
|
52
|
+
when JOB_TYPE_VIDEO
|
53
|
+
icon_klass = "facetime-video"
|
54
|
+
when JOB_TYPE_DUMMY
|
55
|
+
icon_klass = "question-sign"
|
56
|
+
else
|
57
|
+
icon_klass = "label-default"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def location_label uri
|
62
|
+
sprintf(
|
63
|
+
'<div class="transfer-type label label-%s">%s</div>',
|
64
|
+
location_style(uri),
|
65
|
+
uri.class.name.split('::').last
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def job_type job
|
70
|
+
sprintf(
|
71
|
+
'<span class="glyphicon glyphicon-%s" alt="%s"></span> %s',
|
72
|
+
job_style(job),
|
73
|
+
job.type,
|
74
|
+
job.type
|
75
|
+
)
|
46
76
|
end
|
47
77
|
|
48
78
|
def datetime_short datetime
|
data/lib/rest-ftp-daemon/job.rb
CHANGED
@@ -1,18 +1,27 @@
|
|
1
|
-
#
|
1
|
+
# FIXME: prepare files list ar prepare_common
|
2
|
+
# FIXME: scope classes in submodules like Worker::Transfer, Job::Video
|
3
|
+
|
4
|
+
# Represents work to be done along with parameters to process it
|
2
5
|
require "securerandom"
|
3
6
|
|
4
7
|
module RestFtpDaemon
|
5
8
|
class Job
|
6
|
-
include BmcDaemonLib::LoggerHelper
|
7
9
|
include ::NewRelic::Agent::Instrumentation::ControllerInstrumentation
|
8
10
|
include CommonHelpers
|
9
11
|
|
12
|
+
# Logging
|
13
|
+
attr_reader :logger
|
14
|
+
include BmcDaemonLib::LoggerHelper
|
15
|
+
|
10
16
|
# Class constants
|
11
|
-
FIELDS = [:source, :target, :label, :priority, :pool, :notify,
|
17
|
+
FIELDS = [:type, :source, :target, :label, :priority, :pool, :notify,
|
18
|
+
:overwrite, :mkdir, :tempfile,
|
19
|
+
:video_vc, :video_ac, :video_custom
|
20
|
+
]
|
12
21
|
|
13
22
|
# Class options
|
14
|
-
attr_reader :logger
|
15
23
|
attr_accessor :wid
|
24
|
+
attr_accessor :type
|
16
25
|
|
17
26
|
attr_reader :id
|
18
27
|
attr_reader :error
|
@@ -39,7 +48,6 @@ module RestFtpDaemon
|
|
39
48
|
|
40
49
|
# Init context
|
41
50
|
@id = job_id.to_s
|
42
|
-
# @infos = {}
|
43
51
|
#@updated_at = nil
|
44
52
|
@started_at = nil
|
45
53
|
@finished_at = nil
|
@@ -47,6 +55,7 @@ module RestFtpDaemon
|
|
47
55
|
@status = nil
|
48
56
|
@runs = 0
|
49
57
|
@wid = nil
|
58
|
+
@infos = {}
|
50
59
|
|
51
60
|
# Prepare configuration
|
52
61
|
@config = Conf[:transfer] || {}
|
@@ -71,149 +80,95 @@ module RestFtpDaemon
|
|
71
80
|
@pool = DEFAULT_POOL
|
72
81
|
end
|
73
82
|
|
74
|
-
#
|
75
|
-
|
83
|
+
# Prepare sources/target
|
84
|
+
prepare_source
|
85
|
+
prepare_target
|
86
|
+
|
87
|
+
# Handle exceptions
|
88
|
+
rescue RestFtpDaemon::UnsupportedScheme => exception
|
89
|
+
return oops :started, exception
|
76
90
|
end
|
77
91
|
|
78
92
|
def reset
|
79
|
-
#
|
93
|
+
# Update job status
|
94
|
+
set_status JOB_STATUS_PREPARING
|
95
|
+
|
96
|
+
# Flag current job timestamps
|
80
97
|
@queued_at = Time.now
|
81
98
|
@updated_at = Time.now
|
82
99
|
|
83
|
-
#
|
84
|
-
|
85
|
-
|
100
|
+
# Job has been prepared, reset infos
|
101
|
+
set_status JOB_STATUS_PREPARED
|
102
|
+
@infos = {}
|
103
|
+
set_info :job, :prepared_at, Time.now
|
104
|
+
set_info_location :source, @source_loc
|
105
|
+
set_info_location :target, @target_loc
|
86
106
|
|
87
|
-
# Update job status
|
107
|
+
# Update job status, send first notification
|
88
108
|
set_status JOB_STATUS_QUEUED
|
89
109
|
set_error nil
|
90
|
-
|
110
|
+
client_notify :queued
|
111
|
+
log_info "Job.reset notify[queued]"
|
91
112
|
end
|
92
113
|
|
114
|
+
# Process job
|
93
115
|
def process
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
begin
|
98
|
-
prepare
|
116
|
+
# Check prerequisites
|
117
|
+
raise RestFtpDaemon::AssertionFailed, "run/source_loc" unless @source_loc
|
118
|
+
raise RestFtpDaemon::AssertionFailed, "run/target_loc" unless @target_loc
|
99
119
|
|
100
|
-
|
101
|
-
|
120
|
+
# Notify we start working
|
121
|
+
log_info "Job.process notify [started]"
|
122
|
+
client_notify :started
|
102
123
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
rescue
|
110
|
-
return oops :started, exception, "
|
111
|
-
|
112
|
-
|
113
|
-
return oops :started, exception, "assertion_failed"
|
114
|
-
|
115
|
-
rescue URI::InvalidURIError => exception
|
116
|
-
return oops :started, exception, "target_invalid"
|
117
|
-
|
118
|
-
else
|
119
|
-
# Prepare done !
|
120
|
-
set_status JOB_STATUS_PREPARED
|
121
|
-
log_info "Job.process notify [started]"
|
122
|
-
client_notify :started
|
124
|
+
# Before work
|
125
|
+
begin
|
126
|
+
log_debug "Job.process before"
|
127
|
+
before
|
128
|
+
rescue RestFtpDaemon::SourceNotSupported => exception
|
129
|
+
return oops :started, exception
|
130
|
+
rescue Net::FTPConnectionError => exception
|
131
|
+
return oops :started, exception, "ftp_connection_error"
|
132
|
+
rescue StandardError => exception
|
133
|
+
return oops :started, exception, "unexpected_error"
|
123
134
|
end
|
124
135
|
|
125
|
-
#
|
136
|
+
# Do the hard work
|
126
137
|
begin
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
rescue
|
131
|
-
return oops :
|
132
|
-
|
133
|
-
rescue EOFError => exception
|
134
|
-
return oops :ended, exception, "conn_eof"
|
135
|
-
|
136
|
-
rescue Errno::EHOSTDOWN => exception
|
137
|
-
return oops :ended, exception, "conn_host_is_down"
|
138
|
-
|
139
|
-
rescue Errno::EPIPE=> exception
|
140
|
-
return oops :ended, exception, "conn_broken_pipe"
|
141
|
-
|
142
|
-
rescue Errno::ENETUNREACH => exception
|
143
|
-
return oops :ended, exception, "conn_unreachable"
|
144
|
-
|
145
|
-
rescue Errno::ECONNRESET => exception
|
146
|
-
return oops :ended, exception, "conn_reset_by_peer"
|
147
|
-
|
148
|
-
rescue Errno::ENOTCONN => exception
|
149
|
-
return oops :ended, exception, "conn_failed"
|
150
|
-
|
151
|
-
rescue Errno::ECONNREFUSED => exception
|
152
|
-
return oops :ended, exception, "conn_refused"
|
153
|
-
|
154
|
-
rescue Timeout::Error, Errno::ETIMEDOUT, Net::ReadTimeout => exception
|
155
|
-
return oops :ended, exception, "conn_timed_out"
|
156
|
-
|
157
|
-
rescue OpenSSL::SSL::SSLError => exception
|
158
|
-
return oops :ended, exception, "conn_openssl_error"
|
159
|
-
|
160
|
-
rescue Net::FTPReplyError => exception
|
161
|
-
return oops :ended, exception, "ftp_reply_error"
|
162
|
-
|
163
|
-
rescue Net::FTPTempError => exception
|
164
|
-
return oops :ended, exception, "ftp_temp_error"
|
165
|
-
|
166
|
-
rescue Net::FTPPermError => exception
|
167
|
-
return oops :ended, exception, "ftp_perm_error"
|
168
|
-
|
169
|
-
rescue Net::FTPProtoError => exception
|
170
|
-
return oops :ended, exception, "ftp_proto_error"
|
171
|
-
|
172
|
-
rescue Net::FTPError => exception
|
173
|
-
return oops :ended, exception, "ftp_error"
|
174
|
-
|
175
|
-
rescue Net::SFTP::StatusException => exception
|
176
|
-
return oops :ended, exception, "sftp_exception"
|
177
|
-
|
178
|
-
rescue Net::SSH::HostKeyMismatch => exception
|
179
|
-
return oops :ended, exception, "sftp_key_mismatch"
|
180
|
-
|
181
|
-
rescue Net::SSH::AuthenticationFailed => exception
|
182
|
-
return oops :ended, exception, "sftp_auth_failed"
|
183
|
-
|
184
|
-
rescue Errno::EMFILE => exception
|
185
|
-
return oops :ended, exception, "too_many_open_files"
|
186
|
-
|
187
|
-
rescue Errno::EINVAL => exception
|
188
|
-
return oops :ended, exception, "invalid_argument", true
|
189
|
-
|
190
|
-
rescue Encoding::UndefinedConversionError => exception
|
191
|
-
return oops :ended, exception, "encoding_error", true
|
192
|
-
|
193
|
-
rescue RestFtpDaemon::JobSourceNotFound => exception
|
194
|
-
return oops :ended, exception, "source_not_found"
|
195
|
-
|
196
|
-
rescue RestFtpDaemon::JobSourceNotReadable => exception
|
197
|
-
return oops :ended, exception, "source_not_readable"
|
138
|
+
log_debug "Job.process work"
|
139
|
+
set_status JOB_STATUS_WORKING
|
140
|
+
work
|
141
|
+
rescue StandardError => exception
|
142
|
+
return oops :started, exception, "unexpected_error"
|
143
|
+
end
|
198
144
|
|
199
|
-
|
200
|
-
|
145
|
+
# Finalize all this
|
146
|
+
begin
|
147
|
+
log_debug "Job.process after"
|
148
|
+
after
|
149
|
+
rescue StandardError => exception
|
150
|
+
return oops :started, exception, "unexpected_error"
|
151
|
+
end
|
201
152
|
|
202
|
-
|
203
|
-
|
153
|
+
# All done !
|
154
|
+
set_status JOB_STATUS_FINISHED
|
155
|
+
log_info "JobVideo.process notify [ended]"
|
156
|
+
client_notify :ended
|
157
|
+
end
|
204
158
|
|
205
|
-
|
206
|
-
|
159
|
+
def before
|
160
|
+
end
|
161
|
+
def work
|
162
|
+
end
|
163
|
+
def after
|
164
|
+
end
|
207
165
|
|
208
|
-
|
209
|
-
|
166
|
+
def source_uri
|
167
|
+
@source_loc.uri
|
168
|
+
end
|
210
169
|
|
211
|
-
|
212
|
-
|
213
|
-
set_status JOB_STATUS_FINISHED
|
214
|
-
log_info "Job.process notify [ended]"
|
215
|
-
client_notify :ended
|
216
|
-
end
|
170
|
+
def target_uri
|
171
|
+
@target_loc.uri
|
217
172
|
end
|
218
173
|
|
219
174
|
def weight
|
@@ -260,156 +215,35 @@ module RestFtpDaemon
|
|
260
215
|
|
261
216
|
def get_info level1, level2
|
262
217
|
@mutex.synchronize do
|
263
|
-
@infos || {}
|
218
|
+
# @infos || {}
|
264
219
|
@infos[level1][level2] if @infos[level1].is_a? Hash
|
265
220
|
end
|
266
221
|
end
|
267
222
|
|
268
223
|
protected
|
269
224
|
|
270
|
-
def
|
271
|
-
|
225
|
+
def alert_common_method_called
|
226
|
+
log_error "Job PLACEHOLDER METHOD CALLED"
|
272
227
|
end
|
273
228
|
|
274
|
-
def
|
275
|
-
|
229
|
+
def prepare_source
|
230
|
+
raise RestFtpDaemon::AttributeMissing, "source" unless @source
|
231
|
+
@source_loc = Location.new @source
|
232
|
+
log_info "Job.prepare_source #{@source_loc.uri}"
|
276
233
|
end
|
277
234
|
|
278
|
-
def
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
# Stack RANDOM into tokens
|
284
|
-
vectors["RANDOM"] = SecureRandom.hex(JOB_RANDOM_LEN)
|
285
|
-
|
286
|
-
# Replace endpoints defined in config
|
287
|
-
newpath = path.clone
|
288
|
-
vectors.each do |from, to|
|
289
|
-
next if to.to_s.blank?
|
290
|
-
newpath.gsub! tokenize(from), to
|
291
|
-
end
|
292
|
-
|
293
|
-
# Ensure result does not contain tokens after replacement
|
294
|
-
raise RestFtpDaemon::JobUnresolvedTokens if contains_brackets newpath
|
295
|
-
|
296
|
-
# All OK, return this URL stripping multiple slashes
|
297
|
-
newpath.gsub(/([^:])\/\//, '\1/')
|
298
|
-
end
|
299
|
-
|
300
|
-
def prepare
|
301
|
-
# Init
|
302
|
-
@source_path = nil
|
303
|
-
|
304
|
-
# Prepare flags
|
305
|
-
flag_prepare :mkdir, false
|
306
|
-
flag_prepare :overwrite, false
|
307
|
-
flag_prepare :tempfile, true
|
308
|
-
|
309
|
-
# Update job status
|
310
|
-
set_status JOB_STATUS_PREPARING
|
311
|
-
@runs += 1
|
312
|
-
|
313
|
-
# Prepare source
|
314
|
-
raise RestFtpDaemon::JobMissingAttribute unless @source
|
315
|
-
@source_path = File.expand_path replace_tokens(@source)
|
316
|
-
set_info :source, :path, @source_path
|
317
|
-
set_info :source, :method, JOB_METHOD_FILE
|
318
|
-
|
319
|
-
# Prepare target
|
320
|
-
raise RestFtpDaemon::JobMissingAttribute unless @target
|
321
|
-
target_uri = expand_url @target
|
322
|
-
set_info :target, :uri, target_uri.to_s
|
323
|
-
set_info :target, :host, target_uri.host
|
324
|
-
@target_path = Path.new target_uri.path, true
|
325
|
-
|
326
|
-
# Prepare remote (case would be preferable but too hard to use,
|
327
|
-
# as target could be of a descendent class of URI:XXX and not matching directlry)
|
328
|
-
if target_uri.is_a? URI::FTP
|
329
|
-
log_info "Job.prepare target_method FTP"
|
330
|
-
set_info :target, :method, JOB_METHOD_FTP
|
331
|
-
@remote = RemoteFTP.new target_uri, log_prefix, debug: @config[:debug_ftp]
|
332
|
-
|
333
|
-
elsif (target_uri.is_a? URI::FTPES) || (target_uri.is_a? URI::FTPS)
|
334
|
-
log_info "Job.prepare target_method FTPES"
|
335
|
-
set_info :target, :method, JOB_METHOD_FTPS
|
336
|
-
@remote = RemoteFTP.new target_uri, log_prefix, debug: @config[:debug_ftps], ftpes: true
|
337
|
-
|
338
|
-
elsif target_uri.is_a? URI::SFTP
|
339
|
-
log_info "Job.prepare target_method SFTP"
|
340
|
-
set_info :target, :method, JOB_METHOD_SFTP
|
341
|
-
@remote = RemoteSFTP.new target_uri, log_prefix, debug: @config[:debug_sftp]
|
342
|
-
|
343
|
-
else
|
344
|
-
log_info "Job.prepare unknown scheme [#{target_uri.scheme}]"
|
345
|
-
raise RestFtpDaemon::JobTargetUnsupported
|
346
|
-
|
347
|
-
end
|
235
|
+
def prepare_target
|
236
|
+
raise RestFtpDaemon::AttributeMissing, "target" unless @target
|
237
|
+
@target_loc = Location.new @target
|
238
|
+
log_info "Job.prepare_target #{@target_loc.uri}"
|
348
239
|
end
|
349
240
|
|
350
|
-
def
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
raise RestFtpDaemon::JobAssertionFailed, "run/1" unless @source_path
|
357
|
-
raise RestFtpDaemon::JobAssertionFailed, "run/2" unless @target_path
|
358
|
-
@transfer_sent = 0
|
359
|
-
set_info :source, :processed, 0
|
360
|
-
|
361
|
-
# Guess source files from disk
|
362
|
-
set_status JOB_STATUS_CHECKING_SRC
|
363
|
-
sources = find_local @source_path
|
364
|
-
set_info :source, :count, sources.count
|
365
|
-
set_info :source, :files, sources.collect(&:full)
|
366
|
-
log_info "Job.run sources #{sources.collect(&:name)}"
|
367
|
-
raise RestFtpDaemon::JobSourceNotFound if sources.empty?
|
368
|
-
|
369
|
-
# Guess target file name, and fail if present while we matched multiple sources
|
370
|
-
raise RestFtpDaemon::JobTargetDirectoryError if @target_path.name && sources.count>1
|
371
|
-
|
372
|
-
# Connect to remote server and login
|
373
|
-
set_status JOB_STATUS_CONNECTING
|
374
|
-
@remote.connect
|
375
|
-
|
376
|
-
# Prepare target path or build it if asked
|
377
|
-
set_status JOB_STATUS_CHDIR
|
378
|
-
@remote.chdir_or_create @target_path.dir, @mkdir
|
379
|
-
|
380
|
-
# Compute total files size
|
381
|
-
@transfer_total = sources.collect(&:size).sum
|
382
|
-
set_info :transfer, :total, @transfer_total
|
383
|
-
|
384
|
-
# Reset counters
|
385
|
-
@last_data = 0
|
386
|
-
@last_time = Time.now
|
387
|
-
|
388
|
-
# Handle each source file matched, and start a transfer
|
389
|
-
source_processed = 0
|
390
|
-
targets = []
|
391
|
-
sources.each do |source|
|
392
|
-
# Compute target filename
|
393
|
-
full_target = @target_path.clone
|
394
|
-
|
395
|
-
# Add the source file name if none found in the target path
|
396
|
-
unless full_target.name
|
397
|
-
full_target.name = source.name
|
398
|
-
end
|
399
|
-
|
400
|
-
# Do the transfer, for each file
|
401
|
-
remote_push source, full_target
|
402
|
-
|
403
|
-
# Add it to transferred target names
|
404
|
-
targets << full_target.full
|
405
|
-
set_info :target, :files, targets
|
406
|
-
|
407
|
-
# Update counters
|
408
|
-
set_info :source, :processed, source_processed += 1
|
409
|
-
end
|
410
|
-
|
411
|
-
# FTP transfer finished
|
412
|
-
finalize
|
241
|
+
def set_info_location prefix, location
|
242
|
+
return unless location.is_a? Location
|
243
|
+
set_info prefix, :location_uri, location.to_s
|
244
|
+
set_info prefix, :location_scheme, location.scheme
|
245
|
+
set_info prefix, :location_path, location.path
|
246
|
+
set_info prefix, :location_host, location.host
|
413
247
|
end
|
414
248
|
|
415
249
|
private
|
@@ -418,7 +252,7 @@ module RestFtpDaemon
|
|
418
252
|
[@wid, @id, nil]
|
419
253
|
end
|
420
254
|
|
421
|
-
def
|
255
|
+
def scan_local_paths path
|
422
256
|
Dir.glob(path).collect do |file|
|
423
257
|
next unless File.readable? file
|
424
258
|
next unless File.file? file
|
@@ -432,6 +266,20 @@ module RestFtpDaemon
|
|
432
266
|
Thread.current.thread_variable_set :updated_at, now
|
433
267
|
end
|
434
268
|
|
269
|
+
def set_error value
|
270
|
+
@mutex.synchronize do
|
271
|
+
@error = value
|
272
|
+
end
|
273
|
+
touch_job
|
274
|
+
end
|
275
|
+
|
276
|
+
def set_status value
|
277
|
+
@mutex.synchronize do
|
278
|
+
@status = value
|
279
|
+
end
|
280
|
+
touch_job
|
281
|
+
end
|
282
|
+
|
435
283
|
def set_info level1, level2, value
|
436
284
|
@mutex.synchronize do
|
437
285
|
@infos || {}
|
@@ -445,26 +293,14 @@ module RestFtpDaemon
|
|
445
293
|
else
|
446
294
|
@infos[level1][level2] = value
|
447
295
|
end
|
448
|
-
|
449
|
-
# Mark the job as updated
|
450
|
-
touch_job
|
451
296
|
end
|
297
|
+
touch_job
|
452
298
|
end
|
453
299
|
|
454
300
|
def utf8 value
|
455
301
|
value.to_s.encode("UTF-8")
|
456
302
|
end
|
457
303
|
|
458
|
-
def set_error value
|
459
|
-
@error = value
|
460
|
-
touch_job
|
461
|
-
end
|
462
|
-
|
463
|
-
def set_status value
|
464
|
-
@status = value
|
465
|
-
touch_job
|
466
|
-
end
|
467
|
-
|
468
304
|
def flag_prepare name, default
|
469
305
|
# build the flag instance var name
|
470
306
|
variable = "@#{name}"
|
@@ -478,120 +314,6 @@ module RestFtpDaemon
|
|
478
314
|
end
|
479
315
|
end
|
480
316
|
|
481
|
-
def finalize
|
482
|
-
# Close FTP connexion and free up memory
|
483
|
-
log_info "Job.finalize"
|
484
|
-
@remote.close
|
485
|
-
|
486
|
-
# Free-up remote object
|
487
|
-
@remote = nil
|
488
|
-
|
489
|
-
# Update job status
|
490
|
-
set_status JOB_STATUS_DISCONNECTING
|
491
|
-
@finished_at = Time.now
|
492
|
-
|
493
|
-
# Update counters
|
494
|
-
RestFtpDaemon::Counters.instance.increment :jobs, :finished
|
495
|
-
RestFtpDaemon::Counters.instance.add :data, :transferred, @transfer_total
|
496
|
-
end
|
497
|
-
|
498
|
-
def remote_push source, target
|
499
|
-
# Method assertions
|
500
|
-
raise RestFtpDaemon::JobAssertionFailed, "remote_push/1" if @remote.nil?
|
501
|
-
raise RestFtpDaemon::JobAssertionFailed, "remote_push/2" if source.nil?
|
502
|
-
raise RestFtpDaemon::JobAssertionFailed, "remote_push/3" if target.nil?
|
503
|
-
|
504
|
-
# Use source filename if target path provided none (typically with multiple sources)
|
505
|
-
log_info "Job.remote_push [#{source.name}]: [#{source.full}] > [#{target.full}]"
|
506
|
-
set_info :source, :current, source.name
|
507
|
-
|
508
|
-
# Compute temp target name
|
509
|
-
tempname = nil
|
510
|
-
if @tempfile
|
511
|
-
tempname = "#{target.name}.temp-#{identifier(JOB_TEMPFILE_LEN)}"
|
512
|
-
log_debug "Job.remote_push tempname [#{tempname}]"
|
513
|
-
end
|
514
|
-
|
515
|
-
# Remove any existing version if expected, or test its presence
|
516
|
-
if @overwrite
|
517
|
-
@remote.remove! target
|
518
|
-
elsif size = @remote.present?(target)
|
519
|
-
log_debug "Job.remote_push existing (#{format_bytes size, 'B'})"
|
520
|
-
raise RestFtpDaemon::JobTargetFileExists
|
521
|
-
end
|
522
|
-
|
523
|
-
# Start transfer
|
524
|
-
transfer_started_at = Time.now
|
525
|
-
@progress_at = 0
|
526
|
-
@notified_at = transfer_started_at
|
527
|
-
|
528
|
-
# Start the transfer, update job status after each block transfer
|
529
|
-
set_status JOB_STATUS_UPLOADING
|
530
|
-
@remote.push source, target, tempname do |transferred, name|
|
531
|
-
# Update transfer statistics
|
532
|
-
progress transferred, name
|
533
|
-
|
534
|
-
# Touch my worker status
|
535
|
-
touch_job
|
536
|
-
end
|
537
|
-
|
538
|
-
# Compute final bitrate
|
539
|
-
global_transfer_bitrate = get_bitrate @transfer_total, (Time.now - transfer_started_at)
|
540
|
-
set_info :transfer, :bitrate, global_transfer_bitrate.round(0)
|
541
|
-
|
542
|
-
# Done
|
543
|
-
set_info :source, :current, nil
|
544
|
-
end
|
545
|
-
|
546
|
-
def progress transferred, name = ""
|
547
|
-
# What's current time ?
|
548
|
-
now = Time.now
|
549
|
-
notify_after = @config[:notify_after]
|
550
|
-
|
551
|
-
# Update counters
|
552
|
-
@transfer_sent += transferred
|
553
|
-
set_info :transfer, :sent, @transfer_sent
|
554
|
-
|
555
|
-
# Update job info
|
556
|
-
percent0 = (100.0 * @transfer_sent / @transfer_total).round(0)
|
557
|
-
set_info :transfer, :progress, percent0
|
558
|
-
|
559
|
-
# Update job status after each NOTIFY_UPADE_STATUS
|
560
|
-
progressed_ago = (now.to_f - @progress_at.to_f)
|
561
|
-
if (!JOB_UPDATE_INTERVAL.to_f.zero?) && (progressed_ago > JOB_UPDATE_INTERVAL.to_f)
|
562
|
-
@current_bitrate = running_bitrate @transfer_sent
|
563
|
-
set_info :transfer, :bitrate, @current_bitrate.round(0)
|
564
|
-
|
565
|
-
# Log progress
|
566
|
-
stack = []
|
567
|
-
stack << "#{percent0} %"
|
568
|
-
stack << (format_bytes @transfer_sent, "B")
|
569
|
-
stack << (format_bytes @transfer_total, "B")
|
570
|
-
stack << (format_bytes @current_bitrate.round(0), "bps")
|
571
|
-
stack2 = stack.map { |txt| ("%#{LOG_PIPE_LEN.to_i}s" % txt) }.join("\t")
|
572
|
-
log_debug "progress #{stack2} \t#{name}"
|
573
|
-
|
574
|
-
# Remember when we last did it
|
575
|
-
@progress_at = now
|
576
|
-
end
|
577
|
-
|
578
|
-
# Notify if requested
|
579
|
-
notified_ago = (now.to_f - @notified_at.to_f)
|
580
|
-
if (!notify_after.nil?) && (notified_ago > notify_after)
|
581
|
-
# Prepare and send notification
|
582
|
-
notif_status = {
|
583
|
-
progress: percent0,
|
584
|
-
transfer_sent: @transfer_sent,
|
585
|
-
transfer_total: @transfer_total,
|
586
|
-
transfer_bitrate: @current_bitrate.round(0),
|
587
|
-
}
|
588
|
-
client_notify :progress, status: notif_status
|
589
|
-
|
590
|
-
# Remember when we last did it
|
591
|
-
@notified_at = now
|
592
|
-
end
|
593
|
-
end
|
594
|
-
|
595
317
|
def client_notify event, payload = {}
|
596
318
|
# Skip if no URL given
|
597
319
|
return unless @notify
|
@@ -605,34 +327,12 @@ module RestFtpDaemon
|
|
605
327
|
log_error "Job.client_notify EXCEPTION: #{ex.inspect}"
|
606
328
|
end
|
607
329
|
|
608
|
-
def get_bitrate delta_data, delta_time
|
609
|
-
return nil if delta_time.nil? || delta_time.zero?
|
610
|
-
8 * delta_data.to_f.to_f / delta_time
|
611
|
-
end
|
612
|
-
|
613
|
-
def running_bitrate current_data
|
614
|
-
return if @last_time.nil?
|
615
|
-
|
616
|
-
# Compute deltas
|
617
|
-
@last_data ||= 0
|
618
|
-
delta_data = current_data - @last_data
|
619
|
-
delta_time = Time.now - @last_time
|
620
|
-
|
621
|
-
# Update counters
|
622
|
-
@last_time = Time.now
|
623
|
-
@last_data = current_data
|
624
|
-
|
625
|
-
# Return bitrate
|
626
|
-
get_bitrate delta_data, delta_time
|
627
|
-
end
|
628
|
-
|
629
330
|
def oops event, exception, error = nil, include_backtrace = false
|
630
|
-
#
|
631
|
-
|
632
|
-
error = exception.class if error.nil?
|
633
|
-
# error = "DEF #{exception.class}" if error.nil?
|
634
|
-
|
331
|
+
# Default error code derived from exception name
|
332
|
+
error = exception_to_error(exception) if error.nil?
|
635
333
|
message = "Job.oops event[#{event}] error[#{error}] ex[#{exception.class}] #{exception.message}"
|
334
|
+
|
335
|
+
# Backtrace?
|
636
336
|
if include_backtrace
|
637
337
|
log_error message, exception.backtrace
|
638
338
|
else
|
@@ -667,8 +367,6 @@ module RestFtpDaemon
|
|
667
367
|
end
|
668
368
|
|
669
369
|
# NewRelic instrumentation
|
670
|
-
add_transaction_tracer :prepare, category: :task
|
671
|
-
add_transaction_tracer :run, category: :task
|
672
370
|
add_transaction_tracer :client_notify, category: :task
|
673
371
|
add_transaction_tracer :initialize, category: :task
|
674
372
|
|