heroku_hatchet 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: 6.0.0
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-05-06 00:00:00.000000000 Z
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.0.0.pre.1
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.0.0.pre.1
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