kensa 2.2.0 → 2.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5f100c19fcb05b5c0dd0a94287ac64d745050905
4
- data.tar.gz: ac3f713db8e27febba8a41b276de592fa63e469d
3
+ metadata.gz: a1b0f6152cc582f8ce121149cbf51c06d3a31146
4
+ data.tar.gz: 2cf35428fd4e02beb72d185581ea8ed500330c74
5
5
  SHA512:
6
- metadata.gz: c222175a5ce64e55ea5afad73536e716bb38eb66224dd4d84810adcae537dcbb5af2f2b2aea11ee0c7100a3ceeec4c286810962e465149a1bb8673acaede5e1f
7
- data.tar.gz: d9cd6cffdd76e56bf78b9d7c1ad8b21abca12729fa59c0deedd4985d29ec9355f8fe8736002d6357d314c6a1216d60ee5a079de494c19d70953acbb9f8a59e16
6
+ metadata.gz: c90f46e0e4c4757e000078525c306ac07b90e20ca0a2cdd0ce4a718bdfec2dfd24b3a16e8bfc59f39b4c1feedabd92e949d81b5867162fb7fca46dd733f42696
7
+ data.tar.gz: 3e8f395728af6bbb4a3999c7434aaea9f6b44f5b59879e66bcb99909e0da913ab6dac403bdbb825eca500a8754b653b9d1e71042c8d1a74d6cc88519946889dc
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kensa (2.2.0)
4
+ kensa (2.3.0)
5
5
  launchy (~> 2.2.0)
6
6
  mechanize (~> 2.6.0)
7
7
  netrc (~> 0.10.3)
@@ -38,6 +38,7 @@ GEM
38
38
  nokogiri (1.6.5)
39
39
  mini_portile (~> 0.6.0)
40
40
  ntlm-http (0.1.1)
41
+ power_assert (0.2.2)
41
42
  pry (0.9.11.4)
42
43
  coderay (~> 1.0.5)
43
44
  method_source (~> 0.8)
@@ -60,6 +61,8 @@ GEM
60
61
  slop (3.4.3)
61
62
  term-ansicolor (1.3.0)
62
63
  tins (~> 1.0)
64
+ test-unit (3.0.9)
65
+ power_assert
63
66
  tilt (1.3.6)
64
67
  timecop (0.6.1)
65
68
  tins (1.3.5)
@@ -80,4 +83,5 @@ DEPENDENCIES
80
83
  rake
81
84
  rr (~> 1.0.4)
82
85
  sinatra (~> 1.4.2)
86
+ test-unit
83
87
  timecop (~> 0.6.1)
data/kensa.gemspec CHANGED
@@ -38,5 +38,6 @@ Gem::Specification.new do |s|
38
38
  s.add_development_dependency(%q<sinatra>, "~> 1.4.2")
39
39
  s.add_development_dependency(%q<timecop>, "~> 0.6.1")
40
40
  s.add_development_dependency(%q<pry>)
41
+ s.add_development_dependency(%q<test-unit>)
41
42
  end
42
43
 
@@ -68,6 +68,10 @@ module Heroku
68
68
  data['api'][env].chomp("/")
69
69
  end
70
70
  end
71
+
72
+ def api_requires?(feature)
73
+ data["api"].fetch("requires", []).include?(feature)
74
+ end
71
75
  end
72
76
 
73
77
 
@@ -142,11 +146,11 @@ module Heroku
142
146
  end
143
147
  check "all config vars are prefixed with the addon id" do
144
148
  data["api"]["config_vars"].each do |k|
145
- addon_key = data['id'].upcase.gsub('-', '_')
146
- if k =~ /^#{addon_key}_/
149
+ prefix = data["api"]["config_vars_prefix"] || data['id'].upcase.gsub('-', '_')
150
+ if k =~ /^#{prefix}_/
147
151
  true
148
152
  else
149
- error "#{k} is not a valid ENV key - must be prefixed with #{addon_key}_"
153
+ error "#{k} is not a valid ENV key - must be prefixed with #{prefix}_"
150
154
  end
151
155
  end
152
156
  end
@@ -170,7 +174,15 @@ module Heroku
170
174
  test "response"
171
175
 
172
176
  check "contains an id" do
173
- response.is_a?(Hash) && response.has_key?("id")
177
+ response.is_a?(Hash) && response["id"]
178
+ end
179
+
180
+ check "id does not contain heroku_id" do
181
+ if response["id"].to_s.include? data["heroku_id"].scan(/app(\d+)@/).flatten.first
182
+ error "id cannot include heroku_id"
183
+ else
184
+ true
185
+ end
174
186
  end
