chake 0.21.2 → 0.80

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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/.ackrc +1 -0
  3. data/.gitignore +22 -0
  4. data/.gitlab-ci.yml +24 -0
  5. data/.manifest +63 -0
  6. data/.rubocop.yml +53 -0
  7. data/.rubocop_todo.yml +40 -0
  8. data/ChangeLog.md +15 -0
  9. data/README.chef.md +70 -0
  10. data/README.itamae.md +58 -0
  11. data/README.md +110 -85
  12. data/README.shell.md +30 -0
  13. data/Rakefile +34 -10
  14. data/bin/chake +2 -2
  15. data/chake.gemspec +16 -16
  16. data/examples/test/.ssh_config +4 -0
  17. data/examples/test/Rakefile +1 -1
  18. data/examples/test/Vagrantfile +6 -0
  19. data/examples/test/config.rb +4 -4
  20. data/examples/test/cookbooks/basics/recipes/default.rb +1 -0
  21. data/examples/test/cookbooks/example/files/default/test +1 -0
  22. data/examples/test/cookbooks/example/files/{host-homer → host-lemur}/test.asc +0 -0
  23. data/lib/chake.rb +86 -153
  24. data/lib/chake/bootstrap/{01_debian.sh → chef/01_debian.sh} +0 -0
  25. data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
  26. data/lib/chake/config.rb +2 -7
  27. data/lib/chake/config_manager.rb +93 -0
  28. data/lib/chake/config_manager/chef.rb +35 -0
  29. data/lib/chake/config_manager/itamae.rb +58 -0
  30. data/lib/chake/config_manager/shell.rb +34 -0
  31. data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
  32. data/lib/chake/config_manager/skel/chef/config.rb +4 -0
  33. data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
  34. data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
  35. data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
  36. data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
  37. data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
  38. data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
  39. data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
  40. data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
  41. data/lib/chake/connection.rb +83 -0
  42. data/lib/chake/{backend → connection}/local.rb +2 -8
  43. data/lib/chake/{backend → connection}/ssh.rb +6 -14
  44. data/lib/chake/node.rb +49 -29
  45. data/lib/chake/readline.rb +6 -10
  46. data/lib/chake/version.rb +1 -1
  47. data/man/.gitignore +2 -0
  48. data/man/Rakefile +27 -14
  49. data/man/readme2man.sed +5 -5
  50. data/spec/chake/backend/local_spec.rb +5 -6
  51. data/spec/chake/backend/ssh_spec.rb +8 -10
  52. data/spec/chake/backend_spec.rb +1 -2
  53. data/spec/chake/config_manager/chef_spec.rb +38 -0
  54. data/spec/chake/config_manager/itamae_spec.rb +69 -0
  55. data/spec/chake/config_manager/shell_spec.rb +54 -0
  56. data/spec/chake/config_manager_spec.rb +24 -0
  57. data/spec/chake/node_spec.rb +38 -15
  58. data/spec/spec_helper.rb +20 -18
  59. metadata +63 -43
  60. data/coverage/assets/0.11.0/application.css +0 -809
  61. data/coverage/assets/0.11.0/application.js +0 -43679
  62. data/coverage/assets/0.11.0/colorbox/border.png +0 -0
  63. data/coverage/assets/0.11.0/colorbox/controls.png +0 -0
  64. data/coverage/assets/0.11.0/colorbox/loading.gif +0 -0
  65. data/coverage/assets/0.11.0/colorbox/loading_background.png +0 -0
  66. data/coverage/assets/0.11.0/favicon_green.png +0 -0
  67. data/coverage/assets/0.11.0/favicon_red.png +0 -0
  68. data/coverage/assets/0.11.0/favicon_yellow.png +0 -0
  69. data/coverage/assets/0.11.0/loading.gif +0 -0
  70. data/coverage/assets/0.11.0/magnify.png +0 -0
  71. data/coverage/assets/0.11.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  72. data/coverage/assets/0.11.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  73. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  74. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  75. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  76. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  77. data/coverage/assets/0.11.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  78. data/coverage/assets/0.11.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  79. data/coverage/assets/0.11.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  80. data/coverage/assets/0.11.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  81. data/coverage/assets/0.11.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  82. data/coverage/assets/0.11.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  83. data/coverage/assets/0.11.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  84. data/coverage/index.html +0 -4158
  85. data/examples/test/nodes.yaml +0 -7
  86. data/examples/test/tmp/chake/test.local.bootstrap +0 -21
  87. data/examples/test/tmp/chake/test.local.json +0 -7
  88. data/examples/test/tmp/chake/test.local.plain.sha1sum +0 -9
  89. data/lib/chake/backend.rb +0 -80
  90. data/tags +0 -103
