cluster_chef 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +51 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +63 -0
  4. data/Gemfile +18 -0
  5. data/LICENSE +201 -0
  6. data/README.md +332 -0
  7. data/Rakefile +92 -0
  8. data/TODO.md +8 -0
  9. data/VERSION +1 -0
  10. data/chefignore +41 -0
  11. data/cluster_chef.gemspec +115 -0
  12. data/clusters/website_demo.rb +65 -0
  13. data/config/client.rb +59 -0
  14. data/lib/cluster_chef/chef_layer.rb +297 -0
  15. data/lib/cluster_chef/cloud.rb +409 -0
  16. data/lib/cluster_chef/cluster.rb +118 -0
  17. data/lib/cluster_chef/compute.rb +144 -0
  18. data/lib/cluster_chef/cookbook_munger/README.md.erb +47 -0
  19. data/lib/cluster_chef/cookbook_munger/licenses.yaml +16 -0
  20. data/lib/cluster_chef/cookbook_munger/metadata.rb.erb +23 -0
  21. data/lib/cluster_chef/cookbook_munger.rb +588 -0
  22. data/lib/cluster_chef/deprecated.rb +33 -0
  23. data/lib/cluster_chef/discovery.rb +158 -0
  24. data/lib/cluster_chef/dsl_object.rb +123 -0
  25. data/lib/cluster_chef/facet.rb +144 -0
  26. data/lib/cluster_chef/fog_layer.rb +134 -0
  27. data/lib/cluster_chef/private_key.rb +110 -0
  28. data/lib/cluster_chef/role_implications.rb +49 -0
  29. data/lib/cluster_chef/security_group.rb +103 -0
  30. data/lib/cluster_chef/server.rb +265 -0
  31. data/lib/cluster_chef/server_slice.rb +259 -0
  32. data/lib/cluster_chef/volume.rb +93 -0
  33. data/lib/cluster_chef.rb +137 -0
  34. data/notes/aws_console_screenshot.jpg +0 -0
  35. data/rspec.watchr +29 -0
  36. data/spec/cluster_chef/cluster_spec.rb +13 -0
  37. data/spec/cluster_chef/facet_spec.rb +70 -0
  38. data/spec/cluster_chef/server_slice_spec.rb +19 -0
  39. data/spec/cluster_chef/server_spec.rb +112 -0
  40. data/spec/cluster_chef_spec.rb +193 -0
  41. data/spec/spec_helper/dummy_chef.rb +25 -0
  42. data/spec/spec_helper.rb +50 -0
  43. data/spec/test_config.rb +20 -0
  44. data/tasks/chef_config.rb +38 -0
  45. data/tasks/jeweler_use_alt_branch.rb +47 -0
  46. metadata +227 -0
