gusteau 1.0.3.dev → 1.0.4.dev
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -1
- data/bin/gusteau +13 -3
- data/lib/gusteau/chef.rb +4 -6
- data/lib/gusteau/config.rb +47 -23
- data/lib/gusteau/node.rb +4 -5
- data/lib/gusteau/server.rb +4 -3
- data/lib/gusteau/vagrant.rb +8 -6
- data/lib/gusteau/version.rb +1 -1
- data/spec/config/emile.yml +5 -0
- data/spec/lib/gusteau/chef_spec.rb +2 -0
- data/spec/lib/gusteau/config_spec.rb +39 -17
- data/spec/lib/gusteau/ssh_config_spec.rb +3 -2
- data/spec/lib/gusteau/vagrant_spec.rb +57 -54
- metadata +4 -2
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
## 1.0.4.dev / 2013-07-08
|
2
|
+
* Bugfix: `after` hook was not taking effect
|
3
|
+
* Add a quick `show nodename` subcommand for printing out individual node configuration
|
4
|
+
* Add an ability to configure `cookbooks_path` and `roles_path` from within `.gusteau.yml`
|
5
|
+
|
1
6
|
## 1.0.3.dev / 2013-07-07
|
2
|
-
* Implement
|
7
|
+
* Implement `before` and `after` hooks (global and environment-based)
|
3
8
|
|
4
9
|
## 1.0.2.dev / 2013-07-06
|
5
10
|
* Fix Ruby 1.8.7 and Rubinius compatibility
|
data/bin/gusteau
CHANGED
@@ -40,21 +40,31 @@ class Gusteau::CLI < Optitron::CLI
|
|
40
40
|
|
41
41
|
desc 'Lists all known nodes'
|
42
42
|
def list
|
43
|
-
puts
|
43
|
+
puts nodes_list
|
44
|
+
end
|
45
|
+
|
46
|
+
desc 'Show the configuration of a specific node'
|
47
|
+
def show(node_name)
|
48
|
+
puts node(node_name).config.to_yaml
|
44
49
|
end
|
45
50
|
|
46
51
|
private
|
47
52
|
|
48
53
|
def node(node_name)
|
49
54
|
unless node = nodes[node_name]
|
50
|
-
abort "Node '#{node_name}' is unknown.
|
55
|
+
abort "Node '#{node_name}' is unknown. #{nodes_list}"
|
51
56
|
else
|
52
57
|
node
|
53
58
|
end
|
54
59
|
end
|
55
60
|
|
56
61
|
def nodes
|
57
|
-
|
62
|
+
Gusteau::Config.read('.gusteau.yml')
|
63
|
+
Gusteau::Config.nodes
|
64
|
+
end
|
65
|
+
|
66
|
+
def nodes_list
|
67
|
+
"Known nodes are:\n - #{nodes.keys.join("\n - ")}"
|
58
68
|
end
|
59
69
|
|
60
70
|
end
|
data/lib/gusteau/chef.rb
CHANGED
@@ -26,14 +26,12 @@ module Gusteau
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def src_files(dna_path)
|
29
|
-
%W(
|
29
|
+
list = %W(
|
30
30
|
#{dna_path}
|
31
31
|
#{File.expand_path("../../../bootstrap", __FILE__)}
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
./data_bags
|
36
|
-
).select { |file| File.exists? file }
|
32
|
+
data_bags
|
33
|
+
) + Gusteau::Config.settings['cookbooks_path'] + [ Gusteau::Config.settings['roles_path']]
|
34
|
+
list.select { |file| File.exists? file }
|
37
35
|
end
|
38
36
|
end
|
39
37
|
end
|
data/lib/gusteau/config.rb
CHANGED
@@ -2,42 +2,66 @@ require 'gusteau/erb'
|
|
2
2
|
require 'hash_deep_merge'
|
3
3
|
|
4
4
|
module Gusteau
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
5
|
+
class Config
|
6
|
+
include Gusteau::ERB
|
7
|
+
|
8
|
+
def self.read(config_path)
|
9
|
+
@instance = Gusteau::Config.new(config_path)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.nodes
|
13
|
+
@instance.send(:nodes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.settings
|
17
|
+
@instance.send(:settings)
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(config_path)
|
21
|
+
@config = if File.exists?(config_path)
|
22
|
+
read_erb_yaml(config_path)
|
23
23
|
else
|
24
24
|
abort ".gusteau.yml not found"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
+
private
|
29
|
+
|
30
|
+
def nodes
|
31
|
+
env_config = @config['environments']
|
32
|
+
|
33
|
+
@nodes ||= env_config.inject({}) do |nodes, (env_name, env_hash)|
|
34
|
+
if env_hash['nodes']
|
35
|
+
env_hash['nodes'].each_pair do |node_name, node_hash|
|
36
|
+
node_name = "#{env_name}-#{node_name}"
|
37
|
+
nodes[node_name] = build_node(node_name, env_hash, node_hash)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
nodes
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def settings
|
45
|
+
{
|
46
|
+
'cookbooks_path' => @config['cookbooks_path'] || ['cookbooks', 'site-cookbooks'],
|
47
|
+
'roles_path' => @config['roles_path'] || 'roles'
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
28
51
|
#
|
29
52
|
# Node attributes get deep-merged with the environment ones
|
30
53
|
# Node run_list overrides the environment one
|
31
54
|
# Environment before hooks override global ones
|
32
55
|
#
|
33
|
-
def build_node(
|
34
|
-
|
56
|
+
def build_node(node_name, env_hash, node_hash)
|
57
|
+
node_config = {
|
35
58
|
'server' => node_hash,
|
36
59
|
'attributes' => (env_hash['attributes'] || {}).deep_merge(node_hash['attributes'] || {}),
|
37
|
-
'run_list' => node_hash['run_list']
|
38
|
-
'before' => env_hash['before']
|
60
|
+
'run_list' => node_hash['run_list'] || env_hash['run_list'],
|
61
|
+
'before' => env_hash['before'] || @config['before'],
|
62
|
+
'after' => env_hash['after'] || @config['after']
|
39
63
|
}
|
40
|
-
Gusteau::Node.new(node_name,
|
64
|
+
Gusteau::Node.new(node_name, node_config)
|
41
65
|
end
|
42
66
|
end
|
43
67
|
end
|
data/lib/gusteau/node.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'gusteau/server'
|
2
|
-
require 'gusteau/chef'
|
3
2
|
|
4
3
|
module Gusteau
|
5
4
|
class Node
|
@@ -7,11 +6,11 @@ module Gusteau
|
|
7
6
|
|
8
7
|
attr_reader :name, :config, :server
|
9
8
|
|
10
|
-
def initialize(
|
11
|
-
@name =
|
12
|
-
@config =
|
9
|
+
def initialize(node_name, node_config)
|
10
|
+
@name = node_name
|
11
|
+
@config = node_config
|
13
12
|
|
14
|
-
@server = Server.new(@config['server'])
|
13
|
+
@server = Server.new(@config['server'])
|
15
14
|
@dna_path = '/tmp/dna.json'
|
16
15
|
end
|
17
16
|
|
data/lib/gusteau/server.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
|
-
require 'gusteau/
|
1
|
+
require 'gusteau/chef'
|
2
2
|
require 'gusteau/ssh'
|
3
|
+
require 'gusteau/log'
|
3
4
|
|
4
5
|
module Gusteau
|
5
6
|
class Server
|
@@ -8,12 +9,12 @@ module Gusteau
|
|
8
9
|
|
9
10
|
attr_reader :host, :port, :user, :password, :chef
|
10
11
|
|
11
|
-
def initialize(config
|
12
|
+
def initialize(config)
|
12
13
|
@host = config['host']
|
13
14
|
@port = config['port'] || 22
|
14
15
|
@user = config['user'] || 'root'
|
15
16
|
@password = config['password']
|
16
|
-
@chef
|
17
|
+
@chef = Gusteau::Chef.new(self, config['platform'])
|
17
18
|
end
|
18
19
|
|
19
20
|
def upload(files_and_dirs, dest_dir, opts={})
|
data/lib/gusteau/vagrant.rb
CHANGED
@@ -9,7 +9,7 @@ module Gusteau
|
|
9
9
|
extend Gusteau::Log
|
10
10
|
|
11
11
|
def detect(config)
|
12
|
-
options
|
12
|
+
options = Hashie::Mash.new
|
13
13
|
options.defaults = Hashie::Mash.new
|
14
14
|
|
15
15
|
yield options if block_given?
|
@@ -17,7 +17,9 @@ module Gusteau
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def define_nodes(config, options, prefix = nil)
|
20
|
-
Gusteau::Config.
|
20
|
+
Gusteau::Config.read(options[:config_path] || ".gusteau.yml")
|
21
|
+
|
22
|
+
Gusteau::Config.nodes.each_pair do |name, node|
|
21
23
|
if node.config['server']['vagrant']
|
22
24
|
define_vm config, node, options
|
23
25
|
end
|
@@ -75,10 +77,10 @@ module Gusteau
|
|
75
77
|
def define_provisioner(instance, node)
|
76
78
|
instance.vm.provision 'chef_solo' do |chef|
|
77
79
|
chef.data_bags_path = 'data_bags'
|
78
|
-
chef.cookbooks_path = ['
|
79
|
-
chef.roles_path = '
|
80
|
-
chef.json
|
81
|
-
chef.run_list
|
80
|
+
chef.cookbooks_path = Gusteau::Config.settings['cookbooks_path']
|
81
|
+
chef.roles_path = Gusteau::Config.settings['roles_path']
|
82
|
+
chef.json = node.config['attributes'] || {}
|
83
|
+
chef.run_list = node.config['run_list'] || []
|
82
84
|
end
|
83
85
|
end
|
84
86
|
end
|
data/lib/gusteau/version.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
require './spec/spec_helper.rb'
|
2
2
|
|
3
3
|
describe Gusteau::Chef do
|
4
|
+
before { Gusteau::Config.read('./spec/config/emile.yml') }
|
5
|
+
|
4
6
|
let(:platform) { 'centos' }
|
5
7
|
let(:server) { Gusteau::Server.new('host' => 'example.com', 'platform' => platform) }
|
6
8
|
let(:chef) { Gusteau::Chef.new(server, platform) }
|
@@ -2,7 +2,7 @@ require './spec/spec_helper'
|
|
2
2
|
|
3
3
|
describe Gusteau::Config do
|
4
4
|
context "config not found" do
|
5
|
-
subject { Gusteau::Config.
|
5
|
+
subject { Gusteau::Config.read("/tmp/nonexistent/nonsence111") }
|
6
6
|
|
7
7
|
it "should exit with an error" do
|
8
8
|
proc { subject }.must_raise SystemExit
|
@@ -10,27 +10,49 @@ describe Gusteau::Config do
|
|
10
10
|
end
|
11
11
|
|
12
12
|
context "config is found" do
|
13
|
-
|
13
|
+
before { Gusteau::Config.read("./spec/config/remi.yml") }
|
14
14
|
|
15
|
-
|
16
|
-
nodes
|
17
|
-
end
|
15
|
+
describe "#nodes" do
|
16
|
+
let(:nodes) { Gusteau::Config.nodes }
|
18
17
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
18
|
+
it "should name nodes as per environment-node" do
|
19
|
+
nodes.keys.sort.must_equal ["production-db", "production-www", "staging-vm"]
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should override run_list if defined for a node" do
|
23
|
+
nodes['production-db'].config['run_list'].must_equal(["recipe[git]", "recipe[postgresql::server]"])
|
24
|
+
nodes['production-www'].config['run_list'].must_equal(["recipe[varnish]", "recipe[nginx]"])
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should deeply merge the attributes" do
|
28
|
+
nodes['production-db'].config['attributes'].must_equal({
|
29
|
+
'users' => ['alex', 'simon'],
|
30
|
+
'mysql' => {'server_port' => 3307, 'server_root_password' => 'prodsecret' }
|
31
|
+
})
|
32
|
+
end
|
23
33
|
|
24
|
-
|
25
|
-
|
26
|
-
'
|
27
|
-
|
28
|
-
})
|
34
|
+
it "should override the global before hook with an environment one" do
|
35
|
+
nodes['production-www'].config['before'].must_equal(['bundle exec berks install'])
|
36
|
+
nodes['staging-vm'].config['before'].must_equal(['echo "Hello World!"'])
|
37
|
+
end
|
29
38
|
end
|
30
39
|
|
31
|
-
|
32
|
-
|
33
|
-
|
40
|
+
describe "#settings" do
|
41
|
+
let(:settings) { Gusteau::Config.settings }
|
42
|
+
|
43
|
+
it "should have defaults for cookbooks_path, roles_path" do
|
44
|
+
settings['cookbooks_path'].must_equal ['cookbooks', 'site-cookbooks']
|
45
|
+
settings['roles_path'].must_equal 'roles'
|
46
|
+
end
|
47
|
+
|
48
|
+
context "settings defined in the config yml" do
|
49
|
+
before { Gusteau::Config.read("./spec/config/emile.yml") }
|
50
|
+
|
51
|
+
it "should have defaults for cookbooks_path, roles_path" do
|
52
|
+
settings['cookbooks_path'].must_equal ['private-cookbooks', '/home/user/.cookbooks']
|
53
|
+
settings['roles_path'].must_equal 'basic-roles'
|
54
|
+
end
|
55
|
+
end
|
34
56
|
end
|
35
57
|
end
|
36
58
|
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
require './spec/spec_helper.rb'
|
2
2
|
|
3
3
|
describe Gusteau::SSHConfig do
|
4
|
-
|
5
|
-
|
4
|
+
before { Gusteau::Config.read("./spec/config/gusteau.yml") }
|
5
|
+
|
6
|
+
subject { Gusteau::SSHConfig.new(Gusteau::Config.nodes) }
|
6
7
|
|
7
8
|
let(:config) do
|
8
9
|
<<-eos
|
@@ -49,73 +49,76 @@ describe Gusteau::Vagrant do
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
describe "
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
{
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
52
|
+
describe "internal methods" do
|
53
|
+
before { Gusteau::Config.read("./spec/config/gusteau.yml") }
|
54
|
+
let(:node) { Gusteau::Config.nodes['development-playground'] }
|
55
|
+
|
56
|
+
describe "#vm_config" do
|
57
|
+
subject { Gusteau::Vagrant.vm_config(node, options) }
|
58
|
+
|
59
|
+
let(:defaults) do
|
60
|
+
{
|
61
|
+
:box_url => 'https://opscode.s3.amazonaws.com/centos-6.4.box',
|
62
|
+
:cpus => 64,
|
63
|
+
:memory => 4096,
|
64
|
+
}
|
65
|
+
end
|
66
|
+
let(:prefix) { 'hyper' }
|
67
|
+
let(:options) { { :defaults => defaults, :prefix => prefix } }
|
68
|
+
|
69
|
+
let(:expected_label) { 'hyper-development-playground' }
|
70
|
+
let(:expected_config) do
|
71
|
+
{
|
72
|
+
:name => 'development-playground',
|
73
|
+
:label => expected_label,
|
74
|
+
:box_url => 'https://opscode.s3.amazonaws.com/centos-6.4.box',
|
75
|
+
:ip => '192.168.100.21',
|
76
|
+
:cpus => 2,
|
77
|
+
:memory => 4096
|
78
|
+
}
|
79
|
+
end
|
77
80
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
+
it "should merge in defaults" do
|
82
|
+
subject.must_equal(expected_config)
|
83
|
+
end
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
+
context "prefix not specified" do
|
86
|
+
let(:prefix) { nil }
|
87
|
+
let(:expected_label) { 'development-playground' }
|
85
88
|
|
86
|
-
|
87
|
-
|
89
|
+
it "should omit the prefix" do
|
90
|
+
subject.must_equal(expected_config)
|
91
|
+
end
|
88
92
|
end
|
89
|
-
end
|
90
93
|
|
91
|
-
|
92
|
-
|
94
|
+
context "box_url not specified" do
|
95
|
+
let(:defaults) { {} }
|
93
96
|
|
94
|
-
|
95
|
-
|
97
|
+
it "should raise an exception" do
|
98
|
+
proc { subject }.must_raise RuntimeError
|
99
|
+
end
|
96
100
|
end
|
97
101
|
end
|
98
|
-
end
|
99
102
|
|
100
|
-
|
101
|
-
|
102
|
-
let(:chef) { stub_everything('chef') }
|
103
|
+
describe "#define_provisioner" do
|
104
|
+
let(:chef) { stub_everything('chef') }
|
103
105
|
|
104
|
-
|
105
|
-
|
106
|
-
|
106
|
+
before do
|
107
|
+
def subvm.provision(provider); yield chef; end
|
108
|
+
subvm.expects(:chef).returns(chef)
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
+
instance.expects(:vm).at_least_once.returns(subvm)
|
111
|
+
end
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
113
|
+
it "should set the correct Chef JSON" do
|
114
|
+
chef.expects('json='.to_sym).with({"mysql"=>{"server_root_password"=>"guesswhat"}})
|
115
|
+
Gusteau::Vagrant.define_provisioner(instance, node)
|
116
|
+
end
|
115
117
|
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
it "should set the correct Chef run_list" do
|
119
|
+
chef.expects('run_list='.to_sym).with(["recipe[zsh]", "recipe[mysql::server]"])
|
120
|
+
Gusteau::Vagrant.define_provisioner(instance, node)
|
121
|
+
end
|
119
122
|
end
|
120
123
|
end
|
121
124
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gusteau
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.4.dev
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -240,6 +240,7 @@ files:
|
|
240
240
|
- lib/gusteau/ssh_config.rb
|
241
241
|
- lib/gusteau/vagrant.rb
|
242
242
|
- lib/gusteau/version.rb
|
243
|
+
- spec/config/emile.yml
|
243
244
|
- spec/config/gusteau.yml
|
244
245
|
- spec/config/remi.yml
|
245
246
|
- spec/lib/gusteau/bureau_spec.rb
|
@@ -285,7 +286,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
285
286
|
version: '0'
|
286
287
|
segments:
|
287
288
|
- 0
|
288
|
-
hash:
|
289
|
+
hash: 201267403645685756
|
289
290
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
290
291
|
none: false
|
291
292
|
requirements:
|
@@ -299,6 +300,7 @@ signing_key:
|
|
299
300
|
specification_version: 3
|
300
301
|
summary: Making servers provisioning enjoyable since 2013.
|
301
302
|
test_files:
|
303
|
+
- spec/config/emile.yml
|
302
304
|
- spec/config/gusteau.yml
|
303
305
|
- spec/config/remi.yml
|
304
306
|
- spec/lib/gusteau/bureau_spec.rb
|