@@ -2,58 +2,78 @@ require 'uri'
2
2
  require 'etc'
3
3
  require 'forwardable'
4
4
 
5
- require 'chake/backend'
5
+ require 'chake/connection'
6
+ require 'chake/config_manager'
6
7
 
7
8
  module Chake
8
-
9
9
  class Node
10
-
11
10
  extend Forwardable
12
11
 
13
- attr_reader :hostname
14
- attr_reader :port
15
- attr_reader :username
16
- attr_reader :remote_username
17
- attr_reader :path
18
- attr_reader :data
12
+ attr_reader :hostname, :port, :username, :remote_username, :data
13
+
14
+ attr_accessor :silent
19
15
 
20
16
  def self.max_node_name_length
21
17
  @max_node_name_length ||= 0
22
18
  end
23
- def self.max_node_name_length=(value)
24
- @max_node_name_length = value
19
+
20
+ class << self
21
+ attr_writer :max_node_name_length
25
22
  end
26
23
 
27
24
  def initialize(hostname, data = {})
28
- uri = URI.parse(hostname)
29
- if !uri.host && ((!uri.scheme && uri.path) || (uri.scheme && uri.opaque))
30
- uri = URI.parse("ssh://#{hostname}")
31
- end
32
- if uri.path && uri.path.empty?
33
- uri.path = nil
34
- end
35
-
36
- @backend_name = uri.scheme
37
-
25
+ uri = parse_uri(hostname)
26
+ @connection_name = uri.scheme
38
27
  @hostname = uri.host
39
28
  @port = uri.port
40
29
  @username = uri.user || Etc.getpwuid.name
41
30
  @remote_username = uri.user
42
- @path = uri.path || "/var/tmp/chef.#{username}"
31
+ @path = uri.path
43
32
  @data = data
33
+ set_max_node_length
34
+ end
35
+
36
+ def connection
37
+ @connection ||= Chake::Connection.get(@connection_name).new(self)
38
+ end
39
+
40
+ def_delegators :connection, :run, :run_as_root, :run_shell, :rsync, :rsync_dest, :scp, :scp_dest, :skip?
44
41
 
45
- if @hostname.length > self.class.max_node_name_length
46
- self.class.max_node_name_length = @hostname.length
42
+ def config_manager
43
+ @config_manager ||= Chake::ConfigManager.get(self)
44
+ end
45
+
46
+ def_delegators :config_manager, :converge, :apply, :path, :bootstrap_steps, :needs_bootstrap?, :needs_upload?
47
+
48
+ def path
49
+ @path ||= config_manager.path
50
+ end
51
+
52
+ def log(msg)
53
+ return if silent
54
+
55
+ puts("%#{Node.max_node_name_length}<host>s: %<msg>s\n" % { host: hostname, msg: msg })
56
+ end
57
+
58
+ private
59
+
60
+ def parse_uri(hostname)
61
+ uri = URI.parse(hostname)
62
+ if incomplete_uri(uri)
63
+ uri = URI.parse("ssh://#{hostname}")
47
64
  end
65
+ uri.path = nil if uri.path.empty?
66
+ uri
48
67
  end
49
68
 
