xolo-server 2.1.0 → 2.2.2
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.
- checksums.yaml +4 -4
- data/data/client/xolo +16 -4
- data/data/com.pixar.xoloserver.plist +9 -2
- data/lib/xolo/core/base_classes/title.rb +113 -13
- data/lib/xolo/core/base_classes/version.rb +5 -6
- data/lib/xolo/core/exceptions.rb +3 -1
- data/lib/xolo/core/version.rb +1 -1
- data/lib/xolo/server/app.rb +1 -1
- data/lib/xolo/server/configuration.rb +4 -4
- data/lib/xolo/server/helpers/auth.rb +3 -3
- data/lib/xolo/server/helpers/maintenance.rb +194 -90
- data/lib/xolo/server/helpers/progress_streaming.rb +43 -10
- data/lib/xolo/server/mixins/title_jamf_access.rb +269 -51
- data/lib/xolo/server/mixins/version_jamf_access.rb +83 -34
- data/lib/xolo/server/routes/maint.rb +16 -15
- data/lib/xolo/server/routes/titles.rb +3 -0
- data/lib/xolo/server/routes.rb +1 -1
- data/lib/xolo/server/title.rb +28 -5
- data/lib/xolo/server/version.rb +71 -28
- metadata +2 -2
|
@@ -12,7 +12,11 @@ module Xolo
|
|
|
12
12
|
|
|
13
13
|
module Helpers
|
|
14
14
|
|
|
15
|
-
# Nightly
|
|
15
|
+
# Nightly maintenance tasks
|
|
16
|
+
# - accept lingering TEd EAs, if configured to
|
|
17
|
+
# - autorelease versions
|
|
18
|
+
# - clean up old versions
|
|
19
|
+
# - notify title maintainers of old unreleased pilots
|
|
16
20
|
#
|
|
17
21
|
# Also, alerts will be posted, and Emails will be sent to the
|
|
18
22
|
# admins who added versions that have been in pilot for more than
|
|
@@ -29,8 +33,27 @@ module Xolo
|
|
|
29
33
|
# Constants
|
|
30
34
|
#####################################
|
|
31
35
|
|
|
32
|
-
#
|
|
33
|
-
|
|
36
|
+
# TODO: ? use the next 3 values as defaults, and
|
|
37
|
+
# set them in the server config
|
|
38
|
+
|
|
39
|
+
# how often does our maint timer check to see if it should run maintenance?
|
|
40
|
+
# Slightly less than an hour means there will always be at least one check every
|
|
41
|
+
# hour, sometimes two.
|
|
42
|
+
MAINT_CHECK_INTERVAL = 3500
|
|
43
|
+
# MAINT_CHECK_INTERVAL = 600
|
|
44
|
+
|
|
45
|
+
# At what hour should the nightly maintenance run?
|
|
46
|
+
MAINT_HOUR = 2
|
|
47
|
+
# MAINT_HOUR = 10
|
|
48
|
+
|
|
49
|
+
# Maint won't run unless the last run was at least this many
|
|
50
|
+
# seconds ago.
|
|
51
|
+
# 23 hrs ago means that even if we check twice within the
|
|
52
|
+
# MAINT_HOUR, only the first one will run.
|
|
53
|
+
MAINT_MAX_FREQ_SECS = 23 * 3600
|
|
54
|
+
|
|
55
|
+
# the route we POST to, to start the nightly process.
|
|
56
|
+
TIMED_MAINT_TRIGGER_ROUTE = '/maint/maint-internal'
|
|
34
57
|
|
|
35
58
|
# on which day of the month should we send the unreleased pilot notifications?
|
|
36
59
|
UNRELEASED_PILOTS_NOTIFICATION_DAY = 1
|
|
@@ -39,7 +62,7 @@ module Xolo
|
|
|
39
62
|
# be automatically deleted this many days later.
|
|
40
63
|
# If not set in the server config, this is
|
|
41
64
|
# the default value.
|
|
42
|
-
# use 0 or less to disable
|
|
65
|
+
# use 0 or less to disable maintenance of deprecated versions
|
|
43
66
|
DFT_DEPRECATED_LIFETIME_DAYS = 30
|
|
44
67
|
|
|
45
68
|
# If a pilot has not been released in this many
|
|
@@ -55,71 +78,78 @@ module Xolo
|
|
|
55
78
|
# Module Methods
|
|
56
79
|
#####################################
|
|
57
80
|
|
|
58
|
-
# A mutex for the
|
|
59
|
-
#
|
|
60
|
-
# TODO: use Concrrent Ruby instead of Mutex
|
|
81
|
+
# A mutex for the maint process
|
|
61
82
|
#
|
|
62
83
|
# @return [Mutex] the mutex
|
|
63
84
|
#####################
|
|
64
|
-
def self.
|
|
65
|
-
@
|
|
85
|
+
def self.maint_mutex
|
|
86
|
+
@maint_mutex ||= Mutex.new
|
|
66
87
|
end
|
|
67
88
|
|
|
68
|
-
# nightly
|
|
89
|
+
# nightly maint is done by a Concurrent::TimerTask, which checks every
|
|
69
90
|
# hour to see if it should do anything.
|
|
70
91
|
#
|
|
71
|
-
# It will only do the
|
|
72
|
-
# (02:00 - 02:59)
|
|
92
|
+
# It will only do the maint if the current time is in the MAINT_HOUR hour
|
|
93
|
+
# (02:00 - 02:59 if MAINT_HOUR = 2)
|
|
73
94
|
#
|
|
74
|
-
# We trigger the
|
|
95
|
+
# We trigger the maint by POSTing to TIMED_MAINT_TRIGGER_ROUTE, so that it runs
|
|
75
96
|
# in the context of a request, having access to Title and Version instantiation.
|
|
76
97
|
#
|
|
77
98
|
# @return [Concurrent::TimerTask] the timed task to do log rotation
|
|
78
99
|
######################################
|
|
79
|
-
def self.
|
|
80
|
-
return @
|
|
100
|
+
def self.maint_timer_task
|
|
101
|
+
return @maint_timer_task if @maint_timer_task
|
|
102
|
+
|
|
103
|
+
@maint_timer_task =
|
|
104
|
+
Concurrent::TimerTask.new(execution_interval: MAINT_CHECK_INTERVAL) { post_to_start_maint }
|
|
81
105
|
|
|
82
|
-
|
|
83
|
-
Concurrent::TimerTask.new(execution_interval: 3600) { post_to_start_cleanup }
|
|
106
|
+
Xolo::Server.logger.info "Created Concurrent::TimerTask for nightly maintenance. Will check every #{MAINT_CHECK_INTERVAL} secs, and run during the hour of: #{MAINT_HOUR}"
|
|
84
107
|
|
|
85
|
-
|
|
86
|
-
@cleanup_timer_task
|
|
108
|
+
@maint_timer_task
|
|
87
109
|
end
|
|
88
110
|
|
|
89
|
-
# When was our last
|
|
90
|
-
# @return [Time] the time of the last
|
|
111
|
+
# When was our last maint?
|
|
112
|
+
# @return [Time] the time of the last maint, or the epoch if never
|
|
91
113
|
######################################
|
|
92
|
-
def self.
|
|
93
|
-
@
|
|
114
|
+
def self.last_maint
|
|
115
|
+
@last_maint ||= Time.at(0)
|
|
94
116
|
end
|
|
95
117
|
|
|
96
|
-
# Set the time of the last
|
|
97
|
-
# @param time [Time] the time of the last
|
|
98
|
-
# @return [Time] the time of the last
|
|
118
|
+
# Set the time of the last maint
|
|
119
|
+
# @param time [Time] the time of the last maint
|
|
120
|
+
# @return [Time] the time of the last maint
|
|
99
121
|
######################################
|
|
100
|
-
def self.
|
|
101
|
-
@
|
|
122
|
+
def self.last_maint=(time)
|
|
123
|
+
@last_maint = time
|
|
102
124
|
end
|
|
103
125
|
|
|
104
|
-
# post to the server to start the
|
|
105
|
-
# This is done so that the
|
|
126
|
+
# post to the server to start the maint process
|
|
127
|
+
# This is done so that the maint can run in the context of a request,
|
|
106
128
|
# having access to Title and Version instantiation.
|
|
107
129
|
#
|
|
108
|
-
# @param force [Boolean] force the
|
|
130
|
+
# @param force [Boolean] force the maint to run now
|
|
109
131
|
# @return [void]
|
|
110
132
|
######################################
|
|
111
|
-
def self.
|
|
133
|
+
def self.post_to_start_maint(force: false)
|
|
112
134
|
if Xolo::Server.shutting_down?
|
|
113
|
-
Xolo::Server.logger.info 'Not starting
|
|
135
|
+
Xolo::Server.logger.info 'Not starting maintenance, server is shutting down'
|
|
114
136
|
return
|
|
115
137
|
end
|
|
116
138
|
|
|
117
|
-
# only run the
|
|
118
|
-
# and the last one was more than
|
|
119
|
-
|
|
120
|
-
|
|
139
|
+
# only run the maintenance if it's during the MAINT_HOUR
|
|
140
|
+
# and the last one was more than MAINT_MAX_FREQ_SECS ago
|
|
141
|
+
if force
|
|
142
|
+
Xolo::Server.logger.info 'Maint: Starting now due to force'
|
|
143
|
+
|
|
144
|
+
elsif Time.now.hour == MAINT_HOUR && (Time.now - last_maint) > MAINT_MAX_FREQ_SECS
|
|
145
|
+
Xolo::Server.logger.info 'Maint: Starting nightly run now'
|
|
146
|
+
|
|
147
|
+
else
|
|
148
|
+
Xolo::Server.logger.info "Maint: Not starting, it isn't time"
|
|
149
|
+
return
|
|
150
|
+
end
|
|
121
151
|
|
|
122
|
-
uri = URI.parse "https://#{Xolo::Server::Helpers::Auth::IPV4_LOOPBACK}
|
|
152
|
+
uri = URI.parse "https://#{Xolo::Server::Helpers::Auth::IPV4_LOOPBACK}#{TIMED_MAINT_TRIGGER_ROUTE}"
|
|
123
153
|
https = Net::HTTP.new(uri.host, uri.port)
|
|
124
154
|
https.use_ssl = true
|
|
125
155
|
# The server cert may be self-signed and/or doesn't
|
|
@@ -130,32 +160,38 @@ module Xolo
|
|
|
130
160
|
request['Authorization'] = Xolo::Server::Helpers::Auth.internal_auth_token_header
|
|
131
161
|
|
|
132
162
|
response = https.request(request)
|
|
133
|
-
Xolo::Server.logger.info "
|
|
163
|
+
Xolo::Server.logger.info "Maintenance request response: #{response.code} #{response.body}"
|
|
134
164
|
end
|
|
135
165
|
|
|
136
|
-
#
|
|
166
|
+
# Perform maintenance tasks
|
|
137
167
|
# @return [void]
|
|
138
168
|
################################
|
|
139
|
-
def
|
|
169
|
+
def run_maint
|
|
140
170
|
if Xolo::Server.shutting_down?
|
|
141
|
-
log_info '
|
|
171
|
+
log_info 'Maint: Not starting maint, server is shutting down'
|
|
142
172
|
return
|
|
143
173
|
end
|
|
144
|
-
|
|
145
|
-
mutex = Xolo::Server::Helpers::Maintenance.
|
|
174
|
+
|
|
175
|
+
mutex = Xolo::Server::Helpers::Maintenance.maint_mutex
|
|
146
176
|
|
|
147
177
|
if mutex.locked?
|
|
148
|
-
log_warn '
|
|
178
|
+
log_warn 'Maint: already running, skipping this run'
|
|
149
179
|
return
|
|
150
180
|
end
|
|
151
181
|
mutex.lock
|
|
152
|
-
log_info '
|
|
182
|
+
log_info 'Maint: starting'
|
|
183
|
+
|
|
184
|
+
# instantiate all the titles once now, rather than in all the task methods
|
|
185
|
+
title_objects_for_maint = Xolo::Server::Title.all_titles.map { |t| instantiate_title t }
|
|
153
186
|
|
|
154
|
-
# add new
|
|
155
|
-
accept_title_editor_eas
|
|
156
|
-
|
|
187
|
+
# add new maintenance tasks/methods here
|
|
188
|
+
accept_title_editor_eas title_objects_for_maint
|
|
189
|
+
auto_release_versions title_objects_for_maint
|
|
190
|
+
cleanup_versions title_objects_for_maint
|
|
191
|
+
notify_admins_of_unreleased_pilots title_objects_for_maint
|
|
157
192
|
|
|
158
|
-
|
|
193
|
+
Xolo::Server::Helpers::Maintenance.last_maint = Time.now
|
|
194
|
+
log_info 'Maint: complete'
|
|
159
195
|
ensure
|
|
160
196
|
mutex&.unlock
|
|
161
197
|
end
|
|
@@ -164,38 +200,98 @@ module Xolo
|
|
|
164
200
|
# and auto accept them if we need to
|
|
165
201
|
# @return [void]
|
|
166
202
|
######################################
|
|
167
|
-
def accept_title_editor_eas
|
|
203
|
+
def accept_title_editor_eas(title_objects)
|
|
168
204
|
unless Xolo::Server.config.jamf_auto_accept_xolo_eas
|
|
169
|
-
|
|
205
|
+
log_debug 'Maint: The xolo server is not configured to auto-accept Title Editor EAs'
|
|
170
206
|
return
|
|
171
207
|
end
|
|
172
208
|
|
|
173
|
-
log_info '
|
|
209
|
+
log_info 'Maint: Looking for Title Editor EAs to auto-accept'
|
|
174
210
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
title_obj = instantiate_title title
|
|
211
|
+
title_objects.each do |title_obj|
|
|
212
|
+
title = title_obj.title
|
|
178
213
|
next if title_obj.subscribed?
|
|
179
214
|
next unless title_obj.jamf_patch_ea_awaiting_acceptance?
|
|
180
215
|
|
|
181
|
-
log_info "
|
|
216
|
+
log_info "Maint: Auto-accepting Title Editor EA for title '#{title}'"
|
|
182
217
|
title_obj.accept_jamf_patch_ea_via_api
|
|
183
218
|
rescue => e
|
|
184
|
-
log_error "
|
|
219
|
+
log_error "Maint: Error auto-accepting Title Editor EA for title '#{title}': #{e}"
|
|
185
220
|
end # Xolo::Server::Title.all_titles.each
|
|
186
221
|
|
|
187
|
-
log_info '
|
|
222
|
+
log_info 'Maint: Done with Title Editor EAs to auto-accept'
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Do any pending auto-releases
|
|
226
|
+
# @return [void]
|
|
227
|
+
##############################
|
|
228
|
+
def auto_release_versions(title_objects)
|
|
229
|
+
log_info 'Maint: Auto-releasing appropriate versions'
|
|
230
|
+
|
|
231
|
+
today = Date.today
|
|
232
|
+
|
|
233
|
+
# Loop thru the titles
|
|
234
|
+
title_objects.each do |title_obj|
|
|
235
|
+
title = title_obj.title
|
|
236
|
+
|
|
237
|
+
# nothing to do if its nil or 'none'
|
|
238
|
+
next if title_obj.auto_release_delay.nil? || title_obj.auto_release_delay == Xolo::NONE
|
|
239
|
+
|
|
240
|
+
# title must be subscribed and autopkg
|
|
241
|
+
# (enforced in xadm)
|
|
242
|
+
next unless title_obj.subscribed? && !title_obj.autopkg_recipe.pix_empty?
|
|
243
|
+
|
|
244
|
+
# title must have a non-negative integer for auto_release_delay,
|
|
245
|
+
# (number of days to wait before release)
|
|
246
|
+
# could be zero. (enforced in xadm)
|
|
247
|
+
# NOTE: it is stored as a string because it might be 'none'
|
|
248
|
+
# TODO: store none as nil, so we don't have to do the pix_integer check??
|
|
249
|
+
delay = title_obj.auto_release_delay.to_i
|
|
250
|
+
next unless title_obj.auto_release_delay&.pix_integer? && !delay.negative?
|
|
251
|
+
|
|
252
|
+
# Any version with this creation date should be released now, skip if not
|
|
253
|
+
# So if the auto_release_delay is 7, its today - 7 days.
|
|
254
|
+
# if the auto_release_delay is 0, its today - 0 days, aka today
|
|
255
|
+
creation_date_to_be_released_today = today - delay
|
|
256
|
+
|
|
257
|
+
# Find the newest pilot version whose creation date is
|
|
258
|
+
# creation_date_to_be_released_today or earlier
|
|
259
|
+
# That one should be released now.
|
|
260
|
+
vobj_to_release = nil
|
|
261
|
+
|
|
262
|
+
# version objects are in order of newest to oldest
|
|
263
|
+
title_obj.version_objects.each do |vobj|
|
|
264
|
+
next unless vobj.pilot?
|
|
265
|
+
next unless vobj.creation_date.to_date <= creation_date_to_be_released_today
|
|
266
|
+
|
|
267
|
+
vobj_to_release = vobj
|
|
268
|
+
break
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# no versions to release today
|
|
272
|
+
next unless vobj_to_release
|
|
273
|
+
|
|
274
|
+
# release it
|
|
275
|
+
log_debug "Maint: About to auto-release version '#{vobj_to_release.version}' of '#{title}' which came out #{vobj_to_release.creation_date}"
|
|
276
|
+
|
|
277
|
+
# releasing a version is done via the title obj, since it affects the status
|
|
278
|
+
# of all versions.
|
|
279
|
+
title_obj.release vobj_to_release.version
|
|
280
|
+
|
|
281
|
+
log_info "Maint: Auto-released version '#{vobj_to_release.version}' of '#{title}' which came out #{vobj_to_release.creation_date}", alert: true
|
|
282
|
+
end # each title
|
|
283
|
+
|
|
284
|
+
log_debug 'Maint: Done auto-releasing'
|
|
188
285
|
end
|
|
189
286
|
|
|
190
287
|
# Cleanup versions.
|
|
191
288
|
# @return [void]
|
|
192
289
|
################################
|
|
193
|
-
def cleanup_versions
|
|
194
|
-
log_info '
|
|
195
|
-
|
|
196
|
-
Xolo::Server::Title.all_titles.each do |title|
|
|
197
|
-
title_obj = instantiate_title title
|
|
290
|
+
def cleanup_versions(title_objects)
|
|
291
|
+
log_info 'Maint: Cleaning up deprecated and skipped versions'
|
|
198
292
|
|
|
293
|
+
# Loop thru the titles
|
|
294
|
+
title_objects.each do |title_obj|
|
|
199
295
|
title_obj.version_objects.each do |version|
|
|
200
296
|
if version.deprecated?
|
|
201
297
|
cleanup_deprecated_version version
|
|
@@ -203,12 +299,8 @@ module Xolo
|
|
|
203
299
|
cleanup_skipped_version version
|
|
204
300
|
end # case
|
|
205
301
|
end # each version
|
|
206
|
-
|
|
207
|
-
notify_admins_of_unreleased_pilots(title_obj)
|
|
208
302
|
end # each title
|
|
209
|
-
|
|
210
|
-
Xolo::Server::Helpers::Maintenance.last_cleanup = Time.now
|
|
211
|
-
log_info 'Cleanup: versions cleanup complete'
|
|
303
|
+
log_info 'Maint: Version cleanup complete'
|
|
212
304
|
end
|
|
213
305
|
|
|
214
306
|
# Cleanup a deprecated version.
|
|
@@ -223,7 +315,7 @@ module Xolo
|
|
|
223
315
|
days_deprecated = (Time.now - version.deprecation_date) / 86_400
|
|
224
316
|
return unless days_deprecated > deprecated_lifetime_days
|
|
225
317
|
|
|
226
|
-
log_info "
|
|
318
|
+
log_info "Maint: Deleting deprecated version '#{version.version}' of title '#{version.title}'"
|
|
227
319
|
version.delete
|
|
228
320
|
end
|
|
229
321
|
|
|
@@ -234,38 +326,50 @@ module Xolo
|
|
|
234
326
|
def cleanup_skipped_version(version)
|
|
235
327
|
return if Xolo::Server.config.keep_skipped_versions
|
|
236
328
|
|
|
237
|
-
log_info "
|
|
329
|
+
log_info "Maint: Deleting skipped version '#{version.version}' of title '#{version.title}'"
|
|
238
330
|
version.delete
|
|
239
331
|
end
|
|
240
332
|
|
|
241
333
|
# Notify the admins about unreleased pilots if needed
|
|
242
334
|
# @return [void]
|
|
243
335
|
################################
|
|
244
|
-
def notify_admins_of_unreleased_pilots(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
336
|
+
def notify_admins_of_unreleased_pilots(title_objects)
|
|
337
|
+
unless Time.now.day == UNRELEASED_PILOTS_NOTIFICATION_DAY
|
|
338
|
+
log_debug 'Maint: Not notifying admins of old pilots, wrong day of the month.'
|
|
339
|
+
return
|
|
340
|
+
end
|
|
341
|
+
unless unreleased_pilots_notification_days&.positive?
|
|
342
|
+
log_debug 'Maint: Not notifying admins of old pilots, unreleased_pilots_notification_days is not a positive integer'
|
|
343
|
+
return
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
log_info 'Maint: Notifying admins about old unreleased pilots'
|
|
248
347
|
|
|
249
|
-
|
|
250
|
-
|
|
348
|
+
title_objects.each do |title_obj|
|
|
349
|
+
next unless title_obj.latest_version
|
|
251
350
|
|
|
252
|
-
|
|
351
|
+
latest_vers_obj = instantiate_version title: title_obj, version: title_obj.latest_version
|
|
352
|
+
next unless latest_vers_obj.pilot?
|
|
253
353
|
|
|
254
|
-
|
|
354
|
+
days_in_pilot = ((Time.now - latest_vers_obj.creation_date) / 86_400).to_i
|
|
255
355
|
|
|
256
|
-
|
|
356
|
+
next unless days_in_pilot > unreleased_pilots_notification_days
|
|
257
357
|
|
|
258
|
-
|
|
259
|
-
send_alert alert_msg
|
|
358
|
+
alert_msg = "Maint: Notifying #{title_obj.contact_email} about unreleased pilot '#{latest_vers}' of title '#{title_obj.title}', in pilot for #{days_in_pilot} days"
|
|
260
359
|
|
|
261
|
-
|
|
360
|
+
log_info alert_msg
|
|
361
|
+
send_alert alert_msg
|
|
362
|
+
|
|
363
|
+
email_msg = <<~MSG
|
|
262
364
|
The newest version '#{latest_vers_obj.version}' of title '#{title_obj.title}' has been in pilot for #{days_in_pilot} days, which makes it seem like it's not going to be released.
|
|
263
365
|
|
|
264
366
|
To reduce clutter, please consider releasing it, deleting it, or deleting the whole title if it's no longer needed.
|
|
265
367
|
|
|
266
368
|
If this is intentional, you can ignore this monthly message.
|
|
267
|
-
|
|
268
|
-
|
|
369
|
+
MSG
|
|
370
|
+
send_email to: title_obj.contact_email, subject: 'Unreleased Pilot Notification', msg: email_msg
|
|
371
|
+
end # each title
|
|
372
|
+
log_info 'Maint: Done notifying admins about old unreleased pilots'
|
|
269
373
|
end
|
|
270
374
|
|
|
271
375
|
# how many days can a version be deprecated?
|
|
@@ -291,7 +395,7 @@ module Xolo
|
|
|
291
395
|
|
|
292
396
|
progress "Server Shutdown by #{session[:admin]}", log: :info
|
|
293
397
|
|
|
294
|
-
|
|
398
|
+
stop_maint_timer_task
|
|
295
399
|
stop_log_rotation_timer_task
|
|
296
400
|
shutdown_pkg_deletion_pool
|
|
297
401
|
wait_for_object_locks
|
|
@@ -316,12 +420,12 @@ module Xolo
|
|
|
316
420
|
system "/bin/launchctl unload #{SERVER_LAUNCHD_PLIST}"
|
|
317
421
|
end
|
|
318
422
|
|
|
319
|
-
# Stop the
|
|
423
|
+
# Stop the maintenance timer task
|
|
320
424
|
# @return [void]
|
|
321
425
|
################################
|
|
322
|
-
def
|
|
323
|
-
progress 'Stopping the
|
|
324
|
-
Xolo::Server::Helpers::Maintenance.
|
|
426
|
+
def stop_maint_timer_task
|
|
427
|
+
progress 'Stopping the maint timer task', log: :info
|
|
428
|
+
Xolo::Server::Helpers::Maintenance.maint_timer_task.shutdown
|
|
325
429
|
end
|
|
326
430
|
|
|
327
431
|
# Stop the log rotation timer task
|
|
@@ -174,16 +174,49 @@ module Xolo
|
|
|
174
174
|
def stream_progress(stream_file:, stream:)
|
|
175
175
|
log_debug "About to tail: usr/bin/tail -f -c +1 #{Shellwords.escape stream_file.to_s}"
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
177
|
+
begin
|
|
178
|
+
File.open(stream_file, 'r') do |file|
|
|
179
|
+
# Go straight to the end of the file if you only want new lines
|
|
180
|
+
file.seek(0, IO::SEEK_END)
|
|
181
|
+
|
|
182
|
+
heartbeat_counter = 0
|
|
183
|
+
loop do
|
|
184
|
+
# Check if the stream was closed by the user
|
|
185
|
+
break if stream.closed?
|
|
186
|
+
|
|
187
|
+
# Try to read the next line
|
|
188
|
+
# This won't block but will return nil if no line is ready
|
|
189
|
+
if line = file.gets
|
|
190
|
+
break if line.chomp == Xolo::Server::PROGRESS_COMPLETE
|
|
191
|
+
|
|
192
|
+
# Send the line to the browser immediately
|
|
193
|
+
stream << line
|
|
194
|
+
else
|
|
195
|
+
# If no new line, sleep for a fraction of a second
|
|
196
|
+
# This saves your computer's CPU from working too hard
|
|
197
|
+
sleep 0.1
|
|
198
|
+
heartbeat_counter += 1
|
|
199
|
+
if heartbeat_counter == 100 # every 10 secs
|
|
200
|
+
stream << ':heartbeat'
|
|
201
|
+
heartbeat_counter = 0
|
|
202
|
+
end
|
|
203
|
+
end # if
|
|
204
|
+
end # loop
|
|
205
|
+
end # file open
|
|
206
|
+
rescue IOError, Errno::ECONNRESET
|
|
207
|
+
nil
|
|
208
|
+
end # begin
|
|
209
|
+
|
|
210
|
+
# stdin, stdouterr, wait_thr = Open3.popen2e("/usr/bin/tail -f -c +1 #{Shellwords.escape stream_file.to_s}")
|
|
211
|
+
# stdin.close
|
|
212
|
+
|
|
213
|
+
# while line = stdouterr.gets
|
|
214
|
+
# break if line.chomp == Xolo::Server::PROGRESS_COMPLETE
|
|
215
|
+
|
|
216
|
+
# stream << line
|
|
217
|
+
# end
|
|
218
|
+
# stdouterr.close
|
|
219
|
+
# wait_thr.exit
|
|
187
220
|
# TODO: deal with wait_thr.value.exitstatus > 0 ?
|
|
188
221
|
end
|
|
189
222
|
|