rest-ftp-daemon 0.202.2 → 0.210.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.
@@ -3,15 +3,18 @@ require 'securerandom'
3
3
 
4
4
  module RestFtpDaemon
5
5
  class JobQueue < Queue
6
- attr_reader :queued
7
- attr_reader :popped
6
+ # attr_reader :queued
7
+ # attr_reader :popped
8
+
9
+ attr_reader :queue
10
+ attr_reader :jobs
8
11
 
9
12
  def initialize
10
13
  # Instance variables
11
- @queued = []
12
- @popped = []
14
+ @queue = []
15
+ @jobs = []
13
16
  @waiting = []
14
- @queued.taint # enable tainted communication
17
+ @queue.taint # enable tainted communication
15
18
  @waiting.taint
16
19
  self.taint
17
20
  @mutex = Mutex.new
@@ -47,9 +50,11 @@ module RestFtpDaemon
47
50
  end
48
51
 
49
52
  def generate_id
50
- rand(36**8).to_s(36)
51
- @last_id ||= 0
52
- @last_id += 1
53
+ # rand(36**8).to_s(36)
54
+ @mutex.synchronize do
55
+ @last_id ||= 0
56
+ @last_id += 1
57
+ end
53
58
  prefixed_id @last_id
54
59
  end
55
60
 
@@ -76,33 +81,38 @@ module RestFtpDaemon
76
81
  end
77
82
  end
78
83
 
79
- def sorted_by_status status
80
- # Just use the base if filter is empty
84
+ def filter_jobs status
85
+ # No status filter: return all execept queued
81
86
  if status.empty?
82
- elements = all
87
+ @jobs.reject { |job| job.status == JOB_STATUS_QUEUED }
88
+
89
+ # Status filtering: only those jobs
83
90
  else
84
- elements = all.select { |item| item.status == status.to_sym }
85
- end
91
+ @jobs.select { |job| job.status == status.to_sym }
86
92
 
87
- # Sort these elements
88
- elements.sort_by do |item|
89
- w = JOB_WEIGHTS[item.status] || 0
90
- [ w, item.wid.to_s, item.updated_at.to_s]
91
93
  end
92
-
93
94
  end
94
95
 
95
96
  def counts_by_status
96
97
  statuses = {}
97
- all.group_by { |job| job.status }.map { |status, jobs| statuses[status] = jobs.size }
98
+ @jobs.group_by { |job| job.status }.map { |status, jobs| statuses[status] = jobs.size }
98
99
  statuses
99
100
  end
100
101
 
101
- def all
102
- @queued + @popped
102
+ def jobs # change for accessor
103
+ @jobs
104
+ end
105
+
106
+ def jobs_count
107
+ @jobs.length
103
108
  end
104
- def all_size
105
- @queued.length + @popped.length
109
+
110
+ def queued_ids
111
+ @queue.collect(&:id)
112
+ end
113
+
114
+ def jobs_ids
115
+ @jobs.collect(&:id)
106
116
  end
107
117
 
108
118
  def find_by_id id, prefixed = false
@@ -110,8 +120,8 @@ module RestFtpDaemon
110
120
  id = prefixed_id(id) if prefixed
111
121
  info "find_by_id (#{id}, #{prefixed}) > #{id}"
112
122
 
113
- # Search in both queues
114
- @queued.select { |item| item.id == id }.last || @popped.select { |item| item.id == id }.last
123
+ # Search in jobs queues
124
+ @jobs.select { |item| item.id == id }.last
115
125
  end
116
126
 
117
127
  def push job
@@ -121,11 +131,17 @@ module RestFtpDaemon
121
131
 
122
132
  @mutex.synchronize do
123
133
  # Push job into the queue
124
- @queued.push job
134
+ @queue.push job
135
+
136
+ # Store the job into the global jobs list
137
+ @jobs.push job
125
138
 
126
- # Tell the job it's been queued
139
+ # Inform the job that it's been queued
127
140
  job.set_queued if job.respond_to? :set_queued
128
141
 
142
+ # Refresh queue order
143
+ sort_queue!
144
+
129
145
  # Try to wake a worker up
130
146
  begin
131
147
  t = @waiting.shift
@@ -139,17 +155,19 @@ module RestFtpDaemon
139
155
  alias enq push
