whacamole 0.2.0 → 1.0.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.
- checksums.yaml +15 -0
- data/Gemfile.lock +1 -1
- data/README.md +39 -5
- data/lib/whacamole.rb +1 -2
- data/lib/whacamole/config.rb +2 -2
- data/lib/whacamole/heroku_wrapper.rb +3 -3
- data/lib/whacamole/stream.rb +6 -7
- data/lib/whacamole/version.rb +1 -1
- data/spec/config_spec.rb +5 -1
- data/spec/heroku_wrapper_spec.rb +7 -1
- data/spec/stream_spec.rb +17 -0
- metadata +5 -11
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
Njk4YWU0NWQyMzZjMjg4OWJhZGRlNTczYzIzNTllZmIyMmZkNDVmNQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NWU5OTM2Zjg0ZTY3ZDJkNzczMDg4MWMxODYzZTk5YzRlN2FiMTg3OA==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZGY2MDllNzU4ZDg0Y2E5YTY4ODlhMTE3NTJmZDE5NjY0ZmJlZWY2OTVlZDgx
|
10
|
+
ZmUyZGU1ZTBhMWQ3MWVkZmMxZGU1OTEyZTIwZGVkMTdjYjM2M2IxZGRiNTgz
|
11
|
+
ZjBlOTM5MjVhNzQ0NzRlMTg2MGVhYTI5MzYxNzk1ZTg5MDA4YWQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
OTY4MTM5NTU5NWJmMTQzOWQ5NzY1N2VlMWY2MDcyNjk2YjlkMWJmY2QyMGU4
|
14
|
+
MDNjYjkxYzZmNzcwMGJjMTk1ZjcxNjUzMzdiMjliNGQ2OGIzZWVlYjA1NTJh
|
15
|
+
NmQwNDRjZTRmOGZmMTA5ODVjN2EwM2FmYzMxODA1MmE4MzE0YjI=
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,15 +8,15 @@ swapping to disk (aka get super slow).
|
|
8
8
|
|
9
9
|
Here’s what Heroku says about dyno memory usage:
|
10
10
|
|
11
|
-
> Dynos are available in
|
11
|
+
> Dynos are available in a few different sizes. The maximum amount of RAM available to your application depends on the dyno size you use.
|
12
12
|
>
|
13
13
|
> Dynos whose processes exceed their memory quota are identified by an R14 error in the logs. This doesn’t terminate the process, but it does warn of deteriorating application conditions: memory used above quota will swap out to disk, which substantially degrades dyno performance.
|
14
|
+
>
|
15
|
+
> If the memory size of your dyno keeps growing until it reaches five times its quota (for a 1X dyno, 512MB x 5 = 2.5GB), the dyno manager will restart your dyno with an R15 error.
|
14
16
|
>
|
15
|
-
>
|
16
|
-
>
|
17
|
-
> - From https://devcenter.heroku.com/articles/dynos on 8/8/13
|
17
|
+
> - From https://devcenter.heroku.com/articles/dynos on 3/12/14
|
18
18
|
|
19
|
-
Heroku dynos swap to disk for up to
|
19
|
+
Heroku dynos swap to disk for up to 5GB (2X dynos) or up to THIRTY GIGABYTES (PX dynos). That is not good and that is the problem whacamole addresses.
|
20
20
|
|
21
21
|
# Usage
|
22
22
|
|
@@ -45,6 +45,12 @@ end
|
|
45
45
|
Whacamole.configure("ANOTHER HEROKU APP") do |config|
|
46
46
|
config.api_token = ENV['HEROKU_API_TOKEN'] # you could also paste your token in here as a string
|
47
47
|
end
|
48
|
+
|
49
|
+
# you can specify which dynos to watch for each app (default: `web`):
|
50
|
+
Whacamole.configure("HEROKU APP WITH MULTIPLE DYNO TYPES") do |config|
|
51
|
+
config.api_token = ENV['HEROKU_API_TOKEN'] # you could also paste your token in here as a string
|
52
|
+
config.dynos = %w{web worker}
|
53
|
+
end
|
48
54
|
```
|
49
55
|
|
50
56
|
Add whacamole to your Procfile, specifying the config file you created:
|
@@ -62,3 +68,31 @@ $ foreman start whacamole
|
|
62
68
|
# on heroku
|
63
69
|
$ heroku ps:scale whacamole=1 --app YOUR_APP_NAME
|
64
70
|
```
|
71
|
+
|
72
|
+
# Events
|
73
|
+
|
74
|
+
Each ping and restart is available to you, for example in case you want to see with your own eyes that it's working.
|
75
|
+
|
76
|
+
Methods on DynoSize events
|
77
|
+
* event.process (the heroku process, eg "web.1")
|
78
|
+
* event.size (dyno size, eg 444.06)
|
79
|
+
* event.units (units for the size, eg "MB")
|
80
|
+
|
81
|
+
Methods on DynoRestart events
|
82
|
+
* event.process (the heroku process, eg "web.1")
|
83
|
+
|
84
|
+
To access the events, add a handler in your config. Event handlers take a single argument, the event. This handler will log the event in your Heroku logs:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
Whacamole.configure("HEROKU APP NAME") do |config|
|
88
|
+
config.event_handler = lambda do |e|
|
89
|
+
puts e.inspect.to_s
|
90
|
+
end
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
## Self Promotion
|
95
|
+
|
96
|
+
If you like Whacamole, help spread the word! Tell your friends, or at the very least star the repo on github.
|
97
|
+
|
98
|
+
For more heroku goodness, check out http://github.com/arches/table_print and http://github.com/arches/marco-polo
|
data/lib/whacamole.rb
CHANGED
@@ -16,7 +16,7 @@ module Whacamole
|
|
16
16
|
threads = []
|
17
17
|
@@config.each do |app_name, config|
|
18
18
|
threads << Thread.new do
|
19
|
-
heroku = HerokuWrapper.new(app_name, config.api_token)
|
19
|
+
heroku = HerokuWrapper.new(app_name, config.api_token, config.dynos)
|
20
20
|
|
21
21
|
while true
|
22
22
|
stream_url = heroku.create_log_session
|
@@ -27,4 +27,3 @@ module Whacamole
|
|
27
27
|
threads.collect(&:join)
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
data/lib/whacamole/config.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
module Whacamole
|
2
2
|
class Config
|
3
|
-
attr_accessor :app_name, :api_token, :event_handler
|
3
|
+
attr_accessor :app_name, :api_token, :event_handler, :dynos
|
4
4
|
|
5
5
|
def initialize(app_name)
|
6
6
|
self.app_name = app_name
|
7
7
|
self.event_handler ||= lambda { |e| puts e.inspect.to_s }
|
8
|
+
self.dynos ||= %w{web}
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
11
|
-
|
@@ -4,13 +4,14 @@ require 'json'
|
|
4
4
|
|
5
5
|
module Whacamole
|
6
6
|
class HerokuWrapper
|
7
|
-
attr_accessor :api_token, :app_name
|
7
|
+
attr_accessor :api_token, :app_name, :dynos
|
8
8
|
|
9
9
|
RESTART_RATE_LIMIT = 30*60
|
10
10
|
|
11
|
-
def initialize(app_name, api_token)
|
11
|
+
def initialize(app_name, api_token, dynos)
|
12
12
|
self.app_name = app_name
|
13
13
|
self.api_token = api_token
|
14
|
+
self.dynos = dynos
|
14
15
|
end
|
15
16
|
|
16
17
|
def create_log_session
|
@@ -69,4 +70,3 @@ module Whacamole
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
end
|
72
|
-
|
data/lib/whacamole/stream.rb
CHANGED
@@ -9,6 +9,7 @@ module Whacamole
|
|
9
9
|
def initialize(url, restart_handler, &blk)
|
10
10
|
@url = url
|
11
11
|
@restart_handler = restart_handler
|
12
|
+
@dynos = restart_handler.dynos
|
12
13
|
@event_handler = blk
|
13
14
|
end
|
14
15
|
|
@@ -50,21 +51,22 @@ module Whacamole
|
|
50
51
|
|
51
52
|
def memory_size_from_chunk(chunk)
|
52
53
|
sizes = []
|
54
|
+
dynos_regexp = Regexp.new('(' + @dynos.join('|') + ')\.\d+')
|
53
55
|
|
54
56
|
# new log format
|
55
57
|
chunk.split("\n").select{|line| line.include? "sample#memory_total"}.each do |line|
|
56
|
-
dyno = line.match(
|
58
|
+
dyno = line.match(dynos_regexp)
|
57
59
|
next unless dyno
|
58
60
|
size = line.match(/sample#memory_total=([\d\.]+)/)
|
59
|
-
sizes << [dyno[0], size[1]]
|
61
|
+
sizes << [dyno[0], size[1]] unless size.nil?
|
60
62
|
end
|
61
63
|
|
62
64
|
# old log format
|
63
65
|
chunk.split("\n").select{|line| line.include? "measure=memory_total"}.each do |line|
|
64
|
-
dyno = line.match(
|
66
|
+
dyno = line.match(dynos_regexp)
|
65
67
|
next unless dyno
|
66
68
|
size = line.match(/val=([\d\.]+)/)
|
67
|
-
sizes << [dyno[0], size[1]]
|
69
|
+
sizes << [dyno[0], size[1]] unless size.nil?
|
68
70
|
end
|
69
71
|
|
70
72
|
sizes
|
@@ -87,6 +89,3 @@ module Whacamole
|
|
87
89
|
end
|
88
90
|
end
|
89
91
|
end
|
90
|
-
|
91
|
-
|
92
|
-
|
data/lib/whacamole/version.rb
CHANGED
data/spec/config_spec.rb
CHANGED
data/spec/heroku_wrapper_spec.rb
CHANGED
@@ -2,7 +2,13 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
# TODO: use VCR instead of these method expectations
|
4
4
|
describe Whacamole::HerokuWrapper do
|
5
|
-
let(:h) { Whacamole::HerokuWrapper.new("staging", "foobar") }
|
5
|
+
let(:h) { Whacamole::HerokuWrapper.new("staging", "foobar", %w{web worker}) }
|
6
|
+
|
7
|
+
describe "dynos" do
|
8
|
+
it "returns dyno types to monitor as given to initialize" do
|
9
|
+
h.dynos.should == %w{web worker}
|
10
|
+
end
|
11
|
+
end
|
6
12
|
|
7
13
|
describe "authorization" do
|
8
14
|
it "base64-encodes the api token and a preceding colon" do
|
data/spec/stream_spec.rb
CHANGED
@@ -16,6 +16,10 @@ class RestartHandler
|
|
16
16
|
def restart(process)
|
17
17
|
true
|
18
18
|
end
|
19
|
+
|
20
|
+
def dynos
|
21
|
+
%w{web worker}
|
22
|
+
end
|
19
23
|
end
|
20
24
|
|
21
25
|
describe Whacamole::Stream do
|
@@ -67,6 +71,19 @@ describe Whacamole::Stream do
|
|
67
71
|
end
|
68
72
|
end
|
69
73
|
|
74
|
+
it "handles dyno types specified in restart handler" do
|
75
|
+
stream.dispatch_handlers <<-CHUNK
|
76
|
+
2013-08-22T16:39:22.919536+00:00 heroku[web.2]: source=web.2 dyno=heroku.772639.a334caa8-736c-48b3-bac2-d366f75d7fa0 sample#memory_total=101MB sample#memory_rss=581.75MB sample#memory_cache=0.16MB sample#memory_swap=0.03MB sample#memory_pgpgin=0pages sample#memory_pgpgout=179329pages
|
77
|
+
2013-08-22T16:39:22.919536+00:00 heroku[worker.3]: source=worker.3 dyno=heroku.772639.a334caa8-736c-48b3-bac2-d366f75d7fa0 sample#memory_total=101MB sample#memory_rss=581.75MB sample#memory_cache=0.16MB sample#memory_swap=0.03MB sample#memory_pgpgin=0pages sample#memory_pgpgout=179329pages
|
78
|
+
2013-08-22T16:39:22.919536+00:00 heroku[whacamole.1]: source=whacamole.1 dyno=heroku.772639.a334caa8-736c-48b3-bac2-d366f75d7fa0 sample#memory_total=1001MB sample#memory_rss=581.75MB sample#memory_cache=0.16MB sample#memory_swap=0.03MB sample#memory_pgpgin=0pages sample#memory_pgpgout=179329pages
|
79
|
+
CHUNK
|
80
|
+
|
81
|
+
eh.events.length.should == 2
|
82
|
+
|
83
|
+
eh.events.first.process.should == "web.2"
|
84
|
+
eh.events.last.process.should == "worker.3"
|
85
|
+
end
|
86
|
+
|
70
87
|
context "when memory usages is over the threshold" do
|
71
88
|
it "kicks off a restart" do
|
72
89
|
restart_handler.should_receive(:restart).with("web.2")
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whacamole
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Chris Doyle
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date:
|
11
|
+
date: 2014-10-27 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: rspec
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,7 +27,6 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
31
|
- - ~>
|
36
32
|
- !ruby/object:Gem::Version
|
@@ -38,7 +34,6 @@ dependencies:
|
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
38
|
- - ~>
|
44
39
|
- !ruby/object:Gem::Version
|
@@ -73,27 +68,26 @@ files:
|
|
73
68
|
homepage: http://github.com/arches/whacamole
|
74
69
|
licenses:
|
75
70
|
- MIT
|
71
|
+
metadata: {}
|
76
72
|
post_install_message:
|
77
73
|
rdoc_options: []
|
78
74
|
require_paths:
|
79
75
|
- lib
|
80
76
|
required_ruby_version: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
77
|
requirements:
|
83
78
|
- - ! '>='
|
84
79
|
- !ruby/object:Gem::Version
|
85
80
|
version: '0'
|
86
81
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
87
|
-
none: false
|
88
82
|
requirements:
|
89
83
|
- - ! '>='
|
90
84
|
- !ruby/object:Gem::Version
|
91
85
|
version: '0'
|
92
86
|
requirements: []
|
93
87
|
rubyforge_project:
|
94
|
-
rubygems_version:
|
88
|
+
rubygems_version: 2.0.7
|
95
89
|
signing_key:
|
96
|
-
specification_version:
|
90
|
+
specification_version: 4
|
97
91
|
summary: restart heroku dynos that run out of RAM instead of swapping to disk
|
98
92
|
test_files:
|
99
93
|
- spec/config_spec.rb
|