emissary 1.3.20 → 1.3.21

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,183 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'rubygems/spec_fetcher'
17
+ require 'rubygems/dependency_installer'
18
+ require 'rubygems/uninstaller'
19
+
20
+ module Emissary
21
+ class GemHelper
22
+ attr_reader :name, :version
23
+
24
+ def initialize(name)
25
+ @name = name
26
+ @version = current_version
27
+ end
28
+
29
+ def version
30
+ (v = @version.to_s.delete('<>!=~ ').strip).empty? ? 0 : v
31
+ end
32
+
33
+ def normalize_version value
34
+ case value
35
+ when Gem::Requirement, Gem::Version
36
+ value
37
+
38
+ when /^(<|<=|=|!=|>|>=|~>)\s*[0-9.]+$/
39
+ Gem::Requirement.new value
40
+
41
+ when /^[0-9.]+$/, String, Fixnum
42
+ Gem::Version.new value
43
+
44
+ when :current
45
+ Gem::Version.new(self.version)
46
+
47
+ when :all, :any, :installed, :available
48
+ Gem::Requirement.default
49
+
50
+ when :newer_than_me, :latest, :newest
51
+ Gem::Requirement.new "> #{self.version}"
52
+
53
+ when :older_than_me
54
+ Gem::Requirement.new "< #{self.version}"
55
+ end
56
+ end
57
+
58
+ def updateable?
59
+ installed? && !versions(:newer_than_me).empty?
60
+ end
61
+
62
+ def is_a_provider? version = :current
63
+ return false unless (spec = Gem.source_index.find_name(@name, normalize_version(version)).first)
64
+ !Gem::DependencyList.from_source_index(Gem.source_index).ok_to_remove?(spec.full_name)
65
+ end
66
+ alias :have_dependents? :is_a_provider?
67
+ alias :has_dependents? :is_a_provider?
68
+
69
+ def removable? version = :current, ignore_dependents = false
70
+ installed?(version) && (!!ignore_dependents || !have_dependents?(version))
71
+ end
72
+
73
+ def installable? version = :any
74
+ !versions(:any).empty? && !installed?(version) && normalize_version(version) > @version
75
+ end
76
+
77
+ def installed? version = :any
78
+ !Gem.source_index.search(Gem::Dependency.new(name, normalize_version(version))).empty?
79
+ end
80
+
81
+ def current_version
82
+ return Gem::Version.new(0) unless installed?
83
+ specs = Gem.source_index.search(Gem::Dependency.new(name, normalize_version(:newest)))
84
+ specs.map { |spec| spec.version }.sort{ |a,b| a <=> b }.first
85
+ end
86
+
87
+ def versions which = :all
88
+ # don't include others for latest/newest - do include otherwise
89
+ others = [:latest, :newest ].include?(which) ? false : true
90
+ dependency = Gem::Dependency.new(name, normalize_version(which))
91
+ list = Gem::SpecFetcher.fetcher.find_matching(dependency, others).map do |spec, source_uri|
92
+ _, version = spec
93
+ [version, source_uri]
94
+ end.sort { |a,b| b[0] <=> a[0] }
95
+
96
+ which != :installed ? list : list.select { |v| installed? v[0] }
97
+ end
98
+
99
+ def dependents version = :current
100
+ specs = Gem.source_index.find_name(@name, normalize_version(version))
101
+ specs.inject([]) do |list,spec|
102
+ list |= spec.dependent_gems
103
+ list
104
+ end
105
+ end
106
+
107
+ def install version = :latest, source = :default
108
+ return @version unless installable? version
109
+
110
+ source = URI.parse(source).to_s rescue :default
111
+
112
+ options = {}
113
+ options[:version], source_uri = case version
114
+ when :latest
115
+ ver, uri = versions(:newer_than_me).first
116
+ [ ver, source == :default ? uri : source ]
117
+ else
118
+ ver, uri = versions(:newer_than_me).select { |v| v[0] == normalize_version(version) }.flatten
119
+ [ ver, source == :default ? uri : source ]
120
+ end
121
+
122
+ raise ArgumentError, "Bad version '#{version.inspect}' - can't install specified version." unless options[:version]
123
+
124
+ with_gem_source(source_uri) do
125
+ installer = Gem::DependencyInstaller.new options
126
+ installer.install name, options[:version]
127
+ @version = Gem::Version.new(normalize_version(options[:version]).to_s.delete('<>!=~ '))
128
+ end
129
+ end
130
+
131
+ def update version = :latest, source = :default, keep_old = true
132
+ return false unless updateable?
133
+ uninstall(@version, false) unless keep_old
134
+ install version, source
135
+ end
136
+
137
+ def uninstall version = :current, ignore_deps = false, remove_execs = false
138
+ options = {}
139
+ options[:ignore] = !!ignore_deps
140
+ options[:executables] = !!remove_execs
141
+
142
+ options[:version] = case version
143
+ when :current
144
+ @version
145
+ when :all
146
+ options[:all] = true
147
+ Gem::Requirement.default
148
+ else
149
+ versions(:all).select { |v| v[0] == normalize_version(version) }.flatten[0]
150
+ end
151
+
152
+ return true if not installed? version
153
+ raise ArgumentError, "Cannot uninstall version #{version.inspect} - is it installed? [#{options.inspect}]" unless options[:version]
154
+
155
+ unless removable?(version, !!ignore_deps)
156
+ msg = ['Refusing to uninstall gem required by other gems:']
157
+ dependents(options[:version]).each do |gem, dep, satlist|
158
+ msg << " Gem '#{gem.name}-#{gem.version}' depends on '#{dep.name}' (#{dep.requirement})";
159
+ end
160
+ raise Exception, msg.join("\n")
161
+ end
162
+
163
+ Gem::Uninstaller.new(name, options).uninstall
164
+ Gem.refresh
165
+ @version = Gem::Version.new '0'
166
+ end
167
+ alias :remove :uninstall
168
+
169
+ private
170
+
171
+ def with_gem_source(source)
172
+ begin
173
+ original_sources = Gem.sources.dup
174
+ Gem.sources.replace [source]
175
+ return yield
176
+ ensure
177
+ Gem.sources.replace original_sources
178
+ Gem.refresh
179
+ end
180
+ end
181
+ end
182
+
183
+ end
@@ -0,0 +1,183 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'net/http'
17
+ require 'socket'
18
+
19
+ module Emissary
20
+ class Identity
21
+
22
+ INTERNAL_IDENTITY_GLOB = ::File.join(Emissary::LIBPATH, 'emissary', 'identity', '*.rb')
23
+ EXTERNAL_IDENTITY_GLOB = ::File.join(Emissary::EXTERNAL_IDENTITIES, '*.rb')
24
+
25
+ attr_reader :loaded, :methods
26
+ alias :loaded? :loaded
27
+
28
+ private :initialize
29
+
30
+ def initialize
31
+ @loaded = false
32
+ @methods = nil
33
+ @identities = nil
34
+ end
35
+
36
+ def self.instance
37
+ @@instance ||= self.new
38
+ end
39
+
40
+ def self.new(*largs)
41
+ # prevent subclasses from being instantiated directly, except through
42
+ # the identities() method of this parent class. This class is just a
43
+ # simple method factory and the children shouldn't need to be accessed
44
+ # except through the interface provided here.
45
+ if self == Emissary::Identity
46
+ raise Exception, "Cannot instantiate singleton class directly - use #{self.name}#instance" unless __caller__ == :instance
47
+ else
48
+ klass = self.name.split(/::/)[0..-2].join '::'; subklass = self.name.split(/::/).last
49
+ raise Exception, "Cannot instantiate '#{klass}' subclass '#{subklass}' directly" unless __caller__ == :identities
50
+ end
51
+
52
+ allocate.instance_eval(<<-EOS, __FILE__, __LINE__)
53
+ load_identities unless self.class != Emissary::Identity
54
+ self
55
+ EOS
56
+ end
57
+
58
+ # Delegation Factory - Uses registered high priority subclasses
59
+ # for delegation of method calls, falling back to lower priority
60
+ # subclasess when the method isn't available or throws a :pass
61
+ # in the higher priority subclass
62
+ #
63
+ def method_missing name, *args
64
+ # don't perform lookups on subclasses
65
+ super name, *args unless self.class == Emissary::Identity
66
+
67
+ name = name.to_sym
68
+ unless (@methods||={}).has_key? name
69
+ method_delegate = value = nil
70
+
71
+ # loop through each possible identity, higher priority identities
72
+ # first (0 == low, > 0 == higher).
73
+ identities.each do |id,object|
74
+ value = nil
75
+ if object.respond_to?(name)
76
+ method_delegate = catch(:pass) {
77
+ value = object.__send__(name, *args) # test for :pass request
78
+ object
79
+ }
80
+ break unless method_delegate.nil?
81
+ end
82
+ end
83
+
84
+ # if we've gone through all the possible delegates, then pass it
85
+ # up to the superclass (in this case, Object)
86
+ if method_delegate.nil?
87
+ return super(name, *args)
88
+ end
89
+
90
+ @methods[name] = method_delegate
91
+ return value
92
+ end
93
+
94
+ return @methods[name].__send__(name, *args)
95
+ end
96
+
97
+ def self.register name, opts = {}
98
+ priority = if name != :unix
99
+ opts[:priority].to_i > 0 ? opts.delete(:priority).to_i : 1
100
+ else
101
+ # unix identity always gets zero - though this locks us
102
+ # into *nix as our base. XXX maybe rethink this?
103
+ 0
104
+ end
105
+
106
+ (self.registry[priority] ||= []) << name
107
+ end
108
+
109
+ def self.exclude names
110
+ @@exclusions ||= []
111
+ @@exclusions |= (names.is_a?(String) ? names.split(/\s*,\s*/) : names.to_a.collect { |n| n.to_s.to_sym })
112
+ end
113
+
114
+ # Exclude an identity type when delegating identity method calls
115
+ #
116
+ def self.exclusions
117
+ @@exclusions ||= []
118
+ @@exclusions.to_a.map! { |e| e.to_sym }
119
+ end
120
+
121
+ def to_s
122
+ s = ''
123
+ s << '#<%s:0x%x ' % [ self.class, self.__id__ * 2]
124
+ s << to_h.inject([]) { |a,(k,v)| a << %Q|@#{k}="#{v}"| }.join(', ')
125
+ s << '>'
126
+ end
127
+
128
+ # Retrieve a list of all the available identifers through reflection of the subclasses
129
+ #
130
+ def identifiers
131
+ identities.inject([]) do |list,(id,object)|
132
+ list |= object.public_methods - object.class.superclass.public_methods - self.public_methods; list
133
+ end.sort.collect { |id| id.to_sym }
134
+ end
135
+
136
+ # Retreive a hash of all identifiers and their values
137
+ #
138
+ def to_h
139
+ Hash[(identifiers).zip(identifiers.collect { |identifier| self.send(identifier) })]
140
+ end
141
+
142
+ private
143
+
144
+ def self.registry
145
+ @@registry ||= []
146
+ end
147
+
148
+ # Loads all available identities included with this gem
149
+ #
150
+ def load_identities
151
+ return unless not !!loaded?
152
+
153
+ Dir[INTERNAL_IDENTITY_GLOB, EXTERNAL_IDENTITY_GLOB].reject do |id|
154
+ self.class.exclusions.include?("/#{id.to_s.downcase}.rb")
155
+ end.each do |file|
156
+ ::Emissary.logger.info "Loading Identity: #{file}"
157
+ require File.expand_path(file)
158
+ end
159
+
160
+ @loaded = true
161
+ end
162
+
163
+ # Generates list of possible identities after removing registered exclusions
164
+ # and then ordering them highest priority (> 0) to lowest priority (== 0)
165
+ #
166
+ def identities
167
+ @@identities ||= begin
168
+ self.class.registry.reverse.flatten.reject do |id|
169
+ id.nil? || self.class.exclusions.include?(id)
170
+ end.inject([]) do |a,id|
171
+ a << [ id.to_s, ::Emissary.klass_const("Emissary::Identity::#{id.to_s.capitalize}").new ]; a
172
+ end
173
+ end
174
+ end
175
+ end
176
+ end
177
+
178
+ if __FILE__ == $0
179
+ puts "-----> Name: " + (Emissary.identity.name rescue 'Could not acquire name')
180
+ puts "-----> P IP: " + (Emissary.identity.public_ip rescue 'Could not acquire public ip')
181
+ puts "-----> L IP: " + (Emissary.identity.local_ip rescue 'Could not acquire local_ip')
182
+ puts "-- Identity: " + (Emissary.identity.to_s rescue 'identity not set...')
183
+ end
@@ -0,0 +1,64 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ require 'net/http'
17
+ require 'socket'
18
+
19
+ module Emissary
20
+ class Identity::Ec2 < Identity
21
+ register :ec2, :priority => 25
22
+
23
+ QUERY_IP = '169.254.169.254'
24
+ INSTANCE_ID_PATH = '/latest/meta-data/instance-id'
25
+ LOCAL_IPV4_PATH = '/latest/meta-data/local-ipv4'
26
+ PUBLIC_IPV4_PATH = '/latest/meta-data/public-ipv4'
27
+
28
+ def initialize
29
+ @instance_id = nil
30
+ @local_ipv4 = nil
31
+ @public_ipv4 = nil
32
+ end
33
+
34
+ def instance_id
35
+ @instance_id ||= get(INSTANCE_ID_PATH)
36
+ end
37
+
38
+ alias :queue_name :instance_id
39
+
40
+ def local_ip
41
+ @local_ipv4 ||= get(LOCAL_IPV4_PATH)
42
+ end
43
+
44
+ def public_ip
45
+ @public_ipv4 ||= get(PUBLIC_IPV4_PATH)
46
+ end
47
+
48
+ private
49
+
50
+ def get uri
51
+ begin
52
+ http = Net::HTTP.new(QUERY_IP)
53
+ http.open_timeout = 0.5
54
+ http.read_timeout = 0.5
55
+ http.start do |http|
56
+ http.get(uri).body
57
+ end
58
+ rescue Exception => e
59
+ ::Emissary.logger.debug "Passing identifier request to next identity handler due to failed HTTP#get request: #{e.class.name}: #{e.message}"
60
+ throw :pass
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,67 @@
1
+ # Copyright 2010 The New York Times
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ #
16
+ module Emissary
17
+ class Identity::Unix < Identity
18
+ # defaults to priority 0 (which means, it's the last option if nothing else takes it)
19
+ register :unix
20
+
21
+ def name
22
+ @hostname ||= `uname -n`.strip
23
+ end
24
+
25
+ alias :queue_name :name
26
+
27
+ def roles
28
+ ENV['ROLES'] || ''
29
+ end
30
+
31
+ def instance_id
32
+ ENV['INSTANCE_ID'] || -1
33
+ end
34
+
35
+ def server_id
36
+ ENV['SERVER_ID'] || -1
37
+ end
38
+
39
+ def cluster_id
40
+ ENV['CLUSTER_ID'] || -1
41
+ end
42
+
43
+ def account_id
44
+ ENV['ACCOUNT_ID'] || -1
45
+ end
46
+
47
+ def local_ip
48
+ @ip_local ||= begin
49
+ # turn off reverse DNS resolution temporarily
50
+ orig, Socket.do_not_reverse_lookup = Socket.do_not_reverse_lookup, true
51
+ @ip_local = UDPSocket.open { |s| s.connect IP_CHECK_DOMAIN, 1; s.addr.last }
52
+ rescue
53
+ nil
54
+ ensure
55
+ Socket.do_not_reverse_lookup = orig rescue nil
56
+ end
57
+ end
58
+
59
+ def public_ip
60
+ @ip_public ||= begin
61
+ Net::HTTP.get(URI.parse(IP_CHECK_URL)).gsub(/.*?((\d{1,3}\.){3}\d{1,3}).*/m, '\1')
62
+ rescue
63
+ nil
64
+ end
65
+ end
66
+ end
67
+ end