ey_cloud_awareness 0.1.14 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +69 -96
- data/Rakefile +20 -19
- data/VERSION +1 -1
- data/bin/ey_cloud_awareness +6 -0
- data/ey_cloud_awareness.gemspec +16 -16
- data/lib/ey_cloud_awareness.rb +5 -55
- data/lib/ey_cloud_awareness/capistrano_tasks.rb +80 -0
- data/lib/ey_cloud_awareness/engine_yard_cloud_instance.rb +233 -0
- data/lib/ey_cloud_awareness/hash_ext.rb +60 -0
- metadata +29 -31
- data/lib/engine_yard_cloud_instance.rb +0 -207
- data/lib/tasks/capistrano_tasks.rb +0 -111
- data/lib/tasks/ey_cloud_awareness.rake +0 -7
data/README.rdoc
CHANGED
@@ -2,13 +2,13 @@
|
|
2
2
|
|
3
3
|
This gem makes it a little easier to live on the EngineYard cloud:
|
4
4
|
|
5
|
-
*
|
5
|
+
* allow your instances to get information about the app_master, db_master, utility workers, etc. in its EngineYard environment
|
6
|
+
* automatically map cap tasks to all your instances
|
6
7
|
* automatically update your ssh aliases
|
7
|
-
* allow your app to get information about the cluster (aka "environment") it's running in
|
8
8
|
|
9
9
|
We use it over at http://brighterplanet.com.
|
10
10
|
|
11
|
-
==
|
11
|
+
== /etc/chef/dna.json must be readable
|
12
12
|
|
13
13
|
This gem depends on
|
14
14
|
|
@@ -16,42 +16,41 @@ This gem depends on
|
|
16
16
|
|
17
17
|
being READABLE and containing certain attributes as named by EngineYard. You might have to add:
|
18
18
|
|
19
|
-
|
19
|
+
sudo 'chmod a+r /etc/chef/dna.json'
|
20
20
|
|
21
|
-
to your
|
21
|
+
to your before_restart.rb hook.
|
22
22
|
|
23
|
-
==
|
23
|
+
== Using the commandline tool directly
|
24
24
|
|
25
|
-
|
25
|
+
Running this command only works from EngineYard AppCloud Amazon EC2 instances.
|
26
26
|
|
27
|
-
|
27
|
+
* SSH into one of your EngineYard AppCloud Amazon EC2 instances.
|
28
|
+
* Run <tt>sudo gem install ey_cloud_awareness --no-rdoc --no-ri</tt> to make sure the gem binary is installed somewhere in your path.
|
29
|
+
* Run <tt>sudo chmod a+r /etc/chef/dna.json</tt> to make sure everybody can read the EngineYard DNA information.
|
30
|
+
* Run <tt>ey_cloud_awareness</tt> and you should get back a JSON-encoded hash of instance, environment, app, and other metadata.
|
28
31
|
|
29
|
-
|
32
|
+
It returns a cleaned-up combination of Amazon EC2 and EngineYard metadata.
|
33
|
+
|
34
|
+
== Capistrano
|
35
|
+
|
36
|
+
Put this in your <tt>config/deploy.rb</tt> (or your Capfile):
|
30
37
|
|
31
38
|
# don't miss this line just because it's at the top
|
32
|
-
load "#{Gem.searcher.find('ey_cloud_awareness').full_gem_path}/lib/
|
39
|
+
load "#{Gem.searcher.find('ey_cloud_awareness').full_gem_path}/lib/ey_cloud_awareness/capistrano_tasks.rb"
|
33
40
|
|
34
41
|
task :my_production do
|
35
|
-
role :app_master, 'my_app.com'
|
36
|
-
|
37
|
-
set :deploy_to, '/data/my_app' # required
|
38
|
-
find_and_execute_task 'eyc_setup' # required
|
42
|
+
role :app_master, 'my_app.com'
|
43
|
+
find_and_execute_task 'eyc_setup' # gets a fresh list of your environment's instances and sets roles
|
39
44
|
end
|
40
45
|
|
41
46
|
task :my_staging do
|
42
|
-
role :app_master, 'staging.my_app.com'
|
43
|
-
|
44
|
-
set :deploy_to, '/data/my_app' # required
|
45
|
-
find_and_execute_task 'eyc_setup' # required
|
47
|
+
role :app_master, 'staging.my_app.com'
|
48
|
+
find_and_execute_task 'eyc_setup' # gets a fresh list of your environment's instances and sets roles
|
46
49
|
end
|
47
50
|
|
48
51
|
# add more tasks if you have more cloud environments
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
== Running capistrano tasks on your instances
|
53
|
-
|
54
|
-
Commands like these will work:
|
53
|
+
If you want to use EngineYard tasks like
|
55
54
|
|
56
55
|
cap my_production monit:status
|
57
56
|
cap my_production deploy:web:disable
|
@@ -59,100 +58,74 @@ Commands like these will work:
|
|
59
58
|
cap my_production passenger:restart
|
60
59
|
cap my_production nginx:restart
|
61
60
|
|
62
|
-
|
61
|
+
then you should make sure the tasks look like
|
63
62
|
|
63
|
+
[...]
|
64
|
+
require 'eycap/recipes'
|
65
|
+
ssh_options[:compression] = false
|
66
|
+
set :user, 'deploy'
|
67
|
+
set :runner, 'deploy'
|
68
|
+
[...]
|
69
|
+
|
70
|
+
task :my_production do
|
71
|
+
role :app_master, 'my_app.com'
|
72
|
+
find_and_execute_task 'eyc_setup'
|
73
|
+
set :rails_env, 'production' # required for eycap
|
74
|
+
set :deploy_to, '/data/my_app' # required for eycap too
|
75
|
+
end
|
76
|
+
|
77
|
+
task :my_staging do
|
78
|
+
role :app_master, 'staging.my_app.com'
|
79
|
+
find_and_execute_task 'eyc_setup'
|
80
|
+
set :rails_env, 'production' # required for eycap
|
81
|
+
set :deploy_to, '/data/my_app' # required for eycap too
|
82
|
+
end
|
83
|
+
|
64
84
|
== SSH into your instances
|
65
85
|
|
66
|
-
|
86
|
+
You can easily create SSH aliases like...
|
67
87
|
|
68
88
|
ssh my_production-app_master
|
69
89
|
|
70
|
-
|
90
|
+
using the following task:
|
71
91
|
|
72
92
|
cap my_production eyc:ssh
|
73
|
-
|
74
|
-
That will magically add or update a block like
|
75
|
-
|
76
|
-
# START StringReplacer my_production -- DO NOT MODIFY
|
77
|
-
|
78
|
-
# db_master
|
79
|
-
Host my_production-db_master
|
80
|
-
Hostname ec2-222-222-222-59.compute-1.amazonaws.com
|
81
|
-
User my_user
|
82
|
-
StrictHostKeyChecking no
|
83
|
-
|
84
|
-
# utility (1)
|
85
|
-
Host my_production-utility1
|
86
|
-
Hostname ec2-222-222-53-222.compute-1.amazonaws.com
|
87
|
-
User my_user
|
88
|
-
StrictHostKeyChecking no
|
89
|
-
|
90
|
-
# app_master
|
91
|
-
Host my_production-app_master
|
92
|
-
Hostname ec2-222-222-23-222.compute-1.amazonaws.com
|
93
|
-
User my_user
|
94
|
-
StrictHostKeyChecking no
|
95
|
-
|
96
|
-
# END StringReplacer my_production -- DO NOT MODIFY
|
97
|
-
|
98
|
-
Run that again as
|
99
|
-
|
100
93
|
cap my_staging eyc:ssh
|
101
94
|
|
102
|
-
|
103
|
-
|
104
|
-
# START StringReplacer my_staging -- DO NOT MODIFY
|
105
|
-
|
106
|
-
# app_master
|
107
|
-
Host my_staging-app_master
|
108
|
-
Hostname ec2-222-222-7-210.compute-1.amazonaws.com
|
109
|
-
User my_user
|
110
|
-
StrictHostKeyChecking no
|
111
|
-
|
112
|
-
# db_master
|
113
|
-
Host my_staging-db_master
|
114
|
-
Hostname ec2-222-222-52-8.compute-1.amazonaws.com
|
115
|
-
User my_user
|
116
|
-
StrictHostKeyChecking no
|
117
|
-
|
118
|
-
# END StringReplacer my_staging -- DO NOT MODIFY
|
119
|
-
|
120
|
-
This leaves you with lots of useful aliases:
|
95
|
+
That will magically update <tt>~/.ssh/config</tt> with aliases like
|
121
96
|
|
122
97
|
ssh my_production-db_master
|
123
98
|
ssh my_production-app_master
|
124
|
-
ssh my_production-
|
99
|
+
ssh my_production-app0
|
100
|
+
ssh my_production-app1
|
101
|
+
ssh my_production-app2
|
102
|
+
ssh my_production-util0
|
103
|
+
ssh my_production-util1
|
104
|
+
|
125
105
|
ssh my_staging-app_master
|
126
106
|
ssh my_staging-db_master
|
127
107
|
|
128
|
-
|
129
|
-
|
130
|
-
== Just dumping information about your instances
|
108
|
+
Unfortunately, the counters are arbitrary and so the assignment of util0 versus util1, etc. may change every time you update your <tt>.ssh/config</tt> file with this task.
|
131
109
|
|
132
110
|
Once you've done the quickstart, try:
|
133
111
|
|
134
|
-
cap my_production eyc:
|
135
|
-
cap my_production eyc:
|
136
|
-
cap my_production eyc:
|
112
|
+
cap my_production eyc:app_servers # gets a list of your app instances, including app_master
|
113
|
+
cap my_production eyc:utilities # ditto for utility instances
|
114
|
+
cap my_production eyc:db_servers # ditto for db instances
|
137
115
|
cap my_production eyc:all # gets a list of all your instances
|
138
116
|
|
139
|
-
==
|
117
|
+
== Making your instances aware of each other
|
140
118
|
|
141
|
-
|
119
|
+
You can use EngineYardCloudInstance to pull up information on other instances in an environment:
|
142
120
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
end
|
121
|
+
>> db_master = EngineYardCloudInstance.db_master
|
122
|
+
=> #<EngineYardCloudInstance:0xb5e12f70 @instance_id="i-50cf5838">
|
123
|
+
>> db_master.dns_name
|
124
|
+
=> "ec2-67-201-47-30.compute-1.amazonaws.com"
|
148
125
|
|
149
|
-
|
126
|
+
You can also inspect the whole set of metadata itself. This may change over time:
|
150
127
|
|
151
|
-
>>
|
152
|
-
=> [#<EngineYardCloudInstance:0xb5e12f70 @instance_id="i-50cf5838">, #<EngineYardCloudInstance:0xb5e12f5c @instance_id="i-dc9207b4">, #<EngineYardCloudInstance:0xb5e12f34 @instance_id="i-b2d84fda">]
|
153
|
-
>> all_app_instances.first.dns_name
|
154
|
-
=> "ec2-67-201-47-30.compute-1.amazonaws.com"
|
155
|
-
>> pp all_app_instances.first.to_hash
|
128
|
+
>> pp db_master.data
|
156
129
|
=> {:block_device_mapping=>
|
157
130
|
[{:ebs=>
|
158
131
|
{:status=>"attached",
|
@@ -193,19 +166,19 @@ Or whatever you want:
|
|
193
166
|
:dns_name=>"ec2-67-201-47-30.compute-1.amazonaws.com",
|
194
167
|
:ip_address=>"199.99.99.99",
|
195
168
|
:architecture=>"i386",
|
196
|
-
:
|
169
|
+
:role=>"db_master",
|
197
170
|
:monitoring=>{:state=>"disabled"}}
|
198
171
|
|
199
172
|
== A note on caching and network needs
|
200
173
|
|
201
174
|
I tried to be smart about caching the results of network calls.
|
202
175
|
|
203
|
-
Stuff like the
|
176
|
+
Stuff like the present instance id, which is pulled from an EC2 metadata server, is stored in
|
204
177
|
|
205
|
-
|
178
|
+
~/.ey_cloud_awareness/engine_yard_cloud_instance_id
|
206
179
|
|
207
180
|
Please let me know if this causes problems.
|
208
181
|
|
209
182
|
== Copyright
|
210
183
|
|
211
|
-
Copyright (c) 2009 Seamus Abshere. See LICENSE for details.
|
184
|
+
Copyright (c) 2009, 2010 Seamus Abshere. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -10,11 +10,11 @@ begin
|
|
10
10
|
gem.email = "seamus@abshere.net"
|
11
11
|
gem.homepage = "http://github.com/seamusabshere/ey_cloud_awareness"
|
12
12
|
gem.authors = ["Seamus Abshere"]
|
13
|
-
|
14
|
-
gem.add_dependency '
|
13
|
+
gem.executables = 'ey_cloud_awareness'
|
14
|
+
gem.add_dependency 'string_replacer', '>=0.0.1'
|
15
15
|
gem.add_dependency 'activesupport', '>=2.3.4'
|
16
|
-
gem.add_dependency 'amazon-ec2', '>=0.9.
|
17
|
-
gem.add_development_dependency "rspec", ">= 1.2.9"
|
16
|
+
gem.add_dependency 'amazon-ec2', '>=0.9.15'
|
17
|
+
# gem.add_development_dependency "rspec", ">= 1.2.9"
|
18
18
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
19
|
end
|
20
20
|
Jeweler::GemcutterTasks.new
|
@@ -25,21 +25,22 @@ rescue LoadError
|
|
25
25
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
# sabshere 9/21/10 disable until we actually have tests
|
29
|
+
# require 'spec/rake/spectask'
|
30
|
+
# Spec::Rake::SpecTask.new(:spec) do |spec|
|
31
|
+
# spec.libs << 'lib' << 'spec'
|
32
|
+
# spec.spec_files = FileList['spec/**/*_spec.rb']
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# Spec::Rake::SpecTask.new(:rcov) do |spec|
|
36
|
+
# spec.libs << 'lib' << 'spec'
|
37
|
+
# spec.pattern = 'spec/**/*_spec.rb'
|
38
|
+
# spec.rcov = true
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# task :spec => :check_dependencies
|
42
|
+
#
|
43
|
+
# task :default => :spec
|
43
44
|
|
44
45
|
require 'rake/rdoctask'
|
45
46
|
Rake::RDocTask.new do |rdoc|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/ey_cloud_awareness.gemspec
CHANGED
@@ -5,13 +5,15 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ey_cloud_awareness}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Seamus Abshere"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-09-22}
|
13
|
+
s.default_executable = %q{ey_cloud_awareness}
|
13
14
|
s.description = %q{Pull metadata from EC2 and EngineYard so that your EngineYard Cloud instances know about each other.}
|
14
15
|
s.email = %q{seamus@abshere.net}
|
16
|
+
s.executables = ["ey_cloud_awareness"]
|
15
17
|
s.extra_rdoc_files = [
|
16
18
|
"LICENSE",
|
17
19
|
"README.rdoc"
|
@@ -23,11 +25,12 @@ Gem::Specification.new do |s|
|
|
23
25
|
"README.rdoc",
|
24
26
|
"Rakefile",
|
25
27
|
"VERSION",
|
28
|
+
"bin/ey_cloud_awareness",
|
26
29
|
"ey_cloud_awareness.gemspec",
|
27
|
-
"lib/engine_yard_cloud_instance.rb",
|
28
30
|
"lib/ey_cloud_awareness.rb",
|
29
|
-
"lib/
|
30
|
-
"lib/
|
31
|
+
"lib/ey_cloud_awareness/capistrano_tasks.rb",
|
32
|
+
"lib/ey_cloud_awareness/engine_yard_cloud_instance.rb",
|
33
|
+
"lib/ey_cloud_awareness/hash_ext.rb",
|
31
34
|
"spec/ey_cloud_awareness_spec.rb",
|
32
35
|
"spec/spec.opts",
|
33
36
|
"spec/spec_helper.rb"
|
@@ -35,7 +38,7 @@ Gem::Specification.new do |s|
|
|
35
38
|
s.homepage = %q{http://github.com/seamusabshere/ey_cloud_awareness}
|
36
39
|
s.rdoc_options = ["--charset=UTF-8"]
|
37
40
|
s.require_paths = ["lib"]
|
38
|
-
s.rubygems_version = %q{1.3.
|
41
|
+
s.rubygems_version = %q{1.3.7}
|
39
42
|
s.summary = %q{Make your EngineYard cloud instances aware of each other.}
|
40
43
|
s.test_files = [
|
41
44
|
"spec/ey_cloud_awareness_spec.rb",
|
@@ -46,22 +49,19 @@ Gem::Specification.new do |s|
|
|
46
49
|
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
50
|
s.specification_version = 3
|
48
51
|
|
49
|
-
if Gem::Version.new(Gem::
|
50
|
-
s.add_runtime_dependency(%q<
|
52
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<string_replacer>, [">= 0.0.1"])
|
51
54
|
s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
|
52
|
-
s.add_runtime_dependency(%q<amazon-ec2>, [">= 0.9.
|
53
|
-
s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
|
55
|
+
s.add_runtime_dependency(%q<amazon-ec2>, [">= 0.9.15"])
|
54
56
|
else
|
55
|
-
s.add_dependency(%q<
|
57
|
+
s.add_dependency(%q<string_replacer>, [">= 0.0.1"])
|
56
58
|
s.add_dependency(%q<activesupport>, [">= 2.3.4"])
|
57
|
-
s.add_dependency(%q<amazon-ec2>, [">= 0.9.
|
58
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
59
|
+
s.add_dependency(%q<amazon-ec2>, [">= 0.9.15"])
|
59
60
|
end
|
60
61
|
else
|
61
|
-
s.add_dependency(%q<
|
62
|
+
s.add_dependency(%q<string_replacer>, [">= 0.0.1"])
|
62
63
|
s.add_dependency(%q<activesupport>, [">= 2.3.4"])
|
63
|
-
s.add_dependency(%q<amazon-ec2>, [">= 0.9.
|
64
|
-
s.add_dependency(%q<rspec>, [">= 1.2.9"])
|
64
|
+
s.add_dependency(%q<amazon-ec2>, [">= 0.9.15"])
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
data/lib/ey_cloud_awareness.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
require 'open-uri'
|
2
2
|
require 'set'
|
3
3
|
require 'fileutils'
|
4
|
-
require 'json'
|
5
|
-
require 'yaml'
|
6
4
|
require 'AWS' # aka amazon-ec2
|
7
5
|
require 'etc'
|
8
6
|
require 'active_support'
|
9
7
|
require 'active_support/version'
|
10
8
|
%w{
|
9
|
+
active_support/json
|
11
10
|
active_support/core_ext/string
|
12
|
-
active_support/core_ext/class/
|
11
|
+
active_support/core_ext/class/attribute_accessors
|
13
12
|
active_support/inflector/inflections
|
14
13
|
active_support/core_ext/string/inflections
|
15
14
|
active_support/core_ext/hash/keys
|
@@ -18,57 +17,8 @@ require 'active_support/version'
|
|
18
17
|
require active_support_3_requirement
|
19
18
|
end if ActiveSupport::VERSION::MAJOR == 3
|
20
19
|
|
21
|
-
require 'engine_yard_cloud_instance'
|
20
|
+
require 'ey_cloud_awareness/engine_yard_cloud_instance'
|
21
|
+
require 'ey_cloud_awareness/hash_ext'
|
22
22
|
|
23
|
-
|
24
|
-
# http://pragmatig.wordpress.com/2009/04/14/recursive-symbolize_keys/
|
25
|
-
def recursive_symbolize_keys!
|
26
|
-
symbolize_keys!
|
27
|
-
values.select { |v| v.is_a?(Hash) }.each do |hsh|
|
28
|
-
hsh.recursive_symbolize_keys!
|
29
|
-
end
|
30
|
-
# burst thru at least one level of arrays
|
31
|
-
values.select { |v| v.is_a?(Array) }.each do |ary|
|
32
|
-
ary.each do |v|
|
33
|
-
v.recursive_symbolize_keys! if v.is_a?(Hash)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
self
|
37
|
-
end
|
38
|
-
|
39
|
-
XML_ITEM_KEYS = [ :item, 'item' ]
|
40
|
-
|
41
|
-
# :sam => { :item => [{ :foo => :bar }] }
|
42
|
-
# into
|
43
|
-
# :sam => [{:foo => :bar}]
|
44
|
-
def kill_xml_item_keys!
|
45
|
-
if keys.length == 1 and XML_ITEM_KEYS.include?(keys.first)
|
46
|
-
raise ArgumentError, "You need to call kill_xml_item_keys! on { :foo => { :items => [...] } } not on { :items => [...] }"
|
47
|
-
end
|
48
|
-
keys.each do |key|
|
49
|
-
if self[key].is_a?(Hash) and self[key].keys.length == 1 and XML_ITEM_KEYS.include?(self[key].keys.first)
|
50
|
-
# self[:sam] = self[:sam]["item"] (using values.first because we don't know if it's :item or "item")
|
51
|
-
self[key] = delete(key).values.first
|
52
|
-
end
|
53
|
-
end
|
54
|
-
self
|
55
|
-
end
|
56
|
-
|
57
|
-
def recursive_kill_xml_item_keys!
|
58
|
-
kill_xml_item_keys!
|
59
|
-
values.select { |v| v.is_a?(Hash) }.each do |hsh|
|
60
|
-
hsh.recursive_kill_xml_item_keys!
|
61
|
-
end
|
62
|
-
# burst thru at least one level of arrays
|
63
|
-
values.select { |v| v.is_a?(Array) }.each do |ary|
|
64
|
-
ary.each do |v|
|
65
|
-
v.recursive_kill_xml_item_keys! if v.is_a?(Hash)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
self
|
69
|
-
end
|
70
|
-
|
71
|
-
def deep_copy
|
72
|
-
Marshal.load Marshal.dump(self)
|
73
|
-
end
|
23
|
+
module EyCloudAwareness
|
74
24
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'ey_cloud_awareness'
|
2
|
+
|
3
|
+
task :eyc_setup, :roles => :app_master do
|
4
|
+
# pull JSON-encoded metadata
|
5
|
+
output = capture('ey_cloud_awareness').gsub /\s+/, ' '
|
6
|
+
if /(\{.*\})/.match(output)
|
7
|
+
json_str = $1
|
8
|
+
begin
|
9
|
+
set :eyc_proxy, EngineYardCloudInstance.from_json(json_str)
|
10
|
+
rescue
|
11
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Couldn't parse JSON, so just dumping what we got"
|
12
|
+
$stderr.puts json_str
|
13
|
+
raise $!
|
14
|
+
end
|
15
|
+
else
|
16
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Didn't get JSON we recognized back, just dumping what we got"
|
17
|
+
$stderr.puts output
|
18
|
+
raise
|
19
|
+
end
|
20
|
+
|
21
|
+
# now set up roles
|
22
|
+
# role :app_master is already set
|
23
|
+
role :db_master, eyc_proxy.db_master.dns_name
|
24
|
+
eyc_proxy.app_servers.each do |i|
|
25
|
+
role :app, i.dns_name
|
26
|
+
role :web, i.dns_name
|
27
|
+
end
|
28
|
+
eyc_proxy.db_servers.each do |i|
|
29
|
+
role :db, i.dns_name
|
30
|
+
end
|
31
|
+
eyc_proxy.utilities.each do |i|
|
32
|
+
role :util, i.dns_name
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace :eyc do
|
37
|
+
%w{ app_servers db_servers utilities all }.each do |name|
|
38
|
+
task name, :roles => :app_master do
|
39
|
+
require 'pp'
|
40
|
+
pp eyc_proxy.send(name).map(&:to_hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
task :ssh, :roles => :app_master do
|
45
|
+
require 'string_replacer'
|
46
|
+
replacement = []
|
47
|
+
counters = Hash.new(0)
|
48
|
+
eyc_proxy.with_roles.each do |instance|
|
49
|
+
case instance.role
|
50
|
+
when 'db_master'
|
51
|
+
explanation = ''
|
52
|
+
shorthand = 'db_master'
|
53
|
+
when 'app_master'
|
54
|
+
explanation = ''
|
55
|
+
shorthand = 'app_master'
|
56
|
+
when 'solo'
|
57
|
+
explanation = ''
|
58
|
+
shorthand = 'solo'
|
59
|
+
else
|
60
|
+
explanation = " (#{counters[instance.role]})"
|
61
|
+
shorthand = "#{instance.role}#{counters[instance.role]}"
|
62
|
+
counters[instance.role] += 1
|
63
|
+
end
|
64
|
+
replacement << %{
|
65
|
+
# #{instance.role}#{explanation}
|
66
|
+
Host #{eyc_proxy.environment['name']}-#{shorthand}
|
67
|
+
Hostname #{instance.dns_name}
|
68
|
+
User #{instance.user['username']}
|
69
|
+
StrictHostKeyChecking no
|
70
|
+
}
|
71
|
+
end
|
72
|
+
string = replacement.join
|
73
|
+
ssh_config_path = File.expand_path("~/.ssh/config")
|
74
|
+
r = StringReplacer.new ssh_config_path
|
75
|
+
r.replace! string, eyc_proxy.environment['name'], nil
|
76
|
+
|
77
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Added this to #{ssh_config_path}"
|
78
|
+
$stderr.puts string
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
class EngineYardCloudInstance
|
2
|
+
HOMEDIR = File.expand_path "~#{Etc.getpwuid.name}"
|
3
|
+
PRESENT_INSTANCE_ID_CACHE_PATH = "#{HOMEDIR}/.ey_cloud_awareness/engine_yard_cloud_instance_id"
|
4
|
+
PRESENT_SECURITY_GROUP_CACHE_PATH = "#{HOMEDIR}/.ey_cloud_awareness/engine_yard_cloud_security_group"
|
5
|
+
INSTANCES_CACHE_PATH = "#{HOMEDIR}/.ey_cloud_awareness/engine_yard_cloud_ec2_instances.json"
|
6
|
+
DNA_PATH = '/etc/chef/dna.json'
|
7
|
+
|
8
|
+
attr_reader :instance_id
|
9
|
+
def initialize(instance_id)
|
10
|
+
@instance_id = instance_id.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def clear
|
14
|
+
self.class.clear
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
data.merge 'environment' => environment,
|
19
|
+
'apps' => apps,
|
20
|
+
'users' => users
|
21
|
+
end
|
22
|
+
|
23
|
+
def app
|
24
|
+
raise "[EY CLOUD AWARENESS GEM] There is more than one app on this instance, so you can't use the #app method. Use #users.first (etc.) instead." if apps.length > 1
|
25
|
+
apps.first
|
26
|
+
end
|
27
|
+
|
28
|
+
def user
|
29
|
+
raise "[EY CLOUD AWARENESS GEM] There is more than one user on this instance, so you can't use the #user method. Use #users.first (etc.) instead." if users.length > 1
|
30
|
+
users.first
|
31
|
+
end
|
32
|
+
|
33
|
+
def as_json(*)
|
34
|
+
to_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def data
|
38
|
+
self.class.data['instances'][instance_id]
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(name, *args, &block)
|
42
|
+
if data and data.has_key?(name.to_s)
|
43
|
+
data[name.to_s]
|
44
|
+
elsif self.class.data and self.class.data.has_key?(name.to_s)
|
45
|
+
self.class.data[name.to_s]
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def ==(other)
|
52
|
+
self.instance_id == other.instance_id
|
53
|
+
end
|
54
|
+
|
55
|
+
cattr_accessor :proxy
|
56
|
+
cattr_accessor :data_cache
|
57
|
+
cattr_accessor :dna_cache
|
58
|
+
|
59
|
+
class << self
|
60
|
+
# sabshere 9/21/10 from engineyard gem cli.rb
|
61
|
+
# def ssh_host_filter(opts)
|
62
|
+
# return lambda {|instance| true } if opts[:all]
|
63
|
+
# return lambda {|instance| %w(solo app app_master ).include?(instance.role) } if opts[:app_servers]
|
64
|
+
# return lambda {|instance| %w(solo db_master db_slave).include?(instance.role) } if opts[:db_servers ]
|
65
|
+
# return lambda {|instance| %w(solo db_master ).include?(instance.role) } if opts[:db_master ]
|
66
|
+
# return lambda {|instance| %w(db_slave ).include?(instance.role) } if opts[:db_slaves ]
|
67
|
+
# return lambda {|instance| %w(util ).include?(instance.role) &&
|
68
|
+
# opts[:utilities].include?(instance.name) } if opts[:utilities ]
|
69
|
+
# return lambda {|instance| %w(solo app_master ).include?(instance.role) }
|
70
|
+
# end
|
71
|
+
|
72
|
+
def all
|
73
|
+
data['instances'].map { |instance_id, _| new instance_id }
|
74
|
+
end
|
75
|
+
|
76
|
+
def app_servers
|
77
|
+
find_all_by_instance_roles 'app', 'app_master', 'solo'
|
78
|
+
end
|
79
|
+
|
80
|
+
def db_servers
|
81
|
+
find_all_by_instance_roles 'db_master', 'db_slave', 'solo'
|
82
|
+
end
|
83
|
+
|
84
|
+
def db_master
|
85
|
+
find_all_by_instance_roles('db_master').first || find_all_by_instance_roles('solo').first
|
86
|
+
end
|
87
|
+
|
88
|
+
def db_slaves
|
89
|
+
find_all_by_instance_roles 'db_slave'
|
90
|
+
end
|
91
|
+
|
92
|
+
def utilities
|
93
|
+
find_all_by_instance_roles 'util'
|
94
|
+
end
|
95
|
+
|
96
|
+
def app_master
|
97
|
+
find_all_by_instance_roles('app_master').first || find_all_by_instance_roles('solo').first
|
98
|
+
end
|
99
|
+
|
100
|
+
def with_roles
|
101
|
+
all.select { |i| i.role.present? }
|
102
|
+
end
|
103
|
+
|
104
|
+
def present
|
105
|
+
new present_instance_id
|
106
|
+
end
|
107
|
+
|
108
|
+
def find_by_instance_id(instance_id)
|
109
|
+
new instance_id
|
110
|
+
end
|
111
|
+
|
112
|
+
def find_all_by_instance_roles(*args)
|
113
|
+
data['instances'].select { |_, instance| Array.wrap(args).map(&:to_s).include? instance['role'] }.map { |instance_id, _| new instance_id }
|
114
|
+
end
|
115
|
+
|
116
|
+
def clear
|
117
|
+
raise "[EY CLOUD AWARENESS GEM] Can't clear if we used from_hash" if proxy?
|
118
|
+
self.data_cache = nil
|
119
|
+
self.dna_cache = nil
|
120
|
+
ec2_instances true
|
121
|
+
present_security_group true
|
122
|
+
present_instance_id true
|
123
|
+
end
|
124
|
+
|
125
|
+
def data
|
126
|
+
return data_cache if data_cache.is_a? Hash
|
127
|
+
raise "[EY CLOUD AWARENESS GEM] Can't calculate data if we used from_hash" if proxy?
|
128
|
+
self.data_cache = Hash.new
|
129
|
+
data_cache['instances'] = mixed_instances
|
130
|
+
data_cache['environment'] = dna['engineyard']['environment'].except('instances', 'apps')
|
131
|
+
data_cache['apps'] = dna['engineyard']['environment']['apps']
|
132
|
+
data_cache['users'] = dna['users']
|
133
|
+
data_cache['group_id'] = present_security_group
|
134
|
+
data_cache
|
135
|
+
end
|
136
|
+
|
137
|
+
def proxy?
|
138
|
+
!!proxy
|
139
|
+
end
|
140
|
+
|
141
|
+
def to_hash
|
142
|
+
data
|
143
|
+
end
|
144
|
+
|
145
|
+
def as_json(*)
|
146
|
+
to_hash
|
147
|
+
end
|
148
|
+
|
149
|
+
def from_json(str)
|
150
|
+
from_hash ActiveSupport::JSON.decode(str)
|
151
|
+
end
|
152
|
+
|
153
|
+
def from_hash(hash)
|
154
|
+
self.proxy = true
|
155
|
+
self.data_cache = hash
|
156
|
+
self
|
157
|
+
end
|
158
|
+
|
159
|
+
def method_missing(name, *args, &block)
|
160
|
+
if data and data.has_key?(name.to_s)
|
161
|
+
data[name.to_s]
|
162
|
+
else
|
163
|
+
super
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def mixed_instances
|
168
|
+
ec2_instances.inject(Hash.new) do |memo, ec2_instance|
|
169
|
+
instance_id = ec2_instance['instance_id']
|
170
|
+
mixed_instance = dna_instances.detect { |dna_instance| dna_instance['id'] == instance_id }.merge ec2_instance
|
171
|
+
memo[instance_id] = mixed_instance
|
172
|
+
memo
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def dna_instances
|
177
|
+
dna['engineyard']['environment']['instances']
|
178
|
+
end
|
179
|
+
|
180
|
+
def dna
|
181
|
+
raise "[EY CLOUD AWARENESS GEM] Can't see DNA if we used from_hash" if proxy?
|
182
|
+
raise "[EY CLOUD AWARENESS GEM] Can't read DNA from #{DNA_PATH}! You should put 'sudo chmod a+r /etc/chef/dna.json' your your before_migrate.rb!" unless File.readable?(DNA_PATH)
|
183
|
+
self.dna_cache ||= ActiveSupport::JSON.decode(IO.read(DNA_PATH))
|
184
|
+
end
|
185
|
+
|
186
|
+
def present_instance_id(refresh = false)
|
187
|
+
raise "[EY CLOUD AWARENESS GEM] Can't call present_instance_id if we used from_hash" if proxy?
|
188
|
+
if refresh or !File.readable?(PRESENT_INSTANCE_ID_CACHE_PATH)
|
189
|
+
@_present_instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").gets
|
190
|
+
begin
|
191
|
+
FileUtils.mkdir_p File.dirname(PRESENT_INSTANCE_ID_CACHE_PATH)
|
192
|
+
File.open(PRESENT_INSTANCE_ID_CACHE_PATH, 'w') { |f| f.write @_present_instance_id }
|
193
|
+
rescue Errno::EACCES
|
194
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching present instance because #{PRESENT_INSTANCE_ID_CACHE_PATH} can't be written to"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
@_present_instance_id ||= IO.read(PRESENT_INSTANCE_ID_CACHE_PATH)
|
198
|
+
end
|
199
|
+
|
200
|
+
def present_security_group(refresh = false)
|
201
|
+
raise "[EY CLOUD AWARENESS GEM] Can't call present_security_group if we used from_hash" if proxy?
|
202
|
+
if refresh or !File.readable?(PRESENT_SECURITY_GROUP_CACHE_PATH)
|
203
|
+
@_present_security_group = open('http://169.254.169.254/latest/meta-data/security-groups').gets
|
204
|
+
raise "[EY CLOUD AWARENESS GEM] Don't know how to deal with (possibly) multiple security group: #{@_present_security_group}" if @_present_security_group =~ /,;/
|
205
|
+
begin
|
206
|
+
FileUtils.mkdir_p File.dirname(PRESENT_SECURITY_GROUP_CACHE_PATH)
|
207
|
+
File.open(PRESENT_SECURITY_GROUP_CACHE_PATH, 'w') { |f| f.write @_present_security_group }
|
208
|
+
rescue Errno::EACCES
|
209
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching present security group because #{PRESENT_SECURITY_GROUP_CACHE_PATH} can't be written to"
|
210
|
+
end
|
211
|
+
end
|
212
|
+
@_present_security_group ||= IO.read(PRESENT_SECURITY_GROUP_CACHE_PATH)
|
213
|
+
end
|
214
|
+
|
215
|
+
def ec2_instances(refresh = false)
|
216
|
+
raise "[EY CLOUD AWARENESS GEM] Can't call ec2_instances if we used from_hash" if proxy?
|
217
|
+
if refresh or !File.readable?(INSTANCES_CACHE_PATH)
|
218
|
+
ec2 = AWS::EC2::Base.new :access_key_id => dna['aws_secret_id'], :secret_access_key => dna['aws_secret_key']
|
219
|
+
@_ec2_instances = ec2.describe_instances
|
220
|
+
@_ec2_instances.recursive_kill_xml_item_keys!
|
221
|
+
@_ec2_instances = @_ec2_instances['reservationSet'].select { |hash| present_security_group.include? hash['groupSet'].first['groupId'] }
|
222
|
+
@_ec2_instances.map! { |hash| hash['instancesSet'].first.recursive_underscore_keys! }
|
223
|
+
begin
|
224
|
+
FileUtils.mkdir_p File.dirname(INSTANCES_CACHE_PATH)
|
225
|
+
File.open(INSTANCES_CACHE_PATH, 'w') { |f| f.write @_ec2_instances.to_json }
|
226
|
+
rescue Errno::EACCES
|
227
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching instance data because #{INSTANCES_CACHE_PATH} can't be written to"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
@_ec2_instances ||= ActiveSupport::JSON.decode(IO.read(INSTANCES_CACHE_PATH))
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module EyCloudAwareness
|
2
|
+
module HashExt
|
3
|
+
# http://as.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/Keys.html
|
4
|
+
def underscore_keys!
|
5
|
+
keys.each do |key|
|
6
|
+
self[key.to_s.underscore] = delete(key)
|
7
|
+
end
|
8
|
+
self
|
9
|
+
end
|
10
|
+
|
11
|
+
# http://pragmatig.wordpress.com/2009/04/14/recursive-symbolize_keys/
|
12
|
+
def recursive_underscore_keys!
|
13
|
+
underscore_keys!
|
14
|
+
values.select { |v| v.is_a?(Hash) }.each do |hsh|
|
15
|
+
hsh.recursive_underscore_keys!
|
16
|
+
end
|
17
|
+
# burst thru at least one level of arrays
|
18
|
+
values.select { |v| v.is_a?(Array) }.each do |ary|
|
19
|
+
ary.each do |v|
|
20
|
+
v.recursive_underscore_keys! if v.is_a?(Hash)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
XML_ITEM_KEYS = [ :item, 'item' ]
|
27
|
+
|
28
|
+
# :sam => { :item => [{ :foo => :bar }] }
|
29
|
+
# into
|
30
|
+
# :sam => [{:foo => :bar}]
|
31
|
+
def kill_xml_item_keys!
|
32
|
+
if keys.length == 1 and XML_ITEM_KEYS.include?(keys.first)
|
33
|
+
raise ArgumentError, "You need to call kill_xml_item_keys! on { :foo => { :items => [...] } } not on { :items => [...] }"
|
34
|
+
end
|
35
|
+
keys.each do |key|
|
36
|
+
if self[key].is_a?(Hash) and self[key].keys.length == 1 and XML_ITEM_KEYS.include?(self[key].keys.first)
|
37
|
+
# self[:sam] = self[:sam]["item"] (using values.first because we don't know if it's :item or "item")
|
38
|
+
self[key] = delete(key).values.first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def recursive_kill_xml_item_keys!
|
45
|
+
kill_xml_item_keys!
|
46
|
+
values.select { |v| v.is_a?(Hash) }.each do |hsh|
|
47
|
+
hsh.recursive_kill_xml_item_keys!
|
48
|
+
end
|
49
|
+
# burst thru at least one level of arrays
|
50
|
+
values.select { |v| v.is_a?(Array) }.each do |ary|
|
51
|
+
ary.each do |v|
|
52
|
+
v.recursive_kill_xml_item_keys! if v.is_a?(Hash)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
self
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Hash.send :include, EyCloudAwareness::HashExt
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey_cloud_awareness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Seamus Abshere
|
@@ -14,30 +15,34 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
-
default_executable:
|
18
|
+
date: 2010-09-22 00:00:00 -05:00
|
19
|
+
default_executable: ey_cloud_awareness
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
22
|
+
name: string_replacer
|
22
23
|
prerelease: false
|
23
24
|
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
24
26
|
requirements:
|
25
27
|
- - ">="
|
26
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 29
|
27
30
|
segments:
|
31
|
+
- 0
|
32
|
+
- 0
|
28
33
|
- 1
|
29
|
-
|
30
|
-
- 3
|
31
|
-
version: 1.2.3
|
34
|
+
version: 0.0.1
|
32
35
|
type: :runtime
|
33
36
|
version_requirements: *id001
|
34
37
|
- !ruby/object:Gem::Dependency
|
35
38
|
name: activesupport
|
36
39
|
prerelease: false
|
37
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
38
42
|
requirements:
|
39
43
|
- - ">="
|
40
44
|
- !ruby/object:Gem::Version
|
45
|
+
hash: 11
|
41
46
|
segments:
|
42
47
|
- 2
|
43
48
|
- 3
|
@@ -49,34 +54,22 @@ dependencies:
|
|
49
54
|
name: amazon-ec2
|
50
55
|
prerelease: false
|
51
56
|
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
52
58
|
requirements:
|
53
59
|
- - ">="
|
54
60
|
- !ruby/object:Gem::Version
|
61
|
+
hash: 37
|
55
62
|
segments:
|
56
63
|
- 0
|
57
64
|
- 9
|
58
|
-
-
|
59
|
-
version: 0.9.
|
65
|
+
- 15
|
66
|
+
version: 0.9.15
|
60
67
|
type: :runtime
|
61
68
|
version_requirements: *id003
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: rspec
|
64
|
-
prerelease: false
|
65
|
-
requirement: &id004 !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
segments:
|
70
|
-
- 1
|
71
|
-
- 2
|
72
|
-
- 9
|
73
|
-
version: 1.2.9
|
74
|
-
type: :development
|
75
|
-
version_requirements: *id004
|
76
69
|
description: Pull metadata from EC2 and EngineYard so that your EngineYard Cloud instances know about each other.
|
77
70
|
email: seamus@abshere.net
|
78
|
-
executables:
|
79
|
-
|
71
|
+
executables:
|
72
|
+
- ey_cloud_awareness
|
80
73
|
extensions: []
|
81
74
|
|
82
75
|
extra_rdoc_files:
|
@@ -89,11 +82,12 @@ files:
|
|
89
82
|
- README.rdoc
|
90
83
|
- Rakefile
|
91
84
|
- VERSION
|
85
|
+
- bin/ey_cloud_awareness
|
92
86
|
- ey_cloud_awareness.gemspec
|
93
|
-
- lib/engine_yard_cloud_instance.rb
|
94
87
|
- lib/ey_cloud_awareness.rb
|
95
|
-
- lib/
|
96
|
-
- lib/
|
88
|
+
- lib/ey_cloud_awareness/capistrano_tasks.rb
|
89
|
+
- lib/ey_cloud_awareness/engine_yard_cloud_instance.rb
|
90
|
+
- lib/ey_cloud_awareness/hash_ext.rb
|
97
91
|
- spec/ey_cloud_awareness_spec.rb
|
98
92
|
- spec/spec.opts
|
99
93
|
- spec/spec_helper.rb
|
@@ -107,23 +101,27 @@ rdoc_options:
|
|
107
101
|
require_paths:
|
108
102
|
- lib
|
109
103
|
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
none: false
|
110
105
|
requirements:
|
111
106
|
- - ">="
|
112
107
|
- !ruby/object:Gem::Version
|
108
|
+
hash: 3
|
113
109
|
segments:
|
114
110
|
- 0
|
115
111
|
version: "0"
|
116
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
117
114
|
requirements:
|
118
115
|
- - ">="
|
119
116
|
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
120
118
|
segments:
|
121
119
|
- 0
|
122
120
|
version: "0"
|
123
121
|
requirements: []
|
124
122
|
|
125
123
|
rubyforge_project:
|
126
|
-
rubygems_version: 1.3.
|
124
|
+
rubygems_version: 1.3.7
|
127
125
|
signing_key:
|
128
126
|
specification_version: 3
|
129
127
|
summary: Make your EngineYard cloud instances aware of each other.
|
@@ -1,207 +0,0 @@
|
|
1
|
-
class EngineYardCloudInstance
|
2
|
-
homedir = File.expand_path "~#{Etc.getpwuid.name}"
|
3
|
-
CURRENT_INSTANCE_ID_CACHE_PATH = "#{homedir}/.ey_cloud_awareness/engine_yard_cloud_instance_id"
|
4
|
-
CURRENT_SECURITY_GROUP_CACHE_PATH = "#{homedir}/.ey_cloud_awareness/engine_yard_cloud_security_group"
|
5
|
-
INSTANCE_DESCRIPTIONS_CACHE_PATH = "#{homedir}/.ey_cloud_awareness/engine_yard_cloud_ec2_instance_descriptions.yml"
|
6
|
-
DNA_PATH = '/etc/chef/dna.json'
|
7
|
-
|
8
|
-
attr_reader :instance_id
|
9
|
-
def initialize(instance_id)
|
10
|
-
@instance_id = instance_id.to_s
|
11
|
-
end
|
12
|
-
|
13
|
-
def valid?
|
14
|
-
data.present?
|
15
|
-
end
|
16
|
-
|
17
|
-
def clear
|
18
|
-
self.class.clear
|
19
|
-
end
|
20
|
-
|
21
|
-
def to_hash
|
22
|
-
data.deep_copy
|
23
|
-
end
|
24
|
-
|
25
|
-
def data
|
26
|
-
self.class.data[instance_id.to_sym]
|
27
|
-
end
|
28
|
-
|
29
|
-
def method_missing(name, *args, &block)
|
30
|
-
name = name.to_sym
|
31
|
-
if data and data.has_key?(name)
|
32
|
-
data[name]
|
33
|
-
else
|
34
|
-
super
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def ==(other)
|
39
|
-
self.instance_id == other.instance_id
|
40
|
-
end
|
41
|
-
|
42
|
-
class_inheritable_accessor :proxy
|
43
|
-
|
44
|
-
class << self
|
45
|
-
def to_hash
|
46
|
-
clear
|
47
|
-
data.deep_copy
|
48
|
-
end
|
49
|
-
|
50
|
-
def from_hash(hash)
|
51
|
-
self.proxy = true
|
52
|
-
@_data = hash.recursive_symbolize_keys!
|
53
|
-
self
|
54
|
-
end
|
55
|
-
|
56
|
-
def environment
|
57
|
-
@_environment ||= first.environment
|
58
|
-
end
|
59
|
-
|
60
|
-
def app_master
|
61
|
-
find_all_by_instance_roles(:app_master).first || find_all_by_instance_roles(:solo).first
|
62
|
-
end
|
63
|
-
|
64
|
-
def db_master
|
65
|
-
find_all_by_instance_roles(:db_master).first || find_all_by_instance_roles(:solo).first
|
66
|
-
end
|
67
|
-
|
68
|
-
def app
|
69
|
-
find_all_by_instance_roles :app, :app_master, :solo
|
70
|
-
end
|
71
|
-
|
72
|
-
def db
|
73
|
-
find_all_by_instance_roles :db_master, :db_slave, :solo
|
74
|
-
end
|
75
|
-
|
76
|
-
def utility
|
77
|
-
find_all_by_instance_roles :utility
|
78
|
-
end
|
79
|
-
|
80
|
-
def all
|
81
|
-
data.map { |k, _| new k }
|
82
|
-
end
|
83
|
-
|
84
|
-
def with_roles
|
85
|
-
all.reject { |i| i.instance_role == 'unknown' }
|
86
|
-
end
|
87
|
-
|
88
|
-
def current
|
89
|
-
new cached_current_instance_id
|
90
|
-
end
|
91
|
-
|
92
|
-
def first
|
93
|
-
new data.to_a.first.first
|
94
|
-
end
|
95
|
-
|
96
|
-
def find_by_instance_id(instance_id)
|
97
|
-
new instance_id
|
98
|
-
end
|
99
|
-
|
100
|
-
def find_all_by_instance_roles(*args)
|
101
|
-
data.select { |_, v| Array.wrap(args).map(&:to_s).include? v[:instance_role] }.map { |k, _| new k }
|
102
|
-
end
|
103
|
-
|
104
|
-
def clear
|
105
|
-
raise "[EY CLOUD AWARENESS GEM] Can't clear if we used from_hash" if self.proxy
|
106
|
-
@_data = nil
|
107
|
-
@_dna = nil
|
108
|
-
cached_ec2_instance_descriptions true
|
109
|
-
cached_current_security_group true
|
110
|
-
cached_current_instance_id true
|
111
|
-
end
|
112
|
-
|
113
|
-
def data
|
114
|
-
return @_data if @_data
|
115
|
-
raise "[EY CLOUD AWARENESS GEM] Can't calculate data if we used from_hash" if self.proxy
|
116
|
-
@_data = Hash.new
|
117
|
-
cached_ec2_instance_descriptions.each do |ec2_instance_description|
|
118
|
-
@_data[ec2_instance_description['instanceId']] ||= Hash.new
|
119
|
-
member = @_data[ec2_instance_description['instanceId']]
|
120
|
-
# using instance as a pointer
|
121
|
-
if dna[:instance_role] == 'solo'
|
122
|
-
member[:instance_role] = 'solo'
|
123
|
-
elsif dna[:db_host] == ec2_instance_description['dnsName'] or dna[:db_host] == ec2_instance_description['privateDnsName']
|
124
|
-
member[:instance_role] = 'db_master'
|
125
|
-
elsif Array.wrap(dna[:db_slaves]).include? ec2_instance_description['privateDnsName']
|
126
|
-
member[:instance_role] = 'db_slave'
|
127
|
-
elsif Array.wrap(dna[:utility_instances]).include? ec2_instance_description['privateDnsName']
|
128
|
-
member[:instance_role] = 'utility'
|
129
|
-
elsif dna[:master_app_server][:private_dns_name] == ec2_instance_description['privateDnsName']
|
130
|
-
member[:instance_role] = 'app_master'
|
131
|
-
elsif ec2_instance_description['instanceState']['name'] == 'running'
|
132
|
-
member[:instance_role] = 'app'
|
133
|
-
else
|
134
|
-
member[:instance_role] = 'unknown'
|
135
|
-
end
|
136
|
-
member[:group_id] = cached_current_security_group
|
137
|
-
member[:users] = dna[:users]
|
138
|
-
member[:environment] = dna[:environment]
|
139
|
-
@_environment ||= dna[:environment]
|
140
|
-
|
141
|
-
ec2_instance_description.each do |raw_k, raw_v|
|
142
|
-
k = raw_k.underscore.to_sym
|
143
|
-
next if member.keys.include? k
|
144
|
-
member[k] = raw_v
|
145
|
-
end
|
146
|
-
end
|
147
|
-
@_data.recursive_symbolize_keys!
|
148
|
-
@_data
|
149
|
-
end
|
150
|
-
|
151
|
-
def dna
|
152
|
-
raise "[EY CLOUD AWARENESS GEM] Can't see DNA if we used from_hash" if self.proxy
|
153
|
-
raise "[EY CLOUD AWARENESS GEM] Can't read DNA from #{DNA_PATH}! You should put 'sudo chmod a+r /etc/chef/dna.json' your your before_migrate.rb!" unless File.readable?(DNA_PATH)
|
154
|
-
@_dna ||= JSON.load(IO.read(DNA_PATH)).recursive_symbolize_keys!
|
155
|
-
end
|
156
|
-
|
157
|
-
private
|
158
|
-
|
159
|
-
def cached_current_instance_id(refresh = false)
|
160
|
-
raise "[EY CLOUD AWARENESS GEM] Can't call current_instance_id if we used from_hash" if self.proxy
|
161
|
-
if refresh or !File.readable?(CURRENT_INSTANCE_ID_CACHE_PATH)
|
162
|
-
@_cached_current_instance_id = open("http://169.254.169.254/latest/meta-data/instance-id").gets
|
163
|
-
begin
|
164
|
-
FileUtils.mkdir_p File.dirname(CURRENT_INSTANCE_ID_CACHE_PATH)
|
165
|
-
File.open(CURRENT_INSTANCE_ID_CACHE_PATH, 'w') { |f| f.write @_cached_current_instance_id }
|
166
|
-
rescue Errno::EACCES
|
167
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching current instance because #{CURRENT_INSTANCE_ID_CACHE_PATH} can't be written to"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
@_cached_current_instance_id ||= IO.read(CURRENT_INSTANCE_ID_CACHE_PATH)
|
171
|
-
end
|
172
|
-
|
173
|
-
|
174
|
-
def cached_current_security_group(refresh = false)
|
175
|
-
raise "[EY CLOUD AWARENESS GEM] Can't call current_security_group if we used from_hash" if self.proxy
|
176
|
-
if refresh or !File.readable?(CURRENT_SECURITY_GROUP_CACHE_PATH)
|
177
|
-
@_cached_current_security_group = open('http://169.254.169.254/latest/meta-data/security-groups').gets
|
178
|
-
raise "[EY CLOUD AWARENESS GEM] Don't know how to deal with (possibly) multiple security group: #{@_cached_current_security_group}" if @_cached_current_security_group =~ /,;/
|
179
|
-
begin
|
180
|
-
FileUtils.mkdir_p File.dirname(CURRENT_SECURITY_GROUP_CACHE_PATH)
|
181
|
-
File.open(CURRENT_SECURITY_GROUP_CACHE_PATH, 'w') { |f| f.write @_cached_current_security_group }
|
182
|
-
rescue Errno::EACCES
|
183
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching current security group because #{CURRENT_SECURITY_GROUP_CACHE_PATH} can't be written to"
|
184
|
-
end
|
185
|
-
end
|
186
|
-
@_cached_current_security_group ||= IO.read(CURRENT_SECURITY_GROUP_CACHE_PATH)
|
187
|
-
end
|
188
|
-
|
189
|
-
def cached_ec2_instance_descriptions(refresh = false)
|
190
|
-
raise "[EY CLOUD AWARENESS GEM] Can't call cached_ec2_instance_descriptions if we used from_hash" if self.proxy
|
191
|
-
if refresh or !File.readable?(INSTANCE_DESCRIPTIONS_CACHE_PATH)
|
192
|
-
ec2 = AWS::EC2::Base.new :access_key_id => dna[:aws_secret_id], :secret_access_key => dna[:aws_secret_key]
|
193
|
-
@_cached_ec2_instance_descriptions = ec2.describe_instances
|
194
|
-
@_cached_ec2_instance_descriptions.recursive_kill_xml_item_keys!
|
195
|
-
@_cached_ec2_instance_descriptions = @_cached_ec2_instance_descriptions['reservationSet'].select { |hash| cached_current_security_group.include? hash['groupSet'].first['groupId'] }
|
196
|
-
@_cached_ec2_instance_descriptions.map! { |hash| hash['instancesSet'].first }
|
197
|
-
begin
|
198
|
-
FileUtils.mkdir_p File.dirname(INSTANCE_DESCRIPTIONS_CACHE_PATH)
|
199
|
-
File.open(INSTANCE_DESCRIPTIONS_CACHE_PATH, 'w') { |f| f.write @_cached_ec2_instance_descriptions.to_yaml }
|
200
|
-
rescue Errno::EACCES
|
201
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching instance data because #{INSTANCE_DESCRIPTIONS_CACHE_PATH} can't be written to"
|
202
|
-
end
|
203
|
-
end
|
204
|
-
@_cached_ec2_instance_descriptions ||= YAML.load(IO.read(INSTANCE_DESCRIPTIONS_CACHE_PATH))
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
@@ -1,111 +0,0 @@
|
|
1
|
-
require 'ey_cloud_awareness'
|
2
|
-
require 'pp'
|
3
|
-
|
4
|
-
task :eyc_setup, :roles => :app_master do
|
5
|
-
version = Gem.searcher.find('ey_cloud_awareness').version.to_s
|
6
|
-
begin
|
7
|
-
run "gem list ey_cloud_awareness --installed --version #{version}"
|
8
|
-
rescue
|
9
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] app_master doesn't have ey_cloud_awareness --version #{version} installed. You need to have the exact same version installed."
|
10
|
-
raise $!
|
11
|
-
end
|
12
|
-
|
13
|
-
upload File.expand_path(File.join(File.dirname(__FILE__), 'ey_cloud_awareness.rake')), "#{deploy_to}/current/lib/tasks/ey_cloud_awareness.rake"
|
14
|
-
|
15
|
-
output = capture("cd #{deploy_to}/current && rake --silent eyc:to_json RAILS_ENV=#{rails_env}").gsub(/\s+/, ' ')
|
16
|
-
if /(\{.*\})/.match(output)
|
17
|
-
begin
|
18
|
-
set :eyc_proxy, EngineYardCloudInstance.from_hash(ActiveSupport::JSON.decode($1))
|
19
|
-
rescue
|
20
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Couldn't parse JSON, so just dumping what we got"
|
21
|
-
$stderr.puts $1
|
22
|
-
raise $!
|
23
|
-
end
|
24
|
-
else
|
25
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Didn't get JSON we recognized back, just dumping what we got"
|
26
|
-
$stderr.puts output
|
27
|
-
raise
|
28
|
-
end
|
29
|
-
role :db_master, eyc_proxy.db_master.dns_name
|
30
|
-
eyc_proxy.app.each do |i|
|
31
|
-
role :app, i.dns_name
|
32
|
-
role :web, i.dns_name # assuming that you've got nginx on all of your instances
|
33
|
-
end
|
34
|
-
eyc_proxy.db.each { |i| role :db, i.dns_name }
|
35
|
-
end
|
36
|
-
|
37
|
-
namespace :eyc do
|
38
|
-
%w{ app db utility all first }.each do |name|
|
39
|
-
task name, :roles => :app_master do
|
40
|
-
pp eyc_proxy.send(name).map(&:to_hash)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# Used by the eyc:ssh cap task to insert host information into ~/.ssh/config
|
46
|
-
class StringReplacer
|
47
|
-
NEWLINE = "AijQA6tD1wkWqgvLzXD"
|
48
|
-
START_MARKER = '# START StringReplacer %s -- DO NOT MODIFY'
|
49
|
-
END_MARKER = "# END StringReplacer %s -- DO NOT MODIFY#{NEWLINE}"
|
50
|
-
|
51
|
-
attr_accessor :path
|
52
|
-
def initialize(path)
|
53
|
-
@path = path
|
54
|
-
end
|
55
|
-
|
56
|
-
def replace!(replacement, id = 1)
|
57
|
-
new_path = "#{path}.new"
|
58
|
-
backup_path = "#{path}.bak"
|
59
|
-
current_start_marker = START_MARKER % id.to_s
|
60
|
-
current_end_marker = END_MARKER % id.to_s
|
61
|
-
replacement_with_markers = current_start_marker + NEWLINE + replacement + NEWLINE + current_end_marker
|
62
|
-
text = IO.read(path).gsub("\n", NEWLINE)
|
63
|
-
if text.include? current_start_marker
|
64
|
-
text.gsub! /#{Regexp.escape current_start_marker}.*#{Regexp.escape current_end_marker}/, replacement_with_markers
|
65
|
-
else
|
66
|
-
text << NEWLINE << replacement_with_markers
|
67
|
-
end
|
68
|
-
text.gsub! NEWLINE, "\n"
|
69
|
-
File.open(new_path, 'w') { |f| f.write text }
|
70
|
-
FileUtils.mv path, backup_path
|
71
|
-
FileUtils.mv new_path, path
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
namespace :eyc do
|
76
|
-
task :ssh, :roles => :app_master do
|
77
|
-
replacement = []
|
78
|
-
counter = 0
|
79
|
-
eyc_proxy.with_roles.each do |instance|
|
80
|
-
case instance.instance_role
|
81
|
-
when 'db_master'
|
82
|
-
explanation = ''
|
83
|
-
shorthand = 'db_master'
|
84
|
-
when 'app_master'
|
85
|
-
explanation = ''
|
86
|
-
shorthand = 'app_master'
|
87
|
-
when 'solo'
|
88
|
-
explanation = ''
|
89
|
-
shorthand = 'solo'
|
90
|
-
else
|
91
|
-
explanation = " (#{counter})"
|
92
|
-
shorthand = "#{instance.instance_role}#{counter}"
|
93
|
-
counter += 1
|
94
|
-
end
|
95
|
-
replacement << %{
|
96
|
-
# #{instance.instance_role}#{explanation}
|
97
|
-
Host #{eyc_proxy.environment[:name]}-#{shorthand}
|
98
|
-
Hostname #{instance.dns_name}
|
99
|
-
User #{instance.users.first[:username]}
|
100
|
-
StrictHostKeyChecking no
|
101
|
-
}
|
102
|
-
end
|
103
|
-
replacement = replacement.join
|
104
|
-
ssh_config_path = File.expand_path("~/.ssh/config")
|
105
|
-
r = StringReplacer.new ssh_config_path
|
106
|
-
r.replace! replacement, eyc_proxy.environment[:name]
|
107
|
-
|
108
|
-
$stderr.puts "[EY CLOUD AWARENESS GEM] Added this to #{ssh_config_path}"
|
109
|
-
$stderr.puts replacement
|
110
|
-
end
|
111
|
-
end
|