50
- def backend
51
- @backend ||= Chake::Backend.get(@backend_name).new(self)
69
+ def incomplete_uri(uri)
70
+ !uri.host && ((!uri.scheme && uri.path) || (uri.scheme && uri.opaque))
52
71
  end
53
72
 
54
- def_delegators :backend, :run, :run_as_root, :run_shell, :rsync, :rsync_dest, :scp, :scp_dest, :skip?
73
+ def set_max_node_length
74
+ return if @hostname.length <= self.class.max_node_name_length
55
75
 
76
+ self.class.max_node_name_length = @hostname.length
77
+ end
56
78
  end
57
-
58
79
  end
59
-
@@ -4,11 +4,8 @@ require 'readline'
4
4
  require 'chake/tmpdir'
5
5
 
6
6
  module Chake
7
-
8
7
  class Readline
9
-
10
8
  class << self
11
-
12
9
  def history_file
13
10
  raise NotImplementedError
14
11
  end
@@ -22,12 +19,14 @@ module Chake
22
19
  end
23
20
 
24
21
  def init
25
- return if !File.exists?(history_file)
22
+ return unless File.exist?(history_file)
23
+
26
24
  @history = File.readlines(history_file).map(&:strip)
27
25
  end
28
26
 
29
27
  def finish
30
28
  return if !File.writable?(File.dirname(history_file)) || history.empty?
29
+
31
30
  File.open(history_file, 'w') do |f|
32
31
  history.last(500).each do |line|
33
32
  f.puts(line)
@@ -41,18 +40,16 @@ module Chake
41
40
  ::Readline::HISTORY.push(cmd)
42
41
  end
43
42
  input = ::Readline.readline(prompt)
44
- if input && input.strip != '' && input != @last
45
- history.push(input)
46
- end
43
+ history.push(input) if input && input.strip != '' && input != @last
47
44
  input
48
45
  end
49
-
50
46
  end
51
47
 
52
48
  class Commands < Readline
53
49
  def self.history_file
54
50
  File.join(Chake.tmpdir, '.commands_history')
55
51
  end
52
+
56
53
  def self.prompt
57
54
  '$ '
58
55
  end
@@ -62,13 +59,12 @@ module Chake
62
59
  def self.history_file
63
60
  File.join(Chake.tmpdir, '.recipes_history')
64
61
  end
62
+
65
63
  def self.prompt
66
64
  '> '
67
65
  end
68
66
  end
69
-
70
67
  end
71
-
72
68
  end
73
69
 
74
70
  Chake::Readline.constants.each do |subclass|
@@ -1,3 +1,3 @@
1
1
  module Chake
2
- VERSION = "0.21.2"
2
+ VERSION = '0.80'.freeze
3
3
  end
@@ -0,0 +1,2 @@
1
+ chake.1*
2
+ chake.adoc
@@ -1,22 +1,35 @@
1
- task :default => 'man/chake.1'
1
+ MAIN_MANPAGE = 'man/chake.1'.freeze
2
+ OTHER_MANPAGES = %w[
3
+ man/chake-chef.7
4
+ man/chake-itamae.7
5
+ man/chake-shell.7
6
+ ].freeze
7
+ MANPAGES = [MAIN_MANPAGE] + OTHER_MANPAGES
2
8
 
3
- file 'man/chake.1' => ['man/chake.adoc'] do
4
- sh 'asciidoctor --backend manpage --out-file man/chake.1 man/chake.adoc'
5
- end
9
+ task default: :man
10
+ task man: MANPAGES
6
11
 
7
- file 'man/chake.adoc' => ['README.md', 'man/readme2man.sed'] do |t|
8
- sh "sed -f man/readme2man.sed README.md > #{t.name} || (rm -f #{t.name}; false)"
12
+ MANPAGES.each do |man|
13
+ source = "README#{man.pathmap('%n').sub(/^chake/, '').sub('-', '.')}.md"
14
+ file man => [source, 'man/readme2man.sed'] do
15
+ sh "sed -f man/readme2man.sed #{source} > #{man}.ronn || (rm -f #{man}.ronn; false)"
16
+ sh "ronn --roff #{man}.ronn"
17
+ sh "rm -f #{man}.ronn"
18
+ end
9
19
  end
