berkshelf-api 0.1.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 +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/CONTRIBUTING.md +33 -0
- data/Gemfile +40 -0
- data/Guardfile +20 -0
- data/LICENSE +201 -0
- data/README.md +37 -0
- data/Thorfile +39 -0
- data/berkshelf-api.gemspec +35 -0
- data/bin/berks-api +5 -0
- data/lib/berkshelf-api.rb +1 -0
- data/lib/berkshelf/api.rb +25 -0
- data/lib/berkshelf/api/application.rb +114 -0
- data/lib/berkshelf/api/cache_builder.rb +60 -0
- data/lib/berkshelf/api/cache_builder/worker.rb +116 -0
- data/lib/berkshelf/api/cache_builder/worker/chef_server.rb +46 -0
- data/lib/berkshelf/api/cache_builder/worker/opscode.rb +59 -0
- data/lib/berkshelf/api/cache_manager.rb +96 -0
- data/lib/berkshelf/api/config.rb +23 -0
- data/lib/berkshelf/api/cucumber.rb +11 -0
- data/lib/berkshelf/api/dependency_cache.rb +123 -0
- data/lib/berkshelf/api/endpoint.rb +17 -0
- data/lib/berkshelf/api/endpoint/v1.rb +19 -0
- data/lib/berkshelf/api/errors.rb +8 -0
- data/lib/berkshelf/api/generic_server.rb +50 -0
- data/lib/berkshelf/api/logging.rb +37 -0
- data/lib/berkshelf/api/mixin.rb +7 -0
- data/lib/berkshelf/api/mixin/services.rb +48 -0
- data/lib/berkshelf/api/rack_app.rb +5 -0
- data/lib/berkshelf/api/remote_cookbook.rb +3 -0
- data/lib/berkshelf/api/rest_gateway.rb +62 -0
- data/lib/berkshelf/api/rspec.rb +20 -0
- data/lib/berkshelf/api/rspec/server.rb +29 -0
- data/lib/berkshelf/api/site_connector.rb +7 -0
- data/lib/berkshelf/api/site_connector/opscode.rb +162 -0
- data/lib/berkshelf/api/srv_ctl.rb +63 -0
- data/lib/berkshelf/api/version.rb +5 -0
- data/spec/fixtures/reset.pem +27 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/actor_mocking.rb +7 -0
- data/spec/support/chef_server.rb +73 -0
- data/spec/unit/berkshelf/api/application_spec.rb +24 -0
- data/spec/unit/berkshelf/api/cache_builder/worker/chef_server_spec.rb +59 -0
- data/spec/unit/berkshelf/api/cache_builder/worker/opscode_spec.rb +41 -0
- data/spec/unit/berkshelf/api/cache_builder/worker_spec.rb +80 -0
- data/spec/unit/berkshelf/api/cache_builder_spec.rb +37 -0
- data/spec/unit/berkshelf/api/cache_manager_spec.rb +123 -0
- data/spec/unit/berkshelf/api/config_spec.rb +24 -0
- data/spec/unit/berkshelf/api/dependency_cache_spec.rb +109 -0
- data/spec/unit/berkshelf/api/endpoint/v1_spec.rb +18 -0
- data/spec/unit/berkshelf/api/logging_spec.rb +28 -0
- data/spec/unit/berkshelf/api/mixin/services_spec.rb +68 -0
- data/spec/unit/berkshelf/api/rack_app_spec.rb +6 -0
- data/spec/unit/berkshelf/api/rest_gateway_spec.rb +26 -0
- data/spec/unit/berkshelf/api/site_connector/opscode_spec.rb +85 -0
- data/spec/unit/berkshelf/api/srv_ctl_spec.rb +56 -0
- metadata +293 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
module Berkshelf::API::RSpec
|
2
|
+
module Server
|
3
|
+
class << self
|
4
|
+
include Berkshelf::API::Mixin::Services
|
5
|
+
|
6
|
+
def clear_cache
|
7
|
+
cache_manager.clear
|
8
|
+
end
|
9
|
+
|
10
|
+
def instance
|
11
|
+
Berkshelf::API::Application.instance
|
12
|
+
end
|
13
|
+
|
14
|
+
def running?
|
15
|
+
Berkshelf::API::Application.running?
|
16
|
+
end
|
17
|
+
|
18
|
+
def start(options = {})
|
19
|
+
options = options.reverse_merge(port: 26210, log_location: "/dev/null", endpoints: [])
|
20
|
+
Berkshelf::API::Application.config.endpoints = options[:endpoints]
|
21
|
+
Berkshelf::API::Application.run!(options) unless running?
|
22
|
+
end
|
23
|
+
|
24
|
+
def stop
|
25
|
+
Berkshelf::API::Application.shutdown
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,162 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'retryable'
|
3
|
+
require 'archive'
|
4
|
+
require 'tempfile'
|
5
|
+
|
6
|
+
module Berkshelf::API
|
7
|
+
module SiteConnector
|
8
|
+
class Opscode
|
9
|
+
class << self
|
10
|
+
# @param [String] version
|
11
|
+
#
|
12
|
+
# @return [String]
|
13
|
+
def uri_escape_version(version)
|
14
|
+
version.to_s.gsub('.', '_')
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param [String] uri
|
18
|
+
#
|
19
|
+
# @return [String]
|
20
|
+
def version_from_uri(uri)
|
21
|
+
File.basename(uri.to_s).gsub('_', '.')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
include Celluloid
|
26
|
+
include Berkshelf::API::Logging
|
27
|
+
|
28
|
+
V1_API = 'http://cookbooks.opscode.com/api/v1'.freeze
|
29
|
+
|
30
|
+
# @return [String]
|
31
|
+
attr_reader :api_uri
|
32
|
+
# @return [Integer]
|
33
|
+
# how many retries to attempt on HTTP requests
|
34
|
+
attr_reader :retries
|
35
|
+
# @return [Float]
|
36
|
+
# time to wait between retries
|
37
|
+
attr_reader :retry_interval
|
38
|
+
|
39
|
+
# @param [Faraday::Connection] connection
|
40
|
+
# Optional parameter for setting the connection object
|
41
|
+
# This should only be set manually for testing
|
42
|
+
def initialize(uri = V1_API, options = {})
|
43
|
+
options = { retries: 5, retry_interval: 0.5 }.merge(options)
|
44
|
+
@api_uri = uri
|
45
|
+
|
46
|
+
@connection = Faraday.new(uri) do |c|
|
47
|
+
c.response :parse_json
|
48
|
+
c.use Faraday::Adapter::NetHttp
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [Array<String>]
|
53
|
+
# A list of cookbook names available on the server
|
54
|
+
def cookbooks
|
55
|
+
start = 0
|
56
|
+
count = connection.get("cookbooks").body["total"]
|
57
|
+
cookbooks = Array.new
|
58
|
+
|
59
|
+
while count > 0
|
60
|
+
cookbooks += connection.get("cookbooks?start=#{start}&items=#{count}").body["items"]
|
61
|
+
start += 100
|
62
|
+
count -= 100
|
63
|
+
end
|
64
|
+
|
65
|
+
cookbooks.map { |cb| cb["cookbook_name"] }
|
66
|
+
end
|
67
|
+
|
68
|
+
# @param [String] cookbook
|
69
|
+
# the name of the cookbook to find version for
|
70
|
+
#
|
71
|
+
# @return [Array<String>]
|
72
|
+
# A list of versions of this cookbook available on the server
|
73
|
+
def versions(cookbook)
|
74
|
+
response = connection.get("cookbooks/#{cookbook}")
|
75
|
+
|
76
|
+
case response.status
|
77
|
+
when (200..299)
|
78
|
+
response.body['versions'].collect do |version_uri|
|
79
|
+
self.class.version_from_uri(version_uri)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
Array.new
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# @param [String] cookbook
|
87
|
+
# The name of the cookbook to download
|
88
|
+
# @param [String] version
|
89
|
+
# The version of the cookbook to download
|
90
|
+
# @param [String] destination
|
91
|
+
# The directory to download the cookbook to
|
92
|
+
#
|
93
|
+
# @return [String, nil]
|
94
|
+
def download(name, version, destination = Dir.mktmpdir)
|
95
|
+
log.info "downloading #{name}(#{version})"
|
96
|
+
if uri = download_uri(name, version)
|
97
|
+
archive = stream(uri)
|
98
|
+
Archive.extract(archive.path, destination)
|
99
|
+
end
|
100
|
+
ensure
|
101
|
+
archive.unlink unless archive.nil?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Return the location where a cookbook of the given name and version can be downloaded from
|
105
|
+
#
|
106
|
+
# @param [String] cookbook
|
107
|
+
# The name of the cookbook
|
108
|
+
# @param [String] version
|
109
|
+
# The version of the cookbook
|
110
|
+
#
|
111
|
+
# @return [String, nil]
|
112
|
+
def download_uri(name, version)
|
113
|
+
unless cookbook = find(name, version)
|
114
|
+
return nil
|
115
|
+
end
|
116
|
+
cookbook[:file]
|
117
|
+
end
|
118
|
+
|
119
|
+
# @param [String] cookbook
|
120
|
+
# The name of the cookbook to retrieve
|
121
|
+
# @param [String] version
|
122
|
+
# The version of the cookbook to retrieve
|
123
|
+
#
|
124
|
+
# @return [Hashie::Mash, nil]
|
125
|
+
def find(name, version)
|
126
|
+
response = connection.get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")
|
127
|
+
|
128
|
+
case response.status
|
129
|
+
when (200..299)
|
130
|
+
response.body
|
131
|
+
else
|
132
|
+
nil
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Stream the response body of a remote URL to a file on the local file system
|
137
|
+
#
|
138
|
+
# @param [String] target
|
139
|
+
# a URL to stream the response body from
|
140
|
+
#
|
141
|
+
# @return [Tempfile]
|
142
|
+
def stream(target)
|
143
|
+
local = Tempfile.new('opscode-site-stream')
|
144
|
+
local.binmode
|
145
|
+
|
146
|
+
retryable(tries: retries, on: OpenURI::HTTPError, sleep: retry_interval) do
|
147
|
+
open(target, 'rb', connection.headers) do |remote|
|
148
|
+
local.write(remote.read)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
local
|
153
|
+
ensure
|
154
|
+
local.close(false) unless local.nil?
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
attr_reader :connection
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'buff/extensions'
|
3
|
+
|
4
|
+
module Berkshelf
|
5
|
+
module API
|
6
|
+
class SrvCtl
|
7
|
+
class << self
|
8
|
+
# @param [Array] args
|
9
|
+
#
|
10
|
+
# @return [Hash]
|
11
|
+
def parse_options(args, filename)
|
12
|
+
options = Hash.new
|
13
|
+
|
14
|
+
OptionParser.new("Usage: #{filename} [options]") do |opts|
|
15
|
+
opts.on("-p", "--port PORT", Integer, "set the listening port") do |v|
|
16
|
+
options[:port] = v
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-v", "--verbose", "run with verbose output") do
|
20
|
+
options[:log_level] = "INFO"
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-d", "--debug", "run with debug output") do
|
24
|
+
options[:log_level] = "DEBUG"
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-q", "--quiet", "silence output") do
|
28
|
+
options[:log_location] = '/dev/null'
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on_tail("-h", "--help", "show this message") do
|
32
|
+
puts opts
|
33
|
+
exit
|
34
|
+
end
|
35
|
+
end.parse!(args)
|
36
|
+
|
37
|
+
options.symbolize_keys
|
38
|
+
end
|
39
|
+
|
40
|
+
# @param [Array] args
|
41
|
+
# @param [String] filename
|
42
|
+
def run(args, filename)
|
43
|
+
options = parse_options(args, filename)
|
44
|
+
new(options).start
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
attr_reader :options
|
49
|
+
|
50
|
+
# @param [Hash] options
|
51
|
+
# @see {Berkshelf::API::Application.run} for the list of valid options
|
52
|
+
def initialize(options = {})
|
53
|
+
@options = options
|
54
|
+
@options[:eager_build] = true
|
55
|
+
end
|
56
|
+
|
57
|
+
def start
|
58
|
+
require 'berkshelf/api'
|
59
|
+
Berkshelf::API::Application.run(options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpQIBAAKCAQEAyyUMqrTh1IzKOyE0fvXEWC7m0AdMI8/dr9JJMUKtK9vhhP0w
|
3
|
+
rm6m95GoybFM2IRryukFsAxpcir3M1ungTU3Smq4MshhMJ7H9FbvZVfQoknTbCsR
|
4
|
+
w6scg2fBepxT2+fcGRufr8nAh92M3uUkN9bMMTAkt18D4br6035YvdmvHDJERxYq
|
5
|
+
ByA/720AdI9VNSIvw+x8oqsIkXLEdF6dgT9MpG5iWZT66pbFsnNZpRrd4/bFNWBY
|
6
|
+
+13aOqdmjiTL08/EdgQFKMT5qimpos1TuQhA7mwInOjQgzVu9uCDkMiYejaLbUz0
|
7
|
+
lGyS8y4uxu6z2hA900Jg/z+JJuXymH5QAX3GZQIDAQABAoIBAQCtFXkwbYPI1Nht
|
8
|
+
/wG6du5+8B9K+hy+mppY9wPTy+q+Zs9Ev3Fd/fuXDm1QxBckl9c8AMUO1dR2KPOM
|
9
|
+
t7gFl/DvH/SnmCFvCqp1nijFIUgrLlnMXPn6zG0z7RBlxpKQ2IGohufNIEpBuNwR
|
10
|
+
Ag2U4hgChPGTp4ooJ2cVEh7MS5AupYPDbC62dWEdW68aRTWhh2BCGAWBb6s16yl9
|
11
|
+
aZ7+OcxW2eeRJVbRfLkLQEDutJZi5TfOEn5QPc86ZgxcCmnvwulnpnhpz6QCkgQt
|
12
|
+
OP/+KRqDhWSDVCFREVT30fUIj1EWvK7NFWASZQxueZStuIvMEKeFebYfrbHxRFzJ
|
13
|
+
UmaxJnWVAoGBAPbKLpeky6ClccBaHHrCgjzakoDfGgyNKDQ9g753lJxB8nn7d9X4
|
14
|
+
HQpkWpfqAGFRZp1hI2H+VxyUXLh2Ob5OUeTm0OZJll35vycOaQEtfgIScXTcvzn0
|
15
|
+
16J9eX2YY4wIHEEMh85nKk8BEGgiNP5nuEviHocCeYXoi/Zq3+qj6v63AoGBANK5
|
16
|
+
4nyi6LBQFs1CUc7Sh7vjtOE3ia7KeRmOr7gS6QhS3iK3Oa8FzBLJ6ETjN2a9Bw8N
|
17
|
+
cF7I/+cr4s7DUJjxdb53D/J6TVSYORNNCUVnpF/uB2LqqdXDYmpO0PvFkXFoYTnJ
|
18
|
+
kaLAN8uCoLKr6JH9tq3DfXIfDIHiZ+BOIvI070fDAoGBAMDyzEDFmGruTyRLj66u
|
19
|
+
+rJnVVmqlKwxhLhrS+CTj74nlVOnt0a0KMhiM65IRqnPwcHUG5zXBPaUTHXwAS93
|
20
|
+
/nFPwQ37hLPOupPnoVNJZRZrowbyPBQtCJbDMURv64ylHqoBCQDoCd0hANnZvMMX
|
21
|
+
BrFVhfaaibaXXS542r6SD/27AoGAECadHE5kJTdOOBcwK/jo3Fa8g1J9Y/8yvum3
|
22
|
+
wBT69V9clS6T5j08geglvDnqAh7UzquKBEnFi1NKw+wmXkKLcrivaTdEfApavYb3
|
23
|
+
AfHKoGue907jC3Y5Mcquq81ds2J7qTEwz1eKLzfo1yjj32ShvrmwALIuhDn1GjUC
|
24
|
+
6qtx938CgYEApEqvu0nocR1jmVVlLe5uKQBj949dh6NGq0R5Lztz6xufaTYzMC3d
|
25
|
+
AZG9XPPjRqSLs+ylSXJpwHEwoeyLFDaJcO+GgW1/ut4MC2HppOx6aImwDdXMHUWR
|
26
|
+
KYGIFF4AU/IYoBcanAm4s078EH/Oz01B2c7tR2TqabisPgLYe7PXSCw=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
require 'rspec'
|
4
|
+
require 'spork'
|
5
|
+
require 'rack/test'
|
6
|
+
|
7
|
+
Spork.prefork do
|
8
|
+
Dir[File.join(File.expand_path("../../spec/support/**/*.rb", __FILE__))].each { |f| require f }
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.include Berkshelf::RSpec::ChefServer
|
12
|
+
|
13
|
+
config.expect_with :rspec do |c|
|
14
|
+
c.syntax = :expect
|
15
|
+
end
|
16
|
+
|
17
|
+
config.mock_with :rspec
|
18
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
19
|
+
config.filter_run focus: true
|
20
|
+
config.run_all_when_everything_filtered = true
|
21
|
+
|
22
|
+
config.before(:suite) { Berkshelf::RSpec::ChefServer.start }
|
23
|
+
config.before(:all) { Berkshelf::API::Logging.init(location: '/dev/null') }
|
24
|
+
|
25
|
+
config.before do
|
26
|
+
Celluloid.shutdown
|
27
|
+
Celluloid.boot
|
28
|
+
Berkshelf::API::CacheManager.cache_file = tmp_path.join('cerch').to_s
|
29
|
+
clean_tmp_path
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def app_root_path
|
34
|
+
Pathname.new(File.expand_path('../../', __FILE__))
|
35
|
+
end
|
36
|
+
|
37
|
+
def tmp_path
|
38
|
+
app_root_path.join('spec/tmp')
|
39
|
+
end
|
40
|
+
|
41
|
+
def fixtures_path
|
42
|
+
app_root_path.join('spec/fixtures')
|
43
|
+
end
|
44
|
+
|
45
|
+
def clean_tmp_path
|
46
|
+
FileUtils.rm_rf(tmp_path)
|
47
|
+
FileUtils.mkdir_p(tmp_path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Spork.each_run do
|
52
|
+
require 'berkshelf/api'
|
53
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'chef_zero/server'
|
2
|
+
|
3
|
+
module Berkshelf::RSpec
|
4
|
+
module ChefServer
|
5
|
+
class << self
|
6
|
+
def clear_request_log
|
7
|
+
@request_log = Array.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def request_log
|
11
|
+
@request_log ||= Array.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def server
|
15
|
+
@server ||= ChefZero::Server.new(port: PORT, generate_real_keys: false)
|
16
|
+
end
|
17
|
+
|
18
|
+
def server_url
|
19
|
+
(@server && @server.url) || "http://localhost/#{PORT}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def start
|
23
|
+
server.start_background
|
24
|
+
server.on_response do |request, response|
|
25
|
+
request_log << [ request, response ]
|
26
|
+
end
|
27
|
+
clear_request_log
|
28
|
+
|
29
|
+
server
|
30
|
+
end
|
31
|
+
|
32
|
+
def stop
|
33
|
+
@server.stop if @server
|
34
|
+
end
|
35
|
+
|
36
|
+
def running?
|
37
|
+
@server && @server.running?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
PORT = 8889
|
42
|
+
|
43
|
+
def chef_client(name, hash = Hash.new)
|
44
|
+
load_data(:clients, name, hash)
|
45
|
+
end
|
46
|
+
|
47
|
+
def chef_cookbook(name, version, cookbook = Hash.new)
|
48
|
+
ChefServer.server.load_data("cookbooks" => { "#{name}-#{version}" => cookbook })
|
49
|
+
end
|
50
|
+
|
51
|
+
def chef_data_bag(name, hash = Hash.new)
|
52
|
+
ChefServer.server.load_data({ 'data' => { name => hash }})
|
53
|
+
end
|
54
|
+
|
55
|
+
def chef_environment(name, hash = Hash.new)
|
56
|
+
load_data(:environments, name, hash)
|
57
|
+
end
|
58
|
+
|
59
|
+
def chef_node(name, hash = Hash.new)
|
60
|
+
load_data(:nodes, name, hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
def chef_role(name, hash = Hash.new)
|
64
|
+
load_data(:roles, name, hash)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def load_data(key, name, hash)
|
70
|
+
ChefServer.server.load_data(key.to_s => { name => JSON.fast_generate(hash) })
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Berkshelf::API::Application do
|
4
|
+
describe "ClassMethods" do
|
5
|
+
subject { described_class }
|
6
|
+
|
7
|
+
its(:registry) { should be_a(Celluloid::Registry) }
|
8
|
+
|
9
|
+
describe "::run!" do
|
10
|
+
include Berkshelf::API::Mixin::Services
|
11
|
+
|
12
|
+
let(:options) { { log_location: '/dev/null' } }
|
13
|
+
subject(:run) { described_class.run!(options) }
|
14
|
+
|
15
|
+
context "when given true for :disable_http" do
|
16
|
+
it "does not start the REST Gateway" do
|
17
|
+
options[:disable_http] = true
|
18
|
+
run
|
19
|
+
expect { rest_gateway }.to raise_error(Berkshelf::API::NotStartedError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|