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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +26 -6
  3. data/Rakefile +5 -3
  4. data/bin/rest-ftp-daemon +2 -1
  5. data/config.ru +8 -2
  6. data/lib/rest-ftp-daemon.rb +10 -2
  7. data/lib/rest-ftp-daemon/api/debug.rb +17 -5
  8. data/lib/rest-ftp-daemon/api/{job_presenter.rb → entities/job.rb} +25 -2
  9. data/lib/rest-ftp-daemon/api/entities/options.rb +15 -0
  10. data/lib/rest-ftp-daemon/api/jobs.rb +46 -13
  11. data/lib/rest-ftp-daemon/api/root-real.rb +80 -0
  12. data/lib/rest-ftp-daemon/api/root-test.rb +69 -0
  13. data/lib/rest-ftp-daemon/api/root.rb +21 -5
  14. data/lib/rest-ftp-daemon/constants.rb +18 -1
  15. data/lib/rest-ftp-daemon/counters.rb +0 -3
  16. data/lib/rest-ftp-daemon/exceptions.rb +16 -12
  17. data/lib/rest-ftp-daemon/helpers/api.rb +3 -3
  18. data/lib/rest-ftp-daemon/helpers/common.rb +12 -5
  19. data/lib/rest-ftp-daemon/helpers/views.rb +41 -11
  20. data/lib/rest-ftp-daemon/job.rb +118 -420
  21. data/lib/rest-ftp-daemon/job_queue.rb +34 -5
  22. data/lib/rest-ftp-daemon/jobs/dummy.rb +20 -0
  23. data/lib/rest-ftp-daemon/jobs/transfer.rb +319 -0
  24. data/lib/rest-ftp-daemon/jobs/video.rb +107 -0
  25. data/lib/rest-ftp-daemon/location.rb +125 -0
  26. data/lib/rest-ftp-daemon/path.rb +3 -0
  27. data/lib/rest-ftp-daemon/remote_ftp.rb +2 -2
  28. data/lib/rest-ftp-daemon/remote_sftp.rb +2 -2
  29. data/lib/rest-ftp-daemon/static/config.json +411 -0
  30. data/lib/rest-ftp-daemon/static/css/bootstrap.min.css +14 -0
  31. data/lib/rest-ftp-daemon/static/css/{bootstrap.css → bootstrap.old.css} +0 -0
  32. data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.eot +0 -0
  33. data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.svg +288 -0
  34. data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.ttf +0 -0
  35. data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.woff +0 -0
  36. data/lib/rest-ftp-daemon/static/fonts/glyphicons-halflings-regular.woff2 +0 -0
  37. data/lib/rest-ftp-daemon/static/swagger.html +83 -0
  38. data/lib/rest-ftp-daemon/static/swagger/css/print.css +1362 -0
  39. data/lib/rest-ftp-daemon/static/swagger/css/reset.css +125 -0
  40. data/lib/rest-ftp-daemon/static/swagger/css/screen.css +1489 -0
  41. data/lib/rest-ftp-daemon/static/swagger/css/style.css +250 -0
  42. data/lib/rest-ftp-daemon/static/swagger/css/typography.css +14 -0
  43. data/lib/rest-ftp-daemon/static/swagger/fonts/DroidSans-Bold.ttf +0 -0
  44. data/lib/rest-ftp-daemon/static/swagger/fonts/DroidSans.ttf +0 -0
  45. data/lib/rest-ftp-daemon/static/swagger/images/collapse.gif +0 -0
  46. data/lib/rest-ftp-daemon/static/swagger/images/expand.gif +0 -0
  47. data/lib/rest-ftp-daemon/static/swagger/images/explorer_icons.png +0 -0
  48. data/lib/rest-ftp-daemon/static/swagger/images/favicon-16x16.png +0 -0
  49. data/lib/rest-ftp-daemon/static/swagger/images/favicon-32x32.png +0 -0
  50. data/lib/rest-ftp-daemon/static/swagger/images/favicon.ico +0 -0
  51. data/lib/rest-ftp-daemon/static/swagger/images/logo_small.png +0 -0
  52. data/lib/rest-ftp-daemon/static/swagger/images/pet_store_api.png +0 -0
  53. data/lib/rest-ftp-daemon/static/swagger/images/throbber.gif +0 -0
  54. data/lib/rest-ftp-daemon/static/swagger/images/wordnik_api.png +0 -0
  55. data/lib/rest-ftp-daemon/static/swagger/lib/backbone-min.js +15 -0
  56. data/lib/rest-ftp-daemon/static/swagger/lib/es5-shim.js +2065 -0
  57. data/lib/rest-ftp-daemon/static/swagger/lib/handlebars-4.0.5.js +4608 -0
  58. data/lib/rest-ftp-daemon/static/swagger/lib/highlight.9.1.0.pack.js +2 -0
  59. data/lib/rest-ftp-daemon/static/swagger/lib/highlight.9.1.0.pack_extended.js +34 -0
  60. data/lib/rest-ftp-daemon/static/swagger/lib/jquery-1.8.0.min.js +2 -0
  61. data/lib/rest-ftp-daemon/static/swagger/lib/jquery.ba-bbq.min.js +18 -0
  62. data/lib/rest-ftp-daemon/static/swagger/lib/jquery.slideto.min.js +1 -0
  63. data/lib/rest-ftp-daemon/static/swagger/lib/jquery.wiggle.min.js +8 -0
  64. data/lib/rest-ftp-daemon/static/swagger/lib/js-yaml.min.js +3 -0
  65. data/lib/rest-ftp-daemon/static/swagger/lib/jsoneditor.min.js +11 -0
  66. data/lib/rest-ftp-daemon/static/swagger/lib/lodash.min.js +102 -0
  67. data/lib/rest-ftp-daemon/static/swagger/lib/marked.js +1272 -0
  68. data/lib/rest-ftp-daemon/static/swagger/lib/object-assign-pollyfill.js +23 -0
  69. data/lib/rest-ftp-daemon/static/swagger/lib/swagger-oauth.js +347 -0
  70. data/lib/rest-ftp-daemon/static/swagger/lib/swagger-ui.min.js +10 -0
  71. data/lib/rest-ftp-daemon/uri.rb +9 -1
  72. data/lib/rest-ftp-daemon/views/dashboard.haml +1 -1
  73. data/lib/rest-ftp-daemon/views/dashboard_footer.haml +6 -3
  74. data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +3 -3
  75. data/lib/rest-ftp-daemon/views/dashboard_table.haml +25 -20
  76. data/lib/rest-ftp-daemon/workers/transfer.rb +14 -0
  77. data/rest-ftp-daemon.gemspec +14 -4
  78. data/spec/rest-ftp-daemon/features/swagger_spec.rb +24 -0
  79. data/spec/spec_helper.rb +4 -4
  80. 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 => MOUNT_STATUS
