emissary 1.3.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.
- data/LICENSE +203 -0
- data/README.txt +54 -0
- data/VERSION.yml +4 -0
- data/bin/emissary +196 -0
- data/bin/emissary-setup +75 -0
- data/etc/emissary/config.ini +13 -0
- data/etc/init.d/emissary +50 -0
- data/lib/emissary.rb +223 -0
- data/lib/emissary/agent.rb +61 -0
- data/lib/emissary/agent/emissary.rb +163 -0
- data/lib/emissary/agent/error.rb +26 -0
- data/lib/emissary/agent/file.rb +26 -0
- data/lib/emissary/agent/gem.rb +42 -0
- data/lib/emissary/agent/mysql.rb +219 -0
- data/lib/emissary/agent/ping.rb +37 -0
- data/lib/emissary/agent/proxy.rb +26 -0
- data/lib/emissary/agent/rabbitmq.rb +233 -0
- data/lib/emissary/agent/sshkeys.rb +152 -0
- data/lib/emissary/agent/stats.rb +96 -0
- data/lib/emissary/agent/test.rb +40 -0
- data/lib/emissary/config.rb +231 -0
- data/lib/emissary/core_ext/blank.rb +60 -0
- data/lib/emissary/core_ext/misc_object.rb +21 -0
- data/lib/emissary/core_ext/symbolize.rb +33 -0
- data/lib/emissary/daemon.rb +404 -0
- data/lib/emissary/errors.rb +106 -0
- data/lib/emissary/gem_helper.rb +183 -0
- data/lib/emissary/identity.rb +183 -0
- data/lib/emissary/identity/ec2.rb +64 -0
- data/lib/emissary/identity/unix.rb +67 -0
- data/lib/emissary/logger.rb +130 -0
- data/lib/emissary/message.rb +217 -0
- data/lib/emissary/operator.rb +274 -0
- data/lib/emissary/operator/amqp.rb +203 -0
- data/lib/emissary/server.rb +98 -0
- data/lib/emissary/servolux.rb +75 -0
- metadata +262 -0
@@ -0,0 +1,106 @@
|
|
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
|
+
|
18
|
+
class Error< StandardError
|
19
|
+
class ConnectionError < StandardError; end
|
20
|
+
class InvalidMessageFormat < StandardError; end
|
21
|
+
class NotImplementedError < StandardError; end
|
22
|
+
|
23
|
+
attr_reader :origin
|
24
|
+
|
25
|
+
def self.new(*args) #:nodoc:
|
26
|
+
allocate.instance_eval(<<-EOS, __FILE__, __LINE__)
|
27
|
+
alias :original_instance_of? :instance_of?
|
28
|
+
alias :original_kind_of? :kind_of?
|
29
|
+
|
30
|
+
def instance_of? klass
|
31
|
+
self.original_instance_of? klass or origin.instance_of? klass
|
32
|
+
end
|
33
|
+
|
34
|
+
def kind_of? klass
|
35
|
+
self.original_kind_of? klass or origin.kind_of? klass
|
36
|
+
end
|
37
|
+
|
38
|
+
# Call a superclass's #initialize if it has one
|
39
|
+
initialize(*args)
|
40
|
+
|
41
|
+
self
|
42
|
+
EOS
|
43
|
+
end
|
44
|
+
|
45
|
+
def initialize(origin = Exception, message = '')
|
46
|
+
|
47
|
+
case origin
|
48
|
+
when Exception
|
49
|
+
@origin = origin
|
50
|
+
when Class
|
51
|
+
@origin = origin.new message
|
52
|
+
else
|
53
|
+
@origin = Exception.new message
|
54
|
+
end
|
55
|
+
|
56
|
+
super message
|
57
|
+
end
|
58
|
+
|
59
|
+
def origin_backtrace
|
60
|
+
origin.backtrace
|
61
|
+
end
|
62
|
+
|
63
|
+
def origin_message
|
64
|
+
origin.message
|
65
|
+
end
|
66
|
+
|
67
|
+
def message
|
68
|
+
"#{super}\n\t#{self.backtrace.join("\n\t")}\n" +
|
69
|
+
"Origin: #{origin.class}: #{origin_message}\n\t#{origin_backtrace.join("\n\t")}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
if __FILE__ == $0
|
75
|
+
class Emissary::TrackingError < Emissary::Error; end
|
76
|
+
class Emissary::NetworkEncapsulatedError < Emissary::Error; end
|
77
|
+
def a() raise ArgumentError, 'testing'; end
|
78
|
+
def b() a; end
|
79
|
+
def c() b; end
|
80
|
+
def d() c; end
|
81
|
+
def test() d; end
|
82
|
+
|
83
|
+
begin
|
84
|
+
begin
|
85
|
+
begin
|
86
|
+
begin
|
87
|
+
test
|
88
|
+
rescue Exception => e
|
89
|
+
raise Emissary::Error.new e, 'general error'
|
90
|
+
end
|
91
|
+
rescue Emissary::Error => e
|
92
|
+
puts "----------------- 1 -----------------\n#{e.message}"
|
93
|
+
raise Emissary::TrackingError.new(e, 'testing tracking')
|
94
|
+
end
|
95
|
+
rescue Emissary::Error => e
|
96
|
+
puts "----------------- 2 -----------------\n#{e.message}"
|
97
|
+
raise Emissary::NetworkEncapsulatedError.new(e, 'testing network encapsulated')
|
98
|
+
end
|
99
|
+
rescue Emissary::Error => e
|
100
|
+
puts "----------------- 3 -----------------\n#{e.message}"
|
101
|
+
end
|
102
|
+
|
103
|
+
[ Exception, ArgumentError, Emissary::Error, Emissary::TrackingError, Emissary::NetworkEncapsulatedError].each do |k|
|
104
|
+
puts "e.kind_of?(#{k.name}): #{e.kind_of? k}"
|
105
|
+
end
|
106
|
+
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 '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
|