bricolage 5.23.3 → 5.24.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/bricolage/application.rb +160 -37
- data/lib/bricolage/commandlineapplication.rb +0 -0
- data/lib/bricolage/commandutils.rb +0 -20
- data/lib/bricolage/context.rb +19 -4
- data/lib/bricolage/job.rb +38 -38
- data/lib/bricolage/jobfile.rb +2 -1
- data/lib/bricolage/jobnet.rb +21 -3
- data/lib/bricolage/jobnetrunner.rb +102 -62
- data/lib/bricolage/logfilepath.rb +2 -2
- data/lib/bricolage/loglocator.rb +71 -0
- data/lib/bricolage/loglocatorbuilder.rb +29 -0
- data/lib/bricolage/psqldatasource.rb +1 -1
- data/lib/bricolage/s3writer.rb +22 -0
- data/lib/bricolage/version.rb +1 -1
- data/test/home/Gemfile.lock +41 -0
- data/test/home/config/bricolage.yml +3 -0
- data/test/home/config/development/database.yml +113 -0
- data/test/home/config/development/password.yml +6 -0
- data/test/home/config/development/variable.yml +7 -0
- data/test/home/config/streamingload.yml +8 -0
- data/test/home/log/20170117/subsys::job1/20170117_202014082/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::job1/20170117_202334852/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::job1/20170117_202405056/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::migrate/20170117_202548409/subsys-migrate.log +80 -0
- data/test/home/log/20170117/subsys::migrate/20170117_202636115/subsys-migrate.log +79 -0
- data/test/home/log/20170117/subsys::net1/20170117_203022461/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203022461/subsys-job2.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203022461/subsys-job3.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203022461/subsys-job4.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203102130/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203102130/subsys-job2.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203102130/subsys-job3.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203102130/subsys-job4.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203232033/subsys-job1.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203232033/subsys-job2.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203232033/subsys-job3.log +3 -0
- data/test/home/log/20170117/subsys::net1/20170117_203232033/subsys-job4.log +3 -0
- data/test/home/log/20170629/subsys::job1/20170629_155543985/subsys-job1.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_154308811/subsys-job1.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_154308811/subsys-job2.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_154308811/subsys-job3.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_154308811/subsys-job4.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_155330208/subsys-job1.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_155330208/subsys-job2.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_155330208/subsys-job3.log +3 -0
- data/test/home/log/20170629/subsys::net1/20170629_155330208/subsys-job4.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145523511/subsys-job1.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145523511/subsys-job2.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145523511/subsys-job3.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145523511/subsys-job4.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145545097/subsys-job1.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145545097/subsys-job2.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145545097/subsys-job3.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145545097/subsys-job4.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145615702/subsys-job1.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145615702/subsys-job2.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145615702/subsys-job3.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_145615702/subsys-job4.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_153437221/subsys-job1.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_153507519/subsys-job1.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_153507519/subsys-job2.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_153507519/subsys-job3.log +3 -0
- data/test/home/log/20170630/subsys::net1/20170630_153507519/subsys-job4.log +3 -0
- data/test/home/log/20170704/subsys::insert.sql.job/20170704_163033119/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert.sql.job/20170704_172410576/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert.sql.job/20170704_173130175/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert.sql.job/20170704_173201376/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert.sql/20170704_164143661/subsys-insert.sql.log +14 -0
- data/test/home/log/20170704/subsys::insert/20170704_164335210/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert/20170704_164344251/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::insert/20170704_164723299/subsys-insert.log +14 -0
- data/test/home/log/20170704/subsys::net1/20170704_162457694/subsys-job1.log +3 -0
- data/test/home/log/20170704/subsys::net1/20170704_162544282/subsys-job1.log +3 -0
- data/test/home/log/20170704/subsys::net1/20170704_162544282/subsys-job2.log +3 -0
- data/test/home/log/20170704/subsys::net1/20170704_162544282/subsys-job3.log +3 -0
- data/test/home/log/20170704/subsys::net1/20170704_162544282/subsys-job4.log +3 -0
- data/test/home/log/20170706/subsys::net1/20170706_201157129/subsys-job1.log +3 -0
- data/test/home/log/20170706/subsys::net1/20170706_201157129/subsys-job2.log +3 -0
- data/test/home/log/20170706/subsys::net1/20170706_201157129/subsys-job3.log +3 -0
- data/test/home/log/20170706/subsys::net1/20170706_201157129/subsys-job4.log +3 -0
- data/test/home/log/20170707/subsys::insert/20170707_020050817/subsys-insert.log +51 -0
- data/test/home/log/20170707/subsys::insert/20170707_020050817/subsys-insert.log.status +1 -0
- data/test/home/log/20170707/subsys::job1/20170707_020542902/subsys-job1.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_012252058/subsys-job1.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_012252058/subsys-job2.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_012252058/subsys-job3.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_012252058/subsys-job4.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_020039222/subsys-job1.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_020039222/subsys-job2.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_020039222/subsys-job3.log +3 -0
- data/test/home/log/20170707/subsys::net1/20170707_020039222/subsys-job4.log +3 -0
- data/test/home/revert.sh +7 -0
- data/test/home/s +1 -0
- metadata +125 -43
data/lib/bricolage/jobfile.rb
CHANGED
data/lib/bricolage/jobnet.rb
CHANGED
@@ -5,27 +5,45 @@ module Bricolage
|
|
5
5
|
|
6
6
|
# Represents "first" jobnet given by command line (e.g. bricolage-jobnet some.jobnet)
|
7
7
|
class RootJobNet
|
8
|
+
def RootJobNet.load_auto(ctx, path)
|
9
|
+
if path.extname == '.job'
|
10
|
+
load_single_job(ctx, path)
|
11
|
+
else
|
12
|
+
load(ctx, path)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
8
16
|
def RootJobNet.load(ctx, path)
|
9
|
-
root = new(JobNet::FileLoader.new(ctx), JobNet.load(path))
|
17
|
+
root = new(JobNet::FileLoader.new(ctx), JobNet.load(path), path)
|
10
18
|
root.load_recursive
|
11
19
|
root.fix
|
12
20
|
root
|
13
21
|
end
|
14
22
|
|
15
23
|
def RootJobNet.load_single_job(ctx, path)
|
16
|
-
root = new(JobNet::FileLoader.new(ctx), JobNet.load_single_job(path))
|
24
|
+
root = new(JobNet::FileLoader.new(ctx), JobNet.load_single_job(path), path)
|
17
25
|
root.load_recursive
|
18
26
|
root.fix
|
19
27
|
root
|
20
28
|
end
|
21
29
|
|
22
|
-
def initialize(jobnet_loader, start_jobnet)
|
30
|
+
def initialize(jobnet_loader, start_jobnet, path = nil)
|
23
31
|
@jobnet_loader = jobnet_loader
|
24
32
|
@start_jobnet = start_jobnet
|
33
|
+
@path = path
|
25
34
|
@jobnets = {start_jobnet.ref => start_jobnet}
|
26
35
|
@graph = nil
|
27
36
|
end
|
28
37
|
|
38
|
+
def id
|
39
|
+
return nil unless @path
|
40
|
+
@id ||= begin
|
41
|
+
subsys = @path.dirname.basename
|
42
|
+
base = @path.basename.to_s.sub(/\..*\z/, '')
|
43
|
+
"#{subsys}/#{base}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
29
47
|
attr_reader :start_jobnet
|
30
48
|
|
31
49
|
def each_jobnet(&block)
|
@@ -8,6 +8,7 @@ require 'bricolage/datasource'
|
|
8
8
|
require 'bricolage/variables'
|
9
9
|
require 'bricolage/eventhandlers'
|
10
10
|
require 'bricolage/logfilepath'
|
11
|
+
require 'bricolage/loglocatorbuilder'
|
11
12
|
require 'bricolage/logger'
|
12
13
|
require 'bricolage/exception'
|
13
14
|
require 'bricolage/version'
|
@@ -26,8 +27,6 @@ module Bricolage
|
|
26
27
|
@hooks = ::Bricolage
|
27
28
|
@jobnet_id = nil
|
28
29
|
@jobnet_start_time = Time.now
|
29
|
-
@job_start_time = nil
|
30
|
-
@log_path = nil
|
31
30
|
end
|
32
31
|
|
33
32
|
EXIT_SUCCESS = JobResult::EXIT_SUCCESS
|
@@ -37,24 +36,31 @@ module Bricolage
|
|
37
36
|
def main
|
38
37
|
opts = Options.new(self)
|
39
38
|
@hooks.run_before_option_parsing_hooks(opts)
|
40
|
-
opts.parse
|
39
|
+
opts.parse!(ARGV)
|
40
|
+
|
41
41
|
@ctx = Context.for_application(job_path: opts.jobnet_file, environment: opts.environment, global_variables: opts.global_variables)
|
42
|
-
|
43
|
-
|
44
|
-
jobnet =
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
42
|
+
opts.merge_saved_options(@ctx.load_system_options)
|
43
|
+
|
44
|
+
jobnet = RootJobNet.load_auto(@ctx, opts.jobnet_file)
|
45
|
+
@jobnet_id = jobnet.id
|
46
|
+
|
47
|
+
if opts.dump_options?
|
48
|
+
puts "jobnet-id=#{@jobnet_id}"
|
49
|
+
puts "jobnet-file=#{opts.jobnet_file}"
|
50
|
+
opts.option_pairs.each do |key, value|
|
51
|
+
puts "#{key}=#{value.inspect}"
|
49
52
|
end
|
53
|
+
exit EXIT_SUCCESS
|
54
|
+
end
|
55
|
+
|
50
56
|
queue = get_queue(opts)
|
51
57
|
if queue.locked?
|
52
58
|
raise ParameterError, "Job queue is still locked. If you are sure to restart jobnet, #{queue.unlock_help}"
|
53
59
|
end
|
54
60
|
unless queue.queued?
|
55
61
|
enqueue_jobs jobnet, queue
|
56
|
-
logger.info "jobs are queued."
|
57
62
|
end
|
63
|
+
|
58
64
|
if opts.list_jobs?
|
59
65
|
list_jobs queue
|
60
66
|
exit EXIT_SUCCESS
|
@@ -64,6 +70,8 @@ module Bricolage
|
|
64
70
|
puts "OK"
|
65
71
|
exit EXIT_SUCCESS
|
66
72
|
end
|
73
|
+
|
74
|
+
@log_locator_builder = LogLocatorBuilder.for_options(@ctx, opts.log_path_format, opts.log_s3_ds, opts.log_s3_key_format)
|
67
75
|
run_queue queue
|
68
76
|
exit EXIT_SUCCESS
|
69
77
|
rescue OptionError => ex
|
@@ -144,11 +152,11 @@ module Bricolage
|
|
144
152
|
|
145
153
|
def execute_job(ref, queue)
|
146
154
|
logger.debug "job #{ref}"
|
147
|
-
|
155
|
+
job_start_time = Time.now
|
148
156
|
job = Job.load_ref(ref, @ctx)
|
149
157
|
job.compile
|
150
158
|
@hooks.run_before_job_hooks(BeforeJobEvent.new(ref))
|
151
|
-
result = job.execute_in_process(
|
159
|
+
result = job.execute_in_process(log_locator: make_log_locator(ref, job_start_time))
|
152
160
|
@hooks.run_after_job_hooks(AfterJobEvent.new(result))
|
153
161
|
result
|
154
162
|
rescue Exception => ex
|
@@ -157,12 +165,11 @@ module Bricolage
|
|
157
165
|
JobResult.error(ex)
|
158
166
|
end
|
159
167
|
|
160
|
-
def
|
161
|
-
|
162
|
-
@log_path.format(
|
168
|
+
def make_log_locator(ref, job_start_time)
|
169
|
+
@log_locator_builder.build(
|
163
170
|
job_ref: ref,
|
164
171
|
jobnet_id: @jobnet_id,
|
165
|
-
job_start_time:
|
172
|
+
job_start_time: job_start_time,
|
166
173
|
jobnet_start_time: @jobnet_start_time
|
167
174
|
)
|
168
175
|
end
|
@@ -189,51 +196,40 @@ module Bricolage
|
|
189
196
|
File.basename($PROGRAM_NAME, '.*')
|
190
197
|
end
|
191
198
|
|
192
|
-
class Options
|
199
|
+
class Options < CommonApplicationOptions
|
193
200
|
def initialize(app)
|
194
201
|
@app = app
|
195
202
|
@environment = nil
|
203
|
+
@global_variables = Variables.new
|
196
204
|
@jobnet_files = nil
|
197
|
-
|
198
|
-
@
|
199
|
-
if path = ENV['BRICOLAGE_QUEUE_PATH']
|
200
|
-
@enable_queue = true
|
201
|
-
@queue_path = Pathname(path)
|
202
|
-
elsif ENV['BRICOLAGE_ENABLE_QUEUE']
|
203
|
-
@enable_queue = true
|
204
|
-
@queue_path = nil
|
205
|
-
else
|
206
|
-
@enable_queue = false
|
207
|
-
@queue_path = nil
|
208
|
-
end
|
205
|
+
|
206
|
+
@dump_options = false
|
209
207
|
@check_only = false
|
210
208
|
@list_jobs = false
|
211
|
-
@global_variables = Variables.new
|
212
|
-
@parser = OptionParser.new
|
213
|
-
define_options @parser
|
214
|
-
end
|
215
209
|
|
216
|
-
|
217
|
-
attr_reader :jobnet_file
|
218
|
-
attr_reader :log_path
|
219
|
-
|
220
|
-
attr_reader :local_state_dir
|
221
|
-
|
222
|
-
def enable_queue?
|
223
|
-
@enable_queue
|
210
|
+
init_options
|
224
211
|
end
|
225
212
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
213
|
+
def opts_default
|
214
|
+
super.merge({
|
215
|
+
'local-state-dir' => OptionValue.new('default value', nil),
|
216
|
+
'enable-queue' => OptionValue.new('default value', false),
|
217
|
+
'queue-path' => OptionValue.new('default value', '/tmp/bricolage')
|
218
|
+
})
|
230
219
|
end
|
220
|
+
private :opts_default
|
231
221
|
|
232
|
-
def
|
233
|
-
|
222
|
+
def opts_env
|
223
|
+
env = super
|
224
|
+
if ENV['BRICOLAGE_ENABLE_QUEUE']
|
225
|
+
env['enable-queue'] = OptionValue.new('env BRICOLAGE_ENABLE_QUEUE', true)
|
226
|
+
end
|
227
|
+
if path = ENV['BRICOLAGE_QUEUE_PATH']
|
228
|
+
env['queue-path'] = OptionValue.new('env BRICOLAGE_QUEUE_PATH', path)
|
229
|
+
end
|
230
|
+
env
|
234
231
|
end
|
235
|
-
|
236
|
-
attr_reader :global_variables
|
232
|
+
private :opts_env
|
237
233
|
|
238
234
|
def help
|
239
235
|
@parser.help
|
@@ -248,20 +244,17 @@ Options:
|
|
248
244
|
parser.on('-e', '--environment=NAME', "Sets execution environment. [default: #{Context::DEFAULT_ENV}]") {|env|
|
249
245
|
@environment = env
|
250
246
|
}
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
parser.on('--log-path=PATH', 'Log file path template.') {|path|
|
255
|
-
@log_path = LogFilePath.new(path)
|
256
|
-
}
|
247
|
+
|
248
|
+
define_common_options
|
249
|
+
|
257
250
|
parser.on('--local-state-dir=PATH', 'Stores local state in this path.') {|path|
|
258
|
-
@
|
251
|
+
@opts_cmdline['local-state-dir'] = OptionValue.new('--local-state-dir option', path)
|
259
252
|
}
|
260
253
|
parser.on('-Q', '--enable-queue', 'Enables job queue.') {
|
261
|
-
@
|
254
|
+
@opts_cmdline['enable-queue'] = OptionValue.new('--enable-queue option', true)
|
262
255
|
}
|
263
256
|
parser.on('--queue-path=PATH', 'Enables job queue with this path.') {|path|
|
264
|
-
@
|
257
|
+
@opts_cmdline['queue-path'] = OptionValue.new('--queue-path option', path)
|
265
258
|
}
|
266
259
|
parser.on('-c', '--check-only', 'Checks job parameters and quit without executing.') {
|
267
260
|
@check_only = true
|
@@ -273,6 +266,9 @@ Options:
|
|
273
266
|
name, value = name_value.split('=', 2)
|
274
267
|
@global_variables[name] = value
|
275
268
|
}
|
269
|
+
parser.on('--dump-options', 'Shows option parsing result and quit.') {
|
270
|
+
@dump_options = true
|
271
|
+
}
|
276
272
|
parser.on('--help', 'Shows this message and quit.') {
|
277
273
|
@app.puts parser.help
|
278
274
|
@app.exit 0
|
@@ -287,14 +283,58 @@ Options:
|
|
287
283
|
@parser.on(*args, &block)
|
288
284
|
end
|
289
285
|
|
290
|
-
def parse(argv)
|
291
|
-
@parser.parse!
|
286
|
+
def parse!(argv)
|
287
|
+
@parser.parse!(argv)
|
292
288
|
raise OptionError, "missing jobnet file" if argv.empty?
|
293
289
|
raise OptionError, "too many jobnet file" if argv.size > 1
|
294
|
-
@jobnet_file =
|
290
|
+
@jobnet_file = Pathname(argv.first)
|
291
|
+
build_common_options!
|
295
292
|
rescue OptionParser::ParseError => ex
|
296
293
|
raise OptionError, ex.message
|
297
294
|
end
|
295
|
+
|
296
|
+
attr_reader :environment
|
297
|
+
attr_reader :jobnet_file
|
298
|
+
|
299
|
+
attr_reader :global_variables
|
300
|
+
|
301
|
+
def dump_options?
|
302
|
+
@dump_options
|
303
|
+
end
|
304
|
+
|
305
|
+
def check_only?
|
306
|
+
@check_only
|
307
|
+
end
|
308
|
+
|
309
|
+
def list_jobs?
|
310
|
+
@list_jobs
|
311
|
+
end
|
312
|
+
|
313
|
+
def local_state_dir
|
314
|
+
@local_state_dir ||= begin
|
315
|
+
opt = @opts['local-state-dir']
|
316
|
+
Pathname(opt.value)
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
def enable_queue?
|
321
|
+
opt = @opts['enable-queue']
|
322
|
+
opt.value
|
323
|
+
end
|
324
|
+
|
325
|
+
def queue_path
|
326
|
+
if opt = @opts['queue-path']
|
327
|
+
Pathname(opt.value)
|
328
|
+
else
|
329
|
+
nil
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
def option_pairs
|
334
|
+
common_options.merge({
|
335
|
+
'environment' => OptionValue.new(nil, @environment)
|
336
|
+
})
|
337
|
+
end
|
298
338
|
end
|
299
339
|
end
|
300
340
|
|
@@ -14,14 +14,14 @@ module Bricolage
|
|
14
14
|
@template = template
|
15
15
|
end
|
16
16
|
|
17
|
-
Params = Struct.new(:job_ref, :jobnet_id, :job_start_time, :jobnet_start_time)
|
18
|
-
|
19
17
|
def format(job_ref:, jobnet_id:, job_start_time:, jobnet_start_time:)
|
20
18
|
return nil unless @template
|
21
19
|
params = Params.new(job_ref, jobnet_id, job_start_time, jobnet_start_time)
|
22
20
|
fill_template(@template, params)
|
23
21
|
end
|
24
22
|
|
23
|
+
Params = Struct.new(:job_ref, :jobnet_id, :job_start_time, :jobnet_start_time)
|
24
|
+
|
25
25
|
def fill_template(template, params)
|
26
26
|
template.gsub(/%\{\w+\}/) {|var|
|
27
27
|
case var
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Bricolage
|
2
|
+
class LogLocator
|
3
|
+
def LogLocator.empty
|
4
|
+
new(nil, nil)
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(path, s3_writer)
|
8
|
+
@path = path
|
9
|
+
@s3_writer = s3_writer
|
10
|
+
end
|
11
|
+
|
12
|
+
attr_reader :path
|
13
|
+
|
14
|
+
def s3_url
|
15
|
+
return nil unless @s3_writer
|
16
|
+
@s3_writer.url
|
17
|
+
end
|
18
|
+
|
19
|
+
def redirect_stdouts
|
20
|
+
return yield unless @path
|
21
|
+
FileUtils.mkdir_p File.dirname(@path)
|
22
|
+
@original_stdout = $stdout.dup
|
23
|
+
@original_stderr = $stderr.dup
|
24
|
+
begin
|
25
|
+
# Use 'w+' to make readable for retrieve_last_match_from_stderr
|
26
|
+
File.open(@path, 'w+') {|f|
|
27
|
+
f.sync = true
|
28
|
+
$stdout.reopen f
|
29
|
+
$stderr.reopen f
|
30
|
+
}
|
31
|
+
return yield
|
32
|
+
ensure
|
33
|
+
$stdout.reopen @original_stdout; @original_stdout.close
|
34
|
+
$stderr.reopen @original_stderr; @original_stderr.close
|
35
|
+
upload
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# CLUDGE: FIXME: We redirect stderr to the file, we can find error messages from there.
|
40
|
+
# Using a temporary file or Ruby SQL driver is **MUCH** better.
|
41
|
+
def self.slice_last_stderr(re, nth = 0)
|
42
|
+
return unless $stderr.stat.file?
|
43
|
+
$stderr.flush
|
44
|
+
f = $stderr.dup
|
45
|
+
matched = nil
|
46
|
+
begin
|
47
|
+
f.seek(0)
|
48
|
+
f.each do |line|
|
49
|
+
m = line.slice(re, nth)
|
50
|
+
matched = m if m
|
51
|
+
end
|
52
|
+
ensure
|
53
|
+
f.close
|
54
|
+
end
|
55
|
+
matched = matched.to_s.strip
|
56
|
+
matched.empty? ? nil : matched
|
57
|
+
end
|
58
|
+
|
59
|
+
def upload
|
60
|
+
return unless @path
|
61
|
+
return unless @s3_writer
|
62
|
+
# FIXME: Shows HTTP URL?
|
63
|
+
puts "bricolage: S3 log: #{s3_url}"
|
64
|
+
begin
|
65
|
+
@s3_writer.upload(path)
|
66
|
+
rescue => ex
|
67
|
+
puts "warning: S3 upload failed: #{s3_url}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bricolage/loglocator'
|
2
|
+
require 'bricolage/s3writer'
|
3
|
+
|
4
|
+
module Bricolage
|
5
|
+
class LogLocatorBuilder
|
6
|
+
def LogLocatorBuilder.for_options(ctx, log_path_format, s3_ds, s3_key_format)
|
7
|
+
ds = s3_ds ? ctx.get_data_source('s3', s3_ds) : nil
|
8
|
+
new(log_path_format, ds, s3_key_format)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(log_path_format, s3_ds, s3_key_format)
|
12
|
+
@log_path_format = log_path_format
|
13
|
+
@s3_ds = s3_ds
|
14
|
+
@s3_key_format = s3_key_format
|
15
|
+
end
|
16
|
+
|
17
|
+
def build(**params)
|
18
|
+
path = @log_path_format ? @log_path_format.format(**params) : nil
|
19
|
+
s3_writer =
|
20
|
+
if @s3_ds and @s3_key_format
|
21
|
+
key = @s3_key_format.format(**params)
|
22
|
+
S3Writer.new(@s3_ds, key)
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
LogLocator.new(path, s3_writer)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -71,7 +71,7 @@ module Bricolage
|
|
71
71
|
'--no-password',
|
72
72
|
*options,
|
73
73
|
env: get_psql_env
|
74
|
-
msg =
|
74
|
+
msg = LogLocator.slice_last_stderr(/^psql:.*?:\d+: ERROR: (.*)/, 1) unless st.success?
|
75
75
|
JobResult.for_process_status(st, msg)
|
76
76
|
}
|
77
77
|
end
|