ey_cloud_awareness 0.1.1 → 0.1.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/README.rdoc +87 -12
- data/VERSION +1 -1
- data/ey_cloud_awareness.gemspec +4 -2
- data/lib/engine_yard_cloud_instance.rb +53 -20
- data/lib/ey_cloud_awareness.rb +5 -0
- data/lib/tasks/capistrano_tasks.rb +39 -0
- data/lib/tasks/ey_cloud_awareness.rake +7 -0
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -1,17 +1,92 @@
|
|
1
1
|
= ey_cloud_awareness
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
3
|
+
Make your EngineYard cloud instances aware of each other.
|
4
|
+
|
5
|
+
Never download a new <tt>deploy.rb</tt> again.
|
6
|
+
|
7
|
+
== Quick start
|
8
|
+
|
9
|
+
Put this in <tt>config/environment.rb</tt>:
|
10
|
+
|
11
|
+
config.gem 'ey_cloud_awareness', :version => '[WHATEVER THE CURRENT GEM VERSION IS]', :lib => false, :source => 'http://gemcutter.org'
|
12
|
+
|
13
|
+
Put this in your <tt>config/deploy.rb</tt> (or wherever your deploy-related Capfile is):
|
14
|
+
|
15
|
+
load "#{Gem.searcher.find('ey_cloud_awareness').full_gem_path}/lib/tasks/capistrano_tasks.rb"
|
16
|
+
|
17
|
+
task :my_app_production do
|
18
|
+
role :app_master, 'my_app.com' # or you can use its Elastic IP
|
19
|
+
set :rails_env, 'production' # required
|
20
|
+
set :deploy_to, '/data/my_app' # required
|
21
|
+
find_and_execute_task 'eyc_setup' # note that we don't use eyc: namespace
|
22
|
+
end
|
23
|
+
|
24
|
+
task :my_app_staging do
|
25
|
+
role :app_master, 'staging.my_app.com' # or you can use its Elastic IP
|
26
|
+
set :rails_env, 'production' # required
|
27
|
+
set :deploy_to, '/data/my_app' # required
|
28
|
+
find_and_execute_task 'eyc_setup' # note that we don't use eyc: namespace
|
29
|
+
end
|
30
|
+
|
31
|
+
# add more tasks if you have more cloud environments
|
32
|
+
|
33
|
+
Now you should be able to eycap stuff like:
|
34
|
+
|
35
|
+
cap my_app_production monit:status
|
36
|
+
|
37
|
+
...and <b>capistrano will always have a fresh list of your environment's instances</b>. Roles like <tt>:app</tt>, <tt>:db</tt>, and <tt>:utility</tt> are set properly.
|
38
|
+
|
39
|
+
== Just dumping information about your instances
|
40
|
+
|
41
|
+
Once you've done the quickstart, try:
|
42
|
+
|
43
|
+
cap my_app_production eyc:app # gets a list of your app instances, including app_master
|
44
|
+
cap my_app_production eyc:utility # ditto for utility instances
|
45
|
+
cap my_app_production eyc:db # gets your master db instance (FIXME: I don't think it will find slaves)
|
46
|
+
cap my_app_production eyc:all # gets a list of all your instances
|
47
|
+
|
48
|
+
== Using the EngineYardCloudInstance class inside Rails
|
49
|
+
|
50
|
+
I run a memcached server on every app instance, so I have this in <tt>config/environment.rb</tt>:
|
51
|
+
|
52
|
+
Rails::Initializer.run do |config|
|
53
|
+
[...]
|
54
|
+
config.cache_store = :mem_cache_store, EngineYardCloudInstance.app.map { |i| "#{i.private_dns_name}:11211" }
|
55
|
+
[...]
|
56
|
+
end
|
57
|
+
|
58
|
+
Or whatever you want:
|
59
|
+
|
60
|
+
>> all_app_instances = EngineYardCloudInstance.app
|
61
|
+
=> [#<EngineYardCloudInstance:0xb5e12f70 @instance_id="i-50cf5838">, #<EngineYardCloudInstance:0xb5e12f5c @instance_id="i-dc9207b4">, #<EngineYardCloudInstance:0xb5e12f34 @instance_id="i-b2d84fda">]
|
62
|
+
>> all_app_instances.first.dns_name
|
63
|
+
=> "ec2-67-202-43-40.compute-1.amazonaws.com"
|
64
|
+
>> pp all_app_instances.first.to_hash
|
65
|
+
{:dns_name=>"ec2-67-202-43-40.compute-1.amazonaws.com",
|
66
|
+
:instance_role=>"app",
|
67
|
+
:aws_groups=>["ey-app1_production-1256085955-3205-13340"],
|
68
|
+
:aws_instance_id=>"i-50cf5838",
|
69
|
+
:private_dns_name=>"domU-12-31-39-01-99-D3.compute-1.internal",
|
70
|
+
:aws_state=>"running"}
|
71
|
+
=> nil
|
72
|
+
|
73
|
+
== A note on EngineYard dependence
|
74
|
+
|
75
|
+
This gem depends on
|
76
|
+
|
77
|
+
/etc/chef/dna.json
|
78
|
+
|
79
|
+
being present and containing certain attributes as named by EngineYard. Please let me know if something changes.
|
80
|
+
|
81
|
+
== A note on caching and network needs
|
82
|
+
|
83
|
+
I tried to be smart about caching the results of network calls.
|
84
|
+
|
85
|
+
Stuff like the current instance id, which is pulled from an EC2 metadata server, is stored in
|
86
|
+
|
87
|
+
CURRENT_INSTANCE_ID_CACHE_PATH = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/config/engine_yard_cloud_instance_id" : '/etc/engine_yard_cloud_instance_id'
|
88
|
+
|
89
|
+
Please let me know if this causes problems.
|
15
90
|
|
16
91
|
== Copyright
|
17
92
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.2
|
data/ey_cloud_awareness.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ey_cloud_awareness}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.2"
|
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{2009-11-
|
12
|
+
s.date = %q{2009-11-04}
|
13
13
|
s.description = %q{Make your EngineYard cloud instances aware of each other.}
|
14
14
|
s.email = %q{seamus@abshere.net}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -26,6 +26,8 @@ Gem::Specification.new do |s|
|
|
26
26
|
"ey_cloud_awareness.gemspec",
|
27
27
|
"lib/engine_yard_cloud_instance.rb",
|
28
28
|
"lib/ey_cloud_awareness.rb",
|
29
|
+
"lib/tasks/capistrano_tasks.rb",
|
30
|
+
"lib/tasks/ey_cloud_awareness.rake",
|
29
31
|
"spec/ey_cloud_awareness_spec.rb",
|
30
32
|
"spec/spec.opts",
|
31
33
|
"spec/spec_helper.rb"
|
@@ -1,23 +1,28 @@
|
|
1
1
|
class EngineYardCloudInstance
|
2
2
|
CURRENT_INSTANCE_ID_CACHE_PATH = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/config/engine_yard_cloud_instance_id" : '/etc/engine_yard_cloud_instance_id'
|
3
|
+
CURRENT_SECURITY_GROUPS_CACHE_PATH = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/config/engine_yard_cloud_security_groups" : '/etc/engine_yard_cloud_security_groups'
|
3
4
|
INSTANCE_DESCRIPTIONS_CACHE_PATH = defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/config/engine_yard_cloud_instance_descriptions.yml" : '/etc/engine_yard_cloud_instance_descriptions.yml'
|
4
5
|
DNA_PATH = '/etc/chef/dna.json'
|
5
6
|
|
6
7
|
attr_reader :instance_id
|
7
8
|
def initialize(instance_id)
|
8
|
-
@instance_id = instance_id.
|
9
|
+
@instance_id = instance_id.to_s
|
9
10
|
end
|
10
11
|
|
11
12
|
def valid?
|
12
13
|
data.present?
|
13
14
|
end
|
14
15
|
|
15
|
-
def
|
16
|
-
self.class.
|
16
|
+
def clear
|
17
|
+
self.class.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash
|
21
|
+
data.deep_copy
|
17
22
|
end
|
18
23
|
|
19
24
|
def data
|
20
|
-
self.class.data[instance_id]
|
25
|
+
self.class.data[instance_id.to_sym]
|
21
26
|
end
|
22
27
|
|
23
28
|
def method_missing(name, *args, &block)
|
@@ -29,7 +34,20 @@ class EngineYardCloudInstance
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
37
|
+
class_inheritable_accessor :proxy
|
38
|
+
|
32
39
|
class << self
|
40
|
+
def to_hash
|
41
|
+
clear
|
42
|
+
data.deep_copy
|
43
|
+
end
|
44
|
+
|
45
|
+
def from_hash(hash)
|
46
|
+
self.proxy = true
|
47
|
+
@_data = hash.recursive_symbolize_keys!
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
33
51
|
def app
|
34
52
|
find_all_by_instance_roles :app, :app_master
|
35
53
|
end
|
@@ -59,58 +77,73 @@ class EngineYardCloudInstance
|
|
59
77
|
end
|
60
78
|
|
61
79
|
def find_all_by_instance_roles(*args)
|
62
|
-
data.select { |_, v| Array.wrap(args).include? v[:instance_role] }.map { |k, _| new k }
|
80
|
+
data.select { |_, v| Array.wrap(args).map(&:to_s).include? v[:instance_role] }.map { |k, _| new k }
|
63
81
|
end
|
64
82
|
|
65
|
-
def
|
83
|
+
def clear
|
84
|
+
raise "[EY CLOUD AWARENESS GEM] Can't clear if we used from_hash" if self.proxy
|
66
85
|
@_data = nil
|
67
86
|
@_dna = nil
|
68
87
|
cached_instance_descriptions true
|
88
|
+
cached_current_security_groups true
|
69
89
|
cached_current_instance_id true
|
70
90
|
end
|
71
91
|
|
72
92
|
def data
|
73
93
|
return @_data if @_data
|
94
|
+
raise "[EY CLOUD AWARENESS GEM] Can't calculate data if we used from_hash" if self.proxy
|
74
95
|
hash = Hash.new
|
75
96
|
cached_instance_descriptions.each do |instance_description|
|
97
|
+
next unless Set.new(Array.wrap(cached_current_security_groups)).superset? Set.new(instance_description[:aws_groups])
|
76
98
|
hash[instance_description[:aws_instance_id]] ||= Hash.new
|
77
99
|
current = hash[instance_description[:aws_instance_id]]
|
78
100
|
# using current as a pointer
|
79
101
|
if dna[:db_host] == instance_description[:dns_name] or dna[:db_host] == instance_description[:private_dns_name]
|
80
|
-
current[:instance_role] =
|
102
|
+
current[:instance_role] = 'db'
|
81
103
|
elsif Array.wrap(dna[:utility_instances]).include? instance_description[:private_dns_name]
|
82
|
-
current[:instance_role] =
|
104
|
+
current[:instance_role] = 'utility'
|
83
105
|
elsif dna[:master_app_server][:private_dns_name] == instance_description[:private_dns_name]
|
84
|
-
current[:instance_role] =
|
85
|
-
|
86
|
-
current[:instance_role] =
|
106
|
+
current[:instance_role] = 'app_master'
|
107
|
+
elsif instance_description[:aws_state] == 'running'
|
108
|
+
current[:instance_role] = 'app'
|
87
109
|
end
|
88
110
|
current[:private_dns_name] = instance_description[:private_dns_name]
|
89
111
|
current[:dns_name] = instance_description[:dns_name]
|
90
112
|
current[:aws_state] = instance_description[:aws_state]
|
113
|
+
current[:aws_groups] = instance_description[:aws_groups]
|
114
|
+
current[:aws_instance_id] = instance_description[:aws_instance_id]
|
91
115
|
end
|
92
116
|
@_data = hash.recursive_symbolize_keys!
|
93
117
|
end
|
94
118
|
|
95
119
|
def dna
|
120
|
+
raise "[EY CLOUD AWARENESS GEM] Can't see DNA if we used from_hash" if self.proxy
|
96
121
|
@_dna ||= JSON.load(IO.read(DNA_PATH)).recursive_symbolize_keys!
|
97
122
|
end
|
98
123
|
|
99
124
|
private
|
100
125
|
|
101
|
-
def cached_current_instance_id
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
126
|
+
# def cached_current_instance_id
|
127
|
+
# def cached_current_security_groups
|
128
|
+
%w{ current_instance_id current_security_groups }.each do |name|
|
129
|
+
eval %{
|
130
|
+
def cached_#{name}(refresh = false)
|
131
|
+
raise "[EY CLOUD AWARENESS GEM] Can't call #{name} if we used from_hash" if self.proxy
|
132
|
+
if refresh or !File.readable?(#{name.upcase}_CACHE_PATH)
|
133
|
+
@_cached_#{name} = open("http://169.254.169.254/latest/meta-data/#{name.gsub('current_', '').dasherize}").gets
|
134
|
+
begin
|
135
|
+
File.open(#{name.upcase}_CACHE_PATH, 'w') { |f| f.write @_cached_#{name} }
|
136
|
+
rescue Errno::EACCES
|
137
|
+
$stderr.puts "[EY CLOUD AWARENESS GEM] Not caching #{name.humanize.downcase} because \#{#{name.upcase}_CACHE_PATH} can't be written to"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
@_cached_#{name} ||= IO.read(#{name.upcase}_CACHE_PATH)
|
108
141
|
end
|
109
|
-
|
110
|
-
@_cached_current_instance_id ||= IO.read(CURRENT_INSTANCE_ID_CACHE_PATH)
|
142
|
+
}
|
111
143
|
end
|
112
144
|
|
113
145
|
def cached_instance_descriptions(refresh = false)
|
146
|
+
raise "[EY CLOUD AWARENESS GEM] Can't call cached_instance_descriptions if we used from_hash" if self.proxy
|
114
147
|
if refresh or !File.readable?(INSTANCE_DESCRIPTIONS_CACHE_PATH)
|
115
148
|
ec2 = RightAws::Ec2.new dna[:aws_secret_id], dna[:aws_secret_key]
|
116
149
|
@_cached_instance_descriptions = ec2.describe_instances.map(&:recursive_symbolize_keys!)
|
data/lib/ey_cloud_awareness.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'open-uri'
|
2
|
+
require 'set'
|
2
3
|
require 'active_support'
|
3
4
|
require 'right_aws'
|
4
5
|
require File.expand_path(File.join(File.dirname(__FILE__), 'engine_yard_cloud_instance'))
|
@@ -10,4 +11,8 @@ class Hash
|
|
10
11
|
values.select { |v| v.is_a?(Hash) }.each { |h| h.recursive_symbolize_keys! }
|
11
12
|
self
|
12
13
|
end
|
14
|
+
|
15
|
+
def deep_copy
|
16
|
+
Marshal.load(Marshal.dump(self))
|
17
|
+
end
|
13
18
|
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
eyc_proxy.app.each { |i| role :app, i.dns_name }
|
30
|
+
eyc_proxy.db.each { |i| role :db, i.dns_name }
|
31
|
+
end
|
32
|
+
|
33
|
+
namespace :eyc do
|
34
|
+
%w{ app db utility all first }.each do |name|
|
35
|
+
task name, :roles => :app_master do
|
36
|
+
pp eyc_proxy.send(name).map(&:to_hash)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ey_cloud_awareness
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Seamus Abshere
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-11-
|
12
|
+
date: 2009-11-04 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -61,6 +61,8 @@ files:
|
|
61
61
|
- ey_cloud_awareness.gemspec
|
62
62
|
- lib/engine_yard_cloud_instance.rb
|
63
63
|
- lib/ey_cloud_awareness.rb
|
64
|
+
- lib/tasks/capistrano_tasks.rb
|
65
|
+
- lib/tasks/ey_cloud_awareness.rake
|
64
66
|
- spec/ey_cloud_awareness_spec.rb
|
65
67
|
- spec/spec.opts
|
66
68
|
- spec/spec_helper.rb
|