beaker 2.18.0 → 2.19.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 (77) hide show
  1. checksums.yaml +8 -8
  2. data/HISTORY.md +699 -2
  3. data/acceptance/lib/beaker/acceptance/install_utils.rb +58 -0
  4. data/acceptance/pre_suite/puppet_gem/install.rb +1 -8
  5. data/acceptance/pre_suite/puppet_git/install.rb +6 -65
  6. data/acceptance/pre_suite/puppet_pkg/install.rb +1 -1
  7. data/acceptance/tests/foss_utils/clone_git_repo_on.rb +49 -0
  8. data/beaker.gemspec +2 -0
  9. data/lib/beaker/command.rb +1 -1
  10. data/lib/beaker/dsl/helpers/puppet_helpers.rb +8 -6
  11. data/lib/beaker/dsl/helpers/web_helpers.rb +2 -1
  12. data/lib/beaker/dsl/install_utils/aio_defaults.rb +0 -3
  13. data/lib/beaker/dsl/install_utils/foss_defaults.rb +19 -0
  14. data/lib/beaker/dsl/install_utils/foss_utils.rb +164 -67
  15. data/lib/beaker/dsl/install_utils/pe_defaults.rb +9 -11
  16. data/lib/beaker/dsl/install_utils/pe_utils.rb +48 -64
  17. data/lib/beaker/dsl/install_utils/puppet_utils.rb +43 -0
  18. data/lib/beaker/dsl/install_utils/windows_utils.rb +144 -0
  19. data/lib/beaker/dsl/roles.rb +20 -3
  20. data/lib/beaker/dsl/structure.rb +14 -3
  21. data/lib/beaker/host/freebsd/pkg.rb +18 -0
  22. data/lib/beaker/host/freebsd.rb +2 -0
  23. data/lib/beaker/host/unix/exec.rb +3 -3
  24. data/lib/beaker/host/unix/pkg.rb +37 -0
  25. data/lib/beaker/host/windows/exec.rb +3 -0
  26. data/lib/beaker/host.rb +38 -9
  27. data/lib/beaker/host_prebuilt_steps.rb +21 -11
  28. data/lib/beaker/hypervisor/aws_sdk.rb +22 -18
  29. data/lib/beaker/hypervisor/docker.rb +7 -0
  30. data/lib/beaker/hypervisor/openstack.rb +1 -0
  31. data/lib/beaker/hypervisor/vagrant_virtualbox.rb +10 -5
  32. data/lib/beaker/hypervisor/vmpooler.rb +4 -0
  33. data/lib/beaker/logger.rb +12 -1
  34. data/lib/beaker/options/command_line_parser.rb +9 -0
  35. data/lib/beaker/options/options_hash.rb +3 -296
  36. data/lib/beaker/options/parser.rb +12 -0
  37. data/lib/beaker/options/presets.rb +0 -1
  38. data/lib/beaker/platform.rb +3 -1
  39. data/lib/beaker/ssh_connection.rb +48 -23
  40. data/lib/beaker/test_case.rb +1 -1
  41. data/lib/beaker/version.rb +1 -1
  42. data/spec/beaker/dsl/helpers/puppet_helpers_spec.rb +0 -1
  43. data/spec/beaker/dsl/helpers/web_helpers_spec.rb +10 -1
  44. data/spec/beaker/dsl/install_utils/foss_utils_spec.rb +247 -49
  45. data/spec/beaker/dsl/install_utils/pe_utils_spec.rb +116 -26
  46. data/spec/beaker/dsl/install_utils/puppet_utils_spec.rb +57 -0
  47. data/spec/beaker/dsl/install_utils/windows_utils_spec.rb +132 -0
  48. data/spec/beaker/dsl/roles_spec.rb +36 -5
  49. data/spec/beaker/dsl/structure_spec.rb +9 -2
  50. data/spec/beaker/host/unix/pkg_spec.rb +26 -6
  51. data/spec/beaker/host_prebuilt_steps_spec.rb +3 -2
  52. data/spec/beaker/host_spec.rb +24 -6
  53. data/spec/beaker/hypervisor/aixer_spec.rb +1 -1
  54. data/spec/beaker/hypervisor/aws_sdk_spec.rb +595 -58
  55. data/spec/beaker/hypervisor/docker_spec.rb +2 -1
  56. data/spec/beaker/hypervisor/solaris_spec.rb +1 -0
  57. data/spec/beaker/hypervisor/vagrant_spec.rb +20 -5
  58. data/spec/beaker/hypervisor/vagrant_virtualbox_spec.rb +1 -1
  59. data/spec/beaker/logger_spec.rb +39 -0
  60. data/spec/beaker/options/command_line_parser_spec.rb +2 -2
  61. data/spec/beaker/options/options_hash_spec.rb +1 -102
  62. data/spec/beaker/options/parser_spec.rb +19 -0
  63. data/spec/beaker/options/pe_version_scaper_spec.rb +11 -1
  64. data/spec/beaker/options/presets_spec.rb +8 -0
  65. data/spec/beaker/ssh_connection_spec.rb +39 -21
  66. data/spec/helpers.rb +9 -3
  67. data/spec/mocks.rb +2 -0
  68. metadata +35 -11
  69. data/lib/beaker/answers/version20.rb +0 -120
  70. data/lib/beaker/answers/version28.rb +0 -121
  71. data/lib/beaker/answers/version30.rb +0 -227
  72. data/lib/beaker/answers/version32.rb +0 -44
  73. data/lib/beaker/answers/version34.rb +0 -51
  74. data/lib/beaker/answers/version38.rb +0 -29
  75. data/lib/beaker/answers/version40.rb +0 -44
  76. data/lib/beaker/answers.rb +0 -143
  77. data/spec/beaker/answers_spec.rb +0 -547
