chake 0.0.1 → 0.1.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.
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ /examples/test/nodes.yaml
19
+ /examples/test/.tmp
data/Rakefile CHANGED
@@ -1 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
+
3
+ task :default do
4
+ sh 'rspec', '--color'
5
+ end
6
+
7
+ task :release => :default
data/chake.gemspec CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency "bundler", "~> 1.5"
22
22
  spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
23
24
 
24
25
  spec.add_dependency "rake"
25
26
  end
@@ -1,9 +1,5 @@
1
- begin
2
- require 'chake'
3
- rescue LoadError
4
- $: << '../../lib'
5
- retry
6
- end
1
+ $: << '../../lib' # this shouldn't be needed when you have chake installed
2
+ require 'chake'
7
3
 
8
4
  desc 'removes everything'
9
5
  task :clean do
@@ -0,0 +1,18 @@
1
+ -----BEGIN PGP MESSAGE-----
2
+ Version: GnuPG v1
3
+
4
+ hQIMA5A8ZkAWdYz7AQ//dgQhKXuGS0dY04TXa1cXEtOHYC64LLnoo+UZT/KgnkzI
5
+ /IAgLFbAV0Fd9DGLK857qv9fWf/FB5b6loYLPOVEys0mb5aQrqPPFeKwTTXxTJdk
6
+ Ts2NvSVfNZx95Igotm/vkKW6/dbGlDfQSOVQMzhvmKOMMpRP+ixqn+G/nwE/0wQG
7
+ QXc3UPAe9gBFlI9GWLTafhftwrxXiYeNF8N03W9BUk1OiQqJUmDRK9ZjVe/vbEiZ
8
+ iQ689MnF5l+/6gptU0j77QIqk5vEItkl7RISxOS8PDFI+926NV3fZUrRAlu2eDIy
9
+ HNMGkcKTjGoPNrj/UzzmqjP3uNQtKoK559cul7uY44QmsEINoFP3HoAOHeVxm1pG
10
+ 6IG5EY6znwVEYTIVPK21NAPFpfk9yAB53sv6GrtOaYp8FFp+lLHJlPQcOOP51UV9
11
+ GqiAuec7Lgr3iy1yaXIBHFlLG0lmZ1OI41zwqh+Z8EF1NC0gFPhGuOJqBGmBbOxy
12
+ Wt1IFx1JUHi0f277us9pFbQlcUb8tgFZ0J69epBrod+xWaZQKFwLWsCTN66fKJfp
13
+ rqBzRBcNDJwsKOd54v/Cmrws8bwnfB8iNZYuEQdEt9u5TGIZFl4R9k+/pydAQouP
14
+ Z95g4vh2ST2ZEeblnbCc2TFY/7j2O+aXyBzX/4+/bM0kZbFjxCNuefU/v3ZnDwXS
15
+ QAH+NPQ31IiqMmoETUqHzo1UI+hV9em81Llt2bsbgWULp84LEmzczbjAcMi2EWNt
16
+ SzPyJzOLrqEvEYg2O/+bGho=
17
+ =Y2iZ
18
+ -----END PGP MESSAGE-----
@@ -0,0 +1,6 @@
1
+ file '/tmp/chake.test' do
2
+ content "It works!\n"
3
+ mode '0644'
4
+ owner 'root'
5
+ group 'root'
6
+ end
data/lib/chake.rb CHANGED
@@ -5,21 +5,23 @@ require 'json'
5
5
  require 'tmpdir'
6
6
  require 'readline'
7
7
 
8
- $nodes_file = ENV['NODES'] || 'nodes.yaml'
9
- $node_data = File.exists?($nodes_file) && YAML.load_file($nodes_file) || {}
10
- $nodes = $node_data.keys
8
+ require 'chake/node'
9
+
10
+ nodes_file = ENV['NODES'] || 'nodes.yaml'
11
+ node_data = File.exists?(nodes_file) && YAML.load_file(nodes_file) || {}
12
+ $nodes = node_data.map { |node,data| Chake::Node.new(node, data) }
11
13
 
