landrush 1.1.2 → 1.2.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +12 -178
  3. data/.travis.yml +6 -1
  4. data/CHANGELOG.md +18 -1
  5. data/CONTRIBUTING.adoc +112 -0
  6. data/Gemfile +6 -9
  7. data/Guardfile +10 -0
  8. data/README.adoc +100 -0
  9. data/Rakefile +14 -2
  10. data/appveyor.yml +20 -0
  11. data/doc/Development.adoc +112 -0
  12. data/doc/ProxyMobile.adoc +66 -0
  13. data/doc/Troubleshooting.adoc +42 -0
  14. data/doc/Usage.adoc +271 -0
  15. data/features/commands.feature +35 -0
  16. data/features/dns_resolution.feature +6 -5
  17. data/features/{landrush-ip.feature → landrush_ip.feature} +0 -0
  18. data/features/step_definitions/landrush_custom_steps.rb +48 -0
  19. data/features/support/env.rb +25 -1
  20. data/landrush.gemspec +3 -3
  21. data/lib/landrush/action/common.rb +3 -11
  22. data/lib/landrush/action/install_prerequisites.rb +2 -3
  23. data/lib/landrush/action/redirect_dns.rb +1 -1
  24. data/lib/landrush/action/setup.rb +25 -30
  25. data/lib/landrush/action/teardown.rb +8 -11
  26. data/lib/landrush/cap/guest/all/read_host_visible_ip_address.rb +28 -4
  27. data/lib/landrush/cap/guest/linux/add_iptables_rule.rb +1 -1
  28. data/lib/landrush/cap/guest/linux/redirect_dns.rb +2 -2
  29. data/lib/landrush/cap/guest/suse/add_iptables_rule.rb +20 -0
  30. data/lib/landrush/cap/guest/suse/install_iptables.rb +14 -0
  31. data/lib/landrush/cap/guest/suse/iptables_installed.rb +11 -0
  32. data/lib/landrush/cap/host/suse/dnsmasq_installed.rb +11 -0
  33. data/lib/landrush/cap/host/suse/install_dnsmasq.rb +14 -0
  34. data/lib/landrush/cap/host/suse/restart_dnsmasq.rb +21 -0
  35. data/lib/landrush/cap/host/windows/configure_visibility_on_host.rb +1 -1
  36. data/lib/landrush/command.rb +42 -17
  37. data/lib/landrush/config.rb +29 -14
  38. data/lib/landrush/plugin.rb +30 -0
  39. data/lib/landrush/server.rb +96 -138
  40. data/lib/landrush/start_server.rb +11 -0
  41. data/lib/landrush/store.rb +6 -2
  42. data/lib/landrush/util/path.rb +32 -0
  43. data/lib/landrush/util/process_helper.rb +46 -0
  44. data/lib/landrush/util/retry.rb +2 -2
  45. data/lib/landrush/version.rb +1 -1
  46. data/test/landrush/action/setup_test.rb +19 -25
  47. data/test/landrush/action/teardown_test.rb +18 -15
  48. data/test/landrush/cap/guest/all/read_host_visible_ip_address_test.rb +35 -1
  49. data/test/landrush/cap/guest/linux/configured_dns_servers_test.rb +8 -8
  50. data/test/landrush/cap/guest/linux/redirect_dns_test.rb +4 -4
  51. data/test/landrush/config_test.rb +23 -2
  52. data/test/landrush/dependent_vms_test.rb +5 -5
  53. data/test/landrush/issues/255.rb +115 -0
  54. data/test/landrush/server_test.rb +22 -4
  55. data/test/landrush/store_test.rb +28 -13
  56. data/test/support/test_server_daemon.rb +2 -4
  57. data/test/test_helper.rb +37 -14
  58. metadata +30 -15
  59. data/CONTRIBUTING.md +0 -103
  60. data/NOTES.md +0 -28
  61. data/README.md +0 -406
  62. data/doc/proxy-mobile/README.md +0 -50
  63. data/features/step_definitions/dns.rb +0 -19
  64. data/features/step_definitions/ip.rb +0 -13
