heroku_hatchet 6.0.0 → 7.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 +4 -4
- data/.circleci/config.yml +2 -0
- data/CHANGELOG.md +16 -1
- data/README.md +772 -174
- data/bin/hatchet +4 -2
- data/hatchet.gemspec +1 -2
- data/hatchet.json +2 -1
- data/hatchet.lock +2 -0
- data/lib/hatchet.rb +1 -2
- data/lib/hatchet/api_rate_limit.rb +6 -17
- data/lib/hatchet/app.rb +137 -30
- data/lib/hatchet/config.rb +1 -1
- data/lib/hatchet/git_app.rb +27 -1
- data/lib/hatchet/reaper.rb +159 -56
- data/lib/hatchet/reaper/app_age.rb +49 -0
- data/lib/hatchet/reaper/reaper_throttle.rb +55 -0
- data/lib/hatchet/shell_throttle.rb +71 -0
- data/lib/hatchet/test_run.rb +2 -1
- data/lib/hatchet/version.rb +1 -1
- data/spec/hatchet/allow_failure_git_spec.rb +27 -2
- data/spec/hatchet/app_spec.rb +145 -6
- data/spec/hatchet/ci_spec.rb +10 -1
- data/spec/hatchet/lock_spec.rb +12 -1
- data/spec/unit/reaper_spec.rb +153 -0
- data/spec/unit/shell_throttle.rb +28 -0
- metadata +16 -23
data/spec/hatchet/lock_spec.rb
CHANGED
@@ -2,6 +2,10 @@ require "spec_helper"
|
|
2
2
|
require 'yaml'
|
3
3
|
|
4
4
|
describe "LockTest" do
|
5
|
+
before(:all) do
|
6
|
+
puts(`bundle exec hatchet lock`)
|
7
|
+
end
|
8
|
+
|
5
9
|
it "app with failure can be locked to prior commit" do
|
6
10
|
Hatchet::GitApp.new("lock_fail").deploy do |app|
|
7
11
|
expect(app.deployed?).to be_truthy
|
@@ -9,11 +13,18 @@ describe "LockTest" do
|
|
9
13
|
end
|
10
14
|
|
11
15
|
it "app with failure can be locked to master" do
|
12
|
-
puts(`bundle exec hatchet lock`)
|
13
16
|
lock = YAML.load_file("hatchet.lock")
|
14
17
|
name, branch = lock.select { |k, v| k.end_with?("lock_fail_master") }.first
|
15
18
|
|
16
19
|
expect(name).to eq("repo_fixtures/repos/lock/lock_fail_master")
|
17
20
|
expect(branch).to eq("master")
|
18
21
|
end
|
22
|
+
|
23
|
+
it "app with failure can be locked to main" do
|
24
|
+
lock = YAML.load_file("hatchet.lock")
|
25
|
+
name, branch = lock.select { |k, v| k.end_with?("lock_fail_main") }.first
|
26
|
+
|
27
|
+
expect(name).to eq("repo_fixtures/repos/lock/lock_fail_main")
|
28
|
+
expect(branch).to eq("main")
|
29
|
+
end
|
19
30
|
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Reaper" do
|
4
|
+
describe "cycle" do
|
5
|
+
it "does not delete anything if under the limit" do
|
6
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: Object.new, hatchet_app_limit: 1, io: StringIO.new)
|
7
|
+
|
8
|
+
def reaper.get_heroku_apps
|
9
|
+
@called_get_heroku_apps = true
|
10
|
+
|
11
|
+
@mock_apps ||= [{"name" => "hatchet-t-foo", "id" => 1, "maintenance" => true, "created_at" => Time.now.to_s}]
|
12
|
+
end
|
13
|
+
def reaper.check_get_heroku_apps_called; @called_get_heroku_apps ; end
|
14
|
+
def reaper.reap_once; raise "should not be called"; end
|
15
|
+
|
16
|
+
reaper.cycle
|
17
|
+
|
18
|
+
expect(reaper.check_get_heroku_apps_called).to be_truthy
|
19
|
+
end
|
20
|
+
|
21
|
+
it "deletes a maintenance mode app on error" do
|
22
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: Object.new, hatchet_app_limit: 1, io: StringIO.new)
|
23
|
+
|
24
|
+
def reaper.get_heroku_apps
|
25
|
+
@mock_apps ||= [
|
26
|
+
{"name" => "hatchet-t-unfinished", "id" => 2, "maintenance" => false, "created_at" => Time.now.to_s},
|
27
|
+
{"name" => "hatchet-t-foo", "id" => 1, "maintenance" => true, "created_at" => Time.now.to_s}
|
28
|
+
]
|
29
|
+
end
|
30
|
+
def reaper.destroy_with_log(name: , id: )
|
31
|
+
@reaper_destroy_called_with = {"name" => name, "id" => id}
|
32
|
+
end
|
33
|
+
def reaper.destroy_called_with; @reaper_destroy_called_with; end
|
34
|
+
|
35
|
+
reaper.cycle(app_exception_message: true)
|
36
|
+
|
37
|
+
expect(reaper.destroy_called_with).to eq({"name" => "hatchet-t-foo", "id" => 1})
|
38
|
+
end
|
39
|
+
|
40
|
+
it "deletes maintenance mode app when over limit" do
|
41
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: Object.new, hatchet_app_limit: 0, io: StringIO.new)
|
42
|
+
|
43
|
+
def reaper.get_heroku_apps
|
44
|
+
@mock_apps ||= [{"name" => "hatchet-t-foo", "id" => 1, "maintenance" => true, "created_at" => Time.now.to_s}]
|
45
|
+
end
|
46
|
+
def reaper.destroy_with_log(name: , id: )
|
47
|
+
@reaper_destroy_called_with = {"name" => name, "id" => id}
|
48
|
+
end
|
49
|
+
def reaper.destroy_called_with; @reaper_destroy_called_with; end
|
50
|
+
|
51
|
+
reaper.cycle
|
52
|
+
|
53
|
+
expect(reaper.destroy_called_with).to eq({"name" => "hatchet-t-foo", "id" => 1})
|
54
|
+
end
|
55
|
+
|
56
|
+
it "deletes an old app that is past TLL" do
|
57
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: Object.new, hatchet_app_limit: 0, io: StringIO.new)
|
58
|
+
|
59
|
+
def reaper.get_heroku_apps
|
60
|
+
two_days_ago = DateTime.now.new_offset(0) - 2
|
61
|
+
@mock_apps ||= [{"name" => "hatchet-t-foo", "id" => 1, "maintenance" => false, "created_at" => two_days_ago.to_s }]
|
62
|
+
end
|
63
|
+
def reaper.destroy_with_log(name: , id: )
|
64
|
+
@reaper_destroy_called_with = {"name" => name, "id" => id}
|
65
|
+
end
|
66
|
+
def reaper.destroy_called_with; @reaper_destroy_called_with; end
|
67
|
+
|
68
|
+
reaper.cycle
|
69
|
+
|
70
|
+
expect(reaper.destroy_called_with).to eq({"name" => "hatchet-t-foo", "id" => 1})
|
71
|
+
end
|
72
|
+
|
73
|
+
it "sleeps, refreshes app list, and tries again when an old app is not past TTL" do
|
74
|
+
warning = StringIO.new
|
75
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: Object.new, hatchet_app_limit: 1, initial_sleep: 0, io: warning)
|
76
|
+
|
77
|
+
def reaper.get_heroku_apps
|
78
|
+
now = DateTime.now.new_offset(0)
|
79
|
+
@mock_apps ||= [{"name" => "hatchet-t-foo", "id" => 1, "maintenance" => false, "created_at" => now.to_s }]
|
80
|
+
end
|
81
|
+
def reaper.destroy_with_log(name: , id: )
|
82
|
+
@reaper_destroy_called_with = {"name" => name, "id" => id}
|
83
|
+
end
|
84
|
+
def reaper.destroy_called_with; @reaper_destroy_called_with; end
|
85
|
+
def reaper.sleep(val)
|
86
|
+
@_slept_for = val
|
87
|
+
end
|
88
|
+
|
89
|
+
def reaper.get_slept_for_val; @_slept_for; end
|
90
|
+
|
91
|
+
reaper.cycle(app_exception_message: true)
|
92
|
+
|
93
|
+
expect(reaper.get_slept_for_val).to eq(0)
|
94
|
+
expect(reaper.destroy_called_with).to eq(nil)
|
95
|
+
|
96
|
+
expect(warning.string).to match("WARNING")
|
97
|
+
expect(warning.string).to match("total_app_count: 1, hatchet_app_count: 1/#{Hatchet::Reaper::HATCHET_APP_LIMIT}, finished: 0, unfinished: 1")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "app age" do
|
102
|
+
it "calculates young apps" do
|
103
|
+
time_now = DateTime.parse("2020-07-28T14:40:00Z")
|
104
|
+
age = Hatchet::Reaper::AppAge.new(created_at: time_now, time_now: time_now, ttl_minutes: 1)
|
105
|
+
expect(age.in_minutes).to eq(0.0)
|
106
|
+
expect(age.too_young_to_die?).to be_truthy
|
107
|
+
expect(age.can_delete?).to be_falsey
|
108
|
+
expect(age.sleep_for_ttl).to eq(60)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "calculates old apps" do
|
112
|
+
time_now = DateTime.parse("2020-07-28T14:40:00Z")
|
113
|
+
created_at = time_now - 2
|
114
|
+
age = Hatchet::Reaper::AppAge.new(created_at: created_at, time_now: time_now, ttl_minutes: 1)
|
115
|
+
expect(age.in_minutes).to eq(2880.0)
|
116
|
+
expect(age.too_young_to_die?).to be_falsey
|
117
|
+
expect(age.can_delete?).to be_truthy
|
118
|
+
expect(age.sleep_for_ttl).to eq(0)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "reaper throttle" do
|
123
|
+
it "increments and decrements based on min_sleep" do
|
124
|
+
reaper_throttle = Hatchet::Reaper::ReaperThrottle.new(initial_sleep: 2)
|
125
|
+
reaper_throttle.call(max_sleep: 5) do |sleep_for|
|
126
|
+
expect(sleep_for).to eq(2)
|
127
|
+
end
|
128
|
+
reaper_throttle.call(max_sleep: 5) do |sleep_for|
|
129
|
+
expect(sleep_for).to eq(4)
|
130
|
+
end
|
131
|
+
reaper_throttle.call(max_sleep: 5) do |sleep_for|
|
132
|
+
expect(sleep_for).to eq(5)
|
133
|
+
end
|
134
|
+
# The throttle is now reset since it hit the min_sleep value
|
135
|
+
|
136
|
+
reaper_throttle.call(max_sleep: 5) do |sleep_for|
|
137
|
+
expect(sleep_for).to eq(2)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "over limit" do
|
143
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: -> (){}, io: StringIO.new)
|
144
|
+
def reaper.hatchet_app_count; Hatchet::Reaper::HATCHET_APP_LIMIT + 1; end
|
145
|
+
|
146
|
+
expect(reaper.over_limit?).to be_truthy
|
147
|
+
|
148
|
+
reaper = Hatchet::Reaper.new(api_rate_limit: -> (){}, io: StringIO.new)
|
149
|
+
def reaper.hatchet_app_count; Hatchet::Reaper::HATCHET_APP_LIMIT - 1; end
|
150
|
+
|
151
|
+
expect(reaper.over_limit?).to be_falsey
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "ShellThrottle" do
|
4
|
+
it "throttles when throw is called" do
|
5
|
+
platform_api = Hatchet::Runner.new("default_ruby").platform_api
|
6
|
+
|
7
|
+
@count = 0
|
8
|
+
Hatchet::ShellThrottle.new(platform_api: platform_api).call do
|
9
|
+
@count += 1
|
10
|
+
if @count >= 2
|
11
|
+
# No throttle
|
12
|
+
else
|
13
|
+
throw(:throttle)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
expect(@count).to eq(2)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "does not throttle when throw is NOT called" do
|
20
|
+
platform_api = Hatchet::Runner.new("default_ruby").platform_api
|
21
|
+
|
22
|
+
@count = 0
|
23
|
+
Hatchet::ShellThrottle.new(platform_api: platform_api).call do
|
24
|
+
@count += 1
|
25
|
+
end
|
26
|
+
expect(@count).to eq(1)
|
27
|
+
end
|
28
|
+
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heroku_hatchet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Schneeman
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: platform-api
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 3
|
19
|
+
version: '3'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 3
|
26
|
+
version: '3'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rrrretry
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,20 +66,6 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: repl_runner
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.0.3
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.0.3
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: threaded
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -207,6 +193,9 @@ files:
|
|
207
193
|
- lib/hatchet/config.rb
|
208
194
|
- lib/hatchet/git_app.rb
|
209
195
|
- lib/hatchet/reaper.rb
|
196
|
+
- lib/hatchet/reaper/app_age.rb
|
197
|
+
- lib/hatchet/reaper/reaper_throttle.rb
|
198
|
+
- lib/hatchet/shell_throttle.rb
|
210
199
|
- lib/hatchet/tasks.rb
|
211
200
|
- lib/hatchet/test_run.rb
|
212
201
|
- lib/hatchet/version.rb
|
@@ -222,12 +211,14 @@ files:
|
|
222
211
|
- spec/hatchet/local_repo_spec.rb
|
223
212
|
- spec/hatchet/lock_spec.rb
|
224
213
|
- spec/spec_helper.rb
|
214
|
+
- spec/unit/reaper_spec.rb
|
215
|
+
- spec/unit/shell_throttle.rb
|
225
216
|
- tmp/parallel_runtime_test.log
|
226
217
|
homepage: https://github.com/heroku/hatchet
|
227
218
|
licenses:
|
228
219
|
- MIT
|
229
220
|
metadata: {}
|
230
|
-
post_install_message:
|
221
|
+
post_install_message:
|
231
222
|
rdoc_options: []
|
232
223
|
require_paths:
|
233
224
|
- lib
|
@@ -243,7 +234,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
243
234
|
version: '0'
|
244
235
|
requirements: []
|
245
236
|
rubygems_version: 3.1.2
|
246
|
-
signing_key:
|
237
|
+
signing_key:
|
247
238
|
specification_version: 4
|
248
239
|
summary: Hatchet is a an integration testing library for developing Heroku buildpacks.
|
249
240
|
test_files:
|
@@ -257,3 +248,5 @@ test_files:
|
|
257
248
|
- spec/hatchet/local_repo_spec.rb
|
258
249
|
- spec/hatchet/lock_spec.rb
|
259
250
|
- spec/spec_helper.rb
|
251
|
+
- spec/unit/reaper_spec.rb
|
252
|
+
- spec/unit/shell_throttle.rb
|