emissary 1.3.18 → 1.3.19

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.
@@ -1,61 +0,0 @@
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
@@ -1,163 +0,0 @@
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
@@ -1,26 +0,0 @@
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::Error < Agent
18
- def valid_methods
19
- [ :any ]
20
- end
21
-
22
- def activate
23
- message
24
- end
25
- end
26
- end
@@ -1,26 +0,0 @@
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::File < Agent
18
- def valid_methods
19
- [ :any ]
20
- end
21
-
22
- def activate
23
- throw :skip_implicit_response
24
- end
25
- end
26
- end
@@ -1,42 +0,0 @@
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
-
17
- module Emissary
18
- class Agent::Gem < Agent
19
- def valid_methods
20
- [ :update, :install, :remove, :uninstall, :version ]
21
- end
22
-
23
- def version gem_name
24
- ::Emissary.GemHelper.new(gem_name).version
25
- end
26
-
27
- # Updates Emissary from the given source to the given version
28
- def install gem_name, version = :latest, source_url = :default
29
- ::Emissary::GemHelper.new(gem_name).install(version, source_url)
30
- end
31
-
32
- def update gem_name, version = :latest, source_url = :default
33
- ::Emissary::GemHelper.new(gem_name).update(version, source_url)
34
- end
35
-
36
- def uninstall gem_name, version = :latest, ignore_dependencies = true, remove_executables = false
37
- ::Emissary::GemHelper.new(gem_name).uninstall(version, ignore_dependencies, remove_executables)
38
- end
39
- alias :remove :uninstall
40
- end
41
-
42
- end
@@ -1,230 +0,0 @@
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 "mysql"
17
- require "monitor"
18
- require 'timeout'
19
-
20
- module Emissary
21
- class Agent::Mysql < Agent
22
- DEFAULT_COORDINATES_FILE = '/var/nyt/mysql/master.coordinates'.freeze
23
-
24
- def valid_methods
25
- [ :lock, :unlock, :status ]
26
- end
27
-
28
- attr_accessor :coordinates_file
29
-
30
- def lock(host, user, password, timeout = Agent::Mysql::Helper::DEFAULT_TIMEOUT, coordinates_file = nil)
31
- @coordinates_file ||= coordinates_file
32
- @coordinates_file ||= config[:agents][:mysql][:coordinates_file] rescue nil
33
- @coordinates_file ||= DEFAULT_COORDINATES_FILE
34
-
35
- locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password, timeout)
36
- locker.lock!
37
-
38
- filename, position = locker.get_binlog_info
39
-
40
- unless filename.nil?
41
- write_lock_info(filename, position)
42
- response = message.response
43
- response.args = [ filename, position ]
44
- response.status_note = 'Locked'
45
- else
46
- response = message.response
47
- response.status_note = "No binlog information - can't lock."
48
- end
49
-
50
- response
51
- end
52
-
53
- def unlock(host, user, password)
54
- locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password)
55
- raise "The database was not locked! (Possibly timed out.)" unless locker.locked?
56
-
57
- locker.unlock!
58
-
59
- response = message.response
60
- response.status_note = 'Unlocked'
61
- response
62
- end
63
-
64
- def status(host, user, password)
65
- locker = ::Emissary::Agent::Mysql::Helper.new(host, user, password)
66
-
67
- response = message.response
68
- response.status_note = locker.locked? ? 'Locked' : 'Unlocked'
69
- response
70
- end
71
-
72
- private
73
-
74
- def write_lock_info(filename, position)
75
- File.open(coordinates_file, "w") do |file|
76
- file << "#{filename},#{position}"
77
- end
78
- end
79
-
80
- end
81
-
82
- class Agent::Mysql::Helper
83
- DEFAULT_TIMEOUT = 30
84
-
85
- @@class_monitor = Monitor.new
86
-
87
- # only return one locker per host+user combination
88
- def self.new(host, user, password, timeout = nil)
89
- @@class_monitor.synchronize do
90
- (@@lockers||={})["#{host}:#{user}"] ||= begin
91
- allocate.instance_eval(<<-EOS, __FILE__, __LINE__)
92
- initialize(host, user, password, timeout || DEFAULT_TIMEOUT)
93
- self
94
- EOS
95
- end
96
- @@lockers["#{host}:#{user}"].timeout = timeout unless timeout.nil?
97
- @@lockers["#{host}:#{user}"]
98
- end
99
- end
100
-
101
- @@locked_M = Mutex.new
102
- def locked_M() @@locked_M; end
103
-
104
- private
105
-
106
- def initialize(host, user, password, timeout = DEFAULT_TIMEOUT)
107
- @host = host
108
- @user = user
109
- @password = password
110
- @timeout = timeout
111
-
112
- @watcher = nil
113
- @connection = nil
114
- @locked = false
115
- end
116
-
117
-
118
- def connection
119
- begin
120
- @connection.ping() unless @connection.nil?
121
- rescue ::Mysql::Error => e
122
- if e.message =~ /server has gone away/
123
- ::Emissary.logger.notice "Agent::MySQL: MySQL server went away - reconnecting..."
124
- @connection = nil
125
- else
126
- raise e
127
- end
128
- end
129
-
130
- @connection ||= ::Mysql.real_connect(@host, @user, @password)
131
- end
132
-
133
- def disconnect
134
- unless not connected?
135
- ::Emissary.logger.notice "Agent::MySQL: disconnecting from MySQL Server .."
136
- @connection.close
137
- @connection = nil
138
- end
139
- end
140
-
141
- public
142
- attr_accessor :timeout
143
-
144
- def connected?
145
- !!@connection
146
- end
147
-
148
- # Acquire a lock and, with that lock, run a block/closure.
149
- def with_lock
150
- begin
151
- lock! && yield
152
- ensure
153
- unlock!
154
- end
155
- end
156
-
157
- def locked?
158
- !!@locked
159
- end
160
-
161
- def lock!
162
- unless locked?
163
- kill_watcher_thread! # make sure we have a new thread for watching
164
- locked_M.synchronize { @locked = true }
165
- connection.query("FLUSH TABLES WITH READ LOCK")
166
- spawn_lockwatch_thread!
167
- end
168
- end
169
-
170
- def unlock!
171
- begin
172
- unless not locked?
173
- locked_M.synchronize {
174
- connection.query("UNLOCK TABLES")
175
- @locked = false
176
- }
177
- end
178
- ensure
179
- disconnect
180
- kill_watcher_thread!
181
- end
182
- end
183
-
184
- # Test whether our login info is valid by attempting a database
185
- # connection.
186
- def valid?
187
- begin
188
- !!connection
189
- rescue => e
190
- false # Don't throw an exception, just return false.
191
- ensure
192
- disconnect if connected?
193
- end
194
- end
195
-
196
- # Returns [file, position]
197
- def get_binlog_info
198
- raise "get_binlog_info must be called from within a lock." unless locked?
199
- (result = connection.query("SHOW MASTER STATUS")).fetch_row[0,2]
200
- ensure
201
- result.free unless result.nil?
202
- end
203
-
204
- def spawn_lockwatch_thread!
205
- if @watcher.is_a?(Thread) and not @watcher.alive?
206
- ::Emissary.logger.notice "Agent::MySQL: Watcher is dead - restarting..."
207
- @watcher = nil
208
- end
209
-
210
- @watcher ||= Thread.new {
211
- begin
212
- ::Emissary.logger.debug "Agent::MySQL: Entering Watcher Loop"
213
- Timeout.timeout(@timeout) do
214
- loop { break unless locked? }
215
- end
216
- rescue Timeout::Error
217
- ensure
218
- unlock!
219
- Thread.exit
220
- end
221
- }
222
- end
223
-
224
- def kill_watcher_thread!
225
- @watcher.kill unless not @watcher.is_a?(Thread) or not @watcher.alive?
226
- @watcher = nil
227
- end
228
- end
229
- end
230
-