12
- $sample_nodes = <<EOF
13
- host1.my.tld:
14
- run_list:
15
- - recipe[myhost]
16
- EOF
17
14
 
18
15
  desc "Initializes current directory with sample structure"
19
16
  task :init do
20
17
  if !File.exists?('nodes.yaml')
21
18
  File.open('nodes.yaml', 'w') do |f|
22
- f.write($sample_nodes)
19
+ sample_nodes = <<EOF
20
+ host1.my.tld:
21
+ run_list:
22
+ - recipe[myhost]
23
+ EOF
24
+ f.write(sample_nodes)
23
25
  puts "→ nodes.yaml"
24
26
  end
25
27
  end
@@ -45,7 +47,9 @@ end
45
47
 
46
48
  desc 'list nodes'
47
49
  task :nodes do
48
- puts $nodes
50
+ $nodes.each do |node|
51
+ puts "%-40s %-5s\n" % [node.hostname, node.backend]
52
+ end
49
53
  end
50
54
 
51
55
  def encrypted_for(node)
@@ -71,23 +75,25 @@ end
71
75
 
72
76
  $nodes.each do |node|
73
77
 
74
- desc "bootstrap #{node}"
75
- task "bootstrap:#{node}" do
78
+ hostname = node.hostname
79
+
80
+ desc "bootstrap #{hostname}"
81
+ task "bootstrap:#{hostname}" do
76
82
  mkdir_p '.tmp', :verbose => false
77
- config = '.tmp/' + node + '.json'
83
+ config = '.tmp/' + hostname + '.json'
78
84
 
79
85
  seen_before = File.exists?(config)
80
86
 
81
87
  # overwrite config with current contents
82
88
  File.open(config, 'w') do |f|
83
- json_data = $node_data[node]
89
+ json_data = node.data
84
90
  f.write(JSON.dump(json_data))
85
91
  f.write("\n")
86
92
  end
87
93
 
88
94
  unless seen_before
89
95
  begin
90
- sh "ssh root@#{node} 'apt-get -q -y install rsync chef'"
96
+ node.run_as_root('apt-get -q -y install rsync chef')
91
97
  rescue
92
98
  rm_f config
93
99
  raise
@@ -96,41 +102,40 @@ $nodes.each do |node|
96
102
 
97
103
  end
98
104
 
99
- desc "upload data to #{node}"
100
- task "upload:#{node}" do
101
- encrypted = encrypted_for(node)
102
- excludes = encrypted.values.map { |f| "--exclude #{f}" }.join(' ')
105
+ desc "upload data to #{hostname}"
106
+ task "upload:#{hostname}" do
107
+ encrypted = encrypted_for(hostname)
108
+ rsync_excludes = (encrypted.values + encrypted.keys).map { |f| ["--exclude", f] }.flatten
103
109
 
104
- rsync = "rsync -avp --delete --exclude .git/ #{excludes}"
110
+ rsync = "rsync", "-avp", "--exclude", ".git/"
111
+ rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
105
112
 
106
- Dir.mktmpdir do |tmpdir|
107
- sh "#{rsync} --quiet ./ #{tmpdir}/"
108
- files = Dir.chdir(tmpdir) { Dir.glob("**/*").select { |f| !File.directory?(f) } }
109
- if_files_changed(node, 'plain', files) do
110
- rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
111
- sh "#{rsync} #{rsync_logging} ./ root@#{node}:/srv/chef/"
112
- end
113
+ files = Dir.glob("**/*").select { |f| !File.directory?(f) } - encrypted.keys - encrypted.values
114
+ if_files_changed(hostname, 'plain', files) do
115
+ sh *rsync, '--delete', rsync_logging, *rsync_excludes, './', node.rsync_dest
113
116
  end
114
117
 