140
156
 
141
157
  def pop(non_block=false)
142
- # info "JobQueue.pop"
143
158
  @mutex.synchronize do
144
159
  while true
145
- if @queued.empty?
160
+ if @queue.empty?
146
161
  # info "JobQueue.pop: empty"
147
162
  raise ThreadError, "queue empty" if non_block
148
163
  @waiting.push Thread.current
149
164
  @mutex.sleep
150
165
  else
151
- # info "JobQueue.pop: great, I'm not empty!!"
152
- return pick_one
166
+ # Refresh queue order
167
+ # sort_queue!
168
+
169
+ # Extract the heaviest item in the queue
170
+ return @queue.pop
153
171
  end
154
172
  end
155
173
  end
@@ -158,99 +176,89 @@ module RestFtpDaemon
158
176
  alias deq pop
159
177
 
160
178
  def empty?
161
- @queued.empty?
179
+ @queue.empty?
162
180
  end
163
181
 
164
182
  def clear
165
- @queued.clear
183
+ @queue.clear
166
184
  end
167
185
 
168
186
  def num_waiting
169
187
  @waiting.size
170
188
  end
171
189
 
172
- def ordered_queue
173
- @mutex_counters.synchronize do
174
- @queued.sort_by { |item| [item.priority.to_i, - item.id.to_i] }
175
- end
176
- end
177
-
178
- # def ordered_popped
179
- # @mutex_counters.synchronize do
180
- # @popped.sort_by { |item| [ item.wid.to_s, item.updated_at] }
181
- # # @popped.sort_by { |item| [item.status.to_s, item.wid.to_s, item.updated_at, - item.id.to_i] }
182
- # end
183
- # end
184
-
185
190
  protected
186
191
 
187
192
  def prefixed_id id
188
193
  "#{@prefix}.#{id}"
189
194
  end
190
195
 
196
+ def sort_queue!
197
+ @mutex_counters.synchronize do
198
+ @queue.sort_by! &:weight
199
+ end
200
+ end
201
+
191
202
  def conchita_loop
192
203
  info "conchita starting with: #{@conchita.inspect}"
193
204
  loop do
194
- # Do the cleanup
195
- conchita_clean JOB_STATUS_FINISHED
196
- conchita_clean :failed
205
+ # Do the cleanup locking the queues
206
+ # info "conchita: cleanup expired jobs"
207
+ @mutex.synchronize do
208
+ conchita_clean JOB_STATUS_FINISHED
209
+ conchita_clean JOB_STATUS_FAILED
210
+ conchita_clean JOB_STATUS_QUEUED
211
+ end
197
212
  sleep @conchita[:timer]
198
213
  end
199
214
  end
200
215
 
201
- def conchita_clean status
216
+ def conchita_clean status # FIXME: clean both @jobs and @queue
202
217
  # Init
203
218
  return if status.nil?
204
- key = "clean_#{status.to_s}"
205
219
 
206
220
  # Read config state
207
- max_age = @conchita[key.to_s]
208
- return if [nil, false].include? max_age
221
+ maxage = @conchita["clean_#{status.to_s}"] || 0
222
+ #info "conchita_clean status[#{status.to_s}] \t maxage[#{maxage}] s"
223
+ return unless maxage > 0
209
224
 
210
225
  # Delete jobs from the queue if their status is (status)
211
- @popped.delete_if do |job|
212
- # Skip it if wrong status
226
+ @jobs.delete_if do |job|
227
+
228
+ # Skip if wrong status
213
229
  next unless job.status == status.to_sym
214
230
 
215
- # Skip it if updated_at invalid
216
- updated_at = job.updated_at
217
- next if updated_at.nil?
231
+ # Skip if updated_at invalid
232
+ next if job.updated_at.nil?
218
233
 
219
- # Skip it if not aged enough yet
220
- age = Time.now - updated_at
221
- next if age < max_age
234
+ # Skip if not aged enough yet
235
+ age = Time.now - job.updated_at
236
+ next if age < maxage
222
237
 
223
238
  # Ok, we have to clean it up ..
