chake 0.20 → 0.81

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.ackrc +2 -0
  3. data/.gitignore +22 -0
  4. data/.gitlab-ci.yml +24 -0
  5. data/.manifest +65 -0
  6. data/.rubocop.yml +55 -0
  7. data/.rubocop_todo.yml +40 -0
  8. data/ChangeLog.md +36 -0
  9. data/README.chef.md +70 -0
  10. data/README.itamae.md +58 -0
  11. data/README.md +118 -85
  12. data/README.shell.md +30 -0
  13. data/Rakefile +36 -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 +110 -154
  24. data/lib/chake/bootstrap/chef/01_installed.sh +4 -0
  25. data/lib/chake/bootstrap/{01_debian.sh → chef/02_debian.sh} +0 -0
  26. data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
  27. data/lib/chake/config.rb +2 -7
  28. data/lib/chake/config_manager.rb +89 -0
  29. data/lib/chake/config_manager/chef.rb +35 -0
  30. data/lib/chake/config_manager/itamae.rb +57 -0
  31. data/lib/chake/config_manager/shell.rb +34 -0
  32. data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
  33. data/lib/chake/config_manager/skel/chef/config.rb +4 -0
  34. data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
  35. data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
  36. data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
  37. data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
  38. data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
  39. data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
  40. data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
  41. data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
  42. data/lib/chake/connection.rb +83 -0
  43. data/lib/chake/{backend → connection}/local.rb +2 -8
  44. data/lib/chake/{backend → connection}/ssh.rb +6 -14
  45. data/lib/chake/node.rb +49 -29
  46. data/lib/chake/readline.rb +6 -10
  47. data/lib/chake/version.rb +1 -1
  48. data/lib/chake/wipe.rb +18 -0
  49. data/man/.gitignore +2 -0
  50. data/man/Rakefile +28 -14
  51. data/man/readme2man.sed +5 -5
  52. data/spec/chake/backend/local_spec.rb +5 -6
  53. data/spec/chake/backend/ssh_spec.rb +8 -10
  54. data/spec/chake/backend_spec.rb +1 -2
  55. data/spec/chake/config_manager/chef_spec.rb +38 -0
  56. data/spec/chake/config_manager/itamae_spec.rb +87 -0
  57. data/spec/chake/config_manager/shell_spec.rb +54 -0
  58. data/spec/chake/config_manager_spec.rb +23 -0
  59. data/spec/chake/node_spec.rb +38 -15
  60. data/spec/spec_helper.rb +37 -17
  61. metadata +65 -39
  62. data/coverage/assets/0.10.0/application.css +0 -799
  63. data/coverage/assets/0.10.0/application.js +0 -34783
  64. data/coverage/assets/0.10.0/colorbox/border.png +0 -0
  65. data/coverage/assets/0.10.0/colorbox/controls.png +0 -0
  66. data/coverage/assets/0.10.0/colorbox/loading.gif +0 -0
  67. data/coverage/assets/0.10.0/colorbox/loading_background.png +0 -0
  68. data/coverage/assets/0.10.0/favicon_green.png +0 -0
  69. data/coverage/assets/0.10.0/favicon_red.png +0 -0
  70. data/coverage/assets/0.10.0/favicon_yellow.png +0 -0
  71. data/coverage/assets/0.10.0/loading.gif +0 -0
  72. data/coverage/assets/0.10.0/magnify.png +0 -0
  73. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  74. data/coverage/assets/0.10.0/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  75. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  76. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  77. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  78. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  79. data/coverage/assets/0.10.0/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  80. data/coverage/assets/0.10.0/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  81. data/coverage/assets/0.10.0/smoothness/images/ui-icons_222222_256x240.png +0 -0
  82. data/coverage/assets/0.10.0/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  83. data/coverage/assets/0.10.0/smoothness/images/ui-icons_454545_256x240.png +0 -0
  84. data/coverage/assets/0.10.0/smoothness/images/ui-icons_888888_256x240.png +0 -0
  85. data/coverage/assets/0.10.0/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  86. data/coverage/index.html +0 -2326
  87. data/lib/chake/backend.rb +0 -80
@@ -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_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.20"
2
+ VERSION = '0.81'.freeze
3
3
  end