@@ -0,0 +1,93 @@
1
+ module ClusterChef
2
+ #
3
+ # Internal or external storage
4
+ #
5
+ class Volume < ClusterChef::DslObject
6
+ attr_reader :parent
7
+ attr_accessor :fog_volume
8
+ has_keys(
9
+ :name,
10
+ # mountable volume attributes
11
+ :device, :mount_point, :mount_options, :fstype, :mount_dump, :mount_pass,
12
+ # cloud volume attributes
13
+ :attachable, :create_at_launch, :volume_id, :snapshot_id, :size, :keep, :availability_zone,
14
+ # arbitrary tags
15
+ :tags
16
+ )
17
+
18
+ VOLUME_DEFAULTS = {
19
+ :fstype => 'xfs',
20
+ :mount_options => 'defaults,nouuid,noatime',
21
+ :attachable => :ebs,
22
+ :create_at_launch => false,
23
+ :keep => true,
24
+ }
25
+
26
+ # Describes a volume
27
+ #
28
+ # @example
29
+ # ClusterChef::Volume.new( :name => 'redis',
30
+ # :device => '/dev/sdp', :mount_point => '/data/redis', :fstype => 'xfs', :mount_options => 'defaults,nouuid,noatime'
31
+ # :size => 1024, :snapshot_id => 'snap-66494a08', :volume_id => 'vol-12312',
32
+ # :tags => {}, :keep => true )
33
+ #
34
+ def initialize attrs={}
35
+ @parent = attrs.delete(:parent)
36
+ super(attrs)
37
+ @settings[:tags] ||= {}
38
+ end
39
+
40
+ # human-readable description for logging messages and such
41
+ def desc
42
+ "#{name} on #{parent.fullname} (#{volume_id} @ #{device})"
43
+ end
44
+
45
+ def defaults
46
+ self.configure(VOLUME_DEFAULTS)
47
+ end
48
+
49
+ def ephemeral_device?
50
+ volume_id =~ /^ephemeral/
51
+ end
52
+
53
+ # With snapshot specified but volume missing, have it auto-created at launch
54
+ #
55
+ # Be careful with this -- you can end up with multiple volumes claiming to
56
+ # be the same thing.
57
+ #
58
+ def create_at_launch?
59
+ volume_id.blank? && self.create_at_launch
60
+ end
61
+
62
+ def in_cloud?
63
+ !! fog_volume
64
+ end
65
+
66
+ def has_server?
67
+ in_cloud? && fog_volume.server_id.present?
68
+ end
69
+
70
+ def reverse_merge!(other_hsh)
71
+ super(other_hsh)
72
+ self.tags.reverse_merge!(other_hsh.tags) if other_hsh.respond_to?(:tags) && other_hsh.tags.present?
73
+ self
74
+ end
75
+
76
+ # An array of hashes with dorky-looking keys, just like Fog wants it.
77
+ def block_device_mapping
78
+ hsh = { 'DeviceName' => device }
79
+ if ephemeral_device?
80
+ hsh['VirtualName'] = volume_id
81
+ elsif create_at_launch?
82
+ hsh.merge!({
83
+ 'Ebs.SnapshotId' => snapshot_id,
84
+ 'Ebs.VolumeSize' => size,
85
+ 'Ebs.DeleteOnTermination' => (! keep).to_s })
86
+ else
87
+ return
88
+ end
89
+ hsh
90
+ end
91
+
92
+ end
93
+ end
@@ -0,0 +1,137 @@
1
+ require 'chef/mash'
2
+ require 'gorillib/metaprogramming/class_attribute'
3
+ require 'gorillib/hash/reverse_merge'
4
+ require 'gorillib/object/blank'
5
+ require 'gorillib/hash/compact'
6
+ require 'set'
7
+
8
+ require 'cluster_chef/dsl_object'
9
+ require 'cluster_chef/cloud'
10
+ require 'cluster_chef/security_group'
11
+ require 'cluster_chef/compute' # base class for machine attributes
12
+ require 'cluster_chef/facet' # similar machines within a cluster
13
+ require 'cluster_chef/cluster' # group of machines with a common mission
14
+ require 'cluster_chef/server' # realization of a specific facet
15
+ require 'cluster_chef/discovery' # pair servers with Fog and Chef objects
16
+ require 'cluster_chef/server_slice' # collection of server objects
17
+ require 'cluster_chef/volume' # configure external and internal volumes
18
+ require 'cluster_chef/private_key' # coordinate chef keys, cloud keypairs, etc
19
+ require 'cluster_chef/role_implications' # make roles trigger other actions (security groups, etc)
20
+ #
21
+ require 'cluster_chef/chef_layer' # interface to chef for server actions
22
+ require 'cluster_chef/fog_layer' # interface to fog for server actions
23
+ #
24
+ require 'cluster_chef/deprecated' # stuff slated to go away
25
+
26
+ module ClusterChef
27
+
28
+ # path to search for cluster definition files
29
+ def self.cluster_path
30
+ Chef::Config[:cluster_path] ||= Chef::Config[:cookbooks_path].map{|dir| File.expand_path('../clusters', dir) }
31
+ end
32
+
33
+ #
34
+ # Delegates
35
+ def self.clusters
36
+ Chef::Config[:clusters] ||= Mash.new
37
+ end
38
+
39
+ def self.ui=(ui) @ui = ui ; end
40
+ def self.ui() @ui ; end
41
+
42
+ def self.chef_config=(cc) @chef_config = cc ; end
43
+ def self.chef_config() @chef_config ; end
44
+
45
+ #
46
+ # Defines a cluster with the given name.
47
+ #
48
+ # @example
49
+ # ClusterChef.cluster 'demosimple' do
50
+ # cloud :ec2 do
51
+ # availability_zones ['us-east-1d']
52
+ # flavor "t1.micro"
53
+ # image_name "ubuntu-natty"
54
+ # end
55
+ # role :base_role
56
+ # role :chef_client
57
+ #
58
+ # facet :sandbox do
59
+ # instances 2
60
+ # role :nfs_client
61
+ # end
62
+ # end
63
+ #
64
+ #
65
+ def self.cluster(name, attrs={}, &block)
66
+ name = name.to_sym
67
+ cl = ( self.clusters[name] ||= ClusterChef::Cluster.new(name, attrs) )
68
+ cl.configure(&block)
69
+ cl
70
+ end
71
+
72
+ #
73
+ # Return cluster if it's defined. Otherwise, search ClusterChef.cluster_path
74
+ # for an eponymous file, load it, and return the cluster it defines.
75
+ #
76
+ # Raises an error if a matching file isn't found, or if loading that file
77
+ # doesn't define the requested cluster.
78
+ #
79
+ # @return [ClusterChef::Cluster] the requested cluster
80
+ def self.load_cluster(cluster_name)
81
+ raise ArgumentError, "Please supply a cluster name" if cluster_name.to_s.empty?
82
+ return clusters[cluster_name] if clusters[cluster_name]
83
+
84
+ cluster_file = cluster_filenames[cluster_name] or die("Couldn't find a definition for #{cluster_name} in cluster_path: #{cluster_path.inspect}")
85
+
86
+ Chef::Log.info("Loading cluster #{cluster_file}")
87
+
88
+ require cluster_file
89
+ unless clusters[cluster_name] then die("#{cluster_file} was supposed to have the definition for the #{cluster_name} cluster, but didn't") end
90
+
91
+ clusters[cluster_name]
92
+ end
93
+
94
+ #
95
+ # Map from cluster name to file name
96
+ #
97
+ # @return [Hash] map from cluster name to file name
98
+ def self.cluster_filenames
99
+ return @cluster_filenames if @cluster_filenames
100
+ @cluster_filenames = {}
101
+ cluster_path.each do |cp_dir|
102
+ Dir[ File.join(cp_dir, '*.rb') ].each do |filename|
103
+ cluster_name = File.basename(filename).gsub(/\.rb$/, '')
104
+ @cluster_filenames[cluster_name] ||= filename
105
+ end
106
+ end
107
+ @cluster_filenames
108
+ end
109
+
110
+ #
111
+ # Utility to die with an error message.
112
+ # If the last arg is an integer, use it as the exit code.
113
+ #
114
+ def self.die *strings
115
+ exit_code = strings.last.is_a?(Integer) ? strings.pop : -1
116
+ strings.each{|str| ui.warn str }
117
+ exit exit_code
118
+ end
119
+
120
+ #
121
+ # Utility to turn an error into a warning
122
+ #
123
+ # @example
124
+ # ClusterChef.safely do
125
+ # ClusterChef.fog_connection.associate_address(self.fog_server.id, address)
126
+ # end
127
+ #
128
+ def self.safely
129
+ begin
130
+ yield
131
+ rescue StandardError => boom
132
+ ui.info( boom )
133
+ Chef::Log.error( boom )
134
+ Chef::Log.error( boom.backtrace.join("\n") )
135
+ end
136
+ end
137
+ end
Binary file
data/rspec.watchr ADDED
@@ -0,0 +1,29 @@
1
+ # -*- ruby -*-
2
+
3
+ def run_spec(file)
4
+ unless File.exist?(file)
5
+ puts "#{file} does not exist"
6
+ return
7
+ end
8
+
9
+ puts "Running #{file}"
10
+ system "rspec #{file}"
11
+ puts
12
+ end
13
+
14
+ watch("spec/.*/*_spec\.rb") do |match|
15
+ run_spec match[0]
16
+ end
17
+
18
+ watch("lib/(.*)\.rb") do |match|
19
+ file = %{spec/#{match[1]}_spec.rb}
20
+ run_spec file if File.exists?(file)
21
+ end
22
+
23
+ # watch('lib/cluster_chef/cookbook_munger\.rb') do |match|
24
+ # system match[0]
25
+ # end
26
+ #
27
+ # watch('lib/cluster_chef/cookbook_munger/.*\.erb') do |match|
28
+ # system 'lib/cluster_chef/cookbook_munger.rb'
29
+ # end
@@ -0,0 +1,13 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ require CLUSTER_CHEF_DIR("lib/cluster_chef")
4
+
5
+ describe ClusterChef::Cluster do
6
+ describe 'discover!' do
7
+ let(:cluster){ get_example_cluster(:monkeyballs) }
8
+
9
+ it 'enumerates chef nodes' do
10
+ cluster.discover!
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,70 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ require CLUSTER_CHEF_DIR("lib/cluster_chef")
4
+
5
+ describe ClusterChef::Facet do
6
+ let(:cluster){ ClusterChef.cluster(:gibbon) }
7
+ let(:facet){
8
+ cluster.facet(:namenode) do
9
+ instances 5
10
+ end
11
+ }
12
+
13
+ describe 'slicing' do
14
+ it 'has servers' do
15
+ facet.indexes.should == [0, 1, 2, 3, 4]
16
+ facet.valid_indexes.should == [0, 1, 2, 3, 4]
17
+ facet.server(3){ name(:bob) }
18
+ svrs = facet.servers
19
+ svrs.length.should == 5
20
+ svrs.map{|svr| svr.name }.should == ["gibbon-namenode-0", "gibbon-namenode-1", "gibbon-namenode-2", :bob, "gibbon-namenode-4"]
21
+ end
22
+
23
+ it 'servers have bogosity if out of range' do
24
+ facet.server(69).should be_bogus
25
+ facet.servers.select(&:bogus?).map(&:facet_index).should == [69]
26
+ facet.indexes.should == [0, 1, 2, 3, 4, 69]
27
+ facet.valid_indexes.should == [0, 1, 2, 3, 4]
28
+ end
29
+
30
+ it 'returns all on nil or "", but [] means none' do
31
+ facet.server(69)
32
+ facet.slice('' ).map(&:facet_index).should == [0, 1, 2, 3, 4, 69]
33
+ facet.slice(nil).map(&:facet_index).should == [0, 1, 2, 3, 4, 69]
34
+ facet.slice([] ).map(&:facet_index).should == []
35
+ end
36
+
37
+ it 'slice returns all by default' do
38
+ facet.server(69)
39
+ facet.slice().map(&:facet_index).should == [0, 1, 2, 3, 4, 69]
40
+ end
41
+
42
+ it 'with an array returns specified indexes (bogus or not) in sorted order' do
43
+ facet.server(69)
44
+ facet.slice( [3, 1, 0] ).map(&:facet_index).should == [0, 1, 3]
45
+ facet.slice( [3, 1, 69, 0] ).map(&:facet_index).should == [0, 1, 3, 69]
46
+ end
47
+
48
+ it 'with an array does not create new dummy servers' do
49
+ facet.server(69)
50
+ facet.slice( [3, 1, 69, 0, 75, 123] ).map(&:facet_index).should == [0, 1, 3, 69]
51
+ facet.has_server?(75).should be_false
52
+ facet.has_server?(69).should be_true
53
+ end
54
+
55
+ it 'with a string, converts to intervals' do
56
+ facet.slice('1' ).map(&:facet_index).should == [1]
57
+ facet.slice('5' ).map(&:facet_index).should == []
58
+ facet.slice('1-1' ).map(&:facet_index).should == [1]
59
+ facet.slice('0-1' ).map(&:facet_index).should == [0,1]
60
+ facet.slice('0-1,3-4').map(&:facet_index).should == [0,1,3,4]
61
+ facet.slice('0-1,69' ).map(&:facet_index).should == [0,1,69]
62
+ facet.slice('0-2,1-3').map(&:facet_index).should == [0,1,2,3]
63
+ facet.slice('3-1' ).map(&:facet_index).should == []
64
+ facet.slice('2-5' ).map(&:facet_index).should == [2,3,4]
65
+ facet.slice(1).map(&:facet_index).should == [1]
66
+ end
67
+
68
+ end
69
+ end
70
+
@@ -0,0 +1,19 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require CLUSTER_CHEF_DIR("lib/cluster_chef")
3
+
4
+ describe ClusterChef::ServerSlice do
5
+ before do
6
+ @slice = ClusterChef.slice('webserver_demo')
7
+ end
8
+
9
+ describe 'attributes' do
10
+ it 'security groups' do
11
+ @slice.security_groups.keys.sort.should == [
12
+ "default",
13
+ "webserver_demo", "webserver_demo-awesome_website", "webserver_demo-dbnode", "webserver_demo-esnode",
14
+ "webserver_demo-redis_client", "webserver_demo-redis_server",
15
+ "webserver_demo-webnode", "nfs_client", "ssh"
16
+ ]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,112 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+ require CLUSTER_CHEF_DIR("lib/cluster_chef")
3
+
4
+ describe ClusterChef::Server do
5
+ include_context 'dummy_chef'
6
+
7
+ ClusterChef::Server.class_eval do
8
+ def chef_node
9
+ Chef::Node.new
10
+ end
11
+ end
12
+
13
+ ClusterChef::DryRunnable.class_eval do
14
+ def unless_dry_run
15
+ puts "Not doing that"
16
+ end
17
+ end
18
+
19
+ before do
20
+ ClusterChef::Server.stub!(:chef_node).and_return( "HI" )
21
+ Chef::Config.stub!(:validation_key).and_return("I_AM_VALID")
22
+
23
+ foo = ClusterChef::Server.new(ClusterChef::Facet.new(ClusterChef::Cluster.new('hi'),'there'),0)
24
+ puts foo.inspect
25
+ puts foo.chef_node
26
+ @cluster = get_example_cluster('webserver_demo')
27
+ @cluster.resolve!
28
+ @facet = @cluster.facet(:dbnode)
29
+ @server = @facet.server(0)
30
+ end
31
+
32
+ describe 'volumes' do
33
+ describe '#composite_volumes' do
34
+ it 'assembles cluster, facet and server volumes' do
35
+ @server.composite_volumes.length.should == 5
36
+ @cluster.volumes.length.should == 4
37
+ @facet.volumes.length.should == 1
38
+ @server.volumes.length.should == 1
39
+ end
40
+
41
+ it 'composites server attributes onto a volume defined in the facet' do
42
+ vol = @server.composite_volumes[:data]
43
+ vol.to_hash.should == {
44
+ :name => :data,
45
+ :tags => {},
46
+ :snapshot_id => "snap-d9c1edb1",
47
+ :size => 50,
48
+ :keep => true,
49
+ :device => "/dev/sdi",
50
+ :mount_point => "/data/db",
51
+ :mount_options => "defaults,nouuid,noatime",
52
+ :fs_type => "xfs",
53
+ :availability_zone => "us-east-1d"
54
+ }
55
+ end
56
+
57
+ it 'makes block_device_mapping for non-ephemeral storage' do
58
+ vol = @server.composite_volumes[:data]
59
+ vol.block_device_mapping.should == {
60
+ "DeviceName" => "/dev/sdi",
61
+ "Ebs.SnapshotId" => "snap-d9c1edb1",
62
+ "Ebs.VolumeSize" => 50,
63
+ "Ebs.DeleteOnTermination" => "false"
64
+ }
65
+ end
66
+
67
+ it 'skips block_device_mapping for non-ephemeral storage if volume id is present' do
68
+ vol = @facet.server(1).composite_volumes[:data]
69
+ vol.block_device_mapping.should be_nil
70
+ end
71
+
72
+ end
73
+ end
74
+
75
+ describe 'launch' do
76
+ describe '#fog_description_for_launch' do
77
+ it 'has right attributes' do
78
+
79
+ hsh = @server.fog_description_for_launch
80
+ hsh.delete(:user_data)
81
+ hsh.should == {
82
+ :image_id => "ami-08f40561",
83
+ :flavor_id => "m1.large",
84
+ :groups => ["webserver_demo-redis_client", "webserver_demo-dbnode", "default", "ssh", "nfs_client", "webserver_demo"],
85
+ :key_name => :webserver_demo,
86
+ :tags => {:cluster=>:webserver_demo, :facet=>:dbnode, :index=>0},
87
+ :block_device_mapping => [
88
+ {"DeviceName"=>"/dev/sdi", "Ebs.SnapshotId"=>"snap-d9c1edb1", "Ebs.VolumeSize"=>50, "Ebs.DeleteOnTermination"=>"false"},
89
+ {"DeviceName"=>"/dev/sdb", "VirtualName"=>"ephemeral0"},
90
+ {"DeviceName"=>"/dev/sdc", "VirtualName"=>"ephemeral1"},
91
+ {"DeviceName"=>"/dev/sdd", "VirtualName"=>"ephemeral2"},
92
+ {"DeviceName"=>"/dev/sde", "VirtualName"=>"ephemeral3"},
93
+ ],
94
+ :availability_zone => "us-east-1d",
95
+ :monitoring => nil
96
+ }
97
+ end
98
+
99
+ it 'has right user_data' do
100
+ hsh = @server.fog_description_for_launch
101
+ user_data_hsh = JSON.parse( hsh[:user_data] )
102
+ user_data_hsh.keys.should == ["chef_server", "validation_client_name", "validation_key", "attributes"]
103
+ user_data_hsh["attributes"].keys.sort.should == [
104
+ "cluster_name", "facet_name", "facet_index",
105
+ "node_name",
106
+ "webnode_count",
107
+ ]
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,193 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ require CLUSTER_CHEF_DIR("lib/cluster_chef")
4
+
5
+ describe "cluster_chef" do
6
+ describe 'successfuly runs example' do
7
+
8
+ describe 'webserver_demo:' do
9
+ before :all do
10
+ @cluster = get_example_cluster(:webserver_demo)
11
+ @cluster.resolve!
12
+ end
13
+
14
+ it 'loads successfuly' do
15
+ @cluster.should be_a(ClusterChef::Cluster)
16
+ @cluster.name.should == :webserver_demo
17
+ end
18
+
19
+ it 'cluster is right' do
20
+ @cluster.to_hash.should == {
21
+ :name => :webserver_demo,
22
+ :run_list => ["role[base_role]", "role[chef_client]", "role[ssh]", "role[nfs_client]", "role[big_package]", "role[webserver_demo_cluster]"],
23
+ :chef_attributes => { :webnode_count => 6 },
24
+ :facet_name => "webserver_demo_cluster",
25
+ }
26
+ end
27
+
28
+ it 'defaults cluster' do
29
+ defaults_cluster = ClusterChef.cluster(:defaults)
30
+ cloud_hash = defaults_cluster.cloud.to_hash
31
+ [:security_groups, :user_data].each{|k| cloud_hash.delete k }
32
+ cloud_hash.should == {
33
+ :availability_zones => ['us-east-1d'],
34
+ :region => "us-east-1",
35
+ :flavor => "m1.small",
36
+ :image_name => "lucid",
37
+ :backing => "ebs",
38
+ :disable_api_termination => false,
39
+ :public_ip => false,
40
+ :bootstrap_distro => "ubuntu10.04-cluster_chef",
41
+ }
42
+ end
43
+
44
+ it 'cluster cloud is right' do
45
+ cloud_hash = @cluster.cloud.to_hash
46
+ [:security_groups, :user_data].each{|k| cloud_hash.delete k }
47
+ cloud_hash.should == {
48
+ :availability_zones => ['us-east-1d'],
49
+ :region => "us-east-1",
50
+ :flavor => "t1.micro",
51
+ :image_name => "maverick",
52
+ :backing => "instance",
53
+ :disable_api_termination => false,
54
+ :public_ip => false,
55
+ :bootstrap_distro => "ubuntu10.04-cluster_chef",
56
+ :keypair => :webserver_demo,
57
+ }
58
+ end
59
+
60
+ it 'facet cloud is right' do
61
+ cloud_hash = @cluster.facet(:webnode).cloud.to_hash
62
+ [:security_groups, :user_data].each{|k| cloud_hash.delete k }
63
+ cloud_hash.should == {
64
+ :backing => "ebs",
65
+ }
66
+ end
67
+
68
+ it 'webnode facets are right' do
69
+ @cluster.facets.length.should == 3
70
+ fct = @cluster.facet(:webnode)
71
+ fct.to_hash.should == {
72
+ :name => :webnode,
73
+ :run_list => ["role[nginx]", "role[redis_client]", "role[mysql_client]", "role[elasticsearch_client]", "role[awesome_website]", "role[webserver_demo_webnode]"],
74
+ :chef_attributes => {:split_testing=>{:group=>"A"}},
75
+ :facet_role => "webserver_demo_webnode",
76
+ :instances => 6,
77
+ }
78
+ end
79
+
80
+ it 'dbnode facets are right' do
81
+ fct = @cluster.facet(:dbnode)
82
+ fct.to_hash.should == {
83
+ :name => :dbnode,
84
+ :run_list => ["role[mysql_server]", "role[redis_client]", "role[webserver_demo_dbnode]" ],
85
+ :chef_attributes => {},
86
+ :facet_role => "webserver_demo_dbnode",
87
+ :instances => 2,
88
+ }
89
+ fct.cloud.flavor.should == 'c1.xlarge'
90
+ fct.server(0).cloud.flavor.should == 'm1.large'
91
+ end
92
+
93
+ it 'esnode facets are right' do
94
+ fct = @cluster.facet(:esnode)
95
+ fct.to_hash.should == {
96
+ :name => :esnode,
97
+ :run_list => ["role[nginx]", "role[redis_server]", "role[elasticsearch_data_esnode]", "role[elasticsearch_http_esnode]", "role[webserver_demo_esnode]"],
98
+ :chef_attributes => {},
99
+ :facet_role => "webserver_demo_esnode",
100
+ :instances => 1,
101
+ }
102
+ fct.cloud.flavor.should == 'm1.large'
103
+ end
104
+
105
+ it 'cluster security groups are right' do
106
+ gg = @cluster.security_groups
107
+ gg.keys.should == ['default', 'ssh', 'nfs_client', 'webserver_demo']
108
+ end
109
+
110
+ it 'facet webnode security groups are right' do
111
+ gg = @cluster.facet(:webnode).security_groups
112
+ gg.keys.sort.should == ["default", "webserver_demo", "webserver_demo-awesome_website", "webserver_demo-redis_client", "webserver_demo-webnode", "nfs_client", "ssh"]
113
+ gg['webserver_demo-awesome_website'].range_authorizations.should == [[80..80, "0.0.0.0/0", "tcp"], [443..443, "0.0.0.0/0", "tcp"]]
114
+ end
115
+
116
+ it 'facet dbnode security groups are right' do
117
+ gg = @cluster.facet(:dbnode).security_groups
118
+ gg.keys.sort.should == ["default", "webserver_demo", "webserver_demo-dbnode", "webserver_demo-redis_client", "nfs_client", "ssh"]
119
+ end
120
+
121
+ it 'facet esnode security groups are right' do
122
+ gg = @cluster.facet(:esnode).security_groups
123
+ gg.keys.sort.should == ["default", "webserver_demo", "webserver_demo-esnode", "webserver_demo-redis_server", "nfs_client", "ssh"]
124
+ gg['webserver_demo-redis_server'][:name].should == "webserver_demo-redis_server"
125
+ gg['webserver_demo-redis_server'][:description].should == "cluster_chef generated group webserver_demo-redis_server"
126
+ gg['webserver_demo-redis_server'].group_authorizations.should == [['webserver_demo-redis_client', nil]]
127
+ end
128
+
129
+ it 'has servers' do
130
+ @cluster.servers.map(&:fullname).should == [
131
+ "webserver_demo-dbnode-0", "webserver_demo-dbnode-1",
132
+ "webserver_demo-esnode-0",
133
+ "webserver_demo-webnode-0", "webserver_demo-webnode-1", "webserver_demo-webnode-2", "webserver_demo-webnode-3", "webserver_demo-webnode-4", "webserver_demo-webnode-5"
134
+ ]
135
+ end
136
+
137
+ describe 'resolving servers gets right' do
138
+ before do
139
+ @server = @cluster.slice(:webnode, 5).first
140
+ @server.cloud.stub!(:validation_key).and_return("I_AM_VALID")
141
+ @server.resolve!
142
+ end
143
+
144
+ it 'attributes' do
145
+ @server.to_hash.should == {
146
+ :name => 'webserver_demo-webnode-5',
147
+ :run_list => ["role[base_role]", "role[chef_client]", "role[ssh]", "role[nfs_client]", "role[big_package]", "role[webserver_demo_cluster]", "role[nginx]", "role[redis_client]", "role[mysql_client]", "role[elasticsearch_client]", "role[awesome_website]", "role[webserver_demo_webnode]"],
148
+ :instances => 6,
149
+ :chef_attributes => {
150
+ :split_testing => {:group=>"B"},
151
+ :webnode_count => 6,
152
+ :node_name => "webserver_demo-webnode-5",
153
+ :cluster_name => :webserver_demo, :facet_name => :webnode, :facet_index => 5,
154
+ },
155
+ }
156
+ end
157
+
158
+ it 'security groups' do
159
+ @server.security_groups.keys.sort.should == ['default', 'webserver_demo', 'webserver_demo-awesome_website', 'webserver_demo-redis_client', 'webserver_demo-webnode', 'nfs_client', 'ssh']
160
+ end
161
+ it 'run list' do
162
+ @server.run_list.should == ["role[base_role]", "role[chef_client]", "role[ssh]", "role[nfs_client]", "role[big_package]", "role[webserver_demo_cluster]", "role[nginx]", "role[redis_client]", "role[mysql_client]", "role[elasticsearch_client]", "role[awesome_website]", "role[webserver_demo_webnode]"]
163
+ end
164
+
165
+ it 'user_data' do
166
+ @server.cloud.user_data.should == {
167
+ "chef_server" => "https://api.opscode.com/organizations/infochimps",
168
+ "validation_client_name" => "chef-validator",
169
+ "validation_key" => "I_AM_VALID",
170
+ }
171
+ end
172
+
173
+ it 'cloud settings' do
174
+ hsh = @server.cloud.to_hash
175
+ hsh.delete(:security_groups)
176
+ hsh.delete(:user_data)
177
+ hsh.should == {
178
+ :availability_zones => ["us-east-1c"],
179
+ :region => "us-east-1",
180
+ :flavor => "t1.micro",
181
+ :image_name => "maverick",
182
+ :backing => "ebs",
183
+ :disable_api_termination => false,
184
+ :public_ip => false,
185
+ :bootstrap_distro => "ubuntu10.04-cluster_chef",
186
+ :keypair => :webserver_demo,
187
+ }
188
+ end
189
+
190
+ end
191
+ end
192
+ end
193
+ end