berkshelf 1.3.1 → 1.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +6 -0
- data/berkshelf.gemspec +1 -1
- data/lib/berkshelf.rb +5 -12
- data/lib/berkshelf/berksfile.rb +104 -70
- data/lib/berkshelf/cached_cookbook.rb +1 -1
- data/lib/berkshelf/chef/cookbook/chefignore.rb +15 -0
- data/lib/berkshelf/cli.rb +19 -33
- data/lib/berkshelf/cookbook_source.rb +111 -27
- data/lib/berkshelf/errors.rb +3 -1
- data/lib/berkshelf/locations/git_location.rb +19 -0
- data/lib/berkshelf/locations/path_location.rb +2 -1
- data/lib/berkshelf/logger.rb +9 -0
- data/lib/berkshelf/mixin/logging.rb +18 -0
- data/lib/berkshelf/version.rb +1 -1
- data/spec/support/chef_api.rb +2 -2
- data/spec/unit/berkshelf/berksfile_spec.rb +275 -279
- data/spec/unit/berkshelf/chef/cookbook/chefignore_spec.rb +23 -0
- data/spec/unit/berkshelf/cookbook_source_spec.rb +32 -8
- data/spec/unit/berkshelf/locations/git_location_spec.rb +14 -0
- data/spec/unit/berkshelf/logger_spec.rb +29 -0
- data/spec/unit/berkshelf/mixin/logging_spec.rb +25 -0
- data/spec/unit/berkshelf_spec.rb +2 -2
- metadata +17 -12
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# 1.3.1
|
2
|
+
- Support for Vagrant 1.1.x
|
3
|
+
- Move Berkshelf Vagrant plugin into it's [own repository](https://github.com/RiotGames/berkshelf-vagrant)
|
4
|
+
- Added -d flag to output debug information in berks command
|
5
|
+
- Various bug fixes in uploading cookbooks
|
6
|
+
|
1
7
|
# 1.2.0
|
2
8
|
- Remove Vagrant as a gem dependency
|
3
9
|
- Remove Chef as a gem dependency
|
data/berkshelf.gemspec
CHANGED
@@ -33,7 +33,7 @@ Gem::Specification.new do |s|
|
|
33
33
|
s.add_dependency 'mixlib-shellout', '~> 1.1'
|
34
34
|
s.add_dependency 'mixlib-config', '~> 1.1'
|
35
35
|
s.add_dependency 'faraday', '>= 0.8.5'
|
36
|
-
s.add_dependency 'ridley', '
|
36
|
+
s.add_dependency 'ridley', '~> 0.9.0'
|
37
37
|
s.add_dependency 'chozo', '>= 0.6.1'
|
38
38
|
s.add_dependency 'hashie', '>= 2.0.2'
|
39
39
|
s.add_dependency 'minitar'
|
data/lib/berkshelf.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
require 'multi_json'
|
2
|
-
|
3
|
-
# Fix for Facter < 1.7.0 changing LANG to C
|
4
|
-
# https://github.com/puppetlabs/facter/commit/f77584f4
|
5
|
-
begin
|
6
|
-
old_lang = ENV['LANG']
|
7
|
-
require 'ridley'
|
8
|
-
ensure
|
9
|
-
ENV['LANG'] = old_lang
|
10
|
-
end
|
11
|
-
|
2
|
+
require 'ridley'
|
12
3
|
require 'chozo/core_ext'
|
13
4
|
require 'active_support/core_ext'
|
14
5
|
require 'archive/tar/minitar'
|
@@ -46,6 +37,7 @@ module Berkshelf
|
|
46
37
|
autoload :Git, 'berkshelf/git'
|
47
38
|
autoload :InitGenerator, 'berkshelf/init_generator'
|
48
39
|
autoload :Lockfile, 'berkshelf/lockfile'
|
40
|
+
autoload :Logger, 'berkshelf/logger'
|
49
41
|
autoload :Mixin, 'berkshelf/mixin'
|
50
42
|
autoload :Resolver, 'berkshelf/resolver'
|
51
43
|
autoload :UI, 'berkshelf/ui'
|
@@ -53,8 +45,9 @@ module Berkshelf
|
|
53
45
|
require 'berkshelf/location'
|
54
46
|
|
55
47
|
class << self
|
56
|
-
|
48
|
+
include Berkshelf::Mixin::Logging
|
57
49
|
|
50
|
+
attr_accessor :ui
|
58
51
|
attr_writer :cookbook_store
|
59
52
|
|
60
53
|
# @return [Pathname]
|
@@ -79,7 +72,7 @@ module Berkshelf
|
|
79
72
|
end
|
80
73
|
|
81
74
|
# @return [Logger]
|
82
|
-
def
|
75
|
+
def logger
|
83
76
|
Celluloid.logger
|
84
77
|
end
|
85
78
|
|
data/lib/berkshelf/berksfile.rb
CHANGED
@@ -2,6 +2,7 @@ module Berkshelf
|
|
2
2
|
# @author Jamie Winsor <reset@riotgames.com>
|
3
3
|
class Berksfile
|
4
4
|
extend Forwardable
|
5
|
+
include Berkshelf::Mixin::Logging
|
5
6
|
|
6
7
|
class << self
|
7
8
|
# @param [String] file
|
@@ -27,16 +28,16 @@ module Berkshelf
|
|
27
28
|
# @return [String]
|
28
29
|
# expanded filepath to the vendor directory
|
29
30
|
def vendor(cookbooks, path)
|
30
|
-
|
31
|
-
File.join(Dir.pwd, Berkshelf::Chef::Cookbook::Chefignore::FILENAME),
|
32
|
-
File.join(Dir.pwd, 'cookbooks', Berkshelf::Chef::Cookbook::Chefignore::FILENAME)
|
33
|
-
].find { |f| File.exists?(f) }
|
34
|
-
|
35
|
-
chefignore = chefignore_file && Berkshelf::Chef::Cookbook::Chefignore.new(chefignore_file)
|
31
|
+
chefignore = nil
|
36
32
|
path = File.expand_path(path)
|
33
|
+
scratch = Berkshelf.mktmpdir
|
34
|
+
|
37
35
|
FileUtils.mkdir_p(path)
|
38
36
|
|
39
|
-
|
37
|
+
unless (ignore_file = Berkshelf::Chef::Cookbook::Chefignore.find_relative_to(Dir.pwd)).nil?
|
38
|
+
chefignore = Berkshelf::Chef::Cookbook::Chefignore.new(ignore_file)
|
39
|
+
end
|
40
|
+
|
40
41
|
cookbooks.each do |cb|
|
41
42
|
dest = File.join(scratch, cb.cookbook_name, "/")
|
42
43
|
FileUtils.mkdir_p(dest)
|
@@ -73,10 +74,12 @@ module Berkshelf
|
|
73
74
|
def_delegator :downloader, :add_location
|
74
75
|
def_delegator :downloader, :locations
|
75
76
|
|
77
|
+
# @param [String] path
|
78
|
+
# path on disk to the file containing the contents of this Berksfile
|
76
79
|
def initialize(path)
|
77
|
-
@filepath
|
78
|
-
@sources
|
79
|
-
@downloader
|
80
|
+
@filepath = path
|
81
|
+
@sources = Hash.new
|
82
|
+
@downloader = Downloader.new(Berkshelf.cookbook_store)
|
80
83
|
@cached_cookbooks = nil
|
81
84
|
end
|
82
85
|
|
@@ -99,7 +102,8 @@ module Berkshelf
|
|
99
102
|
# cookbook 'artifact', git: 'git://github.com/RiotGames/artifact-cookbook.git'
|
100
103
|
#
|
101
104
|
# @example a cookbook source that will be retrieved from a Chef API (Chef Server)
|
102
|
-
# cookbook 'artifact', chef_api: 'https://api.opscode.com/organizations/vialstudios',
|
105
|
+
# cookbook 'artifact', chef_api: 'https://api.opscode.com/organizations/vialstudios',
|
106
|
+
# node_name: 'reset', client_key: '/Users/reset/.chef/knife.rb'
|
103
107
|
#
|
104
108
|
# @example a cookbook source that will be retrieved from a Chef API using your Berkshelf config
|
105
109
|
# cookbook 'artifact', chef_api: :config
|
@@ -225,7 +229,8 @@ module Berkshelf
|
|
225
229
|
# chef_api :config
|
226
230
|
#
|
227
231
|
# @example using a URL, node_name, and client_key to add a Chef API default location
|
228
|
-
# chef_api "https://api.opscode.com/organizations/vialstudios", node_name: "reset",
|
232
|
+
# chef_api "https://api.opscode.com/organizations/vialstudios", node_name: "reset",
|
233
|
+
# client_key: "/Users/reset/.chef/knife.rb"
|
229
234
|
#
|
230
235
|
# @param [String, Symbol] value
|
231
236
|
# @param [Hash] options
|
@@ -252,7 +257,8 @@ module Berkshelf
|
|
252
257
|
# Only raise an exception if the source is a true duplicate
|
253
258
|
groups = (options[:group].nil? || options[:group].empty?) ? [:default] : options[:group]
|
254
259
|
if !(@sources[name].groups & groups).empty?
|
255
|
-
raise DuplicateSourceDefined,
|
260
|
+
raise DuplicateSourceDefined,
|
261
|
+
"Berksfile contains multiple sources named '#{name}'. Use only one, or put them in different groups."
|
256
262
|
end
|
257
263
|
end
|
258
264
|
|
@@ -393,7 +399,9 @@ module Berkshelf
|
|
393
399
|
missing_cookbooks = (options[:cookbooks] - cookbooks.map(&:cookbook_name))
|
394
400
|
|
395
401
|
unless missing_cookbooks.empty?
|
396
|
-
|
402
|
+
msg = "Could not find cookbooks #{missing_cookbooks.collect{|cookbook| "'#{cookbook}'"}.join(', ')}"
|
403
|
+
msg << " in any of the sources. #{missing_cookbooks.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
|
404
|
+
raise Berkshelf::CookbookNotFound, msg
|
397
405
|
end
|
398
406
|
|
399
407
|
update_lockfile(sources)
|
@@ -443,64 +451,86 @@ module Berkshelf
|
|
443
451
|
outdated
|
444
452
|
end
|
445
453
|
|
446
|
-
# @option options [
|
447
|
-
#
|
448
|
-
#
|
449
|
-
#
|
450
|
-
#
|
451
|
-
# filepath to the client's private key used to authenticate with
|
452
|
-
# the Chef API
|
453
|
-
# @option options [String] :organization
|
454
|
-
# the Organization to connect to. This is only used if you are connecting to
|
455
|
-
# private Chef or hosted Chef
|
456
|
-
# @option options [Boolean] :force Upload the Cookbook even if the version
|
457
|
-
# already exists and is frozen on the target Chef Server
|
458
|
-
# @option options [Boolean] :freeze Freeze the uploaded Cookbook on the Chef
|
459
|
-
# Server so that it cannot be overwritten
|
454
|
+
# @option options [Boolean] :force (false)
|
455
|
+
# Upload the Cookbook even if the version already exists and is frozen on the
|
456
|
+
# target Chef Server
|
457
|
+
# @option options [Boolean] :freeze (true)
|
458
|
+
# Freeze the uploaded Cookbook on the Chef Server so that it cannot be overwritten
|
460
459
|
# @option options [Symbol, Array] :except
|
461
460
|
# Group(s) to exclude which will cause any sources marked as a member of the
|
462
461
|
# group to not be installed
|
463
462
|
# @option options [Symbol, Array] :only
|
464
463
|
# Group(s) to include which will cause any sources marked as a member of the
|
465
464
|
# group to be installed and all others to be ignored
|
466
|
-
# @option
|
465
|
+
# @option options [String, Array] :cookbooks
|
467
466
|
# Names of the cookbooks to retrieve sources for
|
468
|
-
# @option options [Hash] :
|
469
|
-
#
|
470
|
-
# @option options [
|
471
|
-
#
|
472
|
-
# @option options [
|
473
|
-
#
|
474
|
-
#
|
475
|
-
# SSL options
|
476
|
-
# @option options [URI, String, Hash] :proxy
|
477
|
-
# URI, String, or Hash of HTTP proxy options
|
467
|
+
# @option options [Hash] :ssl_verify (true)
|
468
|
+
# Disable/Enable SSL verification during uploads
|
469
|
+
# @option options [Boolean] :skip_dependencies (false)
|
470
|
+
# Skip uploading dependent cookbook(s).
|
471
|
+
# @option options [Boolean] :halt_on_frozen (false)
|
472
|
+
# Raise a FrozenCookbook error if one of the cookbooks being uploaded is already located
|
473
|
+
# on the remote Chef Server and frozen.
|
478
474
|
#
|
479
475
|
# @raise [UploadFailure] if you are uploading cookbooks with an invalid or not-specified client key
|
476
|
+
# @raise [Berkshelf::FrozenCookbook]
|
477
|
+
# if an attempt to upload a cookbook which has been frozen on the target server is made
|
478
|
+
# and the :halt_on_frozen option was true
|
480
479
|
def upload(options = {})
|
481
|
-
|
482
|
-
|
480
|
+
options = options.reverse_merge(
|
481
|
+
force: false,
|
482
|
+
freeze: true,
|
483
|
+
ssl_verify: Berkshelf::Config.instance.ssl.verify,
|
484
|
+
skip_dependencies: false,
|
485
|
+
halt_on_frozen: false
|
486
|
+
)
|
487
|
+
|
488
|
+
ridley_options = options.slice(:ssl)
|
489
|
+
ridley_options[:server_url] = Berkshelf::Config.instance.chef.chef_server_url
|
490
|
+
ridley_options[:client_name] = Berkshelf::Config.instance.chef.node_name
|
491
|
+
ridley_options[:client_key] = Berkshelf::Config.instance.chef.client_key
|
492
|
+
ridley_options[:ssl] = { verify: options[:ssl_verify] }
|
493
|
+
|
494
|
+
unless ridley_options[:server_url].present?
|
495
|
+
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.server_url"
|
496
|
+
end
|
497
|
+
|
498
|
+
unless ridley_options[:client_name].present?
|
499
|
+
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.node_name"
|
500
|
+
end
|
501
|
+
|
502
|
+
unless ridley_options[:client_key].present?
|
503
|
+
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.client_key"
|
504
|
+
end
|
505
|
+
|
506
|
+
conn = Ridley.new(ridley_options)
|
507
|
+
solution = resolve(options)
|
508
|
+
upload_opts = options.slice(:force, :freeze)
|
483
509
|
|
484
510
|
solution.each do |cb|
|
485
|
-
|
486
|
-
upload_opts[:name] = cb.cookbook_name
|
511
|
+
Berkshelf.formatter.upload(cb.cookbook_name, cb.version, conn.server_url)
|
487
512
|
|
488
|
-
|
489
|
-
|
513
|
+
begin
|
514
|
+
conn.cookbook.upload(cb.path, upload_opts.merge(name: cb.cookbook_name))
|
515
|
+
rescue Ridley::Errors::FrozenCookbook => ex
|
516
|
+
if options[:halt_on_frozen]
|
517
|
+
raise Berkshelf::FrozenCookbook, ex
|
518
|
+
end
|
519
|
+
end
|
490
520
|
end
|
491
521
|
|
492
522
|
if options[:skip_dependencies]
|
493
523
|
missing_cookbooks = options.fetch(:cookbooks, nil) - solution.map(&:cookbook_name)
|
494
524
|
unless missing_cookbooks.empty?
|
495
525
|
msg = "Unable to upload cookbooks: #{missing_cookbooks.sort.join(', ')}\n"
|
496
|
-
msg << "Specified cookbooks must be defined within the Berkshelf file when using the
|
526
|
+
msg << "Specified cookbooks must be defined within the Berkshelf file when using the"
|
527
|
+
msg << " `--skip-dependencies` option"
|
497
528
|
raise ExplicitCookbookNotFound.new(msg)
|
498
529
|
end
|
499
530
|
end
|
500
|
-
rescue Ridley::Errors::
|
501
|
-
|
502
|
-
|
503
|
-
raise UploadFailure, msg
|
531
|
+
rescue Ridley::Errors::RidleyError => ex
|
532
|
+
log_exception(ex)
|
533
|
+
raise UploadFailure, ex
|
504
534
|
ensure
|
505
535
|
conn.terminate if conn && conn.alive?
|
506
536
|
end
|
@@ -515,32 +545,14 @@ module Berkshelf
|
|
515
545
|
# group to be installed and all others to be ignored
|
516
546
|
# @option cookbooks [String, Array] :cookbooks
|
517
547
|
# Names of the cookbooks to retrieve sources for
|
548
|
+
# @option options [Boolean] :skip_dependencies
|
549
|
+
# Skip resolving of dependencies
|
518
550
|
#
|
519
551
|
# @return [Array<Berkshelf::CachedCookbooks]
|
520
552
|
def resolve(options = {})
|
521
553
|
resolver(options).resolve
|
522
554
|
end
|
523
555
|
|
524
|
-
# Builds a Resolver instance
|
525
|
-
#
|
526
|
-
# @option options [Symbol, Array] :except
|
527
|
-
# Group(s) to exclude which will cause any sources marked as a member of the
|
528
|
-
# group to not be installed
|
529
|
-
# @option options [Symbol, Array] :only
|
530
|
-
# Group(s) to include which will cause any sources marked as a member of the
|
531
|
-
# group to be installed and all others to be ignored
|
532
|
-
# @option cookbooks [String, Array] :cookbooks
|
533
|
-
# Names of the cookbooks to retrieve sources for
|
534
|
-
#
|
535
|
-
# @return <Berkshelf::Resolver>
|
536
|
-
def resolver(options={})
|
537
|
-
Resolver.new(
|
538
|
-
self.downloader,
|
539
|
-
sources: sources(options),
|
540
|
-
skip_dependencies: options[:skip_dependencies]
|
541
|
-
)
|
542
|
-
end
|
543
|
-
|
544
556
|
# Reload this instance of Berksfile with the given content. The content
|
545
557
|
# is a string that may contain terms from the included DSL.
|
546
558
|
#
|
@@ -569,6 +581,28 @@ module Berkshelf
|
|
569
581
|
File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
|
570
582
|
end
|
571
583
|
|
584
|
+
# Builds a Resolver instance
|
585
|
+
#
|
586
|
+
# @option options [Symbol, Array] :except
|
587
|
+
# Group(s) to exclude which will cause any sources marked as a member of the
|
588
|
+
# group to not be installed
|
589
|
+
# @option options [Symbol, Array] :only
|
590
|
+
# Group(s) to include which will cause any sources marked as a member of the
|
591
|
+
# group to be installed and all others to be ignored
|
592
|
+
# @option options [String, Array] :cookbooks
|
593
|
+
# Names of the cookbooks to retrieve sources for
|
594
|
+
# @option options [Boolean] :skip_dependencies
|
595
|
+
# Skip resolving of dependencies
|
596
|
+
#
|
597
|
+
# @return <Berkshelf::Resolver>
|
598
|
+
def resolver(options = {})
|
599
|
+
Resolver.new(
|
600
|
+
self.downloader,
|
601
|
+
sources: sources(options),
|
602
|
+
skip_dependencies: options[:skip_dependencies]
|
603
|
+
)
|
604
|
+
end
|
605
|
+
|
572
606
|
def write_lockfile(sources)
|
573
607
|
Berkshelf::Lockfile.new(sources).write
|
574
608
|
end
|
@@ -15,6 +15,21 @@ module Berkshelf::Chef::Cookbook
|
|
15
15
|
# See the License for the specific language governing permissions and
|
16
16
|
# limitations under the License.
|
17
17
|
class Chefignore
|
18
|
+
class << self
|
19
|
+
# Traverse a path in relative context to find a Chefignore file
|
20
|
+
#
|
21
|
+
# @param [String] path
|
22
|
+
# path to traverse
|
23
|
+
#
|
24
|
+
# @return [String, nil]
|
25
|
+
def find_relative_to(path)
|
26
|
+
[
|
27
|
+
File.join(path, Berkshelf::Chef::Cookbook::Chefignore::FILENAME),
|
28
|
+
File.join(path, 'cookbooks', Berkshelf::Chef::Cookbook::Chefignore::FILENAME)
|
29
|
+
].find { |f| File.exists?(f) }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
18
33
|
FILENAME = "chefignore".freeze
|
19
34
|
COMMENTS_AND_WHITESPACE = /^\s*(?:#.*)?$/
|
20
35
|
|
data/lib/berkshelf/cli.rb
CHANGED
@@ -31,7 +31,7 @@ module Berkshelf
|
|
31
31
|
end
|
32
32
|
|
33
33
|
if @options[:debug]
|
34
|
-
Berkshelf.
|
34
|
+
Berkshelf.logger.level = ::Logger::DEBUG
|
35
35
|
end
|
36
36
|
|
37
37
|
if @options[:quiet]
|
@@ -199,53 +199,39 @@ module Berkshelf
|
|
199
199
|
type: :array,
|
200
200
|
desc: "Only cookbooks that are in these groups.",
|
201
201
|
aliases: "-o"
|
202
|
-
method_option :
|
202
|
+
method_option :no_freeze,
|
203
203
|
type: :boolean,
|
204
204
|
default: false,
|
205
|
-
desc: "
|
206
|
-
|
205
|
+
desc: "Do not freeze uploaded cookbook(s)."
|
206
|
+
method_option :force,
|
207
207
|
type: :boolean,
|
208
208
|
default: false,
|
209
|
-
desc: "Upload cookbook(s) even if a frozen one exists on the
|
210
|
-
|
209
|
+
desc: "Upload all cookbook(s) even if a frozen one exists on the Chef Server."
|
210
|
+
method_option :ssl_verify,
|
211
211
|
type: :boolean,
|
212
212
|
default: nil,
|
213
|
-
desc: "Disable/Enable SSL verification when uploading cookbooks"
|
214
|
-
|
213
|
+
desc: "Disable/Enable SSL verification when uploading cookbooks."
|
214
|
+
method_option :skip_syntax_check,
|
215
215
|
type: :boolean,
|
216
216
|
default: false,
|
217
|
-
desc: "Skip Ruby syntax check when uploading cookbooks",
|
217
|
+
desc: "Skip Ruby syntax check when uploading cookbooks.",
|
218
218
|
aliases: "-s"
|
219
|
-
|
219
|
+
method_option :skip_dependencies,
|
220
|
+
type: :boolean,
|
221
|
+
desc: "Skip uploading dependent cookbook(s).",
|
222
|
+
default: false,
|
223
|
+
aliases: "-D"
|
224
|
+
method_option :halt_on_frozen,
|
220
225
|
type: :boolean,
|
221
|
-
desc: 'Do not upload dependencies',
|
222
226
|
default: false,
|
223
|
-
|
227
|
+
desc: "Halt uploading and exit if the Chef Server has a frozen version of the cookbook(s)."
|
224
228
|
desc "upload [COOKBOOKS]", "Upload cookbook(s) specified by a Berksfile to the configured Chef Server."
|
225
229
|
def upload(*cookbook_names)
|
226
230
|
berksfile = ::Berkshelf::Berksfile.from_file(options[:berksfile])
|
227
231
|
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
raise UploadFailure, msg
|
232
|
-
end
|
233
|
-
|
234
|
-
unless Berkshelf::Config.instance.chef.node_name.present?
|
235
|
-
msg = "Could not upload cookbooks: Missing Chef node_name."
|
236
|
-
msg << " Generate or update your Berkshelf configuration that contains a valid Chef node_name."
|
237
|
-
raise UploadFailure, msg
|
238
|
-
end
|
239
|
-
|
240
|
-
upload_options = {
|
241
|
-
server_url: Berkshelf::Config.instance.chef.chef_server_url,
|
242
|
-
client_name: Berkshelf::Config.instance.chef.node_name,
|
243
|
-
client_key: Berkshelf::Config.instance.chef.client_key,
|
244
|
-
ssl: {
|
245
|
-
verify: (options[:ssl_verify].nil? ? Berkshelf::Config.instance.ssl.verify : options[:ssl_verify])
|
246
|
-
},
|
247
|
-
cookbooks: cookbook_names
|
248
|
-
}.merge(options).symbolize_keys
|
232
|
+
upload_options = Hash[options.except(:no_freeze, :berksfile)].symbolize_keys
|
233
|
+
upload_options[:cookbooks] = cookbook_names
|
234
|
+
upload_options[:freeze] = false if options[:no_freeze]
|
249
235
|
|
250
236
|
berksfile.upload(upload_options)
|
251
237
|
end
|
@@ -57,6 +57,11 @@ module Berkshelf
|
|
57
57
|
raise InternalError, "Invalid options for Cookbook Source: #{invalid_options.join(', ')}."
|
58
58
|
end
|
59
59
|
|
60
|
+
if (options.keys & [:site, :path, :git]).size > 1
|
61
|
+
invalid = (options.keys & [:site, :path, :git]).map { |opt| "'#{opt}" }
|
62
|
+
raise InternalError, "Cannot specify #{invalid.to_sentence} for a Cookbook Source!"
|
63
|
+
end
|
64
|
+
|
60
65
|
true
|
61
66
|
end
|
62
67
|
end
|
@@ -64,13 +69,10 @@ module Berkshelf
|
|
64
69
|
extend Forwardable
|
65
70
|
|
66
71
|
attr_reader :name
|
72
|
+
attr_reader :options
|
67
73
|
attr_reader :version_constraint
|
68
|
-
attr_reader :groups
|
69
|
-
attr_reader :location
|
70
74
|
attr_accessor :cached_cookbook
|
71
75
|
|
72
|
-
def_delegator :cached_cookbook, :version, :locked_version
|
73
|
-
|
74
76
|
# @param [String] name
|
75
77
|
# @param [Hash] options
|
76
78
|
#
|
@@ -92,52 +94,70 @@ module Berkshelf
|
|
92
94
|
# same as tag
|
93
95
|
# @option options [String] :locked_version
|
94
96
|
def initialize(name, options = {})
|
95
|
-
@name = name
|
96
|
-
@version_constraint = Solve::Constraint.new(options[:constraint] || ">= 0.0.0")
|
97
|
-
@groups = []
|
98
|
-
@cached_cookbook = nil
|
99
|
-
@location = nil
|
100
|
-
|
101
97
|
self.class.validate_options(options)
|
102
98
|
|
103
|
-
|
104
|
-
@location = Location.init(name, version_constraint, options)
|
105
|
-
end
|
106
|
-
|
107
|
-
if @location.is_a?(PathLocation)
|
108
|
-
begin
|
109
|
-
@cached_cookbook = CachedCookbook.from_path(location.path)
|
110
|
-
rescue IOError
|
111
|
-
raise Berkshelf::CookbookNotFound
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
99
|
+
@name = name
|
115
100
|
@locked_version = Solve::Version.new(options[:locked_version]) if options[:locked_version]
|
101
|
+
@version_constraint = Solve::Constraint.new(options[:locked_version] || options[:constraint] || ">= 0.0.0")
|
102
|
+
|
103
|
+
@cached_cookbook, @location = cached_and_location(options)
|
116
104
|
|
117
105
|
add_group(options[:group]) if options[:group]
|
118
106
|
add_group(:default) if groups.empty?
|
119
107
|
end
|
120
108
|
|
121
|
-
def add_group(*
|
122
|
-
|
123
|
-
|
109
|
+
def add_group(*local_groups)
|
110
|
+
local_groups = local_groups.first if local_groups.first.is_a?(Array)
|
111
|
+
|
112
|
+
local_groups.each do |group|
|
124
113
|
group = group.to_sym
|
125
|
-
|
114
|
+
groups << group unless groups.include?(group)
|
126
115
|
end
|
127
116
|
end
|
128
117
|
|
129
118
|
# Returns true if the cookbook source has already been downloaded. A cookbook
|
130
|
-
# source is downloaded when a cached
|
119
|
+
# source is downloaded when a cached cookbook is present.
|
131
120
|
#
|
132
121
|
# @return [Boolean]
|
133
122
|
def downloaded?
|
134
123
|
!self.cached_cookbook.nil?
|
135
124
|
end
|
136
125
|
|
126
|
+
# Returns true if this CookbookSource has the given group.
|
127
|
+
#
|
128
|
+
# @return [Boolean]
|
137
129
|
def has_group?(group)
|
138
130
|
groups.include?(group.to_sym)
|
139
131
|
end
|
140
132
|
|
133
|
+
# Get the locked version of this cookbook. First check the instance variable
|
134
|
+
# and then resort to the cached_cookbook for the version.
|
135
|
+
#
|
136
|
+
# This was formerly a delegator, but it would fail if the `@cached_cookbook`
|
137
|
+
# was nil or undefined.
|
138
|
+
#
|
139
|
+
# @return [Solve::Version, nil]
|
140
|
+
# the locked version of this cookbook
|
141
|
+
def locked_version
|
142
|
+
@locked_version ||= cached_cookbook.try(:version)
|
143
|
+
end
|
144
|
+
|
145
|
+
# The location for this CookbookSource, such as a remote Chef Server, the
|
146
|
+
# community API, :git, or a :path location. By default, this will be the
|
147
|
+
# community API.
|
148
|
+
#
|
149
|
+
# @return [Berkshelf::Location]
|
150
|
+
def location
|
151
|
+
@location
|
152
|
+
end
|
153
|
+
|
154
|
+
# The list of groups this CookbookSource belongs to.
|
155
|
+
#
|
156
|
+
# @return [Array<Symbol>]
|
157
|
+
def groups
|
158
|
+
@groups ||= []
|
159
|
+
end
|
160
|
+
|
141
161
|
def to_s
|
142
162
|
msg = "#{self.name} (#{self.version_constraint}) groups: #{self.groups}"
|
143
163
|
msg << " location: #{self.location}" if self.location
|
@@ -155,5 +175,69 @@ module Berkshelf
|
|
155
175
|
def to_json
|
156
176
|
MultiJson.dump(self.to_hash, pretty: true)
|
157
177
|
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
# Determine the CachedCookbook and Location information from the given options.
|
182
|
+
#
|
183
|
+
# @return [Array<CachedCookbook, Location>]
|
184
|
+
def cached_and_location(options = {})
|
185
|
+
from_path(options) || from_cache(options) || from_default(options)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Attempt to load a CachedCookbook from a local file system path (if the :path
|
189
|
+
# option was given). If one is found, the location and cached_cookbook is
|
190
|
+
# updated. Otherwise, this method will raise a CookbookNotFound exception.
|
191
|
+
#
|
192
|
+
# @raises [Berkshelf::CookbookNotFound]
|
193
|
+
# if no CachedCookbook exists at the given path
|
194
|
+
#
|
195
|
+
# @return [Berkshelf::CachedCookbook]
|
196
|
+
def from_path(options = {})
|
197
|
+
return nil unless options[:path]
|
198
|
+
|
199
|
+
location = PathLocation.new(name, version_constraint, path: options[:path])
|
200
|
+
cached = CachedCookbook.from_path(location.path)
|
201
|
+
|
202
|
+
[cached, location]
|
203
|
+
rescue IOError
|
204
|
+
raise Berkshelf::CookbookNotFound
|
205
|
+
end
|
206
|
+
|
207
|
+
# Attempt to load a CachedCookbook from the local CookbookStore. This will save
|
208
|
+
# the need to make an http request to download a cookbook we already have cached
|
209
|
+
# locally.
|
210
|
+
#
|
211
|
+
# @return [Berkshelf::CachedCookbook, nil]
|
212
|
+
def from_cache(options = {})
|
213
|
+
path = File.join(Berkshelf.cookbooks_dir, filename(options))
|
214
|
+
return nil unless File.exists?(path)
|
215
|
+
|
216
|
+
location = PathLocation.new(name, version_constraint, path: path)
|
217
|
+
cached = CachedCookbook.from_path(path, name: name)
|
218
|
+
|
219
|
+
[cached, location]
|
220
|
+
end
|
221
|
+
|
222
|
+
# Use the default location, and a nil CachedCookbook. If there is no location
|
223
|
+
# specified,
|
224
|
+
#
|
225
|
+
# @return [Array<nil, Location>]
|
226
|
+
def from_default(options = {})
|
227
|
+
if (options.keys & self.class.location_keys.keys).empty?
|
228
|
+
location = nil
|
229
|
+
else
|
230
|
+
location = Location.init(name, version_constraint, options)
|
231
|
+
end
|
232
|
+
|
233
|
+
[nil, location]
|
234
|
+
end
|
235
|
+
|
236
|
+
# The hypothetical location of this CachedCookbook, if it were to exist.
|
237
|
+
#
|
238
|
+
# @return [String]
|
239
|
+
def filename(options = {})
|
240
|
+
"#{name}-#{options[:locked_version]}"
|
241
|
+
end
|
158
242
|
end
|
159
243
|
end
|