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
data/bin/emissary-setup
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rubygems/source_index'
|
5
|
+
|
6
|
+
require 'fileutils'
|
7
|
+
|
8
|
+
class Object
|
9
|
+
def blank?
|
10
|
+
nil? or empty?
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if ARGV.length != 1
|
15
|
+
puts File.basename($0) << ': <config path>'
|
16
|
+
exit 0
|
17
|
+
end
|
18
|
+
|
19
|
+
config_path = ARGV[0].to_s
|
20
|
+
|
21
|
+
if not File.exists?(config_path)
|
22
|
+
puts "Directory [#{config_path}] doesn't exist - creating."
|
23
|
+
FileUtils.mkdir_p config_path
|
24
|
+
end
|
25
|
+
|
26
|
+
si = Gem::SourceIndex.from_gems_in(*Gem::SourceIndex.installed_spec_directories)
|
27
|
+
|
28
|
+
gem_spec = si.find_name('emissary', Gem::Requirement.default).last
|
29
|
+
|
30
|
+
emissary_etc_path = File.join(gem_spec.full_gem_path, 'etc')
|
31
|
+
|
32
|
+
if emissary_etc_path.blank?
|
33
|
+
puts "Unable to find gem 'emissary' in the default gem paths - are you sure you installed it?"
|
34
|
+
exit 1
|
35
|
+
end
|
36
|
+
|
37
|
+
puts "Emissary Path: '#{emissary_etc_path}'"
|
38
|
+
puts "Checking ETC files"
|
39
|
+
|
40
|
+
['emissary', 'init.d', 'sysconfig'].each do |part|
|
41
|
+
|
42
|
+
|
43
|
+
src_path = File.join(emissary_etc_path, part)
|
44
|
+
dst_path = File.join(config_path, part)
|
45
|
+
|
46
|
+
puts "Configuration files for '#{part}' found at: #{src_path}"
|
47
|
+
|
48
|
+
if not File.exists?(dst_path)
|
49
|
+
puts " - Creating missing '#{dst_path}' directory."
|
50
|
+
FileUtils.mkdir_p [ dst_path ]
|
51
|
+
end
|
52
|
+
|
53
|
+
Dir.glob(src_path + '/*').each do |file|
|
54
|
+
src_file = File.join(src_path, File.basename(file))
|
55
|
+
dst_file = File.join(dst_path, File.basename(file))
|
56
|
+
|
57
|
+
if not File.exists?(dst_file)
|
58
|
+
puts " - Copying '#{src_file}' --> '#{dst_file}'"
|
59
|
+
FileUtils.cp src_file, dst_file
|
60
|
+
else
|
61
|
+
puts " - Skipping overwrite of pre-existing configuration file '#{dst_file}'"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
print "Checking run directory - "
|
68
|
+
if not File.exists?('/var/run/emissary')
|
69
|
+
puts "missing!\n - Creating missing '/var/run/emissary' run directory"
|
70
|
+
FileUtils.mkdir_p ['/var/run/emissary']
|
71
|
+
else
|
72
|
+
puts "looks good."
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "Setup done - please adjust your configuration files to meet your particular needs now."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
[general]
|
2
|
+
|
3
|
+
# what operator types are monitoring for message events (comma seperated list)
|
4
|
+
operators = [ amqp ]
|
5
|
+
|
6
|
+
# pid_dir: Where to store the Process ID file which contains the id of the process
|
7
|
+
# and is used for stoping and reloading the service
|
8
|
+
pid_dir = /var/run
|
9
|
+
|
10
|
+
# log_level: the level of information to log. see 'man 3 syslog' for list of log levels
|
11
|
+
log_level = NOTICE
|
12
|
+
|
13
|
+
agents = [ all ]
|
data/etc/init.d/emissary
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/bin/sh
|
2
|
+
# $Id: emissary 496 2009-03-14 02:26:46Z ccorliss $
|
3
|
+
# emissary This shell script takes care of starting and stopping emissary.
|
4
|
+
#
|
5
|
+
# chkconfig: 2345 99 10
|
6
|
+
# description: emissary provides support for handling of network events.
|
7
|
+
|
8
|
+
CONFIG_FILE=/etc/emissary/config.ini
|
9
|
+
EMISSARY_EXECUTABLE="$(ruby -rrubygems -e 'puts Gem.bindir')/emissary"
|
10
|
+
|
11
|
+
[ -f "${CONFIG_FILE}" ] || exit 0
|
12
|
+
|
13
|
+
. /etc/rc.d/init.d/functions
|
14
|
+
|
15
|
+
if [ -f /etc/sysconfig/emissary ]; then
|
16
|
+
. /etc/sysconfig/emissary
|
17
|
+
fi
|
18
|
+
|
19
|
+
# See how we were called.
|
20
|
+
case "$1" in
|
21
|
+
start)
|
22
|
+
# Start daemon.
|
23
|
+
echo -n "Starting emissary: "
|
24
|
+
daemon ${EMISSARY_EXECUTABLE} -d --config-file ${CONFIG_FILE} start
|
25
|
+
touch /var/lock/subsys/emissary
|
26
|
+
echo
|
27
|
+
;;
|
28
|
+
stop)
|
29
|
+
# Stop daemon.
|
30
|
+
echo -n "Shutting down emissary: "
|
31
|
+
daemon ${EMISSARY_EXECUTABLE} --config-file ${CONFIG_FILE} stop
|
32
|
+
echo
|
33
|
+
rm -f /var/lock/subsys/emissary
|
34
|
+
;;
|
35
|
+
restart)
|
36
|
+
daemon ${EMISSARY_EXECUTABLE} --config-file ${CONFIG_FILE} restart
|
37
|
+
;;
|
38
|
+
status)
|
39
|
+
daemon ${EMISSARY_EXECUTABLE} --config-file ${CONFIG_FILE} status
|
40
|
+
;;
|
41
|
+
reconfig)
|
42
|
+
daemon ${EMISSARY_EXECUTABLE} --config-file ${CONFIG_FILE} reconfig
|
43
|
+
;;
|
44
|
+
*)
|
45
|
+
echo "Usage: emissary {start|stop|restart|status|reconfig}"
|
46
|
+
exit 1
|
47
|
+
;;
|
48
|
+
esac
|
49
|
+
|
50
|
+
exit 0
|
data/lib/emissary.rb
ADDED
@@ -0,0 +1,223 @@
|
|
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'
|
17
|
+
require 'uuid'
|
18
|
+
require 'digest/md5'
|
19
|
+
require 'yaml'
|
20
|
+
|
21
|
+
begin
|
22
|
+
require 'thread'
|
23
|
+
require 'fastthread'
|
24
|
+
rescue LoadError
|
25
|
+
end
|
26
|
+
|
27
|
+
module Emissary
|
28
|
+
# :stopdoc:
|
29
|
+
LIBPATH = File.expand_path(File.dirname(File.expand_path(__FILE__))) + File::SEPARATOR
|
30
|
+
PATH = File.dirname(LIBPATH) + File::SEPARATOR
|
31
|
+
VERSION = ::YAML.load(File.read(File.join(PATH, 'VERSION.yml'))).values.join '.'
|
32
|
+
|
33
|
+
EXTERNALS_BASE = File.join(File::SEPARATOR, 'opt')
|
34
|
+
EXTERNAL_IDENTITIES = File.join(EXTERNALS_BASE, 'emissary', 'identities')
|
35
|
+
EXTERNAL_AGENTS = File.join(EXTERNALS_BASE, 'emissary', 'agents')
|
36
|
+
EXTERNAL_OPERATORS = File.join(EXTERNALS_BASE, 'emissary', 'operators')
|
37
|
+
|
38
|
+
DEFAULT_EXCHANGE = :direct
|
39
|
+
DAEMON_RECHECK_INTERVAL = 10
|
40
|
+
IP_CHECK_DOMAIN = 'checkip.dyndns.org'
|
41
|
+
IP_CHECK_URL = "http://#{IP_CHECK_DOMAIN}"
|
42
|
+
# :startdoc:
|
43
|
+
|
44
|
+
class << self
|
45
|
+
@@pid = nil
|
46
|
+
def PID
|
47
|
+
@@pid
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the version string for the library.
|
51
|
+
#
|
52
|
+
def version
|
53
|
+
VERSION
|
54
|
+
end
|
55
|
+
|
56
|
+
# Returns the library path for the module. If any arguments are given,
|
57
|
+
# they will be joined to the end of the libray path using
|
58
|
+
# <tt>File.join</tt>.
|
59
|
+
#
|
60
|
+
def libpath( *args )
|
61
|
+
args.empty? ? LIBPATH : File.join(LIBPATH, args.flatten)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the path for the module. If any arguments are given,
|
65
|
+
# they will be joined to the end of the path using
|
66
|
+
# <tt>File.join</tt>.
|
67
|
+
#
|
68
|
+
def path( *args )
|
69
|
+
args.empty? ? PATH : File.join(PATH, args.flatten)
|
70
|
+
end
|
71
|
+
|
72
|
+
def sublib_path( *args )
|
73
|
+
args.empty? ? PATH : File.join(LIBPATH, 'emissary', args.flatten)
|
74
|
+
end
|
75
|
+
|
76
|
+
def klass_from_handler(klass = nil, handler = nil, *args)
|
77
|
+
klass = if handler and handler.is_a? Class and not handler.nil?
|
78
|
+
raise ArgumentError, "must provide module or subclass of #{klass.name}" unless klass >= handler
|
79
|
+
handler
|
80
|
+
elsif handler.is_a? Module
|
81
|
+
resource_name = "RESOURCE_#{handler.to_s.upcase.split('::').pop}"
|
82
|
+
begin
|
83
|
+
klass.const_get(resource_name)
|
84
|
+
rescue NameError
|
85
|
+
klass.const_set(resource_name, Class.new(klass) { include handler } )
|
86
|
+
end
|
87
|
+
elsif klass.nil?
|
88
|
+
raise ArgumentError, "klass must be a valid class constant for #{name}#klass_from_handler"
|
89
|
+
elsif handler.nil?
|
90
|
+
raise ArgumentError, "handler must be a valid class or module"
|
91
|
+
else
|
92
|
+
klass
|
93
|
+
end
|
94
|
+
|
95
|
+
arity = klass.instance_method(:initialize).arity
|
96
|
+
expected = arity >= 0 ? arity : -(arity + 1)
|
97
|
+
if (arity >= 0 and args.size != expected) or (arity < 0 and args.size < expected)
|
98
|
+
raise ArgumentError, "wrong number of arguments for #{klass}#initialize (#{args.size} for #{expected})"
|
99
|
+
end
|
100
|
+
|
101
|
+
klass
|
102
|
+
end
|
103
|
+
|
104
|
+
def klass_loaded?(class_name)
|
105
|
+
begin
|
106
|
+
!!klass_const(class_name)
|
107
|
+
rescue NameError
|
108
|
+
false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def klass_const(class_name, autoload = false)
|
113
|
+
return class_name if class_name.is_a? Class or class_name.is_a? Module
|
114
|
+
|
115
|
+
klass = Object
|
116
|
+
|
117
|
+
class_name.split('::').each do |c|
|
118
|
+
begin
|
119
|
+
klass = klass.const_get(c.to_sym)
|
120
|
+
rescue NameError => e
|
121
|
+
if autoload
|
122
|
+
require_klass( (klass == Object ? c : "#{klass.to_s}::#{c}") )
|
123
|
+
redo
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
klass.to_s == class_name ? klass : nil
|
129
|
+
end
|
130
|
+
|
131
|
+
def load_klass(class_name)
|
132
|
+
load libpath(*class_name.downcase.split(/::/)) + '.rb'
|
133
|
+
end
|
134
|
+
|
135
|
+
def require_klass(class_name)
|
136
|
+
require libpath(*class_name.downcase.split(/::/))
|
137
|
+
end
|
138
|
+
|
139
|
+
def logger
|
140
|
+
Emissary::Logger.instance
|
141
|
+
end
|
142
|
+
|
143
|
+
def generate_uuid
|
144
|
+
UUID.generate
|
145
|
+
end
|
146
|
+
|
147
|
+
def identity
|
148
|
+
@@identity ||= Emissary::Identity.instance
|
149
|
+
end
|
150
|
+
|
151
|
+
# Sets up a line of communication
|
152
|
+
#
|
153
|
+
def call handler, config, *args
|
154
|
+
unless handler.is_a? Class or handler.is_a? Module
|
155
|
+
klass_name = 'Emissary::Operator::' + handler.to_s.upcase
|
156
|
+
begin
|
157
|
+
require_klass klass_name
|
158
|
+
rescue LoadError => e
|
159
|
+
begin
|
160
|
+
require_klass Emissary::EXTERNAL_OPERATORS + '::' + handler.to_s.upcase
|
161
|
+
#rescue LoadError
|
162
|
+
# raise the original exception if we still haven't found the file
|
163
|
+
# raise e
|
164
|
+
end
|
165
|
+
end
|
166
|
+
handler = klass_const klass_name
|
167
|
+
end
|
168
|
+
|
169
|
+
klass = klass_from_handler(Operator, handler, config)
|
170
|
+
|
171
|
+
operator = klass.new(config, *args)
|
172
|
+
operator.validate_config!
|
173
|
+
block_given? and yield operator
|
174
|
+
operator
|
175
|
+
end
|
176
|
+
|
177
|
+
# Dispatches a message to the agent specified by the message
|
178
|
+
#
|
179
|
+
def dispatch message, config, *args
|
180
|
+
unless message.is_a? Emissary::Message
|
181
|
+
raise ArgumentError, "message is not an Emissary::Message"
|
182
|
+
end
|
183
|
+
|
184
|
+
begin
|
185
|
+
agent_type = 'Emissary::Agent::' + message.agent.to_s.capitalize
|
186
|
+
begin
|
187
|
+
require_klass agent_type
|
188
|
+
rescue LoadError => e
|
189
|
+
begin
|
190
|
+
require_klass Emissary::EXTERNAL_AGENTS + '::' + message.agent.to_s.capitalize
|
191
|
+
rescue LoadError
|
192
|
+
# raise the original exception if we still haven't found the file
|
193
|
+
raise e
|
194
|
+
end
|
195
|
+
end
|
196
|
+
rescue Exception => e
|
197
|
+
Emissary.logger.error "Dispatcher Error: #{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
198
|
+
message = message.error(e)
|
199
|
+
agent_type = 'Emissary::Agent::Error'
|
200
|
+
end
|
201
|
+
|
202
|
+
handler = klass_const agent_type, true
|
203
|
+
|
204
|
+
klass = klass_from_handler(Agent, handler, message, config, *args)
|
205
|
+
|
206
|
+
Emissary.logger.debug " [--] Dispatching message to: #{agent_type}"
|
207
|
+
agent = klass.new(message, config, *args)
|
208
|
+
block_given? and yield agent
|
209
|
+
agent
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
# autoload core object extensions
|
214
|
+
Dir[sublib_path('core_ext', '*.rb')].each do |core_extension|
|
215
|
+
require core_extension
|
216
|
+
end
|
217
|
+
end # module Emissary
|
218
|
+
|
219
|
+
$:.unshift Emissary::LIBPATH
|
220
|
+
|
221
|
+
[ :errors, :logger, :operator, :agent, :identity, :message, :config, :gem_helper ].each do |sublib|
|
222
|
+
require Emissary.sublib_path sublib.to_s
|
223
|
+
end
|
@@ -0,0 +1,61 @@
|
|
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 Agent
|
18
|
+
attr_reader :name, :message, :method, :config, :operator
|
19
|
+
attr_accessor :args
|
20
|
+
|
21
|
+
def initialize message, config, operator
|
22
|
+
@message = message
|
23
|
+
@operator = operator
|
24
|
+
@config = config
|
25
|
+
|
26
|
+
@method = message.method.to_sym rescue :__bad_method__
|
27
|
+
@args = message.args.clone
|
28
|
+
|
29
|
+
unless valid_methods.first == :any or valid_methods.include? @method
|
30
|
+
raise ArgumentError, "Invalid method '#{@method.to_s}' for agent '#{message.agent}'"
|
31
|
+
end
|
32
|
+
|
33
|
+
post_init
|
34
|
+
end
|
35
|
+
|
36
|
+
def post_init(); end
|
37
|
+
|
38
|
+
def valid_methods
|
39
|
+
raise StandardError, 'Not implemented'
|
40
|
+
end
|
41
|
+
|
42
|
+
def activate
|
43
|
+
catch(:skip_implicit_response) do
|
44
|
+
result = self.__send__(method, *args)
|
45
|
+
response = if not result.kind_of? ::Emissary::Message
|
46
|
+
response = message.response
|
47
|
+
response.status = [ :ok, (result == true || result.nil? ? 'Succeeded.' : result ) ]
|
48
|
+
response
|
49
|
+
else
|
50
|
+
result
|
51
|
+
end
|
52
|
+
|
53
|
+
send response
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def send message
|
58
|
+
operator.send message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,163 @@
|
|
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 'tempfile'
|
17
|
+
require 'fileutils'
|
18
|
+
|
19
|
+
module Emissary
|
20
|
+
class Agent::Emissary < Agent
|
21
|
+
INIT_DATA = [
|
22
|
+
::Emissary.identity.name,
|
23
|
+
::Emissary.identity.public_ip,
|
24
|
+
::Emissary.identity.local_ip,
|
25
|
+
::Emissary.identity.instance_id,
|
26
|
+
::Emissary.identity.server_id,
|
27
|
+
::Emissary.identity.cluster_id,
|
28
|
+
::Emissary.identity.account_id,
|
29
|
+
::Emissary.identity.queue_name,
|
30
|
+
::Emissary.version
|
31
|
+
]
|
32
|
+
|
33
|
+
def valid_methods
|
34
|
+
[ :reconfig, :selfupdate, :startup, :shutdown, :initdata, :reinit ]
|
35
|
+
end
|
36
|
+
|
37
|
+
def reconfig new_config
|
38
|
+
throw :skip_implicit_response if new_config.strip.empty?
|
39
|
+
|
40
|
+
if (test(?w, config[:agents][:emissary][:config_path]))
|
41
|
+
begin
|
42
|
+
((tmp = Tempfile.new('new_config')) << new_config).flush
|
43
|
+
Emissary::Daemon.get_config(tmp.path)
|
44
|
+
rescue Exception => e
|
45
|
+
resonse = message.response
|
46
|
+
response.status_type = :error
|
47
|
+
response.status_note = e.message
|
48
|
+
return response
|
49
|
+
else
|
50
|
+
FileUtils.mv tmp.path, config[:agents][:emissary][:config_path]
|
51
|
+
# signal a USR1 to our parent, which will cause it to kill the
|
52
|
+
# children and restart them after rereading it's configuration
|
53
|
+
Process.kill('HUP', config[:parent_pid])
|
54
|
+
ensure
|
55
|
+
tmp.close
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def selfupdate version = :latest
|
62
|
+
begin
|
63
|
+
emissary_gem = ::Emissary::GemHelper.new('emissary')
|
64
|
+
request_version, source_uri = emissary_gem.versions(version).flatten
|
65
|
+
current_version, _ = emissary_gem.versions(:current).flatten
|
66
|
+
|
67
|
+
unless not emissary_gem.installable? request_version
|
68
|
+
::Emissary.logger.debug "Emissary SelfUpdate to version '#{request_version.to_s}' requested."
|
69
|
+
new_version = emissary_gem.update(request_version, source_uri)
|
70
|
+
::Emissary.logger.debug "Emissary gem updated from '#{::Emissary.version}' to '#{new_version}'"
|
71
|
+
else
|
72
|
+
::Emissary.logger.debug " -- SELFUPDATE -- [ VERSION: #{version}]"
|
73
|
+
::Emissary.logger.debug " -- SELFUPDATE -- [ CURRENT_VERSION: #{current_version}]"
|
74
|
+
::Emissary.logger.debug " -- SELFUPDATE -- [ REQUEST_VERSION: #{request_version}]"
|
75
|
+
|
76
|
+
notice = 'Emissary selfupdate skipped - ' + case true
|
77
|
+
when request_version.nil?
|
78
|
+
if version == :latest
|
79
|
+
"already at latest version."
|
80
|
+
else
|
81
|
+
"non-existent version '#{version}'"
|
82
|
+
end
|
83
|
+
when current_version == request_version
|
84
|
+
"already at specified version #{version}."
|
85
|
+
when current_version > request_version
|
86
|
+
"downgrade to version #{request_version} not allowed."
|
87
|
+
else
|
88
|
+
"unable to update from #{::Emissary.version} to requested version #{request_version}."
|
89
|
+
end
|
90
|
+
::Emissary.logger.warn notice
|
91
|
+
response = message.response
|
92
|
+
response.status_note = notice
|
93
|
+
return response
|
94
|
+
end
|
95
|
+
rescue ::Gem::InstallError, ::Gem::GemNotFoundException => e
|
96
|
+
::Emissary.logger.error "Emissary selfupdate failed with reason: #{e.message}"
|
97
|
+
return message.error(e)
|
98
|
+
else
|
99
|
+
::Emissary.logger.debug "SelfUpdate: About to detach and run commands"
|
100
|
+
with_detached_process('emissary-selfupdate') do
|
101
|
+
%x{
|
102
|
+
emissary stop;
|
103
|
+
sleep 2;
|
104
|
+
ps uxa | grep -v grep | grep '(emissary|emop_)' | awk '{ print $2 }' | xargs kill -9;
|
105
|
+
sleep 1;
|
106
|
+
source /etc/cloudrc;
|
107
|
+
emissary start -d;
|
108
|
+
}
|
109
|
+
end
|
110
|
+
::Emissary.logger.debug "SelfUpdate: Child detached"
|
111
|
+
throw :skip_implicit_response
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def startup
|
116
|
+
message = initdata
|
117
|
+
message.recipient = config[:startup]
|
118
|
+
::Emissary.logger.notice "Sending Startup message with args: #{message.args.inspect}"
|
119
|
+
message
|
120
|
+
end
|
121
|
+
alias :reinit :startup
|
122
|
+
|
123
|
+
def initdata
|
124
|
+
response = message.response
|
125
|
+
response.args = INIT_DATA
|
126
|
+
response
|
127
|
+
end
|
128
|
+
|
129
|
+
def shutdown
|
130
|
+
message.recipient = config[:shutdown]
|
131
|
+
message.args = [
|
132
|
+
::Emissary.identity.server_id,
|
133
|
+
::Emissary.identity.cluster_id,
|
134
|
+
::Emissary.identity.account_id,
|
135
|
+
::Emissary.identity.instance_id
|
136
|
+
]
|
137
|
+
::Emissary.logger.notice "Sending Shutdown message with args: #{message.args.inspect}"
|
138
|
+
message
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def with_detached_process(name = nil)
|
144
|
+
raise Exception, 'Block missing for with_detached_process call' unless block_given?
|
145
|
+
|
146
|
+
# completely seperate from our parent process
|
147
|
+
pid = Kernel.fork do
|
148
|
+
Process.setsid
|
149
|
+
exit!(0) if fork
|
150
|
+
$0 = name unless name.nil?
|
151
|
+
Dir.chdir '/'
|
152
|
+
::Emissary.logger.debug "SelfUpdate: Detached and running update command block now..."
|
153
|
+
yield
|
154
|
+
::Emissary.logger.debug "SelfUpdate: Finished running update command block - exiting..."
|
155
|
+
exit!(0)
|
156
|
+
end
|
157
|
+
|
158
|
+
::Emissary.logger.debug "SelfUpdate: Detaching child process now."
|
159
|
+
#don't worry about the child anymore - it's on it's own
|
160
|
+
Process.detach(pid)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|