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.
Files changed (49) hide show
  1. data/.travis.yml +2 -0
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +31 -30
  4. data/bin/gusteau +14 -13
  5. data/gusteau.gemspec +4 -1
  6. data/lib/gusteau.rb +1 -0
  7. data/lib/gusteau/bureau.rb +32 -20
  8. data/lib/gusteau/chef.rb +2 -2
  9. data/lib/gusteau/config.rb +39 -0
  10. data/lib/gusteau/node.rb +11 -43
  11. data/lib/gusteau/ssh_config.rb +8 -7
  12. data/lib/gusteau/vagrant.rb +7 -32
  13. data/lib/gusteau/version.rb +1 -1
  14. data/spec/config/gusteau.yml +60 -0
  15. data/spec/config/remi.yml +29 -0
  16. data/spec/lib/gusteau/bureau_spec.rb +52 -0
  17. data/spec/lib/gusteau/compressed_tar_stream_spec.rb +31 -0
  18. data/spec/lib/gusteau/config_spec.rb +31 -0
  19. data/spec/lib/gusteau/log_spec.rb +34 -0
  20. data/spec/lib/gusteau/node_spec.rb +40 -85
  21. data/spec/lib/gusteau/server_spec.rb +12 -0
  22. data/spec/lib/gusteau/ssh_config_spec.rb +16 -10
  23. data/spec/lib/gusteau/ssh_spec.rb +110 -0
  24. data/spec/lib/gusteau/vagrant_spec.rb +46 -17
  25. data/spec/spec_helper.rb +11 -0
  26. data/template/.gusteau.yml.erb +21 -0
  27. data/template/.kitchen.yml +20 -0
  28. data/template/Berksfile +3 -3
  29. data/template/Gemfile +6 -1
  30. data/template/README.md.erb +70 -0
  31. data/template/Vagrantfile +12 -4
  32. data/template/init.sh +27 -0
  33. data/template/site-cookbooks/cowsay/metadata.rb +10 -0
  34. data/template/site-cookbooks/cowsay/recipes/default.rb +1 -3
  35. data/template/site-cookbooks/platform/metadata.rb +14 -0
  36. data/template/site-cookbooks/platform/recipes/default.rb +3 -0
  37. data/template/test/integration/data_bags/users/remi.json +7 -0
  38. data/template/test/integration/default/serverspec/localhost/cowsay_spec.rb +5 -0
  39. data/template/test/integration/default/serverspec/localhost/platform_spec.rb +25 -0
  40. data/template/test/integration/default/serverspec/spec_helper.rb +9 -0
  41. metadata +81 -17
  42. data/bootstrap/centos.sh +0 -17
  43. data/bootstrap/redhat.sh +0 -17
  44. data/bootstrap/ubuntu.sh +0 -17
  45. data/spec/nodes/development.yml +0 -17
  46. data/spec/nodes/production.yml +0 -18
  47. data/spec/nodes/staging.yml +0 -12
  48. data/template/nodes/example.yml.erb +0 -19
  49. 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
- subject { Gusteau::SSHConfig.new('./spec') }
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 2222
13
- User developer
13
+ Port 22
14
+ User root
14
15
 
15
- Host production
16
- HostName www.example.com
16
+ Host staging-www
17
+ HostName staging.myapp.com
17
18
  Port 22
18
19
  User root
19
20
 
20
- Host staging
21
- HostName staging.example.com
22
- Port 2222
23
- User devops
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) { mock() }
7
- let(:vm) { mock() }
8
- let(:instance) { mock() }
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
- yield instance
14
- end
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.dir = './spec/nodes'
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.dir = './spec/nodes'
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(:node) { ::Gusteau::Node.new('./spec/nodes/development.yml') }
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 => 4,
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
@@ -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]
@@ -2,6 +2,6 @@
2
2
 
3
3
  site :opscode
4
4
 
5
- cookbook 'apt'
6
- cookbook 'build-essential'
7
- cookbook 'user'
5
+ %w{ cowsay platform }.each do |c|
6
+ cookbook c, :path => "./site-cookbooks/#{c}"
7
+ end
@@ -1,6 +1,11 @@
1
1
  # vi:ft=ruby:
2
2
 
3
- source 'http://rubygems.org'
3
+ source 'https://rubygems.org'
4
4
 
5
5
  gem 'berkshelf'
6
6
  gem 'gusteau'
7
+
8
+ group :integration do
9
+ gem 'test-kitchen', github: 'opscode/test-kitchen'
10
+ gem 'kitchen-vagrant', '~> 0.10.0'
11
+ end
@@ -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!*