rest-ftp-daemon 0.435.2 → 0.501.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +35 -29
  3. data/config.ru +1 -1
  4. data/defaults.yml +16 -12
  5. data/lib/rest-ftp-daemon.rb +1 -0
  6. data/lib/rest-ftp-daemon/api/config.rb +1 -2
  7. data/lib/rest-ftp-daemon/api/dashboard.rb +6 -4
  8. data/lib/rest-ftp-daemon/api/debug.rb +6 -5
  9. data/lib/rest-ftp-daemon/api/jobs.rb +1 -1
  10. data/lib/rest-ftp-daemon/api/root.rb +11 -10
  11. data/lib/rest-ftp-daemon/api/status.rb +1 -1
  12. data/lib/rest-ftp-daemon/constants.rb +12 -3
  13. data/lib/rest-ftp-daemon/counters.rb +1 -1
  14. data/lib/rest-ftp-daemon/entities/job.rb +1 -5
  15. data/lib/rest-ftp-daemon/entities/location.rb +4 -3
  16. data/lib/rest-ftp-daemon/entities/options.rb +1 -1
  17. data/lib/rest-ftp-daemon/exceptions.rb +1 -1
  18. data/lib/rest-ftp-daemon/helpers/api.rb +1 -1
  19. data/lib/rest-ftp-daemon/helpers/common.rb +1 -1
  20. data/lib/rest-ftp-daemon/helpers/views.rb +29 -10
  21. data/lib/rest-ftp-daemon/initialize.rb +1 -1
  22. data/lib/rest-ftp-daemon/job.rb +11 -13
  23. data/lib/rest-ftp-daemon/job_queue.rb +9 -10
  24. data/lib/rest-ftp-daemon/jobs/dummy.rb +1 -1
  25. data/lib/rest-ftp-daemon/jobs/errors.rb +13 -15
  26. data/lib/rest-ftp-daemon/jobs/transfer.rb +15 -15
  27. data/lib/rest-ftp-daemon/jobs/video.rb +7 -7
  28. data/lib/rest-ftp-daemon/launcher.rb +1 -1
  29. data/lib/rest-ftp-daemon/location.rb +91 -67
  30. data/lib/rest-ftp-daemon/metrics.rb +2 -2
  31. data/lib/rest-ftp-daemon/notification.rb +1 -1
  32. data/lib/rest-ftp-daemon/paginate.rb +1 -1
  33. data/lib/rest-ftp-daemon/remote/base.rb +8 -3
  34. data/lib/rest-ftp-daemon/remote/ftp.rb +18 -18
  35. data/lib/rest-ftp-daemon/remote/s3.rb +100 -41
  36. data/lib/rest-ftp-daemon/remote/sftp.rb +15 -15
  37. data/lib/rest-ftp-daemon/static/css/main.css +34 -4
  38. data/lib/rest-ftp-daemon/uri.rb +1 -1
  39. data/lib/rest-ftp-daemon/views/dashboard.haml +1 -1
  40. data/lib/rest-ftp-daemon/views/dashboard_counters.haml +1 -1
  41. data/lib/rest-ftp-daemon/views/dashboard_footer.haml +1 -1
  42. data/lib/rest-ftp-daemon/views/dashboard_header.haml +1 -1
  43. data/lib/rest-ftp-daemon/views/dashboard_jobs.haml +1 -2
  44. data/lib/rest-ftp-daemon/views/dashboard_rates.haml +1 -1
  45. data/lib/rest-ftp-daemon/views/dashboard_table.haml +8 -5
  46. data/lib/rest-ftp-daemon/views/dashboard_tokens.haml +1 -1
  47. data/lib/rest-ftp-daemon/views/dashboard_workers.haml +1 -1
  48. data/lib/rest-ftp-daemon/worker_pool.rb +2 -2
  49. data/lib/rest-ftp-daemon/workers/conchita.rb +1 -1
  50. data/lib/rest-ftp-daemon/workers/reporter.rb +1 -1
  51. data/lib/rest-ftp-daemon/workers/transfer.rb +3 -3
  52. data/lib/rest-ftp-daemon/workers/worker.rb +1 -1
  53. data/lib/shared/patch_file.rb +5 -0
  54. data/rest-ftp-daemon.gemspec +2 -2
  55. data/spec/spec_helper.rb +2 -1
  56. data/spec/support/request_helpers.rb +1 -1
  57. metadata +6 -5
