dnapi 1.1.74.jruby192.c

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 (53) hide show
  1. data/lib/dnapi.rb +54 -0
  2. data/lib/dnapi/app.rb +130 -0
  3. data/lib/dnapi/component.rb +50 -0
  4. data/lib/dnapi/component_possessor.rb +49 -0
  5. data/lib/dnapi/components/addons.rb +26 -0
  6. data/lib/dnapi/components/apache.rb +13 -0
  7. data/lib/dnapi/components/cloudkick.rb +13 -0
  8. data/lib/dnapi/components/encrypted_backup.rb +12 -0
  9. data/lib/dnapi/components/exim.rb +10 -0
  10. data/lib/dnapi/components/monitor.rb +12 -0
  11. data/lib/dnapi/components/nagios.rb +28 -0
  12. data/lib/dnapi/components/newrelic.rb +12 -0
  13. data/lib/dnapi/components/passenger3.rb +13 -0
  14. data/lib/dnapi/components/ruby.rb +234 -0
  15. data/lib/dnapi/components/ssmtp.rb +10 -0
  16. data/lib/dnapi/components/stunneled.rb +13 -0
  17. data/lib/dnapi/components/volume.rb +32 -0
  18. data/lib/dnapi/cron.rb +5 -0
  19. data/lib/dnapi/db_stack.rb +92 -0
  20. data/lib/dnapi/ebuild_dep.rb +5 -0
  21. data/lib/dnapi/environment.rb +327 -0
  22. data/lib/dnapi/extensions.rb +32 -0
  23. data/lib/dnapi/gem_dep.rb +9 -0
  24. data/lib/dnapi/instance.rb +69 -0
  25. data/lib/dnapi/monitoring.rb +22 -0
  26. data/lib/dnapi/recipe.rb +27 -0
  27. data/lib/dnapi/ssl_cert.rb +13 -0
  28. data/lib/dnapi/stack.rb +111 -0
  29. data/lib/dnapi/struct.rb +149 -0
  30. data/lib/dnapi/test.rb +114 -0
  31. data/lib/dnapi/test/ext.rb +32 -0
  32. data/lib/dnapi/test/sweatshop.rb +148 -0
  33. data/lib/dnapi/version.rb +3 -0
  34. data/lib/dnapi/vhost.rb +24 -0
  35. data/spec/app_spec.rb +68 -0
  36. data/spec/component_spec.rb +66 -0
  37. data/spec/components/addons_spec.rb +33 -0
  38. data/spec/components/cloudkick_spec.rb +17 -0
  39. data/spec/components/nagios_spec.rb +42 -0
  40. data/spec/components/nodejs_spec.rb +27 -0
  41. data/spec/components/passenger3_spec.rb +12 -0
  42. data/spec/components/ruby_spec.rb +321 -0
  43. data/spec/components/stunneled.rb +15 -0
  44. data/spec/components/volume_spec.rb +21 -0
  45. data/spec/db_stack_spec.rb +111 -0
  46. data/spec/environment_spec.rb +227 -0
  47. data/spec/instance_spec.rb +52 -0
  48. data/spec/proxies.rb +143 -0
  49. data/spec/proxies_spec.rb +76 -0
  50. data/spec/spec_helper.rb +2 -0
  51. data/spec/stack_spec.rb +105 -0
  52. data/spec/struct_spec.rb +100 -0
  53. metadata +181 -0