224
- info "conchita_clean #{status.inspect} max_age:#{max_age} job:#{job.id} age:#{age}"
239
+ info "conchita_clean status[#{status.to_s}] maxage[#{maxage}] job[#{job.id}] age[#{age}]"
240
+
241
+ # Remove it from the queue if present
242
+ job_in_queue = @queue.delete job
243
+ info " removed queued job [#{job.id}]" unless job_in_queue.nil?
244
+
245
+ # Accept to delete it from @jobs
225
246
  true
226
247
  end
227
248
 
228
249
  end
229
250
 
230
- def pick_one # called inside a mutex/sync
231
- # Sort jobs by priority and get the biggest one
232
- picked = ordered_queue.last
233
- return nil if picked.nil?
234
-
235
- # Move it away from the queue to the @popped array
236
- @queued.delete_if { |item| item == picked }
237
- @popped.push picked
238
-
239
- # Return picked
240
- #info "JobQueue.pick_one: #{picked.id}"
241
- picked
242
- end
243
251
 
244
252
  private
245
253
 
246
- def info message, context = {}
254
+ def info message, lines = []
247
255
  return if @logger.nil?
248
256
 
249
- # Inject context
250
- context[:origin] = self.class
251
-
252
257
  # Forward to logger
253
- @logger.info_with_id message, context
258
+ @logger.info_with_id message,
259
+ id: @id,
260
+ lines: lines,
261
+ origin: self.class.to_s
254
262
  end
255
263
 
256
264
  end
@@ -9,8 +9,10 @@ class Logger
9
9
  context[:level] ||= 0
10
10
 
11
11
  # Common message header
12
- field_id = "%#{-DEFAULT_LOGS_ID_LEN.to_i}s" % context[:id].to_s
13
- prefix = "#{field_id}\t#{' '*(context[:level].to_i+1)}"
12
+ field_wid = "%#{-LOG_COL_WID.to_i}s" % context[:wid].to_s
13
+ field_jid = "%#{-LOG_COL_JID.to_i}s" % context[:jid].to_s
14
+ field_id = "%#{-LOG_COL_ID.to_i}s" % context[:id].to_s
15
+ prefix = "#{field_wid} \t#{field_jid} \t#{field_id}\t#{' '*(context[:level].to_i+1)}"
14
16
 
15
17
  # Send main message
16
18
  add Logger::INFO, prefix + message.to_s
@@ -19,7 +21,7 @@ class Logger
19
21
  context[:lines].each do |line|
20
22
  line.strip!
21
23
  next if line.empty?
22
- add Logger::INFO, prefix + ' | ' + line[0..DEFAULT_LOGS_TRIM_LINE]
24
+ add Logger::INFO, prefix + ' | ' + line[0..LOG_TRIM_LINE]
23
25
  end if context[:lines].is_a? Enumerable
24
26
 
25
27
  end
@@ -21,10 +21,11 @@ module RestFtpDaemon
21
21
  # Create the logger and return it
22
22
  logger = Logger.new(logfile, 'daily') #, 10, 1024000)
23
23
  logger.progname = pipe.to_s.upcase
24
+
25
+ # And the formatter
24
26
  logger.formatter = proc do |severity, datetime, progname, message|
25
- # stamp = Time.now.strftime("%Y-%m-%d %H:%M:%S")
26
27
  stamp = datetime.strftime("%Y-%m-%d %H:%M:%S")
27
- field_pipe = "%-#{DEFAULT_LOGS_PIPE_LEN.to_i}s" % progname
28
+ field_pipe = "%-#{LOG_PIPE_LEN.to_i}s" % progname
28
29
  "#{stamp}\t#{field_pipe}\t#{message}\n"
29
30
  end
30
31
 
@@ -1,5 +1,3 @@
1
- require 'net/http'
2
-
3
1
  module RestFtpDaemon
4
2
  class Notification
5
3
  attr_accessor :job_id
@@ -13,28 +11,21 @@ module RestFtpDaemon
13
11
  def initialize url, params
14
12
  # Generate a random key
15
13
  @id = Helpers.identifier(NOTIFY_IDENTIFIER_LEN)
14
+ @jid = nil
16
15
 
17
16
  # Logger
18
17
  @logger = RestFtpDaemon::LoggerPool.instance.get :notify
19
18
 
20
19
  # Check context
21
-
22
20
  if url.nil?
23
21
  info "skipping (missing url): #{params.inspect}"