115
- if_files_changed(node, 'enc', encrypted.keys) do
116
- encrypted.each do |encrypted_file, target_file|
117
- sh "gpg --quiet --batch --use-agent --decrypt #{encrypted_file} | ssh root@#{node} 'cat > /srv/chef/#{target_file}; chmod 600 /srv/chef/#{target_file}'"
118
+ if_files_changed(hostname, 'enc', encrypted.keys) do
119
+ Dir.mktmpdir do |tmpdir|
120
+ encrypted.each do |encrypted_file, target_file|
121
+ target = File.join(tmpdir, target_file)
122
+ mkdir_p(File.dirname(target))
123
+ sh 'gpg', '--quiet', '--batch', '--use-agent', '--output', target, '--decrypt', encrypted_file
124
+ end
125
+ sh *rsync, rsync_logging, tmpdir + '/', node.rsync_dest
118
126
  end
119
127
  end
120
128
  end
121
129
 
122
- desc "converge #{node}"
123
- task "converge:#{node}" => ["bootstrap:#{node}", "upload:#{node}"] do
130
+ desc "converge #{hostname}"
131
+ task "converge:#{hostname}" => ["bootstrap:#{hostname}", "upload:#{hostname}"] do
124
132
  chef_logging = Rake.application.options.silent && '-l fatal' || ''
125
- sh "ssh root@#{node} 'chef-solo -c /srv/chef/config.rb #{chef_logging} -j /srv/chef/.tmp/#{node}.json'"
133
+ node.run_as_root "chef-solo -c #{node.path}/config.rb #{chef_logging} -j #{node.path}/.tmp/#{hostname}.json"
126
134
  end
127
135
 
128
- desc "run a command on #{node}"
129
- task "run:#{node}" => 'run_input' do
130
- cmd = ['ssh', "root@#{node}", $cmd]
131
- IO.popen(cmd).lines.each do |line|
132
- puts "#{node}: #{line}"
133
- end
136
+ desc "run a command on #{hostname}"
137
+ task "run:#{hostname}" => 'run_input' do
138
+ node.run($cmd)
134
139
  end
135
140
  end
136
141
 
@@ -139,15 +144,15 @@ task :run_input do
139
144
  end
140
145
 
141
146
  desc "upload to all nodes"
142
- task :upload => $nodes.map { |node| "upload:#{node}" }
147
+ task :upload => $nodes.map { |node| "upload:#{node.hostname}" }
143
148
 
144
149
  desc "bootstrap all nodes"
145
- task :bootstrap => $nodes.map { |node| "bootstrap:#{node}" }
150
+ task :bootstrap => $nodes.map { |node| "bootstrap:#{node.hostname}" }
146
151
 
147
152
  desc "converge all nodes (default)"
148
- task "converge" => $nodes.map { |node| "converge:#{node}" }
153
+ task "converge" => $nodes.map { |node| "converge:#{node.hostname}" }
149
154
 
150
155
  task "run a command on all nodes"
151
- task :run => $nodes.map { |node| "run:#{node}" }
156
+ task :run => $nodes.map { |node| "run:#{node.hostname}" }
152
157
 
153
158
  task :default => :converge