175
187
 
176
188
  screen.message " (id #{response['id']})"
@@ -222,7 +234,7 @@ module Heroku
222
234
  end
223
235
 
224
236
  check "syslog_drain_url is returned if required" do
225
- return true unless data.has_key?("requires") && data["requires"].include?("syslog_drain")
237
+ return true unless api_requires?("syslog_drain")
226
238
 
227
239
  drain_url = response['syslog_drain_url']
228
240
 
@@ -244,7 +256,6 @@ module Heroku
244
256
 
245
257
  end
246
258
 
247
-
248
259
  class ApiCheck < Check
249
260
  def base_path
250
261
  if data['api'][env].is_a? Hash
@@ -261,6 +272,61 @@ module Heroku
261
272
  def credentials
262
273
  [ data['id'], data['api']['password'] ]
263
274
  end
275
+
276
+ def callback
277
+ "http://localhost:7779/callback/999"
278
+ end
279
+
280
+ def create_provision_payload
281
+ payload = {
282
+ :heroku_id => heroku_id,
283
+ :plan => data[:plan] || 'test',
284
+ :callback_url => callback,
285
+ :logplex_token => nil,
286
+ :region => "amazon-web-services::us-east-1",
287
+ :options => data[:options] || {},
288
+ :uuid => SecureRandom.uuid
289
+ }
290
+
291
+ if api_requires?("syslog_drain")
292
+ payload[:log_drain_token] = SecureRandom.hex
293
+ end
294
+ payload
295
+ end
296
+ end
297
+
298
+ class DuplicateProvisionCheck < ApiCheck
299
+ include HTTP
300
+
301
+ READLEN = 1024 * 10
302
+
303
+ def call!
304
+
305
+ json = nil
306
+ response = nil
307
+
308
+ code = nil
309
+ json = nil
310
+ reader, writer = nil
311
+
312
+ payload = create_provision_payload
313
+
314
+ code1, json1 = post(credentials, base_path, payload)
315
+ code2, json2 = post(credentials, base_path, payload)
316
+
317
+ json1 = OkJson.decode(json1)
318
+ json2 = OkJson.decode(json2)
319
+
320
+ if api_requires?("many_per_app")
321
+ check "returns different ids" do
322
+ if json1["id"] == json2["id"]
323
+ error "multiple provisions cannot return the same id"
324
+ else
325
+ true
326
+ end
327
+ end
328
+ end
329
+ end
264
330
  end
265
331
 
266
332
  class ProvisionCheck < ApiCheck
@@ -274,17 +340,9 @@ module Heroku
274
340
 
275
341
  code = nil
276
342
  json = nil
277
- callback = "http://localhost:7779/callback/999"
278
343
  reader, writer = nil
279
344
 
280
- payload = {
281
- :heroku_id => heroku_id,
282
- :plan => data[:plan] || 'test',
283
- :callback_url => callback,
284
- :logplex_token => nil,
285
- :region => "amazon-web-services::us-east-1",
286
- :options => data[:options] || {}
287
- }
345
+ payload = create_provision_payload
288
346
 
289
347
  if data[:async]
290
348
  reader, writer = IO.pipe
@@ -345,7 +403,11 @@ module Heroku
345
403
 
346
404
  data[:provision_response] = response
347
405
 
348
- run ProvisionResponseCheck, data
406
+ run ProvisionResponseCheck, data.merge("heroku_id" => heroku_id)
407
+
408
+ if !api_requires?("many_per_app")
409
+ run DuplicateProvisionCheck, data
410
+ end
349
411
  end
350
412
 
351
413
  ensure
@@ -26,7 +26,8 @@ module Heroku
26
26
  "regions": [ "us" ],
27
27
  "password": "#{@password}",#{ sso_key }
28
28
  "production": "https://yourapp.com/",
29
- "test": "http://localhost:#{@port}/"
29
+ "test": "http://localhost:#{@port}/",
30
+ "requires": []
30
31
  }
31
32
  }
32
33
  JSON
@@ -38,6 +39,7 @@ JSON
38
39
  "id": "myaddon",
