the-maestro 0.2.0

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 (60) hide show
  1. data/.document +5 -0
  2. data/.gitignore +25 -0
  3. data/LICENSE +23 -0
  4. data/README.rdoc +378 -0
  5. data/Rakefile +116 -0
  6. data/VERSION +1 -0
  7. data/lib/maestro.rb +354 -0
  8. data/lib/maestro/cloud.rb +384 -0
  9. data/lib/maestro/cloud/aws.rb +1231 -0
  10. data/lib/maestro/dsl_property.rb +15 -0
  11. data/lib/maestro/log4r/console_formatter.rb +18 -0
  12. data/lib/maestro/log4r/file_formatter.rb +24 -0
  13. data/lib/maestro/node.rb +123 -0
  14. data/lib/maestro/operating_system.rb +53 -0
  15. data/lib/maestro/operating_system/cent_os.rb +23 -0
  16. data/lib/maestro/operating_system/debian.rb +40 -0
  17. data/lib/maestro/operating_system/fedora.rb +23 -0
  18. data/lib/maestro/operating_system/ubuntu.rb +100 -0
  19. data/lib/maestro/role.rb +36 -0
  20. data/lib/maestro/tasks.rb +52 -0
  21. data/lib/maestro/validator.rb +32 -0
  22. data/rails/init.rb +1 -0
  23. data/test/integration/base_aws.rb +156 -0
  24. data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.json +41 -0
  25. data/test/integration/fixtures/config/maestro/cookbooks/emacs/metadata.rb +3 -0
  26. data/test/integration/fixtures/config/maestro/cookbooks/emacs/recipes/default.rb +21 -0
  27. data/test/integration/fixtures/config/maestro/roles/default.json +9 -0
  28. data/test/integration/fixtures/config/maestro/roles/web.json +9 -0
  29. data/test/integration/helper.rb +8 -0
  30. data/test/integration/test_aws_cloud.rb +805 -0
  31. data/test/integration/test_cent_os.rb +104 -0
  32. data/test/integration/test_debian.rb +119 -0
  33. data/test/integration/test_fedora.rb +104 -0
  34. data/test/integration/test_ubuntu.rb +149 -0
  35. data/test/unit/fixtures/invalid-clouds-not-a-directory/config/maestro/clouds +1 -0
  36. data/test/unit/fixtures/invalid-cookbooks-not-a-directory/config/maestro/cookbooks +0 -0
  37. data/test/unit/fixtures/invalid-maestro-not-a-directory/config/maestro +0 -0
  38. data/test/unit/fixtures/invalid-missing-cookbooks/config/maestro/clouds/valid.yml +21 -0
  39. data/test/unit/fixtures/invalid-missing-roles/config/maestro/clouds/valid.yml +21 -0
  40. data/test/unit/fixtures/invalid-roles-not-a-directory/config/maestro/roles +1 -0
  41. data/test/unit/fixtures/ssh/id_rsa-maestro-test-keypair +27 -0
  42. data/test/unit/helper.rb +6 -0
  43. data/test/unit/test_aws_cloud.rb +133 -0
  44. data/test/unit/test_aws_ec2_node.rb +76 -0
  45. data/test/unit/test_aws_elb_node.rb +221 -0
  46. data/test/unit/test_aws_rds_node.rb +380 -0
  47. data/test/unit/test_cent_os.rb +28 -0
  48. data/test/unit/test_cloud.rb +142 -0
  49. data/test/unit/test_debian.rb +62 -0
  50. data/test/unit/test_fedora.rb +28 -0
  51. data/test/unit/test_invalid_mode.rb +11 -0
  52. data/test/unit/test_maestro.rb +140 -0
  53. data/test/unit/test_node.rb +50 -0
  54. data/test/unit/test_operating_system.rb +19 -0
  55. data/test/unit/test_rails_mode.rb +77 -0
  56. data/test/unit/test_role.rb +59 -0
  57. data/test/unit/test_standalone_mode.rb +75 -0
  58. data/test/unit/test_ubuntu.rb +95 -0
  59. data/the-maestro.gemspec +150 -0
  60. metadata +228 -0
