the-maestro 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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