beaker 1.16.0 → 1.17.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|