@@ -0,0 +1,35 @@
1
+ Feature: Landrush reload
2
+ Landrush DNS server should restart on a 'vagrant reload'
3
+
4
+ Scenario Outline: booting a box and restarting it
5
+ Given a file named "Vagrantfile" with:
6
+ """
7
+ Vagrant.configure("2") do |config|
8
+ config.vm.box = '<box>'
9
+ config.vm.synced_folder '.', '/vagrant', disabled: true
10
+ config.landrush.enabled = true
11
+ end
12
+ """
13
+ When I successfully run `bundle exec vagrant up --provider <provider>`
14
+ Then Landrush is running
15
+
16
+ When I successfully run `bundle exec vagrant landrush set foo 1.2.3.4`
17
+ And I successfully run `bundle exec vagrant landrush set bar 4.3.1.1`
18
+ And I successfully run `bundle exec vagrant landrush ls`
19
+ Then stdout from "bundle exec vagrant landrush ls" should match /^foo.*1.2.3.4$/
20
+ Then stdout from "bundle exec vagrant landrush ls" should match /^bar.*4.3.2.1$/
21
+
22
+ When I successfully run `bundle exec vagrant landrush rm --all`
23
+ And I successfully run `bundle exec vagrant landrush ls`
24
+ Then stdout from "bundle exec vagrant landrush ls" should match /^foo.*1.2.3.4$/
25
+ Then stdout from "bundle exec vagrant landrush ls" should match /^bar.*4.3.2.1$/
26
+
27
+ When I successfully run `bundle exec vagrant reload`
28
+ Then Landrush is running
29
+
30
+ When I successfully run `bundle exec vagrant landrush stop`
31
+ Then Landrush is not running
32
+
33
+ Examples:
34
+ | box | provider |
35
+ | debian/jessie64 | virtualbox |
@@ -16,9 +16,9 @@ Feature: dns_resolution
16
16
  end
