elastic_beans 0.10.0.alpha4 → 0.10.0.alpha5
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.
- checksums.yaml +4 -4
- data/lib/elastic_beans/application.rb +15 -5
- data/lib/elastic_beans/command/kill.rb +1 -1
- data/lib/elastic_beans/exec/sqs_consumer.rb +53 -48
- data/lib/elastic_beans/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d754b33da7a3f07521bc482202e613379f857d34
|
4
|
+
data.tar.gz: 18565aa2ae2b834401eb8c6e129dc7db2fa26bf5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d701c345a20ac52fa4be4ac428ebc85f7051a176dac0132d70961c7f280e8b4c1d0c37f300b7b8ed0fe21aea8d42b6610f030e44fba1dcf4733472eed4b5060
|
7
|
+
data.tar.gz: 2f83cbf28762e060e3b88f158794d496decb39d2c91af4d587f7ea62b2fc3add9b6ff8dfdd0bc9fe8572c77f006114c8b1d34bcd9345e3188210d3d1f4af76d1
|
@@ -146,8 +146,16 @@ module ElasticBeans
|
|
146
146
|
prefix: command_key_prefix,
|
147
147
|
max_keys: 100,
|
148
148
|
).contents
|
149
|
-
objects.each_with_object([])
|
149
|
+
objects.each_with_object([]) do |object, commands|
|
150
150
|
begin
|
151
|
+
next unless object.key.end_with?('.json')
|
152
|
+
begin
|
153
|
+
s3.head_object(bucket: bucket_name, key: "#{object.key}.killed")
|
154
|
+
next
|
155
|
+
rescue ::Aws::S3::Errors::NotFound
|
156
|
+
# not killed, move on
|
157
|
+
end
|
158
|
+
|
151
159
|
response = s3.get_object(bucket: bucket_name, key: object.key)
|
152
160
|
command = ElasticBeans::Exec::Command.from_json(response.body.read)
|
153
161
|
commands << command unless command.freeze_instance?
|
@@ -155,7 +163,9 @@ module ElasticBeans
|
|
155
163
|
rescue ::Aws::S3::Errors::NoSuchKey
|
156
164
|
rescue JSON::ParserError
|
157
165
|
end
|
158
|
-
|
166
|
+
end
|
167
|
+
rescue ::Aws::S3::Errors::AccessDenied
|
168
|
+
raise AccessDeniedS3Error.new(bucket: bucket_name, key: "#{command_key_prefix}*")
|
159
169
|
end
|
160
170
|
|
161
171
|
# Fetches the +ExecQueueUrl+ Output from the application CloudFormation stack.
|
@@ -165,7 +175,7 @@ module ElasticBeans
|
|
165
175
|
end
|
166
176
|
|
167
177
|
# Schedules the given ElasticBeans::Exec::Command or +id+ for termination.
|
168
|
-
#
|
178
|
+
# Adds a special piece of metadata to kill the command and relies on the SQSConsumer to terminate it.
|
169
179
|
#
|
170
180
|
# Raises an error if the application does not exist.
|
171
181
|
def kill_command(command_or_id)
|
@@ -179,8 +189,8 @@ module ElasticBeans
|
|
179
189
|
command_id = command_or_id
|
180
190
|
end
|
181
191
|
|
182
|
-
key = "#{command_key_prefix}#{command_id}.json"
|
183
|
-
s3.
|
192
|
+
key = "#{command_key_prefix}#{command_id}.json.killed"
|
193
|
+
s3.put_object(
|
184
194
|
bucket: bucket_name,
|
185
195
|
key: key,
|
186
196
|
)
|
@@ -13,7 +13,7 @@ You can find an enqueued command's ID by running `ps`.
|
|
13
13
|
|
14
14
|
Commands are run in an "exec" environment, separate from your webserver or worker environments.
|
15
15
|
When they are enqueued, metadata is created in S3 and removed when the command is complete.
|
16
|
-
|
16
|
+
Adding a special piece of metadata cancels the command.
|
17
17
|
The command is sent a SIGTERM, and then a SIGKILL if it has not yet died.
|
18
18
|
|
19
19
|
Requires AWS credentials to be set in the environment, i.e. AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.
|
@@ -31,9 +31,10 @@ class SQSConsumer
|
|
31
31
|
sleep(LOOP_PERIOD)
|
32
32
|
if (message = receive_message)
|
33
33
|
command = command_from_message(message)
|
34
|
-
|
34
|
+
if killing_command?(command)
|
35
35
|
delete_message(message)
|
36
|
-
|
36
|
+
log("Skipping command ID=#{command['id']} `#{command['command']}' because it was killed")
|
37
|
+
remove_metadata(command: command)
|
37
38
|
next
|
38
39
|
end
|
39
40
|
|
@@ -137,6 +138,53 @@ class SQSConsumer
|
|
137
138
|
@killed
|
138
139
|
end
|
139
140
|
|
141
|
+
def killing_command?(command)
|
142
|
+
metadata = command['metadata']
|
143
|
+
if metadata && metadata['bucket'] && metadata['key']
|
144
|
+
s3.head_object(
|
145
|
+
bucket: metadata['bucket'],
|
146
|
+
key: "#{metadata['key']}.killed",
|
147
|
+
)
|
148
|
+
true
|
149
|
+
end
|
150
|
+
rescue ::Aws::S3::Errors::NotFound
|
151
|
+
false
|
152
|
+
rescue StandardError => e
|
153
|
+
log("[metadata_exists] #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
|
154
|
+
end
|
155
|
+
|
156
|
+
def kill_command(command, force: false)
|
157
|
+
if force
|
158
|
+
signal = "KILL"
|
159
|
+
else
|
160
|
+
signal = "TERM"
|
161
|
+
end
|
162
|
+
|
163
|
+
log_command(
|
164
|
+
"Sending signal '#{signal}' to" \
|
165
|
+
" command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}" \
|
166
|
+
" because it was killed"
|
167
|
+
)
|
168
|
+
Process.kill(signal, command_pid)
|
169
|
+
|
170
|
+
unless force
|
171
|
+
# give the command a little bit to die
|
172
|
+
sleep(TERM_TIMEOUT)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def while_killed(command)
|
177
|
+
catch(:killed) do
|
178
|
+
loop do
|
179
|
+
sleep(KILL_PERIOD)
|
180
|
+
|
181
|
+
if killing_command?(command)
|
182
|
+
yield if block_given?
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
140
188
|
def receive_message
|
141
189
|
sqs.receive_message(
|
142
190
|
queue_url: queue_url,
|
@@ -171,62 +219,19 @@ class SQSConsumer
|
|
171
219
|
log("[update_metadata] #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
|
172
220
|
end
|
173
221
|
|
174
|
-
def
|
222
|
+
def remove_metadata(command:)
|
175
223
|
metadata = command['metadata']
|
176
224
|
if metadata && metadata['bucket'] && metadata['key']
|
177
|
-
s3.
|
225
|
+
s3.delete_object(
|
178
226
|
bucket: metadata['bucket'],
|
179
227
|
key: metadata['key'],
|
180
228
|
)
|
181
|
-
true
|
182
|
-
end
|
183
|
-
rescue ::Aws::S3::Errors::NotFound
|
184
|
-
false
|
185
|
-
rescue StandardError => e
|
186
|
-
log("[metadata_exists] #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
|
187
|
-
end
|
188
|
-
|
189
|
-
def remove_metadata(command:)
|
190
|
-
metadata = command['metadata']
|
191
|
-
if metadata && metadata['bucket'] && metadata['key']
|
192
229
|
s3.delete_object(
|
193
230
|
bucket: metadata['bucket'],
|
194
|
-
key: metadata['key'],
|
231
|
+
key: "#{metadata['key']}.killed",
|
195
232
|
)
|
196
233
|
end
|
197
234
|
rescue StandardError => e
|
198
235
|
log("[remove_metadata] #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
|
199
236
|
end
|
200
|
-
|
201
|
-
def kill_command(command, force: false)
|
202
|
-
if force
|
203
|
-
signal = "KILL"
|
204
|
-
else
|
205
|
-
signal = "TERM"
|
206
|
-
end
|
207
|
-
|
208
|
-
log_command(
|
209
|
-
"Sending signal '#{signal}' to" \
|
210
|
-
" command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}" \
|
211
|
-
" because it was killed"
|
212
|
-
)
|
213
|
-
Process.kill(signal, command_pid)
|
214
|
-
|
215
|
-
unless force
|
216
|
-
# give the command a little bit to die
|
217
|
-
sleep(TERM_TIMEOUT)
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def while_killed(command)
|
222
|
-
catch(:killed) do
|
223
|
-
loop do
|
224
|
-
sleep(KILL_PERIOD)
|
225
|
-
|
226
|
-
if !metadata_exists?(command: command)
|
227
|
-
yield if block_given?
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
237
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elastic_beans
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.10.0.
|
4
|
+
version: 0.10.0.alpha5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Stegman
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-04-
|
11
|
+
date: 2017-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: aws-sdk
|