berkshelf-api 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 +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +26 -21
- data/berkshelf-api.gemspec +3 -3
- data/lib/berkshelf/api/application.rb +4 -2
- data/lib/berkshelf/api/cache_builder/worker/chef_server.rb +1 -1
- data/lib/berkshelf/api/cache_builder/worker/opscode.rb +1 -1
- data/lib/berkshelf/api/cache_builder/worker.rb +4 -47
- data/lib/berkshelf/api/cache_builder.rb +5 -11
- data/lib/berkshelf/api/cache_manager.rb +111 -35
- data/lib/berkshelf/api/config.rb +5 -0
- data/lib/berkshelf/api/dependency_cache.rb +25 -4
- data/lib/berkshelf/api/endpoint/v1.rb +6 -1
- data/lib/berkshelf/api/remote_cookbook.rb +9 -1
- data/lib/berkshelf/api/rest_gateway.rb +112 -18
- data/lib/berkshelf/api/rspec/server.rb +4 -1
- data/lib/berkshelf/api/site_connector/opscode.rb +1 -1
- data/lib/berkshelf/api/version.rb +1 -1
- data/lib/berkshelf/api.rb +1 -0
- data/spec/unit/berkshelf/api/cache_builder/worker/chef_server_spec.rb +0 -21
- data/spec/unit/berkshelf/api/cache_builder/worker_spec.rb +0 -40
- data/spec/unit/berkshelf/api/cache_builder_spec.rb +49 -6
- data/spec/unit/berkshelf/api/cache_manager_spec.rb +36 -7
- data/spec/unit/berkshelf/api/dependency_cache_spec.rb +7 -0
- data/spec/unit/berkshelf/api/endpoint/v1_spec.rb +18 -5
- data/spec/unit/berkshelf/api/rest_gateway_spec.rb +1 -7
- metadata +11 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f238c1aec4a689f0461aaea5792d0588501e4d8c
|
4
|
+
data.tar.gz: f99f8dac7ac5aa4133ac4bef0420e74d128cd01f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0910bcf676f10ea3f0c7061a685400ee232350b39fe08866ed89558e6d3dc649cb6a6b0288041a59924c010825a23510fada8adc043466a37b56374b74ad1543
|
7
|
+
data.tar.gz: de9395b533f039629f2eef14bd583de33839afac67e225844616c5cdc22a6d3038b3852839e2f110b4f19e2e227ec946284d58bfc0f9dfd04792a645b966d1aa
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
[](http://badge.fury.io/rb/berkshelf-api)
|
3
3
|
[](http://travis-ci.org/RiotGames/berkshelf-api)
|
4
4
|
[](https://gemnasium.com/RiotGames/berkshelf-api)
|
5
|
+
[](https://codeclimate.com/github/RiotGames/berkshelf-api)
|
5
6
|
|
6
7
|
A server which indexes cookbooks from various sources and hosts it over a REST API
|
7
8
|
|
@@ -23,35 +24,39 @@ Ruby 1.9.1 and 1.9.2 are not officially supported. If you encounter problems, pl
|
|
23
24
|
|
24
25
|
## Configuring Endpoints
|
25
26
|
|
26
|
-
|
27
|
+
You may configure the endpoints to index by editing the JSON configuration file (default: `~/.berkshelf/api-server/config.json`).
|
27
28
|
|
28
29
|
### Opscode Community Site
|
29
30
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
````json
|
32
|
+
{
|
33
|
+
"endpoints": [
|
34
|
+
{
|
35
|
+
"type": "opscode",
|
36
|
+
"options": {
|
37
|
+
"url": "http://cookbooks.opscode.com/api/v1"
|
37
38
|
}
|
38
|
-
|
39
|
-
|
39
|
+
}
|
40
|
+
]
|
41
|
+
}
|
42
|
+
```
|
40
43
|
|
41
44
|
### Chef Server
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
46
|
+
```json
|
47
|
+
{
|
48
|
+
"endpoints": [
|
49
|
+
{
|
50
|
+
"type": "chef_server",
|
51
|
+
"options": {
|
52
|
+
"url": "https://api.opscode.com/organizations/vialstudios",
|
53
|
+
"client_name": "berkshelf",
|
54
|
+
"client_key": "/etc/berkshelf/api-server/client.pem"
|
52
55
|
}
|
53
|
-
|
54
|
-
|
56
|
+
}
|
57
|
+
]
|
58
|
+
}
|
59
|
+
```
|
55
60
|
|
56
61
|
## Contributing
|
57
62
|
|
data/berkshelf-api.gemspec
CHANGED
@@ -19,9 +19,9 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
spec.required_ruby_version = ">= 1.9.3"
|
21
21
|
|
22
|
-
spec.add_dependency 'ridley', '~> 1.
|
23
|
-
spec.add_dependency 'celluloid', '~> 0.
|
24
|
-
spec.add_dependency 'reel', '
|
22
|
+
spec.add_dependency 'ridley', '~> 1.6'
|
23
|
+
spec.add_dependency 'celluloid', '~> 0.15'
|
24
|
+
spec.add_dependency 'reel', '>= 0.4.0'
|
25
25
|
spec.add_dependency 'grape', '~> 0.5.0'
|
26
26
|
spec.add_dependency 'hashie', '>= 2.0.4'
|
27
27
|
spec.add_dependency 'faraday'
|
@@ -116,7 +116,10 @@ module Berkshelf::API
|
|
116
116
|
loop do
|
117
117
|
supervisor = run!(options)
|
118
118
|
|
119
|
-
|
119
|
+
while supervisor.alive?
|
120
|
+
sleep 0.1
|
121
|
+
instance.terminate if @shutdown
|
122
|
+
end
|
120
123
|
|
121
124
|
break if @shutdown
|
122
125
|
|
@@ -153,7 +156,6 @@ module Berkshelf::API
|
|
153
156
|
# if there is no running instance
|
154
157
|
def shutdown
|
155
158
|
@shutdown = true
|
156
|
-
instance.terminate
|
157
159
|
end
|
158
160
|
end
|
159
161
|
end
|
@@ -19,7 +19,7 @@ module Berkshelf::API
|
|
19
19
|
connection.cookbook.all.each do |cookbook, versions|
|
20
20
|
versions.each do |version|
|
21
21
|
cookbook_versions << RemoteCookbook.new(cookbook, version, self.class.worker_type,
|
22
|
-
@connection.server_url)
|
22
|
+
@connection.server_url, priority)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -19,7 +19,7 @@ module Berkshelf::API
|
|
19
19
|
[ cookbook, connection.future(:versions, cookbook) ]
|
20
20
|
end.each do |cookbook, versions|
|
21
21
|
versions.value.each do |version|
|
22
|
-
cookbook_versions << RemoteCookbook.new(cookbook, version, self.class.worker_type, @connection.api_uri)
|
22
|
+
cookbook_versions << RemoteCookbook.new(cookbook, version, self.class.worker_type, @connection.api_uri, priority)
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
@@ -16,8 +16,11 @@ module Berkshelf::API
|
|
16
16
|
include Berkshelf::API::Mixin::Services
|
17
17
|
|
18
18
|
attr_reader :options
|
19
|
+
attr_reader :priority
|
19
20
|
|
20
|
-
def initialize(options = {})
|
21
|
+
def initialize(options = {})
|
22
|
+
@priority = options[:priority]
|
23
|
+
end
|
21
24
|
|
22
25
|
# @abstract
|
23
26
|
#
|
@@ -36,52 +39,6 @@ module Berkshelf::API
|
|
36
39
|
raise RuntimeError, "must be implemented"
|
37
40
|
end
|
38
41
|
|
39
|
-
def build
|
40
|
-
log.info "#{self} building..."
|
41
|
-
log.info "#{self} determining if the cache is stale..."
|
42
|
-
if stale?
|
43
|
-
log.info "#{self} cache is stale."
|
44
|
-
update_cache
|
45
|
-
else
|
46
|
-
log.info "#{self} cache is up to date."
|
47
|
-
end
|
48
|
-
|
49
|
-
log.info "clearing diff"
|
50
|
-
clear_diff
|
51
|
-
end
|
52
|
-
|
53
|
-
# @return [Array<Array<RemoteCookbook>, Array<RemoteCookbook>>]
|
54
|
-
def diff
|
55
|
-
@diff ||= cache_manager.diff(cookbooks)
|
56
|
-
end
|
57
|
-
|
58
|
-
def update_cache
|
59
|
-
created_cookbooks, deleted_cookbooks = diff
|
60
|
-
|
61
|
-
log.info "#{self} adding (#{created_cookbooks.length}) items..."
|
62
|
-
created_cookbooks.collect do |remote|
|
63
|
-
[ remote, future(:metadata, remote) ]
|
64
|
-
end.each do |remote, metadata|
|
65
|
-
cache_manager.add(remote, metadata.value)
|
66
|
-
end
|
67
|
-
|
68
|
-
log.info "#{self} removing (#{deleted_cookbooks.length}) items..."
|
69
|
-
deleted_cookbooks.each { |remote| cache_manager.remove(remote.name, remote.version) }
|
70
|
-
|
71
|
-
log.info "#{self} cache updated."
|
72
|
-
cache_manager.save
|
73
|
-
end
|
74
|
-
|
75
|
-
def stale?
|
76
|
-
created_cookbooks, deleted_cookbooks = diff
|
77
|
-
created_cookbooks.any? || deleted_cookbooks.any?
|
78
|
-
end
|
79
|
-
|
80
|
-
private
|
81
|
-
|
82
|
-
def clear_diff
|
83
|
-
@diff = nil
|
84
|
-
end
|
85
42
|
end
|
86
43
|
|
87
44
|
class << self
|
@@ -8,6 +8,7 @@ module Berkshelf::API
|
|
8
8
|
|
9
9
|
include Berkshelf::API::GenericServer
|
10
10
|
include Berkshelf::API::Logging
|
11
|
+
include Berkshelf::API::Mixin::Services
|
11
12
|
|
12
13
|
server_name :cache_builder
|
13
14
|
finalizer :finalize_callback
|
@@ -18,21 +19,14 @@ module Berkshelf::API
|
|
18
19
|
@worker_supervisor = WorkerSupervisor.new(@worker_registry)
|
19
20
|
@building = false
|
20
21
|
|
21
|
-
Application.config.endpoints.
|
22
|
+
Application.config.endpoints.each_with_index do |endpoint, index|
|
22
23
|
endpoint_options = endpoint.options.to_hash.deep_symbolize_keys
|
23
|
-
@worker_supervisor.supervise(CacheBuilder::Worker[endpoint.type], endpoint_options)
|
24
|
+
@worker_supervisor.supervise(CacheBuilder::Worker[endpoint.type], endpoint_options.merge(priority: index))
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
|
-
# Issue a single build command to all workers
|
28
|
-
#
|
29
|
-
# @return [Array]
|
30
28
|
def build
|
31
|
-
|
32
|
-
begin
|
33
|
-
f.value
|
34
|
-
rescue; end
|
35
|
-
end
|
29
|
+
cache_manager.process_workers(workers)
|
36
30
|
end
|
37
31
|
|
38
32
|
# Issue a build command to all workers at the scheduled interval
|
@@ -44,7 +38,7 @@ module Berkshelf::API
|
|
44
38
|
loop do
|
45
39
|
@building = true
|
46
40
|
build
|
47
|
-
sleep
|
41
|
+
sleep interval
|
48
42
|
end
|
49
43
|
end
|
50
44
|
|
@@ -10,12 +10,16 @@ module Berkshelf::API
|
|
10
10
|
include Berkshelf::API::GenericServer
|
11
11
|
include Berkshelf::API::Logging
|
12
12
|
|
13
|
+
extend Forwardable
|
14
|
+
def_delegators :@cache, :warmed?, :set_warmed, :clear
|
15
|
+
|
13
16
|
SAVE_INTERVAL = 30.0
|
14
17
|
|
15
18
|
server_name :cache_manager
|
16
19
|
finalizer :finalize_callback
|
17
|
-
exclusive :
|
20
|
+
exclusive :merge, :add, :remove
|
18
21
|
|
22
|
+
# @return [DependencyCache]
|
19
23
|
attr_reader :cache
|
20
24
|
|
21
25
|
def initialize
|
@@ -30,14 +34,77 @@ module Berkshelf::API
|
|
30
34
|
#
|
31
35
|
# @return [Hash]
|
32
36
|
def add(cookbook, metadata)
|
33
|
-
|
37
|
+
log.debug "#{self} adding (#{cookbook.name}, #{cookbook.version})"
|
38
|
+
cache.add(cookbook, metadata)
|
34
39
|
end
|
35
40
|
|
36
|
-
#
|
41
|
+
# Remove the cached item matching the given name and version
|
37
42
|
#
|
38
|
-
# @
|
39
|
-
|
40
|
-
|
43
|
+
# @param [#to_s] name
|
44
|
+
# @param [#to_s] version
|
45
|
+
#
|
46
|
+
# @return [DependencyCache]
|
47
|
+
def remove(name, version)
|
48
|
+
log.debug "#{self} removing (#{name}, #{version})"
|
49
|
+
cache.remove(name, version)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Loops through a list of workers and merges their cookbook sets into the cache
|
53
|
+
#
|
54
|
+
# @param [Array<CacheBuilder::Worker::Base>] workers
|
55
|
+
# The workers for this cache
|
56
|
+
#
|
57
|
+
# @return [Boolean]
|
58
|
+
def process_workers(workers)
|
59
|
+
# If the cache has been warmed already, we want to spawn
|
60
|
+
# workers for all the endpoints concurrently. However, if the
|
61
|
+
# cache is cold we want to run sequentially, so higher priority
|
62
|
+
# endpoints can work before lower priority, avoiding duplicate
|
63
|
+
# downloads.
|
64
|
+
# We don't want crashing workers to crash the CacheManager.
|
65
|
+
# Crashes are logged so just ignore the exceptions
|
66
|
+
if warmed?
|
67
|
+
Array(workers).flatten.collect do |worker|
|
68
|
+
self.future(:process_worker, worker)
|
69
|
+
end.each do |f|
|
70
|
+
f.value rescue nil
|
71
|
+
end
|
72
|
+
else
|
73
|
+
Array(workers).flatten.each do |worker|
|
74
|
+
process_worker(worker) rescue nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
self.set_warmed
|
78
|
+
end
|
79
|
+
|
80
|
+
# @param [CacheBuilder::Worker::Base] worker
|
81
|
+
def process_worker(worker)
|
82
|
+
log.info "processing #{worker}"
|
83
|
+
remote_cookbooks = worker.cookbooks
|
84
|
+
log.info "found #{remote_cookbooks.size} cookbooks from #{worker}"
|
85
|
+
created_cookbooks, deleted_cookbooks = diff(remote_cookbooks, worker.priority)
|
86
|
+
log.debug "#{created_cookbooks.size} cookbooks to be added to the cache from #{worker}"
|
87
|
+
log.debug "#{deleted_cookbooks.size} cookbooks to be removed from the cache from #{worker}"
|
88
|
+
|
89
|
+
# Process metadata in chunks - Ridley cookbook resource uses a
|
90
|
+
# task_class TaskThread, which means each future gets its own
|
91
|
+
# thread. If we have many (>2000) cookbooks we can easily
|
92
|
+
# exhaust the available threads on the system.
|
93
|
+
created_cookbooks_with_metadata = []
|
94
|
+
until created_cookbooks.empty?
|
95
|
+
work = created_cookbooks.slice!(0,500)
|
96
|
+
log.info "processing metadata for #{work.size} cookbooks with #{created_cookbooks.size} remaining on #{worker}"
|
97
|
+
work.map! do |remote|
|
98
|
+
[ remote, worker.future(:metadata, remote) ]
|
99
|
+
end.map! do |remote, metadata|
|
100
|
+
[remote, metadata.value]
|
101
|
+
end
|
102
|
+
created_cookbooks_with_metadata += work
|
103
|
+
end
|
104
|
+
|
105
|
+
log.info "about to merge cookbooks"
|
106
|
+
merge(created_cookbooks_with_metadata, deleted_cookbooks)
|
107
|
+
log.info "#{self} cache updated."
|
41
108
|
end
|
42
109
|
|
43
110
|
# Check if the cache knows about the given cookbook version
|
@@ -51,44 +118,53 @@ module Berkshelf::API
|
|
51
118
|
end
|
52
119
|
|
53
120
|
def load_save
|
121
|
+
log.info "Loading save from #{self.class.cache_file}"
|
54
122
|
@cache = DependencyCache.from_file(self.class.cache_file)
|
123
|
+
log.info "Cache contains #{@cache.cookbooks.size} items"
|
55
124
|
end
|
56
125
|
|
57
|
-
|
58
|
-
#
|
59
|
-
# @param [#to_s] name
|
60
|
-
# @param [#to_s] version
|
61
|
-
#
|
62
|
-
# @return [DependencyCache]
|
63
|
-
def remove(name, version)
|
64
|
-
@cache.remove(name, version)
|
65
|
-
end
|
126
|
+
private
|
66
127
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
128
|
+
def merge(created_cookbooks, deleted_cookbooks)
|
129
|
+
log.info "#{self} adding (#{created_cookbooks.length}) items..."
|
130
|
+
created_cookbooks.each do |remote_with_metadata|
|
131
|
+
remote, metadata = remote_with_metadata
|
132
|
+
add(remote, metadata)
|
133
|
+
end
|
72
134
|
|
73
|
-
|
74
|
-
|
75
|
-
#
|
76
|
-
# @return [Array<Array<RemoteCookbook>, Array<RemoteCookbook>>]
|
77
|
-
# A tuple of Arrays of RemoteCookbooks
|
78
|
-
# The first array contains items not in the cache
|
79
|
-
# The second array contains items in the cache, but not in the cookbooks parameter
|
80
|
-
def diff(cookbooks)
|
81
|
-
known_cookbooks = cache.cookbooks
|
82
|
-
created_cookbooks = cookbooks - known_cookbooks
|
83
|
-
deleted_cookbooks = known_cookbooks - cookbooks
|
84
|
-
[ created_cookbooks, deleted_cookbooks ]
|
85
|
-
end
|
135
|
+
log.info "#{self} removing (#{deleted_cookbooks.length}) items..."
|
136
|
+
deleted_cookbooks.each { |remote| remove(remote.name, remote.version) }
|
86
137
|
|
87
|
-
|
138
|
+
log.info "#{self} cache updated."
|
139
|
+
save
|
140
|
+
end
|
141
|
+
|
142
|
+
def save
|
143
|
+
if warmed?
|
144
|
+
log.info "Saving the cache to: #{self.class.cache_file}"
|
145
|
+
cache.save(self.class.cache_file)
|
146
|
+
log.info "Cache saved!"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
# @param [Array<RemoteCookbook>] cookbooks
|
151
|
+
# An array of RemoteCookbooks representing all the cookbooks on the indexed site
|
152
|
+
# @param [Integer] worker_priority
|
153
|
+
# The priority/ID of the endpoint that is running
|
154
|
+
# @return [Array(Array<RemoteCookbook>, Array<RemoteCookbook>)]
|
155
|
+
# A tuple of Arrays of RemoteCookbooks
|
156
|
+
# The first array contains items not in the cache
|
157
|
+
# The second array contains items in the cache, but not in the cookbooks parameter
|
158
|
+
def diff(cookbooks, worker_priority)
|
159
|
+
known_cookbooks = cache.cookbooks.select { |c| c.priority <= worker_priority }
|
160
|
+
created_cookbooks = cookbooks - known_cookbooks
|
161
|
+
deleted_cookbooks = (known_cookbooks - cookbooks).select { |c| c.priority == worker_priority }
|
162
|
+
[ created_cookbooks, deleted_cookbooks ]
|
163
|
+
end
|
88
164
|
|
89
165
|
def finalize_callback
|
90
166
|
log.info "Cache Manager shutting down..."
|
91
|
-
|
167
|
+
save
|
92
168
|
end
|
93
169
|
end
|
94
170
|
end
|
data/lib/berkshelf/api/config.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'buff/config/json'
|
2
|
+
require 'digest/sha1'
|
2
3
|
|
3
4
|
module Berkshelf::API
|
4
5
|
class Config < Buff::Config::JSON
|
@@ -24,5 +25,9 @@ module Berkshelf::API
|
|
24
25
|
}
|
25
26
|
}
|
26
27
|
]
|
28
|
+
|
29
|
+
def endpoints_checksum
|
30
|
+
Digest::SHA1.hexdigest(endpoints.collect {|x| x.to_hash }.to_s)
|
31
|
+
end
|
27
32
|
end
|
28
33
|
end
|
@@ -6,6 +6,7 @@ module Berkshelf::API
|
|
6
6
|
# {
|
7
7
|
# "cookbook_name" => {
|
8
8
|
# "x.y.z" => {
|
9
|
+
# :endpoint_priority => 1,
|
9
10
|
# :dependencies => { "cookbook_name" => "constraint" },
|
10
11
|
# :platforms => { "platform" => "constraint" }
|
11
12
|
# }
|
@@ -32,12 +33,19 @@ module Berkshelf::API
|
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
36
|
+
include Berkshelf::API::Logging
|
35
37
|
extend Forwardable
|
36
38
|
def_delegators :@cache, :[], :[]=
|
37
39
|
|
38
40
|
# @param [Hash] contents
|
39
41
|
def initialize(contents = {})
|
40
|
-
@
|
42
|
+
@warmed = false
|
43
|
+
@cache = Hash[contents].with_indifferent_access
|
44
|
+
if @cache['endpoints_checksum'] && (@cache['endpoints_checksum'] != Application.config.endpoints_checksum)
|
45
|
+
log.warn "Endpoints in config have changed - invalidating cache"
|
46
|
+
@cache.clear
|
47
|
+
end
|
48
|
+
@cache.delete('endpoints_checksum')
|
41
49
|
end
|
42
50
|
|
43
51
|
# @param [RemoteCookbook] cookbook
|
@@ -49,6 +57,7 @@ module Berkshelf::API
|
|
49
57
|
dependencies = metadata.dependencies || Hash.new
|
50
58
|
@cache[cookbook.name.to_s] ||= Hash.new
|
51
59
|
@cache[cookbook.name.to_s][cookbook.version.to_s] = {
|
60
|
+
endpoint_priority: cookbook.priority,
|
52
61
|
platforms: platforms,
|
53
62
|
dependencies: dependencies,
|
54
63
|
location_type: cookbook.location_type,
|
@@ -99,9 +108,13 @@ module Berkshelf::API
|
|
99
108
|
@cache.to_hash
|
100
109
|
end
|
101
110
|
|
111
|
+
# @param [Hash] options
|
112
|
+
#
|
102
113
|
# @return [String]
|
103
114
|
def to_json(options = {})
|
104
|
-
|
115
|
+
output = to_hash
|
116
|
+
output['endpoints_checksum'] = Application.config.endpoints_checksum if options[:saving]
|
117
|
+
JSON.generate(output, options)
|
105
118
|
end
|
106
119
|
|
107
120
|
# @return [Array<RemoteCookbook>]
|
@@ -109,7 +122,7 @@ module Berkshelf::API
|
|
109
122
|
[].tap do |remote_cookbooks|
|
110
123
|
@cache.each_pair do |name, versions|
|
111
124
|
versions.each do |version, metadata|
|
112
|
-
remote_cookbooks << RemoteCookbook.new(name, version, metadata[:location_type], metadata[:location_path])
|
125
|
+
remote_cookbooks << RemoteCookbook.new(name, version, metadata[:location_type], metadata[:location_path], metadata[:endpoint_priority])
|
113
126
|
end
|
114
127
|
end
|
115
128
|
end
|
@@ -117,7 +130,15 @@ module Berkshelf::API
|
|
117
130
|
|
118
131
|
def save(path)
|
119
132
|
FileUtils.mkdir_p(File.dirname(path))
|
120
|
-
File.open(path, 'w+') { |f| f.write(self.to_json) }
|
133
|
+
File.open(path, 'w+') { |f| f.write(self.to_json(saving: true)) }
|
134
|
+
end
|
135
|
+
|
136
|
+
def warmed?
|
137
|
+
@warmed
|
138
|
+
end
|
139
|
+
|
140
|
+
def set_warmed
|
141
|
+
@warmed = true
|
121
142
|
end
|
122
143
|
end
|
123
144
|
end
|
@@ -1,3 +1,11 @@
|
|
1
1
|
module Berkshelf::API
|
2
|
-
class RemoteCookbook < Struct.new(:name, :version, :location_type, :location_path)
|
2
|
+
class RemoteCookbook < Struct.new(:name, :version, :location_type, :location_path, :priority)
|
3
|
+
def hash
|
4
|
+
"#{name}|#{version}".hash
|
5
|
+
end
|
6
|
+
|
7
|
+
def eql?(other)
|
8
|
+
self.hash == other.hash
|
9
|
+
end
|
10
|
+
end
|
3
11
|
end
|
@@ -13,50 +13,144 @@ module Berkshelf::API
|
|
13
13
|
workers: 10
|
14
14
|
}.freeze
|
15
15
|
|
16
|
+
INITIAL_BODY = ''
|
17
|
+
|
18
|
+
CONTENT_TYPE_ORIG = 'Content-Type'.freeze
|
19
|
+
CONTENT_LENGTH_ORIG = 'Content-Length'.freeze
|
20
|
+
CONTENT_TYPE = 'CONTENT_TYPE'.freeze
|
21
|
+
CONTENT_LENGTH = 'CONTENT_LENGTH'.freeze
|
22
|
+
|
23
|
+
SERVER_SOFTWARE = 'SERVER_SOFTWARE'.freeze
|
24
|
+
SERVER_NAME = 'SERVER_NAME'.freeze
|
25
|
+
SERVER_PORT = 'SERVER_PORT'.freeze
|
26
|
+
SERVER_PROTOCOL = 'SERVER_PROTOCOL'.freeze
|
27
|
+
GATEWAY_INTERFACE = "GATEWAY_INTERFACE".freeze
|
28
|
+
LOCALHOST = 'localhost'.freeze
|
29
|
+
HTTP_VERSION = 'HTTP_VERSION'.freeze
|
30
|
+
CGI_1_1 = 'CGI/1.1'.freeze
|
31
|
+
REMOTE_ADDR = 'REMOTE_ADDR'.freeze
|
32
|
+
CONNECTION = 'HTTP_CONNECTION'.freeze
|
33
|
+
SCRIPT_NAME = 'SCRIPT_NAME'.freeze
|
34
|
+
PATH_INFO = 'PATH_INFO'.freeze
|
35
|
+
REQUEST_METHOD = 'REQUEST_METHOD'.freeze
|
36
|
+
QUERY_STRING = 'QUERY_STRING'.freeze
|
37
|
+
HTTP_1_0 = 'HTTP/1.0'.freeze
|
38
|
+
HTTP_1_1 = 'HTTP/1.1'.freeze
|
39
|
+
HTTP_ = 'HTTP_'.freeze
|
40
|
+
HOST = 'Host'.freeze
|
41
|
+
|
42
|
+
RACK_INPUT = 'rack.input'.freeze
|
43
|
+
RACK_LOGGER = 'rack.logger'.freeze
|
44
|
+
RACK_VERSION = 'rack.version'.freeze
|
45
|
+
RACK_ERRORS = 'rack.errors'.freeze
|
46
|
+
RACK_MULTITHREAD = 'rack.multithread'.freeze
|
47
|
+
RACK_MULTIPROCESS = 'rack.multiprocess'.freeze
|
48
|
+
RACK_RUN_ONCE = 'rack.run_once'.freeze
|
49
|
+
RACK_URL_SCHEME = 'rack.url_scheme'.freeze
|
50
|
+
RACK_WEBSOCKET = 'rack.websocket'.freeze
|
51
|
+
|
52
|
+
PROTO_RACK_ENV = {
|
53
|
+
RACK_VERSION => ::Rack::VERSION,
|
54
|
+
RACK_ERRORS => STDERR,
|
55
|
+
RACK_MULTITHREAD => true,
|
56
|
+
RACK_MULTIPROCESS => false,
|
57
|
+
RACK_RUN_ONCE => false,
|
58
|
+
RACK_URL_SCHEME => "http".freeze,
|
59
|
+
SCRIPT_NAME => ENV[SCRIPT_NAME] || "",
|
60
|
+
SERVER_PROTOCOL => HTTP_1_1,
|
61
|
+
SERVER_SOFTWARE => "berkshelf-api/#{Berkshelf::API::VERSION}".freeze,
|
62
|
+
GATEWAY_INTERFACE => CGI_1_1
|
63
|
+
}.freeze
|
64
|
+
|
16
65
|
# @return [String]
|
17
66
|
attr_reader :host
|
67
|
+
|
18
68
|
# @return [Integer]
|
19
69
|
attr_reader :port
|
70
|
+
|
20
71
|
# @return [Integer]
|
21
72
|
attr_reader :workers
|
22
73
|
|
23
|
-
|
74
|
+
# @return [Berkshelf::API::RackApp]
|
75
|
+
attr_reader :app
|
24
76
|
|
25
77
|
server_name :rest_gateway
|
26
|
-
finalizer :finalize_callback
|
27
78
|
|
28
79
|
# @option options [String] :host ('0.0.0.0')
|
29
80
|
# @option options [Integer] :port (26200)
|
30
81
|
# @option options [Boolean] :quiet (false)
|
31
82
|
# @option options [Integer] :workers (10)
|
32
83
|
def initialize(options = {})
|
33
|
-
options
|
34
|
-
options[:
|
35
|
-
|
36
|
-
@host = options[:host]
|
37
|
-
@port = options[:port]
|
38
|
-
@workers = options[:workers]
|
39
|
-
@handler = ::Rack::Handler::Reel.new(options)
|
40
|
-
@pool = ::Reel::RackWorker.pool(size: @workers, args: [ @handler ])
|
84
|
+
options = DEFAULT_OPTIONS.merge(options)
|
85
|
+
@host = options[:host]
|
86
|
+
@port = options[:port]
|
41
87
|
|
42
88
|
log.info "REST Gateway listening on #{@host}:#{@port}"
|
43
89
|
super(@host, @port, &method(:on_connect))
|
90
|
+
@app = Berkshelf::API::RackApp.new
|
44
91
|
end
|
45
92
|
|
46
|
-
# @param [Reel::Connection] connection
|
47
93
|
def on_connect(connection)
|
48
|
-
|
94
|
+
while request = connection.request
|
95
|
+
case request
|
96
|
+
when request.websocket?
|
97
|
+
request.respond(:bad_request, "WebSockets not supported")
|
98
|
+
else
|
99
|
+
route_request(connection, request)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def route_request(connection, request)
|
105
|
+
status, headers, body_parts = app.call(request_env(request, connection))
|
106
|
+
body, is_stream = response_body(body_parts)
|
107
|
+
|
108
|
+
response_klass = is_stream ? Reel::StreamResponse : Reel::Response
|
109
|
+
response = response_klass.new(status, headers, body)
|
110
|
+
connection.respond(response)
|
111
|
+
end
|
112
|
+
|
113
|
+
def request_env(request, connection)
|
114
|
+
env = env(request)
|
115
|
+
env[REMOTE_ADDR] = connection.remote_ip
|
116
|
+
env
|
49
117
|
end
|
50
118
|
|
51
119
|
private
|
52
120
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
121
|
+
def env(request)
|
122
|
+
env = Hash[PROTO_RACK_ENV]
|
123
|
+
|
124
|
+
env[RACK_INPUT] = StringIO.new(request.body.to_s || INITIAL_BODY)
|
125
|
+
env[RACK_INPUT].set_encoding(Encoding::BINARY) if env[RACK_INPUT].respond_to?(:set_encoding)
|
126
|
+
env[SERVER_NAME], env[SERVER_PORT] = (request[HOST]||'').split(':', 2)
|
127
|
+
env[SERVER_PORT] ||= port.to_s
|
128
|
+
env[HTTP_VERSION] = request.version || env[SERVER_PROTOCOL]
|
129
|
+
env[REQUEST_METHOD] = request.method
|
130
|
+
env[PATH_INFO] = request.path
|
131
|
+
env[QUERY_STRING] = request.query_string || ''
|
132
|
+
|
133
|
+
(_ = request.headers.delete CONTENT_TYPE_ORIG) && (env[CONTENT_TYPE] = _)
|
134
|
+
(_ = request.headers.delete CONTENT_LENGTH_ORIG) && (env[CONTENT_LENGTH] = _)
|
135
|
+
|
136
|
+
request.headers.each_pair do |key, val|
|
137
|
+
env[HTTP_ + key.gsub('-', '_').upcase] = val
|
138
|
+
end
|
139
|
+
env
|
140
|
+
end
|
57
141
|
|
58
|
-
def
|
59
|
-
|
142
|
+
def response_body(body_parts)
|
143
|
+
if body_parts.respond_to?(:to_path)
|
144
|
+
::File.new(body_parts.to_path)
|
145
|
+
else
|
146
|
+
body = ''
|
147
|
+
body_parts.each do |c|
|
148
|
+
return [c, true] if c.is_a?(Reel::Stream)
|
149
|
+
body << c
|
150
|
+
end
|
151
|
+
body_parts.close if body_parts.respond_to?(:close)
|
152
|
+
body
|
153
|
+
end
|
60
154
|
end
|
61
155
|
end
|
62
156
|
end
|
@@ -18,7 +18,10 @@ module Berkshelf::API::RSpec
|
|
18
18
|
def start(options = {})
|
19
19
|
options = options.reverse_merge(port: 26210, log_location: "/dev/null", endpoints: [])
|
20
20
|
Berkshelf::API::Application.config.endpoints = options[:endpoints]
|
21
|
-
|
21
|
+
unless running?
|
22
|
+
Berkshelf::API::Application.run!(options)
|
23
|
+
cache_builder.build
|
24
|
+
end
|
22
25
|
end
|
23
26
|
|
24
27
|
def stop
|
@@ -92,7 +92,7 @@ module Berkshelf::API
|
|
92
92
|
#
|
93
93
|
# @return [String, nil]
|
94
94
|
def download(name, version, destination = Dir.mktmpdir)
|
95
|
-
log.
|
95
|
+
log.debug "downloading #{name}(#{version})"
|
96
96
|
if uri = download_uri(name, version)
|
97
97
|
archive = stream(uri)
|
98
98
|
Archive.extract(archive.path, destination)
|
data/lib/berkshelf/api.rb
CHANGED
@@ -35,25 +35,4 @@ describe Berkshelf::API::CacheBuilder::Worker::ChefServer do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
end
|
38
|
-
|
39
|
-
describe "#build" do
|
40
|
-
before do
|
41
|
-
Berkshelf::API::CacheManager.start
|
42
|
-
chef_cookbook("ruby", "1.0.0")
|
43
|
-
chef_cookbook("ruby", "2.0.0")
|
44
|
-
chef_cookbook("elixir", "3.0.0")
|
45
|
-
chef_cookbook("elixir", "3.0.1")
|
46
|
-
end
|
47
|
-
|
48
|
-
let(:cache) { Berkshelf::API::CacheManager.instance.cache }
|
49
|
-
|
50
|
-
it "adds each item to the cache" do
|
51
|
-
subject.build
|
52
|
-
expect(cache).to have_cookbook("ruby", "1.0.0")
|
53
|
-
expect(cache).to have_cookbook("ruby", "2.0.0")
|
54
|
-
expect(cache).to have_cookbook("elixir", "3.0.0")
|
55
|
-
expect(cache).to have_cookbook("elixir", "3.0.1")
|
56
|
-
expect(cache.cookbooks).to have(4).items
|
57
|
-
end
|
58
|
-
end
|
59
38
|
end
|
@@ -37,44 +37,4 @@ describe Berkshelf::API::CacheBuilder::Worker::Base do
|
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
|
-
|
41
|
-
let(:cache_manager) { double(:diff => :chicken) }
|
42
|
-
subject { described_class.new }
|
43
|
-
|
44
|
-
describe "#diff" do
|
45
|
-
it "should delegate to the cache_manager to calculate the diff" do
|
46
|
-
subject.should_receive(:cache_manager).and_return(cache_manager)
|
47
|
-
subject.should_receive(:cookbooks).and_return(:cookbooks)
|
48
|
-
|
49
|
-
expect(subject.diff).to eql(:chicken)
|
50
|
-
end
|
51
|
-
|
52
|
-
it "should memoize the diff to prevent recalculating" do
|
53
|
-
subject.should_receive(:cache_manager).exactly(1).times.and_return(cache_manager)
|
54
|
-
subject.should_receive(:cookbooks).and_return(:cookbooks)
|
55
|
-
|
56
|
-
subject.diff
|
57
|
-
subject.diff
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe "#clear_diff" do
|
62
|
-
it "should set the diff to nil" do
|
63
|
-
subject.should_receive(:cache_manager).and_return(cache_manager)
|
64
|
-
subject.should_receive(:cookbooks).and_return(:cookbooks)
|
65
|
-
|
66
|
-
subject.diff
|
67
|
-
expect(subject.instance_variable_get(:@diff)).to eql(:chicken)
|
68
|
-
subject.send(:clear_diff)
|
69
|
-
expect(subject.instance_variable_get(:@diff)).to eql(nil)
|
70
|
-
end
|
71
|
-
|
72
|
-
it "memoizes the diff to prevent recalculating" do
|
73
|
-
subject.should_receive(:cache_manager).exactly(1).times.and_return(cache_manager)
|
74
|
-
subject.should_receive(:cookbooks).and_return(:cookbooks)
|
75
|
-
|
76
|
-
subject.diff
|
77
|
-
subject.diff
|
78
|
-
end
|
79
|
-
end
|
80
40
|
end
|
@@ -1,17 +1,21 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Berkshelf::API::CacheBuilder do
|
4
|
+
|
5
|
+
before { Berkshelf::API::CacheManager.start }
|
4
6
|
let(:instance) { described_class.new }
|
5
7
|
|
6
8
|
describe "#build" do
|
7
9
|
subject(:build) { instance.build }
|
8
10
|
let(:workers) { [ double('worker') ] }
|
9
11
|
let(:future) { double('future', value: nil) }
|
12
|
+
let(:cache_manager) { double('cache_manager') }
|
10
13
|
|
11
14
|
before { instance.stub(workers: workers) }
|
12
15
|
|
13
|
-
it "
|
14
|
-
|
16
|
+
it "asks the cache_manager to process all of its actors" do
|
17
|
+
instance.stub(:cache_manager).and_return(cache_manager)
|
18
|
+
cache_manager.should_receive(:process_workers).with(instance.workers).and_return(future)
|
15
19
|
build
|
16
20
|
end
|
17
21
|
end
|
@@ -26,12 +30,51 @@ describe Berkshelf::API::CacheBuilder do
|
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
29
|
-
|
30
|
-
|
33
|
+
context "when no workers are explicitly configured" do
|
34
|
+
it "has one worker started by default" do
|
35
|
+
expect(workers).to have(1).item
|
36
|
+
end
|
37
|
+
|
38
|
+
it "has an opscode worker started by default" do
|
39
|
+
expect(workers.first).to be_a(described_class::Worker::Opscode)
|
40
|
+
end
|
31
41
|
end
|
32
42
|
|
33
|
-
|
34
|
-
|
43
|
+
context "when there are multiple workers" do
|
44
|
+
let(:endpoint_array) { [ first_worker, second_worker ] }
|
45
|
+
let(:first_worker) { double(options: endpoint_options.dup.merge(priority: 0), type: 'chicken') }
|
46
|
+
let(:second_worker) { double(options: endpoint_options.dup.merge(priority: 1), type: 'tuna') }
|
47
|
+
let(:endpoint_options) do
|
48
|
+
{
|
49
|
+
"url" => "www.fake.com",
|
50
|
+
"client_name" => "fake",
|
51
|
+
"client_key" => "/path/to/fake.key"
|
52
|
+
}
|
53
|
+
end
|
54
|
+
let(:dummy_endpoint_klass) do
|
55
|
+
Class.new do
|
56
|
+
attr_reader :options
|
57
|
+
include Celluloid
|
58
|
+
|
59
|
+
def initialize(options = {})
|
60
|
+
@options = options
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
before do
|
66
|
+
Berkshelf::API::Application.config.stub(:endpoints).and_return(endpoint_array)
|
67
|
+
Berkshelf::API::CacheBuilder::Worker.stub(:[]).and_return(dummy_endpoint_klass, dummy_endpoint_klass)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "has two workers" do
|
71
|
+
expect(workers).to have(2).items
|
72
|
+
end
|
73
|
+
|
74
|
+
it "keeps the ordering" do
|
75
|
+
expect(workers.first.options[:priority]).to be(0)
|
76
|
+
expect(workers.last.options[:priority]).to be(1)
|
77
|
+
end
|
35
78
|
end
|
36
79
|
end
|
37
80
|
end
|
@@ -67,23 +67,48 @@ describe Berkshelf::API::CacheManager do
|
|
67
67
|
end
|
68
68
|
|
69
69
|
describe "#diff" do
|
70
|
-
let(:cookbook_one) { Berkshelf::API::RemoteCookbook.new("ruby", "1.2.3", "opscode") }
|
71
|
-
let(:cookbook_two) { Berkshelf::API::RemoteCookbook.new("elixir", "2.0.0", "opscode") }
|
70
|
+
let(:cookbook_one) { Berkshelf::API::RemoteCookbook.new("ruby", "1.2.3", "opscode", nil, 1) }
|
71
|
+
let(:cookbook_two) { Berkshelf::API::RemoteCookbook.new("elixir", "2.0.0", "opscode", nil, 1) }
|
72
72
|
let(:comparison) { Array.new }
|
73
73
|
|
74
74
|
before do
|
75
|
-
subject.add
|
76
|
-
subject.add
|
75
|
+
subject.send(:add, cookbook_one, double(dependencies: nil, platforms: nil))
|
76
|
+
subject.send(:add, cookbook_two, double(dependencies: nil, platforms: nil))
|
77
77
|
|
78
|
-
@created, @deleted = @diff = subject.diff
|
78
|
+
@created, @deleted = @diff = subject.send(:diff, comparison, 1)
|
79
79
|
end
|
80
80
|
|
81
81
|
it "returns two items" do
|
82
82
|
expect(@diff).to have(2).items
|
83
83
|
end
|
84
84
|
|
85
|
+
context "when there are more than one worker endpoints" do
|
86
|
+
let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode", nil, 2) }
|
87
|
+
let(:comparison) { [ cookbook_one, cookbook_two, new_cookbook ] }
|
88
|
+
|
89
|
+
before do
|
90
|
+
@created, @deleted = @diff = subject.send(:diff, comparison, 2)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "only creates cookbooks that have the same or lower priority" do
|
94
|
+
expect(@created).to eql([new_cookbook])
|
95
|
+
end
|
96
|
+
|
97
|
+
context "when the cookbook has been deleted" do
|
98
|
+
let(:comparison) { [cookbook_one] }
|
99
|
+
|
100
|
+
before do
|
101
|
+
@created, @deleted = @diff = subject.send(:diff, comparison, 1)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "only deletes cookbooks at the same priority" do
|
105
|
+
expect(@deleted).to eql([cookbook_two])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
85
110
|
context "when there are created and deleted cookbooks" do
|
86
|
-
let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode") }
|
111
|
+
let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode", nil, 1) }
|
87
112
|
let(:comparison) { [ cookbook_one, new_cookbook ] }
|
88
113
|
|
89
114
|
it "should return created and deleted cookbooks" do
|
@@ -93,7 +118,7 @@ describe Berkshelf::API::CacheManager do
|
|
93
118
|
end
|
94
119
|
|
95
120
|
context "when there are only created cookbooks" do
|
96
|
-
let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode") }
|
121
|
+
let(:new_cookbook) { Berkshelf::API::RemoteCookbook.new("ruby", "3.0.0", "opscode", nil, 1) }
|
97
122
|
let(:comparison) { [ cookbook_one, cookbook_two, new_cookbook ] }
|
98
123
|
|
99
124
|
it "should return only created cookbooks" do
|
@@ -105,6 +130,10 @@ describe Berkshelf::API::CacheManager do
|
|
105
130
|
context "when there are only deleted cookbooks" do
|
106
131
|
let(:comparison) { [ cookbook_one ] }
|
107
132
|
|
133
|
+
before do
|
134
|
+
@created, @deleted = @diff = subject.send(:diff, comparison, 1)
|
135
|
+
end
|
136
|
+
|
108
137
|
it "should return only deleted cookbooks" do
|
109
138
|
expect(@created).to be_empty
|
110
139
|
expect(@deleted).to eql([cookbook_two])
|
@@ -67,6 +67,13 @@ describe Berkshelf::API::DependencyCache do
|
|
67
67
|
|
68
68
|
subject { described_class.new(contents) }
|
69
69
|
|
70
|
+
context "when a new DependencyCache is created" do
|
71
|
+
it "should allow indifferent access to items in the cache" do
|
72
|
+
expect(subject[:chicken]).to be_a(Hash)
|
73
|
+
expect(subject[:chicken][:'1.0'][:dependencies]).to be_a(Hash)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
70
77
|
describe "#cookbooks" do
|
71
78
|
it "should return a list of RemoteCookbooks" do
|
72
79
|
expected_value = [
|
@@ -8,11 +8,24 @@ describe Berkshelf::API::Endpoint::V1 do
|
|
8
8
|
let(:app) { described_class.new }
|
9
9
|
|
10
10
|
describe "GET /universe" do
|
11
|
-
|
12
|
-
|
13
|
-
let(:app_cache) { cache_manager.cache }
|
11
|
+
context "the cache has been warmed" do
|
12
|
+
before { cache_manager.set_warmed; get '/universe' }
|
14
13
|
|
15
|
-
|
16
|
-
|
14
|
+
subject { last_response }
|
15
|
+
let(:app_cache) { cache_manager.cache }
|
16
|
+
|
17
|
+
its(:status) { should be(200) }
|
18
|
+
its(:body) { should eq(app_cache.to_json) }
|
19
|
+
end
|
20
|
+
|
21
|
+
context "the cache is still warming" do
|
22
|
+
before { get '/universe' }
|
23
|
+
|
24
|
+
subject { last_response }
|
25
|
+
let(:app_cache) { cache_manager.cache }
|
26
|
+
|
27
|
+
its(:status) { should be(503) }
|
28
|
+
its(:headers) { should have_key("Retry-After") }
|
29
|
+
end
|
17
30
|
end
|
18
31
|
end
|
@@ -9,18 +9,12 @@ describe Berkshelf::API::RESTGateway do
|
|
9
9
|
|
10
10
|
its(:host) { should eql(described_class::DEFAULT_OPTIONS[:host]) }
|
11
11
|
its(:port) { should eql(described_class::DEFAULT_OPTIONS[:port]) }
|
12
|
-
its(:
|
13
|
-
its(:rack_app) { should be_a(Berkshelf::API::RackApp) }
|
12
|
+
its(:app) { should be_a(Berkshelf::API::RackApp) }
|
14
13
|
|
15
14
|
context "given a different port" do
|
16
15
|
before { options[:port] = 26210 }
|
17
16
|
its(:port) { should eql(26210) }
|
18
17
|
end
|
19
|
-
|
20
|
-
context "given a different amount of workers" do
|
21
|
-
before { options[:workers] = 20 }
|
22
|
-
its(:workers) { should eql(20) }
|
23
|
-
end
|
24
18
|
end
|
25
19
|
end
|
26
20
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: berkshelf-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamie Winsor
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-10-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: ridley
|
@@ -17,42 +17,42 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - ~>
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: 1.
|
20
|
+
version: '1.6'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - ~>
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: 1.
|
27
|
+
version: '1.6'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: celluloid
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
32
|
- - ~>
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 0.
|
34
|
+
version: '0.15'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - ~>
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 0.
|
41
|
+
version: '0.15'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: reel
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- - '
|
46
|
+
- - '>='
|
47
47
|
- !ruby/object:Gem::Version
|
48
|
-
version: 0.4.0
|
48
|
+
version: 0.4.0
|
49
49
|
type: :runtime
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- - '
|
53
|
+
- - '>='
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 0.4.0
|
55
|
+
version: 0.4.0
|
56
56
|
- !ruby/object:Gem::Dependency
|
57
57
|
name: grape
|
58
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -191,6 +191,7 @@ files:
|
|
191
191
|
- .gitignore
|
192
192
|
- .ruby-version
|
193
193
|
- .travis.yml
|
194
|
+
- CHANGELOG.md
|
194
195
|
- CONTRIBUTING.md
|
195
196
|
- Gemfile
|
196
197
|
- Guardfile
|