elastic_beans 0.10.0.alpha3 → 0.10.0.alpha4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 583357835167edf4cae60e1c2151130c44ea75bd
4
- data.tar.gz: 9ca2c8eb722305dee6e9c2b6e222b1204ba410b0
3
+ metadata.gz: ab319bee66fb6845dab2666354b537f014d57922
4
+ data.tar.gz: 1a6f05149363a12674100472bba10579d3e18d00
5
5
  SHA512:
6
- metadata.gz: 2f53b9bed978bb88683abe9ea0c3e1024afa6a0aeab7c5481a676729720f6e443cc4351f0221acfa7c16d55d040358130c1bc9895cdb657850827adf1de2cae5
7
- data.tar.gz: f2a75273d6299587372ea58eee84439f878c2028c605fdd5a187edce554064ba2cb19789ba41cf0acb5ec0ffbfc024ad70f52ca6c1188b00b325afdd312b6997
6
+ metadata.gz: 8ef907a6ddabf1243e976d882b22d1524510816ff58690c8141f55866781b77295294aa3b9117d8800828072650e9733786ae0da9c3cd9830f1d1c16dbdb781e
7
+ data.tar.gz: c9c517314e4403cfe788ab28667f35a029d388feefaa6f8c3b8100258fb18e6082e4306ad7321a69def8ea3313e6b44577acda3dec2a8d873612b221b4a6ad40
@@ -149,7 +149,8 @@ module ElasticBeans
149
149
  objects.each_with_object([]) { |object, commands|
150
150
  begin
151
151
  response = s3.get_object(bucket: bucket_name, key: object.key)
152
- commands << ElasticBeans::Exec::Command.from_json(response.body.read)
152
+ command = ElasticBeans::Exec::Command.from_json(response.body.read)
153
+ commands << command unless command.freeze_instance?
153
154
  # skip finished or invalid commands
154
155
  rescue ::Aws::S3::Errors::NoSuchKey
155
156
  rescue JSON::ParserError
@@ -21,9 +21,10 @@ module ElasticBeans
21
21
  # Command metadata, such as where to store execution data in S3.
22
22
  attr_reader :metadata
23
23
 
24
- def initialize(command_string:, id: nil, instance_id: nil, start_time: nil, metadata: nil)
24
+ def initialize(command_string:, freeze_instance: false, id: nil, instance_id: nil, start_time: nil, metadata: nil)
25
25
  @id = id || SecureRandom.uuid
26
26
  @command_string = command_string
27
+ @freeze = freeze_instance
27
28
  @instance_id = instance_id
28
29
  @start_time = start_time
29
30
  @metadata = metadata || {}
@@ -33,10 +34,16 @@ module ElasticBeans
33
34
  id == other.id
34
35
  end
35
36
 
37
+ # Instead of a +command_string+ to execute, instruct the instance to not do anything.
38
+ def freeze_instance?
39
+ @freeze
40
+ end
41
+
36
42
  def to_json
37
43
  attributes = {}
38
44
  attributes[:id] = id
39
- attributes[:command] = command_string
45
+ attributes[:command] = command_string if command_string
46
+ attributes[:freeze] = true if freeze_instance?
40
47
  attributes[:instance_id] = instance_id if instance_id
41
48
  attributes[:start_time] = start_time.iso8601 if start_time
42
49
  attributes[:metadata] = metadata
@@ -48,11 +55,18 @@ module ElasticBeans
48
55
  new(
49
56
  id: attributes["id"],
50
57
  command_string: attributes["command"],
58
+ freeze_instance: attributes["freeze"],
51
59
  instance_id: attributes["instance_id"],
52
60
  start_time: attributes["start_time"] ? Time.iso8601(attributes["start_time"]) : nil,
53
61
  metadata: attributes["metadata"],
54
62
  )
55
63
  end
64
+
65
+ # A special ElasticBeans::Exec::Command that instructs the instance to not do anything.
66
+ # Used for interactive exec commands, like +rails console+.
67
+ def self.freeze_instance
68
+ new(command_string: nil, freeze_instance: true)
69
+ end
56
70
  end
57
71
  end
58
72
  end
@@ -7,6 +7,9 @@ require "time"
7
7
  # @!visibility private
8
8
  class SQSConsumer
9
9
  LOOP_PERIOD = 1
10
+ KILL_PERIOD = 5
11
+ TERM_TIMEOUT = 25
12
+ FREEZE_TIMEOUT = 86400
10
13
 
11
14
  def initialize(instance_id:, logdir:, queue_url:, s3:, sqs:)
12
15
  @instance_id = instance_id
@@ -34,47 +37,50 @@ class SQSConsumer
34
37
  next
35
38
  end
36
39
 
37
- log_command("Executing command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}...")
38
40
  start_time = Time.now
39
- @command_pid = Process.spawn(
40
- "#{command_script} #{command['command']}",
41
- out: [command_logfile, "a"],
42
- err: [command_logfile, "a"],
43
- )
41
+ if command['freeze']
42
+ log("Freezing command ID=#{command['id']} on host #{`hostname`.chomp}...")
43
+ else
44
+ log_command("Executing command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}...")
45
+ @command_pid = Process.spawn(
46
+ "#{command_script} #{command['command']}",
47
+ out: [command_logfile, "a"],
48
+ err: [command_logfile, "a"],
49
+ )
50
+ end
51
+
44
52
  delete_message(message)
45
53
  update_metadata(command: command, start_time: start_time)
46
54
 
47
- # poll command metadata to see if it is still available, if it disappears then kill the process
48
- killed_thread = Thread.new do
49
- loop do
50
- sleep 5
51
-
52
- if !metadata_exists?(command: command)
55
+ if command['freeze']
56
+ begin
57
+ Timeout.timeout(FREEZE_TIMEOUT) do
58
+ while_killed(command) do
59
+ throw(:killed)
60
+ end
61
+ log("Defrosting from freeze command ID=#{command['id']}")
62
+ end
63
+ rescue Timeout::Error
64
+ log("Freeze command ID=#{command['id']} never terminated; timing out after '#{FREEZE_TIMEOUT}' seconds")
65
+ end
66
+ else
67
+ # poll command metadata to see if it is still available, if it disappears then kill the process
68
+ killed_thread = Thread.new do
69
+ while_killed(command) do
53
70
  if already_killed?
54
- log_command(
55
- "Sending KILL signal to command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}" \
56
- " because it was killed and has not terminated yet"
57
- )
58
- Process.kill("KILL", command_pid)
59
- break
71
+ kill_command(command, force: true)
72
+ throw(:killed)
60
73
  else
61
- log_command(
62
- "Sending TERM signal to command ID=#{command['id']} `#{command['command']}' on host #{`hostname`.chomp} pid #{command_pid}" \
63
- " because it was killed"
64
- )
65
- Process.kill("TERM", command_pid)
74
+ kill_command(command)
66
75
  @killed = true
67
- # give the command a little bit to die
68
- sleep 25
69
76
  end
70
77
  end
71
78
  end
79
+ _, status = Process.wait2(command_pid)
80
+ killed_thread.kill if killed_thread.alive?
81
+ log_command("Command ID=#{command['id']} `#{command['command']}' exited on host #{`hostname`.chomp}: #{status}")
72
82
  end
73
83
 
74
- _, status = Process.wait2(command_pid)
75
- killed_thread.kill if killed_thread.alive?
76
- log_command("Command ID=#{command['id']} `#{command['command']}' exited on host #{`hostname`.chomp}: #{status}")
77
-
78
84
  @command_pid = nil
79
85
  @killed = nil
80
86
  remove_metadata(command: command)
@@ -191,4 +197,36 @@ class SQSConsumer
191
197
  rescue StandardError => e
192
198
  log("[remove_metadata] #{e.class.name}: #{e.message}\n#{e.backtrace.join("\n")}")
193
199
  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
194
232
  end
@@ -1,3 +1,3 @@
1
1
  module ElasticBeans
2
- VERSION = "0.10.0.alpha3"
2
+ VERSION = "0.10.0.alpha4"
3
3
  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.alpha3
4
+ version: 0.10.0.alpha4
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-24 00:00:00.000000000 Z
11
+ date: 2017-04-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk