beaker 1.16.0 → 1.17.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.
- checksums.yaml +8 -8
- data/CONTRIBUTING.md +90 -0
- data/HISTORY.md +654 -2
- data/beaker.gemspec +1 -0
- data/lib/beaker/answers/version34.rb +4 -0
- data/lib/beaker/cli.rb +49 -2
- data/lib/beaker/dsl/helpers.rb +356 -196
- data/lib/beaker/dsl/install_utils.rb +135 -16
- data/lib/beaker/dsl/patterns.rb +37 -0
- data/lib/beaker/dsl/roles.rb +29 -0
- data/lib/beaker/dsl.rb +2 -1
- data/lib/beaker/host/unix.rb +14 -10
- data/lib/beaker/host/windows.rb +2 -0
- data/lib/beaker/host.rb +96 -1
- data/lib/beaker/host_prebuilt_steps.rb +41 -51
- data/lib/beaker/hypervisor/aws_sdk.rb +80 -16
- data/lib/beaker/hypervisor/ec2_helper.rb +1 -1
- data/lib/beaker/logger.rb +17 -0
- data/lib/beaker/options/command_line_parser.rb +3 -0
- data/lib/beaker/options/hosts_file_parser.rb +7 -4
- data/lib/beaker/options/options_hash.rb +2 -2
- data/lib/beaker/options/parser.rb +1 -1
- data/lib/beaker/options/presets.rb +128 -83
- data/lib/beaker/perf.rb +58 -0
- data/lib/beaker/shared/host_manager.rb +81 -0
- data/lib/beaker/shared.rb +2 -2
- data/lib/beaker/ssh_connection.rb +14 -7
- data/lib/beaker/test_case.rb +13 -0
- data/lib/beaker/test_suite.rb +23 -5
- data/lib/beaker/version.rb +1 -1
- data/lib/beaker.rb +1 -1
- data/spec/beaker/answers_spec.rb +13 -8
- data/spec/beaker/dsl/ezbake_utils_spec.rb +8 -9
- data/spec/beaker/dsl/helpers_spec.rb +299 -51
- data/spec/beaker/dsl/install_utils_spec.rb +75 -10
- data/spec/beaker/dsl/roles_spec.rb +36 -1
- data/spec/beaker/host_prebuilt_steps_spec.rb +21 -5
- data/spec/beaker/host_spec.rb +187 -23
- data/spec/beaker/hypervisor/ec2_helper_spec.rb +4 -4
- data/spec/beaker/hypervisor/vagrant_spec.rb +1 -1
- data/spec/beaker/options/hosts_file_parser_spec.rb +5 -0
- data/spec/beaker/options/options_hash_spec.rb +2 -2
- data/spec/beaker/options/parser_spec.rb +6 -0
- data/spec/beaker/options/presets_spec.rb +18 -2
- data/spec/beaker/perf_spec.rb +87 -0
- data/spec/beaker/shared/{host_role_parser_spec.rb → host_manager_spec.rb} +36 -5
- data/spec/beaker/test_suite_spec.rb +4 -3
- data/spec/matchers.rb +31 -3
- data/spec/mocks.rb +31 -25
- metadata +24 -5
- data/lib/beaker/shared/host_role_parser.rb +0 -36
data/lib/beaker/dsl/helpers.rb
CHANGED
@@ -3,6 +3,9 @@ require 'resolv'
|
|
3
3
|
require 'inifile'
|
4
4
|
require 'timeout'
|
5
5
|
require 'beaker/dsl/outcomes'
|
6
|
+
require 'beaker/options'
|
7
|
+
require 'hocon'
|
8
|
+
require 'hocon/config_error'
|
6
9
|
|
7
10
|
module Beaker
|
8
11
|
module DSL
|
@@ -74,19 +77,16 @@ module Beaker
|
|
74
77
|
# @return [Result] An object representing the outcome of *command*.
|
75
78
|
# @raise [FailTest] Raises an exception if *command* obviously fails.
|
76
79
|
def on(host, command, opts = {}, &block)
|
77
|
-
|
80
|
+
block_on host do | host |
|
81
|
+
if command.is_a? Command
|
82
|
+
command = command.cmd_line(host)
|
83
|
+
end
|
78
84
|
cmd_opts = {}
|
85
|
+
#add any additional environment variables to the command
|
79
86
|
if opts[:environment]
|
80
87
|
cmd_opts['ENV'] = opts[:environment]
|
81
88
|
end
|
82
89
|
command = Command.new(command.to_s, [], cmd_opts)
|
83
|
-
end
|
84
|
-
if host.is_a? String or host.is_a? Symbol
|
85
|
-
host = hosts_as(host) #check by role
|
86
|
-
end
|
87
|
-
if host.is_a? Array
|
88
|
-
host.map { |h| on h, command, opts, &block }
|
89
|
-
else
|
90
90
|
@result = host.exec(command, opts)
|
91
91
|
|
92
92
|
# Also, let additional checking be performed by the caller.
|
@@ -100,8 +100,7 @@ module Beaker
|
|
100
100
|
yield @result
|
101
101
|
end
|
102
102
|
end
|
103
|
-
|
104
|
-
return @result
|
103
|
+
@result
|
105
104
|
end
|
106
105
|
end
|
107
106
|
|
@@ -179,11 +178,10 @@ module Beaker
|
|
179
178
|
#
|
180
179
|
# @return [Result] Returns the result of the SCP operation
|
181
180
|
def scp_from host, from_path, to_path, opts = {}
|
182
|
-
|
183
|
-
host.each { |h| scp_from h, from_path, to_path, opts }
|
184
|
-
else
|
181
|
+
block_on host do | host |
|
185
182
|
@result = host.do_scp_from(from_path, to_path, opts)
|
186
183
|
@result.log logger
|
184
|
+
@result
|
187
185
|
end
|
188
186
|
end
|
189
187
|
|
@@ -201,11 +199,10 @@ module Beaker
|
|
201
199
|
#
|
202
200
|
# @return [Result] Returns the result of the SCP operation
|
203
201
|
def scp_to host, from_path, to_path, opts = {}
|
204
|
-
|
205
|
-
host.each { |h| scp_to h, from_path, to_path, opts }
|
206
|
-
else
|
202
|
+
block_on host do | host |
|
207
203
|
@result = host.do_scp_to(from_path, to_path, opts)
|
208
204
|
@result.log logger
|
205
|
+
@result
|
209
206
|
end
|
210
207
|
end
|
211
208
|
|
@@ -349,7 +346,7 @@ module Beaker
|
|
349
346
|
# @option opts [String] :source The location on the test runners box where the files are found
|
350
347
|
# @option opts [String] :module_name The name of the module to be copied over
|
351
348
|
def puppet_module_install_on(host, opts = {})
|
352
|
-
|
349
|
+
block_on host do | h |
|
353
350
|
on h, puppet("module install #{opts[:module_name]}")
|
354
351
|
end
|
355
352
|
end
|
@@ -405,31 +402,14 @@ module Beaker
|
|
405
402
|
# @raise [SkipTest] Raises skip test if there are no valid hosts for
|
406
403
|
# this test case after confinement.
|
407
404
|
def confine(type, criteria, host_array = nil, &block)
|
408
|
-
provided_hosts = host_array ? true : false
|
409
405
|
hosts_to_modify = host_array || hosts
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
hosts_to_modify = hosts_to_modify.reject do |host|
|
418
|
-
yield host
|
419
|
-
end
|
420
|
-
end
|
421
|
-
when :to
|
422
|
-
hosts_to_modify = hosts_to_modify.select do |host|
|
423
|
-
inspect_host host, property, value
|
424
|
-
end
|
425
|
-
if block_given?
|
426
|
-
hosts_to_modify = hosts_to_modify.select do |host|
|
427
|
-
yield host
|
428
|
-
end
|
429
|
-
end
|
430
|
-
else
|
431
|
-
raise "Unknown option #{type}"
|
432
|
-
end
|
406
|
+
case type
|
407
|
+
when :except
|
408
|
+
hosts_to_modify = hosts_to_modify - select_hosts(criteria, hosts_to_modify, &block)
|
409
|
+
when :to
|
410
|
+
hosts_to_modify = select_hosts(criteria, hosts_to_modify, &block)
|
411
|
+
else
|
412
|
+
raise "Unknown option #{type}"
|
433
413
|
end
|
434
414
|
if hosts_to_modify.empty?
|
435
415
|
logger.warn "No suitable hosts with: #{criteria.inspect}"
|
@@ -456,6 +436,59 @@ module Beaker
|
|
456
436
|
end
|
457
437
|
end
|
458
438
|
|
439
|
+
#Return a set of hosts that meet the given criteria
|
440
|
+
# @param [Hash{Symbol,String=>String,Regexp,Array<String,Regexp>}]
|
441
|
+
# criteria Specify the criteria with which a host should be
|
442
|
+
# considered for inclusion. The key is any attribute
|
443
|
+
# of the host that will be yielded by {Beaker::Host#[]}.
|
444
|
+
# The value can be any string/regex or array of strings/regexp.
|
445
|
+
# The values are compared using [Enumerable#any?] so that if one
|
446
|
+
# value of an array matches the host is considered a match for that
|
447
|
+
# criteria.
|
448
|
+
# @param [Array<Host>] host_array This creatively named parameter is
|
449
|
+
# an optional array of hosts to confine to. If not passed in, this
|
450
|
+
# method will modify {Beaker::TestCase#hosts} in place.
|
451
|
+
# @param [Proc] block Addition checks to determine suitability of hosts
|
452
|
+
# for selection. Each host that is still valid after checking
|
453
|
+
# *criteria* is then passed in turn into this block. The block
|
454
|
+
# should return true if the host matches this additional criteria.
|
455
|
+
#
|
456
|
+
# @return [Array<Host>] Returns an array of hosts that meet the provided criteria
|
457
|
+
def select_hosts(criteria, host_array = nil, &block)
|
458
|
+
hosts_to_select_from = host_array || hosts
|
459
|
+
criteria.each_pair do |property, value|
|
460
|
+
hosts_to_select_from = hosts_to_select_from.select do |host|
|
461
|
+
inspect_host host, property, value
|
462
|
+
end
|
463
|
+
end
|
464
|
+
if block_given?
|
465
|
+
hosts_to_select_from = hosts_to_select_from.select do |host|
|
466
|
+
yield host
|
467
|
+
end
|
468
|
+
end
|
469
|
+
hosts_to_select_from
|
470
|
+
end
|
471
|
+
|
472
|
+
# Return the name of the puppet user.
|
473
|
+
#
|
474
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
475
|
+
#
|
476
|
+
# @note This method assumes puppet is installed on the host.
|
477
|
+
#
|
478
|
+
def puppet_user(host)
|
479
|
+
return host.puppet('master')['group']
|
480
|
+
end
|
481
|
+
|
482
|
+
# Return the name of the puppet group.
|
483
|
+
#
|
484
|
+
# @param [Host] host One object that acts like a Beaker::Host
|
485
|
+
#
|
486
|
+
# @note This method assumes puppet is installed on the host.
|
487
|
+
#
|
488
|
+
def puppet_group(host)
|
489
|
+
return host.puppet('master')['user']
|
490
|
+
end
|
491
|
+
|
459
492
|
# @!visibility private
|
460
493
|
def inspect_host(host, property, one_or_more_values)
|
461
494
|
values = Array(one_or_more_values)
|
@@ -489,16 +522,24 @@ module Beaker
|
|
489
522
|
# specified, if no section is specified the
|
490
523
|
# a puppet.conf file will be written with the
|
491
524
|
# options put in a section named after [mode]
|
492
|
-
#
|
493
|
-
#
|
494
|
-
#
|
495
|
-
#
|
525
|
+
# @option conf_opts [String] :__commandline_args__ A special setting for
|
526
|
+
# command_line arguments such as --debug or
|
527
|
+
# --logdest, which cannot be set in
|
528
|
+
# puppet.conf. For example:
|
496
529
|
#
|
497
530
|
# :__commandline_args__ => '--logdest /tmp/a.log'
|
498
531
|
#
|
499
532
|
# These will only be applied when starting a FOSS
|
500
533
|
# master, as a pe master is just bounced.
|
501
|
-
#
|
534
|
+
# @option conf_opts [Hash] :__service_args__ A special setting of options
|
535
|
+
# for controlling how the puppet master service is
|
536
|
+
# handled. The only setting currently is
|
537
|
+
# :bypass_service_script, which if set true will
|
538
|
+
# force stopping and starting a webrick master
|
539
|
+
# using the start_puppet_from_source_* methods,
|
540
|
+
# even if it seems the host has passenger.
|
541
|
+
# This is needed in FOSS tests to initialize
|
542
|
+
# SSL.
|
502
543
|
# @param [File] testdir The temporary directory which will hold backup
|
503
544
|
# configuration, and other test artifacts.
|
504
545
|
#
|
@@ -524,16 +565,43 @@ module Beaker
|
|
524
565
|
def with_puppet_running_on host, conf_opts, testdir = host.tmpdir(File.basename(@path)), &block
|
525
566
|
raise(ArgumentError, "with_puppet_running_on's conf_opts must be a Hash. You provided a #{conf_opts.class}: '#{conf_opts}'") if !conf_opts.kind_of?(Hash)
|
526
567
|
cmdline_args = conf_opts[:__commandline_args__]
|
527
|
-
|
568
|
+
service_args = conf_opts[:__service_args__] || {}
|
569
|
+
conf_opts = conf_opts.reject { |k,v| [:__commandline_args__, :__service_args__].include?(k) }
|
528
570
|
|
529
571
|
curl_retries = host['master-start-curl-retries'] || options['master-start-curl-retries']
|
530
572
|
logger.debug "Setting curl retries to #{curl_retries}"
|
531
573
|
|
574
|
+
if options[:is_jvm_puppet]
|
575
|
+
confdir = host.puppet('master')['confdir']
|
576
|
+
vardir = host.puppet('master')['vardir']
|
577
|
+
|
578
|
+
if cmdline_args
|
579
|
+
split_args = cmdline_args.split()
|
580
|
+
|
581
|
+
split_args.each do |arg|
|
582
|
+
case arg
|
583
|
+
when /--confdir=(.*)/
|
584
|
+
confdir = $1
|
585
|
+
when /--vardir=(.*)/
|
586
|
+
vardir = $1
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
|
591
|
+
jvm_puppet_opts = { "jruby-puppet" => {
|
592
|
+
"master-conf-dir" => confdir,
|
593
|
+
"master-var-dir" => vardir,
|
594
|
+
}}
|
595
|
+
|
596
|
+
jvm_puppet_conf = File.join("#{host['jvm-puppet-confdir']}", "jvm-puppet.conf")
|
597
|
+
modify_tk_config(host, jvm_puppet_conf, jvm_puppet_opts)
|
598
|
+
end
|
599
|
+
|
532
600
|
begin
|
533
601
|
backup_file = backup_the_file(host, host['puppetpath'], testdir, 'puppet.conf')
|
534
602
|
lay_down_new_puppet_conf host, conf_opts, testdir
|
535
603
|
|
536
|
-
if host[
|
604
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
537
605
|
bounce_service( host, host['puppetservice'], curl_retries )
|
538
606
|
else
|
539
607
|
puppet_master_started = start_puppet_from_source_on!( host, cmdline_args )
|
@@ -547,9 +615,9 @@ module Beaker
|
|
547
615
|
|
548
616
|
ensure
|
549
617
|
begin
|
550
|
-
restore_puppet_conf_from_backup( host, backup_file )
|
551
618
|
|
552
|
-
if host[
|
619
|
+
if host.use_service_scripts? && !service_args[:bypass_service_script]
|
620
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
553
621
|
bounce_service( host, host['puppetservice'], curl_retries )
|
554
622
|
else
|
555
623
|
if puppet_master_started
|
@@ -557,6 +625,7 @@ module Beaker
|
|
557
625
|
else
|
558
626
|
dump_puppet_log(host)
|
559
627
|
end
|
628
|
+
restore_puppet_conf_from_backup( host, backup_file )
|
560
629
|
end
|
561
630
|
|
562
631
|
rescue Exception => teardown_exception
|
@@ -655,7 +724,7 @@ module Beaker
|
|
655
724
|
# @!visibility private
|
656
725
|
def dump_puppet_log(host)
|
657
726
|
syslogfile = case host['platform']
|
658
|
-
when /fedora|centos|el/ then '/var/log/messages'
|
727
|
+
when /fedora|centos|el|redhat|scientific/ then '/var/log/messages'
|
659
728
|
when /ubuntu|debian/ then '/var/log/syslog'
|
660
729
|
else return
|
661
730
|
end
|
@@ -687,10 +756,89 @@ module Beaker
|
|
687
756
|
new_conf
|
688
757
|
end
|
689
758
|
|
759
|
+
# Modify the given TrapperKeeper config file.
|
760
|
+
#
|
761
|
+
# @param [Host] host A host object
|
762
|
+
# @param [OptionsHash] options_hash New hash which will be merged into
|
763
|
+
# the given TrapperKeeper config.
|
764
|
+
# @param [String] config_file_path Path to the TrapperKeeper config on
|
765
|
+
# the given host which is to be
|
766
|
+
# modified.
|
767
|
+
# @param [Bool] replace If set true, instead of updating the existing
|
768
|
+
# TrapperKeeper configuration, replace it entirely
|
769
|
+
# with the contents of the given hash.
|
770
|
+
#
|
771
|
+
# @note TrapperKeeper config files can be HOCON, JSON, or Ini. We don't
|
772
|
+
# particularly care which of these the file named by `config_file_path` on
|
773
|
+
# the SUT actually is, just that the contents can be parsed into a map.
|
774
|
+
#
|
775
|
+
def modify_tk_config(host, config_file_path, options_hash, replace=false)
|
776
|
+
if options_hash.empty?
|
777
|
+
return nil
|
778
|
+
end
|
779
|
+
|
780
|
+
new_hash = Beaker::Options::OptionsHash.new
|
781
|
+
|
782
|
+
if replace
|
783
|
+
new_hash.merge!(options_hash)
|
784
|
+
else
|
785
|
+
if not host.file_exist?( config_file_path )
|
786
|
+
raise "Error: #{config_file_path} does not exist on #{host}"
|
787
|
+
end
|
788
|
+
file_string = host.exec( Command.new( "cat #{config_file_path}" )).stdout
|
789
|
+
|
790
|
+
begin
|
791
|
+
tk_conf_hash = read_tk_config_string(file_string)
|
792
|
+
rescue RuntimeError
|
793
|
+
raise "Error reading trapperkeeper config: #{config_file_path} at host: #{host}"
|
794
|
+
end
|
795
|
+
|
796
|
+
new_hash.merge!(tk_conf_hash)
|
797
|
+
new_hash.merge!(options_hash)
|
798
|
+
end
|
799
|
+
|
800
|
+
file_string = JSON.dump(new_hash)
|
801
|
+
create_remote_file host, config_file_path, file_string
|
802
|
+
end
|
803
|
+
|
804
|
+
# The Trapperkeeper config service will accept HOCON (aka typesafe), JSON,
|
805
|
+
# or Ini configuration files which means we need to safely handle the the
|
806
|
+
# exceptions that might come from parsing the given string with the wrong
|
807
|
+
# parser and fall back to the next valid parser in turn. We finally raise
|
808
|
+
# a RuntimeException if none of the parsers succeed.
|
809
|
+
#
|
810
|
+
# @!visibility private
|
811
|
+
def read_tk_config_string( string )
|
812
|
+
begin
|
813
|
+
return Hocon.parse(string)
|
814
|
+
rescue Hocon::ConfigError
|
815
|
+
nil
|
816
|
+
end
|
817
|
+
|
818
|
+
begin
|
819
|
+
return JSON.parse(string)
|
820
|
+
rescue JSON::JSONError
|
821
|
+
nil
|
822
|
+
end
|
823
|
+
|
824
|
+
begin
|
825
|
+
return IniFile.new(string)
|
826
|
+
rescue IniFile::Error
|
827
|
+
nil
|
828
|
+
end
|
829
|
+
|
830
|
+
raise "Failed to read TrapperKeeper config!"
|
831
|
+
end
|
832
|
+
|
690
833
|
# @!visibility private
|
691
834
|
def bounce_service host, service, curl_retries = 120
|
692
|
-
host.
|
693
|
-
|
835
|
+
if host.graceful_restarts?
|
836
|
+
apachectl_path = host.is_pe? ? "#{host['puppetsbindir']}/apache2ctl" : 'apache2ctl'
|
837
|
+
host.exec(Command.new("#{apachectl_path} graceful"))
|
838
|
+
else
|
839
|
+
host.exec puppet_resource('service', service, 'ensure=stopped')
|
840
|
+
host.exec puppet_resource('service', service, 'ensure=running')
|
841
|
+
end
|
694
842
|
curl_with_retries(" #{service} ", host, "https://localhost:8140", [35, 60], curl_retries)
|
695
843
|
end
|
696
844
|
|
@@ -769,79 +917,76 @@ module Beaker
|
|
769
917
|
# validation, etc.
|
770
918
|
#
|
771
919
|
def apply_manifest_on(host, manifest, opts = {}, &block)
|
772
|
-
|
773
|
-
|
774
|
-
|
920
|
+
block_on host do | host |
|
921
|
+
|
922
|
+
on_options = {}
|
923
|
+
on_options[:acceptable_exit_codes] = Array(opts[:acceptable_exit_codes])
|
924
|
+
|
925
|
+
puppet_apply_opts = {}
|
926
|
+
puppet_apply_opts[:verbose] = nil
|
927
|
+
puppet_apply_opts[:parseonly] = nil if opts[:parseonly]
|
928
|
+
puppet_apply_opts[:trace] = nil if opts[:trace]
|
929
|
+
puppet_apply_opts[:parser] = 'future' if opts[:future_parser]
|
930
|
+
puppet_apply_opts[:modulepath] = opts[:modulepath] if opts[:modulepath]
|
931
|
+
puppet_apply_opts[:noop] = nil if opts[:noop]
|
932
|
+
|
933
|
+
# From puppet help:
|
934
|
+
# "... an exit code of '2' means there were changes, an exit code of
|
935
|
+
# '4' means there were failures during the transaction, and an exit
|
936
|
+
# code of '6' means there were both changes and failures."
|
937
|
+
if [opts[:catch_changes],opts[:catch_failures],opts[:expect_failures],opts[:expect_changes]].compact.length > 1
|
938
|
+
raise(ArgumentError,
|
939
|
+
'Cannot specify more than one of `catch_failures`, ' +
|
940
|
+
'`catch_changes`, `expect_failures`, or `expect_changes` ' +
|
941
|
+
'for a single manifest')
|
775
942
|
end
|
776
|
-
end
|
777
943
|
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
puppet_apply_opts = {}
|
782
|
-
puppet_apply_opts[:verbose] = nil
|
783
|
-
puppet_apply_opts[:parseonly] = nil if opts[:parseonly]
|
784
|
-
puppet_apply_opts[:trace] = nil if opts[:trace]
|
785
|
-
puppet_apply_opts[:parser] = 'future' if opts[:future_parser]
|
786
|
-
puppet_apply_opts[:modulepath] = opts[:modulepath] if opts[:modulepath]
|
787
|
-
puppet_apply_opts[:noop] = nil if opts[:noop]
|
788
|
-
|
789
|
-
# From puppet help:
|
790
|
-
# "... an exit code of '2' means there were changes, an exit code of
|
791
|
-
# '4' means there were failures during the transaction, and an exit
|
792
|
-
# code of '6' means there were both changes and failures."
|
793
|
-
if [opts[:catch_changes],opts[:catch_failures],opts[:expect_failures],opts[:expect_changes]].compact.length > 1
|
794
|
-
raise(ArgumentError,
|
795
|
-
'Cannot specify more than one of `catch_failures`, ' +
|
796
|
-
'`catch_changes`, `expect_failures`, or `expect_changes` ' +
|
797
|
-
'for a single manifest')
|
798
|
-
end
|
944
|
+
if opts[:catch_changes]
|
945
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
799
946
|
|
800
|
-
|
801
|
-
|
947
|
+
# We're after idempotency so allow exit code 0 only.
|
948
|
+
on_options[:acceptable_exit_codes] |= [0]
|
949
|
+
elsif opts[:catch_failures]
|
950
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
802
951
|
|
803
|
-
|
804
|
-
|
805
|
-
|
806
|
-
|
952
|
+
# We're after only complete success so allow exit codes 0 and 2 only.
|
953
|
+
on_options[:acceptable_exit_codes] |= [0, 2]
|
954
|
+
elsif opts[:expect_failures]
|
955
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
807
956
|
|
808
|
-
|
809
|
-
|
810
|
-
|
811
|
-
|
957
|
+
# We're after failures specifically so allow exit codes 1, 4, and 6 only.
|
958
|
+
on_options[:acceptable_exit_codes] |= [1, 4, 6]
|
959
|
+
elsif opts[:expect_changes]
|
960
|
+
puppet_apply_opts['detailed-exitcodes'] = nil
|
812
961
|
|
813
|
-
|
814
|
-
|
815
|
-
|
816
|
-
|
962
|
+
# We're after changes specifically so allow exit code 2 only.
|
963
|
+
on_options[:acceptable_exit_codes] |= [2]
|
964
|
+
else
|
965
|
+
# Either use the provided acceptable_exit_codes or default to [0]
|
966
|
+
on_options[:acceptable_exit_codes] |= [0]
|
967
|
+
end
|
817
968
|
|
818
|
-
#
|
819
|
-
|
820
|
-
|
821
|
-
#
|
822
|
-
|
823
|
-
|
969
|
+
# Not really thrilled with this implementation, might want to improve it
|
970
|
+
# later. Basically, there is a magic trick in the constructor of
|
971
|
+
# PuppetCommand which allows you to pass in a Hash for the last value in
|
972
|
+
# the *args Array; if you do so, it will be treated specially. So, here
|
973
|
+
# we check to see if our caller passed us a hash of environment variables
|
974
|
+
# that they want to set for the puppet command. If so, we set the final
|
975
|
+
# value of *args to a new hash with just one entry (the value of which
|
976
|
+
# is our environment variables hash)
|
977
|
+
if opts.has_key?(:environment)
|
978
|
+
puppet_apply_opts['ENV'] = opts[:environment]
|
979
|
+
end
|
824
980
|
|
825
|
-
|
826
|
-
|
827
|
-
# PuppetCommand which allows you to pass in a Hash for the last value in
|
828
|
-
# the *args Array; if you do so, it will be treated specially. So, here
|
829
|
-
# we check to see if our caller passed us a hash of environment variables
|
830
|
-
# that they want to set for the puppet command. If so, we set the final
|
831
|
-
# value of *args to a new hash with just one entry (the value of which
|
832
|
-
# is our environment variables hash)
|
833
|
-
if opts.has_key?(:environment)
|
834
|
-
puppet_apply_opts['ENV'] = opts[:environment]
|
835
|
-
end
|
981
|
+
file_path = host.tmpfile('apply_manifest.pp')
|
982
|
+
create_remote_file(host, file_path, manifest + "\n")
|
836
983
|
|
837
|
-
|
838
|
-
|
984
|
+
if host[:default_apply_opts].respond_to? :merge
|
985
|
+
puppet_apply_opts = host[:default_apply_opts].merge( puppet_apply_opts )
|
986
|
+
end
|
839
987
|
|
840
|
-
|
841
|
-
puppet_apply_opts = host[:default_apply_opts].merge( puppet_apply_opts )
|
988
|
+
on host, puppet('apply', file_path, puppet_apply_opts), on_options, &block
|
842
989
|
end
|
843
|
-
|
844
|
-
on host, puppet('apply', file_path, puppet_apply_opts), on_options, &block
|
845
990
|
end
|
846
991
|
|
847
992
|
# Runs 'puppet apply' on default host, piping manifest through stdin
|
@@ -853,9 +998,7 @@ module Beaker
|
|
853
998
|
# @deprecated
|
854
999
|
def run_agent_on(host, arg='--no-daemonize --verbose --onetime --test',
|
855
1000
|
options={}, &block)
|
856
|
-
|
857
|
-
host.each { |h| run_agent_on h, arg, options, &block }
|
858
|
-
else
|
1001
|
+
block_on host do | host |
|
859
1002
|
on host, puppet_agent(arg), options, &block
|
860
1003
|
end
|
861
1004
|
end
|
@@ -863,32 +1006,34 @@ module Beaker
|
|
863
1006
|
# FIX: this should be moved into host/platform
|
864
1007
|
# @visibility private
|
865
1008
|
def run_cron_on(host, action, user, entry="", &block)
|
866
|
-
|
867
|
-
|
868
|
-
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
873
|
-
|
874
|
-
|
875
|
-
|
1009
|
+
block_on host do | host |
|
1010
|
+
platform = host['platform']
|
1011
|
+
if platform.include?('solaris') || platform.include?('aix') then
|
1012
|
+
case action
|
1013
|
+
when :list then args = '-l'
|
1014
|
+
when :remove then args = '-r'
|
1015
|
+
when :add
|
1016
|
+
on( host,
|
1017
|
+
"echo '#{entry}' > /var/spool/cron/crontabs/#{user}",
|
1018
|
+
&block )
|
1019
|
+
end
|
876
1020
|
|
877
|
-
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
1021
|
+
else # default for GNU/Linux platforms
|
1022
|
+
case action
|
1023
|
+
when :list then args = '-l -u'
|
1024
|
+
when :remove then args = '-r -u'
|
1025
|
+
when :add
|
1026
|
+
on( host,
|
1027
|
+
"echo '#{entry}' > /tmp/#{user}.cron && " +
|
1028
|
+
"crontab -u #{user} /tmp/#{user}.cron",
|
1029
|
+
&block )
|
1030
|
+
end
|
886
1031
|
end
|
887
|
-
end
|
888
1032
|
|
889
|
-
|
890
|
-
|
891
|
-
|
1033
|
+
if args
|
1034
|
+
case action
|
1035
|
+
when :list, :remove then on(host, "crontab #{args} #{user}", &block)
|
1036
|
+
end
|
892
1037
|
end
|
893
1038
|
end
|
894
1039
|
end
|
@@ -899,23 +1044,24 @@ module Beaker
|
|
899
1044
|
# A teardown step is also added to make sure unstubbing of the host is
|
900
1045
|
# removed always.
|
901
1046
|
#
|
902
|
-
# @param
|
1047
|
+
# @param [Host, Array<Host>, String, Symbol] machine One or more hosts to act upon,
|
1048
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
903
1049
|
# @param ip_spec [Hash{String=>String}] a hash containing the host to ip
|
904
1050
|
# mappings
|
905
1051
|
# @example Stub puppetlabs.com on the master to 127.0.0.1
|
906
1052
|
# stub_hosts_on(master, 'puppetlabs.com' => '127.0.0.1')
|
907
1053
|
def stub_hosts_on(machine, ip_spec)
|
908
|
-
|
909
|
-
|
910
|
-
|
911
|
-
|
912
|
-
|
1054
|
+
block_on machine do | host |
|
1055
|
+
ip_spec.each do |address, ip|
|
1056
|
+
logger.notify("Stubbing address #{address} to IP #{ip} on machine #{host}")
|
1057
|
+
on( host, puppet('resource', 'host', address, 'ensure=present', "ip=#{ip}") )
|
1058
|
+
end
|
913
1059
|
|
914
|
-
|
915
|
-
|
916
|
-
|
917
|
-
|
918
|
-
|
1060
|
+
teardown do
|
1061
|
+
ip_spec.each do |address, ip|
|
1062
|
+
logger.notify("Unstubbing address #{address} to IP #{ip} on machine #{host}")
|
1063
|
+
on( host, puppet('resource', 'host', address, 'ensure=absent') )
|
1064
|
+
end
|
919
1065
|
end
|
920
1066
|
end
|
921
1067
|
end
|
@@ -943,8 +1089,10 @@ module Beaker
|
|
943
1089
|
#use global options hash
|
944
1090
|
forge_host ||= options[:forge_host]
|
945
1091
|
@forge_ip ||= Resolv.getaddress(forge_host)
|
946
|
-
|
947
|
-
|
1092
|
+
block_on machine do | host |
|
1093
|
+
stub_hosts_on(host, 'forge.puppetlabs.com' => @forge_ip)
|
1094
|
+
stub_hosts_on(host, 'forgeapi.puppetlabs.com' => @forge_ip)
|
1095
|
+
end
|
948
1096
|
end
|
949
1097
|
|
950
1098
|
# This wraps the method `stub_hosts` and makes the stub specific to
|
@@ -1024,32 +1172,36 @@ module Beaker
|
|
1024
1172
|
end
|
1025
1173
|
|
1026
1174
|
#stops the puppet agent running on the host
|
1175
|
+
# @param [Host, Array<Host>, String, Symbol] agent One or more hosts to act upon,
|
1176
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
1027
1177
|
def stop_agent_on(agent)
|
1028
|
-
|
1029
|
-
|
1030
|
-
|
1031
|
-
|
1032
|
-
|
1033
|
-
|
1034
|
-
|
1178
|
+
block_on agent do | host |
|
1179
|
+
vardir = agent.puppet['vardir']
|
1180
|
+
agent_running = true
|
1181
|
+
while agent_running
|
1182
|
+
result = on host, "[ -e '#{vardir}/state/agent_catalog_run.lock' ]", :acceptable_exit_codes => [0,1]
|
1183
|
+
agent_running = (result.exit_code == 0)
|
1184
|
+
sleep 2 unless agent_running
|
1185
|
+
end
|
1035
1186
|
|
1036
|
-
|
1037
|
-
|
1038
|
-
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1187
|
+
# The agent service is `pe-puppet` everywhere EXCEPT certain linux distros on PE 2.8
|
1188
|
+
# In all the case that it is different, this init script will exist. So we can assume
|
1189
|
+
# that if the script doesn't exist, we should just use `pe-puppet`
|
1190
|
+
result = on agent, "[ -e /etc/init.d/pe-puppet-agent ]", :acceptable_exit_codes => [0,1]
|
1191
|
+
agent_service = (result.exit_code == 0) ? 'pe-puppet-agent' : 'pe-puppet'
|
1192
|
+
|
1193
|
+
# Under a number of stupid circumstances, we can't stop the
|
1194
|
+
# agent using puppet. This is usually because of issues with
|
1195
|
+
# the init script or system on that particular configuration.
|
1196
|
+
avoid_puppet_at_all_costs = false
|
1197
|
+
avoid_puppet_at_all_costs ||= agent['platform'] =~ /el-4/
|
1198
|
+
avoid_puppet_at_all_costs ||= agent['pe_ver'] && version_is_less(agent['pe_ver'], '3.2') && agent['platform'] =~ /sles/
|
1199
|
+
|
1200
|
+
if avoid_puppet_at_all_costs
|
1201
|
+
on agent, "/etc/init.d/#{agent_service} stop"
|
1202
|
+
else
|
1203
|
+
on agent, puppet_resource('service', agent_service, 'ensure=stopped')
|
1204
|
+
end
|
1053
1205
|
end
|
1054
1206
|
end
|
1055
1207
|
|
@@ -1068,31 +1220,34 @@ module Beaker
|
|
1068
1220
|
|
1069
1221
|
# Ensure the host has requested a cert, then sign it
|
1070
1222
|
#
|
1071
|
-
# @param [Host] host
|
1223
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
1224
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
1072
1225
|
#
|
1073
1226
|
# @return nil
|
1074
1227
|
# @raise [FailTest] if process times out
|
1075
1228
|
def sign_certificate_for(host)
|
1076
|
-
|
1229
|
+
block_on host do | host |
|
1230
|
+
if [master, dashboard, database].include? host
|
1077
1231
|
|
1078
|
-
|
1079
|
-
|
1232
|
+
on host, puppet( 'agent -t' ), :acceptable_exit_codes => [0,1,2]
|
1233
|
+
on master, puppet( "cert --allow-dns-alt-names sign #{host}" ), :acceptable_exit_codes => [0,24]
|
1080
1234
|
|
1081
|
-
|
1235
|
+
else
|
1082
1236
|
|
1083
|
-
|
1237
|
+
hostname = Regexp.escape host.node_name
|
1084
1238
|
|
1085
|
-
|
1086
|
-
|
1087
|
-
|
1088
|
-
|
1239
|
+
last_sleep = 0
|
1240
|
+
next_sleep = 1
|
1241
|
+
(0..10).each do |i|
|
1242
|
+
fail_test("Failed to sign cert for #{hostname}") if i == 10
|
1089
1243
|
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1093
|
-
|
1094
|
-
|
1244
|
+
on master, puppet("cert --sign --all"), :acceptable_exit_codes => [0,24]
|
1245
|
+
break if on(master, puppet("cert --list --all")).stdout =~ /\+ "?#{hostname}"?/
|
1246
|
+
sleep next_sleep
|
1247
|
+
(last_sleep, next_sleep) = next_sleep, last_sleep+next_sleep
|
1248
|
+
end
|
1095
1249
|
|
1250
|
+
end
|
1096
1251
|
end
|
1097
1252
|
end
|
1098
1253
|
|
@@ -1104,7 +1259,8 @@ module Beaker
|
|
1104
1259
|
|
1105
1260
|
# Get a facter fact from a provided host
|
1106
1261
|
#
|
1107
|
-
# @param [Host] host
|
1262
|
+
# @param [Host, Array<Host>, String, Symbol] host One or more hosts to act upon,
|
1263
|
+
# or a role (String or Symbol) that identifies one or more hosts.
|
1108
1264
|
# @param [String] name The name of the fact to query for
|
1109
1265
|
# @!macro common_opts
|
1110
1266
|
#
|
@@ -1112,7 +1268,11 @@ module Beaker
|
|
1112
1268
|
# @raise [FailTest] Raises an exception if call to facter fails
|
1113
1269
|
def fact_on(host, name, opts = {})
|
1114
1270
|
result = on host, facter(name, opts)
|
1115
|
-
|
1271
|
+
if result.kind_of?(Array)
|
1272
|
+
result.map { |res| res.stdout.chomp }
|
1273
|
+
else
|
1274
|
+
result.stdout.chomp
|
1275
|
+
end
|
1116
1276
|
end
|
1117
1277
|
|
1118
1278
|
# Get a facter fact from the default host
|
@@ -1200,7 +1360,7 @@ module Beaker
|
|
1200
1360
|
module_name = nil
|
1201
1361
|
if File.exists?("#{root_module_dir}/metadata.json")
|
1202
1362
|
logger.debug "Attempting to parse Modulename from metadata.json"
|
1203
|
-
module_json = JSON.parse
|
1363
|
+
module_json = JSON.parse(File.read "#{root_module_dir}/metadata.json")
|
1204
1364
|
if(module_json.has_key?('name'))
|
1205
1365
|
module_name = get_module_name(module_json['name'])
|
1206
1366
|
end
|