gusteau 0.4.8 → 1.0.0.dev
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/.travis.yml +2 -0
- data/CHANGELOG.md +4 -0
- data/README.md +31 -30
- data/bin/gusteau +14 -13
- data/gusteau.gemspec +4 -1
- data/lib/gusteau.rb +1 -0
- data/lib/gusteau/bureau.rb +32 -20
- data/lib/gusteau/chef.rb +2 -2
- data/lib/gusteau/config.rb +39 -0
- data/lib/gusteau/node.rb +11 -43
- data/lib/gusteau/ssh_config.rb +8 -7
- data/lib/gusteau/vagrant.rb +7 -32
- data/lib/gusteau/version.rb +1 -1
- data/spec/config/gusteau.yml +60 -0
- data/spec/config/remi.yml +29 -0
- data/spec/lib/gusteau/bureau_spec.rb +52 -0
- data/spec/lib/gusteau/compressed_tar_stream_spec.rb +31 -0
- data/spec/lib/gusteau/config_spec.rb +31 -0
- data/spec/lib/gusteau/log_spec.rb +34 -0
- data/spec/lib/gusteau/node_spec.rb +40 -85
- data/spec/lib/gusteau/server_spec.rb +12 -0
- data/spec/lib/gusteau/ssh_config_spec.rb +16 -10
- data/spec/lib/gusteau/ssh_spec.rb +110 -0
- data/spec/lib/gusteau/vagrant_spec.rb +46 -17
- data/spec/spec_helper.rb +11 -0
- data/template/.gusteau.yml.erb +21 -0
- data/template/.kitchen.yml +20 -0
- data/template/Berksfile +3 -3
- data/template/Gemfile +6 -1
- data/template/README.md.erb +70 -0
- data/template/Vagrantfile +12 -4
- data/template/init.sh +27 -0
- data/template/site-cookbooks/cowsay/metadata.rb +10 -0
- data/template/site-cookbooks/cowsay/recipes/default.rb +1 -3
- data/template/site-cookbooks/platform/metadata.rb +14 -0
- data/template/site-cookbooks/platform/recipes/default.rb +3 -0
- data/template/test/integration/data_bags/users/remi.json +7 -0
- data/template/test/integration/default/serverspec/localhost/cowsay_spec.rb +5 -0
- data/template/test/integration/default/serverspec/localhost/platform_spec.rb +25 -0
- data/template/test/integration/default/serverspec/spec_helper.rb +9 -0
- metadata +81 -17
- data/bootstrap/centos.sh +0 -17
- data/bootstrap/redhat.sh +0 -17
- data/bootstrap/ubuntu.sh +0 -17
- data/spec/nodes/development.yml +0 -17
- data/spec/nodes/production.yml +0 -18
- data/spec/nodes/staging.yml +0 -12
- data/template/nodes/example.yml.erb +0 -19
- data/template/roles/platform.rb +0 -8
data/lib/gusteau/vagrant.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'hashie'
|
2
|
-
require 'gusteau
|
2
|
+
require 'gusteau'
|
3
3
|
|
4
4
|
class Hash; include Hashie::Extensions::SymbolizeKeys; end
|
5
5
|
|
@@ -17,14 +17,8 @@ module Gusteau
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def define_nodes(config, options, prefix = nil)
|
20
|
-
|
21
|
-
|
22
|
-
info "The 'prefix' param is deprecated. Please refer to the new Gusteau::Vagrant syntax."
|
23
|
-
end
|
24
|
-
|
25
|
-
Dir.glob("#{options[:dir] || './nodes'}/**/*.yml").sort.each do |path|
|
26
|
-
node = ::Gusteau::Node.new(path)
|
27
|
-
if node.config['vagrant']
|
20
|
+
Gusteau::Config.nodes(options[:config_path] || ".gusteau.yml").each_pair do |name, node|
|
21
|
+
if node.config['server']['vagrant']
|
28
22
|
define_vm config, node, options
|
29
23
|
end
|
30
24
|
end
|
@@ -32,7 +26,7 @@ module Gusteau
|
|
32
26
|
|
33
27
|
def vm_config(node, options)
|
34
28
|
defaults = options[:defaults] || {}
|
35
|
-
config = node.config['vagrant']
|
29
|
+
config = node.config['server']['vagrant']
|
36
30
|
label = options[:prefix] ? "#{options[:prefix]}-#{node.name}" : node.name
|
37
31
|
|
38
32
|
config = {} if config == true
|
@@ -82,30 +76,11 @@ module Gusteau
|
|
82
76
|
instance.vm.provision 'chef_solo' do |chef|
|
83
77
|
chef.data_bags_path = 'data_bags'
|
84
78
|
chef.cookbooks_path = ['cookbooks', 'site-cookbooks']
|
85
|
-
chef.
|
86
|
-
|
87
|
-
|
88
|
-
(node.config['roles'] || []).each { |r| chef.add_role(r) }
|
79
|
+
chef.roles_path = 'roles'
|
80
|
+
chef.json = node.config['attributes'] || {}
|
81
|
+
chef.run_list = node.config['run_list'] || []
|
89
82
|
end
|
90
83
|
end
|
91
|
-
|
92
|
-
def ssh(nodename)
|
93
|
-
vagrant_cmd("ssh #{nodename}")
|
94
|
-
end
|
95
|
-
|
96
|
-
def provision(nodename)
|
97
|
-
vagrant_cmd("provision #{nodename}")
|
98
|
-
end
|
99
|
-
|
100
|
-
def vagrant_cmd(cmd)
|
101
|
-
info "Running 'vagrant #{cmd}'"
|
102
|
-
Kernel.system("vagrant #{cmd}")
|
103
|
-
Kernel.exit $?.exitstatus
|
104
|
-
end
|
105
|
-
|
106
|
-
def run(nodename)
|
107
|
-
Kernel.abort "'run' is not supported for vagrant-only nodes. Please configure 'server' for #{nodename} node"
|
108
|
-
end
|
109
84
|
end
|
110
85
|
end
|
111
86
|
|
data/lib/gusteau/version.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
default_node: &default_node
|
2
|
+
platform: centos
|
3
|
+
user: billy
|
4
|
+
|
5
|
+
environments:
|
6
|
+
development:
|
7
|
+
attributes:
|
8
|
+
mysql:
|
9
|
+
server_root_password: guesswhat
|
10
|
+
run_list:
|
11
|
+
- recipe[zsh]
|
12
|
+
- recipe[mysql::server]
|
13
|
+
nodes:
|
14
|
+
playground:
|
15
|
+
host: 192.168.100.21
|
16
|
+
vagrant:
|
17
|
+
IP: 192.168.100.21
|
18
|
+
cpus: 2
|
19
|
+
|
20
|
+
staging:
|
21
|
+
attributes:
|
22
|
+
users:
|
23
|
+
- alex
|
24
|
+
- mitya
|
25
|
+
run_list:
|
26
|
+
- recipe[basic-system]
|
27
|
+
- recipe[java]
|
28
|
+
nodes:
|
29
|
+
www:
|
30
|
+
<<: *default_node
|
31
|
+
host: staging.myapp.com
|
32
|
+
user: root
|
33
|
+
attributes:
|
34
|
+
java:
|
35
|
+
jdk_version: 7
|
36
|
+
run_list: recipe[jenkins]
|
37
|
+
|
38
|
+
production:
|
39
|
+
attributes:
|
40
|
+
users:
|
41
|
+
- alex
|
42
|
+
- simon
|
43
|
+
run_list:
|
44
|
+
- recipe[git]
|
45
|
+
- recipe[nginx]
|
46
|
+
nodes:
|
47
|
+
db:
|
48
|
+
<<: *default_node
|
49
|
+
host: db.myapp.com
|
50
|
+
attributes:
|
51
|
+
mysql:
|
52
|
+
server_root_password: prodsecret
|
53
|
+
run_list: recipe[mysql::client]
|
54
|
+
www:
|
55
|
+
<<: *default_node
|
56
|
+
host: www.myapp.com
|
57
|
+
attributes:
|
58
|
+
nginx:
|
59
|
+
workers: 4
|
60
|
+
run_list: recipe[varnish], recipe[nginx]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
default_node: &default_node
|
2
|
+
platform: centos
|
3
|
+
user: billy
|
4
|
+
|
5
|
+
environments:
|
6
|
+
production:
|
7
|
+
attributes:
|
8
|
+
users:
|
9
|
+
- alex
|
10
|
+
- simon
|
11
|
+
mysql:
|
12
|
+
server_port: 3307
|
13
|
+
run_list:
|
14
|
+
- recipe[git]
|
15
|
+
- recipe[postgresql::server]
|
16
|
+
nodes:
|
17
|
+
db:
|
18
|
+
<<: *default_node
|
19
|
+
host: db.myapp.com
|
20
|
+
attributes:
|
21
|
+
mysql:
|
22
|
+
server_root_password: prodsecret
|
23
|
+
www:
|
24
|
+
<<: *default_node
|
25
|
+
host: www.myapp.com
|
26
|
+
attributes:
|
27
|
+
nginx:
|
28
|
+
workers: 4
|
29
|
+
run_list: [ 'recipe[varnish]', 'recipe[nginx]' ]
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'etc'
|
4
|
+
|
5
|
+
describe Gusteau::Bureau do
|
6
|
+
|
7
|
+
let(:login) { Etc.getlogin }
|
8
|
+
let(:bureau_path) { "/tmp/gusteau-test/bureau" }
|
9
|
+
|
10
|
+
before { FileUtils.mkdir_p("/tmp/gusteau-test") }
|
11
|
+
after { FileUtils.rm_rf(bureau_path) }
|
12
|
+
|
13
|
+
subject { Gusteau::Bureau.new(bureau_path) }
|
14
|
+
|
15
|
+
it "should obtain the correct user login" do
|
16
|
+
subject.instance_variable_get("@login").must_equal login
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#generate!" do
|
20
|
+
before { subject.generate!(false) }
|
21
|
+
it "should create a basic structure" do
|
22
|
+
assert Dir.exists?(bureau_path)
|
23
|
+
|
24
|
+
%w{ Berksfile Vagrantfile .kitchen.yml }.each do |f|
|
25
|
+
assert File.exists?("#{bureau_path}/#{f}")
|
26
|
+
end
|
27
|
+
|
28
|
+
%w{ data_bags site-cookbooks test }.each do |d|
|
29
|
+
assert Dir.exists?("#{bureau_path}/#{d}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should process the README template" do
|
34
|
+
readme_path = "#{bureau_path}/README.md"
|
35
|
+
assert File.exists?(readme_path)
|
36
|
+
assert File.read(readme_path).include?("Welcome to your example Chef-Repo, #{login}")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should process the .gusteau.yml template" do
|
40
|
+
config_path = "#{bureau_path}/.gusteau.yml"
|
41
|
+
assert File.exists?(config_path)
|
42
|
+
assert File.read(config_path).include?("Good job, #{login}")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should process the user data_bag template" do
|
46
|
+
config_path = "#{bureau_path}/data_bags/users/#{login}.json"
|
47
|
+
assert File.exists?(config_path)
|
48
|
+
assert File.read(config_path).include?(login)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
describe Gusteau::CompressedTarStream do
|
4
|
+
|
5
|
+
let(:compressor_class) do
|
6
|
+
class Example
|
7
|
+
include Gusteau::Log
|
8
|
+
include Gusteau::CompressedTarStream
|
9
|
+
attr_accessor :host, :port, :user, :password
|
10
|
+
end
|
11
|
+
Example
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:compressor) { compressor_class.new }
|
15
|
+
|
16
|
+
describe "#compressed_tar_stream" do
|
17
|
+
let(:tp) { "/tmp/gusteau-spec-11" }
|
18
|
+
|
19
|
+
before do
|
20
|
+
FileUtils.mkdir_p(tp)
|
21
|
+
%w{ a b c }.each { |l| FileUtils.touch("#{tp}/#{l}.rb") }
|
22
|
+
end
|
23
|
+
|
24
|
+
after { FileUtils.rm_rf(tp) }
|
25
|
+
|
26
|
+
it "should compress the files" do
|
27
|
+
res = compressor.send(:compressed_tar_stream, ["#{tp}/a.rb", "#{tp}/b.rb", "#{tp}/c.rb"])
|
28
|
+
assert res.length > 0
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
describe Gusteau::Config do
|
4
|
+
context "config not found" do
|
5
|
+
subject { Gusteau::Config.nodes("/tmp/nonexistent/nonsence111") }
|
6
|
+
|
7
|
+
it "should exit with an error" do
|
8
|
+
proc { subject }.must_raise SystemExit
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "config is found" do
|
13
|
+
let(:nodes) { Gusteau::Config.nodes("./spec/config/remi.yml") }
|
14
|
+
|
15
|
+
it "should name nodes as per environment-node" do
|
16
|
+
nodes.keys.must_equal ["production-db", "production-www"]
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should override run_list if defined for a node" do
|
20
|
+
nodes['production-db'].config['run_list'].must_equal(["recipe[git]", "recipe[postgresql::server]"])
|
21
|
+
nodes['production-www'].config['run_list'].must_equal(["recipe[varnish]", "recipe[nginx]"])
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should deeply merge the attributes" do
|
25
|
+
nodes['production-db'].config['attributes'].must_equal({
|
26
|
+
'users' => ['alex', 'simon'],
|
27
|
+
'mysql' => {'server_port' => 3307, 'server_root_password' => 'prodsecret' }
|
28
|
+
})
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
describe Gusteau::Log do
|
4
|
+
|
5
|
+
let(:fixed_time) { Time.new(2013,7,25, 14,40,23, "+00:00") }
|
6
|
+
|
7
|
+
let(:logger_class) { class Example; include Gusteau::Log; end }
|
8
|
+
let(:logger) { logger_class.new }
|
9
|
+
|
10
|
+
before do
|
11
|
+
Time.expects(:now).at_least_once.returns fixed_time
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#log" do
|
15
|
+
it "should log start and end when block is given" do
|
16
|
+
Inform.expects(:info).with('%{prompt}test', {:prompt => '[2013-07-25T14:40:23] GUSTEAU: '})
|
17
|
+
Inform.expects(:info).with('%{prompt}DONE (in 0.00s)', {:prompt => '[2013-07-25T14:40:23] GUSTEAU: '})
|
18
|
+
logger.log('test') { true }
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should only log start when block isn't given" do
|
22
|
+
Inform.expects(:info).with('%{prompt}hehe', {:prompt => '[2013-07-25T14:40:23] GUSTEAU: '})
|
23
|
+
logger.log('hehe')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#log_error" do
|
28
|
+
it "should output the prompt followed by a message" do
|
29
|
+
Inform.expects(:error).with('%{prompt}error!', {:prompt => '[2013-07-25T14:40:23] GUSTEAU: '})
|
30
|
+
logger.log_error('error!')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -1,102 +1,57 @@
|
|
1
1
|
require './spec/spec_helper'
|
2
2
|
|
3
3
|
describe Gusteau::Node do
|
4
|
-
it "should raise if node yaml doesn't exist" do
|
5
|
-
proc { Gusteau::Node.new('spec/nodes/void.yml') }.must_raise RuntimeError
|
6
|
-
end
|
7
|
-
|
8
|
-
describe "#dna" do
|
9
|
-
let(:node) { Gusteau::Node.new('spec/nodes/production.yml') }
|
10
|
-
|
11
|
-
let(:dna) { node.send(:dna, include_all, recipes) }
|
12
|
-
let(:include_all) { true }
|
13
|
-
let(:recipes) { [] }
|
14
4
|
|
15
|
-
|
16
|
-
let(:
|
17
|
-
|
18
|
-
it "should create a new dna file" do
|
19
|
-
File.exists?(dna[:path]).must_equal true
|
5
|
+
describe "#initialize" do
|
6
|
+
let(:node) do
|
7
|
+
Gusteau::Node.new 'test', { 'server' => { 'host' => 'example.com' } }
|
20
8
|
end
|
21
9
|
|
22
|
-
it "should
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
context "recipes not specified" do
|
27
|
-
it "should contain a full run_list" do
|
28
|
-
json['run_list'].must_equal ["role[redhat]", "recipe[rvm]", "recipe[ntp]", "recipe[rails::apps]"]
|
29
|
-
end
|
10
|
+
it "should create a server attribute" do
|
11
|
+
node.server.host.must_equal 'example.com'
|
30
12
|
end
|
13
|
+
end
|
31
14
|
|
32
|
-
|
33
|
-
|
34
|
-
|
15
|
+
let(:node) do
|
16
|
+
config = {
|
17
|
+
'run_list' => [ "recipe[zsh]", "recipe[git]" ],
|
18
|
+
'server' => { 'host' => 'example.com' }
|
19
|
+
}
|
20
|
+
Gusteau::Node.new 'test', config
|
21
|
+
end
|
35
22
|
|
36
|
-
|
37
|
-
|
38
|
-
|
23
|
+
describe "#converge" do
|
24
|
+
it "should run chef with the full run_list" do
|
25
|
+
dna = {
|
26
|
+
:path => '/tmp/dna.json',
|
27
|
+
:hash => {
|
28
|
+
:instance_role => 'test',
|
29
|
+
:run_list => ['recipe[zsh]', 'recipe[git]']
|
30
|
+
}
|
31
|
+
}
|
32
|
+
node.server.chef.expects(:run).with({}, dna)
|
33
|
+
node.converge
|
39
34
|
end
|
40
35
|
end
|
41
36
|
|
42
|
-
describe "#
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
context "vagrant is defined, server is not" do
|
54
|
-
before do
|
55
|
-
node.stubs(:server).returns(nil)
|
56
|
-
end
|
57
|
-
|
58
|
-
it "should call 'vagrant ssh'" do
|
59
|
-
Kernel.expects(:system).with("vagrant ssh development")
|
60
|
-
Kernel.expects(:exit)
|
61
|
-
node.ssh
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
context "neither server nor vagrant are defined" do
|
66
|
-
before do
|
67
|
-
node.stubs(:server).returns(nil)
|
68
|
-
node.config['vagrant'] = nil
|
69
|
-
end
|
70
|
-
|
71
|
-
it "should exit with an error" do
|
72
|
-
Kernel.expects(:abort)
|
73
|
-
node.ssh
|
74
|
-
end
|
75
|
-
end
|
37
|
+
describe "#apply" do
|
38
|
+
it "should run chef with the specific run_list" do
|
39
|
+
dna = {
|
40
|
+
:path => '/tmp/dna.json',
|
41
|
+
:hash => {
|
42
|
+
:instance_role => 'test',
|
43
|
+
:run_list => ['recipe[nagios]', 'role[base]']
|
44
|
+
}
|
45
|
+
}
|
46
|
+
node.server.chef.expects(:run).with({}, dna)
|
47
|
+
node.apply({}, [ "recipe[nagios]", "role[base]" ])
|
76
48
|
end
|
49
|
+
end
|
77
50
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
node.server.expects(:chef).returns(chef)
|
83
|
-
chef.expects(:run)
|
84
|
-
|
85
|
-
node.run('test')
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
context "vagrant is defined, server is not" do
|
90
|
-
before do
|
91
|
-
node.stubs(:server).returns(nil)
|
92
|
-
end
|
93
|
-
|
94
|
-
it "should exit with an error" do
|
95
|
-
Kernel.expects(:abort).with("'run' is not supported for vagrant-only nodes. Please configure 'server' for development node")
|
96
|
-
node.run('test')
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
51
|
+
describe "#ssh" do
|
52
|
+
it "should call server.ssh" do
|
53
|
+
node.server.expects(:ssh)
|
54
|
+
node.ssh
|
100
55
|
end
|
101
56
|
end
|
102
57
|
end
|