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
@@ -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
|