@@ -0,0 +1,18 @@
1
+ require 'singleton'
2
+
3
+ module Chake
4
+ class Wipe
5
+ include Singleton
6
+
7
+ if system('which', 'wipe', out: '/dev/null', err: :out)
8
+ def wipe(file)
9
+ system('wipe', '-rfs', file)
10
+ end
11
+ else
12
+ warn 'W: please install "wipe" program for secure deletion, falling back to unlink(2)'
13
+ def wipe(file)
14
+ File.unlink(file)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,2 @@
1
+ chake.1*
2
+ chake.adoc
@@ -1,22 +1,36 @@
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
+ sh 'sed', '-i', '-e', 's/\\\\\'/\'/', man
19
+ end
9
20
  end
10
21
 
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
22
+ task install: MANPAGES do
23
+ prefix = ENV['PREFIX'] || File.exist?('debian/rules') && '/usr' || '/usr/local'
24
+ man1 = File.join(*[ENV['DESTDIR'], prefix, 'share/man/man1'].compact)
25
+ man7 = File.join(*[ENV['DESTDIR'], prefix, 'share/man/man7'].compact)
26
+ target = { '.1' => man1, '.7' => man7 }
27
+ sh 'install', '-d', '-m', '0755', man1
28
+ sh 'install', '-d', '-m', '0755', man7
29
+ MANPAGES.each do |m|
30
+ sh 'install', '-m', '0644', m, target[m.pathmap('%x')]
31
+ end
17
32
  end
18
33
 
19
34
  task :clean do
20
- rm_f 'man/chake.1'
21
- rm_f 'man/chake.adoc'
35
+ rm_f MANPAGES
22
36
  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,87 @@
1
+ require 'spec_helper'
2
+ require 'chake/node'
3
+ require 'chake/config_manager/itamae'
4
+
5
+ describe Chake::ConfigManager::Itamae do
6
+ let(:hostname) { 'foobar' }
7
+ let(:node) do
8
+ Chake::Node.new(hostname).tap do |n|
9
+ n.silent = true
10
+ n.data['itamae'] = ['foo.rb', 'bar.rb']
11
+ end
12
+ end
13
+ let(:cfg) { Chake::ConfigManager::Itamae.new(node) }
14
+ let(:output) { StringIO.new("line1\nline2\n") }
15
+
16
+ it 'does not require uploading' do
17
+ expect(cfg.needs_upload?).to eq(false)
18
+ end
19
+
20
+ it 'calls itamae when converging' do
21
+ expect(IO).to receive(:popen).with(
22
+ array_including('itamae', 'foo.rb', 'bar.rb'),
23
+ 'r',
24
+ err: %i[child out]
25
+ ).and_return(output)
26
+ cfg.converge
27
+ end
28
+
29
+ it 'calls itamae when applying' do
30
+ expect(IO).to receive(:popen).with(
31
+ array_including('itamae', 'foobarbaz.rb'),
32
+ 'r',
33
+ err: %i[child out]
34
+ ).and_return(output)
35
+ cfg.apply('foobarbaz.rb')
36
+ end
37
+
38
+ context 'for ssh hosts' do
39
+ let(:hostname) { 'ssh://theusernanme@thehostname' }
40
+ it 'calls itamae ssh subcommand' do
41
+ expect(IO).to receive(:popen).with(
42
+ array_including('itamae', 'ssh', '--host=thehostname', '--user=theusernanme'),
43
+ anything,
44
+ err: anything
45
+ ).and_return(output)
46
+ cfg.converge
47
+ end
48
+ end
49
+
50
+ context 'for local hosts' do
51
+ let(:hostname) { 'local://localhostname' }
52
+ it 'calls itamae with local subcommand' do
53
+ expect(IO).to receive(:popen).with(
54
+ array_including('itamae', 'local', /--node-json=.*/, 'foo.rb', 'bar.rb'),
55
+ anything,
56
+ err: anything
57
+ ).and_return(output)
58
+ cfg.converge
59
+ end
60
+ end
61
+
62
+ it 'throws an error for unsupported connection' do
63
+ allow(node).to receive(:connection).and_return(Object.new)
64
+ expect(-> { cfg.converge }).to raise_error(NotImplementedError)
65
+ end
66
+
67
+ it 'handles silent mode' do
68
+ expect(IO).to receive(:popen).with(
69
+ array_including('--log-level=warn'),
70
+ anything,
71
+ err: anything
72
+ ).and_return(output)
73
+ cfg.converge
74
+ end
75
+
76
+ RSpec::Matchers.define_negated_matcher :array_excluding, :include
77
+
78
+ it 'handles non-silent mode' do
79
+ node.silent = false
80
+ expect(IO).to receive(:popen).with(
81
+ array_excluding('--log-level=warn'),
82
+ anything,
83
+ err: anything
84
+ ).and_return(output)
85
+ silence($stdout) { cfg.converge }
86
+ end
87
+ end