ey_cloud_awareness 0.1.14 → 0.2.0

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.
@@ -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