data/lib/beaker/host.rb CHANGED
@@ -20,6 +20,14 @@ module Beaker
20
20
  @command = command
21
21
  end
22
22
 
23
+ def has_key?(k)
24
+ cmd = PuppetCommand.new(@command, '--configprint all')
25
+ keys = @host.exec(cmd).stdout.split("\n").collect do |x|
26
+ x[/^[^\s]+/]
27
+ end
28
+ keys.include?(k)
29
+ end
30
+
23
31
  def [](k)
24
32
  cmd = PuppetCommand.new(@command, "--configprint #{k.to_s}")
25
33
  @host.exec(cmd).stdout.strip
@@ -206,8 +214,9 @@ module Beaker
206
214
  end
207
215
 
208
216
  #Return the ip address of this host
217
+ #Always pull fresh, because this can sometimes change
209
218
  def ip
210
- self[:ip] ||= get_ip
219
+ self['ip'] = get_ip
211
220
  end
212
221
 
213
222
  #@return [Boolean] true if x86_64, false otherwise
@@ -216,13 +225,29 @@ module Beaker
216
225
  end
217
226
 
218
227
  def connection
219
- @connection ||= SshConnection.connect( reachable_name,
228
+ # create new connection object if necessary
229
+ @connection ||= SshConnection.connect( { :ip => self['ip'], :vmhostname => self['vmhostname'], :hostname => @name },
220
230
  self['user'],
221
231
  self['ssh'], { :logger => @logger } )
232
+ # update connection information
233
+ if self['ip'] && (@connection.ip != self['ip'])
234
+ @connection.ip = self['ip']
235
+ end
236
+ if self['vmhostname'] && (@connection.vmhostname != self['vmhostname'])
237
+ @connection.vmhostname = self['vmhostname']
238
+ end
239
+ if @name && (@connection.hostname != @name)
240
+ @connection.hostname = @name
241
+ end
242
+ @connection
222
243
  end
223
244
 
224
245
  def close
225
246
  @connection.close if @connection
247
+ # update connection information
248
+ @connection.ip = self['ip'] if self['ip']
249
+ @connection.vmhostname = self['vmhostname'] if self['vmhostname']
250
+ @connection.hostname = @name
226
251
  @connection = nil
227
252
  end
228
253
 
@@ -234,7 +259,11 @@ module Beaker
234
259
  output_callback = nil
235
260
  else
236
261
  @logger.debug "\n#{log_prefix} #{Time.new.strftime('%H:%M:%S')}$ #{cmdline}"
237
- output_callback = logger.method(:host_output)
262
+ if @options[:color_host_output]
263
+ output_callback = logger.method(:color_host_output)
264
+ else
265
+ output_callback = logger.method(:host_output)
266
+ end
238
267
  end
239
268
 
240
269
  unless $dry_run
@@ -457,12 +486,12 @@ module Beaker
457
486
  end
458
487
 
459
488
  [
460
- 'unix',
461
- 'aix',
462
- 'mac',
463
- 'freebsd',
464
- 'windows',
465
- 'pswindows',
489
+ 'unix',
490
+ 'aix',
491
+ 'mac',
492
+ 'freebsd',
493
+ 'windows',
494
+ 'pswindows',
466
495
  ].each do |lib|
467
496
  require "beaker/host/#{lib}"
468
497
  end
@@ -14,6 +14,7 @@ module Beaker
14
14
  TRIES = 5
15
15
  UNIX_PACKAGES = ['curl', 'ntpdate']
16
16
  FREEBSD_PACKAGES = ['curl', 'perl5']
17
+ OPENBSD_PACKAGES = ['curl']
17
18
  WINDOWS_PACKAGES = ['curl']
18
19
  PSWINDOWS_PACKAGES = []
19
20
  SLES10_PACKAGES = ['curl']
@@ -103,6 +104,8 @@ module Beaker
103
104
  check_and_install_packages_if_needed(host, PSWINDOWS_PACKAGES)
104
105
  when host['platform'] =~ /freebsd/
105
106
  check_and_install_packages_if_needed(host, FREEBSD_PACKAGES)
107
+ when host['platform'] =~ /openbsd/
108
+ check_and_install_packages_if_needed(host, OPENBSD_PACKAGES)
106
109
  when host['platform'] !~ /debian|aix|solaris|windows|sles-|osx-|cumulus|f5-/
107
110
  check_and_install_packages_if_needed(host, UNIX_PACKAGES)
108
111
  end
@@ -319,6 +322,8 @@ module Beaker
319
322
  host.exec(Command.new('sudo cp -r .ssh /var/root/.'), {:pty => true})
320
323
  elsif host['platform'] =~ /freebsd/
321
324
  host.exec(Command.new('sudo cp -r .ssh /root/.'), {:pty => true})
325
+ elsif host['platform'] =~ /openbsd/
326
+ host.exec(Command.new('sudo cp -r .ssh /root/.'), {:pty => true})
322
327
  else
323
328
  host.exec(Command.new('sudo su -c "cp -r .ssh /root/."'), {:pty => true})
324
329
  end
@@ -336,7 +341,10 @@ module Beaker
336
341
  def hack_etc_hosts hosts, opts
337
342
  etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
338
343
  hosts.each do |host|
339
- etc_hosts += "#{host['vm_ip'] || host['ip'].to_s}\t#{host[:vmhostname] || host.name}\n"
344
+ ip = host['vm_ip'] || host['ip'].to_s
345
+ hostname = host[:vmhostname] || host.name
346
+ domain = get_domain_name(host)
347
+ etc_hosts += "#{ip}\t#{hostname}.#{domain} #{hostname}\n"
340
348
  end
341
349
  hosts.each do |host|
342
350
  set_etc_hosts(host, etc_hosts)
@@ -359,6 +367,8 @@ module Beaker
359
367
  host.exec(Command.new("sudo sed -i '' 's/#PermitRootLogin yes/PermitRootLogin Yes/g' /etc/sshd_config"))
360
368
  elsif host['platform'] =~ /freebsd/
361
369
  host.exec(Command.new("sudo sed -i -e 's/#PermitRootLogin no/PermitRootLogin yes/g' /etc/ssh/sshd_config"), {:pty => true} )
370
+ elsif host['platform'] =~ /openbsd/
371
+ host.exec(Command.new("sudo perl -pi -e 's/^PermitRootLogin no/PermitRootLogin yes/' /etc/ssh/sshd_config"), {:pty => true} )
362
372
  elsif not host.is_powershell?
363
373
  host.exec(Command.new("sudo su -c \"sed -ri 's/^#?PermitRootLogin no|^#?PermitRootLogin yes/PermitRootLogin yes/' /etc/ssh/sshd_config\""), {:pty => true})
364
374
  else
@@ -371,7 +381,7 @@ module Beaker
371
381
  host.exec(Command.new("sudo -E systemctl restart sshd.service"), {:pty => true})
372
382
  elsif host['platform'] =~ /centos|el-|redhat|fedora|eos/
373
383
  host.exec(Command.new("sudo -E /sbin/service sshd reload"), {:pty => true})
374
- elsif host['platform'] =~ /freebsd/
384
+ elsif host['platform'] =~ /(free|open)bsd/
375
385
  host.exec(Command.new("sudo /etc/rc.d/sshd restart"))
376
386
  else
377
387
  logger.warn("Attempting to update ssh on non-supported platform: #{host.name}: #{host['platform']}")
@@ -520,7 +530,7 @@ module Beaker
520
530
  host.exec(Command.new("echo '\nPermitUserEnvironment yes' >> /etc/ssh/sshd_config"))
521
531
  host.exec(Command.new("stopsrc -g ssh"))
522
532
  host.exec(Command.new("startsrc -g ssh"))
523
- when /freebsd/
533
+ when /(free|open)bsd/
524
534
  host.exec(Command.new("sudo perl -pi -e 's/^#?PermitUserEnvironment no/PermitUserEnvironment yes/' /etc/ssh/sshd_config"), {:pty => true} )
525
535
  host.exec(Command.new("sudo /etc/rc.d/sshd restart"))
526
536
  end
@@ -532,6 +542,13 @@ module Beaker
532
542
  host.exec(Command.new("touch #{host[:ssh_env_file]}"))
533
543
  #add the constructed env vars to this host
534
544
  host.add_env_var('PATH', '$PATH')
545
+ # FIXME
546
+ if host['platform'] =~ /openbsd-(\d)\.?(\d)-(.+)/
547
+ version = "#{$1}.#{$2}"
548
+ arch = $3
549
+ arch = 'amd64' if ['x64', 'x86_64'].include?(arch)
550
+ host.add_env_var('PKG_PATH', "http://ftp.openbsd.org/pub/OpenBSD/#{version}/packages/#{arch}/")
551
+ end
535
552
  end
536
553
  #add the env var set to this test host
537
554
  env.each_pair do |var, value|
@@ -539,14 +556,7 @@ module Beaker
539
556
  end
540
557
  # REMOVE POST BEAKER 3: backwards compatability, do some setup based upon the global type
541
558
  # this is the worst and i hate it
542
- if host[:type]
543
- case host[:type]
544
- when /git|foss|aio/
545
- Class.new.extend(Beaker::DSL).configure_foss_defaults_on(host)
546
- when /pe/
547
- Class.new.extend(Beaker::DSL).configure_pe_defaults_on(host)
548
- end
549
- end
559
+ Class.new.extend(Beaker::DSL).configure_type_defaults_on(host)
550
560
 
551
561
  #close the host to re-establish the connection with the new sshd settings
552
562
  host.close
@@ -127,7 +127,7 @@ module Beaker
127
127
 
128
128
  # Return all instances currently on ec2.
129
129
  # @see AwsSdk#instance_by_id
130
- # @return [Array<AWS::EC2::Instance>] An array of AWS::EC2 instance objects
130
+ # @return [AWS::EC2::InstanceCollection] An array of AWS::EC2 instance objects
131
131
  def instances
132
132
  @ec2.instances
133
133
  end
@@ -142,7 +142,7 @@ module Beaker
142
142
 
143
143
  # Return all VPCs currently on ec2.
144
144
  # @see AwsSdk#vpc_by_id
145
- # @return [Array<AWS::EC2::VPC>] An array of AWS::EC2 vpc objects
145
+ # @return [AWS::EC2::VPCCollection] An array of AWS::EC2 vpc objects
146
146
  def vpcs
147
147
  @ec2.vpcs
148
148
  end
@@ -157,7 +157,7 @@ module Beaker
157
157
 
158
158
  # Return all security groups currently on ec2.
159
159
  # @see AwsSdk#security_goup_by_id
160
- # @return [Array<AWS::EC2::SecurityGroup>] An array of AWS::EC2 security group objects
160
+ # @return [AWS::EC2::SecurityGroupCollection] An array of AWS::EC2 security group objects
161
161
  def security_groups
162
162
  @ec2.security_groups
163
163
  end
@@ -224,7 +224,7 @@ module Beaker
224
224
 
225
225
  # Create an EC2 instance for host, tag it, and return it.
226
226
  #
227
- # @return [AWS::EC2::Instance)]
227
+ # @return [void]
228
228
  # @api private
229
229
  def create_instance(host, ami_spec, subnet_id)
230
230
  amitype = host['vmname'] || host['platform']
@@ -489,28 +489,32 @@ module Beaker
489
489
  nil
490
490
  end
491
491
 
492
+ # Return a valid /etc/hosts line for a given host
493
+ #
494
+ # @param [Beaker::Host] host Beaker::Host object for generating /etc/hosts entry
495
+ # @param [Symbol] interface Symbol identifies which ip should be used for host
496
+ # @return [String] formatted hosts entry for host
497
+ # @api private
498
+ def etc_hosts_entry(host, interface = :ip)
499
+ name = host.name
500
+ domain = get_domain_name(host)
501
+ ip = host[interface.to_s]
502
+ "#{ip}\t#{name} #{name}.#{domain} #{host['dns_name']}\n"
503
+ end
504
+
492
505
  # Configure /etc/hosts for each node
493
506
  #
494
507
  # @return [void]
495
508
  # @api private
496
509
  def configure_hosts
497
510
  @hosts.each do |host|
498
- etc_hosts = "127.0.0.1\tlocalhost localhost.localdomain\n"
499
- name = host.name
500
- domain = get_domain_name(host)
501
- ip = host['private_ip']
502
- etc_hosts += "#{ip}\t#{name} #{name}.#{domain} #{host['dns_name']}\n"
503
- @hosts.each do |neighbor|
504
- if neighbor == host
505
- next
506
- end
507
- name = neighbor.name
508
- domain = get_domain_name(neighbor)
509
- ip = neighbor['ip']
510
- etc_hosts += "#{ip}\t#{name} #{name}.#{domain} #{neighbor['dns_name']}\n"
511
+ host_entries = @hosts.map do |h|
512
+ h == host ? etc_hosts_entry(h, :private_ip) : etc_hosts_entry(h)
511
513
  end
512
- set_etc_hosts(host, etc_hosts)
514
+ host_entries.unshift "127.0.0.1\tlocalhost localhost.localdomain\n"
515
+ set_etc_hosts(host, host_entries.join(''))
513
516
  end
517
+ nil
514
518
  end
515
519
 
516
520
  # Enables root for instances with custom username like ubuntu-amis
@@ -176,6 +176,13 @@ module Beaker
176
176
  RUN apt-get update
177
177
  RUN apt-get install -y openssh-server openssh-client #{Beaker::HostPrebuiltSteps::CUMULUS_PACKAGES.join(' ')}
178
178
  EOF
179
+ when /fedora-22/
180
+ dockerfile += <<-EOF
181
+ RUN dnf clean all
182
+ RUN dnf install -y sudo openssh-server openssh-clients #{Beaker::HostPrebuiltSteps::UNIX_PACKAGES.join(' ')}
183
+ RUN ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key
184
+ RUN ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key
185
+ EOF
179
186
  when /^el-/, /centos/, /fedora/, /redhat/, /eos/
180
187
  dockerfile += <<-EOF
181
188
  RUN yum clean all
@@ -153,6 +153,7 @@ module Beaker
153
153
  vm.volumes.each do |vol|
154
154
  @logger.debug "Deleting volume #{vol.name} for OpenStack host #{vm.name}"
155
155
  vm.detach_volume(vol.id)
156
+ vol.wait_for { ready? }
156
157
  vol.destroy
157
158
  end
158
159
  end
@@ -18,7 +18,7 @@ class Beaker::VagrantVirtualbox < Beaker::Vagrant
18
18
  def self.provider_vfile_section(host, options)
19
19
  provider_section = ""
20
20
  provider_section << " v.vm.provider :virtualbox do |vb|\n"
21
- provider_section << " vb.customize ['modifyvm', :id, '--memory', '#{options['vagrant_memsize'] ||= '1024'}']\n"
21
+ provider_section << " vb.customize ['modifyvm', :id, '--memory', '#{options['vagrant_memsize'] ||= '1024'}', '--cpus', '#{options['vagrant_cpus'] ||= '1'}']\n"
22
22
  provider_section << " vb.vbguest.auto_update = false" if options[:vbguest_plugin] == 'disable'
23
23
 
24
24
  # Guest volume support
@@ -52,17 +52,22 @@ class Beaker::VagrantVirtualbox < Beaker::Vagrant
52
52
  end
53
53
  end
54
54
 
55
+ provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnshostresolver1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
56
+
57
+ provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnsproxy1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
58
+
59
+ provider_section << " vb.gui = true\n" unless host['vb_gui'].nil?
60
+
61
+ provider_section << " [\"modifyvm\", :id, \"--cpuidset\", \"1\",\"000206a7\",\"02100800\",\"1fbae3bf\",\"bfebfbff\"\]" if /osx/i.match(host['platform'])
62
+
55
63
  if host['disk_path']
56
64
  unless File.exist?(host['disk_path'])
57
65
  host['disk_path'] = File.join(host['disk_path'], "#{host.name}.vmdk")
58
66
  provider_section << " vb.customize ['createhd', '--filename', '#{host['disk_path']}', '--size', #{host['disk_size'] ||= 5 * 1024}, '--format', 'vmdk']\n"
59
67
  end
60
68
  provider_section << " vb.customize ['storageattach', :id, '--storagectl', 'SATA Controller', '--port', 1, '--device', 0, '--type', 'hdd', '--medium','#{host['disk_path']}']\n"
61
- provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnshostresolver1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
62
- provider_section << " vb.customize [\"modifyvm\", :id, \"--natdnsproxy1\", \"#{host['natdns']}\"]\n" unless host['natdns'].nil?
63
- provider_section << " vb.gui = true\n" unless host['vb_gui'].nil?
64
- provider_section << " [\"modifyvm\", :id, \"--cpuidset\", \"1\",\"000206a7\",\"02100800\",\"1fbae3bf\",\"bfebfbff\"\]" if /osx/i.match(host['platform'])
65
69
  end
70
+
66
71
  provider_section << " end\n"
67
72
 
68
73
  provider_section
@@ -194,6 +194,10 @@ module Beaker
194
194
  http = Net::HTTP.new( uri.host, uri.port )
195
195
  request = Net::HTTP::Delete.new(uri.request_uri)
196
196
 
197
+ if @credentials[:vmpooler_token]
198
+ request['X-AUTH-TOKEN'] = @credentials[:vmpooler_token]
199
+ end
200
+
197
201
  begin
198
202
  response = http.request(request)
199
203
  rescue *SSH_EXCEPTIONS => e
data/lib/beaker/logger.rb CHANGED
@@ -26,6 +26,7 @@ module Beaker
26
26
  BRIGHT_MAGENTA = "\e[01;35m"
27
27
  BRIGHT_CYAN = "\e[01;36m"
28
28
  BRIGHT_WHITE = "\e[01;37m"
29
+ NONE = ""
29
30
 
30
31
  # The defined log levels. Each log level also reports messages at levels lower than itself
31
32
  LOG_LEVELS = {
@@ -186,6 +187,16 @@ module Beaker
186
187
  optionally_color GREY, string, false
187
188
  end
188
189
 
190
+ # Custom reporting for messages generated by host SUTs - to preserve output
191
+ # Will not print unless we are at {LOG_LEVELS} 'verbose' or higher.
192
+ # Preserves outout by not stripping out colour codes
193
+ # @param args[Array<String>] Strings to be reported
194
+ def color_host_output *args
195
+ return unless is_verbose?
196
+ string = args.join
197
+ optionally_color NONE, string, false
198
+ end
199
+
189
200
  # Custom reporting for performance/sysstat messages
190
201
  # Will not print unless we are at {LOG_LEVELS} 'debug' or higher.
191
202
  # @param args[Array<String>] Strings to be reported
@@ -270,7 +281,7 @@ module Beaker
270
281
  @destinations.each do |to|
271
282
  to.print color_code if @color
272
283
  to.send print_statement, convert( msg )
273
- to.print NORMAL if @color
284
+ to.print NORMAL if @color unless color_code == NONE
274
285
  end
275
286
  end
276
287
 
@@ -121,6 +121,15 @@ module Beaker
121
121
  @cmd_options[:color] = bool
122
122
  end
123
123
 
124
+ opts.on '--[no-]color-host-output',
125
+ 'Ensure SUT colored output is preserved',
126
+ '(default: false)' do |bool|
127
+ @cmd_options[:color_host_output] = bool
128
+ if bool
129
+ @cmd_options[:color_host_output] = true
130
+ end
131
+ end
132
+
124
133
  opts.on '--log-level LEVEL',
125
134
  'Log level',
126
135
  'Supported LEVEL keywords:',