10
20
 
11
- task :install => 'man/chake.1' do
12
- prefix = ENV['PREFIX'] || File.exists?('debian/rules') && '/usr' || '/usr/local'
13
- target = [ENV["DESTDIR"], prefix , 'share/man/man1'].compact
14
- man = File.join(*target)
15
- sh 'install', '-d', '-m', '0755', man
16
- sh 'install', '-m', '0644', 'man/chake.1', man
21
+ task install: MANPAGES do
22
+ prefix = ENV['PREFIX'] || File.exist?('debian/rules') && '/usr' || '/usr/local'
23
+ man1 = File.join(*[ENV['DESTDIR'], prefix, 'share/man/man1'].compact)
24
+ man7 = File.join(*[ENV['DESTDIR'], prefix, 'share/man/man7'].compact)
25
+ target = { '.1' => man1, '.7' => man7 }
26
+ sh 'install', '-d', '-m', '0755', man1
27
+ sh 'install', '-d', '-m', '0755', man7
28
+ MANPAGES.each do |m|
29
+ sh 'install', '-m', '0644', m, target[m.pathmap('%x')]
30
+ end
17
31
  end
18
32
 
19
33
  task :clean do
20
- rm_f 'man/chake.1'
21
- rm_f 'man/chake.adoc'
34
+ rm_f MANPAGES
22
35
  end