39
40
  "api": {
40
41
  "config_vars": [ "MYADDON_URL" ],
42
+ "requires": [],
41
43
  "regions": [ "us" ],
42
44
  "password": "#{@password}",#{ sso_key }
43
45
  "production": {
@@ -1,5 +1,5 @@
1
1
  module Heroku
2
2
  module Kensa
3
- VERSION = '2.2.0'
3
+ VERSION = '2.3.0'
4
4
  end
5
5
  end
@@ -7,6 +7,7 @@ class AllCheckTest < Test::Unit::TestCase
7
7
 
8
8
  setup do
9
9
  @data = Manifest.new(:method => :get).skeleton
10
+ @data["api"]["requires"] << "many_per_app"
10
11
  @data['api']['password'] = 'secret'
11
12
  @data['api']['test'] += "working"
12
13
  @file = File.dirname(__FILE__) + "/resources/runner.rb"
@@ -0,0 +1,34 @@
1
+ require 'test/helper'
2
+
3
+ class DuplicateProvisionCheckTest < Test::Unit::TestCase
4
+ include Heroku::Kensa
5
+ include ProviderMock
6
+
7
+ def check; DuplicateProvisionCheck; end
8
+
9
+ setup do
10
+ @data = Manifest.new(method: :post).skeleton
11
+ @data["api"]["password"] = "secret"
12
+ ProviderServer::ProvisionRecord.reset
13
+ end
14
+
15
+ context "when the provider supports many_per_app" do
16
+ setup { @data["api"]["requires"] = ["many_per_app"] }
17
+
18
+ test "allows duplicate provision attempts" do
19
+ use_provider_endpoint "working_duplicate"
20
+ assert_valid
21
+ end
22
+
23
+ test "duplicate provisions return different provider ids" do
24
+ use_provider_endpoint "working_duplicate"
25
+ assert_valid
26
+ end
27
+
28
+ test "fails when deuplicate provisions return the same provider id" do
29
+ use_provider_endpoint "working"
30
+ assert_invalid
31
+ end
32
+ end
33
+ end
34
+
data/test/helper.rb CHANGED
@@ -10,6 +10,24 @@ require 'fakefs/safe'
10
10
  class Test::Unit::TestCase
11
11
  include RR::Adapters::TestUnit
12
12
 
13
+ class ProvisionRecord
14
+ def self.provisions
15
+ @provisions ||= 0
16
+ end
17
+
18
+ def self.incr
19
+ @provisions += 1
20
+ end
21
+
22
+ def self.reset
23
+ @provisions = 0
24
+ end
25
+
26
+ def self.count
27
+ @provisions
28
+ end
29
+ end
30
+
13
31
  module ProviderMock
14
32
  def setup
15
33
  Artifice.activate_with(ProviderServer)
@@ -97,6 +97,34 @@ class ManifestCheckTest < Test::Unit::TestCase
97
97
  assert_invalid
98
98
  end
99
99
 
100
+ context "when config_var_prefix is set" do
101
+ context "to something other than the slug" do
102
+ test "config vars matching config_vars_prefix are valid" do
103
+ @data["api"]["config_vars_prefix"] = "THING"
104
+ @data["api"]["config_vars"] = ['THING_URL']
105
+ assert_valid
106
+ end
107
+
108
+ test "config vars matching slug are invalid" do
109
+ @data["api"]["config_vars_prefix"] = "THING"
110
+ @data["api"]["config_vars"] << "MYADDON_URL"
111
+ assert_invalid
112
+ end
113
+ end
114
+ end
115
+
116
+ context "when config_var_prefix is not set" do
117
+ test "config vars matching slug are valid" do
118
+ @data["api"]["config_vars"] << 'MYADDON_URL'
119
+ assert_valid
120
+ end
121
+
122
+ test "config vars not matching slug are invalid" do
123
+ @data["api"]["config_vars"] << "NOT_SLUG_URL"
124
+ assert_invalid
125
+ end
126
+ end
127
+
100
128
  test "assert config var prefixes match addon id" do
101
129
  @data["api"]["config_vars"] << 'MONGO_URL'
102
130
  assert_invalid
@@ -10,6 +10,7 @@ class ProvisionCheckTest < Test::Unit::TestCase
10
10
  context "with sso #{method}" do
11
11
  setup do
12
12
  @data = Manifest.new(:method => method).skeleton
13
+ @data["api"]["requires"] = ["many_per_app"]
13
14
  @data['api']['password'] = 'secret'
14
15
  end
15
16
 
@@ -29,6 +30,13 @@ class ProvisionCheckTest < Test::Unit::TestCase
29
30
  assert_valid
30
31
  end
31
32
 
33
+ context "when supporting many_per_app" do
34
+ test "passes duplicate provision check" do
35
+ @data["api"]["requires"] = ["many_per_app"]
36
+ assert_valid
37
+ end
38
+ end
39
+
32
40
  test "provision call with extra params" do
33
41
  use_provider_endpoint "cmd-line-options"
34
42
  @data[:options] = {:foo => 'bar', :bar => 'baz'}
@@ -13,6 +13,7 @@ class ProvisionResponseCheckTest < Test::Unit::TestCase
13
13
  }}
