berkshelf 6.0.1 → 6.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 +4 -4
- data/.travis.yml +8 -3
- data/Gemfile +1 -28
- data/Gemfile.lock +27 -94
- data/Rakefile +10 -0
- data/appveyor.yml +2 -2
- data/berkshelf.gemspec +2 -1
- data/features/commands/install.feature +21 -0
- data/features/step_definitions/berksfile_steps.rb +1 -1
- data/features/step_definitions/environment_steps.rb +1 -1
- data/features/step_definitions/json_steps.rb +2 -2
- data/features/support/env.rb +0 -2
- data/lib/berkshelf/berksfile.rb +1 -1
- data/lib/berkshelf/chef_repo_universe.rb +45 -0
- data/lib/berkshelf/cli.rb +1 -2
- data/lib/berkshelf/community_rest.rb +6 -37
- data/lib/berkshelf/config.rb +3 -0
- data/lib/berkshelf/downloader.rb +2 -2
- data/lib/berkshelf/formatters/human.rb +3 -1
- data/lib/berkshelf/installer.rb +14 -6
- data/lib/berkshelf/locations/git.rb +0 -2
- data/lib/berkshelf/mixin/git.rb +4 -3
- data/lib/berkshelf/shell_out.rb +17 -0
- data/lib/berkshelf/source.rb +30 -17
- data/lib/berkshelf/source_uri.rb +1 -1
- data/lib/berkshelf/ssl_policies.rb +1 -1
- data/lib/berkshelf/streaming_file_adapter.rb +22 -0
- data/lib/berkshelf/version.rb +1 -1
- data/lib/berkshelf/visualizer.rb +3 -3
- data/spec/fixtures/complex-cookbook-path/cookbooks/app/metadata.rb +2 -0
- data/spec/fixtures/complex-cookbook-path/cookbooks/jenkins-config/metadata.rb +4 -0
- data/spec/fixtures/complex-cookbook-path/cookbooks/jenkins/metadata.rb +2 -0
- data/spec/support/git.rb +18 -17
- data/spec/support/kitchen.rb +0 -14
- data/spec/unit/berkshelf/chef_repo_universe_spec.rb +37 -0
- data/spec/unit/berkshelf/config_spec.rb +46 -0
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +0 -8
- data/spec/unit/berkshelf/downloader_spec.rb +12 -9
- data/spec/unit/berkshelf/init_generator_spec.rb +0 -1
- data/spec/unit/berkshelf/locations/git_spec.rb +2 -2
- data/spec/unit/berkshelf/resolver/graph_spec.rb +3 -2
- data/spec/unit/berkshelf/source_spec.rb +55 -44
- data/spec/unit/berkshelf/ssl_policies_spec.rb +3 -2
- data/spec/unit/berkshelf/uploader_spec.rb +1 -0
- data/spec/unit/berkshelf/visualizer_spec.rb +1 -1
- metadata +30 -7
- data/Guardfile +0 -18
- data/lib/berkshelf/commands/test_command.rb +0 -13
@@ -7,5 +7,5 @@ Given /^the environment variable (.+) is "(.+)"$/ do |variable, value|
|
|
7
7
|
end
|
8
8
|
|
9
9
|
Given /^the environment variable (.+) is \$TEST_BERKSHELF_ARTIFACTORY_API_KEY$/ do |variable|
|
10
|
-
set_environment_variable(variable, ENV[
|
10
|
+
set_environment_variable(variable, ENV["TEST_BERKSHELF_ARTIFACTORY_API_KEY"])
|
11
11
|
end
|
@@ -16,8 +16,8 @@ end
|
|
16
16
|
|
17
17
|
# Pending Ridley allowing newer Faraday and Celluloid.
|
18
18
|
def clean_json_output(output)
|
19
|
-
output.gsub(/^.+warning: constant ::Fixnum is deprecated$/,
|
20
|
-
.gsub(/^.*forwarding to private method Celluloid::PoolManager#url_prefix$/,
|
19
|
+
output.gsub(/^.+warning: constant ::Fixnum is deprecated$/, "") \
|
20
|
+
.gsub(/^.*forwarding to private method Celluloid::PoolManager#url_prefix$/, "")
|
21
21
|
end
|
22
22
|
|
23
23
|
Then /^the output should contain JSON:$/ do |data|
|
data/features/support/env.rb
CHANGED
@@ -12,7 +12,6 @@ require "berkshelf/api/cucumber" unless windows?
|
|
12
12
|
Dir["spec/support/**/*.rb"].each { |f| require File.expand_path(f) }
|
13
13
|
|
14
14
|
World(Berkshelf::RSpec::PathHelpers)
|
15
|
-
World(Berkshelf::RSpec::Kitchen)
|
16
15
|
|
17
16
|
CHEF_SERVER_PORT = 26310
|
18
17
|
BERKS_API_PORT = 26210
|
@@ -34,7 +33,6 @@ Before do
|
|
34
33
|
aruba.config.main_class = Berkshelf::Cli::Runner
|
35
34
|
@dirs = ["spec/tmp/aruba"] # set aruba's temporary directory
|
36
35
|
|
37
|
-
stub_kitchen!
|
38
36
|
clean_tmp_path
|
39
37
|
Berkshelf.initialize_filesystem
|
40
38
|
Berkshelf::CookbookStore.instance.initialize_filesystem
|
data/lib/berkshelf/berksfile.rb
CHANGED
@@ -0,0 +1,45 @@
|
|
1
|
+
require "berkshelf/api_client/remote_cookbook"
|
2
|
+
require "ridley/chef/cookbook"
|
3
|
+
|
4
|
+
module Berkshelf
|
5
|
+
# Shim to look like a Berkshelf::APIClient but for a chef repo folder.
|
6
|
+
#
|
7
|
+
# @since 6.1
|
8
|
+
class ChefRepoUniverse
|
9
|
+
def initialize(uri, **options)
|
10
|
+
@uri = uri
|
11
|
+
@path = options[:path]
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def universe
|
16
|
+
Dir.entries(cookbooks_path).sort.each_with_object([]) do |entry, cookbooks|
|
17
|
+
next if entry[0] == "." # Skip hidden folders.
|
18
|
+
entry_path = "#{cookbooks_path}/#{entry}"
|
19
|
+
next unless File.directory?(entry_path) # Skip non-dirs.
|
20
|
+
cookbook = begin
|
21
|
+
Ridley::Chef::Cookbook.from_path(entry_path)
|
22
|
+
rescue IOError
|
23
|
+
next # It wasn't a cookbook.
|
24
|
+
end
|
25
|
+
cookbooks << Berkshelf::APIClient::RemoteCookbook.new(
|
26
|
+
cookbook.cookbook_name,
|
27
|
+
cookbook.version,
|
28
|
+
location_type: "file_store",
|
29
|
+
location_path: entry_path,
|
30
|
+
dependencies: cookbook.metadata.dependencies
|
31
|
+
)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def cookbooks_path
|
38
|
+
if File.exist?("#{@path}/cookbooks")
|
39
|
+
"#{@path}/cookbooks"
|
40
|
+
else
|
41
|
+
@path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/berkshelf/cli.rb
CHANGED
@@ -3,7 +3,6 @@ require_relative "config"
|
|
3
3
|
require_relative "init_generator"
|
4
4
|
require_relative "cookbook_generator"
|
5
5
|
require_relative "commands/shelf"
|
6
|
-
require_relative "commands/test_command"
|
7
6
|
|
8
7
|
module Berkshelf
|
9
8
|
class Cli < Thor
|
@@ -262,7 +261,7 @@ module Berkshelf
|
|
262
261
|
banner: "URL"
|
263
262
|
desc "search NAME", "Search the remote source for cookbooks matching the partial name"
|
264
263
|
def search(name)
|
265
|
-
source = Source.new(options[:source])
|
264
|
+
source = Source.new(nil, options[:source])
|
266
265
|
cookbooks = source.search(name)
|
267
266
|
Berkshelf.formatter.search(cookbooks)
|
268
267
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require "
|
1
|
+
require "berkshelf/streaming_file_adapter"
|
2
2
|
require "retryable"
|
3
3
|
require "mixlib/archive"
|
4
4
|
|
@@ -80,7 +80,7 @@ module Berkshelf
|
|
80
80
|
interval: @retry_interval,
|
81
81
|
exceptions: [Faraday::Error::TimeoutError]
|
82
82
|
|
83
|
-
b.
|
83
|
+
b.use Berkshelf::StreamingFileAdapter
|
84
84
|
end
|
85
85
|
|
86
86
|
super(api_uri, options)
|
@@ -116,7 +116,7 @@ module Berkshelf
|
|
116
116
|
response = get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")
|
117
117
|
|
118
118
|
# Artifactory responds with a 200 and blank body for unknown cookbooks.
|
119
|
-
if response.status == 200 && response.body.to_s ==
|
119
|
+
if response.status == 200 && response.body.to_s == ""
|
120
120
|
response.env.status = 404
|
121
121
|
end
|
122
122
|
|
@@ -137,7 +137,7 @@ module Berkshelf
|
|
137
137
|
response = get("cookbooks/#{name}")
|
138
138
|
|
139
139
|
# Artifactory responds with a 200 and blank body for unknown cookbooks.
|
140
|
-
if response.status == 200 && response.body.to_s ==
|
140
|
+
if response.status == 200 && response.body.to_s == ""
|
141
141
|
response.env.status = 404
|
142
142
|
end
|
143
143
|
|
@@ -189,44 +189,13 @@ module Berkshelf
|
|
189
189
|
local = Tempfile.new("community-rest-stream")
|
190
190
|
local.binmode
|
191
191
|
|
192
|
-
Retryable.retryable(tries: retries, on:
|
193
|
-
|
194
|
-
local.write(remote.read)
|
195
|
-
end
|
192
|
+
Retryable.retryable(tries: retries, on: Faraday::Error::ConnectionFailed, sleep: retry_interval) do
|
193
|
+
get(target, nil, streaming_file: local)
|
196
194
|
end
|
197
195
|
|
198
196
|
local
|
199
197
|
ensure
|
200
198
|
local.close(false) unless local.nil?
|
201
199
|
end
|
202
|
-
|
203
|
-
private
|
204
|
-
|
205
|
-
def open_uri_options(target)
|
206
|
-
options = {}
|
207
|
-
options.merge!(headers)
|
208
|
-
options.merge!(open_uri_proxy_options(target))
|
209
|
-
options.merge!(ssl_verify_mode: ssl_verify_mode)
|
210
|
-
end
|
211
|
-
|
212
|
-
def open_uri_proxy_options(target)
|
213
|
-
proxy_uri = URI.parse(target).find_proxy()
|
214
|
-
return {} if proxy_uri.nil?
|
215
|
-
|
216
|
-
proxy = Faraday::ProxyOptions.from(proxy_uri)
|
217
|
-
if proxy && proxy[:user] && proxy[:password]
|
218
|
-
{ proxy_http_basic_authentication: [ proxy[:uri], proxy[:user], proxy[:password] ] }
|
219
|
-
else
|
220
|
-
{ proxy: proxy[:uri] }
|
221
|
-
end
|
222
|
-
end
|
223
|
-
|
224
|
-
def ssl_verify_mode
|
225
|
-
if Berkshelf::Config.instance.ssl.verify.nil? || Berkshelf::Config.instance.ssl.verify
|
226
|
-
OpenSSL::SSL::VERIFY_PEER
|
227
|
-
else
|
228
|
-
OpenSSL::SSL::VERIFY_NONE
|
229
|
-
end
|
230
|
-
end
|
231
200
|
end
|
232
201
|
end
|
data/lib/berkshelf/config.rb
CHANGED
@@ -110,6 +110,9 @@ module Berkshelf
|
|
110
110
|
attribute "chef.trusted_certs_dir",
|
111
111
|
type: String,
|
112
112
|
default: Berkshelf.chef_config.trusted_certs_dir
|
113
|
+
attribute "chef.artifactory_api_key",
|
114
|
+
type: String,
|
115
|
+
default: Berkshelf.chef_config.artifactory_api_key
|
113
116
|
attribute "cookbook.copyright",
|
114
117
|
type: String,
|
115
118
|
default: Berkshelf.chef_config.cookbook_copyright
|
data/lib/berkshelf/downloader.rb
CHANGED
@@ -63,9 +63,9 @@ module Berkshelf
|
|
63
63
|
|
64
64
|
case remote_cookbook.location_type
|
65
65
|
when :opscode, :supermarket
|
66
|
-
options = {}
|
66
|
+
options = { ssl: source.options[:ssl] }
|
67
67
|
if source.type == :artifactory
|
68
|
-
options[:headers] = {
|
68
|
+
options[:headers] = { "X-Jfrog-Art-Api" => source.options[:api_key] }
|
69
69
|
end
|
70
70
|
CommunityREST.new(remote_cookbook.location_path, options).download(name, version)
|
71
71
|
when :chef_server
|
@@ -19,7 +19,9 @@ module Berkshelf
|
|
19
19
|
def install(source, cookbook)
|
20
20
|
message = "Installing #{cookbook.name} (#{cookbook.version})"
|
21
21
|
|
22
|
-
|
22
|
+
if source.type == :chef_repo
|
23
|
+
message << " from #{cookbook.location_path}"
|
24
|
+
elsif !source.default?
|
23
25
|
message << " from #{source}"
|
24
26
|
message << " ([#{cookbook.location_type}] #{cookbook.location_path})"
|
25
27
|
end
|
data/lib/berkshelf/installer.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
require "berkshelf/api-client"
|
2
|
+
require "concurrent/executors"
|
3
|
+
require "concurrent/future"
|
2
4
|
|
3
5
|
module Berkshelf
|
4
6
|
class Installer
|
@@ -10,14 +12,15 @@ module Berkshelf
|
|
10
12
|
def initialize(berksfile)
|
11
13
|
@berksfile = berksfile
|
12
14
|
@lockfile = berksfile.lockfile
|
13
|
-
@
|
15
|
+
@pool = Concurrent::FixedThreadPool.new([Concurrent.processor_count - 1, 2].max)
|
16
|
+
@worker = Worker.new(berksfile)
|
14
17
|
end
|
15
18
|
|
16
19
|
def build_universe
|
17
20
|
berksfile.sources.collect do |source|
|
18
21
|
Thread.new do
|
19
22
|
begin
|
20
|
-
Berkshelf.formatter.msg("Fetching cookbook index from #{source
|
23
|
+
Berkshelf.formatter.msg("Fetching cookbook index from #{source}...")
|
21
24
|
source.build_universe
|
22
25
|
rescue Berkshelf::APIClientError => ex
|
23
26
|
Berkshelf.formatter.warn "Error retrieving universe from source: #{source}"
|
@@ -61,10 +64,9 @@ module Berkshelf
|
|
61
64
|
private
|
62
65
|
|
63
66
|
attr_reader :worker
|
67
|
+
attr_reader :pool
|
64
68
|
|
65
69
|
class Worker
|
66
|
-
include Celluloid
|
67
|
-
|
68
70
|
attr_reader :berksfile
|
69
71
|
attr_reader :downloader
|
70
72
|
|
@@ -132,7 +134,10 @@ module Berkshelf
|
|
132
134
|
build_universe
|
133
135
|
end
|
134
136
|
|
135
|
-
|
137
|
+
futures = dependencies.sort.map { |dependency| Concurrent::Future.execute(executor: pool) { worker.install(dependency) } }
|
138
|
+
cookbooks = futures.map(&:value)
|
139
|
+
rejects = futures.select(&:rejected?)
|
140
|
+
raise rejects.first.reason unless rejects.empty?
|
136
141
|
|
137
142
|
[dependencies, cookbooks]
|
138
143
|
end
|
@@ -173,7 +178,10 @@ module Berkshelf
|
|
173
178
|
|
174
179
|
Berkshelf.log.debug " Starting resolution..."
|
175
180
|
|
176
|
-
|
181
|
+
futures = resolver.resolve.sort.map { |dependency| Concurrent::Future.execute(executor: pool) { worker.install(dependency) } }
|
182
|
+
cookbooks = futures.map(&:value)
|
183
|
+
rejects = futures.select(&:rejected?)
|
184
|
+
raise rejects.first.reason unless rejects.empty?
|
177
185
|
|
178
186
|
[dependencies, cookbooks]
|
179
187
|
end
|
data/lib/berkshelf/mixin/git.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
require "
|
1
|
+
require "berkshelf/shell_out"
|
2
2
|
|
3
3
|
module Berkshelf
|
4
4
|
module Mixin
|
5
5
|
module Git
|
6
|
+
include Berkshelf::ShellOut
|
6
7
|
# Perform a git command.
|
7
8
|
#
|
8
9
|
# @param [String] command
|
@@ -17,9 +18,9 @@ module Berkshelf
|
|
17
18
|
raise GitNotInstalled.new
|
18
19
|
end
|
19
20
|
|
20
|
-
response =
|
21
|
+
response = shell_out(%{git #{command}})
|
21
22
|
|
22
|
-
if
|
23
|
+
if response.error?
|
23
24
|
raise GitCommandError.new(command, cache_path, response.stderr)
|
24
25
|
end
|
25
26
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "mixlib/shellout"
|
2
|
+
|
3
|
+
module Berkshelf
|
4
|
+
module ShellOut
|
5
|
+
def shell_out(*args, **options)
|
6
|
+
cmd = Mixlib::ShellOut.new(*args, **options)
|
7
|
+
cmd.run_command
|
8
|
+
cmd
|
9
|
+
end
|
10
|
+
|
11
|
+
def shell_out!(*args)
|
12
|
+
cmd = shell_out(*args)
|
13
|
+
cmd.error!
|
14
|
+
cmd
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/berkshelf/source.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "berkshelf/api-client"
|
2
|
+
require "berkshelf/chef_repo_universe"
|
2
3
|
require "berkshelf/ssl_policies"
|
3
4
|
require "openssl"
|
4
5
|
|
@@ -10,9 +11,10 @@ module Berkshelf
|
|
10
11
|
attr_accessor :uri_string
|
11
12
|
attr_accessor :options
|
12
13
|
|
14
|
+
# @param [Berkshelf::Berksfile] berksfile
|
13
15
|
# @param [String, Berkshelf::SourceURI] source
|
14
|
-
def initialize(source, **options)
|
15
|
-
@options = {timeout: api_timeout, open_timeout: [(api_timeout / 10), 3].max, ssl: {}}
|
16
|
+
def initialize(berksfile, source, **options)
|
17
|
+
@options = { timeout: api_timeout, open_timeout: [(api_timeout / 10), 3].max, ssl: {} }
|
16
18
|
@options.update(options)
|
17
19
|
case source
|
18
20
|
when String
|
@@ -35,7 +37,13 @@ module Berkshelf
|
|
35
37
|
@options[:client_name] ||= Berkshelf::Config.instance.chef.node_name
|
36
38
|
@options[:client_key] ||= Berkshelf::Config.instance.chef.client_key
|
37
39
|
when :artifactory
|
38
|
-
@options[:api_key] ||= Berkshelf::Config.instance.chef.artifactory_api_key || ENV[
|
40
|
+
@options[:api_key] ||= Berkshelf::Config.instance.chef.artifactory_api_key || ENV["ARTIFACTORY_API_KEY"]
|
41
|
+
when :chef_repo
|
42
|
+
@options[:path] = uri_string
|
43
|
+
# If given a relative path, expand it against the Berksfile's folder.
|
44
|
+
@options[:path] = File.expand_path(@options[:path], File.dirname(berksfile ? berksfile.filepath : Dir.pwd))
|
45
|
+
# Lie because this won't actually parse as a URI.
|
46
|
+
@uri_string = "file://#{@options[:path]}"
|
39
47
|
end
|
40
48
|
# Set some default SSL options.
|
41
49
|
Berkshelf::Config.instance.ssl.each do |key, value|
|
@@ -50,17 +58,19 @@ module Berkshelf
|
|
50
58
|
end
|
51
59
|
|
52
60
|
def api_client
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
@api_client ||= case type
|
62
|
+
when :chef_server
|
63
|
+
APIClient.chef_server(server_url: uri.to_s, **options)
|
64
|
+
when :artifactory
|
65
|
+
# Don't accidentally mutate the options.
|
66
|
+
client_options = options.dup
|
67
|
+
api_key = client_options.delete(:api_key)
|
68
|
+
APIClient.new(uri, headers: { "X-Jfrog-Art-Api" => api_key }, **client_options)
|
69
|
+
when :chef_repo
|
70
|
+
ChefRepoUniverse.new(uri_string, **options)
|
71
|
+
else
|
72
|
+
APIClient.new(uri, **options)
|
73
|
+
end
|
64
74
|
end
|
65
75
|
|
66
76
|
def uri
|
@@ -133,15 +143,18 @@ module Berkshelf
|
|
133
143
|
end
|
134
144
|
|
135
145
|
def to_s
|
136
|
-
|
137
|
-
|
146
|
+
case type
|
147
|
+
when :supermarket
|
148
|
+
uri.to_s
|
149
|
+
when :chef_repo
|
150
|
+
options[:path]
|
138
151
|
else
|
139
152
|
"#{type}: #{uri}"
|
140
153
|
end
|
141
154
|
end
|
142
155
|
|
143
156
|
def inspect
|
144
|
-
"#<#{self.class.name} #{type}: #{uri.to_s.inspect}, #{options.map {|k, v| "#{k}: #{v.inspect}" }.join(', ')}>"
|
157
|
+
"#<#{self.class.name} #{type}: #{uri.to_s.inspect}, #{options.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')}>"
|
145
158
|
end
|
146
159
|
|
147
160
|
def hash
|
data/lib/berkshelf/source_uri.rb
CHANGED
@@ -22,7 +22,7 @@ module Berkshelf
|
|
22
22
|
end
|
23
23
|
|
24
24
|
def trusted_certs_dir
|
25
|
-
config_dir = Berkshelf.config.chef.trusted_certs_dir.to_s.tr('\\',
|
25
|
+
config_dir = Berkshelf.config.chef.trusted_certs_dir.to_s.tr('\\', "/")
|
26
26
|
if config_dir.empty? || !::File.exist?(config_dir)
|
27
27
|
File.join(ENV["HOME"], ".chef", "trusted_certs")
|
28
28
|
else
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "faraday/adapter/net_http"
|
2
|
+
|
3
|
+
module Berkshelf
|
4
|
+
class StreamingFileAdapter < Faraday::Adapter::NetHttp
|
5
|
+
def call(env)
|
6
|
+
env[:streaming_file] = env[:request_headers].delete(:streaming_file) if env[:request_headers] && env[:request_headers][:streaming_file]
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def perform_request(http, env)
|
11
|
+
if env[:streaming_file]
|
12
|
+
http.request(create_request(env)) do |response|
|
13
|
+
response.read_body do |chunk|
|
14
|
+
env[:streaming_file].write(chunk) if response.code == "200"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|