@@ -0,0 +1,46 @@
1
+ module Chake
2
+
3
+ class Backend < Struct.new(:node)
4
+
5
+ def rsync_dest
6
+ node.path + '/'
7
+ end
8
+
9
+ def run(cmd)
10
+ IO.popen(command_runner + [cmd]).lines.each do |line|
11
+ puts [node.hostname, line.strip].join(': ')
12
+ end
13
+ end
14
+
15
+ def run_as_root(cmd)
16
+ if node.username == 'root'
17
+ run(cmd)
18
+ else
19
+ run('sudo ' + cmd)
20
+ end
21
+ end
22
+
23
+ def to_s
24
+ self.class.backend_name
25
+ end
26
+
27
+ def self.backend_name
28
+ name.split("::").last.downcase
29
+ end
30
+
31
+ def self.inherited(subclass)
32
+ @backends ||= []
33
+ @backends << subclass
34
+ end
35
+
36
+ def self.get(name)
37
+ backend = @backends.find { |b| b.backend_name == name }
38
+ backend || raise(ArgumentError.new("Invalid backend name: #{name}"))
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ require 'chake/backend/ssh'
46
+ require 'chake/backend/local'
@@ -0,0 +1,15 @@
1
+ module Chake
2
+
3
+ class Backend
4
+
5
+ class Local < Backend
6
+
7
+ def command_runner
8
+ ['sh', '-c']
9
+ end
10
+
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,25 @@
1
+ module Chake
2
+
3
+ class Backend
4
+
5
+ class Ssh < Backend
6
+
7
+ def rsync_dest
8
+ [ssh_target, node.path + '/'].join(':')
9
+ end
10
+
11
+ def command_runner
12
+ ['ssh', ssh_target]
13
+ end
14
+
15
+ private
16
+
17
+ def ssh_target
18
+ [node.username, node.hostname].compact.join('@')
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
data/lib/chake/node.rb ADDED
@@ -0,0 +1,44 @@
1
+ require 'uri'
2
+ require 'etc'
3
+ require 'forwardable'
4
+
5
+ require 'chake/backend'
6
+
7
+ module Chake
8
+
9
+ class Node
10
+
11
+ extend Forwardable
12
+
13
+ attr_reader :hostname
14
+ attr_reader :username
15
+ attr_reader :path
16
+ attr_reader :data
17
+
18
+ def initialize(hostname, data = {})
19
+ uri = URI.parse(hostname)
20
+ if !uri.scheme && !uri.host && uri.path
21
+ uri = URI.parse("ssh://#{hostname}")
22
+ end
23
+ if uri.path.empty?
24
+ uri.path = nil
25
+ end
26
+
27
+ @backend_name = uri.scheme
28
+
29
+ @hostname = uri.hostname
30
+ @username = uri.user || Etc.getpwuid.name
31
+ @path = uri.path || "/tmp/chef.#{username}"
32
+ @data = data
33
+ end
34
+
35
+ def backend
36
+ @backend ||= Chake::Backend.get(@backend_name).new(self)
37
+ end
38
+
39
+ def_delegators :backend, :run, :run_as_root, :rsync_dest
40
+
41
+ end
42
+
43
+ end
44
+
data/lib/chake/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Chake
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chake::Backend::Local do
4
+
5
+ include_examples "Chake::Backend", Chake::Backend::Local
6
+
7
+ let(:node) { Chake::Node.new('local://myhost/srv/chef') }
8
+
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/') }
12
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Chake::Backend::Ssh do
4
+
5
+ include_examples "Chake::Backend", Chake::Backend::Ssh, ->() { }
6
+
7
+ let(:node) { Chake::Node.new('ssh://myuser@myhost/srv/chef') }
8
+
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/') }
12
+
13
+ end
@@ -0,0 +1,2 @@
1
+ require 'chake/backend'
2
+
@@ -0,0 +1,50 @@
1
+ require 'chake/node'
2
+
3
+ describe Chake::Node do
4
+
5
+ before do
6
+ ent = double
7
+ ent.stub(:name).and_return('jonhdoe')
8
+ Etc.stub(:getpwuid).and_return(ent)
9
+ end
10
+
11
+ let(:simple) { Chake::Node.new('hostname') }
12
+ it('has a name') { expect(simple.hostname).to eq('hostname') }
13
+ it('uses ssh by default') { expect(simple.backend).to be_an_instance_of(Chake::Backend::Ssh) }
14
+ it('user current username by default') {
15
+ expect(simple.username).to eq('jonhdoe')
16
+ }
17
+ it('write to /tmp/chef.$username') {
18
+ expect(simple.path).to eq('/tmp/chef.jonhdoe')
19
+ }
20
+
21
+ let(:with_username) { Chake::Node.new('username@hostname') }
22
+ it('accepts username') { expect(with_username.username).to eq('username') }
23
+ it('uses ssh') { expect(with_username.backend).to be_an_instance_of(Chake::Backend::Ssh) }
24
+
25
+ let(:with_backend) { Chake::Node.new('local://hostname')}
26
+ it('accepts backend as URI scheme') { expect(with_backend.backend).to be_an_instance_of(Chake::Backend::Local) }
27
+
28
+ it('wont accept any backend') do
29
+ expect { Chake::Node.new('foobar://bazqux').backend }.to raise_error(ArgumentError)
30
+ end
31
+
32
+ let(:with_data) { Chake::Node.new('local://localhost', 'run_list' => ['recipe[common]']) }
33
+ it('takes data') do
34
+ expect(with_data.data).to be_a(Hash)
35
+ end
36
+
37
+ [:run, :run_as_root, :rsync_dest].each do |method|
38
+ it("delegates #{method} to backend") do
39
+ node = simple
40
+
41
+ backend = double
42
+ args = Object.new
43
+ node.stub(:backend).and_return(backend)
44
+
45
+ backend.should_receive(method).with(args)
46
+ node.send(method, args)
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,29 @@
1
+ require 'chake/node'
2
+ require 'chake/backend'
3
+
4
+
5
+ shared_examples "Chake::Backend" do |backend_class|
6
+
7
+ let(:backend) { backend_class.new(node) }
8
+
9
+ it('runs commands') do
10
+ io = StringIO.new("line 1\nline 2\n")
11
+ IO.should_receive(:popen).with(backend.command_runner + ['something']).and_return(io)
12
+ backend.should_receive(:puts).with("myhost: line 1")
13
+ backend.should_receive(:puts).with("myhost: line 2")
14
+ backend.run('something')
15
+ end
16
+
17
+ it('runs as root') do
18
+ backend.should_receive(:run).with('sudo something')
19
+ backend.run_as_root('something')
20
+ end
21
+
22
+ it('does not use sudo if already root') do
23
+ backend.node.stub(:username).and_return('root')
24
+ backend.should_receive(:run).with('something')
25
+ backend.run_as_root('something')
26
+ end
27
+
28
+ end
29
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chake
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-02-21 00:00:00.000000000 Z
12
+ date: 2014-02-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -43,6 +43,22 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
46
62
  - !ruby/object:Gem::Dependency
