beaker 1.16.0 → 1.17.0

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