rest-ftp-daemon 0.435.2 → 0.501.0

Sign up to get free protection for your applications and to get access to all the features.
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