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.
Files changed (51) hide show
  1. checksums.yaml +8 -8
  2. data/CONTRIBUTING.md +90 -0
  3. data/HISTORY.md +654 -2
  4. data/beaker.gemspec +1 -0
  5. data/lib/beaker/answers/version34.rb +4 -0
  6. data/lib/beaker/cli.rb +49 -2
  7. data/lib/beaker/dsl/helpers.rb +356 -196
  8. data/lib/beaker/dsl/install_utils.rb +135 -16
  9. data/lib/beaker/dsl/patterns.rb +37 -0
  10. data/lib/beaker/dsl/roles.rb +29 -0
  11. data/lib/beaker/dsl.rb +2 -1
  12. data/lib/beaker/host/unix.rb +14 -10
  13. data/lib/beaker/host/windows.rb +2 -0
  14. data/lib/beaker/host.rb +96 -1
  15. data/lib/beaker/host_prebuilt_steps.rb +41 -51
  16. data/lib/beaker/hypervisor/aws_sdk.rb +80 -16
  17. data/lib/beaker/hypervisor/ec2_helper.rb +1 -1
  18. data/lib/beaker/logger.rb +17 -0
  19. data/lib/beaker/options/command_line_parser.rb +3 -0
  20. data/lib/beaker/options/hosts_file_parser.rb +7 -4
  21. data/lib/beaker/options/options_hash.rb +2 -2
  22. data/lib/beaker/options/parser.rb +1 -1
  23. data/lib/beaker/options/presets.rb +128 -83
  24. data/lib/beaker/perf.rb +58 -0
  25. data/lib/beaker/shared/host_manager.rb +81 -0
  26. data/lib/beaker/shared.rb +2 -2
  27. data/lib/beaker/ssh_connection.rb +14 -7
  28. data/lib/beaker/test_case.rb +13 -0
  29. data/lib/beaker/test_suite.rb +23 -5
  30. data/lib/beaker/version.rb +1 -1
  31. data/lib/beaker.rb +1 -1
  32. data/spec/beaker/answers_spec.rb +13 -8
  33. data/spec/beaker/dsl/ezbake_utils_spec.rb +8 -9
  34. data/spec/beaker/dsl/helpers_spec.rb +299 -51
  35. data/spec/beaker/dsl/install_utils_spec.rb +75 -10
  36. data/spec/beaker/dsl/roles_spec.rb +36 -1
  37. data/spec/beaker/host_prebuilt_steps_spec.rb +21 -5
  38. data/spec/beaker/host_spec.rb +187 -23
  39. data/spec/beaker/hypervisor/ec2_helper_spec.rb +4 -4
  40. data/spec/beaker/hypervisor/vagrant_spec.rb +1 -1
  41. data/spec/beaker/options/hosts_file_parser_spec.rb +5 -0
  42. data/spec/beaker/options/options_hash_spec.rb +2 -2
  43. data/spec/beaker/options/parser_spec.rb +6 -0
  44. data/spec/beaker/options/presets_spec.rb +18 -2
  45. data/spec/beaker/perf_spec.rb +87 -0
  46. data/spec/beaker/shared/{host_role_parser_spec.rb → host_manager_spec.rb} +36 -5
  47. data/spec/beaker/test_suite_spec.rb +4 -3
  48. data/spec/matchers.rb +31 -3
  49. data/spec/mocks.rb +31 -25
  50. metadata +24 -5
  51. 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
- consoleport = ENV['BEAKER_CONSOLEPORT'] || ENV['consoleport']
14
- h.merge({
15
- :home => ENV['HOME'],
16
- :project => ENV['BEAKER_PROJECT'] || ENV['BEAKER_project'],
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 => 'Beaker',
60
- :department => ENV['USER'] || ENV['USERNAME'] || 'unknown',
61
- :validate => true,
62
- :jenkins_build_url => nil,
63
- :forge_host => 'vulcan-acceptance.delivery.puppetlabs.net',
64
- :log_level => 'verbose',
65
- :trace_limit => 10,
66
- :hosts_file => 'sample.cfg',
67
- :options_file => nil,
68
- :type => 'pe',
69
- :provision => true,
70
- :preserve_hosts => 'never',
71
- :root_keys => false,
72
- :quiet => false,
73
- :project_root => File.expand_path(File.join(File.dirname(__FILE__), "../")),
74
- :xml_dir => 'junit',
75
- :xml_file => 'beaker_junit.xml',
76
- :xml_stylesheet => 'junit.xsl',
77
- :log_dir => 'log',
78
- :color => true,
79
- :dry_run => false,
80
- :timeout => 300,
81
- :fail_mode => 'slow',
82
- :timesync => false,
83
- :repo_proxy => false,
84
- :package_proxy => false,
85
- :add_el_extras => false,
86
- :consoleport => 443,
87
- :pe_dir => '/opt/enterprise/dists',
88
- :pe_version_file => 'LATEST',
89
- :pe_version_file_win => 'LATEST-win',
90
- :dot_fog => File.join(ENV['HOME'], '.fog'),
91
- :ec2_yaml => 'config/image_templates/ec2.yaml',
92
- :help => false,
93
- :ssh => {
94
- :config => false,
95
- :paranoid => false,
96
- :timeout => 300,
97
- :auth_methods => ["publickey"],
98
- :port => 22,
99
- :forward_agent => true,
100
- :keys => ["#{ENV['HOME']}/.ssh/id_rsa"],
101
- :user_known_hosts_file => "#{ENV['HOME']}/.ssh/known_hosts",
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
@@ -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', 'host_role_parser', 'timed' ].each do |lib|
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::HostRoleParser
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 = "SCP'ed file #{source} to #{@hostname}:#{target}"
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 = "SCP'ed file #{@hostname}:#{source} to #{target}"
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
@@ -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
 
@@ -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
- status.add_child(status.document.create_cdata(test.exception.backtrace.join('\n')))
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
- stdout.add_child(stdout.document.create_cdata(strip_color_codes(test.sublog)))
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
- stderr.add_child(stderr.document.create_cdata(strip_color_codes(test.stderr)))
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
 
@@ -1,5 +1,5 @@
1
1
  module Beaker
2
2
  module Version
3
- STRING = '1.16.0'
3
+ STRING = '1.17.0'
4
4
  end
5
5
  end
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
@@ -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.env_vars }
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.env_vars }
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.env_vars }
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.env_vars }
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.env_vars }
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.env_vars }
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