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
@@ -4,50 +4,84 @@ module Beaker
|
|
4
4
|
#into the Beaker options Object.
|
5
5
|
module Presets
|
6
6
|
|
7
|
+
# This is a constant that describes the variables we want to collect
|
8
|
+
# from the environment. The keys correspond to the keys in
|
9
|
+
# `self.presets` (flattened) The values are an optional array of
|
10
|
+
# environment variable names to look for. The array structure allows
|
11
|
+
# us to define multiple environment variables for the same
|
12
|
+
# configuration value. They are checked in the order they are arrayed
|
13
|
+
# so that preferred and "fallback" values work as expected.
|
14
|
+
ENVIRONMENT_SPEC = {
|
15
|
+
:home => 'HOME',
|
16
|
+
:project => ['BEAKER_PROJECT', 'BEAKER_project'],
|
17
|
+
:department => ['BEAKER_DEPARTMENT', 'BEAKER_department'],
|
18
|
+
:jenkins_build_url => ['BEAKER_BUILD_URL', 'BUILD_URL'],
|
19
|
+
:consoleport => ['BEAKER_CONSOLEPORT', 'consoleport'],
|
20
|
+
:is_pe => ['BEAKER_IS_PE', 'IS_PE'],
|
21
|
+
:pe_dir => ['BEAKER_PE_DIR', 'pe_dist_dir'],
|
22
|
+
:pe_version_file => ['BEAKER_PE_VERSION_FILE', 'pe_version_file'],
|
23
|
+
:pe_ver => ['BEAKER_PE_VER', 'pe_ver'],
|
24
|
+
:forge_host => ['BEAKER_FORGE_HOST', 'forge_host'],
|
25
|
+
:package_proxy => ['BEAKER_PACKAGE_PROXY'],
|
26
|
+
:release_apt_repo_url => ['BEAKER_RELEASE_APT_REPO', 'RELEASE_APT_REPO'],
|
27
|
+
:release_yum_repo_url => ['BEAKER_RELEASE_YUM_REPO', 'RELEASE_YUM_REPO'],
|
28
|
+
:dev_builds_url => ['BEAKER_DEV_BUILDS_URL', 'DEV_BUILDS_URL'],
|
29
|
+
:q_puppet_enterpriseconsole_auth_user_email => 'q_puppet_enterpriseconsole_auth_user_email',
|
30
|
+
:q_puppet_enterpriseconsole_auth_password => 'q_puppet_enterpriseconsole_auth_password',
|
31
|
+
:q_puppet_enterpriseconsole_smtp_host => 'q_puppet_enterpriseconsole_smtp_host',
|
32
|
+
:q_puppet_enterpriseconsole_smtp_port => 'q_puppet_enterpriseconsole_smtp_port',
|
33
|
+
:q_puppet_enterpriseconsole_smtp_username => 'q_puppet_enterpriseconsole_smtp_username',
|
34
|
+
:q_puppet_enterpriseconsole_smtp_password => 'q_puppet_enterpriseconsole_smtp_password',
|
35
|
+
:q_puppet_enterpriseconsole_smtp_use_tls => 'q_puppet_enterpriseconsole_smtp_use_tls',
|
36
|
+
:q_verify_packages => 'q_verify_packages',
|
37
|
+
:q_puppetdb_password => 'q_puppetdb_password',
|
38
|
+
}
|
39
|
+
|
40
|
+
# Takes an environment_spec and searches the processes environment variables accordingly
|
41
|
+
#
|
42
|
+
# @param [Hash{Symbol=>Array,String}] env_var_spec the spec of what env vars to search for
|
43
|
+
#
|
44
|
+
# @return [Hash] Found environment values
|
45
|
+
def self.collect_env_vars( env_var_spec )
|
46
|
+
env_var_spec.inject({:answers => {}}) do |memo, key_value|
|
47
|
+
key, value = key_value[0], key_value[1]
|
48
|
+
|
49
|
+
set_env_var = Array(value).detect {|possible_variable| ENV[possible_variable] }
|
50
|
+
memo[key] = ENV[set_env_var] if set_env_var
|
51
|
+
|
52
|
+
memo
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Takes a hash where the values are found environment configuration values
|
57
|
+
# and munges them to appropriate Beaker configuration values
|
58
|
+
#
|
59
|
+
# @param [Hash{Symbol=>String}] found_env_vars Environment variables to munge
|
60
|
+
#
|
61
|
+
# @return [Hash] Environment config values munged appropriately
|
62
|
+
def self.munge_found_env_vars( found_env_vars )
|
63
|
+
found_env_vars[:answers] ||= {}
|
64
|
+
found_env_vars.each_pair do |key,value|
|
65
|
+
found_env_vars[:answers][key] = value if key.to_s =~ /q_/
|
66
|
+
end
|
67
|
+
found_env_vars[:consoleport] &&= found_env_vars[:consoleport].to_i
|
68
|
+
found_env_vars[:type] = found_env_vars[:is_pe] == 'true' || found_env_vars[:is_pe] == 'yes' ? 'pe' : nil
|
69
|
+
found_env_vars[:pe_version_file_win] = found_env_vars[:pe_version_file]
|
70
|
+
found_env_vars[:answers].delete_if {|key, value| value.nil? or value.empty? }
|
71
|
+
found_env_vars.delete_if {|key, value| value.nil? or value.empty? }
|
72
|
+
end
|
73
|
+
|
74
|
+
|
7
75
|
# Generates an OptionsHash of the environment variables of interest to Beaker
|
8
76
|
#
|
9
77
|
# @return [OptionsHash] The supported environment variables in an OptionsHash,
|
10
78
|
# empty or nil environment variables are removed from the OptionsHash
|
11
79
|
def self.env_vars
|
12
80
|
h = Beaker::Options::OptionsHash.new
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
:department => ENV['BEAKER_DEPARTMENT'] || ENV['BEAKER_department'],
|
18
|
-
:jenkins_build_url => ENV['BEAKER_BUILD_URL'] || ENV['BUILD_URL'],
|
19
|
-
:release_apt_repo_url=> ENV['BEAKER_RELEASE_APT_REPO'] || ENV['RELEASE_APT_REPO'] || "http://apt.puppetlabs.com",
|
20
|
-
:release_yum_repo_url=> ENV['BEAKER_RELEASE_YUM_REPO'] || ENV['RELEASE_YUM_REPO'] || "http://yum.puppetlabs.com",
|
21
|
-
:dev_builds_url => ENV['BEAKER_DEV_BUILDS_URL'] || ENV['DEV_BUILDS_URL'] || "http://builds.puppetlabs.lan",
|
22
|
-
:consoleport => consoleport ? consoleport.to_i : nil,
|
23
|
-
:type => (ENV['BEAKER_IS_PE'] || ENV['IS_PE']) ? 'pe' : nil,
|
24
|
-
:pe_dir => ENV['BEAKER_PE_DIR'] || ENV['pe_dist_dir'],
|
25
|
-
:pe_version_file => ENV['BEAKER_PE_VERSION_FILE'] || ENV['pe_version_file'],
|
26
|
-
:pe_version_file_win => ENV['BEAKER_PE_VERSION_FILE'] || ENV['pe_version_file'],
|
27
|
-
:pe_ver => ENV['BEAKER_PE_VER'] || ENV['pe_ver'],
|
28
|
-
:forge_host => ENV['BEAKER_FORGE_HOST'] || ENV['forge_host'],
|
29
|
-
:answers => {
|
30
|
-
:q_puppet_enterpriseconsole_auth_user_email =>
|
31
|
-
ENV['q_puppet_enterpriseconsole_auth_user_email'] || 'admin@example.com',
|
32
|
-
:q_puppet_enterpriseconsole_auth_password =>
|
33
|
-
ENV['q_puppet_enterpriseconsole_auth_password'] || '~!@#$%^*-/ aZ',
|
34
|
-
:q_puppet_enterpriseconsole_smtp_host =>
|
35
|
-
ENV['q_puppet_enterpriseconsole_smtp_host'],
|
36
|
-
:q_puppet_enterpriseconsole_smtp_port =>
|
37
|
-
ENV['q_puppet_enterpriseconsole_smtp_port'] || 25,
|
38
|
-
:q_puppet_enterpriseconsole_smtp_username =>
|
39
|
-
ENV['q_puppet_enterpriseconsole_smtp_username'],
|
40
|
-
:q_puppet_enterpriseconsole_smtp_password =>
|
41
|
-
ENV['q_puppet_enterpriseconsole_smtp_password'],
|
42
|
-
:q_puppet_enterpriseconsole_smtp_use_tls =>
|
43
|
-
ENV['q_puppet_enterpriseconsole_smtp_use_tls'] || 'n',
|
44
|
-
:q_verify_packages =>
|
45
|
-
ENV['q_verify_packages'] || 'y',
|
46
|
-
:q_puppetdb_password =>
|
47
|
-
ENV['q_puppetdb_password'] || '~!@#$%^*-/ aZ',
|
48
|
-
},
|
49
|
-
:package_proxy => ENV['BEAKER_PACKAGE_PROXY']
|
50
|
-
}.delete_if {|key, value| value.nil? or value.empty? })
|
81
|
+
|
82
|
+
found = munge_found_env_vars( collect_env_vars( ENVIRONMENT_SPEC ))
|
83
|
+
|
84
|
+
return h.merge( found )
|
51
85
|
end
|
52
86
|
|
53
87
|
# Generates an OptionsHash of preset values for arguments supported by Beaker
|
@@ -56,53 +90,64 @@ module Beaker
|
|
56
90
|
def self.presets
|
57
91
|
h = Beaker::Options::OptionsHash.new
|
58
92
|
h.merge({
|
59
|
-
:project
|
60
|
-
:department
|
61
|
-
:validate
|
62
|
-
:jenkins_build_url
|
63
|
-
:forge_host
|
64
|
-
:log_level
|
65
|
-
:trace_limit
|
66
|
-
:
|
67
|
-
:options_file
|
68
|
-
:type
|
69
|
-
:provision
|
70
|
-
:preserve_hosts
|
71
|
-
:root_keys
|
72
|
-
:quiet
|
73
|
-
:project_root
|
74
|
-
:xml_dir
|
75
|
-
:xml_file
|
76
|
-
:xml_stylesheet
|
77
|
-
:log_dir
|
78
|
-
:color
|
79
|
-
:dry_run
|
80
|
-
:timeout
|
81
|
-
:fail_mode
|
82
|
-
:timesync
|
83
|
-
:repo_proxy
|
84
|
-
:package_proxy
|
85
|
-
:add_el_extras
|
86
|
-
:
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
92
|
-
:
|
93
|
-
:
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
93
|
+
:project => 'Beaker',
|
94
|
+
:department => ENV['USER'] || ENV['USERNAME'] || 'unknown',
|
95
|
+
:validate => true,
|
96
|
+
:jenkins_build_url => nil,
|
97
|
+
:forge_host => 'vulcan-acceptance.delivery.puppetlabs.net',
|
98
|
+
:log_level => 'verbose',
|
99
|
+
:trace_limit => 10,
|
100
|
+
:"master-start-curl-retries" => 0,
|
101
|
+
:options_file => nil,
|
102
|
+
:type => 'pe',
|
103
|
+
:provision => true,
|
104
|
+
:preserve_hosts => 'never',
|
105
|
+
:root_keys => false,
|
106
|
+
:quiet => false,
|
107
|
+
:project_root => File.expand_path(File.join(File.dirname(__FILE__), "../")),
|
108
|
+
:xml_dir => 'junit',
|
109
|
+
:xml_file => 'beaker_junit.xml',
|
110
|
+
:xml_stylesheet => 'junit.xsl',
|
111
|
+
:log_dir => 'log',
|
112
|
+
:color => true,
|
113
|
+
:dry_run => false,
|
114
|
+
:timeout => 300,
|
115
|
+
:fail_mode => 'slow',
|
116
|
+
:timesync => false,
|
117
|
+
:repo_proxy => false,
|
118
|
+
:package_proxy => false,
|
119
|
+
:add_el_extras => false,
|
120
|
+
:release_apt_repo_url => "http://apt.puppetlabs.com",
|
121
|
+
:release_yum_repo_url => "http://yum.puppetlabs.com",
|
122
|
+
:dev_builds_url => "http://builds.puppetlabs.lan",
|
123
|
+
:consoleport => 443,
|
124
|
+
:pe_dir => '/opt/enterprise/dists',
|
125
|
+
:pe_version_file => 'LATEST',
|
126
|
+
:pe_version_file_win => 'LATEST-win',
|
127
|
+
:answers => {
|
128
|
+
:q_puppet_enterpriseconsole_auth_user_email => 'admin@example.com',
|
129
|
+
:q_puppet_enterpriseconsole_auth_password => '~!@#$%^*-/ aZ',
|
130
|
+
:q_puppet_enterpriseconsole_smtp_port => 25,
|
131
|
+
:q_puppet_enterpriseconsole_smtp_use_tls => 'n',
|
132
|
+
:q_verify_packages => 'y',
|
133
|
+
:q_puppetdb_password => '~!@#$%^*-/ aZ'
|
134
|
+
},
|
135
|
+
:dot_fog => File.join(ENV['HOME'], '.fog'),
|
136
|
+
:ec2_yaml => 'config/image_templates/ec2.yaml',
|
137
|
+
:help => false,
|
138
|
+
:collect_perf_data => false,
|
139
|
+
:ssh => {
|
140
|
+
:config => false,
|
141
|
+
:paranoid => false,
|
142
|
+
:timeout => 300,
|
143
|
+
:auth_methods => ["publickey"],
|
144
|
+
:port => 22,
|
145
|
+
:forward_agent => true,
|
146
|
+
:keys => ["#{ENV['HOME']}/.ssh/id_rsa"],
|
147
|
+
:user_known_hosts_file => "#{ENV['HOME']}/.ssh/known_hosts",
|
148
|
+
}
|
103
149
|
})
|
104
150
|
end
|
105
|
-
|
106
151
|
end
|
107
152
|
end
|
108
153
|
end
|
data/lib/beaker/perf.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
module Beaker
|
2
|
+
# The Beaker Perf class. A single instance is created per Beaker run.
|
3
|
+
class Perf
|
4
|
+
|
5
|
+
# Create the Perf instance and runs setup_perf_on_host on all hosts if --collect-perf-data
|
6
|
+
# was used as an option on the Baker command line invocation. Instances of this class do not
|
7
|
+
# hold state and its methods are helpers for remotely executing tasks for performance data
|
8
|
+
# gathering with sysstat/sar
|
9
|
+
#
|
10
|
+
# @param [Array<Host>] hosts All from the configuration
|
11
|
+
# @param [Hash] options Options to alter execution
|
12
|
+
# @return [void]
|
13
|
+
def initialize( hosts, options )
|
14
|
+
@hosts = hosts
|
15
|
+
@options = options
|
16
|
+
@logger = options[:logger]
|
17
|
+
@perf_timestamp = Time.now
|
18
|
+
@hosts.map { |h| setup_perf_on_host(h) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Some systems need special modification to make sysstat work. This is done here.
|
22
|
+
# @param [Host] host The host we are working with
|
23
|
+
# @return [void]
|
24
|
+
def setup_perf_on_host(host)
|
25
|
+
@logger.perf_output("Setup perf on host: " + host)
|
26
|
+
if host['platform'] =~ /debian|ubuntu/
|
27
|
+
@logger.perf_output("Modify /etc/default/sysstat on Debian and Ubuntu platforms")
|
28
|
+
host.exec(Command.new('sed -i s/ENABLED=\"false\"/ENABLED=\"true\"/ /etc/default/sysstat'))
|
29
|
+
elsif host['platform'] =~ /sles/
|
30
|
+
@logger.perf_output("Creating symlink from /etc/sysstat/sysstat.cron to /etc/cron.d")
|
31
|
+
host.exec(Command.new('ln -s /etc/sysstat/sysstat.cron /etc/cron.d'),:acceptable_exit_codes => [0,1])
|
32
|
+
end
|
33
|
+
if host['platform'] =~ /debian|ubuntu|redhat|centos/ # SLES doesn't need this step
|
34
|
+
host.exec(Command.new('service sysstat start'))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Iterate over all hosts, calling get_perf_data
|
39
|
+
# @param [void]
|
40
|
+
# @return [void]
|
41
|
+
def print_perf_info()
|
42
|
+
@perf_end_timestamp = Time.now
|
43
|
+
@hosts.map { |h| get_perf_data(h, @perf_timestamp, @perf_end_timestamp) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# If host is a supported (ie linux) platform, generate a performance report
|
47
|
+
# @param [Host] host The host we are working with
|
48
|
+
# @param [Time] perf_start The beginning time for the SAR report
|
49
|
+
# @param [Time] perf_end The ending time for the SAR report
|
50
|
+
# @return [void] The report is sent to the logging output
|
51
|
+
def get_perf_data(host, perf_start, perf_end)
|
52
|
+
@logger.perf_output("Getting perf data for host: " + host)
|
53
|
+
if host['platform'] =~ /debian|ubuntu|redhat|centos|sles/
|
54
|
+
host.exec(Command.new("sar -A -s #{perf_start.strftime("%H:%M:%S")} -e #{perf_end.strftime("%H:%M:%S")}"))
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Beaker
|
2
|
+
module Shared
|
3
|
+
#Methods for managing Hosts.
|
4
|
+
#- selecting hosts by role (Symbol or String)
|
5
|
+
#- selecting hosts by name (String)
|
6
|
+
#- adding additional method definitions for selecting by role
|
7
|
+
#- executing blocks of code against selected sets of hosts
|
8
|
+
module HostManager
|
9
|
+
|
10
|
+
#Find hosts from a given array of hosts that all have the desired role.
|
11
|
+
#@param [Array<Host>] hosts The hosts to examine
|
12
|
+
#@param [String] desired_role The hosts returned will have this role in their roles list
|
13
|
+
#@return [Array<Host>] The hosts that have the desired role in their roles list
|
14
|
+
def hosts_with_role(hosts, desired_role = nil)
|
15
|
+
hosts.select do |host|
|
16
|
+
desired_role.nil? or host['roles'].include?(desired_role.to_s)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
#Find hosts from a given array of hosts that all have the desired name.
|
21
|
+
#@param [Array<Host>] hosts The hosts to examine
|
22
|
+
#@param [String] name The hosts returned will have this name
|
23
|
+
#@return [Array<Host>] The hosts that have the desired name
|
24
|
+
def hosts_with_name(hosts, name = nil)
|
25
|
+
hosts.select do |host|
|
26
|
+
name.nil? or host.name =~ /\A#{name}/
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#Find a single host with the role provided. Raise an error if more than one host is found to have the
|
31
|
+
#provided role.
|
32
|
+
#@param [Array<Host>] hosts The hosts to examine
|
33
|
+
#@param [String] role The host returned will have this role in its role list
|
34
|
+
#@return [Host] The single host with the desired role in its roles list
|
35
|
+
#@raise [ArgumentError] Raised if more than one host has the given role defined, or if no host has the
|
36
|
+
# role defined.
|
37
|
+
def only_host_with_role(hosts, role)
|
38
|
+
a_host = hosts_with_role(hosts, role)
|
39
|
+
case
|
40
|
+
when a_host.length == 0
|
41
|
+
raise ArgumentError, "There should be one host with #{role} defined!"
|
42
|
+
when a_host.length > 1
|
43
|
+
host_string = ( a_host.map { |host| host.name } ).join( ', ')
|
44
|
+
raise ArgumentError, "There should be only one host with #{role} defined, but I found #{a_host.length} (#{host_string})"
|
45
|
+
end
|
46
|
+
a_host.first
|
47
|
+
end
|
48
|
+
|
49
|
+
#Execute a block selecting the hosts that match with the provided criteria
|
50
|
+
#@param [Array<Host>, Host] hosts The host or hosts to run the provided block against
|
51
|
+
#@param [String, Symbol] filter Optional filter to apply to provided hosts - limits by name or role
|
52
|
+
#@param [Block] block This method will yield to a block of code passed by the caller
|
53
|
+
def run_block_on hosts = [], filter = nil, &block
|
54
|
+
result = nil
|
55
|
+
block_hosts = hosts #the hosts to apply the block to after any filtering
|
56
|
+
if filter
|
57
|
+
if not hosts.empty?
|
58
|
+
block_hosts = hosts_with_role(hosts, filter) #check by role
|
59
|
+
if block_hosts.empty?
|
60
|
+
block_hosts = hosts_with_name(hosts, filter) #check by name
|
61
|
+
end
|
62
|
+
if block_hosts.length == 1 #we only found one matching host, don't need it wrapped in an array
|
63
|
+
block_hosts = block_hosts.pop
|
64
|
+
end
|
65
|
+
else
|
66
|
+
raise ArgumentError, "Unable to sort for #{filter} type hosts when provided with [] as Hosts"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
if block_hosts.is_a? Array
|
70
|
+
result = block_hosts.map do |h|
|
71
|
+
run_block_on h, nil, &block
|
72
|
+
end
|
73
|
+
else
|
74
|
+
result = yield block_hosts
|
75
|
+
end
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/beaker/shared.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
[ 'repetition', 'error_handler', '
|
1
|
+
[ 'repetition', 'error_handler', 'host_manager', 'timed' ].each do |lib|
|
2
2
|
require "beaker/shared/#{lib}"
|
3
3
|
end
|
4
4
|
module Beaker
|
5
5
|
module Shared
|
6
6
|
include Beaker::Shared::ErrorHandler
|
7
|
-
include Beaker::Shared::
|
7
|
+
include Beaker::Shared::HostManager
|
8
8
|
include Beaker::Shared::Repetition
|
9
9
|
include Beaker::Shared::Timed
|
10
10
|
end
|
@@ -94,6 +94,7 @@ module Beaker
|
|
94
94
|
@ssh.loop
|
95
95
|
|
96
96
|
result.finalize!
|
97
|
+
@logger.last_result = result
|
97
98
|
result
|
98
99
|
end
|
99
100
|
|
@@ -165,14 +166,17 @@ module Beaker
|
|
165
166
|
def scp_to source, target, options = {}, dry_run = false
|
166
167
|
return if dry_run
|
167
168
|
|
168
|
-
options[:recursive]=File.directory?(source) if options[:recursive].nil?
|
169
|
-
|
170
|
-
@ssh.scp.upload! source, target, options
|
169
|
+
options[:recursive] = File.directory?(source) if options[:recursive].nil?
|
170
|
+
options[:chunk_size] = options[:chunk_size] || 16384
|
171
171
|
|
172
172
|
result = Result.new(@hostname, [source, target])
|
173
|
+
result.stdout = "\n"
|
174
|
+
@ssh.scp.upload! source, target, options do |ch, name, sent, total|
|
175
|
+
result.stdout << "\tcopying %s: %10d/%d\n" % [name, sent, total]
|
176
|
+
end
|
173
177
|
|
174
178
|
# Setting these values allows reporting via result.log(test_name)
|
175
|
-
result.stdout
|
179
|
+
result.stdout << " SCP'ed file #{source} to #{@hostname}:#{target}"
|
176
180
|
|
177
181
|
# Net::Scp always returns 0, so just set the return code to 0.
|
178
182
|
result.exit_code = 0
|
@@ -185,13 +189,16 @@ module Beaker
|
|
185
189
|
return if dry_run
|
186
190
|
|
187
191
|
options[:recursive] = true if options[:recursive].nil?
|
188
|
-
|
189
|
-
@ssh.scp.download! source, target, options
|
192
|
+
options[:chunk_size] = options[:chunk_size] || 16384
|
190
193
|
|
191
194
|
result = Result.new(@hostname, [source, target])
|
195
|
+
result.stdout = "\n"
|
196
|
+
@ssh.scp.download! source, target, options do |ch, name, sent, total|
|
197
|
+
result.stdout << "\tcopying %s: %10d/%d\n" % [name, sent, total]
|
198
|
+
end
|
192
199
|
|
193
200
|
# Setting these values allows reporting via result.log(test_name)
|
194
|
-
result.stdout
|
201
|
+
result.stdout << " SCP'ed file #{@hostname}:#{source} to #{target}"
|
195
202
|
|
196
203
|
# Net::Scp always returns 0, so just set the return code to 0.
|
197
204
|
result.exit_code = 0
|
data/lib/beaker/test_case.rb
CHANGED
@@ -55,6 +55,9 @@ module Beaker
|
|
55
55
|
#The full log for this test
|
56
56
|
attr_accessor :sublog
|
57
57
|
|
58
|
+
#The result for the last command run
|
59
|
+
attr_accessor :last_result
|
60
|
+
|
58
61
|
# A Hash of 'product name' => 'version installed', only set when
|
59
62
|
# products are installed via git or PE install steps. See the 'git' or
|
60
63
|
# 'pe' directories within 'ROOT/setup' for examples.
|
@@ -121,6 +124,15 @@ module Beaker
|
|
121
124
|
class << self
|
122
125
|
def run_test
|
123
126
|
@logger.start_sublog
|
127
|
+
@logger.last_result = nil
|
128
|
+
|
129
|
+
#add arbitrary role methods
|
130
|
+
roles = []
|
131
|
+
@hosts.each do |host|
|
132
|
+
roles << host[:roles]
|
133
|
+
end
|
134
|
+
add_role_def( roles.flatten.uniq )
|
135
|
+
|
124
136
|
@runtime = Benchmark.realtime do
|
125
137
|
begin
|
126
138
|
test = File.read(path)
|
@@ -145,6 +157,7 @@ module Beaker
|
|
145
157
|
end
|
146
158
|
end
|
147
159
|
@sublog = @logger.get_sublog
|
160
|
+
@last_result = @logger.last_result
|
148
161
|
return self
|
149
162
|
end
|
150
163
|
|
data/lib/beaker/test_suite.rb
CHANGED
@@ -155,6 +155,20 @@ module Beaker
|
|
155
155
|
text.gsub(/(\e|\^\[)\[(\d*;)*\d*m/, '')
|
156
156
|
end
|
157
157
|
|
158
|
+
# Escape invalid XML UTF-8 codes from provided string, see http://www.w3.org/TR/xml/#charsets for valid
|
159
|
+
# character specification
|
160
|
+
# @param [String] string The string to remove invalid codes from
|
161
|
+
def escape_invalid_xml_chars string
|
162
|
+
re = /\u0009|\u000A|\u000D|[\u0020-\uD7FF]|[\uE000-\uFFFD]|[\u10000-\u10FFFF]/
|
163
|
+
return string.chars.map{|i| i =~ re ? i : "\\#{i.unpack("U*").join}"}.join
|
164
|
+
end
|
165
|
+
|
166
|
+
# Remove color codes and invalid XML characters from provided string
|
167
|
+
# @param [String] string The string to format
|
168
|
+
def format_cdata string
|
169
|
+
escape_invalid_xml_chars(strip_color_codes(string))
|
170
|
+
end
|
171
|
+
|
158
172
|
#Format and print the {TestSuiteResult} as JUnit XML
|
159
173
|
#@param [String] xml_file The full path to print the output to.
|
160
174
|
#@param [String] stylesheet The full path to a JUnit XML stylesheet
|
@@ -178,7 +192,8 @@ module Beaker
|
|
178
192
|
end
|
179
193
|
else
|
180
194
|
#no existing file, create a new one
|
181
|
-
doc = Nokogiri::XML::Document.new
|
195
|
+
doc = Nokogiri::XML::Document.new()
|
196
|
+
doc.encoding = 'UTF-8'
|
182
197
|
pi = Nokogiri::XML::ProcessingInstruction.new(doc, "xml-stylesheet", "type=\"text/xsl\" href=\"#{File.basename(stylesheet)}\"")
|
183
198
|
pi.parent = doc
|
184
199
|
suites = Nokogiri::XML::Node.new('testsuites', doc)
|
@@ -217,7 +232,8 @@ module Beaker
|
|
217
232
|
status['type'] = test.test_status.to_s
|
218
233
|
if test.exception then
|
219
234
|
status['message'] = test.exception.to_s.gsub(/\e/, '')
|
220
|
-
|
235
|
+
data = format_cdata(test.exception.backtrace.join('\n'))
|
236
|
+
status.add_child(status.document.create_cdata(data))
|
221
237
|
end
|
222
238
|
item.add_child(status)
|
223
239
|
end
|
@@ -236,13 +252,15 @@ module Beaker
|
|
236
252
|
|
237
253
|
if test.sublog then
|
238
254
|
stdout = Nokogiri::XML::Node.new('system-out', doc)
|
239
|
-
|
255
|
+
data = format_cdata(test.sublog)
|
256
|
+
stdout.add_child(stdout.document.create_cdata(data))
|
240
257
|
item.add_child(stdout)
|
241
258
|
end
|
242
259
|
|
243
|
-
if test.stderr then
|
260
|
+
if test.last_result and test.last_result.stderr and not test.last_result.stderr.empty? then
|
244
261
|
stderr = Nokogiri::XML::Node.new('system-err', doc)
|
245
|
-
|
262
|
+
data = format_cdata(test.last_result.stderr)
|
263
|
+
stderr.add_child(stderr.document.create_cdata(data))
|
246
264
|
item.add_child(stderr)
|
247
265
|
end
|
248
266
|
|
data/lib/beaker/version.rb
CHANGED
data/lib/beaker.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'rubygems' unless defined?(Gem)
|
2
2
|
module Beaker
|
3
3
|
|
4
|
-
%w( version platform test_suite result command options network_manager cli ).each do |lib|
|
4
|
+
%w( version platform test_suite result command options network_manager cli perf ).each do |lib|
|
5
5
|
begin
|
6
6
|
require "beaker/#{lib}"
|
7
7
|
rescue LoadError
|
data/spec/beaker/answers_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
module Beaker
|
4
4
|
describe Answers do
|
5
5
|
let( :basic_hosts ) { make_hosts( { 'pe_ver' => @ver } ) }
|
6
|
-
let( :options ) { Beaker::Options::Presets.
|
6
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
7
7
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'database', 'dashboard']
|
8
8
|
basic_hosts[1]['platform'] = 'windows'
|
9
9
|
basic_hosts }
|
@@ -59,7 +59,7 @@ module Beaker
|
|
59
59
|
|
60
60
|
module Answers
|
61
61
|
describe Version34 do
|
62
|
-
let( :options ) { Beaker::Options::Presets.
|
62
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
63
63
|
let( :basic_hosts ) { make_hosts( {'pe_ver' => @ver } ) }
|
64
64
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'agent']
|
65
65
|
basic_hosts[1]['roles'] = ['dashboard', 'agent']
|
@@ -73,10 +73,17 @@ module Beaker
|
|
73
73
|
expect( host[:answers] ).to be === answers[host.name]
|
74
74
|
end
|
75
75
|
end
|
76
|
+
|
77
|
+
it 'should add q_jvm_puppetmaster to the master answers' do
|
78
|
+
@ver = '3.4'
|
79
|
+
answers = subject.answers( hosts, master_certname, options )
|
80
|
+
expect( subject.answers( hosts, master_certname, options )['vm1']).to include :q_jvm_puppetmaster
|
81
|
+
end
|
82
|
+
|
76
83
|
end
|
77
84
|
|
78
85
|
describe Version32 do
|
79
|
-
let( :options ) { Beaker::Options::Presets.
|
86
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
80
87
|
let( :basic_hosts ) { make_hosts( {'pe_ver' => @ver } ) }
|
81
88
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'agent']
|
82
89
|
basic_hosts[1]['roles'] = ['dashboard', 'agent']
|
@@ -106,7 +113,7 @@ module Beaker
|
|
106
113
|
end
|
107
114
|
|
108
115
|
describe Version30 do
|
109
|
-
let( :options ) { Beaker::Options::Presets.
|
116
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
110
117
|
let( :basic_hosts ) { make_hosts( { 'pe_ver' => @ver } ) }
|
111
118
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'database', 'dashboard']
|
112
119
|
basic_hosts[1]['platform'] = 'windows'
|
@@ -142,7 +149,7 @@ module Beaker
|
|
142
149
|
end
|
143
150
|
|
144
151
|
describe Version28 do
|
145
|
-
let( :options ) { Beaker::Options::Presets.
|
152
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
146
153
|
let( :basic_hosts ) { make_hosts( { 'pe_ver' => @ver } ) }
|
147
154
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'database', 'dashboard']
|
148
155
|
basic_hosts[1]['platform'] = 'windows'
|
@@ -174,7 +181,7 @@ module Beaker
|
|
174
181
|
|
175
182
|
end
|
176
183
|
describe Version20 do
|
177
|
-
let( :options ) { Beaker::Options::Presets.
|
184
|
+
let( :options ) { Beaker::Options::Presets.presets }
|
178
185
|
let( :basic_hosts ) { make_hosts( { 'pe_ver' => @ver } ) }
|
179
186
|
let( :hosts ) { basic_hosts[0]['roles'] = ['master', 'database', 'dashboard']
|
180
187
|
basic_hosts[1]['platform'] = 'windows'
|
@@ -203,8 +210,6 @@ module Beaker
|
|
203
210
|
expect( host[:answers] ).to be === answers[host.name]
|
204
211
|
end
|
205
212
|
end
|
206
|
-
|
207
213
|
end
|
208
|
-
|
209
214
|
end
|
210
215
|
end
|