38
- mount RestFtpDaemon::API::Jobs => MOUNT_JOBS
39
- mount RestFtpDaemon::API::Dashbaord => MOUNT_BOARD
40
- mount RestFtpDaemon::API::Config => MOUNT_CONFIG
41
- mount RestFtpDaemon::API::Debug => MOUNT_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
- JOB_STATUS_RUNNING = "running"
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"
@@ -11,9 +11,6 @@ module RestFtpDaemon
11
11
 
12
12
  # Create mutex
13
13
  @mutex = Mutex.new
14
-
15
-
16
- set :system, :started_at, Time.now
17
14
  end
18
15
 
19
16
  def set group, name, value
@@ -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
- class JobUnresolvedTokens < RestFtpDaemonException; end
14
- class JobAssertionFailed < RestFtpDaemonException; end
15
- class JobMissingAttribute < RestFtpDaemonException; end
16
- class JobSourceNotFound < RestFtpDaemonException; end
17
- class JobSourceNotReadable < RestFtpDaemonException; end
18
- class JobTargetUnsupported < RestFtpDaemonException; end
19
- class JobTargetUnparseable < RestFtpDaemonException; end
20
- class JobTargetFileExists < RestFtpDaemonException; end
21
- class JobTargetDirectoryError < RestFtpDaemonException; end
22
- class JobTargetPermissionError < RestFtpDaemonException; end
23
- class JobTooManyOpenFiles < RestFtpDaemonException; end
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 = Conf.to_hash
18
- config[:users] = Conf[:users].keys if Conf[:users]
19
- config[:endpoints] = Conf[:endpoints].keys if Conf[: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 job_method_label method
34
- return if method.nil?
35
- klass = case method
36
- when JOB_METHOD_FILE
37
- "label-primary"
38
- when JOB_METHOD_FTP
39
- "label-warning"
40
- when JOB_METHOD_FTPS
41
- "label-success"
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
- "label-default"
44
+ "default"
44
45
  end
45
- "<div class=\"transfer-method label #{klass}\">#{method.upcase}</div>"
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>&nbsp;%s',
72
+ job_style(job),
73
+ job.type,
74
+ job.type
75
+ )
46
76
  end
