the-maestro 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2010 Brian Ploetz <bploetz@gmail.com>
3
+ Copyright (c) 2010 Brian Ploetz <bploetz (at) gmail.com>
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person
6
6
  obtaining a copy of this software and associated documentation
data/README.rdoc CHANGED
@@ -22,7 +22,7 @@ Add the following gem dependency to your Rails project's <code>config/environmen
22
22
 
23
23
  Rails::Initializer.run do |config|
24
24
 
25
- config.gem "the-maestro", :lib => "maestro", :version => "0.3.0", :source => "http://gemcutter.org"
25
+ config.gem "the-maestro", :lib => "maestro", :version => "0.3.2", :source => "http://gemcutter.org"
26
26
 
27
27
  end
28
28
 
@@ -132,7 +132,7 @@ A cloud name may only contain alphanumerics and dashes.
132
132
 
133
133
  ==== aws_cloud attributes
134
134
 
135
- The following attributes must be present after your aws_cloud declaration:
135
+ The following attributes must be present within your aws_cloud declaration:
136
136
 
137
137
  - <b>keypair_name</b>: A keypair associated with your Amazon Web Services account
138
138
  - <b>keypair_file</b>: Fully qualified path to the keypair file on your computer matching the above keypair_name
@@ -175,7 +175,7 @@ Creates an Elastic Cloud Compute (EC2) node. An EC2 node name may only contain a
175
175
 
176
176
  The following attributes must be present:
177
177
 
178
- - <b>roles</b>: An array of Chef roles to apply to this node. The role names in the array must match the Chef roles that are defined in config/maestro/roles.
178
+ - <b>roles</b>: An array of Chef roles to apply to this node. The role names in the array must match the Chef roles that are defined in <code>config/maestro/roles</code>.
179
179
  - <b>ami</b>: The AMI identifier to use.
180
180
  - <b>ssh_user</b>: The name of the user to use to ssh to the instance.
181
181
  - <b>instance_type</b>: The instance type to use for this EC2 node.
@@ -183,6 +183,8 @@ The following attributes must be present:
183
183
 
184
184
  The following attributes are optional:
185
185
 