@@ -1,6 +1,6 @@
1
- 1a :doctype: manpage
2
-
3
- /^## Install/,/^[^#]/ d
4
- /^## Contributing/,$ d
5
-
1
+ # Capitalize section titles
6
2
  s/^\(##\+\)\(.*\)/\1 \U\2/
3
+
4
+ # Turn fenced code blocks into 4-space indented blocks
5
+ /^```/,/```/ s/^/ /
6
+ /^ ```.*/ d
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Chake::Backend::Local do
3
+ describe Chake::Connection::Local do
4
+ include_examples 'Chake::Connection', Chake::Connection::Local
4
5
 
5
- include_examples "Chake::Backend", Chake::Backend::Local
6
+ let(:node) { Chake::Node.new('local://myusername@myhost/srv/chake') }
6
7
 
7
- let(:node) { Chake::Node.new('local://myusername@myhost/srv/chef') }
8
+ it('runs commands with sh -c') { expect(connection.command_runner).to eq(['sh', '-c']) }
8
9
 
9
- it('runs commands with sh -c') { expect(backend.command_runner).to eq(['sh', '-c']) }
10
-
11
- it('rsyncs locally') { expect(backend.rsync_dest).to eq('/srv/chef/') }
10
+ it('rsyncs locally') { expect(connection.rsync_dest).to eq('/srv/chake/') }
12
11
 
13
12
  it('skips if hostname is not the local hostname') do
14
13
  allow(Socket).to receive(:gethostname).and_return('otherhost')
@@ -1,14 +1,13 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Chake::Backend::Ssh do
3
+ describe Chake::Connection::Ssh do
4
+ include_examples 'Chake::Connection', Chake::Connection::Ssh
4
5
 
5
- include_examples "Chake::Backend", Chake::Backend::Ssh
6
+ let(:node) { Chake::Node.new('ssh://myuser@myhost/srv/chake') }
6
7
 
7
- let(:node) { Chake::Node.new('ssh://myuser@myhost/srv/chef') }
8
+ it('runs commands with ssh') { expect(connection.command_runner).to eq(['ssh', 'myuser@myhost']) }
8
9
 
9
- it('runs commands with ssh') { expect(backend.command_runner).to eq(['ssh', 'myuser@myhost']) }
10
-
11
- it('rsyncs over ssh') { expect(backend.rsync_dest).to eq('myuser@myhost:/srv/chef/') }
10
+ it('rsyncs over ssh') { expect(connection.rsync_dest).to eq('myuser@myhost:/srv/chake/') }
12
11
 
13
12
  it 'uses no remote username if none was passed' do
14
13
  node = Chake::Node.new('theserver')
@@ -24,14 +23,13 @@ describe Chake::Backend::Ssh do
24
23
  context 'with a custom port' do
25
24
  let(:node) { Chake::Node.new('ssh://myhost:2222') }
26
25
  it 'uses port with ssh' do
27
- expect(backend.command_runner).to eq(['ssh', '-p', '2222', 'myhost'])
26
+ expect(connection.command_runner).to eq(['ssh', '-p', '2222', 'myhost'])
28
27
  end
29
28
  it 'uses port with scp' do
30
- expect(backend.scp).to eq(['scp', '-P', '2222'])
29
+ expect(connection.scp).to eq(['scp', '-P', '2222'])
31
30
  end
32
31
  it 'uses port with rsync' do
33
- expect(backend.send(:rsync_ssh)).to eq(['-e', 'ssh -p 2222'])
32
+ expect(connection.send(:rsync_ssh)).to eq(['-e', 'ssh -p 2222'])
34
33
  end
35
34
  end
36
-
37
35
  end
@@ -1,2 +1 @@
1
- require 'chake/backend'
2
-
1
+ require 'chake/connection'
@@ -0,0 +1,38 @@
1
+ require 'chake/node'
2
+ require 'chake/config_manager/chef'
3
+
4
+ describe Chake::ConfigManager::Chef do
5
+ let(:node) do
6
+ Chake::Node.new('foobar')
7
+ end
8
+
9
+ subject do
10
+ Chake::ConfigManager::Chef.new(node)
11
+ end
12
+
13
+ it 'provides a name' do
14
+ expect(subject.name).to eq('chef')
15
+ end
16
+
17
+ it 'calls chef-solo on converge' do
18
+ expect(subject).to receive(:logging).and_return('-l debug')
19
+ expect(node).to receive(:run_as_root).with(%r{chef-solo -c #{node.path}/config.rb -l debug -j #{node.path}/#{Chake.tmpdir}/foobar.json})
20
+ subject.converge
21
+ end
22
+
23
+ it 'calls chef-solo on apply' do
24
+ expect(subject).to receive(:logging).and_return('-l debug')
25
+ expect(node).to receive(:run_as_root).with(%r{chef-solo -c #{node.path}/config.rb -l debug -j #{node.path}/#{Chake.tmpdir}/foobar.json --override-runlist recipe\[myrecipe\]})
26
+ subject.apply('myrecipe')
27
+ end
28
+
29
+ context 'logging' do
30
+ it 'logs when requested' do
31
+ expect(subject.send(:logging)).to eq('')
32
+ end
33
+ it 'only show fatal errrrs when requested' do
34
+ node.silent = true
35
+ expect(subject.send(:logging)).to eq('-l fatal')
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,69 @@
1
+ require 'chake/node'
2
+ require 'chake/config_manager/itamae'
3
+
4
+ describe Chake::ConfigManager::Itamae do
5
+ let(:hostname) { 'foobar' }
6
+ let(:node) do
7
+ Chake::Node.new(hostname).tap do |n|
8
+ n.silent = true
9
+ n.data['itamae'] = ['foo.rb', 'bar.rb']
10
+ end
11
+ end
12
+ let(:cfg) { Chake::ConfigManager::Itamae.new(node) }
13
+ let(:output) { StringIO.new("line1\nline2\n") }
14
+
15
+ it 'does not require bootstrapping' do
16
+ expect(cfg.needs_bootstrap?).to eq(false)
17
+ end
18
+
19
+ it 'does not require uploading' do
20
+ expect(cfg.needs_upload?).to eq(false)
21
+ end
22
+
23
+ it 'calls itamae when converging' do
24
+ expect(IO).to receive(:popen).with(
25
+ array_including('itamae', 'foo.rb', 'bar.rb'),
26
+ 'r',
27
+ err: %i[child out]
28
+ ).and_return(output)
29
+ cfg.converge
30
+ end
31
+
32
+ it 'calls itamae when applying' do
33
+ expect(IO).to receive(:popen).with(
34
+ array_including('itamae', 'foobarbaz.rb'),
35
+ 'r',
36
+ err: %i[child out]
37
+ ).and_return(output)
38
+ cfg.apply('foobarbaz.rb')
39
+ end
40
+
41
+ context 'for ssh hosts' do
42
+ let(:hostname) { 'ssh://theusernanme@thehostname' }
43
+ it 'calls itamae ssh subcommand' do
44
+ expect(IO).to receive(:popen).with(
45
+ array_including('itamae', 'ssh', '--host=thehostname', '--user=theusernanme'),
46
+ anything,
47
+ err: anything
48
+ ).and_return(output)
49
+ cfg.converge
50
+ end
51
+ end
52
+
53
+ context 'for local hosts' do
54
+ let(:hostname) { 'local://localhostname' }
55
+ it 'calls itamae with local subcommand' do
56
+ expect(IO).to receive(:popen).with(
57
+ ['itamae', 'local', /--node-json=.*/, 'foo.rb', 'bar.rb'],
58
+ anything,
59
+ err: anything
60
+ ).and_return(output)
61
+ cfg.converge
62
+ end
63
+ end
64
+
65
+ it 'throws an error for unsupported connection' do
66
+ allow(node).to receive(:connection).and_return(Object.new)
67
+ expect(-> { cfg.converge }).to raise_error(NotImplementedError)
68
+ end
69
+ end
@@ -0,0 +1,54 @@
1
+ require 'chake/node'
2
+ require 'chake/config_manager'
3
+ require 'chake/config_manager/shell'
4
+
5
+ describe Chake::ConfigManager::Shell do |_c|
6
+ let(:node) do
7
+ Chake::Node.new('foobar').tap do |n|
8
+ allow(n).to receive(:path).and_return(nil)
9
+ end
10
+ end
11
+ it 'accepts node with explicit config_manager in data' do
12
+ node.data['config_manager'] = 'shell'
13
+ expect(Chake::ConfigManager.get(node)).to be_a(Chake::ConfigManager::Shell)
14
+ end
15
+ it 'accepts node with `shell` in data' do
16
+ node.data['shell'] = ['date']
17
+ expect(Chake::ConfigManager.get(node)).to be_a(Chake::ConfigManager::Shell)
18
+ end
19
+
20
+ let(:subject) { Chake::ConfigManager::Shell.new(node) }
21
+
22
+ it 'calls all shell commands on converge' do
23
+ node.data['shell'] = %w[date true]
24
+ expect(node).to receive(:run_as_root).with("sh -xec 'date && true'")
25
+ subject.converge
26
+ end
27
+
28
+ it 'changes to node path to run commands' do
29
+ node.data['shell'] = %w[true]
30
+ allow(node).to receive(:path).and_return('/foo')
31
+ expect(node).to receive(:run_as_root).with("sh -xec 'cd /foo && true'")
32
+ subject.converge
33
+ end
34
+
35
+ it 'calls given shell command on apply' do
36
+ node.data['shell'] = %w[date true]
37
+ expect(node).to receive(:run_as_root).with("sh -xec 'reboot'")
38
+ subject.apply('reboot')
39
+ end
40
+
41
+ it 'hides output on converge in silent mode' do
42
+ node.data['shell'] = ['date']
43
+ node.silent = true
44
+ expect(node).to receive(:run_as_root).with("sh -ec 'date' >/dev/null")
45
+ subject.converge
46
+ end
47
+
48
+ it 'hides output on apply in silent mode' do
49
+ node.data['shell'] = ['date']
50
+ node.silent = true
51
+ expect(node).to receive(:run_as_root).with("sh -ec 'reboot' >/dev/null")
52
+ subject.apply('reboot')
53
+ end
54
+ end