24
22
  return
25
-
26
23
  elsif params[:event].nil?
27
24
  info "skipping (missing event): #{params.inspect}"
28
25
  return
29
-
30
- else
31
- #info "created: OK"
32
- # info "created: #{params.class}"
33
- info "created #{params.inspect}"
34
-
35
26
  end
36
27
 
37
- # Params
28
+ # Build body and extract job ID if provided
38
29
  body = {
39
30
  id: params[:id].to_s,
40
31
  signal: "#{NOTIFY_PREFIX}.#{params[:event].to_s}",
@@ -42,6 +33,9 @@ module RestFtpDaemon
42
33
  host: Settings.host.to_s,
43
34
  }
44
35
  body[:status] = params[:status] if params[:status].is_a? Enumerable
36
+ @jid = params[:id]
37
+ info "initialized"
38
+
45
39
 
46
40
  # Send message in a thread
47
41
  Thread.new do |thread|
@@ -55,6 +49,7 @@ module RestFtpDaemon
55
49
  data = body.to_json
56
50
  info "sending #{data}"
57
51
 
52
+
58
53
  # Prepare HTTP client
59
54
  http = Net::HTTP.new uri.host, uri.port
60
55
  # http.initialize_http_header({'User-Agent' => APP_NAME})
@@ -68,7 +63,7 @@ module RestFtpDaemon
68
63
  if response_lines.size > 1
69
64
  human_size = Helpers.format_bytes(response.body.bytesize, "B")
70
65
  #human_size = 0
71
- info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", lines: response_lines
66
+ info "received [#{response.code}] #{human_size} (#{response_lines.size} lines)", response_lines
72
67
  else
73
68
  info "received [#{response.code}] #{response.body.strip}"
74
69
  end
@@ -79,14 +74,14 @@ module RestFtpDaemon
79
74
 
80
75
  protected
81
76
 
82
- def info message, context = {}
77
+ def info message, lines = []
83
78
  return if @logger.nil?
84
79
 
85
- # Inject context
86
- context[:id] = @id
87
- context[:origin] = self.class
88
-
89
- @logger.info_with_id message, context
80
+ @logger.info_with_id message,
81
+ id: @id,
82
+ jid: @jid,
83
+ lines: lines,
84
+ origin: self.class.to_s
90
85
  end
91
86
 
92
87
  end
@@ -26,7 +26,7 @@
26
26
 
27
27
  .row
28
28
  #box-jobs.col-md-12
29
- = render :dashboard_jobs, {jobs: jobs, only: only}
29
+ = render :dashboard_jobs, {current: current, queue: queue, only: only}
30
30
 
31
31
  .row
32
32
  #box-tokens.col-md-6
@@ -1,9 +1,10 @@
1
- - count_all = $queue.all_size
1
+ - count_all = $queue.jobs_count
2
2
  - counts_by_status = $queue.counts_by_status
3
+ -# jobs_without_queue = jobs
3
4
 
4
5
 
5
6
  %h2
6
- Jobs &nbsp;
7
+ Jobs table &nbsp;
7
8
 
8
9
  .btn-group.btn-group-md
9
10
  - klass = only.empty? ? "btn-info" : ""
@@ -16,6 +17,7 @@
16
17
  #{status} (#{count})
17
18
 
18
19
  %table.table.table-striped.table-hover.table-condensed#jobs
20
+
19
21
  %thead
20
22
  %tr
21
23
  %th ID
@@ -23,13 +25,23 @@
23
25
  %th source
24
26
  %th <=>
25
27
  %th target
26
- %th date
28
+ %th queued
27
29
  %th{width: 220} status
28
30
  %th{width: 150} error
29
31
  %th.text-right size
30
32
  %th.text-right bitrate
31
33
  %th info
32
34
 
33
- - if true
34
- %tbody.current
35
- = render :dashboard_table, {jobs: jobs}
35
+
36
+ - unless queue.empty?
37
+ %tbody.jobs
38
+ = render :dashboard_table, {jobs: queue.reverse}
39
+
40
+ %thead
41
+ %tr
42
+ %td{colspan: 13}
43
+ %br
44
+
45
+ - unless current.empty?
46
+ %tbody.jobs
47
+ = render :dashboard_table, {jobs: current.reverse}