jamie 0.1.0.alpha18 → 0.1.0.alpha19
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.
- data/lib/jamie/driver/dummy.rb +17 -13
- data/lib/jamie/version.rb +1 -1
- data/lib/jamie.rb +343 -106
- data/spec/jamie_spec.rb +79 -59
- metadata +2 -2
data/lib/jamie/driver/dummy.rb
CHANGED
@@ -27,33 +27,37 @@ module Jamie
|
|
27
27
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
28
28
|
class Dummy < Jamie::Driver::Base
|
29
29
|
|
30
|
-
|
30
|
+
default_config 'sleep', 0
|
31
|
+
|
32
|
+
def create(state)
|
31
33
|
state['my_id'] = "#{instance.name}-#{Time.now.to_i}"
|
32
|
-
report(:create,
|
34
|
+
report(:create, state)
|
33
35
|
end
|
34
36
|
|
35
|
-
def converge(
|
36
|
-
report(:converge,
|
37
|
+
def converge(state)
|
38
|
+
report(:converge, state)
|
37
39
|
end
|
38
40
|
|
39
|
-
def setup(
|
40
|
-
report(:setup,
|
41
|
+
def setup(state)
|
42
|
+
report(:setup, state)
|
41
43
|
end
|
42
44
|
|
43
|
-
def verify(
|
44
|
-
report(:verify,
|
45
|
+
def verify(state)
|
46
|
+
report(:verify, state)
|
45
47
|
end
|
46
48
|
|
47
|
-
def destroy(
|
48
|
-
report(:destroy,
|
49
|
+
def destroy(state)
|
50
|
+
report(:destroy, state)
|
49
51
|
state.delete('my_id')
|
50
52
|
end
|
51
53
|
|
52
54
|
private
|
53
55
|
|
54
|
-
def report(action,
|
55
|
-
|
56
|
-
"instance=#{instance} with state=#{state}"
|
56
|
+
def report(action, state)
|
57
|
+
info("[Dummy] Action ##{action} called on " +
|
58
|
+
"instance=#{instance} with state=#{state}")
|
59
|
+
sleep(config['sleep'].to_f) if config['sleep'].to_f > 0.0
|
60
|
+
debug("[Dummy] Action ##{action} completed (#{config['sleep']}s).")
|
57
61
|
end
|
58
62
|
end
|
59
63
|
end
|
data/lib/jamie/version.rb
CHANGED
data/lib/jamie.rb
CHANGED
@@ -17,11 +17,13 @@
|
|
17
17
|
# limitations under the License.
|
18
18
|
|
19
19
|
require 'base64'
|
20
|
+
require 'benchmark'
|
20
21
|
require 'delegate'
|
21
22
|
require 'digest'
|
22
23
|
require 'erb'
|
23
24
|
require 'fileutils'
|
24
25
|
require 'json'
|
26
|
+
require 'logger'
|
25
27
|
require 'mixlib/shellout'
|
26
28
|
require 'net/https'
|
27
29
|
require 'net/scp'
|
@@ -36,11 +38,22 @@ require 'jamie/version'
|
|
36
38
|
|
37
39
|
module Jamie
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
41
|
+
class << self
|
42
|
+
|
43
|
+
attr_accessor :logger
|
44
|
+
|
45
|
+
# Returns the root path of the Jamie gem source code.
|
46
|
+
#
|
47
|
+
# @return [Pathname] root path of gem
|
48
|
+
def source_root
|
49
|
+
@source_root ||= Pathname.new(File.expand_path('../../', __FILE__))
|
50
|
+
end
|
51
|
+
|
52
|
+
def default_logger
|
53
|
+
env_log = ENV['JAMIE_LOG'] && ENV['JAMIE_LOG'].downcase.to_sym
|
54
|
+
|
55
|
+
Logger.new(:console => STDOUT, :level => env_log)
|
56
|
+
end
|
44
57
|
end
|
45
58
|
|
46
59
|
# Base configuration class for Jamie. This class exposes configuration such
|
@@ -58,11 +71,8 @@ module Jamie
|
|
58
71
|
# Default path to the Jamie YAML file
|
59
72
|
DEFAULT_YAML_FILE = File.join(Dir.pwd, '.jamie.yml').freeze
|
60
73
|
|
61
|
-
# Default log level verbosity
|
62
|
-
DEFAULT_LOG_LEVEL = :info
|
63
|
-
|
64
74
|
# Default driver plugin to use
|
65
|
-
DEFAULT_DRIVER_PLUGIN = "
|
75
|
+
DEFAULT_DRIVER_PLUGIN = "dummy".freeze
|
66
76
|
|
67
77
|
# Default base path which may contain `data_bags/` directories
|
68
78
|
DEFAULT_TEST_BASE_PATH = File.join(Dir.pwd, 'test/integration').freeze
|
@@ -92,7 +102,7 @@ module Jamie
|
|
92
102
|
# suite combinations
|
93
103
|
def instances
|
94
104
|
@instances ||= Collection.new(suites.map { |suite|
|
95
|
-
platforms.map { |platform|
|
105
|
+
platforms.map { |platform| new_instance(suite, platform) }
|
96
106
|
}.flatten)
|
97
107
|
end
|
98
108
|
|
@@ -103,7 +113,10 @@ module Jamie
|
|
103
113
|
|
104
114
|
# @return [Symbol] log level verbosity
|
105
115
|
def log_level
|
106
|
-
@log_level ||=
|
116
|
+
@log_level ||= begin
|
117
|
+
ENV['JAMIE_LOG'] && ENV['JAMIE_LOG'].downcase.to_sym ||
|
118
|
+
Jamie::DEFAULT_LOG_LEVEL
|
119
|
+
end
|
107
120
|
end
|
108
121
|
|
109
122
|
# @return [String] base path that may contain a common `data_bags/`
|
@@ -160,15 +173,45 @@ module Jamie
|
|
160
173
|
end
|
161
174
|
|
162
175
|
def new_platform(hash)
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
176
|
+
Platform.new(hash)
|
177
|
+
end
|
178
|
+
|
179
|
+
def new_driver(hash)
|
180
|
+
hash['driver_config'] ||= Hash.new
|
181
|
+
hash['driver_config']['jamie_root'] = jamie_root
|
182
|
+
Driver.for_plugin(hash['driver_plugin'], hash['driver_config'])
|
168
183
|
end
|
169
184
|
|
170
|
-
def
|
171
|
-
|
185
|
+
def new_instance(suite, platform)
|
186
|
+
log_root = File.expand_path(File.join(jamie_root, ".jamie", "logs"))
|
187
|
+
platform_hash = platform_driver_hash(platform.name)
|
188
|
+
driver = new_driver(merge_driver_hash(platform_hash))
|
189
|
+
FileUtils.mkdir_p(log_root)
|
190
|
+
|
191
|
+
Instance.new(
|
192
|
+
'suite' => suite,
|
193
|
+
'platform' => platform,
|
194
|
+
'driver' => driver,
|
195
|
+
'jr' => Jr.new(suite.name),
|
196
|
+
'logger' => new_instance_logger(log_root)
|
197
|
+
)
|
198
|
+
end
|
199
|
+
|
200
|
+
def platform_driver_hash(platform_name)
|
201
|
+
h = yaml['platforms'].find { |p| p['name'] == platform_name } || Hash.new
|
202
|
+
|
203
|
+
h.select { |key, value| %w(driver_plugin driver_config).include?(key) }
|
204
|
+
end
|
205
|
+
|
206
|
+
def new_instance_logger(log_root)
|
207
|
+
level = Util.to_logger_level(self.log_level)
|
208
|
+
|
209
|
+
lambda do |name|
|
210
|
+
logfile = File.join(log_root, "#{name}.log")
|
211
|
+
|
212
|
+
Logger.new(:stdout => STDOUT, :logdev => logfile,
|
213
|
+
:level => level, :progname => name)
|
214
|
+
end
|
172
215
|
end
|
173
216
|
|
174
217
|
def yaml
|
@@ -194,8 +237,12 @@ module Jamie
|
|
194
237
|
end
|
195
238
|
end
|
196
239
|
|
197
|
-
def
|
198
|
-
|
240
|
+
def jamie_root
|
241
|
+
File.dirname(yaml_file)
|
242
|
+
end
|
243
|
+
|
244
|
+
def merge_driver_hash(driver_hash)
|
245
|
+
default_driver_hash.rmerge(common_driver_hash.rmerge(driver_hash))
|
199
246
|
end
|
200
247
|
|
201
248
|
def calculate_roles_path(suite_name)
|
@@ -230,15 +277,143 @@ module Jamie
|
|
230
277
|
end
|
231
278
|
end
|
232
279
|
|
233
|
-
def
|
234
|
-
{ 'driver_plugin' => DEFAULT_DRIVER_PLUGIN }
|
280
|
+
def default_driver_hash
|
281
|
+
{ 'driver_plugin' => DEFAULT_DRIVER_PLUGIN, 'driver_config' => {} }
|
235
282
|
end
|
236
283
|
|
237
|
-
def
|
284
|
+
def common_driver_hash
|
238
285
|
yaml.select { |key, value| %w(driver_plugin driver_config).include?(key) }
|
239
286
|
end
|
240
287
|
end
|
241
288
|
|
289
|
+
# Default log level verbosity
|
290
|
+
DEFAULT_LOG_LEVEL = :info
|
291
|
+
|
292
|
+
# Logging implementation for Jamie. By default the console/stdout output will
|
293
|
+
# be displayed differently than the file log output. Therefor, this class
|
294
|
+
# wraps multiple loggers that conform to the stdlib `Logger` class behavior.
|
295
|
+
#
|
296
|
+
# @author Fletcher Nichol <fnichol@nichol.ca>
|
297
|
+
class Logger
|
298
|
+
|
299
|
+
include ::Logger::Severity
|
300
|
+
|
301
|
+
def initialize(options = {})
|
302
|
+
@loggers = []
|
303
|
+
@loggers << logdev_logger(options[:logdev]) if options[:logdev]
|
304
|
+
@loggers << stdout_logger(options[:stdout]) if options[:stdout]
|
305
|
+
@loggers << stdout_logger(STDOUT) if @loggers.empty?
|
306
|
+
|
307
|
+
self.progname = options[:progname] || "Jamie"
|
308
|
+
self.level = options[:level] || default_log_level
|
309
|
+
end
|
310
|
+
|
311
|
+
%w{ level progname datetime_format debug? info? error? warn? fatal?
|
312
|
+
}.each do |meth|
|
313
|
+
define_method(meth) do |*args|
|
314
|
+
@loggers.first.public_send(meth, *args)
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
%w{ level= progname= datetime_format= add <<
|
319
|
+
banner debug info error warn fatal unknown close
|
320
|
+
}.map(&:to_sym).each do |meth|
|
321
|
+
define_method(meth) do |*args|
|
322
|
+
result = nil
|
323
|
+
@loggers.each { |l| result = l.public_send(meth, *args) }
|
324
|
+
result
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
private
|
329
|
+
|
330
|
+
def default_log_level
|
331
|
+
Util.to_logger_level(Jamie::DEFAULT_LOG_LEVEL)
|
332
|
+
end
|
333
|
+
|
334
|
+
def stdout_logger(stdout)
|
335
|
+
logger = StdoutLogger.new(stdout)
|
336
|
+
logger.formatter = proc do |severity, datetime, progname, msg|
|
337
|
+
"#{msg}\n"
|
338
|
+
end
|
339
|
+
logger
|
340
|
+
end
|
341
|
+
|
342
|
+
def logdev_logger(filepath_or_logdev)
|
343
|
+
LogdevLogger.new(logdev(filepath_or_logdev))
|
344
|
+
end
|
345
|
+
|
346
|
+
def logdev(filepath_or_logdev)
|
347
|
+
if filepath_or_logdev.is_a? String
|
348
|
+
file = File.open(File.expand_path(filepath_or_logdev), "ab")
|
349
|
+
file.sync = true
|
350
|
+
file
|
351
|
+
else
|
352
|
+
filepath_or_logdev
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
# Internal class which adds a #banner method call that displays the
|
357
|
+
# message with a callout arrow.
|
358
|
+
class LogdevLogger < ::Logger
|
359
|
+
|
360
|
+
alias_method :super_info, :info
|
361
|
+
|
362
|
+
def <<(msg)
|
363
|
+
msg =~ /\n/ ? msg.split("\n").each { |l| format_line(l) } : super
|
364
|
+
end
|
365
|
+
|
366
|
+
def banner(msg = nil, &block)
|
367
|
+
super_info("-----> #{msg}", &block)
|
368
|
+
end
|
369
|
+
|
370
|
+
private
|
371
|
+
|
372
|
+
def format_line(line)
|
373
|
+
case line
|
374
|
+
when %r{^-----> } then banner(line.gsub(%r{^[ >-]{6} }, ''))
|
375
|
+
when %r{^>>>>>> } then error(line.gsub(%r{^[ >-]{6} }, ''))
|
376
|
+
when %r{^ } then info(line.gsub(%r{^[ >-]{6} }, ''))
|
377
|
+
else info(line)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
# Internal class which reformats logging methods for display as console
|
383
|
+
# output.
|
384
|
+
class StdoutLogger < LogdevLogger
|
385
|
+
|
386
|
+
def debug(msg = nil, &block)
|
387
|
+
super("D #{msg}", &block)
|
388
|
+
end
|
389
|
+
|
390
|
+
def info(msg = nil, &block)
|
391
|
+
super(" #{msg}", &block)
|
392
|
+
end
|
393
|
+
|
394
|
+
def warn(msg = nil, &block)
|
395
|
+
super("$$$$$$ #{msg}", &block)
|
396
|
+
end
|
397
|
+
|
398
|
+
def error(msg = nil, &block)
|
399
|
+
super(">>>>>> #{msg}", &block)
|
400
|
+
end
|
401
|
+
|
402
|
+
def fatal(msg = nil, &block)
|
403
|
+
super("!!!!!! #{msg}", &block)
|
404
|
+
end
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
module Logging
|
409
|
+
|
410
|
+
%w{banner debug info warn error fatal}.map(&:to_sym).each do |meth|
|
411
|
+
define_method(meth) do |*args|
|
412
|
+
logger.public_send(meth, *args)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
242
417
|
# A Chef run_list and attribute hash that will be used in a convergence
|
243
418
|
# integration.
|
244
419
|
#
|
@@ -299,10 +474,6 @@ module Jamie
|
|
299
474
|
# @return [String] logical name of this platform
|
300
475
|
attr_reader :name
|
301
476
|
|
302
|
-
# @return [Driver::Base] driver object which will manage this platform's
|
303
|
-
# lifecycle actions
|
304
|
-
attr_reader :driver
|
305
|
-
|
306
477
|
# @return [Array] Array of Chef run_list items
|
307
478
|
attr_reader :run_list
|
308
479
|
|
@@ -314,8 +485,6 @@ module Jamie
|
|
314
485
|
# @param [Hash] options configuration for a new platform
|
315
486
|
# @option options [String] :name logical name of this platform
|
316
487
|
# (**Required**)
|
317
|
-
# @option options [Driver::Base] :driver subclass of Driver::Base which
|
318
|
-
# will manage this platform's lifecycle actions (**Required**)
|
319
488
|
# @option options [Array<String>] :run_list Array of Chef run_list
|
320
489
|
# items
|
321
490
|
# @option options [Hash] :attributes Hash of Chef node attributes
|
@@ -323,7 +492,6 @@ module Jamie
|
|
323
492
|
validate_options(options)
|
324
493
|
|
325
494
|
@name = options['name']
|
326
|
-
@driver = options['driver']
|
327
495
|
@run_list = Array(options['run_list'])
|
328
496
|
@attributes = options['attributes'] || Hash.new
|
329
497
|
end
|
@@ -331,7 +499,7 @@ module Jamie
|
|
331
499
|
private
|
332
500
|
|
333
501
|
def validate_options(opts)
|
334
|
-
%w(name
|
502
|
+
%w(name).each do |k|
|
335
503
|
raise ArgumentError, "Attribute '#{k}' is required." if opts[k].nil?
|
336
504
|
end
|
337
505
|
end
|
@@ -344,25 +512,44 @@ module Jamie
|
|
344
512
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
345
513
|
class Instance
|
346
514
|
|
515
|
+
include Logging
|
516
|
+
|
347
517
|
# @return [Suite] the test suite configuration
|
348
518
|
attr_reader :suite
|
349
519
|
|
350
520
|
# @return [Platform] the target platform configuration
|
351
521
|
attr_reader :platform
|
352
522
|
|
523
|
+
# @return [Driver::Base] driver object which will manage this instance's
|
524
|
+
# lifecycle actions
|
525
|
+
attr_reader :driver
|
526
|
+
|
353
527
|
# @return [Jr] jr command string generator
|
354
528
|
attr_reader :jr
|
355
529
|
|
530
|
+
# @return [Logger] the logger for this instance
|
531
|
+
attr_reader :logger
|
532
|
+
|
356
533
|
# Creates a new instance, given a suite and a platform.
|
357
534
|
#
|
358
|
-
# @param
|
359
|
-
# @
|
360
|
-
|
361
|
-
|
535
|
+
# @param [Hash] options configuration for a new suite
|
536
|
+
# @option options [Suite] :suite the suite
|
537
|
+
# @option options [Platform] :platform the platform
|
538
|
+
# @option options [Driver::Base] :driver the driver
|
539
|
+
# @option options [Jr] :jr the jr command string generator
|
540
|
+
# @option options [Logger] :logger the instance logger
|
541
|
+
def initialize(options = {})
|
542
|
+
options = { 'logger' => Jamie.logger }.merge(options)
|
543
|
+
validate_options(options)
|
544
|
+
logger = options['logger']
|
545
|
+
|
546
|
+
@suite = options['suite']
|
547
|
+
@platform = options['platform']
|
548
|
+
@driver = options['driver']
|
549
|
+
@jr = options['jr']
|
550
|
+
@logger = logger.is_a?(Proc) ? logger.call(name) : logger
|
362
551
|
|
363
|
-
@
|
364
|
-
@platform = platform
|
365
|
-
@jr = Jr.new(@suite.name)
|
552
|
+
@driver.instance = self
|
366
553
|
end
|
367
554
|
|
368
555
|
# @return [String] name of this instance
|
@@ -370,6 +557,10 @@ module Jamie
|
|
370
557
|
"#{suite.name}-#{platform.name}".gsub(/_/, '-').gsub(/\./, '')
|
371
558
|
end
|
372
559
|
|
560
|
+
def to_s
|
561
|
+
"<#{name}>"
|
562
|
+
end
|
563
|
+
|
373
564
|
# Returns a combined run_list starting with the platform's run_list
|
374
565
|
# followed by the suite's run_list.
|
375
566
|
#
|
@@ -456,12 +647,14 @@ module Jamie
|
|
456
647
|
# @todo rescue Driver::ActionFailed and return some kind of null object
|
457
648
|
# to gracfully stop action chaining
|
458
649
|
def test(destroy_mode = :passing)
|
459
|
-
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
650
|
+
elapsed = Benchmark.measure do
|
651
|
+
banner "Cleaning up any prior instances of #{self}"
|
652
|
+
destroy
|
653
|
+
banner "Testing #{self}"
|
654
|
+
verify
|
655
|
+
destroy if destroy_mode == :passing
|
656
|
+
end
|
657
|
+
info "Testing of #{self} complete (#{elapsed.real} seconds)."
|
465
658
|
self
|
466
659
|
ensure
|
467
660
|
destroy if destroy_mode == :always
|
@@ -469,57 +662,63 @@ module Jamie
|
|
469
662
|
|
470
663
|
private
|
471
664
|
|
472
|
-
def validate_options(
|
473
|
-
|
474
|
-
|
665
|
+
def validate_options(opts)
|
666
|
+
%w(suite platform driver jr logger).each do |k|
|
667
|
+
raise ArgumentError, "Attribute '#{k}' is required." if opts[k].nil?
|
668
|
+
end
|
475
669
|
end
|
476
670
|
|
477
671
|
def transition_to(desired)
|
672
|
+
result = nil
|
478
673
|
FSM.actions(last_action, desired).each do |transition|
|
479
|
-
send("#{transition}_action")
|
674
|
+
result = send("#{transition}_action")
|
480
675
|
end
|
676
|
+
result
|
481
677
|
end
|
482
678
|
|
483
679
|
def create_action
|
484
|
-
|
485
|
-
action(:create) { |state|
|
486
|
-
|
680
|
+
banner "Creating #{self}"
|
681
|
+
elapsed = action(:create) { |state| driver.create(state) }
|
682
|
+
info "Creation of #{self} complete (#{elapsed.real} seconds)."
|
487
683
|
self
|
488
684
|
end
|
489
685
|
|
490
686
|
def converge_action
|
491
|
-
|
492
|
-
action(:converge) { |state|
|
493
|
-
|
687
|
+
banner "Converging #{self}"
|
688
|
+
elapsed = action(:converge) { |state| driver.converge(state) }
|
689
|
+
info "Convergence of #{self} complete (#{elapsed.real} seconds)."
|
494
690
|
self
|
495
691
|
end
|
496
692
|
|
497
693
|
def setup_action
|
498
|
-
|
499
|
-
action(:setup) { |state|
|
500
|
-
|
694
|
+
banner "Setting up #{self}"
|
695
|
+
elapsed = action(:setup) { |state| driver.setup(state) }
|
696
|
+
info "Setup of #{self} complete (#{elapsed.real} seconds)."
|
501
697
|
self
|
502
698
|
end
|
503
699
|
|
504
700
|
def verify_action
|
505
|
-
|
506
|
-
action(:verify) { |state|
|
507
|
-
|
701
|
+
banner "Verifying #{self}"
|
702
|
+
elapsed = action(:verify) { |state| driver.verify(state) }
|
703
|
+
info "Verification of #{self} complete (#{elapsed.real} seconds)."
|
508
704
|
self
|
509
705
|
end
|
510
706
|
|
511
707
|
def destroy_action
|
512
|
-
|
513
|
-
action(:destroy) { |state|
|
708
|
+
banner "Destroying #{self}"
|
709
|
+
elapsed = action(:destroy) { |state| driver.destroy(state) }
|
514
710
|
destroy_state
|
515
|
-
|
711
|
+
info "Destruction of #{self} complete (#{elapsed.real} seconds)."
|
516
712
|
self
|
517
713
|
end
|
518
714
|
|
519
715
|
def action(what)
|
520
716
|
state = load_state
|
521
|
-
|
717
|
+
elapsed = Benchmark.measure do
|
718
|
+
yield state if block_given?
|
719
|
+
end
|
522
720
|
state['last_action'] = what.to_s
|
721
|
+
elapsed
|
523
722
|
ensure
|
524
723
|
dump_state(state)
|
525
724
|
end
|
@@ -541,7 +740,7 @@ module Jamie
|
|
541
740
|
|
542
741
|
def statefile
|
543
742
|
File.expand_path(File.join(
|
544
|
-
|
743
|
+
driver['jamie_root'], ".jamie", "#{name}.yml"
|
545
744
|
))
|
546
745
|
end
|
547
746
|
|
@@ -706,7 +905,7 @@ module Jamie
|
|
706
905
|
jr_stream_file = "#{jr_bin} stream-file #{remote_path} #{md5} #{perms}"
|
707
906
|
|
708
907
|
<<-STREAMFILE.gsub(/^ {8}/, '')
|
709
|
-
echo "
|
908
|
+
echo "Uploading #{remote_path} (mode=#{perms})"
|
710
909
|
cat <<"__EOFSTREAM__" | #{sudo}#{jr_stream_file}
|
711
910
|
#{Base64.encode64(local_file)}
|
712
911
|
__EOFSTREAM__
|
@@ -745,6 +944,22 @@ module Jamie
|
|
745
944
|
gsub(/([a-z\d])([A-Z])/, '\1_\2').
|
746
945
|
downcase
|
747
946
|
end
|
947
|
+
|
948
|
+
def self.to_logger_level(symbol)
|
949
|
+
return nil unless [:debug, :info, :warn, :error, :fatal].include?(symbol)
|
950
|
+
|
951
|
+
Logger.const_get(symbol.to_s.upcase)
|
952
|
+
end
|
953
|
+
|
954
|
+
def self.from_logger_level(const)
|
955
|
+
case const
|
956
|
+
when Logger::DEBUG then :debug
|
957
|
+
when Logger::INFO then :info
|
958
|
+
when Logger::WARN then :warn
|
959
|
+
when Logger::ERROR then :error
|
960
|
+
else :fatal
|
961
|
+
end
|
962
|
+
end
|
748
963
|
end
|
749
964
|
|
750
965
|
# Mixin that wraps a command shell out invocation, providing a #run_command
|
@@ -764,12 +979,12 @@ module Jamie
|
|
764
979
|
# and context
|
765
980
|
def run_command(cmd, use_sudo = false, log_subject = "local")
|
766
981
|
cmd = "sudo #{cmd}" if use_sudo
|
767
|
-
subject = "
|
982
|
+
subject = "[#{log_subject} command]"
|
768
983
|
|
769
|
-
|
770
|
-
sh = Mixlib::ShellOut.new(cmd, :live_stream =>
|
984
|
+
info("#{subject} BEGIN (#{display_cmd(cmd)})")
|
985
|
+
sh = Mixlib::ShellOut.new(cmd, :live_stream => logger, :timeout => 60000)
|
771
986
|
sh.run_command
|
772
|
-
|
987
|
+
info("#{subject} END (#{sh.execution_time} seconds)")
|
773
988
|
sh.error!
|
774
989
|
rescue Mixlib::ShellOut::ShellCommandFailed => ex
|
775
990
|
raise ShellCommandFailed, ex.message
|
@@ -809,6 +1024,9 @@ module Jamie
|
|
809
1024
|
class Base
|
810
1025
|
|
811
1026
|
include ShellOut
|
1027
|
+
include Logging
|
1028
|
+
|
1029
|
+
attr_writer :instance
|
812
1030
|
|
813
1031
|
def initialize(config)
|
814
1032
|
@config = config
|
@@ -827,42 +1045,49 @@ module Jamie
|
|
827
1045
|
|
828
1046
|
# Creates an instance.
|
829
1047
|
#
|
830
|
-
# @param instance [Instance] an instance
|
831
1048
|
# @param state [Hash] mutable instance and driver state
|
832
1049
|
# @raise [ActionFailed] if the action could not be completed
|
833
|
-
def create(
|
1050
|
+
def create(state) ; end
|
834
1051
|
|
835
1052
|
# Converges a running instance.
|
836
1053
|
#
|
837
|
-
# @param instance [Instance] an instance
|
838
1054
|
# @param state [Hash] mutable instance and driver state
|
839
1055
|
# @raise [ActionFailed] if the action could not be completed
|
840
|
-
def converge(
|
1056
|
+
def converge(state) ; end
|
841
1057
|
|
842
1058
|
# Sets up an instance.
|
843
1059
|
#
|
844
|
-
# @param instance [Instance] an instance
|
845
1060
|
# @param state [Hash] mutable instance and driver state
|
846
1061
|
# @raise [ActionFailed] if the action could not be completed
|
847
|
-
def setup(
|
1062
|
+
def setup(state) ; end
|
848
1063
|
|
849
1064
|
# Verifies a converged instance.
|
850
1065
|
#
|
851
|
-
# @param instance [Instance] an instance
|
852
1066
|
# @param state [Hash] mutable instance and driver state
|
853
1067
|
# @raise [ActionFailed] if the action could not be completed
|
854
|
-
def verify(
|
1068
|
+
def verify(state) ; end
|
855
1069
|
|
856
1070
|
# Destroys an instance.
|
857
1071
|
#
|
858
|
-
# @param instance [Instance] an instance
|
859
1072
|
# @param state [Hash] mutable instance and driver state
|
860
1073
|
# @raise [ActionFailed] if the action could not be completed
|
861
|
-
def destroy(
|
1074
|
+
def destroy(state) ; end
|
862
1075
|
|
863
1076
|
protected
|
864
1077
|
|
865
|
-
attr_reader :config
|
1078
|
+
attr_reader :config, :instance
|
1079
|
+
|
1080
|
+
def logger
|
1081
|
+
instance.logger
|
1082
|
+
end
|
1083
|
+
|
1084
|
+
def puts(msg)
|
1085
|
+
info(msg)
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
def print(msg)
|
1089
|
+
info(msg)
|
1090
|
+
end
|
866
1091
|
|
867
1092
|
def run_command(cmd, use_sudo = nil, log_subject = nil)
|
868
1093
|
use_sudo = config['use_sudo'] if use_sudo.nil?
|
@@ -882,26 +1107,26 @@ module Jamie
|
|
882
1107
|
|
883
1108
|
# Base class for a driver that uses SSH to communication with an instance.
|
884
1109
|
# A subclass must implement the following methods:
|
885
|
-
# * #create(
|
886
|
-
# * #destroy(
|
1110
|
+
# * #create(state)
|
1111
|
+
# * #destroy(state)
|
887
1112
|
#
|
888
1113
|
# @author Fletcher Nichol <fnichol@nichol.ca>
|
889
1114
|
class SSHBase < Base
|
890
1115
|
|
891
|
-
def create(
|
1116
|
+
def create(state)
|
892
1117
|
raise NotImplementedError, "#create must be implemented by subclass."
|
893
1118
|
end
|
894
1119
|
|
895
|
-
def converge(
|
1120
|
+
def converge(state)
|
896
1121
|
ssh_args = build_ssh_args(state)
|
897
1122
|
|
898
1123
|
install_omnibus(ssh_args) if config['require_chef_omnibus']
|
899
1124
|
prepare_chef_home(ssh_args)
|
900
|
-
upload_chef_data(ssh_args
|
1125
|
+
upload_chef_data(ssh_args)
|
901
1126
|
run_chef_solo(ssh_args)
|
902
1127
|
end
|
903
1128
|
|
904
|
-
def setup(
|
1129
|
+
def setup(state)
|
905
1130
|
ssh_args = build_ssh_args(state)
|
906
1131
|
|
907
1132
|
if instance.jr.setup_cmd
|
@@ -909,7 +1134,7 @@ module Jamie
|
|
909
1134
|
end
|
910
1135
|
end
|
911
1136
|
|
912
|
-
def verify(
|
1137
|
+
def verify(state)
|
913
1138
|
ssh_args = build_ssh_args(state)
|
914
1139
|
|
915
1140
|
if instance.jr.run_cmd
|
@@ -918,7 +1143,7 @@ module Jamie
|
|
918
1143
|
end
|
919
1144
|
end
|
920
1145
|
|
921
|
-
def destroy(
|
1146
|
+
def destroy(state)
|
922
1147
|
raise NotImplementedError, "#destroy must be implemented by subclass."
|
923
1148
|
end
|
924
1149
|
|
@@ -926,6 +1151,8 @@ module Jamie
|
|
926
1151
|
|
927
1152
|
def build_ssh_args(state)
|
928
1153
|
opts = Hash.new
|
1154
|
+
opts[:user_known_hosts_file] = "/dev/null"
|
1155
|
+
opts[:paranoid] = false
|
929
1156
|
opts[:password] = config['password'] if config['password']
|
930
1157
|
opts[:keys] = Array(config['ssh_key']) if config['ssh_key']
|
931
1158
|
|
@@ -948,7 +1175,7 @@ module Jamie
|
|
948
1175
|
ssh(ssh_args, "sudo rm -rf #{chef_home} && mkdir -p #{chef_home}/cache")
|
949
1176
|
end
|
950
1177
|
|
951
|
-
def upload_chef_data(ssh_args
|
1178
|
+
def upload_chef_data(ssh_args)
|
952
1179
|
Jamie::ChefDataUploader.new(
|
953
1180
|
instance, ssh_args, config['jamie_root'], chef_home
|
954
1181
|
).upload
|
@@ -956,7 +1183,8 @@ module Jamie
|
|
956
1183
|
|
957
1184
|
def run_chef_solo(ssh_args)
|
958
1185
|
ssh(ssh_args, <<-RUN_SOLO)
|
959
|
-
sudo chef-solo -c #{chef_home}/solo.rb -j #{chef_home}/dna.json
|
1186
|
+
sudo chef-solo -c #{chef_home}/solo.rb -j #{chef_home}/dna.json \
|
1187
|
+
--log_level #{Util.from_logger_level(logger.level)}
|
960
1188
|
RUN_SOLO
|
961
1189
|
end
|
962
1190
|
|
@@ -980,11 +1208,11 @@ module Jamie
|
|
980
1208
|
channel.exec(cmd) do |ch, success|
|
981
1209
|
|
982
1210
|
channel.on_data do |ch, data|
|
983
|
-
|
1211
|
+
logger << data
|
984
1212
|
end
|
985
1213
|
|
986
1214
|
channel.on_extended_data do |ch, type, data|
|
987
|
-
|
1215
|
+
logger << data
|
988
1216
|
end
|
989
1217
|
|
990
1218
|
channel.on_request("exit-status") do |ch, data|
|
@@ -997,7 +1225,7 @@ module Jamie
|
|
997
1225
|
end
|
998
1226
|
|
999
1227
|
def wait_for_sshd(hostname)
|
1000
|
-
|
1228
|
+
logger << "." until test_ssh(hostname)
|
1001
1229
|
end
|
1002
1230
|
|
1003
1231
|
def test_ssh(hostname)
|
@@ -1022,6 +1250,7 @@ module Jamie
|
|
1022
1250
|
class ChefDataUploader
|
1023
1251
|
|
1024
1252
|
include ShellOut
|
1253
|
+
include Logging
|
1025
1254
|
|
1026
1255
|
def initialize(instance, ssh_args, jamie_root, chef_home)
|
1027
1256
|
@instance = instance
|
@@ -1044,6 +1273,10 @@ module Jamie
|
|
1044
1273
|
|
1045
1274
|
attr_reader :instance, :ssh_args, :jamie_root, :chef_home
|
1046
1275
|
|
1276
|
+
def logger
|
1277
|
+
instance.logger
|
1278
|
+
end
|
1279
|
+
|
1047
1280
|
def upload_json(scp)
|
1048
1281
|
json_file = StringIO.new(instance.dna.to_json)
|
1049
1282
|
scp.upload!(json_file, "#{chef_home}/dna.json")
|
@@ -1055,24 +1288,26 @@ module Jamie
|
|
1055
1288
|
end
|
1056
1289
|
|
1057
1290
|
def upload_cookbooks(scp)
|
1058
|
-
|
1059
|
-
scp.upload!(
|
1291
|
+
ckbks_dir = local_cookbooks
|
1292
|
+
scp.upload!(ckbks_dir, "#{chef_home}/cookbooks",
|
1060
1293
|
:recursive => true
|
1061
1294
|
) do |ch, name, sent, total|
|
1062
|
-
|
1063
|
-
|
1295
|
+
if sent == total
|
1296
|
+
info("Uploaded #{name.sub(%r{^#{ckbks_dir}/}, '')} (#{total} bytes)")
|
1297
|
+
end
|
1064
1298
|
end
|
1065
1299
|
ensure
|
1066
|
-
FileUtils.rmtree(
|
1300
|
+
FileUtils.rmtree(ckbks_dir)
|
1067
1301
|
end
|
1068
1302
|
|
1069
1303
|
def upload_data_bags(scp)
|
1070
|
-
|
1071
|
-
scp.upload!(
|
1304
|
+
dbags_dir = instance.suite.data_bags_path
|
1305
|
+
scp.upload!(dbags_dir, "#{chef_home}/data_bags",
|
1072
1306
|
:recursive => true
|
1073
1307
|
) do |ch, name, sent, total|
|
1074
|
-
|
1075
|
-
|
1308
|
+
if sent == total
|
1309
|
+
info("Uploaded #{name.sub(%r{^#{dbags_dir}/}, '')} (#{total} bytes)")
|
1310
|
+
end
|
1076
1311
|
end
|
1077
1312
|
end
|
1078
1313
|
|
@@ -1081,8 +1316,9 @@ module Jamie
|
|
1081
1316
|
scp.upload!(roles_dir, "#{chef_home}/roles",
|
1082
1317
|
:recursive => true
|
1083
1318
|
) do |ch, name, sent, total|
|
1084
|
-
|
1085
|
-
|
1319
|
+
if sent == total
|
1320
|
+
info("Uploaded #{name.sub(%r{^#{roles_dir}/}, '')} (#{total} bytes)")
|
1321
|
+
end
|
1086
1322
|
end
|
1087
1323
|
end
|
1088
1324
|
|
@@ -1095,7 +1331,6 @@ module Jamie
|
|
1095
1331
|
if instance.suite.data_bags_path
|
1096
1332
|
solo << %{data_bag_path "#{chef_home}/data_bags"}
|
1097
1333
|
end
|
1098
|
-
solo << %{log_level :info}
|
1099
1334
|
solo.join("\n")
|
1100
1335
|
end
|
1101
1336
|
|
@@ -1182,3 +1417,5 @@ module Jamie
|
|
1182
1417
|
end
|
1183
1418
|
end
|
1184
1419
|
end
|
1420
|
+
|
1421
|
+
Jamie.logger = Jamie.default_logger
|
data/spec/jamie_spec.rb
CHANGED
@@ -29,8 +29,10 @@ SimpleCov.start 'gem'
|
|
29
29
|
|
30
30
|
require 'fakefs/spec_helpers'
|
31
31
|
require 'minitest/autorun'
|
32
|
+
require 'ostruct'
|
32
33
|
|
33
34
|
require 'jamie'
|
35
|
+
require 'jamie/driver/dummy'
|
34
36
|
|
35
37
|
# Nasty hack to redefine IO.read in terms of File#read for fakefs
|
36
38
|
class IO
|
@@ -44,13 +46,14 @@ describe Jamie::Config do
|
|
44
46
|
|
45
47
|
let(:config) { Jamie::Config.new("/tmp/.jamie.yml") }
|
46
48
|
|
49
|
+
before do
|
50
|
+
FileUtils.mkdir_p("/tmp")
|
51
|
+
end
|
52
|
+
|
47
53
|
describe "#platforms" do
|
48
54
|
|
49
55
|
it "returns platforms loaded from a jamie.yml" do
|
50
|
-
stub_yaml!({'platforms' => [
|
51
|
-
{ 'name' => 'one', 'driver_plugin' => 'dummy' },
|
52
|
-
{ 'name' => 'two', 'driver_plugin' => 'dummy' },
|
53
|
-
]})
|
56
|
+
stub_yaml!({'platforms' => [ { 'name' => 'one' }, { 'name' => 'two' } ]})
|
54
57
|
config.platforms.size.must_equal 2
|
55
58
|
config.platforms[0].name.must_equal 'one'
|
56
59
|
config.platforms[1].name.must_equal 'two'
|
@@ -60,29 +63,6 @@ describe Jamie::Config do
|
|
60
63
|
stub_yaml!({})
|
61
64
|
config.platforms.must_equal []
|
62
65
|
end
|
63
|
-
|
64
|
-
it "returns a platform containing a driver instance" do
|
65
|
-
stub_yaml!({'platforms' => [
|
66
|
-
{ 'name' => 'platform', 'driver_plugin' => 'dummy' }
|
67
|
-
]})
|
68
|
-
config.platforms.first.driver.must_be_instance_of Jamie::Driver::Dummy
|
69
|
-
end
|
70
|
-
|
71
|
-
it "returns a platform with a driver initialized with jamie_root" do
|
72
|
-
stub_yaml!({'platforms' => [
|
73
|
-
{ 'name' => 'platform', 'driver_plugin' => 'dummy' }
|
74
|
-
]})
|
75
|
-
config.platforms.first.driver['jamie_root'].must_equal "/tmp"
|
76
|
-
end
|
77
|
-
|
78
|
-
it "returns a platform with a driver initialized with passed in config" do
|
79
|
-
stub_yaml!({'platforms' => [
|
80
|
-
{ 'name' => 'platform', 'driver_plugin' => 'dummy',
|
81
|
-
'driver_config' => { 'foo' => 'bar' }
|
82
|
-
}
|
83
|
-
]})
|
84
|
-
config.platforms.first.driver['foo'].must_equal "bar"
|
85
|
-
end
|
86
66
|
end
|
87
67
|
|
88
68
|
describe "#suites" do
|
@@ -146,8 +126,8 @@ describe Jamie::Config do
|
|
146
126
|
it "returns instances loaded from a jamie.yml" do
|
147
127
|
stub_yaml!({
|
148
128
|
'platforms' => [
|
149
|
-
{ 'name' => 'p1'
|
150
|
-
{ 'name' => 'p2'
|
129
|
+
{ 'name' => 'p1' },
|
130
|
+
{ 'name' => 'p2' },
|
151
131
|
],
|
152
132
|
'suites' => [
|
153
133
|
{ 'name' => 's1', 'run_list' => [] },
|
@@ -157,29 +137,58 @@ describe Jamie::Config do
|
|
157
137
|
config.instances.size.must_equal 4
|
158
138
|
config.instances.map { |i| i.name }.must_equal %w{s1-p1 s1-p2 s2-p1 s2-p2}
|
159
139
|
end
|
140
|
+
|
141
|
+
it "returns an instance containing a driver instance" do
|
142
|
+
stub_yaml!({
|
143
|
+
'platforms' => [ { 'name' => 'platform', 'driver_plugin' => 'dummy' } ],
|
144
|
+
'suites' => [ { 'name' => 'suite', 'run_list' => [] }]
|
145
|
+
})
|
146
|
+
config.instances.first.driver.must_be_instance_of Jamie::Driver::Dummy
|
147
|
+
end
|
148
|
+
|
149
|
+
it "returns an instance with a driver initialized with jamie_root" do
|
150
|
+
stub_yaml!({
|
151
|
+
'platforms' => [ { 'name' => 'platform', 'driver_plugin' => 'dummy' } ],
|
152
|
+
'suites' => [ { 'name' => 'suite', 'run_list' => [] }]
|
153
|
+
})
|
154
|
+
config.instances.first.driver['jamie_root'].must_equal "/tmp"
|
155
|
+
end
|
156
|
+
|
157
|
+
it "returns an instance with a driver initialized with passed in config" do
|
158
|
+
stub_yaml!({
|
159
|
+
'platforms' => [
|
160
|
+
{ 'name' => 'platform', 'driver_plugin' => 'dummy',
|
161
|
+
'driver_config' => { 'foo' => 'bar' } }
|
162
|
+
],
|
163
|
+
'suites' => [ { 'name' => 'suite', 'run_list' => [] }]
|
164
|
+
})
|
165
|
+
config.instances.first.driver['foo'].must_equal "bar"
|
166
|
+
end
|
160
167
|
end
|
161
168
|
|
162
169
|
describe "jamie.local.yml" do
|
163
170
|
|
164
171
|
it "merges in configuration with jamie.yml" do
|
165
172
|
stub_yaml!(".jamie.yml", {
|
166
|
-
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
|
173
|
+
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ],
|
174
|
+
'suites' => [ { 'name' => 's1', 'run_list' => [] } ]
|
167
175
|
})
|
168
176
|
stub_yaml!(".jamie.local.yml", {
|
169
177
|
'driver_config' => { 'foo' => 'bar' }
|
170
178
|
})
|
171
|
-
config.
|
179
|
+
config.instances.first.driver['foo'].must_equal 'bar'
|
172
180
|
end
|
173
181
|
|
174
182
|
it "merges over configuration in jamie.yml" do
|
175
183
|
stub_yaml!(".jamie.yml", {
|
176
184
|
'driver_config' => { 'foo' => 'nope' },
|
177
|
-
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
|
185
|
+
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ],
|
186
|
+
'suites' => [ { 'name' => 's1', 'run_list' => [] } ]
|
178
187
|
})
|
179
188
|
stub_yaml!(".jamie.local.yml", {
|
180
189
|
'driver_config' => { 'foo' => 'bar' }
|
181
190
|
})
|
182
|
-
config.
|
191
|
+
config.instances.first.driver['foo'].must_equal 'bar'
|
183
192
|
end
|
184
193
|
end
|
185
194
|
|
@@ -200,7 +209,8 @@ describe Jamie::Config do
|
|
200
209
|
|
201
210
|
it "evaluates jamie.local.yml through erb before loading" do
|
202
211
|
stub_yaml!({
|
203
|
-
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ]
|
212
|
+
'platforms' => [ { 'name' => 'p1', 'driver_plugin' => 'dummy' } ],
|
213
|
+
'suites' => [ { 'name' => 's1', 'run_list' => [] } ]
|
204
214
|
})
|
205
215
|
FileUtils.mkdir_p "/tmp"
|
206
216
|
File.open("/tmp/.jamie.local.yml", "wb") do |f|
|
@@ -212,8 +222,8 @@ describe Jamie::Config do
|
|
212
222
|
<% end %>
|
213
223
|
YAML
|
214
224
|
end
|
215
|
-
config.
|
216
|
-
config.
|
225
|
+
config.instances.first.driver['noodle'].must_equal "soup"
|
226
|
+
config.instances.first.driver['mushroom'].must_equal "soup"
|
217
227
|
end
|
218
228
|
end
|
219
229
|
|
@@ -335,7 +345,7 @@ end
|
|
335
345
|
|
336
346
|
describe Jamie::Platform do
|
337
347
|
|
338
|
-
let(:opts) do ; { 'name' => 'plata'
|
348
|
+
let(:opts) do ; { 'name' => 'plata' } ; end
|
339
349
|
let(:platform) { Jamie::Platform.new(opts) }
|
340
350
|
|
341
351
|
it "raises an ArgumentError if name is missing" do
|
@@ -343,11 +353,6 @@ describe Jamie::Platform do
|
|
343
353
|
proc { Jamie::Platform.new(opts) }.must_raise ArgumentError
|
344
354
|
end
|
345
355
|
|
346
|
-
it "raises an ArgumentError if driver is missing" do
|
347
|
-
opts.delete('driver')
|
348
|
-
proc { Jamie::Platform.new(opts) }.must_raise ArgumentError
|
349
|
-
end
|
350
|
-
|
351
356
|
it "returns an empty Array given no run_list" do
|
352
357
|
platform.run_list.must_equal []
|
353
358
|
end
|
@@ -359,7 +364,6 @@ describe Jamie::Platform do
|
|
359
364
|
it "returns attributes from constructor" do
|
360
365
|
opts.merge!({ 'run_list' => [ 'a', 'b' ], 'attributes' => { 'c' => 'd' }})
|
361
366
|
platform.name.must_equal 'plata'
|
362
|
-
platform.driver.must_equal 'imadriver'
|
363
367
|
platform.run_list.must_equal [ 'a', 'b' ]
|
364
368
|
platform.attributes.must_equal({ 'c' => 'd' })
|
365
369
|
end
|
@@ -373,18 +377,28 @@ describe Jamie::Instance do
|
|
373
377
|
end
|
374
378
|
|
375
379
|
let(:platform) do
|
376
|
-
Jamie::Platform.new({ 'name' => 'platform',
|
380
|
+
Jamie::Platform.new({ 'name' => 'platform',
|
377
381
|
'run_list' => 'platform_list', 'attributes' => { 'p' => 'pp' } })
|
378
382
|
end
|
379
383
|
|
380
|
-
let(:
|
384
|
+
let(:driver) { Jamie::Driver::Dummy.new({}) }
|
385
|
+
|
386
|
+
let(:jr) { Jamie::Jr.new(suite.name) }
|
387
|
+
|
388
|
+
let(:opts) do
|
389
|
+
{ 'suite' => suite, 'platform' => platform, 'driver' => driver, 'jr' => jr }
|
390
|
+
end
|
391
|
+
|
392
|
+
let(:instance) { Jamie::Instance.new(opts) }
|
381
393
|
|
382
394
|
it "raises an ArgumentError if suite is missing" do
|
383
|
-
|
395
|
+
opts.delete('suite')
|
396
|
+
proc { Jamie::Instance.new(opts) }.must_raise ArgumentError
|
384
397
|
end
|
385
398
|
|
386
399
|
it "raises an ArgumentError if platform is missing" do
|
387
|
-
|
400
|
+
opts.delete('platform')
|
401
|
+
proc { Jamie::Instance.new(opts) }.must_raise ArgumentError
|
388
402
|
end
|
389
403
|
|
390
404
|
it "returns suite" do
|
@@ -402,10 +416,13 @@ describe Jamie::Instance do
|
|
402
416
|
describe "#name" do
|
403
417
|
|
404
418
|
def combo(suite_name, platform_name)
|
405
|
-
Jamie::
|
406
|
-
|
407
|
-
|
419
|
+
opts['suite'] = Jamie::Suite.new(
|
420
|
+
'name' => suite_name, 'run_list' => []
|
421
|
+
)
|
422
|
+
opts['platform'] = Jamie::Platform.new(
|
423
|
+
'name' => platform_name
|
408
424
|
)
|
425
|
+
Jamie::Instance.new(opts)
|
409
426
|
end
|
410
427
|
|
411
428
|
it "combines the suite and platform names with a dash" do
|
@@ -428,11 +445,13 @@ describe Jamie::Instance do
|
|
428
445
|
describe "#run_list" do
|
429
446
|
|
430
447
|
def combo(suite_list, platform_list)
|
431
|
-
Jamie::
|
432
|
-
|
433
|
-
Jamie::Platform.new({ 'name' => 'platform', 'driver' => 'd',
|
434
|
-
'run_list' => platform_list })
|
448
|
+
opts['suite'] = Jamie::Suite.new(
|
449
|
+
'name' => 'suite', 'run_list' => suite_list
|
435
450
|
)
|
451
|
+
opts['platform'] = Jamie::Platform.new(
|
452
|
+
'name' => 'platform', 'run_list' => platform_list
|
453
|
+
)
|
454
|
+
Jamie::Instance.new(opts)
|
436
455
|
end
|
437
456
|
|
438
457
|
it "combines the platform then suite run_lists" do
|
@@ -451,12 +470,13 @@ describe Jamie::Instance do
|
|
451
470
|
describe "#attributes" do
|
452
471
|
|
453
472
|
def combo(suite_attrs, platform_attrs)
|
454
|
-
Jamie::
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
473
|
+
opts['suite'] = Jamie::Suite.new(
|
474
|
+
'name' => 'suite', 'run_list' => [], 'attributes' => suite_attrs
|
475
|
+
)
|
476
|
+
opts['platform'] = Jamie::Platform.new(
|
477
|
+
'name' => 'platform', 'attributes' => platform_attrs
|
459
478
|
)
|
479
|
+
Jamie::Instance.new(opts)
|
460
480
|
end
|
461
481
|
|
462
482
|
it "merges suite and platform hashes together" do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jamie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.
|
4
|
+
version: 0.1.0.alpha19
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-03 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: thor
|