berkshelf 6.3.4 → 7.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|