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.
@@ -2,13 +2,13 @@
2
2
 
3
3
  This gem makes it a little easier to live on the EngineYard cloud:
4
4
 
5
- * automatically run cap tasks on all your instances
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
- == Don't read this
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
- `sudo chmod a+r /etc/chef/dna.json`
19
+ sudo 'chmod a+r /etc/chef/dna.json'
20
20
 
21
- to your before_migrate.rb script.
21
+ to your before_restart.rb hook.
22
22
 
23
- == Quick start
23
+ == Using the commandline tool directly
24
24
 
25
- Put this in <tt>config/environment.rb</tt>:
25
+ Running this command only works from EngineYard AppCloud Amazon EC2 instances.
26
26
 
27
- config.gem 'ey_cloud_awareness', :version => '[WHATEVER THE CURRENT GEM VERSION IS]', :lib => false, :source => 'http://gemcutter.org'
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
- Put this in your <tt>config/deploy.rb</tt> (or whereever your Capfile is):
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/tasks/capistrano_tasks.rb"
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' # or you can use its Elastic IP
36
- set :rails_env, 'production' # required
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' # or you can use its Elastic IP
43
- set :rails_env, 'production' # required
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
- That should be all.
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
- Every time you use them, a special <tt>eyc_setup</tt> task is run that gets a fresh list of your environment's instances and sets roles like <tt>:app</tt>, <tt>:web</tt>, and <tt>:db</tt>.
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
- Let's say you want to ssh into...
86
+ You can easily create SSH aliases like...
67
87
 
68
88
  ssh my_production-app_master
69
89
 
70
- Well, you need to keep your <tt>~/.ssh/config</tt> up-to-date. Easy!
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
- ... and it adds
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-utility1
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
- Note that the numbers after app [slaves], db [slaves], and utility [slaves] may change every time you run the task.
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:app # gets a list of your app instances, including app_master
135
- cap my_production eyc:utility # ditto for utility instances
136
- cap my_production eyc:db # ditto for db instances
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
- == Using the EngineYardCloudInstance class inside Rails
117
+ == Making your instances aware of each other
140
118
 
141
- I run a memcached server on every app instance, so I have this in <tt>config/environment.rb</tt>:
119
+ You can use EngineYardCloudInstance to pull up information on other instances in an environment:
142
120
 
143
- Rails::Initializer.run do |config|
144
- [...]
145
- config.cache_store = :mem_cache_store, EngineYardCloudInstance.app.map { |i| "#{i.private_dns_name}:11211" }
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
- Or whatever you want:
126
+ You can also inspect the whole set of metadata itself. This may change over time:
150
127
 
151
- >> all_app_instances = EngineYardCloudInstance.app
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
- :instance_role=>"solo",
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 current instance id, which is pulled from an EC2 metadata server, is stored in
176
+ Stuff like the present instance id, which is pulled from an EC2 metadata server, is stored in
204
177
 
205
- CURRENT_INSTANCE_ID_CACHE_PATH = File.expand_path '~/.ey_cloud_awareness/engine_yard_cloud_instance_id'
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
- # gem.rubyforge_project = "ey_cloud_awareness"
14
- gem.add_dependency 'json', '>=1.2.3'
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.10'
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
- require 'spec/rake/spectask'
29
- Spec::Rake::SpecTask.new(:spec) do |spec|
30
- spec.libs << 'lib' << 'spec'
31
- spec.spec_files = FileList['spec/**/*_spec.rb']
32
- end
33
-
34
- Spec::Rake::SpecTask.new(:rcov) do |spec|
35
- spec.libs << 'lib' << 'spec'
36
- spec.pattern = 'spec/**/*_spec.rb'
37
- spec.rcov = true
38
- end
39
-
40
- task :spec => :check_dependencies
41
-
42
- task :default => :spec
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.14
1
+ 0.2.0
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ey_cloud_awareness'
4
+
5
+ EngineYardCloudInstance.clear
6
+ puts EngineYardCloudInstance.to_json
@@ -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.1.14"
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-04-16}
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/tasks/capistrano_tasks.rb",
30
- "lib/tasks/ey_cloud_awareness.rake",
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.6}
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::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
50
- s.add_runtime_dependency(%q<json>, [">= 1.2.3"])
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.10"])
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<json>, [">= 1.2.3"])
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.10"])
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<json>, [">= 1.2.3"])
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.10"])
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
 
@@ -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/inheritable_attributes
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
- class Hash
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
- - 1
8
- - 14
9
- version: 0.1.14
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-04-16 00:00:00 -04:00
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: json
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
- - 2
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
- - 10
59
- version: 0.9.10
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/tasks/capistrano_tasks.rb
96
- - lib/tasks/ey_cloud_awareness.rake
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.6
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
@@ -1,7 +0,0 @@
1
- require 'ey_cloud_awareness'
2
-
3
- namespace :eyc do
4
- task :to_json do
5
- puts EngineYardCloudInstance.to_hash.to_json
6
- end
7
- end