@@ -0,0 +1,380 @@
1
+ require 'helper'
2
+
3
+ # Unit tests for Maestro::Node::Aws::Rds
4
+ class TestAwsRdsNode < Test::Unit::TestCase
5
+
6
+ context "Maestro::Node::Aws::Rds" do
7
+ setup do
8
+ @cloud = aws_cloud :test do
9
+ roles {}
10
+ nodes do
11
+ ec2_node "web-1" do end
12
+ rds_node "db-1" do
13
+ availability_zone "us-east-1"
14
+ engine "MySQL5.1"
15
+ db_instance_class "db.m1.small"
16
+ master_username "root"
17
+ master_user_password "password"
18
+ port 3306
19
+ allocated_storage 5
20
+ preferred_maintenance_window "Sun:03:00-Sun:07:00"
21
+ backup_retention_period 7
22
+ preferred_backup_window "03:00-05:00"
23
+ db_parameters [{:name => "some_param", :value => "1"},
24
+ {:name => "some_other_param", :value => "foo"}]
25
+ end
26
+ end
27
+ end
28
+ @node = @cloud.nodes["db-1"]
29
+ end
30
+
31
+ should "be an Rds node" do
32
+ assert @node.is_a? Maestro::Node::Aws::Rds
33
+ end
34
+
35
+ should "be invalid due to name too long" do
36
+ cloud = aws_cloud :test do
37
+ roles {}
38
+ nodes do
39
+ ec2_node "web-1" do end
40
+ rds_node "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1234" do end
41
+ end
42
+ end
43
+ node = cloud.nodes["XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX1234"]
44
+ node.validate
45
+ assert !node.valid?
46
+ assert node.validation_errors.any? {|message| !message.index("name must be less than 64 characters").nil?}
47
+ end
48
+
49
+ should "be invalid due to name containing invalid characters" do
50
+ "!@\#$%^&*()_=+~`\"{[}]\\|/?.>,<".each_char do |char|
51
+ cloud = aws_cloud :test do
52
+ roles {}
53
+ nodes do
54
+ ec2_node "web-1" do end
55
+ rds_node "#{char}" do end
56
+ end
57
+ end
58
+ node = cloud.nodes["#{char}"]
59
+ node.validate
60
+ assert !node.valid?
61
+ assert node.validation_errors.any? {|message| !message.index("name may only contain alphanumerics and hyphens").nil?}
62
+ end
63
+ end
64
+
65
+ should "be invalid due to name not starting with a letter" do
66
+ "0123456789!@\#$%^&*()_=+~`\"{[}]\\|/?.>,<".each_char do |char|
67
+ cloud = aws_cloud :test do
68
+ roles {}
69
+ nodes do
70
+ ec2_node "web-1" do end
71
+ rds_node "#{char}" do end
72
+ end
73
+ end
74
+ node = cloud.nodes["#{char}"]
75
+ node.validate
76
+ assert !node.valid?
77
+ assert node.validation_errors.any? {|message| !message.index("name must start with a letter").nil?}
78
+ end
79
+ end
80
+
81
+ should "be invalid due to name ending with hyphen" do
82
+ cloud = aws_cloud :test do
83
+ roles {}
84
+ nodes do
85
+ ec2_node "web-1" do end
86
+ rds_node "test-" do end
87
+ end
88
+ end
89
+ node = cloud.nodes["test-"]
90
+ node.validate
91
+ assert !node.valid?
92
+ assert node.validation_errors.any? {|message| !message.index("name must not end with a hypen").nil?}
93
+ end
94
+
95
+ should "be invalid due to name containing two consecutive hyphens" do
96
+ cloud = aws_cloud :test do
97
+ roles {}
98
+ nodes do
99
+ ec2_node "web-1" do end
100
+ rds_node "te--st" do end
101
+ end
102
+ end
103
+ node = cloud.nodes["te--st"]
104
+ node.validate
105
+ assert !node.valid?
106
+ assert node.validation_errors.any? {|message| !message.index("name must not contain two consecutive hyphens").nil?}
107
+ end
108
+
109
+ should "be invalid due to missing availability_zone" do
110
+ @node.availability_zone nil
111
+ @node.validate
112
+ assert !@node.valid?
113
+ assert @node.validation_errors.any? {|message| !message.index("node missing availability_zone").nil?}
114
+ end
115
+
116
+ should "be invalid due to missing db_instance_class" do
117
+ @node.db_instance_class nil
118
+ @node.validate
119
+ assert !@node.valid?
120
+ assert @node.validation_errors.any? {|message| !message.index("node missing db_instance_class").nil?}
121
+ end
122
+
123
+ should "be invalid due to invalid db_instance_class" do
124
+ @node.db_instance_class "foo"
125
+ @node.validate
126
+ assert !@node.valid?
127
+ assert @node.validation_errors.any? {|message| !message.index("node db_instance_class is invalid").nil?}
128
+ end
129
+
130
+ should "be valid db_instance_classes" do
131
+ ["db.m1.small", "db.m1.large", "db.m1.xlarge", "db.m2.2xlarge", "db.m2.4xlarge"].each do |ic|
132
+ @node.validation_errors.clear
133
+ @node.db_instance_class ic
134
+ @node.validate
135
+ assert !@node.validation_errors.any? {|message| !message.index("node db_instance_class is invalid").nil?}
136
+ end
137
+ end
138
+
139
+ should "be invalid due to missing engine" do
140
+ @node.engine nil
141
+ @node.validate
142
+ assert !@node.valid?
143
+ assert @node.validation_errors.any? {|message| !message.index("node missing engine").nil?}
144
+ end
145
+
146
+ should "be invalid due to invalid engine" do
147
+ @node.engine "foo"
148
+ @node.validate
149
+ assert !@node.valid?
150
+ assert @node.validation_errors.any? {|message| !message.index("node engine is invalid").nil?}
151
+ end
152
+
153
+ should "be valid engines" do
154
+ ["MySQL5.1"].each do |e|
155
+ @node.validation_errors.clear
156
+ @node.engine e
157
+ @node.validate
158
+ assert !@node.validation_errors.any? {|message| !message.index("node engine is invalid").nil?}
159
+ end
160
+ end
161
+
162
+ should "be invalid due to missing master_username" do
163
+ @node.master_username nil
164
+ @node.validate
165
+ assert !@node.valid?
166
+ assert @node.validation_errors.any? {|message| !message.index("node missing master_username").nil?}
167
+ end
168
+
169
+ should "be invalid due to master_username too long" do
170
+ @node.master_username "XXXXXXXXXXXXXXXX"
171
+ @node.validate
172
+ assert !@node.valid?
173
+ assert @node.validation_errors.any? {|message| !message.index("master_username must be less than 16 characters").nil?}
174
+ end
175
+
176
+ should "be invalid due to master_username not starting with a letter" do
177
+ "0123456789!@\#$%^&*()_=+~`\"{[}]\\|/?.>,<".each_char do |char|
178
+ @node.validation_errors.clear
179
+ @node.master_username "#{char}"
180
+ @node.validate
181
+ assert !@node.valid?
182
+ assert @node.validation_errors.any? {|message| !message.index("master_username must start with a letter").nil?}
183
+ end
184
+ end
185
+
186
+ should "be invalid due to master_username containing invalid characters" do
187
+ "!@\#$%^&*()-_=+~`\"{[}]\\|/?.>,<".each_char do |char|
188
+ @node.validation_errors.clear
189
+ @node.master_username "a1#{char}9Z"
190
+ @node.validate
191
+ assert !@node.valid?
192
+ assert @node.validation_errors.any? {|message| !message.index("master_username may only contain alphanumerics").nil?}
193
+ end
194
+ end
195
+
196
+ should "be invalid due to missing master_user_password" do
197
+ @node.master_user_password nil
198
+ @node.validate
199
+ assert !@node.valid?
200
+ assert @node.validation_errors.any? {|message| !message.index("node missing master_user_password").nil?}
201
+ end
202
+
203
+ should "be invalid due to master_user_password too short" do
204
+ @node.master_user_password "X"
205
+ @node.validate
206
+ assert !@node.valid?
207
+ assert @node.validation_errors.any? {|message| !message.index("master_user_password must be between 4 and 16 characters in length").nil?}
208
+ end
209
+
210
+ should "be invalid due to master_user_password too long" do
211
+ @node.master_user_password "XXXXXXXXXXXXXXXXX"
212
+ @node.validate
213
+ assert !@node.valid?
214
+ assert @node.validation_errors.any? {|message| !message.index("master_user_password must be between 4 and 16 characters in length").nil?}
215
+ end
216
+
217
+ should "be invalid due to master_user_password containing invalid characters" do
218
+ "!@\#$%^&*()-_=+~`\"{[}]\\|/?.>,<".each_char do |char|
219
+ @node.validation_errors.clear
220
+ @node.master_user_password "a1#{char}9Z"
221
+ @node.validate
222
+ assert !@node.valid?
223
+ assert @node.validation_errors.any? {|message| !message.index("master_user_password may only contain alphanumerics").nil?}
224
+ end
225
+ end
226
+
227
+ should "be invalid due to missing port" do
228
+ @node.port nil
229
+ @node.validate
230
+ assert !@node.valid?
231
+ assert @node.validation_errors.any? {|message| !message.index("node missing port").nil?}
232
+ end
233
+
234
+ should "be invalid due to port too low" do
235
+ @node.port 1
236
+ @node.validate
237
+ assert !@node.valid?
238
+ assert @node.validation_errors.any? {|message| !message.index("port must be between 1150 and 65535").nil?}
239
+ end
240
+
241
+ should "be invalid due to port too high" do
242
+ @node.port 111111
243
+ @node.validate
244
+ assert !@node.valid?
245
+ assert @node.validation_errors.any? {|message| !message.index("port must be between 1150 and 65535").nil?}
246
+ end
247
+
248
+ should "be invalid due to port not a number" do
249
+ @node.port false
250
+ @node.validate
251
+ assert !@node.valid?
252
+ assert @node.validation_errors.any? {|message| !message.index("port must be a number").nil?}
253
+ end
254
+
255
+ should "be invalid due to missing allocated_storage" do
256
+ @node.allocated_storage nil
257
+ @node.validate
258
+ assert !@node.valid?
259
+ assert @node.validation_errors.any? {|message| !message.index("node missing allocated_storage").nil?}
260
+ end
261
+
262
+ should "be invalid due to allocated_storage too low" do
263
+ @node.allocated_storage 1
264
+ @node.validate
265
+ assert !@node.valid?
266
+ assert @node.validation_errors.any? {|message| !message.index("allocated_storage must be between 5 and 1024").nil?}
267
+ end
268
+
269
+ should "be invalid due to allocated_storage too high" do
270
+ @node.allocated_storage 1025
271
+ @node.validate
272
+ assert !@node.valid?
273
+ assert @node.validation_errors.any? {|message| !message.index("allocated_storage must be between 5 and 1024").nil?}
274
+ end
275
+
276
+ should "be invalid due to allocated_storage not a number" do
277
+ @node.allocated_storage false
278
+ @node.validate
279
+ assert !@node.valid?
280
+ assert @node.validation_errors.any? {|message| !message.index("allocated_storage must be a number").nil?}
281
+ end
282
+
283
+ should "be invalid due to incorrect preferred_maintenance_window start day value" do
284
+ @node.preferred_maintenance_window "FOO:15:00-Sun:09:00"
285
+ @node.validate
286
+ assert !@node.valid?
287
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
288
+ end
289
+
290
+ should "be invalid due to incorrect preferred_maintenance_window start hour value" do
291
+ @node.preferred_maintenance_window "Tue:25:00-Sun:09:00"
292
+ @node.validate
293
+ assert !@node.valid?
294
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
295
+ end
296
+
297
+ should "be invalid due to incorrect preferred_maintenance_window start minute value" do
298
+ @node.preferred_maintenance_window "FOO:25:60-Sun:09:00"
299
+ @node.validate
300
+ assert !@node.valid?
301
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
302
+ end
303
+
304
+ should "be invalid due to incorrect preferred_maintenance_window end day value" do
305
+ @node.preferred_maintenance_window "Sun:15:00-Bar:09:00"
306
+ @node.validate
307
+ assert !@node.valid?
308
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
309
+ end
310
+
311
+ should "be invalid due to incorrect preferred_maintenance_window end hour value" do
312
+ @node.preferred_maintenance_window "Tue:03:30-Tue:29:00"
313
+ @node.validate
314
+ assert !@node.valid?
315
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
316
+ end
317
+
318
+ should "be invalid due to incorrect preferred_maintenance_window end minute value" do
319
+ @node.preferred_maintenance_window "Sat:03:00-Sat:03:345"
320
+ @node.validate
321
+ assert !@node.valid?
322
+ assert @node.validation_errors.any? {|message| !message.index("preferred_maintenance_window must be in UTC format 'ddd:hh24:mi-ddd:hh24:mi'").nil?}
323
+ end
324
+
325
+ should "be invalid due to backup_retention_period too low" do
326
+ @node.backup_retention_period -1
327
+ @node.validate
328
+ assert !@node.valid?
329
+ assert @node.validation_errors.any? {|message| !message.index("backup_retention_period must be between 0 and 8").nil?}
330
+ end
331
+
332
+ should "be invalid due to backup_retention_period too high" do
333
+ @node.backup_retention_period 9
334
+ @node.validate
335
+ assert !@node.valid?
336
+ assert @node.validation_errors.any? {|message| !message.index("backup_retention_period must be between 0 and 8").nil?}
337
+ end
338
+
339
+ should "be invalid due to backup_retention_period not a number" do
340
+ @node.backup_retention_period false
341
+ @node.validate
342
+ assert !@node.valid?
343
+ assert @node.validation_errors.any? {|message| !message.index("backup_retention_period must be a number").nil?}
344
+ end
345
+
346
+ should "be invalid due to incorrect preferred_backup_window start hour value" do
347
+ @node.preferred_backup_window "25:00-09:00"
348
+ @node.validate
349
+ assert !@node.valid?
350
+ assert @node.validation_errors.any? {|message| !message.index("preferred_backup_window must be in UTC format 'hh24:mi-hh24:mi'").nil?}
351
+ end
352
+
353
+ should "be invalid due to incorrect preferred_backup_window start minute value" do
354
+ @node.preferred_backup_window "03:60-09:00"
355
+ @node.validate
356
+ assert !@node.valid?
357
+ assert @node.validation_errors.any? {|message| !message.index("preferred_backup_window must be in UTC format 'hh24:mi-hh24:mi'").nil?}
358
+ end
359
+
360
+ should "be invalid due to incorrect preferred_backup_window end hour value" do
361
+ @node.preferred_backup_window "03:00-28:00"
362
+ @node.validate
363
+ assert !@node.valid?
364
+ assert @node.validation_errors.any? {|message| !message.index("preferred_backup_window must be in UTC format 'hh24:mi-hh24:mi'").nil?}
365
+ end
366
+
367
+ should "be invalid due to incorrect preferred_backup_window end minute value" do
368
+ @node.preferred_backup_window "03:00-05:60"
369
+ @node.validate
370
+ assert !@node.valid?
371
+ assert @node.validation_errors.any? {|message| !message.index("preferred_backup_window must be in UTC format 'hh24:mi-hh24:mi'").nil?}
372
+ end
373
+
374
+ should "be valid" do
375
+ @node.validate
376
+ assert @node.valid?
377
+ assert @node.validation_errors.empty?
378
+ end
379
+ end
380
+ end
@@ -0,0 +1,28 @@
1
+ require 'helper'
2
+
3
+ # Unit tests for Maestro::OperatingSystem::CentOs
4
+ class TestCentOs < Test::Unit::TestCase
5
+
6
+ context "Maestro::OperatingSystem::CentOs" do
7
+ setup do
8
+ end
9
+
10
+ context "CentOS" do
11
+ should "create from etc/issue string" do
12
+ os = Maestro::OperatingSystem.create_from_etc_issue("CentOS")
13
+ assert os.instance_of? Maestro::OperatingSystem::CentOs
14
+ end
15
+
16
+ should "respond to chef_install_script" do
17
+ os = Maestro::OperatingSystem.create_from_etc_issue("CentOS")
18
+ assert os.respond_to? :chef_install_script
19
+ end
20
+
21
+ should "respond to etc_issue_str" do
22
+ os = Maestro::OperatingSystem.create_from_etc_issue("CentOS")
23
+ assert os.respond_to? :etc_issue_string
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,142 @@
1
+ require 'helper'
2
+
3
+ # Unit tests for Maestro::Cloud
4
+ class TestCloud < Test::Unit::TestCase
5
+
6
+ context "Maestro::Cloud" do
7
+ should "return raise error due to unsupported cloud method" do
8
+ assert_raise NoMethodError do
9
+ @cloud = bogus_cloud :test do
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ context "A Cloud instance" do
16
+ setup do
17
+ @cloud = aws_cloud :test do
18
+ keypair_name "XXXXXXX-keypair"
19
+ keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
20
+
21
+ roles do
22
+ role "web" do
23
+ public_ports [80, 443]
24
+ end
25
+ end
26
+
27
+ nodes do
28
+ ec2_node "web-1" do
29
+ roles ["web"]
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+
36
+ should "raise exception on space in name" do
37
+ assert_raise StandardError do
38
+ @cloud = aws_cloud "foo bar" do
39
+ keypair_name "XXXXXXX-keypair"
40
+ keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
41
+ roles {}
42
+ nodes {}
43
+ end
44
+ end
45
+ end
46
+
47
+ should "be invalid due to missing keypair name" do
48
+ @cloud.keypair_name nil
49
+ @cloud.validate
50
+ assert !@cloud.valid?
51
+ assert @cloud.validation_errors.any? {|message| !message.index("Missing keypair_name").nil? }
52
+ end
53
+
54
+ should "be invalid due to missing keypair file" do
55
+ @cloud.keypair_file nil
56
+ @cloud.validate
57
+ assert !@cloud.valid?
58
+ assert @cloud.validation_errors.any? {|message| !message.index("Missing keypair_file").nil? }
59
+ end
60
+
61
+ should "be invalid due to missing roles" do
62
+ @cloud.roles = nil
63
+ @cloud.validate
64
+ assert !@cloud.valid?
65
+ assert @cloud.validation_errors.any? {|message| !message.index("Missing roles").nil? }
66
+ end
67
+
68
+ should "allow dynamic role creation" do
69
+ cloud = aws_cloud :test do
70
+ keypair_name "XXXXXXX-keypair"
71
+ keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
72
+
73
+ roles do
74
+ 3.times do |i|
75
+ role "web-#{i+1}" do
76
+ public_ports [80, 443]
77
+ end
78
+ end
79
+ end
80
+ nodes {}
81
+ end
82
+ assert cloud.roles.size == 3
83
+ end
84
+
85
+ should "allow dynamic node creation" do
86
+ cloud = aws_cloud :test do
87
+ keypair_name "XXXXXXX-keypair"
88
+ keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
89
+ roles {}
90
+ nodes do
91
+ 7.times do |i|
92
+ ec2_node "web-#{i+1}" do
93
+ roles ["web"]
94
+ end
95
+ end
96
+ end
97
+ end
98
+ assert cloud.nodes.size == 7
99
+ end
100
+
101
+ should "be invalid due to duplicate roles" do
102
+ cloud = aws_cloud :test do
103
+ keypair_name "XXXXXXX-keypair"
104
+ keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
105
+
106
+ roles do
107
+ role "web" do
108
+ public_ports [80, 443]
109
+ end
110
+ role "web" do
111
+ public_ports [80, 443]
112
+ end
113
+ end
114
+ nodes {}
115
+ end
116
+ cloud.validate
117
+ assert !cloud.valid?
118
+ assert cloud.validation_errors.any? {|message| !message.index("Duplicate role definition: web").nil?}
119
+ end
120
+
121
+ should "be invalid due to invalid roles, public-ports not an array" do
122
+ @cloud.roles["web"].public_ports 3
123
+ @cloud.validate
124
+ assert !@cloud.valid?
125
+ assert @cloud.validation_errors.any? {|message| !message.index("public_ports attribute must be an Array").nil? }
126
+ end
127
+
128
+ should "be invalid due to invalid roles, public-ports not numbers" do
129
+ @cloud.roles["web"].public_ports [3, Hash.new]
130
+ @cloud.validate
131
+ assert !@cloud.valid?
132
+ assert @cloud.validation_errors.any? {|message| !message.index("public_ports attribute must be an Array of numbers").nil? }
133
+ end
134
+
135
+ should "be invalid due to missing nodes" do
136
+ @cloud.nodes = nil
137
+ @cloud.validate
138
+ assert !@cloud.valid?
139
+ assert @cloud.validation_errors.any? {|message| !message.index("Missing nodes").nil? }
140
+ end
141
+ end
142
+ end