@@ -4,76 +4,135 @@ require 'aws-sdk-resources'
4
4
  module RestFtpDaemon
5
5
  module Remote
6
6
  class RemoteS3 < RemoteBase
7
-
8
- MULTIPART_THRESHOLD_MB = 4
7
+ include CommonHelpers
9
8
 
10
9
  # Class options
11
10
  attr_reader :client
12
11
  attr_reader :target
13
12
 
14
- def prepare
15
- @multipart_threshold = MULTIPART_THRESHOLD_MB.to_i * 1024 * 1024
16
- log_debug "RemoteS3.prepare target[#{@target.inspect}] #{@multipart_threshold}"
17
- end
13
+ # def prepare
14
+ # end
18
15
 
19
16
  def connect
20
17
  super
21
18
 
22
19
  # Connect init
23
- log_debug "RemoteS3.connect region[#{target.aws_region}] id[#{target.aws_id}]"
20
+ log_debug "connect region[#{target.aws_region}] id[#{target.aws_id}]"
24
21
 
25
22
  # Connect remote server
26
- @client = Aws::S3::Resource.new(
23
+ @client = Aws::S3::Client.new(
27
24
  region: @target.aws_region,
28
25
  credentials: Aws::Credentials.new(@target.aws_id, @target.aws_secret),
29
- # thread_count: 4,
30
26
  http_wire_trace: @debug
31
27
  )
32
- #s3 = Aws::S3::Client.new(http_wire_trace: true)
28
+ end
29
+
30
+ def size_if_exists target
31
+ log_debug "size_if_exists [#{target.path}]"
32
+ object = @client.get_object(bucket: target.aws_bucket, key: target.path)
33
+ rescue Aws::S3::Errors::NotFound => e
34
+ return false
35
+ else
36
+ return object.content_length
33
37
  end
34
38
 
35
39
  def upload source, target, use_temp_name = false, &callback
36
40
  # Push init
37
41
  raise RestFtpDaemon::AssertionFailed, "upload/client" if @client.nil?
38
- log_debug "RemoteS3.upload bucket[#{target.aws_bucket}] name[#{target.name}]"
39
-
40
- # Update progress before
41
- #yield 0, target.name
42
- # Point to the right bucket and object
43
- bucket = @client.bucket(target.aws_bucket)
44
- object = bucket.object(target.name)
45
-
46
- # Do the transfer
47
- object.upload_file(source.path, {
48
- multipart_threshold: @multipart_threshold
49
- })
42
+ log_debug "upload bucket[#{target.aws_bucket}] path[#{target.path}]"
50
43
 
51
- # Wait for transfer to complete
52
- object.wait_until_exists do |waiter|
53
- # waiter.delay = 1
54
- # # log_debug "- progress[#{progress}] total[#{total}]"
55
- # waiter.before_wait do |attempts, response|
56
- # puts "#{attempts} made"
57
- # puts response.error.inspect
58
- # puts response.data.inspect
59
- # end
60
- # log_debug "- progress[] #{waiter.inspect}"
44
+ # Do the transfer, passing the file to the best method
45
+ File.open(source.filepath, 'r', encoding: 'BINARY') do |file|
46
+ if file.size >= JOB_S3_MIN_PART
47
+ upload_multipart file, target.aws_bucket, target.path, target.name, &callback
48
+ else
49
+ upload_onefile file, target.aws_bucket, target.path, target.name, &callback
50
+ end
61
51
  end
62
52
 
63
- # Update progress after
64
- #yield target.size, target.name
65
-
66
- # Dump information about this file
67
- log_debug "RemoteS3.upload url[#{object.public_url}]"
68
- log_debug "RemoteS3.upload etag[#{object.etag}]"
69
- set_info :target_aws_public_url, object.public_url
70
- set_info :target_aws_etag, object.etag
53
+ # We're all set
54
+ log_debug "RemoteS3.upload done"
71
55
  end
72
56
 
73
57
  def connected?
74
58
  !@client.nil?
75
59
  end
76
60
 
61
+ private
62
+
63
+ def upload_onefile file, s3_bucket, s3_path, s3_name, &callback
64
+ log_debug "upload_onefile"
65
+ @client.put_object(bucket: s3_bucket, key: s3_path, body: file)
66
+ end
67
+
68
+ def upload_multipart file, s3_bucket, s3_path, s3_name, &callback
69
+ # Init
70
+ current_part = 1
71
+
72
+ # Compute parameters
73
+ file_size = file.size
74
+ parts_size = compute_parts_size(file_size)
75
+ parts_count = (file_size.to_f / parts_size).ceil
76
+ log_debug "upload_multipart", {
77
+ file_size: format_bytes(file_size, "B"),
78
+ parts_size: format_bytes(parts_size, "B"),
79
+ parts_count: parts_count
80
+ }
81
+
82
+ # Prepare basic opts
83
+ options = {
84
+ bucket: s3_bucket,
85
+ key: s3_path,
86
+ }
87
+
88
+ # Declare multipart upload
89
+ mpu_create_response = @client.create_multipart_upload(options)
90
+ options[:upload_id] = mpu_create_response.upload_id
91
+ log_debug "created multipart id[#{options[:upload_id]}]"
92
+
93
+ # Upload each part
94
+ file.each_part(parts_size) do |part|
95
+ # Prepare part upload
96
+ opts = options.merge({
97
+ body: part,
98
+ part_number: current_part,
99
+ })
100
+ log_debug "upload_part [#{current_part}/#{parts_count}]"
101
+ resp = @client.upload_part(opts)
102
+
103
+ # Send progress info upwards
104
+ yield parts_size, s3_name
105
+
106
+ # Increment part number
107
+ current_part += 1
108
+ end
109
+
110
+ # Retrieve parts and complete upload
111
+ log_debug "complete_multipart_upload"
112
+ parts_resp = @client.list_parts(options)
113
+
114
+ those_parts = parts_resp.parts.map do |part|
115
+ { part_number: part.part_number, etag: part.etag }
116
+ end
117
+ opts = options.merge({
118
+ multipart_upload: {
119
+ parts: those_parts
120
+ }
121
+ })
122
+ mpu_complete_response = @client.complete_multipart_upload(opts)
123
+ end
124
+
125
+ def compute_parts_size filesize
126
+ # Initial part size is minimal
127
+ partsize_mini = JOB_S3_MIN_PART
128
+
129
+ # Other partsize if too many blocks
130
+ partsize_bigf = (filesize.to_f / JOB_S3_MAX_COUNT).ceil
131
+
132
+ # Decide
133
+ return [partsize_mini, partsize_bigf].max
134
+ end
135
+
77
136
  end
78
137
  end
79
- end
138
+ end
@@ -8,14 +8,14 @@ module RestFtpDaemon
8
8
  # Class options
9
9
  attr_reader :sftp
10
10
 
11
- def prepare
12
- end
11
+ # def prepare
12
+ # end
13
13
 
14
14
  def connect
15
15
  super
16
16
 
17
17
  # Connect init
18
- log_debug "RemoteSFTP.connect [#{@target.user}]@[#{@target.host}]:[#{@target.port}]"
18
+ log_debug "connect [#{@target.user}]@[#{@target.host}]:[#{@target.port}]"
19
19
 
20
20
  # Debug level
21
21
  verbosity = @debug ? Logger::DEBUG : false
@@ -30,9 +30,9 @@ module RestFtpDaemon
30
30
  )