17
17
  """
18
18
  When I successfully run `bundle exec vagrant up --provider <provider>`
19
- Then the hostname "my-host.landrush-acceptance-test" should resolve to "10.10.10.123" on the internal DNS server
20
- And the hostname "my-host.landrush-acceptance-test" should resolve to "10.10.10.123" on the host
19
+ Then the hostname "my-host.landrush-acceptance-test" should resolve to "10.10.10.123" on the host
21
20
  And the hostname "my-host.landrush-acceptance-test" should resolve to "10.10.10.123" on the guest
21
+ And the hostname "my-host.landrush-acceptance-test" should resolve to "10.10.10.123" on the internal DNS server
22
22
 
23
23
  When I successfully run `bundle exec vagrant landrush set my-static-host.landrush-acceptance-test 42.42.42.42`
24
24
  Then the hostname "my-static-host.landrush-acceptance-test" should resolve to "42.42.42.42" on the internal DNS server
@@ -30,6 +30,7 @@ Feature: dns_resolution
30
30
  And the hostname "my-static-cname-host.landrush-acceptance-test" should resolve to "42.42.42.42" on the host
31
31
 
32
32
  Examples:
33
- | box | provider |
34
- | debian/jessie64 | virtualbox |
35
- #| ubuntu/wily64 | virtualbox |
33
+ | box | provider |
34
+ | debian/jessie64 | virtualbox |
35
+ #| opensuse/openSUSE-42.1-x86_64 | virtualbox |
36
+ #| ubuntu/wily64 | virtualbox |
@@ -0,0 +1,48 @@
1
+ require 'landrush/server'
2
+
3
+ Then(/^the hostname "([^"]+)" should resolve to "([^"]+)" on the internal DNS server$/) do |host, ip|
4
+ port = Landrush::Server.port
5
+ resolver = Resolv::DNS.new(nameserver_port: [['localhost', port]], search: ['local'], ndots: 1)
6
+ ip_resolved = resolver.getaddress(host).to_s
7
+ expect(ip_resolved).to eq(ip)
8
+ end
9
+
10
+ Then(/^the hostname "([^"]+)" should resolve to "([^"]+)" on the host$/) do |host, ip|
11
+ addrinfo = Addrinfo.getaddrinfo(host, nil, Socket::AF_INET)
12
+ ip_resolved = addrinfo.first.ip_address
13
+ expect(ip_resolved).to eq(ip)
14
+ end
15
+
16
+ Then(/^the hostname "([^"]+)" should resolve to "([^"]+)" on the guest/) do |host, ip|
17
+ run("bundle exec vagrant ssh -c \"dig +short '#{host}' A\"")
18
+ expect(last_command_started).to have_output(/^#{ip}$/)
19
+ end
20
+
21
+ Then(/^the host visible IP address of the guest is the IP of interface "([^"]+)"/) do |interface|
22
+ cmd = "bundle exec vagrant ssh -c \"ip addr list #{interface} | grep 'inet ' | cut -d' ' -f6| cut -d/ -f1\""
23
+ run(cmd)
24
+ expect(last_command_started).to have_output(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/)
25
+
26
+ ip = last_command_started.output.split("\n").last
27
+
28
+ run('bundle exec vagrant landrush list')
29
+
30
+ expect(last_command_started).to have_output(/#{ip}$/)
31
+ end
32
+
33
+ Then(/^Landrush is( not)? running$/) do |negated|
34
+ run('bundle exec vagrant landrush status')
35
+ if negated
36
+ expect(last_command_started).to have_output(/Daemon status: stopped/)
37
+ else
38
+ expect(last_command_started).to have_output(/Daemon status: running pid=[0-9]+/)
39
+ end
40
+ end
41
+
42
+ Then(%r{^stdout from "([^"]*)" should( not)? match /(.*)/$}) do |cmd, negated, regexp|
43
+ if negated
44
+ aruba.command_monitor.find(Aruba.platform.detect_ruby(cmd)).send(:stdout) !~ /#{regexp}/
45
+ else
46
+ aruba.command_monitor.find(Aruba.platform.detect_ruby(cmd)).send(:stdout) =~ /#{regexp}/
47
+ end
48
+ end
@@ -1,15 +1,39 @@
1
1
  require 'aruba/cucumber'
2
2
  require 'komenda'
3
+ require 'fileutils'
4
+ require 'find'
3
5
 
4
6
  Aruba.configure do |config|
5
- config.exit_timeout = 300
7
+ config.exit_timeout = 3600
6
8
  config.activate_announcer_on_command_failure = [:stdout, :stderr]
7
9
  config.working_directory = 'build/aruba'
8
10
  end
9
11
 
12
+ Before do |_scenario|
13
+ # Making sure that all tests run in a pristine environment
14
+ # Create the Vagrant home directory for the tests
15
+ vagrant_home = File.join(File.dirname(__FILE__), '..', '..', 'build', 'vagrant.d')
16
+ # Make sure the Vagrant home directory is "clean".
17
+ # We keep the boxes directory to not have to re-download the boxes each time
18
+ ENV['VAGRANT_HOME'] = vagrant_home
19
+ Dir.new(ENV['VAGRANT_HOME']).entries.reject { |file| 'boxes'.eql?(file) || '.'.eql?(file) || '..'.eql?(file) }
20
+ .each { |file| FileUtils.rmtree(File.join(ENV['VAGRANT_HOME'], file)) }
21
+
22
+ # Actual gems are in ~/vagrant.d/gems/gems
23
+ gems_path = File.join(vagrant_home, 'gems', 'gems')
24
+ FileUtils.mkdir_p gems_path
25
+
26
+ # Find the path to the Bundler gems
27
+ bundler_gem_path = File.join(Bundler.rubygems.find_name('bundler').first.base_dir, 'gems')
28
+
29
+ # Copy the gems to the Vagrant gems dir
30
+ FileUtils.cp_r bundler_gem_path, gems_path, verbose: false
31
+ end
32
+
10
33
  After do |_scenario|
11
34
  Komenda.run('bundle exec vagrant landrush stop', fail_on_fail: true)
12
35
 
36
+ # If there is a Vagrantfile from previous run, delete it
13
37
  if File.exist?(File.join(aruba.config.working_directory, 'Vagrantfile'))
14
38
  Komenda.run('bundle exec vagrant destroy -f', cwd: aruba.config.working_directory, fail_on_fail: true)
15
39
  end
@@ -19,16 +19,16 @@ Gem::Specification.new do |spec|
19
19
  and down, and you can configure static entries to be returned from the
20
20
  server as well. See the README for more documentation.
21
21
  DESCRIP
22
- spec.summary = %q{a vagrant plugin providing consistent DNS visible on host and guests}
22
+ spec.summary = 'a vagrant plugin providing consistent DNS visible on host and guests'
23
23
  spec.homepage = 'https://github.com/vagrant-landrush/landrush'
24
24
  spec.license = 'MIT'
25
25
 
26
- spec.files = `git ls-files`.split($/)
26
+ spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
27
27
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
28
28
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
29
29
  spec.require_paths = ['lib']
30
30
 
31
31
  spec.add_dependency 'rubydns', '0.8.5'
32
32
  spec.add_dependency 'win32-process'
33
- spec.add_dependency 'landrush-ip', '~> 0.2.3'
33
+ spec.add_dependency 'landrush-ip', '~> 0.2.5'
34
34
  end
@@ -1,5 +1,6 @@
1
1
  module Landrush
2
2
  module Action
3
+ # A module containing shared functionality for Vagrant middleware classes
3
4
  module Common
4
5
  SUPPORTED_PROVIDERS = {
5
6
  'VagrantPlugins::ProviderVirtualBox::Provider' => :virtualbox,
@@ -16,14 +17,7 @@ module Landrush
16
17
 
17
18
  def initialize(app, env)
18
19
  @app = app
19
- end
20
-
21
- def handle_action_stack(env)
22
20
  @env = env
23
-
24
- yield
25
-
26
- app.call(env)
27
21
  end
28
22
 
29
23
  def virtualbox?
@@ -47,7 +41,7 @@ module Landrush
47
41
  raise "The landrush plugin does not support the #{key} provider yet!"
48
42
  end
49
43
 
50
- if provider_name == :parallels && Gem::Version.new(VagrantPlugins::Parallels::VERSION) < Gem::Version.new("1.0.3")
44
+ if provider_name == :parallels && Gem::Version.new(VagrantPlugins::Parallels::VERSION) < Gem::Version.new('1.0.3')
51
45
  raise "The landrush plugin supports the Parallels provider v1.0.3 and later. Please, update your 'vagrant-parallels' plugin."
52
46
  end
53
47
 
@@ -73,9 +67,7 @@ module Landrush
73
67
  end
74
68
 
75
69
  def read_machine_hostname
76
- if machine.config.vm.hostname
77
- return machine.config.vm.hostname
78
- end
70
+ return machine.config.vm.hostname if machine.config.vm.hostname
79
71
 
80
72
  "#{Pathname.pwd.basename}.#{config.tld}"
81
73
  end
@@ -4,9 +4,8 @@ module Landrush
4
4
  include Common
5
5
 
6
6
  def call(env)
7
- handle_action_stack(env) do
8
- install_prerequisites if enabled?
9
- end
7
+ install_prerequisites if enabled?
8
+ app.call(env)
10
9
  end
11
10
 
12
11
  def install_prerequisites
@@ -4,7 +4,7 @@ module Landrush
4
4
  include Common
5
5
 
6
6
  def call(env)
7
- handle_action_stack(env) {}
7
+ app.call(env)
8
8
 
9
9
  # This is after the middleware stack returns, which, since we're right
10
10
  # before the Network action, should mean that all interfaces are good
@@ -7,11 +7,11 @@ module Landrush
7
7
  # Make sure we use the right data directory for Landrush
8
8
  # Seems Vagrant only makes home_path available in this case, compared to custom commands where there is also data_dir
9
9
  Server.working_dir = File.join(env[:home_path], 'data', 'landrush')
10
+ Server.gems_dir = File.join(env[:gems_path].to_s, 'gems')
11
+ Server.ui = env[:ui]
10
12
 
11
- handle_action_stack(env) do
12
- pre_boot_setup if enabled?
13
- end
14
-
13
+ pre_boot_setup if enabled?
14
+ app.call(env)
15
15
  # This is after the middleware stack returns, which, since we're right
16
16
  # before the Network action, should mean that all interfaces are good
17
17
  # to go.
@@ -19,21 +19,25 @@ module Landrush
19
19
  end
20
20
 
21
21
  def host_ip_address
22
- static_private_network_ip || machine.guest.capability(:read_host_visible_ip_address)
22
+ if private_network_ips.include? machine.config.landrush.host_ip_address
23
+ machine.config.landrush.host_ip_address
24
+ else
25
+ machine.guest.capability(:read_host_visible_ip_address)
26
+ end
23
27
  end
24
28
 
25
29
  private
26
30
 
27
31
  def pre_boot_setup
28
- record_dependent_vm
29
32
  add_prerequisite_network_interface
30
- configure_server
31
- start_server
32
33
  end
33
34
 
34
35
  def post_boot_setup
36
+ record_dependent_vm
37
+ configure_server
35
38
  record_machine_dns_entry
36
39
  setup_static_dns
40
+ start_server
37
41
  return unless machine.config.landrush.host_redirect_dns?
38
42
  env[:host].capability(:configure_visibility_on_host, host_ip_address, config.tld)
39
43
  end
@@ -62,15 +66,13 @@ module Landrush
62
66
  def setup_static_dns
63
67
  config.hosts.each do |hostname, dns_value|
64
68
  dns_value ||= host_ip_address
65
- unless Store.hosts.has?(hostname, dns_value)
66
- info "adding static DNS entry: #{hostname} => #{dns_value}"
67
- Store.hosts.set hostname, dns_value
68
- if ip_address?(dns_value)
69
- reverse_dns = IPAddr.new(dns_value).reverse
70
- info "adding static reverse DNS entry: #{reverse_dns} => #{dns_value}"
71
- Store.hosts.set(reverse_dns, hostname)
72
- end
73
- end
69
+ next if Store.hosts.has?(hostname, dns_value)
70
+ info "adding static DNS entry: #{hostname} => #{dns_value}"
71
+ Store.hosts.set hostname, dns_value
72
+ next unless ip_address?(dns_value)
73
+ reverse_dns = IPAddr.new(dns_value).reverse
74
+ info "adding static reverse DNS entry: #{reverse_dns} => #{dns_value}"
75
+ Store.hosts.set(reverse_dns, hostname)
74
76
  end
75
77
  end
76
78
 
@@ -97,19 +99,12 @@ module Landrush
97
99
  machine.config.vm.networks.any? { |type, _| type == :private_network }
98
100
  end
99
101
 
100
- # machine.config.vm.networks is an array of two elements. The first containing the type as symbol, the second is a
101
- # hash containing other config data which varies between types
102
- def static_private_network_ip
103
- # select all statically defined private network ip
104
- private_networks = machine.config.vm.networks.select {|network| :private_network == network[0] && !network[1][:ip].nil?}
105
- .map {|network| network[1][:ip]}
106
- if machine.config.landrush.host_ip_address.nil?
107
- private_networks[0] if private_networks.length == 1
108
- elsif private_networks.include? machine.config.landrush.host_ip_address
109
- machine.config.landrush.host_ip_address
110
- end
111
- # If there is more than one private network or there is no match between config.landrush.host_ip_address
112
- # and the discovered addresses we will pass on to read_host_visible_ip_address capability
102
+ # @return [Array<String] IPv4 addresses of all private networks
103
+ def private_network_ips
104
+ # machine.config.vm.networks is an array of two elements. The first containing the type as symbol, the second is a
105
+ # hash containing other config data which varies between types
106
+ machine.config.vm.networks.select { |network| :private_network == network[0] && !network[1][:ip].nil? }
107
+ .map { |network| network[1][:ip] }
113
108
  end
114
109
  end
115
110
  end
@@ -8,26 +8,23 @@ module Landrush
8
8
  # Seems Vagrant only makes home_path available in this case, compared to custom commands where there is also data_dir
9
9
  Server.working_dir = File.join(env[:home_path], 'data', 'landrush')
10
10
 
11
- handle_action_stack(env) do
12
- teardown if enabled?
13
- end
11
+ teardown if enabled?
12
+ app.call(env)
14
13
  end
15
14
 
16
15
  def teardown
17
16
  teardown_machine_dns
18
17
  DependentVMs.remove(machine_hostname)
19
18
 
20
- if DependentVMs.none?
21
- teardown_static_dns
22
- teardown_server
23
- end
19
+ return unless DependentVMs.none?
20
+ teardown_static_dns
21
+ teardown_server
24
22
  end
25
23
 
26
24
  def teardown_machine_dns
27
- if Store.hosts.has? machine_hostname
28
- info "removing machine entry: #{machine_hostname}"
29
- Store.hosts.delete(machine_hostname)
30
- end
25
+ return unless Store.hosts.has? machine_hostname
26
+ info "removing machine entry: #{machine_hostname}"
27
+ Store.hosts.delete(machine_hostname)
31
28
  end
32
29
 
33
30
  def teardown_static_dns
@@ -14,11 +14,26 @@ module Landrush
14
14
  addresses
15
15
  end
16
16
 
17
+ def self.filter_preferred_addresses(addresses)
18
+ if @machine.config.landrush.host_interface_class == :any
19
+ addresses = addresses.select do |addr|
20
+ (addr.key?('ipv4') && !addr['ipv4'].empty?) ||
21
+ (addr.key?('ipv6') && !addr['ipv6'].empty?)
22
+ end
23
+ else
24
+ key = @machine.config.landrush.host_interface_class.to_s
25
+
26
+ addresses = addresses.select do |addr|
27
+ (addr.key?(key) && !addr[key].empty?)
28
+ end
29
+ end
30
+
31
+ addresses
32
+ end
33
+
17
34
  def self.read_host_visible_ip_address(machine)
18
35
  @machine = machine
19
36
 
20
- @machine.guest.capability(:landrush_ip_install) unless @machine.guest.capability(:landrush_ip_installed)
21
-
22
37
  addr = nil
23
38
  addresses = machine.guest.capability(:landrush_ip_get)
24
39
 
@@ -33,15 +48,24 @@ module Landrush
33
48
 
34
49
  if addr.nil?
35
50
  addresses = filter_addresses addresses
51
+ raise 'No addresses found' if addresses.empty?
36
52
 
53
+ addresses = filter_preferred_addresses addresses
37
54
  raise 'No addresses found' if addresses.empty?
38
55
 
39
56
  addr = addresses.last
40
57
  end
41
58
 
42
- ip = IPAddr.new(addr['ipv4'])
59
+ # Keep preferring IPv4 over IPv6.
60
+ key = if machine.config.landrush.host_interface_class == :any
61
+ addr['ipv4'].empty? ? 'ipv6' : 'ipv4'
62
+ else
63
+ machine.config.landrush.host_interface_class.to_s
64
+ end
65
+
66
+ ip = IPAddr.new(addr[key])
43
67
 
44
- machine.env.ui.info "[landrush] Using #{addr['name']} (#{addr['ipv4']})"
68
+ machine.env.ui.info "[landrush] Using #{addr['name']} (#{addr[key]})"
45
69
 
46
70
  ip.to_s
47
71
  end
@@ -10,7 +10,7 @@ module Landrush
10
10
  machine.communicate.sudo(command) do |data, type|
11
11
  if [:stderr, :stdout].include?(type)
12
12
  color = (type == :stdout) ? :green : :red
13
- machine.env.ui.info(data.chomp, :color => color, :prefix => false)
13
+ machine.env.ui.info(data.chomp, color: color, prefix: false)
14
14
  end
15
15
  end
16
16
  end
@@ -2,9 +2,9 @@ module Landrush
2
2
  module Cap
3
3
  module Linux
4
4
  module RedirectDns
5
- def self.redirect_dns(machine, target={})
5
+ def self.redirect_dns(machine, target = {})
6
6
  dns_servers = machine.guest.capability(:configured_dns_servers)
7
- %w[tcp udp].each do |proto|
7
+ %w(tcp udp).each do |proto|
8
8
  dns_servers.each do |dns_server|
9
9
  machine.guest.capability(
10
10
  :add_iptables_rule,
@@ -0,0 +1,20 @@
1
+ module Landrush
2
+ module Cap
3
+ module Suse
4
+ module AddIptablesRule
5
+ def self.add_iptables_rule(machine, rule)
6
+ _run(machine, %(/usr/sbin/iptables -C #{rule} 2> /dev/null || /usr/sbin/iptables -A #{rule}))
7
+ end
8
+
9
+ def self._run(machine, command)
10
+ machine.communicate.sudo(command) do |data, type|
11
+ if [:stderr, :stdout].include?(type)
12
+ color = (type == :stdout) ? :green : :red
13
+ machine.env.ui.info(data.chomp, color: color, prefix: false)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end