chake 0.21.2 → 0.80

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