31
31
  end
32
32
 
33
- def present? target
34
- log_debug "RemoteSFTP.present? [#{target.name}]"
35
- stat = @sftp.stat! target.path
33
+ def size_if_exists target
34
+ log_debug "size_if_exists [#{target.name}]"
35
+ stat = @sftp.stat! target.filepath
36
36
 
37
37
  rescue Net::SFTP::StatusException
38
38
  return false
@@ -41,8 +41,8 @@ module RestFtpDaemon
41
41
  end
42
42
 
43
43
  def remove! target
44
- log_debug "RemoteSFTP.remove! [#{target.name}]"
45
- @sftp.remove target.path
44
+ log_debug "remove! [#{target.name}]"
45
+ @sftp.remove target.filepath
46
46
 
47
47
  rescue Net::SFTP::StatusException
48
48
  log_debug "#{LOG_INDENT}[#{target.name}] file not found"
@@ -51,7 +51,7 @@ module RestFtpDaemon
51
51
  end
52
52
 
53
53
  def mkdir directory
54
- log_debug "RemoteSFTP.mkdir [#{directory}]"
54
+ log_debug "mkdir [#{directory}]"
55
55
  @sftp.mkdir! directory
56
56
 
57
57
  rescue StandardError => ex
@@ -60,7 +60,7 @@ module RestFtpDaemon
60
60
 