186
+ - <b>cookbook_attributes</b>: Chef cookbook attribute overrides, in JSON format (see {the Chef documentation}[http://wiki.opscode.com/display/chef/Attributes#Attributes-AttributesinJSON] for more information).
187
+ Since the attribute JSON format necessitates lots of " characters, the use of heredocs is recomended to avoid manual escaping of " characters and improve readability. See the example below.
186
188
  - <b>elastic_ip</b>: The Elastic IP address to associate with this node.
187
189
  - <b>ebs_volume_id</b>: The id of the EBS Volume to attach to this node.
188
190
  - <b>ebs_device</b>: The device to attach the EBS Volume to.
@@ -195,6 +197,20 @@ An example ec2_node:
195
197
  ssh_user "ubuntu"
196
198
  instance_type "m1.small"
197
199
  availability_zone "us-east-1b"
200
+ cookbook_attributes <<-COOKBOOK_ATTRIBUTES
201
+ "apache": {
202
+ "listen_ports": ["81", "8181"],
203
+ "worker" : {
204
+ "startservers" : "5"
205
+ }
206
+ },
207
+ "mysql": {
208
+ "server_root_password": "XXXXXXXX",
209
+ "tunable": {
210
+ "max_connections": "500"
211
+ }
212
+ }
213
+ COOKBOOK_ATTRIBUTES
198
214
  elastic_ip "123.45.678.90"
199
215
  ebs_volume_id "vol-xxxxxxxx"
200
216
  ebs_device "/dev/sdh"
@@ -284,9 +300,9 @@ At any time, you may run the following Rake task to validate your Maestro config
284
300
 
285
301
  rake maestro:validate_configs
286
302
 
287
- This will validate all of the files in your Maestro config directory
303
+ This will validate all of the files in your Maestro config directory, and report any errors.
288
304
 
289
- === Conduct
305
+ === Conduct!
290
306
 
291
307
  Maestro creates Rake tasks for all of your valid clouds. These tasks allow you to provision, configure, and deploy to your cloud.
292
308
  A very simplistic workflow:
@@ -322,9 +338,9 @@ For a more in depth explanation of Chef, please consult {the Chef documentation}
322
338
  Maestro will log cloud-wide workflow messages to STDOUT. In addition, log files will be created for your cloud, as well as each of the nodes in your cloud. These
323
339
  can be found at the following location:
324
340
 
325
- YOUR_RAILS_APP/log/maestro/clouds/<i>cloud-name</i>/
326
- YOUR_RAILS_APP/log/maestro/clouds/<i>cloud-name</i>/<i>cloud-name</i>.log
327
- YOUR_RAILS_APP/log/maestro/clouds/<i>cloud-name</i>/<i>node-name</i>.log
341
+ YOUR_RAILS_APP/log/maestro/clouds/cloud-name/
342
+ YOUR_RAILS_APP/log/maestro/clouds/cloud-name/cloud-name.log
343
+ YOUR_RAILS_APP/log/maestro/clouds/cloud-name/node-name.log
328
344
 
329
345
  Node-specific messages are not logged to STDOUT, but only to the node's log file. For troubleshooting problems configuring your nodes, please
330
346
  consult the individual node log files for information.
@@ -337,6 +353,11 @@ consult the individual node log files for information.
337
353
  TODO...
338
354
 
339
355
 
356
+ == Where To Get Help
357
+
358
+ The {Maestro Users Google Group}[http://groups.google.com/group/maestro-users] is the recommended place to get help.
359
+
360
+
340
361
  == Contributing
341
362
  Please use the {Issues}[http://github.com/bploetz/maestro/issues] page on the Maestro GitHub site to report bugs or feature requests.
342
363
 
@@ -383,7 +404,7 @@ To generate RDoc locally:
383
404
 
384
405
  (The MIT License)
385
406
 
386
- Copyright (c) 2010 Brian Ploetz <bploetz@gmail.com>
407
+ Copyright (c) 2010 Brian Ploetz <bploetz (at) gmail.com>
387
408
 
388
409
  Permission is hereby granted, free of charge, to any person
389
410
  obtaining a copy of this software and associated documentation
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.1
1
+ 0.3.2
data/lib/maestro/cloud.rb CHANGED
@@ -22,6 +22,8 @@ module Maestro
22
22
  attr_reader :configurable_nodes
23
23
  # String containing the full path to this Cloud's log directory
24
24
  attr_reader :log_directory
25
+ # String containing the full path to this Cloud's log file
26
+ attr_reader :log_file
25
27
  dsl_property :keypair_name, :keypair_file
26
28
 
27
29
  # Creates a new Cloud object.
@@ -123,7 +125,7 @@ module Maestro
123
125
  else
124
126
  @logger.info "chef-solo already installed on Nodes #{@configurable_nodes.keys.inspect}"
125
127
  end
126
- @logger.info "Running chef-solo on Nodes #{@configurable_nodes.keys.inspect}..."
128
+ @logger.info "Running chef-solo on Nodes #{@configurable_nodes.keys.inspect}. This may take a few minutes..."
127
129
  session = open_ssh_session
128
130
  run_chef_solo(session)
129
131
  session.close
@@ -315,6 +317,7 @@ module Maestro
315
317
  File.new(cloud_log_file, "a+")
316
318
  @logger.info "Created #{cloud_log_file}"
317
319
  end
320
+ @log_file = cloud_log_file
318
321
  outputter = Log4r::FileOutputter.new("#{@name}-file", :formatter => FileFormatter.new, :filename => cloud_log_file, :truncate => false)
319
322
  @logger.add(outputter)
320
323
  rescue RuntimeError => rerr
data/lib/maestro/node.rb CHANGED
@@ -19,7 +19,9 @@ module Maestro
19
19
  # the IP address of this Node
20
20
  attr_accessor :ip_address
21
21
  # the logger of this Node
22
- attr_accessor :logger
22
+ attr_reader :logger
23
+ # String containing the fully qualified path to the log file of this Node
24
+ attr_reader :log_file
23
25
 
24
26
  # Creates a new Node
25
27
  def initialize(name, cloud, &block)
@@ -65,6 +67,7 @@ module Maestro
65
67
  node_log_file = @cloud.log_directory + "/#{@name.to_s}.log"
66
68
  if !File.exists?(node_log_file)
67
69
  File.new(node_log_file, "a+")
70
+ @log_file = node_log_file
68
71
  @logger.info "Created #{node_log_file}"
69
72
  end
70
73
  outputter = Log4r::FileOutputter.new("#{@name.to_s}-file", :formatter => FileFormatter.new, :filename => node_log_file, :truncate => false)
@@ -91,7 +94,7 @@ module Maestro
91
94
 
92
95
  DEFAULT_SSH_USER = "root"
93
96
 
94
- dsl_property :roles, :ssh_user
97
+ dsl_property :roles, :ssh_user, :cookbook_attributes
95
98
  # the file name of this Configurable Node's Chef Node JSON file
96
99
  attr_accessor :json_filename
97
100
  # the contents of this Configurable Node's Chef Node JSON file
@@ -107,7 +110,17 @@ module Maestro
107
110
  @json.chop! if @json =~ /\s$/
108
111
  @json.chop! if @json =~ /,$/
109
112
  end
110
- @json = @json + "]}"
113
+ @json = @json + "]"
114
+ if !@cookbook_attributes.nil?
115
+ @cookbook_attributes.gsub!(/(\n|\n\r)\s{0,}/, ' ')
116
+ @cookbook_attributes.strip!
117
+ @cookbook_attributes.chop! if @cookbook_attributes =~ /,$/
118
+ @cookbook_attributes.slice!(0) if @cookbook_attributes =~ /^\{/
119
+ @json = @json + ", "
120
+ @json = @json + @cookbook_attributes
121
+ @json.chop! if @json =~ /\s$/
122
+ end
123
+ @json = @json + " }"
111
124
  end
112
125
 
113
126
  protected
@@ -116,8 +129,10 @@ module Maestro
116
129
  def validate_internal
117
130
  super
118
131
  invalidate "'#{@name}' Node missing roles map" if roles.nil?
132
+ if !cookbook_attributes.nil?
133
+ invalidate "'#{@name}' Node's cookbook_attributes must be a String" if !cookbook_attributes.is_a?(String)
134
+ end
119
135
  end
120
136
  end
121
-
122
137
  end
123
138
  end
@@ -14,6 +14,7 @@ class TestCloud < Test::Unit::TestCase
14
14
 
15
15
  context "A Cloud instance" do
16
16
  setup do
17
+ ENV[Maestro::MAESTRO_DIR_ENV_VAR] = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'standalone')
17
18
  @cloud = aws_cloud :test do
18
19
  keypair_name "XXXXXXX-keypair"
19
20
  keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
@@ -32,6 +33,11 @@ class TestCloud < Test::Unit::TestCase
32
33
  end
33
34
  end
34
35
 
36
+ teardown do
37
+ FileUtils.rm_rf([Maestro.maestro_log_directory], :secure => true) if File.exists?(Maestro.maestro_log_directory)
38
+ ENV.delete Maestro::MAESTRO_DIR_ENV_VAR
39
+ end
40
+
35
41
 
36
42
  should "raise exception on space in name" do
37
43
  assert_raise StandardError do
@@ -138,5 +144,18 @@ class TestCloud < Test::Unit::TestCase
138
144
  assert !@cloud.valid?
139
145
  assert @cloud.validation_errors.any? {|message| !message.index("Missing nodes").nil? }
140
146
  end
147
+
148
+ should "create logs" do
149
+ assert_nothing_raised do
150
+ base_dir = ENV[Maestro::MAESTRO_DIR_ENV_VAR]
151
+ assert !File.exists?("#{base_dir}/log/maestro/clouds/foo")
152
+ assert !File.exists?("#{base_dir}/log/maestro/clouds/foo/foo.log")
153
+ cloud = aws_cloud :foo do end
154
+ assert cloud.log_file.eql? "#{base_dir}/log/maestro/clouds/foo/foo.log"
155
+ assert cloud.log_directory.eql? "#{base_dir}/log/maestro/clouds/foo"
156
+ assert File.exists?("#{base_dir}/log/maestro/clouds/foo/foo.log")
157
+ assert File.exists?("#{base_dir}/log/maestro/clouds/foo")
158
+ end
159
+ end
141
160
  end
142
161
  end
@@ -0,0 +1,51 @@
1
+ require 'helper'
2
+
3
+ # Unit tests for Maestro::Node::Configurable
4
+ class TestConfigurableNode < Test::Unit::TestCase
5
+
6
+ context "Maestro::Node::Configurable" do
7
+ setup do
8
+ @cloud = aws_cloud :test do
9
+ nodes do
10
+ ec2_node "web-1" do
11
+ roles ["web", "mail"]
12
+ cookbook_attributes <<-COOKBOOK_ATTRIBUTES
13
+ "apache": {
14
+ "listen_ports": ["81", "8181"],
15
+ "worker" : {
16
+ "startservers" : "5"
17
+ }
18
+ },
19
+ "mysql": {
20
+ "server_root_password": "XXXXXXXX",
21
+ "tunable": {
22
+ "max_connections": "500"
23
+ }
24
+ }
25
+ COOKBOOK_ATTRIBUTES
26
+ end
27
+ end
28
+ end
29
+ @node = @cloud.nodes["web-1"]
30
+ end
31
+
32
+
33
+ should "be invalid due to missing role map" do
34
+ @node.roles nil
35
+ @node.validate
36
+ assert !@node.valid?
37
+ assert @node.validation_errors.any? {|message| !message.index("missing roles map").nil? }
38
+ end
39
+
40
+ should "be invalid due to cookbook_attributes not a string" do
41
+ @node.cookbook_attributes Hash.new
42
+ @node.validate
43
+ assert !@node.valid?
44
+ assert @node.validation_errors.any? {|message| !message.index("cookbook_attributes must be a String").nil? }
45
+ end
46
+
47
+ should "have valid JSON" do
48
+ assert @node.json.eql? "{ \"run_list\": [\"role[web]\", \"role[mail]\"], \"apache\": { \"listen_ports\": [\"81\", \"8181\"], \"worker\" : { \"startservers\" : \"5\" } }, \"mysql\": { \"server_root_password\": \"XXXXXXXX\", \"tunable\": { \"max_connections\": \"500\" } } }"
49
+ end
50
+ end
51
+ end
@@ -1,36 +1,21 @@
1
1
  require 'helper'
2
2
 
3
- # Unit tests for Maestro::Node
3
+ # Unit tests for Maestro::Node::Base
4
4
  class TestNode < Test::Unit::TestCase
5
5
 
6
- context "Maestro::Node" do
6
+ context "Maestro::Node::Base" do
7
7
  setup do
8
- @cloud = aws_cloud :test do
9
- keypair_name "XXXXXXX-keypair"
10
- keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
11
-
12
- roles do
13
- role "web" do
14
- public_ports [80, 443]
15
- end
16
- end
17
-
18
- nodes do
19
- ec2_node "web-1" do
20
- roles ["web"]
21
- end
22
- end
23
- end
24
- @node = @cloud.nodes["web-1"]
8
+ ENV[Maestro::MAESTRO_DIR_ENV_VAR] = File.join(File.dirname(File.expand_path(__FILE__)), 'fixtures', 'standalone')
25
9
  end
26
-
27
-
10
+
11
+ teardown do
12
+ FileUtils.rm_rf([Maestro.maestro_log_directory], :secure => true) if File.exists?(Maestro.maestro_log_directory)
13
+ ENV.delete Maestro::MAESTRO_DIR_ENV_VAR
14
+ end
15
+
28
16
  should "raise exception on space in name" do
29
17
  assert_raise StandardError do
30
- @cloud = aws_cloud :test do
31
- keypair_name "XXXXXXX-keypair"
32
- keypair_file "/path/to/id_rsa-XXXXXXX-keypair"
33
- roles {}
18
+ cloud = aws_cloud :test do
34
19
  nodes do
35
20
  ec2_node "foo bar" do
36
21
  roles ["web"]
@@ -40,11 +25,20 @@ class TestNode < Test::Unit::TestCase
40
25
  end
41
26
  end
42
27
 
43
- should "be invalid due to missing role map" do
44
- @node.roles nil
45
- @node.validate
46
- assert !@node.valid?
47
- assert @node.validation_errors.any? {|message| !message.index("missing roles map").nil? }
28
+ should "create log file" do
29
+ assert_nothing_raised do
30
+ base_dir = ENV[Maestro::MAESTRO_DIR_ENV_VAR]
31
+ assert !File.exists?("#{base_dir}/log/maestro/clouds/test/foo.log")
32
+ cloud = aws_cloud :test do
33
+ nodes do
34
+ ec2_node "foo" do
35
+ roles ["web"]
36
+ end
37
+ end
38
+ end
39
+ assert cloud.nodes["foo"].log_file.eql? "#{base_dir}/log/maestro/clouds/test/foo.log"
40
+ assert File.exists?("#{base_dir}/log/maestro/clouds/test/foo.log")
41
+ end
48
42
  end
49
43
  end
50
44
  end
data/the-maestro.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{the-maestro}
8
- s.version = "0.3.1"
8
+ s.version = "0.3.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Ploetz"]
12
- s.date = %q{2010-05-21}
12
+ s.date = %q{2010-05-23}
13
13
  s.description = %q{Maestro is a cloud provisioning, configuration, and deployment utility for your Ruby and Ruby On Rails applications.}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
@@ -64,6 +64,7 @@ Gem::Specification.new do |s|
64
64
  "test/unit/test_aws_rds_node.rb",
65
65
  "test/unit/test_cent_os.rb",
66
66
  "test/unit/test_cloud.rb",
67
+ "test/unit/test_configurable_node.rb",
67
68
  "test/unit/test_debian.rb",
68
69
  "test/unit/test_fedora.rb",
69
70
  "test/unit/test_invalid_mode.rb",
@@ -98,6 +99,7 @@ Gem::Specification.new do |s|
98
99
  "test/unit/test_aws_rds_node.rb",
99
100
  "test/unit/test_cent_os.rb",
100
101
  "test/unit/test_cloud.rb",
102
+ "test/unit/test_configurable_node.rb",
101
103
  "test/unit/test_debian.rb",
102
104
  "test/unit/test_fedora.rb",
103
105
  "test/unit/test_invalid_mode.rb",
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: the-maestro
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brian Ploetz
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-05-21 00:00:00 -04:00
12
+ date: 2010-05-23 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -160,6 +160,7 @@ files:
160
160
  - test/unit/test_aws_rds_node.rb
161
161
  - test/unit/test_cent_os.rb
162
162
  - test/unit/test_cloud.rb
163
+ - test/unit/test_configurable_node.rb
163
164
  - test/unit/test_debian.rb
164
165
  - test/unit/test_fedora.rb
165
166
  - test/unit/test_invalid_mode.rb
@@ -216,6 +217,7 @@ test_files:
216
217
  - test/unit/test_aws_rds_node.rb
217
218
  - test/unit/test_cent_os.rb
218
219
  - test/unit/test_cloud.rb
220
+ - test/unit/test_configurable_node.rb
219
221
  - test/unit/test_debian.rb
220
222
  - test/unit/test_fedora.rb
221
223
  - test/unit/test_invalid_mode.rb