chake 0.21.1 → 0.82

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 +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 +38 -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 +0 -64
  19. data/examples/test/config.rb +4 -4
  20. data/examples/test/cookbooks/example/files/{host-homer → host-lemur}/test.asc +0 -0
  21. data/lib/chake.rb +111 -153
  22. data/lib/chake/bootstrap/chef/01_installed.sh +4 -0
  23. data/lib/chake/bootstrap/{01_debian.sh → chef/02_debian.sh} +0 -0
  24. data/lib/chake/bootstrap/{99_unsupported.sh → chef/99_unsupported.sh} +0 -0
  25. data/lib/chake/config.rb +10 -8
  26. data/lib/chake/config_manager.rb +89 -0
  27. data/lib/chake/config_manager/chef.rb +35 -0
  28. data/lib/chake/config_manager/itamae.rb +57 -0
  29. data/lib/chake/config_manager/shell.rb +34 -0
  30. data/lib/chake/config_manager/skel/chef/Rakefile +1 -0
  31. data/lib/chake/config_manager/skel/chef/config.rb +4 -0
  32. data/lib/chake/config_manager/skel/chef/cookbooks/basics/recipes/default.rb +1 -0
  33. data/lib/chake/config_manager/skel/chef/nodes.yaml +3 -0
  34. data/lib/chake/config_manager/skel/itamae/Rakefile +1 -0
  35. data/lib/chake/config_manager/skel/itamae/cookbooks/basics/default.rb +1 -0
  36. data/lib/chake/config_manager/skel/itamae/nodes.yaml +3 -0
  37. data/lib/chake/config_manager/skel/itamae/roles/basic.rb +1 -0
  38. data/lib/chake/config_manager/skel/shell/Rakefile +1 -0
  39. data/lib/chake/config_manager/skel/shell/nodes.yaml +3 -0
  40. data/lib/chake/connection.rb +83 -0
  41. data/lib/chake/{backend → connection}/local.rb +2 -8
  42. data/lib/chake/{backend → connection}/ssh.rb +6 -14
  43. data/lib/chake/node.rb +49 -29
  44. data/lib/chake/readline.rb +6 -10
  45. data/lib/chake/version.rb +1 -1
  46. data/lib/chake/wipe.rb +18 -0
  47. data/man/.gitignore +2 -0
  48. data/man/Rakefile +28 -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 +87 -0
  55. data/spec/chake/config_manager/shell_spec.rb +54 -0
  56. data/spec/chake/config_manager_spec.rb +23 -0
  57. data/spec/chake/node_spec.rb +38 -15
  58. data/spec/spec_helper.rb +37 -17
  59. metadata +66 -47
  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
data/lib/chake/node.rb CHANGED
@@ -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|
data/lib/chake/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chake
2
- VERSION = "0.21.1"
2
+ VERSION = '0.82'.freeze
3
3
  end
data/lib/chake/wipe.rb ADDED
@@ -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 the \`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
data/man/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ chake.1*
2
+ chake.adoc
data/man/Rakefile CHANGED
@@ -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/\\\\\'/\'/g', 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
data/man/readme2man.sed CHANGED
@@ -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