47
77
 
48
78
  def datetime_short datetime
@@ -1,18 +1,27 @@
1
- # Reprensents work to be done along with parameters to process it
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, :overwrite, :mkdir, :tempfile]
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
- # Set job queue, thus reset
75
- # reset
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
- # Flag current job
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
- # Send first notification
84
- log_info "Job.initialize notify[queued]}]"
85
- client_notify :queued
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
- @infos = {}
110
+ client_notify :queued
111
+ log_info "Job.reset notify[queued]"
91
112
  end
92
113
 
114
+ # Process job
93
115
  def process
94
- log_info "Job.process update_interval[#{JOB_UPDATE_INTERVAL}]"
95
-
96
- # Prepare job
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
- rescue RestFtpDaemon::JobMissingAttribute => exception
101
- return oops :started, exception, "missing_attribute"
120
+ # Notify we start working
121
+ log_info "Job.process notify [started]"
122
+ client_notify :started
102
123
 
103
- rescue RestFtpDaemon::JobUnresolvedTokens => exception
104
- return oops :started, exception, "unresolved_tokens"
105
-
106
- rescue RestFtpDaemon::JobTargetUnparseable => exception
107
- return oops :started, exception, "target_unparseable"
108
-
109
- rescue RestFtpDaemon::JobTargetUnsupported => exception
110
- return oops :started, exception, "target_unsupported"
111
-
112
- rescue RestFtpDaemon::JobAssertionFailed => exception
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
- # Process job
136
+ # Do the hard work
126
137
  begin
127
- #return oops :ended, Exception.new, "ftp_perm_error"
128
- run
129
-
130
- rescue SocketError => exception
131
- return oops :ended, exception, "conn_socket_error"
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
- rescue RestFtpDaemon::JobTargetFileExists => exception
200
- return oops :ended, exception, "target_file_exists"
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
- rescue RestFtpDaemon::JobTargetDirectoryError => exception
203
- return oops :ended, exception, "target_directory_missing"
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
- rescue RestFtpDaemon::JobTargetPermissionError => exception
206
- return oops :ended, exception, "target_permission_error"
159
+ def before
160
+ end
161
+ def work
162
+ end
163
+ def after
164
+ end
207
165
 
208
- rescue RestFtpDaemon::JobAssertionFailed => exception
209
- return oops :ended, exception, "assertion_failed"
166
+ def source_uri
167
+ @source_loc.uri
168
+ end
210
169
 
211
- else
212
- # All done !
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 expand_url path
271
- URI.parse replace_tokens(path)
225
+ def alert_common_method_called
226
+ log_error "Job PLACEHOLDER METHOD CALLED"
272
227
  end
273
228
 
274
- def contains_brackets item
275
- /\[.*\]/.match(item)
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 replace_tokens path
279
- # Ensure endpoints are not a nil value
280
- return path unless @endpoints.is_a? Enumerable
281
- vectors = @endpoints.clone
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 run
351
- # Update job status
352
- set_status JOB_STATUS_RUNNING
353
- @started_at = Time.now
354
-
355
- # Method assertions and init
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 find_local path
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
- # Log this error
631
- # error = exception.class.to_s.encoding.to_s if error.nil?
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