chake 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/Rakefile +6 -0
- data/chake.gemspec +1 -0
- data/examples/test/Rakefile +2 -6
- data/examples/test/cookbooks/example/files/default/test.asc +18 -0
- data/examples/test/cookbooks/example/recipes/default.rb +6 -0
- data/lib/chake.rb +48 -43
- data/lib/chake/backend.rb +46 -0
- data/lib/chake/backend/local.rb +15 -0
- data/lib/chake/backend/ssh.rb +25 -0
- data/lib/chake/node.rb +44 -0
- data/lib/chake/version.rb +1 -1
- data/spec/chake/backend/local_spec.rb +12 -0
- data/spec/chake/backend/ssh_spec.rb +13 -0
- data/spec/chake/backend_spec.rb +2 -0
- data/spec/chake/node_spec.rb +50 -0
- data/spec/spec_helper.rb +29 -0
- metadata +35 -6
- data/examples/test/.tmp/lvh.me.plain.sha1sum +0 -4
- data/examples/test/cookbooks/myhost/recipes/default.rb +0 -1
- data/examples/test/nodes.yaml +0 -3
data/.gitignore
CHANGED
data/Rakefile
CHANGED
data/chake.gemspec
CHANGED
data/examples/test/Rakefile
CHANGED
@@ -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-----
|
data/lib/chake.rb
CHANGED
@@ -5,21 +5,23 @@ require 'json'
|
|
5
5
|
require 'tmpdir'
|
6
6
|
require 'readline'
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
75
|
-
|
78
|
+
hostname = node.hostname
|
79
|
+
|
80
|
+
desc "bootstrap #{hostname}"
|
81
|
+
task "bootstrap:#{hostname}" do
|
76
82
|
mkdir_p '.tmp', :verbose => false
|
77
|
-
config = '.tmp/' +
|
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 =
|
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
|
-
|
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 #{
|
100
|
-
task "upload:#{
|
101
|
-
encrypted = encrypted_for(
|
102
|
-
|
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 --
|
110
|
+
rsync = "rsync", "-avp", "--exclude", ".git/"
|
111
|
+
rsync_logging = Rake.application.options.silent && '--quiet' || '--verbose'
|
105
112
|
|
106
|
-
Dir.
|
107
|
-
|
108
|
-
|
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(
|
116
|
-
|
117
|
-
|
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 #{
|
123
|
-
task "converge:#{
|
130
|
+
desc "converge #{hostname}"
|
131
|
+
task "converge:#{hostname}" => ["bootstrap:#{hostname}", "upload:#{hostname}"] do
|
124
132
|
chef_logging = Rake.application.options.silent && '-l fatal' || ''
|
125
|
-
|
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 #{
|
129
|
-
task "run:#{
|
130
|
-
|
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,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
@@ -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,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
|
data/spec/spec_helper.rb
ADDED
@@ -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
|
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-
|
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/
|
81
|
-
- examples/test/
|
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 +0,0 @@
|
|
1
|
-
package 'openssh-server'
|
data/examples/test/nodes.yaml
DELETED