berkshelf 6.3.4 → 7.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/.gitignore +1 -0
- data/.rubocop.yml +10 -0
- data/.travis.yml +6 -8
- data/CHANGELOG.md +7 -19
- data/Gemfile +10 -0
- data/Gemfile.lock +68 -103
- data/Thorfile +2 -2
- data/berkshelf.gemspec +6 -8
- data/features/commands/info.feature +50 -8
- data/features/commands/shelf/show.feature +10 -40
- data/features/commands/upload.feature +73 -0
- data/features/commands/vendor.feature +43 -0
- data/features/json_formatter.feature +1 -1
- data/features/step_definitions/chef_server_steps.rb +2 -2
- data/features/step_definitions/filesystem_steps.rb +16 -0
- data/features/support/env.rb +11 -10
- data/lib/berkshelf.rb +15 -20
- data/lib/berkshelf/berksfile.rb +57 -47
- data/lib/berkshelf/cached_cookbook.rb +120 -19
- data/lib/berkshelf/chef_config_compat.rb +50 -0
- data/lib/berkshelf/chef_repo_universe.rb +2 -2
- data/lib/berkshelf/cli.rb +3 -42
- data/lib/berkshelf/community_rest.rb +40 -61
- data/lib/berkshelf/config.rb +92 -118
- data/lib/berkshelf/cookbook_store.rb +3 -2
- data/lib/berkshelf/core_ext/file.rb +1 -1
- data/lib/berkshelf/dependency.rb +1 -10
- data/lib/berkshelf/downloader.rb +19 -7
- data/lib/berkshelf/errors.rb +3 -0
- data/lib/berkshelf/location.rb +1 -1
- data/lib/berkshelf/locations/base.rb +1 -1
- data/lib/berkshelf/lockfile.rb +17 -13
- data/lib/berkshelf/logger.rb +62 -1
- data/lib/berkshelf/packager.rb +1 -1
- data/lib/berkshelf/resolver.rb +1 -1
- data/lib/berkshelf/ridley_compat.rb +22 -3
- data/lib/berkshelf/uploader.rb +76 -48
- data/lib/berkshelf/version.rb +1 -1
- data/spec/fixtures/cookbook-path-uploader/apt-2.3.6/metadata.rb +2 -0
- data/spec/fixtures/cookbook-path-uploader/build-essential-1.4.2/metadata.rb +2 -0
- data/spec/fixtures/cookbook-path-uploader/jenkins-2.0.3/metadata.rb +5 -0
- data/spec/fixtures/cookbook-path-uploader/jenkins-config-0.1.0/metadata.rb +4 -0
- data/spec/fixtures/cookbook-path-uploader/runit-1.5.8/metadata.rb +5 -0
- data/spec/fixtures/cookbook-path-uploader/yum-3.0.6/metadata.rb +2 -0
- data/spec/fixtures/cookbook-path-uploader/yum-epel-0.2.0/metadata.rb +3 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/support/chef_api.rb +4 -4
- data/spec/support/chef_server.rb +1 -1
- data/spec/support/matchers/file_system_matchers.rb +1 -3
- data/spec/support/path_helpers.rb +1 -1
- data/spec/unit/berkshelf/berksfile_spec.rb +3 -24
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +13 -15
- data/spec/unit/berkshelf/community_rest_spec.rb +3 -12
- data/spec/unit/berkshelf/config_spec.rb +4 -4
- data/spec/unit/berkshelf/downloader_spec.rb +6 -11
- data/spec/unit/berkshelf/lockfile_spec.rb +10 -7
- data/spec/unit/berkshelf/source_spec.rb +1 -1
- data/spec/unit/berkshelf/ssl_policies_spec.rb +2 -5
- data/spec/unit/berkshelf/uploader_spec.rb +60 -10
- data/spec/unit/berkshelf/visualizer_spec.rb +2 -2
- metadata +49 -102
- data/features/commands/cookbook.feature +0 -35
- data/features/commands/init.feature +0 -27
- data/features/config.feature +0 -111
- data/lib/berkshelf/base_generator.rb +0 -42
- data/lib/berkshelf/cookbook_generator.rb +0 -133
- data/lib/berkshelf/init_generator.rb +0 -195
- data/lib/berkshelf/streaming_file_adapter.rb +0 -22
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +0 -108
- data/spec/unit/berkshelf/init_generator_spec.rb +0 -265
@@ -1,9 +1,8 @@
|
|
1
|
-
require "berkshelf/streaming_file_adapter"
|
2
1
|
require "retryable"
|
3
2
|
require "mixlib/archive"
|
4
3
|
|
5
4
|
module Berkshelf
|
6
|
-
class CommunityREST
|
5
|
+
class CommunityREST
|
7
6
|
class << self
|
8
7
|
# @param [String] target
|
9
8
|
# file path to the tar.gz archive on disk
|
@@ -58,6 +57,8 @@ module Berkshelf
|
|
58
57
|
# @return [Float]
|
59
58
|
# time to wait between retries
|
60
59
|
attr_reader :retry_interval
|
60
|
+
# @return [Berkshelf::RidleyCompat]
|
61
|
+
attr_reader :connection
|
61
62
|
|
62
63
|
# @param [String] uri (CommunityREST::V1_API)
|
63
64
|
# location of community site to connect to
|
@@ -67,23 +68,14 @@ module Berkshelf
|
|
67
68
|
# @option options [Float] :retry_interval (0.5)
|
68
69
|
# how often we should pause between retries
|
69
70
|
def initialize(uri = V1_API, options = {})
|
70
|
-
options
|
71
|
+
options = options.dup
|
72
|
+
options = { retries: 5, retry_interval: 0.5, ssl: Berkshelf::Config.instance.ssl }.merge(options)
|
71
73
|
@api_uri = uri
|
74
|
+
options[:server_url] = uri
|
72
75
|
@retries = options.delete(:retries)
|
73
76
|
@retry_interval = options.delete(:retry_interval)
|
74
77
|
|
75
|
-
|
76
|
-
b.response :parse_json
|
77
|
-
b.response :follow_redirects
|
78
|
-
b.request :retry,
|
79
|
-
max: @retries,
|
80
|
-
interval: @retry_interval,
|
81
|
-
exceptions: [Faraday::Error::TimeoutError]
|
82
|
-
|
83
|
-
b.use Berkshelf::StreamingFileAdapter
|
84
|
-
end
|
85
|
-
|
86
|
-
super(api_uri, options)
|
78
|
+
@connection = Berkshelf::RidleyCompatJSON.new(options)
|
87
79
|
end
|
88
80
|
|
89
81
|
# Download and extract target cookbook archive to the local file system,
|
@@ -97,79 +89,69 @@ module Berkshelf
|
|
97
89
|
# @return [String, nil]
|
98
90
|
# cookbook filepath, or nil if archive does not contain a cookbook
|
99
91
|
def download(name, version)
|
100
|
-
archive = stream(find(name, version)[
|
92
|
+
archive = stream(find(name, version)["file"])
|
101
93
|
scratch = Dir.mktmpdir
|
102
94
|
extracted = self.class.unpack(archive.path, scratch)
|
103
95
|
|
104
96
|
if File.cookbook?(extracted)
|
105
97
|
extracted
|
106
98
|
else
|
107
|
-
|
108
|
-
|
109
|
-
File.cookbook?(dir)
|
110
|
-
end
|
99
|
+
Dir.glob("#{extracted}/*").find do |dir|
|
100
|
+
File.cookbook?(dir)
|
111
101
|
end
|
112
|
-
File.join(extracted, dir) if dir
|
113
102
|
end
|
114
103
|
ensure
|
115
104
|
archive.unlink unless archive.nil?
|
116
105
|
end
|
117
106
|
|
118
107
|
def find(name, version)
|
119
|
-
|
108
|
+
body = connection.get("cookbooks/#{name}/versions/#{self.class.uri_escape_version(version)}")
|
120
109
|
|
121
110
|
# Artifactory responds with a 200 and blank body for unknown cookbooks.
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
else
|
132
|
-
raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
|
133
|
-
end
|
111
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
|
112
|
+
|
113
|
+
body
|
114
|
+
rescue CookbookNotFound
|
115
|
+
raise
|
116
|
+
rescue Berkshelf::APIClient::ServiceNotFound
|
117
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
|
118
|
+
rescue
|
119
|
+
raise CommunitySiteError.new(api_uri, "'#{name}' (#{version})")
|
134
120
|
end
|
135
121
|
|
136
122
|
# Returns the latest version of the cookbook and its download link.
|
137
123
|
#
|
138
124
|
# @return [String]
|
139
125
|
def latest_version(name)
|
140
|
-
|
126
|
+
body = connection.get("cookbooks/#{name}")
|
141
127
|
|
142
128
|
# Artifactory responds with a 200 and blank body for unknown cookbooks.
|
143
|
-
|
144
|
-
response.env.status = 404
|
145
|
-
end
|
129
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
|
146
130
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
else
|
153
|
-
raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
|
154
|
-
end
|
131
|
+
self.class.version_from_uri body["latest_version"]
|
132
|
+
rescue Berkshelf::APIClient::ServiceNotFound
|
133
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
|
134
|
+
rescue
|
135
|
+
raise CommunitySiteError.new(api_uri, "the latest version of '#{name}'")
|
155
136
|
end
|
156
137
|
|
157
138
|
# @param [String] name
|
158
139
|
#
|
159
140
|
# @return [Array]
|
160
141
|
def versions(name)
|
161
|
-
|
142
|
+
body = connection.get("cookbooks/#{name}")
|
162
143
|
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
when 404
|
169
|
-
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
|
170
|
-
else
|
171
|
-
raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
|
144
|
+
# Artifactory responds with a 200 and blank body for unknown cookbooks.
|
145
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'") if body.nil?
|
146
|
+
|
147
|
+
body["versions"].collect do |version_uri|
|
148
|
+
self.class.version_from_uri(version_uri)
|
172
149
|
end
|
150
|
+
|
151
|
+
rescue Berkshelf::APIClient::ServiceNotFound
|
152
|
+
raise CookbookNotFound.new(name, nil, "at `#{api_uri}'")
|
153
|
+
rescue
|
154
|
+
raise CommunitySiteError.new(api_uri, "versions of '#{name}'")
|
173
155
|
end
|
174
156
|
|
175
157
|
# @param [String] name
|
@@ -191,12 +173,9 @@ module Berkshelf
|
|
191
173
|
def stream(target)
|
192
174
|
local = Tempfile.new("community-rest-stream")
|
193
175
|
local.binmode
|
194
|
-
|
195
|
-
|
196
|
-
get(target, nil, streaming_file: local)
|
176
|
+
Retryable.retryable(tries: retries, on: Berkshelf::APIClientError, sleep: retry_interval) do
|
177
|
+
connection.streaming_request(target, {}, local)
|
197
178
|
end
|
198
|
-
|
199
|
-
local
|
200
179
|
ensure
|
201
180
|
local.close(false) unless local.nil?
|
202
181
|
end
|
data/lib/berkshelf/config.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
-
require "
|
1
|
+
require "mixlib/config"
|
2
2
|
require "openssl"
|
3
3
|
|
4
|
+
# we need this method, but have to inject it into mixlib-config directly
|
5
|
+
# to have it available from config contexts
|
6
|
+
module Mixlib
|
7
|
+
module Config
|
8
|
+
def each(&block)
|
9
|
+
save(true).each(&block)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
4
14
|
module Berkshelf
|
5
|
-
class Config
|
15
|
+
class Config
|
6
16
|
class << self
|
7
17
|
# @return [String]
|
8
18
|
def store_location
|
@@ -16,7 +26,7 @@ module Berkshelf
|
|
16
26
|
|
17
27
|
# @return [String]
|
18
28
|
def path
|
19
|
-
path = File.
|
29
|
+
path = File.exist?(local_location) ? local_location : store_location
|
20
30
|
File.expand_path(path)
|
21
31
|
end
|
22
32
|
|
@@ -33,7 +43,7 @@ module Berkshelf
|
|
33
43
|
# @return [String, nil]
|
34
44
|
# the contents of the file
|
35
45
|
def file
|
36
|
-
File.read(path) if File.
|
46
|
+
File.read(path) if File.exist?(path)
|
37
47
|
end
|
38
48
|
|
39
49
|
# Instantiate and return or just return the currently instantiated Berkshelf
|
@@ -62,130 +72,94 @@ module Berkshelf
|
|
62
72
|
#
|
63
73
|
# @return [Config]
|
64
74
|
def coerce_ssl
|
65
|
-
ssl = @instance
|
75
|
+
ssl = @instance[:ssl]
|
66
76
|
ssl[:ca_cert] = OpenSSL::X509::Certificate.new(File.read(ssl[:ca_cert])) if ssl[:ca_cert] && ssl[:ca_cert].is_a?(String)
|
67
77
|
ssl[:client_cert] = OpenSSL::X509::Certificate.new(File.read(ssl[:client_cert])) if ssl[:client_cert] && ssl[:client_cert].is_a?(String)
|
68
78
|
ssl[:client_key] = OpenSSL::PKey::RSA.new(File.read(ssl[:client_key])) if ssl[:client_key] && ssl[:client_key].is_a?(String)
|
69
79
|
@instance
|
70
80
|
end
|
81
|
+
|
82
|
+
def from_file(path)
|
83
|
+
new(path)
|
84
|
+
end
|
85
|
+
|
86
|
+
def from_json(json)
|
87
|
+
new.from_json(json)
|
88
|
+
end
|
89
|
+
|
90
|
+
def from_hash(hash)
|
91
|
+
new.from_hash(hash)
|
92
|
+
end
|
71
93
|
end
|
72
94
|
|
95
|
+
attr_accessor :path
|
96
|
+
|
73
97
|
# @param [String] path
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
98
|
+
def initialize(path = self.class.path)
|
99
|
+
# this is a bit tricky, mixlib-config wants to extend a class and create effectively a global config object while
|
100
|
+
# what we want to do is use an instance, so we create an anonymous class and shove it into an instance variable.
|
101
|
+
# this is actually similar to what mixlib-config itself does to create config contexts.
|
102
|
+
@klass = Class.new
|
103
|
+
@klass.extend(Mixlib::Config)
|
104
|
+
@klass.extend(BerksConfig)
|
105
|
+
|
106
|
+
@path = File.expand_path(path)
|
107
|
+
@klass.from_file(@path) if File.exist?(@path)
|
108
|
+
# yeah, if !File.exist?() you just get back an empty config object
|
109
|
+
end
|
110
|
+
|
111
|
+
def method_missing(method, *args, &block)
|
112
|
+
@klass.send(method, *args, &block)
|
113
|
+
end
|
114
|
+
|
115
|
+
module BerksConfig
|
116
|
+
def self.extended(base)
|
117
|
+
base.class_exec do
|
118
|
+
config_strict_mode true
|
119
|
+
config_context :api do
|
120
|
+
default :timeout, "30"
|
121
|
+
end
|
122
|
+
config_context :chef do
|
123
|
+
default :chef_server_url, Berkshelf.chef_config.chef_server_url
|
124
|
+
default :validation_client_name, Berkshelf.chef_config.validation_client_name
|
125
|
+
default :validation_key_path, Berkshelf.chef_config.validation_key
|
126
|
+
default :client_key, Berkshelf.chef_config.client_key
|
127
|
+
default :node_name, Berkshelf.chef_config.node_name
|
128
|
+
default :trusted_certs_dir, Berkshelf.chef_config.trusted_certs_dir
|
129
|
+
default :artifactory_api_key, Berkshelf.chef_config.artifactory_api_key
|
130
|
+
end
|
131
|
+
config_context :cookbook do
|
132
|
+
default :copyright, Berkshelf.chef_config.cookbook_copyright
|
133
|
+
default :email, Berkshelf.chef_config.cookbook_email
|
134
|
+
default :license, Berkshelf.chef_config.cookbook_license
|
135
|
+
end
|
136
|
+
default :allowed_licenses, Array.new
|
137
|
+
default :raise_license_exception, false
|
138
|
+
config_context :vagrant do
|
139
|
+
config_context :vm do
|
140
|
+
default :box, "bento/ubuntu-14.04"
|
141
|
+
default :forward_port, Hash.new
|
142
|
+
default :provision, "chef_solo"
|
143
|
+
config_context :omnibus do
|
144
|
+
default :version, "latest"
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
config_context :ssl do
|
149
|
+
default :verify, true
|
150
|
+
default :cert_store, false
|
151
|
+
default :ca_file, nil
|
152
|
+
default :ca_path, nil
|
153
|
+
default :ca_cert, nil
|
154
|
+
default :client_cert, nil
|
155
|
+
default :client_key, nil
|
156
|
+
end
|
157
|
+
default :github, []
|
158
|
+
default :gitlab, []
|
159
|
+
# :git, :ssh, or :https
|
160
|
+
default :github_protocol, :https
|
88
161
|
end
|
89
162
|
end
|
90
163
|
end
|
91
|
-
|
92
|
-
attribute "api.timeout",
|
93
|
-
type: String,
|
94
|
-
default: "30"
|
95
|
-
attribute "chef.chef_server_url",
|
96
|
-
type: String,
|
97
|
-
default: Berkshelf.chef_config.chef_server_url
|
98
|
-
attribute "chef.validation_client_name",
|
99
|
-
type: String,
|
100
|
-
default: Berkshelf.chef_config.validation_client_name
|
101
|
-
attribute "chef.validation_key_path",
|
102
|
-
type: String,
|
103
|
-
default: Berkshelf.chef_config.validation_key
|
104
|
-
attribute "chef.client_key",
|
105
|
-
type: String,
|
106
|
-
default: Berkshelf.chef_config.client_key
|
107
|
-
attribute "chef.node_name",
|
108
|
-
type: String,
|
109
|
-
default: Berkshelf.chef_config.node_name
|
110
|
-
attribute "chef.trusted_certs_dir",
|
111
|
-
type: String,
|
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
|
116
|
-
attribute "cookbook.copyright",
|
117
|
-
type: String,
|
118
|
-
default: Berkshelf.chef_config.cookbook_copyright
|
119
|
-
attribute "cookbook.email",
|
120
|
-
type: String,
|
121
|
-
default: Berkshelf.chef_config.cookbook_email
|
122
|
-
attribute "cookbook.license",
|
123
|
-
type: String,
|
124
|
-
default: Berkshelf.chef_config.cookbook_license
|
125
|
-
attribute "allowed_licenses",
|
126
|
-
type: Array,
|
127
|
-
default: Array.new
|
128
|
-
attribute "raise_license_exception",
|
129
|
-
type: Buff::Boolean,
|
130
|
-
default: false
|
131
|
-
attribute "vagrant.vm.box",
|
132
|
-
type: String,
|
133
|
-
default: "bento/ubuntu-14.04",
|
134
|
-
required: true
|
135
|
-
# @todo Deprecated, remove?
|
136
|
-
attribute "vagrant.vm.box_url",
|
137
|
-
type: String,
|
138
|
-
default: nil
|
139
|
-
attribute "vagrant.vm.forward_port",
|
140
|
-
type: Hash,
|
141
|
-
default: Hash.new
|
142
|
-
attribute "vagrant.vm.provision",
|
143
|
-
type: String,
|
144
|
-
default: "chef_solo"
|
145
|
-
# @todo Deprecated, remove. There's a really weird tri-state here where
|
146
|
-
# nil is used to represent an unset value, just FYI
|
147
|
-
attribute "vagrant.omnibus.enabled",
|
148
|
-
type: Buff::Boolean,
|
149
|
-
default: nil
|
150
|
-
attribute "vagrant.omnibus.version",
|
151
|
-
type: String,
|
152
|
-
default: "latest"
|
153
|
-
attribute "ssl.verify",
|
154
|
-
type: Buff::Boolean,
|
155
|
-
default: true,
|
156
|
-
required: true
|
157
|
-
attribute "ssl.cert_store",
|
158
|
-
type: Buff::Boolean,
|
159
|
-
default: false,
|
160
|
-
required: false
|
161
|
-
attribute "ssl.ca_file",
|
162
|
-
type: String,
|
163
|
-
default: nil,
|
164
|
-
required: false
|
165
|
-
attribute "ssl.ca_path",
|
166
|
-
type: String,
|
167
|
-
default: nil,
|
168
|
-
required: false
|
169
|
-
attribute "ssl.client_cert",
|
170
|
-
type: String,
|
171
|
-
default: nil,
|
172
|
-
required: false
|
173
|
-
attribute "ssl.client_key",
|
174
|
-
type: String,
|
175
|
-
default: nil,
|
176
|
-
required: false
|
177
|
-
attribute "github",
|
178
|
-
type: Array,
|
179
|
-
default: [],
|
180
|
-
required: false
|
181
|
-
attribute "gitlab",
|
182
|
-
type: Array,
|
183
|
-
default: [],
|
184
|
-
required: false
|
185
|
-
attribute "github_protocol",
|
186
|
-
# :git, :ssh, or :https
|
187
|
-
type: Symbol,
|
188
|
-
default: :https,
|
189
|
-
required: false
|
190
164
|
end
|
191
165
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "fileutils"
|
2
|
+
require "chef/exceptions"
|
2
3
|
|
3
4
|
module Berkshelf
|
4
5
|
class CookbookStore
|
@@ -67,7 +68,7 @@ module Berkshelf
|
|
67
68
|
destination = cookbook_path(name, version)
|
68
69
|
FileUtils.mv(path, destination)
|
69
70
|
cookbook(name, version)
|
70
|
-
rescue
|
71
|
+
rescue
|
71
72
|
FileUtils.rm_f(destination)
|
72
73
|
raise
|
73
74
|
end
|
@@ -108,7 +109,7 @@ module Berkshelf
|
|
108
109
|
|
109
110
|
begin
|
110
111
|
CachedCookbook.from_store_path(path)
|
111
|
-
rescue
|
112
|
+
rescue Chef::Exceptions::MetadataNotValid
|
112
113
|
# Skip cached cookbooks that do not have a name attribute.
|
113
114
|
skipped_cookbooks << File.basename(path)
|
114
115
|
next
|
@@ -7,7 +7,7 @@ class File
|
|
7
7
|
#
|
8
8
|
# @return [Boolean]
|
9
9
|
def cookbook?(path)
|
10
|
-
File.
|
10
|
+
File.exist?(File.join(path, "metadata.json")) || File.exist?(File.join(path, "metadata.rb"))
|
11
11
|
end
|
12
12
|
alias_method :chef_cookbook?, :cookbook?
|
13
13
|
end
|