14
14
  @data = Manifest.new.skeleton.merge(:provision_response => @response)
15
15
  @data['api']['config_vars'] << "MYADDON_CONFIG"
16
+ @data["heroku_id"] = "app987@kensa.heroku.com"
16
17
  end
17
18
 
18
19
  test "is valid if no errors" do
@@ -24,6 +25,11 @@ class ProvisionResponseCheckTest < Test::Unit::TestCase
24
25
  assert_invalid
25
26
  end
26
27
 
28
+ test "id does not contain the heroku_id" do
29
+ @response["id"] = "987"
30
+ assert_invalid
31
+ end
32
+
27
33
  describe "when config is present" do
28
34
 
29
35
  test "is a hash" do
@@ -74,7 +80,7 @@ class ProvisionResponseCheckTest < Test::Unit::TestCase
74
80
 
75
81
  describe "when syslog drain is required" do
76
82
  setup do
77
- @data["requires"] = ["syslog_drain"]
83
+ @data["api"]["requires"] = ["syslog_drain"]
78
84
  end
79
85
 
80
86
  test "response is invalid without a syslog_drain_url" do
@@ -53,13 +53,50 @@ ERB
53
53
  end
54
54
  end
55
55
 
56
+ class ProvisionRecord
57
+ def self.provisions
58
+ @provisions ||= 0
59
+ end
60
+
61
+ def self.incr
62
+ @provisions += 1
63
+ end
64
+
65
+ def self.reset
66
+ @provisions = 0
67
+ end
68
+
69
+ def self.count
70
+ @provisions
71
+ end
72
+ end
73
+
74
+ post '/working_duplicate/heroku/resources' do
75
+ heroku_only!
76
+ ProvisionRecord.incr
77
+ { id: ProvisionRecord.count }.to_json
78
+ end
79
+
80
+ post '/duplicate/heroku/resources' do
81
+ heroku_only!
82
+ ProvisionRecord.incr
83
+
84
+ if ProvisionRecord.count > 1
85
+ status 422
86
+ {}.to_json
87
+ else
88
+ status 201
89
+ { id: 123 }.to_json
90
+ end
91
+ end
92
+
56
93
  post '/heroku/resources' do
57
94
  heroku_only!
58
95
  { :id => 123 }.to_json
59
96
  end
60
97
 
61
98
  post '/working/heroku/resources' do
62
- json_must_include(%w{heroku_id plan callback_url logplex_token options})
99
+ json_must_include(%w{heroku_id plan callback_url options})
63
100
  heroku_only!
64
101
  { :id => 123 }.to_json
65
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kensa
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Blake Mizerany
@@ -13,7 +13,7 @@ authors:
13
13
  autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
- date: 2015-04-09 00:00:00.000000000 Z
16
+ date: 2015-05-14 00:00:00.000000000 Z
17
17
  dependencies:
18
18
  - !ruby/object:Gem::Dependency
19
19
  name: launchy
@@ -203,6 +203,20 @@ dependencies:
203
203
  - - ">="
204
204
  - !ruby/object:Gem::Version
205
205
  version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: test-unit
208
+ requirement: !ruby/object:Gem::Requirement
209
+ requirements:
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: '0'
213
+ type: :development
214
+ prerelease: false
215
+ version_requirements: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - ">="
218
+ - !ruby/object:Gem::Version
219
+ version: '0'
206
220
  description: Kensa is a command-line tool to help add-on providers integrating their
207
221
  services with Heroku. It manages manifest files, and provides a TDD-like approach
208
222
  for programmers to test and develop their APIs.
@@ -236,6 +250,7 @@ files:
236
250
  - test/all_check_test.rb
237
251
  - test/create_test.rb
238
252
  - test/deprovision_check_test.rb
253
+ - test/duplicate_provision_check_test.rb
239
254
  - test/helper.rb
240
255
  - test/init_test.rb
241
256
  - test/manifest_check_test.rb
@@ -276,6 +291,7 @@ test_files:
276
291
  - test/all_check_test.rb
277
292
  - test/create_test.rb
278
293
  - test/deprovision_check_test.rb
294
+ - test/duplicate_provision_check_test.rb
279
295
  - test/helper.rb
280
296
  - test/init_test.rb
281
297
  - test/manifest_check_test.rb