47
63
  name: rake
48
64
  requirement: !ruby/object:Gem::Requirement
@@ -74,13 +90,21 @@ files:
74
90
  - README.md
75
91
  - Rakefile
76
92
  - chake.gemspec
77
- - examples/test/.tmp/lvh.me.plain.sha1sum
78
93
  - examples/test/Rakefile
79
94
  - examples/test/config.rb
80
- - examples/test/cookbooks/myhost/recipes/default.rb
81
- - examples/test/nodes.yaml
95
+ - examples/test/cookbooks/example/files/default/test.asc
96
+ - examples/test/cookbooks/example/recipes/default.rb
82
97
  - lib/chake.rb
98
+ - lib/chake/backend.rb
99
+ - lib/chake/backend/local.rb
100
+ - lib/chake/backend/ssh.rb
101
+ - lib/chake/node.rb
83
102
  - lib/chake/version.rb
103
+ - spec/chake/backend/local_spec.rb
104
+ - spec/chake/backend/ssh_spec.rb
105
+ - spec/chake/backend_spec.rb
106
+ - spec/chake/node_spec.rb
107
+ - spec/spec_helper.rb
84
108
  homepage: https://github.com/terceiro/chake
85
109
  licenses:
86
110
  - MIT
@@ -106,4 +130,9 @@ rubygems_version: 1.8.23
106
130
  signing_key:
107
131
  specification_version: 3
108
132
  summary: Simple host management with chef and rake. No chef server required.
109
- test_files: []
133
+ test_files:
134
+ - spec/chake/backend/local_spec.rb
135
+ - spec/chake/backend/ssh_spec.rb
136
+ - spec/chake/backend_spec.rb
137
+ - spec/chake/node_spec.rb
138
+ - spec/spec_helper.rb
@@ -1,4 +0,0 @@
1
- a70248580db38a6659bba1887c16ead12d8d2f6c config.rb
2
- 3f89ae2a25368bb08d8ecbe1b12028cdbd29bb23 nodes.yaml
3
- 7e805ef45e1ee657afb162fd98e882caf9113bc0 cookbooks/myhost/recipes/default.rb
4
- ff86a66c0e8bb3dd6f526ab94ffd57f23c7153f1 Rakefile
@@ -1 +0,0 @@
1
- package 'openssh-server'
@@ -1,3 +0,0 @@
1
- lvh.me:
2
- run_list:
3
- - recipe[myhost]