61
61
  def chdir_or_create directory, mkdir = false
62
62
  # Init, extract my parent name and my own name
63
- log_debug "RemoteSFTP.chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
63
+ log_debug "chdir_or_create mkdir[#{mkdir}] dir[#{directory}]"
64
64
  parent, _current = split_path(directory)
65
65
 
66
66
  # Access this directory
@@ -98,8 +98,8 @@ module RestFtpDaemon
98
98
  end
99
99
 
100
100
  # Do the transfer
101
- log_debug "RemoteSFTP.upload temp[#{use_temp_name}] name[#{dest.name}]"
102
- @sftp.upload! source.path, dest.path do |event, _uploader, *args|
101
+ log_debug "upload temp[#{use_temp_name}] name[#{dest.name}]"
102
+ @sftp.upload! source.filepath, dest.filepath do |event, _uploader, *args|
103
103
  case event
104
104
  when :open then
105
105
  # args[0] : file metadata
@@ -124,8 +124,8 @@ module RestFtpDaemon
124
124
  # Move the file back to its original name
125
125
  if use_temp_name
126
126
  flags = 0x00000001
127
- log_debug "RemoteSFTP.upload rename [#{dest.name}] > [#{target.name}]"
128
- @sftp.rename! dest.path, target.path, flags
127
+ log_debug "upload rename [#{dest.name}] > [#{target.name}]"
128
+ @sftp.rename! dest.filepath, target.filepath, flags
129
129
  end
130
130
 
131
131
  # progress:
@@ -143,4 +143,4 @@ module RestFtpDaemon
143
143
 
144
144
  end
145
145
  end
