the-maestro 0.3.1 → 0.3.2
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.
- data/LICENSE +1 -1
- data/README.rdoc +30 -9
- data/VERSION +1 -1
- data/lib/maestro/cloud.rb +4 -1
- data/lib/maestro/node.rb +19 -4
- data/test/unit/test_cloud.rb +19 -0
- data/test/unit/test_configurable_node.rb +51 -0
- data/test/unit/test_node.rb +24 -30
- data/the-maestro.gemspec +4 -2
- metadata +4 -2
data/LICENSE
CHANGED
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.
|
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
|
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
|
326
|
-
YOUR_RAILS_APP/log/maestro/clouds
|
327
|
-
YOUR_RAILS_APP/log/maestro/clouds
|
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
|
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
|
+
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
|
-
|
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
|
data/test/unit/test_cloud.rb
CHANGED
@@ -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
|
data/test/unit/test_node.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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 "
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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.
|
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-
|
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.
|
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-
|
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
|