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.
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
@@ -1,5 +1,5 @@
1
1
  require 'hashie'
2
- require 'gusteau/log'
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
- if prefix
21
- options[:prefix] = prefix
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.json = node.config['json'] || {}
86
-
87
- (node.config['recipes'] || []).each { |r| chef.add_recipe(r) }
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
 
@@ -1,3 +1,3 @@
1
1
  module Gusteau
2
- VERSION = "0.4.8"
2
+ VERSION = "1.0.0.dev"
3
3
  end
@@ -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
- let(:path) { '/tmp/testdna.json' }
16
- let(:json) { JSON::parse(File.read dna[:path]) }
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 support ERB" do
23
- json['environment'].must_equal "production"
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
- context "recipes specified" do
33
- let(:include_all) { false }
34
- let(:recipes) { ['rvm', 'rails::apps'] }
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
- it "should only include the specified item into run_list" do
37
- json['run_list'].must_equal ["recipe[rvm]", "recipe[rails::apps]"]
38
- end
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 "#vagrant_wrap" do
43
- let(:node) { Gusteau::Node.new('spec/nodes/development.yml') }
44
-
45
- describe "#ssh" do
46
- context "server is defined" do
47
- it "should call server.ssh" do
48
- node.server.expects(:ssh)
49
- node.ssh
50
- end
51
- end
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
- describe "#run" do
79
- context "server is defined" do
80
- it "should call server.chef.run" do
81
- chef = mock()
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