146
- end
146
+ end
@@ -149,16 +149,46 @@ body {
149
149
 
150
150
 
151
151
 
152
-
153
-
154
-
155
152
  .indicators .btn {
156
153
  cursor: pointer;
157
154
  opacity: 1;
158
155
  filter: alpha(opacity=100);
156
+ }
157
+
158
+ .label-group .label {
159
+ /*border: 1px solid red;*/
160
+ }
161
+
162
+ .label-group>.label:not(:last-child) {
163
+ border-bottom-right-radius: 0;
164
+ border-top-right-radius: 0;
165
+ margin-right: 0;
166
+ }
167
+
168
+ .label-group>.label:not(:first-child) {
169
+ border-bottom-left-radius: 0;
170
+ border-top-left-radius: 0;
171
+ margin-left: 0;
172
+ }
173
+
174
+ .label.label-simple {
175
+ color: black;
176
+ border: 1px solid silver;
177
+ background-color: white;
178
+ }
179
+ /*
180
+ .label-group>.label:first-child:not(:last-child) {
181
+ border-bottom-right-radius: 0;
182
+ border-top-right-radius: 0;
183
+ }
184
+
185
+ .label-group>.label:last-child:not(:first-child) {
186
+ border-bottom-left-radius: 0;
187
+ border-top-left-radius: 0;
159
188
  }
189
+ */
160
190
 
161
191
  .transfer-type {
162
- width: 40px;
192
+ /*width: 40px;*/
163
193
  }
164
194
 
@@ -23,4 +23,4 @@ module URI
23
23
  @@schemes["SFTP"] = SFTP
24
24
  @@schemes["S3"] = S3
25
25
  @@schemes["FILE"] = FILE
26
- end
26
+ end
@@ -40,4 +40,4 @@
40
40
 
41
41
  .footer
42
42
  .container-fluid
43
- = render :dashboard_footer
43
+ = render :dashboard_footer
@@ -22,4 +22,4 @@
22
22
  %tr
23
23
  %td
24
24
  %td= group
25
- %td.text-right= values
25
+ %td.text-right= values
@@ -39,4 +39,4 @@
39
39
 
40
40
  .btn-group.btn-group-sm
41
41
  .btn.btn-default.btn-info.disabled Started
42
- .btn.btn-default.disabled= datetime_short(Conf.app_started)
42
+ .btn.btn-default.disabled= datetime_short(Conf.app_started)
@@ -35,4 +35,4 @@
35
35
 
36
36
  .btn-group.btn-group-sm
37
37
  .btn.btn-default.btn-success.disabled Transferred
38
- .btn.btn-default.disabled= format_bytes(info_trans, "B", 1)
38
+ .btn.btn-default.disabled= format_bytes(info_trans, "B", 1)
@@ -56,5 +56,4 @@
56
56
 
57
57
  - unless jobs.empty?
58
58
  %tbody.jobs
59
- = render :dashboard_table, {jobs: jobs}
60
-
59
+ = render :dashboard_table, {jobs: jobs}
@@ -25,4 +25,4 @@
25
25
  %td
26
26
  = group
27
27
  %td.text-right
28
- = format_bytes(rate, "bps")
28
+ = format_bytes(rate, "bps")
@@ -34,12 +34,15 @@
34
34
  %td= job.label
35
35
 
36
36
  %td
37
- = location_label job.source_uri
38
- = token_highlight job.source
37
+ = location_label job.source_loc
38
+ = job.source_uri.path
39
+ =# token_highlight job.source
39
40
 
40
41
  %td
41
- = location_label job.target_uri
42
- = token_highlight job.target
42
+ = location_label job.target_loc
43
+ = job.target_uri.path
44
+
45
+ =# token_highlight job.target
43
46
 
44
47
  %td
45
48
  %span.push-status
@@ -85,4 +88,4 @@
85
88
  .label.label-default.flag.worker-label= job.priority
86
89
 
87
90
  %td
88
- .label.flag.worker-label{class: job_tentatives_style(job.tentatives)}= job.tentatives
91
+ .label.flag.worker-label{class: job_tentatives_style(job.tentatives)}= job.tentatives
@@ -3,4 +3,4 @@
3
3
 
4
4
  %h2 Endpoint tokens
5
5
  - tokens.each do |name, value|
6
- = token_to_label name, value
6
+ = token_to_label name, value
@@ -34,4 +34,4 @@
34
34
  - no_news_for = (Time.now - vars[:updated_at]).round(0)
35
35
  = formatted_duration no_news_for
36
36
  - else
37
- = "?"
37
+ = "?"
@@ -32,7 +32,7 @@ module RestFtpDaemon
32
32
  if !(pools.is_a? Hash)
33
33
  log_error "create_threads: one JobWorker is the minimum (#{pools.inspect}"
34
34
  end
35
- log_info "WorkerPool creating all workers with #{pools.to_hash.inspect}"
35
+ log_info "creating all workers with #{pools.to_hash.inspect}"
36
36
 
37
37
  # Start ConchitaWorker and ReporterWorker
38
38
  create_thread ConchitaWorker, :conchita
@@ -101,4 +101,4 @@ module RestFtpDaemon
101
101
  add_transaction_tracer :create_thread, category: :task
102
102
 
103
103
  end
104
- end
104
+ end
@@ -41,4 +41,4 @@ module RestFtpDaemon
41
41
  end
42
42
 
43
43
  end
44
- end
44
+ end
@@ -72,4 +72,4 @@ module RestFtpDaemon
72
72
  end
73
73
 
74
74
  end
75
- end
75
+ end
@@ -15,7 +15,7 @@ module RestFtpDaemon
15
15
  return "invalid timeout" unless @config[:timeout].to_i > 0
16
16
 
17
17
  # Log that
18
- log_info "JobWorker worker_init", {
18
+ log_info "worker_init", {
19
19
  pool: @pool,
20
20
  timeout: @config[:timeout]
21
21
  }
@@ -80,7 +80,7 @@ module RestFtpDaemon
80
80
  # If job status requires a retry, just restack it
81
81
  if !job.error
82
82
  # Processing successful
83
- log_info "job_result: finished with success"
83
+ log_info "job_result: finished successfully"
84
84
  worker_status WORKER_STATUS_FINISHED, job
85
85
 
86
86
  elsif error_not_eligible(job)
@@ -141,4 +141,4 @@ module RestFtpDaemon
141
141
  end
142
142
 
143
143
  end
144
- end
144
+ end