@@ -0,0 +1,227 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe DNApi::Environment do
4
+
5
+ def instance_info
6
+ @id ||= 0
7
+ @id += 1
8
+ {:id => @id,
9
+ :public_hostname => "public_host.#{@id}",
10
+ :private_hostname => "private_host.#{@id}"}
11
+ end
12
+
13
+ def legacy_provision_instance
14
+ require 'ostruct'
15
+ OpenStruct.new(instance_info)
16
+ end
17
+
18
+ describe "#db_host" do
19
+ it 'is the public hostname of the solo instance if the environment consists of a solo instance' do
20
+ environment = DNApi::Environment.new
21
+ environment.build_instance(instance_info.merge(:role => :solo))
22
+ environment.db_host.should eql('public_host.1')
23
+ end
24
+
25
+ it 'is the public hostname of the database server' do
26
+ environment = DNApi::Environment.new
27
+ db_instance_info = instance_info
28
+ environment.build_instance(db_instance_info.merge(:role => :db_master))
29
+ environment.db_host.should eql(db_instance_info[:public_hostname])
30
+ end
31
+
32
+ it "responds to legacy 'api'" do
33
+ environment = DNApi::Environment.new
34
+ instance = legacy_provision_instance
35
+ environment.build_instance(:role => :db_master, :provisioned => instance)
36
+ environment.db_host.should eql(instance.public_hostname)
37
+ end
38
+ end
39
+
40
+ describe "#db_slaves" do
41
+ it 'is an array of all instances with the db_slave role' do
42
+ environment = DNApi::Environment.new
43
+ environment.build_instance(instance_info.merge(:role => :app_master))
44
+ environment.build_instance(instance_info.merge(:role => :db_master))
45
+ environment.build_instance(instance_info.merge(:role => :db_slave))
46
+ environment.build_instance(instance_info.merge(:role => :db_slave))
47
+ environment.build_instance(instance_info.merge(:role => :db_slave))
48
+ environment.should have(3).db_slaves
49
+ end
50
+ end
51
+
52
+ describe "#utility_instances" do
53
+ it 'is an array of all instances with the utility_instances role' do
54
+ environment = DNApi::Environment.new
55
+ environment.build_instance(instance_info.merge(:role => :app_master))
56
+ environment.build_instance(instance_info.merge(:role => :db_master))
57
+ environment.build_instance(instance_info.merge(:role => :db_slave))
58
+ environment.build_instance(instance_info.merge(:role => :util, :name => "foo"))
59
+ environment.build_instance(instance_info.merge(:role => :util, :name => "bar"))
60
+ environment.should have(2).utility_instances
61
+ end
62
+ end
63
+
64
+ describe '#recipes' do
65
+ it 'includes the stack recipes' do
66
+ env = DNApi::Environment.new(:stack => DNApi::Stack::NginxMongrel)
67
+ env.build_app
68
+ env.recipes.should include(*DNApi::Stack::NginxMongrel.recipes)
69
+ end
70
+
71
+ it 'includes the app recipes' do
72
+ env = DNApi::Environment.new(:stack => DNApi::Stack::NginxMongrel)
73
+ env.build_app
74
+ env.recipes.should include(*DNApi::Recipe.defaults)
75
+ end
76
+ end
77
+
78
+ describe '#mysql_backup_cron' do
79
+ it 'is nil if there is no backup_window' do
80
+ env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Mysql)
81
+ env.mysql_backup_cron.should be_nil
82
+ end
83
+
84
+ it 'runs eybackup --quiet' do
85
+ env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Mysql)
86
+ env.mysql_backup_cron.command.should == 'eybackup --quiet'
87
+ end
88
+
89
+ it 'is 10 past the hour' do
90
+ env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Mysql)
91
+ env.mysql_backup_cron.minute.should == '10'
92
+ end
93
+
94
+ it 'is at 1 AM PST if the backup interal is 24 hours' do
95
+ env = DNApi::Environment.new(:backup_window => 1, :backup_interval => 24, :db_stack => DNApi::DbStack::Mysql)
96
+ env.mysql_backup_cron.hour.should == '1'
97
+ end
98
+
99
+ it 'is at every other hour if the backup interval is 12 hours' do
100
+ env = DNApi::Environment.new(:backup_window => 1, :backup_interval => 12, :db_stack => DNApi::DbStack::Mysql)
101
+ env.mysql_backup_cron.hour.should == '*/12'
102
+ end
103
+ end
104
+
105
+ describe '#snapshot_cron' do
106
+ it 'runs ey-snapshots --snapshot --quiet' do
107
+ env = DNApi::Environment.new(:backup_window => 1)
108
+ env.snapshot_cron.command.should == 'ey-snapshots --snapshot --quiet'
109
+ end
110
+ it 'is in the last half of the hour' do
111
+ env = DNApi::Environment.new(:backup_window => 1)
112
+ env.snapshot_cron.minute.should =~ %r{0/[3-5]\d}
113
+ end
114
+ end
115
+
116
+ describe '#postgres_backup_cron' do
117
+ it 'runs eybackup -e postgresql --quiet' do
118
+ env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Postgres)
119
+ env.postgres_backup_cron.command.should == 'eybackup -e postgresql --quiet'
120
+ end
121
+
122
+ it 'is 20 past the hour' do
123
+ env = DNApi::Environment.new(:backup_window => 1, :db_stack => DNApi::DbStack::Postgres)
124
+ env.postgres_backup_cron.minute.should == '20'
125
+ end
126
+
127
+ context "disabled backups" do
128
+ it 'returns nil when backups are disabled on Postgres DbStack' do
129
+ env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres)
130
+ env.postgres_backup_cron.should be_nil
131
+ end
132
+
133
+ it 'returns nil when backups are disabled on Postgres9 DbStack' do
134
+ env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres9)
135
+ env.postgres_backup_cron.should be_nil
136
+ end
137
+
138
+ it 'returns nil when backups are disabled on Postgres91 DbStack' do
139
+ env = DNApi::Environment.new(:backup_window => 0, :db_stack => DNApi::DbStack::Postgres91)
140
+ env.postgres_backup_cron.should be_nil
141
+ end
142
+ end
143
+ end
144
+
145
+ it "has newrelic" do
146
+ env = DNApi::Environment.new
147
+
148
+ env.newrelic_key = "foo"
149
+ env.newrelic_key.should == "foo"
150
+
151
+ env.build_app(:newrelic => true)
152
+ env.apps.first.newrelic?.should be_true
153
+
154
+ env.build_app.newrelic?.should be_false
155
+ end
156
+
157
+ it "ignores disabled instances when listing enabled app servers hostnames" do
158
+ env = DNApi.gen(:default, 'nginx_passenger', ['rails'])
159
+ env.build_instance(instance_info.merge(:role => :db_master, :enabled => false))
160
+ env.build_instance(instance_info.merge(:role => :app_master, :enabled => false))
161
+ instance = env.build_instance(instance_info.merge(:role => :app, :enabled => false))
162
+ env.enabled_app_servers_hostnames.should be_empty
163
+
164
+ dna = JSON.parse(instance.to_dna.to_json)
165
+ dna["members"].should be_empty
166
+ end
167
+
168
+ describe '#backup_window' do
169
+ it 'should default to 14' do
170
+ DNApi::Environment.new.backup_window.should == 14
171
+ end
172
+ end
173
+
174
+ describe "#dna_for_instance" do
175
+ it "raises an error when the instance is not found" do
176
+ env = DNApi.gen(:default, 'nginx_passenger', [])
177
+ instance = env.build_instance(instance_info.merge(:role => :solo))
178
+ lambda { DNApi::Environment.new.dna_for_instance(instance) }.
179
+ should raise_error(DNApi::InstanceNotFound)
180
+ end
181
+ end
182
+
183
+ specify "monitoring" do
184
+ default = DNApi::Environment.new
185
+ default.monitoring.should be_monit
186
+ default.monitoring = "god"
187
+ default.monitoring.should be_god
188
+
189
+ monit = DNApi::Environment.new(:monitoring => "monit").monitoring
190
+ monit.should be_monit
191
+ monit.name.should == "monit"
192
+ monit.label.should == "Monit"
193
+
194
+ god = DNApi::Environment.new(:monitoring => "god").monitoring
195
+ god.should be_god
196
+ god.name.should == "god"
197
+ god.label.should == "god"
198
+ end
199
+
200
+ specify "components" do
201
+ env = DNApi::Environment.new
202
+ env.components.should be_empty
203
+ env.components << DNApi::Component[:god].new
204
+ env.has_component?(:god).should be_true
205
+ end
206
+
207
+ describe "#components" do
208
+ it 'dumps components into a hash' do
209
+ env = DNApi::Environment.new
210
+ env.add_component(:god)
211
+ DNApi::Environment.from(JSON.parse(env.to_json)).components.should_not be_empty
212
+ end
213
+ end
214
+
215
+ describe "#ruby_component" do
216
+ before(:each) do
217
+ @env = DNApi::Environment.new
218
+ end
219
+
220
+ DNApi::Components::RubyVersion.all.each do |ruby_component|
221
+ it "returns #{ruby_component.key_id} when appropriate" do
222
+ @env.add_component(ruby_component.key_id)
223
+ @env.ruby_component.should == @env.component(ruby_component.key_id)
224
+ end
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,52 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe DNApi::Instance do
4
+ before(:each) do
5
+ @environment = DNApi::Environment.new
6
+ @instance = @environment.build_instance(:id => 'default', :role => :app_master)
7
+ end
8
+
9
+ it 'has selectors for environment data' do
10
+ @instance.apps.should == @environment.apps
11
+ @instance.recipes.should == @environment.recipes
12
+ @instance.solo?.should == @environment.solo?
13
+ @instance.ssh_username == @environment.ssh_username
14
+ @instance.ssh_password == @environment.ssh_password
15
+ end
16
+
17
+ it "has newrelic attributes when it has the :newrelic component" do
18
+ @instance.add_component(:newrelic, :seed => 'bar', :token => 'sekretz')
19
+
20
+ @instance.newrelic_seed.should == "bar"
21
+ @instance.newrelic_token.should == "sekretz"
22
+ end
23
+
24
+ it "lacks newrelic attributes when it lacks the :newrelic component" do
25
+ @instance.should_not have_component(:newrelic)
26
+ @instance.newrelic_token.should be_nil
27
+ end
28
+
29
+ it "allows adding a ssmtp component" do
30
+ @instance.add_component(:ssmtp)
31
+ @instance.should have_component(:ssmtp)
32
+ end
33
+
34
+ it "allows adding a ssmtp component" do
35
+ @instance.add_component(:exim, :user => "test", :password => "suck", :host => "example.org", :outbound_host => "sendgrid")
36
+
37
+ @instance.should have_component(:exim)
38
+
39
+ exim = @instance.component(:exim)
40
+ exim.user.should == "test"
41
+ exim.password.should == "suck"
42
+ exim.host.should == "example.org"
43
+ exim.outbound_host.should == "sendgrid"
44
+ end
45
+
46
+ it 'allows adding multiple volume components' do
47
+ @instance.add_component(:ebs_volume, :mountpoint => '/data', :size => 5 * 1024 * 1024)
48
+ @instance.add_component(:ebs_volume, :mountpoint => '/db', :size => 5 * 1024 * 1024)
49
+
50
+ @instance.volumes.size.should == 2
51
+ end
52
+ end
data/spec/proxies.rb ADDED
@@ -0,0 +1,143 @@
1
+ class Object
2
+ def deep_clone
3
+ Marshal.load(Marshal.dump(self))
4
+ end
5
+ end
6
+
7
+ module DNApi
8
+ class ArrayProxy
9
+ def self.build(array)
10
+ new(array).build
11
+ end
12
+
13
+ def initialize(array)
14
+ @array = array
15
+ end
16
+
17
+ def build
18
+ @positions = (0..(@array.size - 1)).to_a
19
+ @original_array = @array.deep_clone
20
+ @array.map! do |value|
21
+ case value
22
+ when Hash
23
+ HashProxy.build(value)
24
+ when Array
25
+ ArrayProxy.build(value)
26
+ else
27
+ value
28
+ end
29
+ end
30
+ self
31
+ end
32
+
33
+ def index_of(&block)
34
+ @original_array.each_with_index do |value,i|
35
+ return i if yield value
36
+ end
37
+ raise "Could not find the element in #{inspect}"
38
+ end
39
+
40
+ def size
41
+ @array.size
42
+ end
43
+
44
+ def empty?
45
+ @array.empty?
46
+ end
47
+
48
+ def fully_accessed?
49
+ @positions.empty? && unaccessed_children.empty?
50
+ end
51
+
52
+ def unaccessed
53
+ {:positions => @positions, :children => unaccessed_children}
54
+ end
55
+
56
+ def unaccessed_children
57
+ foo = {}
58
+ @array.each_with_index do |value,i|
59
+ case value
60
+ when HashProxy, ArrayProxy
61
+ foo[i] = value.unaccessed unless value.fully_accessed?
62
+ when Hash, Array
63
+ raise "This should be a #{value.class}Proxy: #{value.inspect}"
64
+ end
65
+ end
66
+ foo
67
+ end
68
+
69
+ def [](position)
70
+ @positions.delete(position)
71
+ @array[position]
72
+ end
73
+
74
+ def inspect
75
+ @array.inspect
76
+ end
77
+ end
78
+
79
+ class HashProxy
80
+ def self.build(hash)
81
+ new(hash).build
82
+ end
83
+
84
+ def initialize(hash)
85
+ @hash = hash
86
+ end
87
+
88
+ def build
89
+ @keys = @hash.keys
90
+ @hash.each do |key,value|
91
+ case value
92
+ when Hash
93
+ @hash[key] = HashProxy.build(value)
94
+ when Array
95
+ @hash[key] = ArrayProxy.build(value)
96
+ end
97
+ end
98
+ self
99
+ end
100
+
101
+ def has_key?(key)
102
+ @hash.has_key?(key)
103
+ end
104
+
105
+ def keys
106
+ @hash.keys
107
+ end
108
+
109
+ def any?
110
+ @hash.any?
111
+ end
112
+
113
+ def fully_accessed?
114
+ @keys.empty? && unaccessed_children.empty?
115
+ end
116
+
117
+ def unaccessed
118
+ {:keys => @keys, :children => unaccessed_children}
119
+ end
120
+
121
+ def unaccessed_children
122
+ foo = {}
123
+ @hash.each do |key,value|
124
+ case value
125
+ when HashProxy, ArrayProxy
126
+ foo[key] = value.unaccessed unless value.fully_accessed?
127
+ when Hash, Array
128
+ raise "This should be a #{value.class}Proxy: #{key.inspect} #{value.inspect}"
129
+ end
130
+ end
131
+ foo
132
+ end
133
+
134
+ def [](key)
135
+ @keys.delete(key)
136
+ @hash[key]
137
+ end
138
+
139
+ def inspect
140
+ @hash.inspect
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,76 @@
1
+ require File.dirname(__FILE__) + '/proxies'
2
+ require 'pp'
3
+ require 'sexp_processor' #defines Object#deep_copy
4
+
5
+ describe DNApi::HashProxy do
6
+ describe "with no elements" do
7
+ it "is fully accessed" do
8
+ DNApi::HashProxy.build({}).should be_fully_accessed
9
+ end
10
+ end
11
+
12
+ describe "with a single element" do
13
+ before(:each) do
14
+ @hash = DNApi::HashProxy.build({:foo => :bar})
15
+ end
16
+
17
+ it "is not fully accessed" do
18
+ @hash.should_not be_fully_accessed
19
+ end
20
+
21
+ describe "which has been accessed" do
22
+ it "is fully accessed" do
23
+ @hash[:foo]
24
+ @hash.should be_fully_accessed
25
+ end
26
+ end
27
+ end
28
+
29
+ describe "with a nested hash" do
30
+ it "should not be fully accessed unless it's children have been fully accessed" do
31
+ @hash = DNApi::HashProxy.build({:foo => {:bar => :baz}})
32
+ lambda { @hash[:foo] }.should_not change { @hash.fully_accessed? }
33
+ @hash.unaccessed[:keys].should be_empty
34
+ @hash.unaccessed[:children].should == {:foo => {:keys => [:bar], :children => {}}}
35
+ @hash[:foo][:bar]
36
+ @hash.should be_fully_accessed
37
+ end
38
+ end
39
+
40
+ describe "with a nested array" do
41
+ it "should not be fully accessed until it's elements have been accessed" do
42
+ @hash = DNApi::HashProxy.build({:foo => [:bar, :baz]})
43
+ lambda { @hash[:foo] }.should_not change { @hash.fully_accessed? }
44
+ @hash.unaccessed[:keys].should be_empty
45
+ @hash.unaccessed[:children].should == {:foo => {:positions => [0, 1], :children => {}}}
46
+ @hash[:foo][0]
47
+ @hash[:foo][1]
48
+ @hash.should be_fully_accessed
49
+ end
50
+ end
51
+ end
52
+
53
+ describe DNApi::ArrayProxy do
54
+ describe "with no elements" do
55
+ it "is fully accessed" do
56
+ DNApi::ArrayProxy.build([]).should be_fully_accessed
57
+ end
58
+ end
59
+
60
+ describe "with a single element" do
61
+ before(:each) do
62
+ @array = DNApi::ArrayProxy.build([:foo])
63
+ end
64
+
65
+ it "is not fully accessed" do
66
+ @array.should_not be_fully_accessed
67
+ end
68
+
69
+ describe "which has been accessed" do
70
+ it "is fully accessed" do
71
+ @array[0]
72
+ @array.should be_fully_accessed
73
+ end
74
+ end
75
+ end
76
+ end