gusteau 0.4.8 → 1.0.0.dev
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -54,6 +54,18 @@ describe Gusteau::Server do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
describe "#upload" do
|
58
|
+
let(:pr) { "/tmp/gusteau-test" }
|
59
|
+
|
60
|
+
before { ["#{pr}/cookbooks", "#{pr}/.git"].each { |d| FileUtils.mkdir_p(d) } }
|
61
|
+
after { FileUtils.rm_rf(pr) }
|
62
|
+
|
63
|
+
it "skips the excluded files" do
|
64
|
+
server.expects(:send_files).with(["#{pr}/cookbooks"], "/etc/chef")
|
65
|
+
server.upload(["#{pr}/cookbooks", "#{pr}/.git"], "/etc/chef", { :exclude => "#{pr}/.git" })
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
57
69
|
describe "#prepared_cmd" do
|
58
70
|
subject { server.send(:prepared_cmd, 'cd /etc/chef && touch test') }
|
59
71
|
|
@@ -1,26 +1,32 @@
|
|
1
1
|
require './spec/spec_helper.rb'
|
2
2
|
|
3
3
|
describe Gusteau::SSHConfig do
|
4
|
-
|
4
|
+
let(:nodes) { Gusteau::Config.nodes("./spec/config/gusteau.yml") }
|
5
|
+
subject { Gusteau::SSHConfig.new(nodes) }
|
5
6
|
|
6
7
|
let(:config) do
|
7
8
|
<<-eos
|
8
9
|
# BEGIN GUSTEAU NODES
|
9
10
|
|
10
|
-
Host development
|
11
|
+
Host development-playground
|
11
12
|
HostName 192.168.100.21
|
12
|
-
Port
|
13
|
-
User
|
13
|
+
Port 22
|
14
|
+
User root
|
14
15
|
|
15
|
-
Host
|
16
|
-
HostName
|
16
|
+
Host staging-www
|
17
|
+
HostName staging.myapp.com
|
17
18
|
Port 22
|
18
19
|
User root
|
19
20
|
|
20
|
-
Host
|
21
|
-
HostName
|
22
|
-
Port
|
23
|
-
User
|
21
|
+
Host production-db
|
22
|
+
HostName db.myapp.com
|
23
|
+
Port 22
|
24
|
+
User billy
|
25
|
+
|
26
|
+
Host production-www
|
27
|
+
HostName www.myapp.com
|
28
|
+
Port 22
|
29
|
+
User billy
|
24
30
|
|
25
31
|
# END GUSTEAU NODES
|
26
32
|
eos
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require './spec/spec_helper'
|
2
|
+
|
3
|
+
describe Gusteau::SSH do
|
4
|
+
let(:connector_class) do
|
5
|
+
class Example
|
6
|
+
include Gusteau::SSH
|
7
|
+
attr_accessor :host, :port, :user, :password
|
8
|
+
end
|
9
|
+
Example
|
10
|
+
end
|
11
|
+
|
12
|
+
let(:connector) { connector_class.new }
|
13
|
+
|
14
|
+
describe "#conn" do
|
15
|
+
before do
|
16
|
+
connector.host = 'microsoft.com'
|
17
|
+
connector.port = 2202
|
18
|
+
connector.user = 'ray'
|
19
|
+
end
|
20
|
+
|
21
|
+
context "password is present" do
|
22
|
+
it "should use SSH port and password when present" do
|
23
|
+
connector.password = 'qwerty123'
|
24
|
+
|
25
|
+
Net::SSH.expects(:start).with('microsoft.com', 'ray', {:port => 2202, :password => 'qwerty123'})
|
26
|
+
connector.conn
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "password is not present" do
|
31
|
+
it "should not use password" do
|
32
|
+
Net::SSH.expects(:start).with('microsoft.com', 'ray', {:port => 2202})
|
33
|
+
connector.conn
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "send methods" do
|
39
|
+
let(:conn) { stub_everything('conn') }
|
40
|
+
let(:channel) { stub_everything('channel') }
|
41
|
+
|
42
|
+
before do
|
43
|
+
connector.expects(:conn).at_least_once.returns(conn)
|
44
|
+
|
45
|
+
def conn.open_channel
|
46
|
+
yield channel
|
47
|
+
channel # is this the correct way to test it?
|
48
|
+
end
|
49
|
+
conn.expects(:channel).at_least_once.returns(channel)
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#send_command" do
|
53
|
+
context "user is root" do
|
54
|
+
before { connector.user = 'root' }
|
55
|
+
|
56
|
+
it "should execute the command as is" do
|
57
|
+
channel.expects(:exec).with('cowsay')
|
58
|
+
connector.send_command 'cowsay'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "user is not root" do
|
63
|
+
before { connector.user = 'vaskas' }
|
64
|
+
|
65
|
+
it "should execute the command with sudo" do
|
66
|
+
channel.expects(:exec).with("sudo -- sh -c 'cowsay'")
|
67
|
+
connector.send_command 'cowsay'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "success status" do
|
72
|
+
let(:success) { true }
|
73
|
+
|
74
|
+
before do
|
75
|
+
def channel.exec(cmd); yield true, success; end
|
76
|
+
channel.expects(:success).returns(success)
|
77
|
+
end
|
78
|
+
|
79
|
+
context "command succeeded" do
|
80
|
+
it "should start receiving data" do
|
81
|
+
channel.expects(:on_data)
|
82
|
+
connector.send_command 'sl'
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "command failed" do
|
87
|
+
let(:success) { false }
|
88
|
+
|
89
|
+
it "should raise an exception" do
|
90
|
+
proc { connector.send_command 'sl' }.must_raise RuntimeError
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#send_files" do
|
97
|
+
before do
|
98
|
+
connector.user = 'root'
|
99
|
+
connector.expects(:compressed_tar_stream).returns(mock())
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should execute the extraction command and send the data" do
|
103
|
+
channel.expects(:exec).with("tar zxf - -C /etc/chef")
|
104
|
+
channel.expects(:send_data)
|
105
|
+
|
106
|
+
connector.send_files(%w{ a b }, '/etc/chef')
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -1,31 +1,39 @@
|
|
1
1
|
require './spec/spec_helper.rb'
|
2
2
|
|
3
3
|
describe Gusteau::Vagrant do
|
4
|
+
let(:instance) { mock() }
|
5
|
+
let(:subvm) { stub_everything('subvm') }
|
4
6
|
|
5
7
|
describe "#detect" do
|
6
|
-
let(:config)
|
7
|
-
let(:vm)
|
8
|
-
let(:
|
9
|
-
let(:subvm) { stub_everything('subvm') }
|
8
|
+
let(:config) { mock() }
|
9
|
+
let(:vm) { mock() }
|
10
|
+
let(:virtualbox) { stub_everything('virtualbox') }
|
10
11
|
|
11
12
|
before do
|
12
|
-
def vm.define(name)
|
13
|
-
|
14
|
-
|
13
|
+
def vm.define(name); yield instance; end
|
14
|
+
vm.expects(:instance).at_least_once.returns(instance)
|
15
|
+
|
16
|
+
def subvm.provider(type); yield virtualbox; end
|
17
|
+
subvm.expects(:virtualbox).at_least_once.returns(virtualbox)
|
15
18
|
|
16
19
|
config.expects(:vm).at_least_once.returns(vm)
|
17
|
-
vm.expects(:instance).at_least_once.returns(instance)
|
18
20
|
instance.expects(:vm).at_least_once.returns(subvm)
|
19
21
|
end
|
20
22
|
|
21
23
|
it "should define vm instances with correct settings" do
|
22
|
-
subvm.expects('box='.to_sym).with('development')
|
24
|
+
subvm.expects('box='.to_sym).with('development-playground')
|
23
25
|
subvm.expects('box_url='.to_sym).with("http://a.com/b.box")
|
24
26
|
subvm.expects(:network).with(:private_network, { :ip => '192.168.100.21' })
|
25
27
|
subvm.expects(:provision).never
|
26
28
|
|
29
|
+
virtualbox.expects(:customize).with ['modifyvm', :id,
|
30
|
+
'--memory', 1024,
|
31
|
+
'--name', 'development-playground',
|
32
|
+
'--cpus', 2,
|
33
|
+
'--natdnsproxy1', 'on']
|
34
|
+
|
27
35
|
Gusteau::Vagrant.detect(config) do |setup|
|
28
|
-
setup.
|
36
|
+
setup.config_path = './spec/config/gusteau.yml'
|
29
37
|
setup.defaults.box_url = "http://a.com/b.box"
|
30
38
|
end
|
31
39
|
end
|
@@ -34,7 +42,7 @@ describe Gusteau::Vagrant do
|
|
34
42
|
subvm.expects(:provision).with('chef_solo')
|
35
43
|
|
36
44
|
Gusteau::Vagrant.detect(config) do |setup|
|
37
|
-
setup.
|
45
|
+
setup.config_path = './spec/config/gusteau.yml'
|
38
46
|
setup.defaults.box_url = "http://a.com/b.box"
|
39
47
|
setup.provision = true
|
40
48
|
end
|
@@ -42,6 +50,7 @@ describe Gusteau::Vagrant do
|
|
42
50
|
end
|
43
51
|
|
44
52
|
describe "#vm_config" do
|
53
|
+
let(:node) { Gusteau::Config.nodes("./spec/config/gusteau.yml")['development-playground'] }
|
45
54
|
subject { Gusteau::Vagrant.vm_config(node, options) }
|
46
55
|
|
47
56
|
let(:defaults) do
|
@@ -54,16 +63,14 @@ describe Gusteau::Vagrant do
|
|
54
63
|
let(:prefix) { 'hyper' }
|
55
64
|
let(:options) { { :defaults => defaults, :prefix => prefix } }
|
56
65
|
|
57
|
-
let(:
|
58
|
-
|
59
|
-
let(:expected_label) { 'hyper-development' }
|
66
|
+
let(:expected_label) { 'hyper-development-playground' }
|
60
67
|
let(:expected_config) do
|
61
68
|
{
|
62
|
-
:name => 'development',
|
69
|
+
:name => 'development-playground',
|
63
70
|
:label => expected_label,
|
64
71
|
:box_url => 'https://opscode.s3.amazonaws.com/centos-6.4.box',
|
65
72
|
:ip => '192.168.100.21',
|
66
|
-
:cpus =>
|
73
|
+
:cpus => 2,
|
67
74
|
:memory => 4096
|
68
75
|
}
|
69
76
|
end
|
@@ -74,7 +81,7 @@ describe Gusteau::Vagrant do
|
|
74
81
|
|
75
82
|
context "prefix not specified" do
|
76
83
|
let(:prefix) { nil }
|
77
|
-
let(:expected_label) { 'development' }
|
84
|
+
let(:expected_label) { 'development-playground' }
|
78
85
|
|
79
86
|
it "should omit the prefix" do
|
80
87
|
subject.must_equal(expected_config)
|
@@ -89,4 +96,26 @@ describe Gusteau::Vagrant do
|
|
89
96
|
end
|
90
97
|
end
|
91
98
|
end
|
99
|
+
|
100
|
+
describe "#define_provisioner" do
|
101
|
+
let(:node) { Gusteau::Config.nodes("./spec/config/gusteau.yml")['development-playground'] }
|
102
|
+
let(:chef) { stub_everything('chef') }
|
103
|
+
|
104
|
+
before do
|
105
|
+
def subvm.provision(provider); yield chef; end
|
106
|
+
subvm.expects(:chef).returns(chef)
|
107
|
+
|
108
|
+
instance.expects(:vm).at_least_once.returns(subvm)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should set the correct Chef JSON" do
|
112
|
+
chef.expects('json='.to_sym).with({"mysql"=>{"server_root_password"=>"guesswhat"}})
|
113
|
+
Gusteau::Vagrant.define_provisioner(instance, node)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should set the correct Chef run_list" do
|
117
|
+
chef.expects('run_list='.to_sym).with(["recipe[zsh]", "recipe[mysql::server]"])
|
118
|
+
Gusteau::Vagrant.define_provisioner(instance, node)
|
119
|
+
end
|
120
|
+
end
|
92
121
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
require 'coveralls'
|
2
|
+
require 'simplecov'
|
3
|
+
|
4
|
+
if ENV['COVERAGE'] == 'coveralls'
|
5
|
+
Coveralls.wear!
|
6
|
+
else
|
7
|
+
SimpleCov.start do
|
8
|
+
add_filter "/spec/"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
1
12
|
$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
2
13
|
require 'lib/gusteau'
|
3
14
|
require 'minitest/autorun'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
environments:
|
2
|
+
example:
|
3
|
+
attributes:
|
4
|
+
users:
|
5
|
+
- <%= @login %>
|
6
|
+
cowsay:
|
7
|
+
greeting: "Good job, <%= @login %>!"
|
8
|
+
|
9
|
+
run_list:
|
10
|
+
- recipe[platform]
|
11
|
+
- recipe[cowsay]
|
12
|
+
|
13
|
+
nodes:
|
14
|
+
box:
|
15
|
+
host: 33.33.33.10
|
16
|
+
user: vagrant
|
17
|
+
password: vagrant
|
18
|
+
vagrant:
|
19
|
+
IP: 33.33.33.10
|
20
|
+
cpu: 1
|
21
|
+
memory: 256
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
driver_plugin: vagrant
|
3
|
+
|
4
|
+
platforms:
|
5
|
+
- name: ubuntu-13.04
|
6
|
+
driver_config:
|
7
|
+
box: opscode-ubuntu-13.04
|
8
|
+
box_url: https://opscode-vm.s3.amazonaws.com/vagrant/opscode_ubuntu-13.04_provisionerless.box
|
9
|
+
require_chef_omnibus: true
|
10
|
+
|
11
|
+
suites:
|
12
|
+
- name: default
|
13
|
+
attributes:
|
14
|
+
users:
|
15
|
+
- remi
|
16
|
+
cowsay:
|
17
|
+
greeting: "Hello test-kitchen"
|
18
|
+
run_list:
|
19
|
+
- recipe[platform]
|
20
|
+
- recipe[cowsay]
|
data/template/Berksfile
CHANGED
data/template/Gemfile
CHANGED
@@ -0,0 +1,70 @@
|
|
1
|
+
# chef-repo
|
2
|
+
|
3
|
+
Welcome to your example Chef-Repo, <%= @login %>.
|
4
|
+
|
5
|
+
Now that you've installed everything, let's have a look around.
|
6
|
+
|
7
|
+
## Structure
|
8
|
+
|
9
|
+
### .gusteau.yml
|
10
|
+
|
11
|
+
A configuration file for all your environments and nodes. This project features one environment, `example` which only includes one node, `box`.
|
12
|
+
|
13
|
+
### Vagrantfile
|
14
|
+
|
15
|
+
A configuration file for Vagrant which includes gusteau as a plugin. It detects all your `.gusteau.yml` nodes with `vagrant` keys in them and configures Vagrant instances for them. The `example-box` node is one such example.
|
16
|
+
|
17
|
+
### cookbooks
|
18
|
+
|
19
|
+
Gusteau uploads `site-cookbooks` and `cookbooks` directories when you converge a node. Between the two, `cookbooks` is a directory for your vendorized cookbooks. This example project uses Berkshelf, so in order to populate the directory run
|
20
|
+
|
21
|
+
```
|
22
|
+
berks install --path ./cookbooks
|
23
|
+
```
|
24
|
+
|
25
|
+
### site-cookbooks
|
26
|
+
|
27
|
+
Your repo-specific or work-in-progress cookbooks. A good place to store your [wrapper cookbooks](http://devopsanywhere.blogspot.com.au/2012/11/how-to-write-reusable-chef-cookbooks.html).
|
28
|
+
|
29
|
+
### data_bags
|
30
|
+
|
31
|
+
Chef Data bags. A way to store large pieces of domain-specifc configuration. See the included `users` data_bag for an example.
|
32
|
+
|
33
|
+
## Using gusteau
|
34
|
+
|
35
|
+
To try gusteau first with an example Vagrant node, bring it up:
|
36
|
+
|
37
|
+
```
|
38
|
+
vagrant up example-box
|
39
|
+
```
|
40
|
+
|
41
|
+
Then run `gusteau converge`:
|
42
|
+
|
43
|
+
```
|
44
|
+
gusteau converge example-box
|
45
|
+
```
|
46
|
+
|
47
|
+
## Testing your Chef-Repo
|
48
|
+
|
49
|
+
It is a very good practice to have tests for your Chef-driven infrastructure.
|
50
|
+
This project includes an example [test-kitchen](https://github.com/opscode/test-kitchen) setup to get you started.
|
51
|
+
|
52
|
+
The tests are implemented using the excellent [serverspec](http://serverspec.org/) framework.
|
53
|
+
|
54
|
+
There are two important bits to have a look at:
|
55
|
+
|
56
|
+
### test
|
57
|
+
|
58
|
+
The directory where integration tests are placed.
|
59
|
+
|
60
|
+
### .kitchen.yml
|
61
|
+
|
62
|
+
Test-kitchen configuration file where test platforms and suites are specified.
|
63
|
+
|
64
|
+
To run the tests:
|
65
|
+
|
66
|
+
```
|
67
|
+
bundle exec kitchen test
|
68
|
+
```
|
69
|
+
|
70
|
+
*Happy cooking with Chef and Gusteau!*
|