berkshelf 2.0.18 → 3.0.0.beta1

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.
Files changed (128) hide show
  1. data/.ruby-version +1 -1
  2. data/.travis.yml +4 -1
  3. data/CHANGELOG.md +2 -26
  4. data/Gemfile +12 -2
  5. data/README.md +9 -1
  6. data/berkshelf.gemspec +9 -18
  7. data/bin/berks +3 -13
  8. data/features/apply_command.feature +11 -9
  9. data/features/berksfile.feature +8 -10
  10. data/features/config.feature +1 -2
  11. data/features/configure_command.feature +13 -14
  12. data/features/contingent_command.feature +13 -1
  13. data/features/cookbook_command.feature +2 -4
  14. data/features/groups_install.feature +10 -2
  15. data/features/help.feature +1 -1
  16. data/features/init_command.feature +5 -7
  17. data/features/install_command.feature +157 -228
  18. data/features/json_formatter.feature +27 -15
  19. data/features/licenses.feature +18 -12
  20. data/features/list_command.feature +6 -1
  21. data/features/lockfile.feature +116 -72
  22. data/features/outdated_command.feature +3 -47
  23. data/features/package_command.feature +10 -7
  24. data/features/shelf/show.feature +2 -2
  25. data/features/shelf/uninstall.feature +2 -2
  26. data/features/show_command.feature +10 -3
  27. data/features/step_definitions/chef/config_steps.rb +12 -0
  28. data/features/step_definitions/chef_server_steps.rb +16 -16
  29. data/features/step_definitions/cli_steps.rb +3 -79
  30. data/features/step_definitions/config_steps.rb +43 -0
  31. data/features/step_definitions/environment_steps.rb +7 -0
  32. data/features/step_definitions/filesystem_steps.rb +12 -57
  33. data/features/step_definitions/gem_steps.rb +1 -2
  34. data/features/step_definitions/json_steps.rb +3 -1
  35. data/features/step_definitions/lockfile_steps.rb +4 -0
  36. data/features/step_definitions/utility_steps.rb +0 -19
  37. data/features/support/aruba.rb +12 -0
  38. data/features/support/env.rb +52 -57
  39. data/features/update_command.feature +37 -23
  40. data/features/upload_command.feature +96 -160
  41. data/generator_files/Berksfile.erb +2 -1
  42. data/generator_files/Vagrantfile.erb +3 -0
  43. data/generator_files/default_test.rb.erb +1 -1
  44. data/generator_files/helpers.rb.erb +1 -1
  45. data/lib/berkshelf.rb +43 -24
  46. data/lib/berkshelf/api_client.rb +67 -0
  47. data/lib/berkshelf/api_client/remote_cookbook.rb +42 -0
  48. data/lib/berkshelf/berksfile.rb +232 -420
  49. data/lib/berkshelf/cached_cookbook.rb +22 -10
  50. data/lib/berkshelf/chef/config.rb +1 -0
  51. data/lib/berkshelf/cli.rb +66 -68
  52. data/lib/berkshelf/commands/shelf.rb +1 -1
  53. data/lib/berkshelf/community_rest.rb +10 -17
  54. data/lib/berkshelf/config.rb +23 -27
  55. data/lib/berkshelf/cookbook_generator.rb +3 -4
  56. data/lib/berkshelf/cookbook_store.rb +74 -17
  57. data/lib/berkshelf/core_ext/file.rb +2 -2
  58. data/lib/berkshelf/core_ext/pathname.rb +7 -5
  59. data/lib/berkshelf/{cookbook_source.rb → dependency.rb} +47 -67
  60. data/lib/berkshelf/downloader.rb +49 -106
  61. data/lib/berkshelf/errors.rb +64 -71
  62. data/lib/berkshelf/formatters.rb +11 -9
  63. data/lib/berkshelf/formatters/human_readable.rb +9 -9
  64. data/lib/berkshelf/formatters/json.rb +14 -4
  65. data/lib/berkshelf/init_generator.rb +3 -3
  66. data/lib/berkshelf/installer.rb +136 -0
  67. data/lib/berkshelf/location.rb +91 -131
  68. data/lib/berkshelf/locations/git_location.rb +9 -11
  69. data/lib/berkshelf/locations/github_location.rb +1 -1
  70. data/lib/berkshelf/locations/path_location.rb +10 -27
  71. data/lib/berkshelf/lockfile.rb +92 -70
  72. data/lib/berkshelf/logger.rb +4 -7
  73. data/lib/berkshelf/mixin/config.rb +21 -4
  74. data/lib/berkshelf/resolver.rb +60 -150
  75. data/lib/berkshelf/resolver/graph.rb +44 -0
  76. data/lib/berkshelf/source.rb +55 -0
  77. data/lib/berkshelf/source_uri.rb +38 -0
  78. data/lib/berkshelf/version.rb +1 -1
  79. data/spec/config/knife.rb +1 -1
  80. data/spec/fixtures/cassettes/Berkshelf_Resolver/_initialize/adds_the_dependencies_of_the_dependency_as_dependencies.yml +3694 -0
  81. data/spec/fixtures/cookbooks/example_cookbook/Berksfile.lock +1 -1
  82. data/spec/fixtures/lockfiles/default.lock +1 -1
  83. data/spec/spec_helper.rb +20 -121
  84. data/spec/support/chef_api.rb +3 -4
  85. data/spec/support/chef_server.rb +20 -11
  86. data/spec/support/git.rb +127 -0
  87. data/spec/support/kitchen.rb +12 -0
  88. data/spec/support/path_helpers.rb +69 -0
  89. data/spec/unit/berkshelf/api_client/remote_cookbook_spec.rb +23 -0
  90. data/spec/unit/berkshelf/api_client_spec.rb +57 -0
  91. data/spec/unit/berkshelf/berksfile_spec.rb +206 -324
  92. data/spec/unit/berkshelf/cached_cookbook_spec.rb +73 -38
  93. data/spec/unit/berkshelf/community_rest_spec.rb +30 -71
  94. data/spec/unit/berkshelf/config_spec.rb +3 -14
  95. data/spec/unit/berkshelf/cookbook_generator_spec.rb +1 -2
  96. data/spec/unit/berkshelf/cookbook_store_spec.rb +12 -7
  97. data/spec/unit/berkshelf/dependency_spec.rb +285 -0
  98. data/spec/unit/berkshelf/downloader_spec.rb +4 -183
  99. data/spec/unit/berkshelf/formatters/null_spec.rb +1 -1
  100. data/spec/unit/berkshelf/formatters_spec.rb +4 -2
  101. data/spec/unit/berkshelf/git_spec.rb +15 -15
  102. data/spec/unit/berkshelf/installer_spec.rb +39 -0
  103. data/spec/unit/berkshelf/location_spec.rb +87 -114
  104. data/spec/unit/berkshelf/locations/git_location_spec.rb +41 -53
  105. data/spec/unit/berkshelf/locations/path_location_spec.rb +13 -23
  106. data/spec/unit/berkshelf/lockfile_spec.rb +38 -40
  107. data/spec/unit/berkshelf/resolver/graph_spec.rb +44 -0
  108. data/spec/unit/berkshelf/resolver_spec.rb +34 -83
  109. data/spec/unit/berkshelf/source_spec.rb +23 -0
  110. data/spec/unit/berkshelf/source_uri_spec.rb +29 -0
  111. metadata +149 -188
  112. checksums.yaml +0 -7
  113. data/features/default_locations.feature +0 -127
  114. data/features/step_definitions/berksfile_steps.rb +0 -8
  115. data/features/step_definitions/configure_cli_steps.rb +0 -19
  116. data/features/vendor_install.feature +0 -19
  117. data/lib/berkshelf/core_ext/openuri.rb +0 -36
  118. data/lib/berkshelf/core_ext/rbzip2.rb +0 -8
  119. data/lib/berkshelf/locations/chef_api_location.rb +0 -228
  120. data/lib/berkshelf/locations/site_location.rb +0 -92
  121. data/lib/berkshelf/test.rb +0 -35
  122. data/spec/knife.rb.sample +0 -12
  123. data/spec/support/test_generators.rb +0 -27
  124. data/spec/unit/berkshelf/cli_spec.rb +0 -16
  125. data/spec/unit/berkshelf/cookbook_source_spec.rb +0 -358
  126. data/spec/unit/berkshelf/core_ext/pathname_spec.rb +0 -46
  127. data/spec/unit/berkshelf/locations/chef_api_location_spec.rb +0 -139
  128. data/spec/unit/berkshelf/locations/site_location_spec.rb +0 -19
@@ -1,5 +1,6 @@
1
- site :opscode
1
+ source "http://api.berkshelf.com"
2
2
  <% if options[:chef_minitest] -%>
3
+
3
4
  group :integration do
4
5
  cookbook 'minitest-handler'
5
6
  end
@@ -62,6 +62,9 @@ Vagrant.configure("2") do |config|
62
62
  # View the documentation for the provider you're using for more
63
63
  # information on available options.
64
64
 
65
+ config.ssh.max_tries = 40
66
+ config.ssh.timeout = 120
67
+
65
68
  # The path to the Berksfile to use with Vagrant Berkshelf
66
69
  # config.berkshelf.berksfile_path = "./Berksfile"
67
70
 
@@ -2,7 +2,7 @@ require File.expand_path('../support/helpers', __FILE__)
2
2
 
3
3
  describe '<%= cookbook_name %>::default' do
4
4
 
5
- include Helpers::<%= cookbook_name.capitalize.sub('-','_') %>
5
+ include Helpers::<%= cookbook_name.capitalize.gsub('-','_') %>
6
6
 
7
7
  # Example spec tests can be found at http://git.io/Fahwsw
8
8
  it 'runs no tests by default' do
@@ -1,5 +1,5 @@
1
1
  module Helpers
2
- module <%= cookbook_name.capitalize.sub('-','_') %>
2
+ module <%= cookbook_name.capitalize.gsub('-','_') %>
3
3
  include MiniTest::Chef::Assertions
4
4
  include MiniTest::Chef::Context
5
5
  include MiniTest::Chef::Resources
@@ -1,6 +1,6 @@
1
- require 'active_support/core_ext'
1
+ require 'buff/extensions'
2
2
  require 'archive/tar/minitar'
3
- require 'chozo/core_ext'
3
+ require 'celluloid'
4
4
  require 'digest/md5'
5
5
  require 'forwardable'
6
6
  require 'hashie'
@@ -12,24 +12,24 @@ require 'thor'
12
12
  require 'tmpdir'
13
13
  require 'uri'
14
14
  require 'zlib'
15
- require 'rbzip2'
15
+
16
+ JSON.create_id = nil
16
17
 
17
18
  require_relative 'berkshelf/core_ext'
18
- require_relative 'berkshelf/errors'
19
- require_relative 'berkshelf/mixin'
20
19
  require_relative 'berkshelf/thor_ext'
21
20
 
22
- JSON.create_id = nil
23
-
24
21
  module Berkshelf
22
+ require_relative 'berkshelf/errors'
23
+ require_relative 'berkshelf/mixin'
24
+
25
25
  DEFAULT_FILENAME = 'Berksfile'.freeze
26
26
 
27
27
  class << self
28
28
  include Berkshelf::Mixin::Logging
29
29
 
30
+ attr_writer :berkshelf_path
30
31
  attr_accessor :ui
31
32
  attr_accessor :logger
32
- attr_writer :cookbook_store
33
33
 
34
34
  # @return [Pathname]
35
35
  def root
@@ -50,21 +50,42 @@ module Berkshelf
50
50
  #
51
51
  # @return [String]
52
52
  def berkshelf_path
53
- ENV['BERKSHELF_PATH'] || File.expand_path('~/.berkshelf')
53
+ @berkshelf_path || ENV['BERKSHELF_PATH'] || File.expand_path('~/.berkshelf')
54
+ end
55
+
56
+ # The Berkshelf configuration.
57
+ #
58
+ # @return [Berkshelf::Config]
59
+ def config
60
+ Berkshelf::Config.instance
61
+ end
62
+
63
+ # @param [Berkshelf::Config]
64
+ def config=(config)
65
+ Berkshelf::Config.set_config(config)
54
66
  end
55
67
 
56
68
  # The Chef configuration file.
57
69
  #
58
70
  # @return [Berkshelf::Chef::Config]
59
71
  def chef_config
60
- @chef_config ||= Berkshelf::Chef::Config.load
72
+ Berkshelf::Chef::Config.instance
61
73
  end
62
74
 
63
- # Set the Chef configuration file.
64
- #
65
- # @param [Berkshelf::Chef::Config] new_config
66
- # the new configuration file to use
67
- attr_writer :chef_config
75
+ # @param [Berkshelf::Chef::Config]
76
+ def chef_config=(config)
77
+ Berkshelf::Chef::Config.set_config(config)
78
+ end
79
+
80
+ # Initialize the filepath for the Berkshelf path..
81
+ def initialize_filesystem
82
+ FileUtils.mkdir_p(berkshelf_path, mode: 0755)
83
+
84
+ unless File.writable?(berkshelf_path)
85
+ raise InsufficientPrivledges, "You do not have permission to write to '#{berkshelf_path}'!" +
86
+ " Please either chown the directory or use a different filepath."
87
+ end
88
+ end
68
89
 
69
90
  # @return [String]
70
91
  def tmp_dir
@@ -80,13 +101,9 @@ module Berkshelf
80
101
  Dir.mktmpdir(nil, tmp_dir)
81
102
  end
82
103
 
83
- def cookbooks_dir
84
- File.join(berkshelf_path, 'cookbooks')
85
- end
86
-
87
104
  # @return [Berkshelf::CookbookStore]
88
105
  def cookbook_store
89
- @cookbook_store ||= CookbookStore.new(cookbooks_dir)
106
+ CookbookStore.instance
90
107
  end
91
108
 
92
109
  # Get the appropriate Formatter object based on the formatter
@@ -122,6 +139,7 @@ module Berkshelf
122
139
  end
123
140
  end
124
141
 
142
+ require_relative 'berkshelf/api_client'
125
143
  require_relative 'berkshelf/base_generator'
126
144
  require_relative 'berkshelf/berksfile'
127
145
  require_relative 'berkshelf/cached_cookbook'
@@ -129,21 +147,22 @@ require_relative 'berkshelf/chef'
129
147
  require_relative 'berkshelf/cli'
130
148
  require_relative 'berkshelf/community_rest'
131
149
  require_relative 'berkshelf/cookbook_generator'
132
- require_relative 'berkshelf/cookbook_source'
133
150
  require_relative 'berkshelf/cookbook_store'
134
151
  require_relative 'berkshelf/config'
152
+ require_relative 'berkshelf/dependency'
135
153
  require_relative 'berkshelf/downloader'
136
154
  require_relative 'berkshelf/formatters'
137
155
  require_relative 'berkshelf/git'
138
156
  require_relative 'berkshelf/init_generator'
157
+ require_relative 'berkshelf/installer'
139
158
  require_relative 'berkshelf/location'
140
159
  require_relative 'berkshelf/lockfile'
141
160
  require_relative 'berkshelf/logger'
142
161
  require_relative 'berkshelf/resolver'
143
- require_relative 'berkshelf/test' if ENV['RUBY_ENV'] == 'test'
162
+ require_relative 'berkshelf/source'
163
+ require_relative 'berkshelf/source_uri'
144
164
  require_relative 'berkshelf/ui'
145
165
  require_relative 'berkshelf/version'
146
166
 
147
- Ridley.logger = Berkshelf.logger = Logger.new(STDOUT)
167
+ Ridley.logger = Celluloid.logger = Berkshelf.logger = Logger.new(STDOUT)
148
168
  Berkshelf.logger.level = Logger::WARN
149
- Celluloid.logger.level = Logger::FATAL if defined?(Celluloid)
@@ -0,0 +1,67 @@
1
+ require 'addressable/uri'
2
+
3
+ module Berkshelf
4
+ # Used to communicate with a remotely hosted [Berkshelf API Server](https://github.com/riotgames/berkshelf-api).
5
+ #
6
+ # @example
7
+ # client = Berkshelf::APIClient.new("http://api.berkshelf.com")
8
+ # client.universe #=> [...]
9
+ class APIClient < Faraday::Connection
10
+ require_relative 'api_client/remote_cookbook'
11
+
12
+ # @return [Addressable::URI]
13
+ attr_reader :url
14
+
15
+ # @return [Integer]
16
+ # how many retries to attempt on HTTP requests
17
+ attr_reader :retries
18
+
19
+ # @return [Float]
20
+ # time to wait between retries
21
+ attr_reader :retry_interval
22
+
23
+ # @param [String, Addressable::URI] url
24
+ #
25
+ # @option options [Integer] :retries
26
+ # how many retries to perform before giving up
27
+ # @option options [Float] :retry_interval
28
+ # how long to wait (in seconds) between each retry
29
+ def initialize(url, options = {})
30
+ options = options.reverse_merge(retries: 5, retry_interval: 0.5)
31
+ @url = Addressable::URI.parse(url)
32
+ @retries = options[:retries]
33
+ @retry_interval = options[:retry_interval]
34
+
35
+ builder = Faraday::Builder.new do |b|
36
+ b.response :parse_json
37
+ b.response :gzip
38
+ b.request :retry,
39
+ max: self.retries,
40
+ interval: self.retry_interval,
41
+ exceptions: [ Faraday::Error::TimeoutError ]
42
+
43
+ b.adapter :net_http
44
+ end
45
+
46
+ super(self.url, builder: builder)
47
+ end
48
+
49
+ # Retrieves the entire universe of known cookbooks from the API source
50
+ #
51
+ # @return [Array<APIClient::RemoteCookbook>]
52
+ def universe
53
+ response = get("universe")
54
+
55
+ case response.status
56
+ when 200
57
+ [].tap do |cookbooks|
58
+ response.body.each do |name, versions|
59
+ versions.each { |version, attributes| cookbooks << RemoteCookbook.new(name, version, attributes) }
60
+ end
61
+ end
62
+ else
63
+ raise RuntimeError, "bad response #{response.inspect}"
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,42 @@
1
+ module Berkshelf
2
+ class APIClient
3
+ # A representation of cookbook metadata indexed by a Berkshelf API Server. Returned
4
+ # by sending messages to a {Berkshelf::APIClient} and used to download cookbooks
5
+ # indexed by the Berkshelf API Server.
6
+ class RemoteCookbook
7
+ # @return [String]
8
+ attr_reader :name
9
+ # @return [String]
10
+ attr_reader :version
11
+
12
+ # @param [String] name
13
+ # @param [String] version
14
+ # @param [Hash] attributes
15
+ def initialize(name, version, attributes = {})
16
+ @name = name
17
+ @version = version
18
+ @attributes = attributes
19
+ end
20
+
21
+ # @return [Hash]
22
+ def dependencies
23
+ @attributes[:dependencies]
24
+ end
25
+
26
+ # @return [Hash]
27
+ def platforms
28
+ @attributes[:platforms]
29
+ end
30
+
31
+ # @return [Symbol]
32
+ def location_type
33
+ @attributes[:location_type].to_sym
34
+ end
35
+
36
+ # @return [String]
37
+ def location_path
38
+ @attributes[:location_path]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,71 +1,38 @@
1
1
  module Berkshelf
2
2
  class Berksfile
3
3
  class << self
4
+ # The sources to use if no sources are explicitly provided
5
+ #
6
+ # @return [Array<Berkshelf::Source>]
7
+ def default_sources
8
+ @default_sources ||= [ Source.new(DEFAULT_API_URL) ]
9
+ end
10
+
4
11
  # @param [#to_s] file
5
12
  # a path on disk to a Berksfile to instantiate from
6
13
  #
7
14
  # @return [Berksfile]
8
15
  def from_file(file)
9
- raise BerksfileNotFound.new(file) unless File.exist?(file)
10
-
11
- begin
12
- new(file).dsl_eval_file(file)
13
- rescue => ex
14
- raise BerksfileReadError.new(ex)
15
- end
16
- end
17
-
18
- # Copy all cached_cookbooks to the given directory. Each cookbook will be contained in
19
- # a directory named after the name of the cookbook.
20
- #
21
- # @param [Array<CachedCookbook>] cookbooks
22
- # an array of CachedCookbooks to be copied to a vendor directory
23
- # @param [String] path
24
- # filepath to vendor cookbooks to
25
- #
26
- # @return [String]
27
- # expanded filepath to the vendor directory
28
- def vendor(cookbooks, path)
29
- chefignore = nil
30
- path = File.expand_path(path)
31
- scratch = Berkshelf.mktmpdir
32
-
33
- FileUtils.mkdir_p(path)
34
-
35
- unless (ignore_file = Berkshelf::Chef::Cookbook::Chefignore.find_relative_to(Dir.pwd)).nil?
36
- chefignore = Berkshelf::Chef::Cookbook::Chefignore.new(ignore_file)
37
- end
38
-
39
- cookbooks.each do |cb|
40
- dest = File.join(scratch, cb.cookbook_name, '/')
41
- FileUtils.mkdir_p(dest)
42
-
43
- # Dir.glob does not support backslash as a File separator
44
- src = cb.path.to_s.gsub('\\', '/')
45
- files = Dir.glob(File.join(src, '*'))
46
-
47
- # Filter out files using chefignore
48
- files = chefignore.remove_ignores_from(files) if chefignore
49
-
50
- FileUtils.cp_r(files, dest)
51
- end
52
-
53
- FileUtils.remove_dir(path, force: true)
54
- FileUtils.mv(scratch, path)
55
-
56
- path
16
+ new(file).dsl_eval_file(file)
17
+ rescue Errno::ENOENT => ex
18
+ raise BerksfileNotFound, "No Berksfile or Berksfile.lock found at: #{file}"
19
+ rescue => ex
20
+ raise BerksfileReadError.new(ex)
57
21
  end
58
22
  end
59
23
 
24
+ DEFAULT_API_URL = "http://api.berkshelf.com".freeze
25
+
60
26
  include Berkshelf::Mixin::Logging
61
27
  include Berkshelf::Mixin::DSLEval
62
28
  extend Forwardable
63
29
 
30
+ expose_method :source
31
+ expose_method :site # @todo remove in Berkshelf 4.0
32
+ expose_method :chef_api # @todo remove in Berkshelf 4.0
64
33
  expose_method :metadata
65
- expose_method :group
66
- expose_method :site
67
- expose_method :chef_api
68
34
  expose_method :cookbook
35
+ expose_method :group
69
36
 
70
37
  @@active_group = nil
71
38
 
@@ -73,90 +40,53 @@ module Berkshelf
73
40
  # The path on disk to the file representing this instance of Berksfile
74
41
  attr_reader :filepath
75
42
 
76
- # @return [Berkshelf::Downloader]
77
- attr_reader :downloader
78
-
79
43
  # @return [Array<Berkshelf::CachedCookbook>]
80
44
  attr_reader :cached_cookbooks
81
45
 
82
- def_delegator :downloader, :add_location
83
- def_delegator :downloader, :locations
84
-
85
46
  # @param [String] path
86
47
  # path on disk to the file containing the contents of this Berksfile
87
48
  def initialize(path)
88
49
  @filepath = path
89
- @sources = Hash.new
90
- @downloader = Downloader.new(Berkshelf.cookbook_store)
50
+ @dependencies = Hash.new
91
51
  @cached_cookbooks = nil
52
+ @sources = Array.new
92
53
  end
93
54
 
94
55
  # Add a cookbook dependency to the Berksfile to be retrieved and have it's dependencies recursively retrieved
95
56
  # and resolved.
96
57
  #
97
- # @example a cookbook source that will be retrieved from one of the default locations
58
+ # @example a cookbook dependency that will be retrieved from one of the default locations
98
59
  # cookbook 'artifact'
99
60
  #
100
- # @example a cookbook source that will be retrieved from a path on disk
61
+ # @example a cookbook dependency that will be retrieved from a path on disk
101
62
  # cookbook 'artifact', path: '/Users/reset/code/artifact'
102
63
  #
103
- # @example a cookbook source that will be retrieved from a remote community site
104
- # cookbook 'artifact', site: 'http://cookbooks.opscode.com/api/v1/cookbooks'
105
- #
106
- # @example a cookbook source that will be retrieved from the latest API of the Opscode Community Site
107
- # cookbook 'artifact', site: :opscode
108
- #
109
- # @example a cookbook source that will be retrieved from a Git server
64
+ # @example a cookbook dependency that will be retrieved from a Git server
110
65
  # cookbook 'artifact', git: 'git://github.com/RiotGames/artifact-cookbook.git'
111
66
  #
112
- # @example a cookbook source that will be retrieved from a Chef API (Chef Server)
113
- # cookbook 'artifact', chef_api: 'https://api.opscode.com/organizations/vialstudios',
114
- # node_name: 'reset', client_key: '/Users/reset/.chef/knife.rb'
115
- #
116
- # @example a cookbook source that will be retrieved from a Chef API using your Berkshelf config
117
- # cookbook 'artifact', chef_api: :config
118
- #
119
67
  # @overload cookbook(name, version_constraint, options = {})
120
68
  # @param [#to_s] name
121
69
  # @param [#to_s] version_constraint
122
- # @param [Hash] options
123
70
  #
124
71
  # @option options [Symbol, Array] :group
125
72
  # the group or groups that the cookbook belongs to
126
- # @option options [String, Symbol] :chef_api
127
- # a URL to a Chef API. Alternatively the symbol :config can be provided
128
- # which will instantiate this location with the values found in your
129
- # Berkshelf configuration.
130
- # @option options [String] :site
131
- # a URL pointing to a community API endpoint
132
73
  # @option options [String] :path
133
74
  # a filepath to the cookbook on your local disk
134
75
  # @option options [String] :git
135
76
  # the Git URL to clone
136
77
  #
137
- # @see ChefAPILocation
138
- # @see SiteLocation
139
78
  # @see PathLocation
140
79
  # @see GitLocation
141
80
  # @overload cookbook(name, options = {})
142
81
  # @param [#to_s] name
143
- # @param [Hash] options
144
82
  #
145
83
  # @option options [Symbol, Array] :group
146
84
  # the group or groups that the cookbook belongs to
147
- # @option options [String, Symbol] :chef_api
148
- # a URL to a Chef API. Alternatively the symbol :config can be provided
149
- # which will instantiate this location with the values found in your
150
- # Berkshelf configuration.
151
- # @option options [String] :site
152
- # a URL pointing to a community API endpoint
153
85
  # @option options [String] :path
154
86
  # a filepath to the cookbook on your local disk
155
87
  # @option options [String] :git
156
88
  # the Git URL to clone
157
89
  #
158
- # @see ChefAPILocation
159
- # @see SiteLocation
160
90
  # @see PathLocation
161
91
  # @see GitLocation
162
92
  def cookbook(*args)
@@ -170,7 +100,7 @@ module Berkshelf
170
100
  options[:group] += @@active_group
171
101
  end
172
102
 
173
- add_source(name, constraint, options)
103
+ add_dependency(name, constraint, options)
174
104
  end
175
105
 
176
106
  def group(*args)
@@ -179,8 +109,8 @@ module Berkshelf
179
109
  @@active_group = nil
180
110
  end
181
111
 
182
- # Use a Cookbook metadata file to determine additional cookbook sources to retrieve. All
183
- # sources found in the metadata will use the default locations set in the Berksfile (if any are set)
112
+ # Use a Cookbook metadata file to determine additional cookbook dependencies to retrieve. All
113
+ # dependencies found in the metadata will use the default locations set in the Berksfile (if any are set)
184
114
  # or the default locations defined by Berkshelf.
185
115
  #
186
116
  # @param [Hash] options
@@ -195,68 +125,85 @@ module Berkshelf
195
125
 
196
126
  name = metadata.name.presence || File.basename(File.expand_path(path))
197
127
 
198
- add_source(name, nil, { path: path, metadata: true })
128
+ add_dependency(name, nil, path: path, metadata: true)
199
129
  end
200
130
 
201
- # Add a 'Site' default location which will be used to resolve cookbook sources that do not
202
- # contain an explicit location.
203
- #
204
- # @note
205
- # specifying the symbol :opscode as the value of the site default location is an alias for the
206
- # latest API of the Opscode Community Site.
131
+ # Add a Berkshelf API source to use when building the index of known cookbooks. The indexes will be
132
+ # searched in the order they are added. If a cookbook is found in the first source then a cookbook
133
+ # in a second source would not be used.
207
134
  #
208
135
  # @example
209
- # site :opscode
210
- # site "http://cookbooks.opscode.com/api/v1/cookbooks"
136
+ # source "http://api.berkshelf.com"
137
+ # source "http://berks-api.riotgames.com"
211
138
  #
212
- # @param [String, Symbol] value
139
+ # @param [String] api_url
140
+ # url for the api to add
213
141
  #
214
- # @return [Hash]
215
- def site(value)
216
- add_location(:site, value)
142
+ # @raise [Berkshelf::InvalidSourceURI]
143
+ #
144
+ # @return [Array<SourceURI>]
145
+ def source(api_url)
146
+ new_source = Source.new(api_url)
147
+ @sources.push(new_source) unless @sources.include?(new_source)
217
148
  end
218
149
 
219
- # Add a 'Chef API' default location which will be used to resolve cookbook sources that do not
220
- # contain an explicit location.
221
- #
222
- # @note
223
- # specifying the symbol :config as the value of the chef_api default location will attempt to use the
224
- # contents of your Berkshelf configuration to find the Chef API to interact with.
225
- #
226
- # @example using the symbol :config to add a Chef API default location
227
- # chef_api :config
228
- #
229
- # @example using a URL, node_name, and client_key to add a Chef API default location
230
- # chef_api 'https://api.opscode.com/organizations/vialstudios', node_name: 'reset',
231
- # client_key: '/Users/reset/.chef/knife.rb'
232
- #
233
- # @param [String, Symbol] value
234
- # @param [Hash] options
150
+ # @return [Array<SourceURI>]
151
+ def sources
152
+ @sources.empty? ? self.class.default_sources : @sources
153
+ end
154
+
155
+ # @todo remove in Berkshelf 4.0
156
+ #
157
+ # @raise [Berkshelf::DeprecatedError]
158
+ def site(*args)
159
+ if args.first == :opscode
160
+ Berkshelf.formatter.deprecation "Your Berksfile contains a site location pointing to the Opscode Community " +
161
+ "Site (site :opscode). Site locations have been replaced by the source location. Change this to: " +
162
+ "'source \"http://api.berkshelf.com\" to remove this warning. For more information visit " +
163
+ "https://github.com/RiotGames/berkshelf/wiki/deprecated-locations"
164
+ source(DEFAULT_API_URL)
165
+ return
166
+ end
167
+
168
+ raise Berkshelf::DeprecatedError.new "Your Berksfile contains a site location. Site locations have been " +
169
+ " replaced by the source location. Please remove your site location and try again. For more information " +
170
+ " visit https://github.com/RiotGames/berkshelf/wiki/deprecated-locations"
171
+ end
172
+
173
+ # @todo remove in Berkshelf 4.0
235
174
  #
236
- # @return [Hash]
237
- def chef_api(value, options = {})
238
- add_location(:chef_api, value, options)
175
+ # @raise [Berkshelf::DeprecatedError]
176
+ def chef_api(*args)
177
+ raise Berkshelf::DeprecatedError.new "Your Berksfile contains a chef_api location. Chef API locations have " +
178
+ " been replaced by the source location. Please remove your site location and try again. For more " +
179
+ " information visit https://github.com/RiotGames/berkshelf/wiki/deprecated-locations"
239
180
  end
240
181
 
241
- # Add a source of the given name and constraint to the array of sources.
182
+ # Add a dependency of the given name and constraint to the array of dependencies.
242
183
  #
243
184
  # @param [String] name
244
- # the name of the source to add
185
+ # the name of the dependency to add
245
186
  # @param [String, Solve::Constraint] constraint
246
- # the constraint to lock the source to
247
- # @param [Hash] options
187
+ # the constraint to lock the dependency to
188
+ #
189
+ # @option options [Symbol, Array] :group
190
+ # the group or groups that the cookbook belongs to
191
+ # @option options [String] :path
192
+ # a filepath to the cookbook on your local disk
193
+ # @option options [String] :git
194
+ # the Git URL to clone
248
195
  #
249
- # @raise [DuplicateSourceDefined] if a source is added whose name conflicts
250
- # with a source who has already been added.
196
+ # @raise [DuplicateDependencyDefined] if a dependency is added whose name conflicts
197
+ # with a dependency who has already been added.
251
198
  #
252
- # @return [Array<Berkshelf::CookbookSource]
253
- def add_source(name, constraint = nil, options = {})
254
- if has_source?(name)
255
- # Only raise an exception if the source is a true duplicate
199
+ # @return [Array<Berkshelf::Dependency]
200
+ def add_dependency(name, constraint = nil, options = {})
201
+ if has_dependency?(name)
202
+ # Only raise an exception if the dependency is a true duplicate
256
203
  groups = (options[:group].nil? || options[:group].empty?) ? [:default] : options[:group]
257
- if !(@sources[name].groups & groups).empty?
258
- raise DuplicateSourceDefined,
259
- "Berksfile contains multiple sources named '#{name}'. Use only one, or put them in different groups."
204
+ if !(@dependencies[name].groups & groups).empty?
205
+ raise DuplicateDependencyDefined,
206
+ "Berksfile contains multiple entries named '#{name}'. Use only one, or put them in different groups."
260
207
  end
261
208
  end
262
209
 
@@ -266,47 +213,36 @@ module Berkshelf
266
213
 
267
214
  options[:constraint] = constraint
268
215
 
269
- @sources[name] = CookbookSource.new(self, name, options)
216
+ @dependencies[name] = Berkshelf::Dependency.new(self, name, options)
270
217
  end
271
218
 
272
- # @param [#to_s] source
273
- # the source to remove
219
+ # @param [#to_s] dependency
220
+ # the dependency to remove
274
221
  #
275
- # @return [Berkshelf::CookbookSource]
276
- def remove_source(source)
277
- @sources.delete(source.to_s)
222
+ # @return [Berkshelf::Dependency]
223
+ def remove_dependency(dependency)
224
+ @dependencies.delete(dependency.to_s)
278
225
  end
279
226
 
280
- # @param [#to_s] source
281
- # the source to check presence of
227
+ # @param [#to_s] dependency
228
+ # the dependency to check presence of
282
229
  #
283
230
  # @return [Boolean]
284
- def has_source?(source)
285
- @sources.has_key?(source.to_s)
231
+ def has_dependency?(dependency)
232
+ @dependencies.has_key?(dependency.to_s)
286
233
  end
287
234
 
288
- # The list of cookbook sources specified in this Berksfile
289
- #
290
- # @param [Array] sources
291
- # the list of sources to filter
292
- #
293
235
  # @option options [Symbol, Array] :except
294
- # group(s) to exclude to exclude from the returned Array of sources
236
+ # Group(s) to exclude which will cause any dependencies marked as a member of the
295
237
  # group to not be installed
296
238
  # @option options [Symbol, Array] :only
297
- # group(s) to include which will cause any sources marked as a member of the
239
+ # Group(s) to include which will cause any dependencies marked as a member of the
298
240
  # group to be installed and all others to be ignored
299
241
  # @option cookbooks [String, Array] :cookbooks
300
- # names of the cookbooks to retrieve sources for
301
- #
302
- # @raise [Berkshelf::ArgumentError]
303
- # if a value for both :except and :only is provided
242
+ # Names of the cookbooks to retrieve dependencies for
304
243
  #
305
- # @return [Array<Berkshelf::CookbookSource>]
306
- # the list of cookbook sources that match the given options
307
- def sources(options = {})
308
- l_sources = @sources.values
309
-
244
+ # @return [Array<Berkshelf::Dependency>]
245
+ def dependencies(options = {})
310
246
  cookbooks = Array(options[:cookbooks])
311
247
  except = Array(options[:except]).collect(&:to_sym)
312
248
  only = Array(options[:only]).collect(&:to_sym)
@@ -318,65 +254,65 @@ module Berkshelf
318
254
  if !except.empty? && !only.empty?
319
255
  Berkshelf.ui.warn 'Cookbooks were specified, ignoring :except and :only'
320
256
  end
321
- l_sources.select { |source| cookbooks.include?(source.name) }
257
+ @dependencies.values.select { |dependency| cookbooks.include?(dependency.name) }
322
258
  when !except.empty?
323
- l_sources.select { |source| (except & source.groups).empty? }
259
+ @dependencies.values.select { |dependency| (except & dependency.groups).empty? }
324
260
  when !only.empty?
325
- l_sources.select { |source| !(only & source.groups).empty? }
261
+ @dependencies.values.select { |dependency| !(only & dependency.groups).empty? }
326
262
  else
327
- l_sources
263
+ @dependencies.values
328
264
  end
329
265
  end
330
266
 
331
- # Find a source defined in this berksfile by name.
267
+ # Find a dependency defined in this berksfile by name.
332
268
  #
333
269
  # @param [String] name
334
- # the name of the cookbook source to search for
335
- # @return [Berkshelf::CookbookSource, nil]
336
- # the cookbook source, or nil if one does not exist
270
+ # the name of the cookbook dependency to search for
271
+ # @return [Berkshelf::Dependency, nil]
272
+ # the cookbook dependency, or nil if one does not exist
337
273
  def find(name)
338
- @sources[name]
274
+ @dependencies[name]
339
275
  end
340
276
 
341
277
  # @return [Hash]
342
- # a hash containing group names as keys and an array of CookbookSources
278
+ # a hash containing group names as keys and an array of Berkshelf::Dependencies
343
279
  # that are a member of that group as values
344
280
  #
345
281
  # Example:
346
282
  # {
347
283
  # nautilus: [
348
- # #<Berkshelf::CookbookSource: nginx (~> 1.0.0)>,
349
- # #<Berkshelf::CookbookSource: mysql (~> 1.2.4)>
284
+ # #<Berkshelf::Dependency: nginx (~> 1.0.0)>,
285
+ # #<Berkshelf::Dependency: mysql (~> 1.2.4)>
350
286
  # ],
351
287
  # skarner: [
352
- # #<Berkshelf::CookbookSource: nginx (~> 1.0.0)>
288
+ # #<Berkshelf::Dependency: nginx (~> 1.0.0)>
353
289
  # ]
354
290
  # }
355
291
  def groups
356
292
  {}.tap do |groups|
357
- sources.each do |source|
358
- source.groups.each do |group|
293
+ dependencies.each do |dependency|
294
+ dependency.groups.each do |group|
359
295
  groups[group] ||= []
360
- groups[group] << source
296
+ groups[group] << dependency
361
297
  end
362
298
  end
363
299
  end
364
300
  end
365
301
 
366
302
  # @param [String] name
367
- # name of the source to return
303
+ # name of the dependency to return
368
304
  #
369
- # @return [Berkshelf::CookbookSource]
305
+ # @return [Berkshelf::Dependency]
370
306
  def [](name)
371
- @sources[name]
307
+ @dependencies[name]
372
308
  end
373
- alias_method :get_source, :[]
309
+ alias_method :get_dependency, :[]
374
310
 
375
- # Install the sources listed in the Berksfile, respecting the locked
311
+ # Install the dependencies listed in the Berksfile, respecting the locked
376
312
  # versions in the Berksfile.lock.
377
313
  #
378
314
  # 1. Check that a lockfile exists. If a lockfile does not exist, all
379
- # sources are considered to be "unlocked". If a lockfile is specified, a
315
+ # dependencies are considered to be "unlocked". If a lockfile is specified, a
380
316
  # definition is created via the following algorithm:
381
317
  #
382
318
  # - For each source, see if there exists a locked version that still
@@ -390,98 +326,67 @@ module Berkshelf
390
326
  # - Remove any locked sources that no longer exist in the Berksfile
391
327
  # (i.e. a cookbook source was removed from the Berksfile).
392
328
  #
393
- # 2. Resolve the collection of locked and unlocked sources.
329
+ # 2. Resolve the collection of locked and unlocked dependencies.
394
330
  #
395
331
  # 3. Write out a new lockfile.
396
332
  #
397
333
  # @option options [Symbol, Array] :except
398
- # Group(s) to exclude which will cause any sources marked as a member of the
334
+ # Group(s) to exclude which will cause any dependencies marked as a member of the
399
335
  # group to not be installed
400
336
  # @option options [Symbol, Array] :only
401
- # Group(s) to include which will cause any sources marked as a member of the
337
+ # Group(s) to include which will cause any dependencies marked as a member of the
402
338
  # group to be installed and all others to be ignored
403
- # @option options [String] :path
404
- # a path to "vendor" the cached_cookbooks resolved by the resolver. Vendoring
405
- # is a technique for packaging all cookbooks resolved by a Berksfile.
406
- # @option options [Boolean] :update_lockfile (true)
407
- # a boolean method indicating whether we should update the lockfile
339
+ # @option cookbooks [String, Array] :cookbooks
340
+ # Names of the cookbooks to retrieve dependencies for
408
341
  #
409
- # @raise [Berkshelf::OutdatedCookbookSource]
342
+ # @raise [Berkshelf::OutdatedDependency]
410
343
  # if the lockfile constraints do not satisfy the Berskfile constraints
411
- # @raise [Berkshelf::ArgumentError]
412
- # if there are missing or conflicting options
413
344
  #
414
345
  # @return [Array<Berkshelf::CachedCookbook>]
415
346
  def install(options = {})
416
- local_sources = apply_lockfile(sources(options))
417
-
418
- resolver = resolve(local_sources)
419
- @cached_cookbooks = resolver[:solution]
420
- local_sources = resolver[:sources]
421
-
422
- verify_licenses!
423
-
424
- self.class.vendor(@cached_cookbooks, options[:path]) if options[:path]
425
-
426
- lockfile.update(local_sources) unless options[:update_lockfile] == false
427
-
428
- self.cached_cookbooks
347
+ Installer.new(self).run(options)
429
348
  end
430
349
 
431
350
  # @option options [Symbol, Array] :except
432
- # Group(s) to exclude which will cause any sources marked as a member of the
351
+ # Group(s) to exclude which will cause any dependencies marked as a member of the
433
352
  # group to not be installed
434
353
  # @option options [Symbol, Array] :only
435
- # Group(s) to include which will cause any sources marked as a member of the
354
+ # Group(s) to include which will cause any dependencies marked as a member of the
436
355
  # group to be installed and all others to be ignored
437
356
  # @option cookbooks [String, Array] :cookbooks
438
- # Names of the cookbooks to retrieve sources for
357
+ # Names of the cookbooks to retrieve dependencies for
439
358
  def update(options = {})
440
359
  validate_cookbook_names!(options)
441
360
 
442
361
  # Unlock any/all specified cookbooks
443
- sources(options).each { |source| lockfile.unlock(source) }
362
+ dependencies(options).each { |dependency| lockfile.unlock(dependency) }
444
363
 
445
364
  # NOTE: We intentionally do NOT pass options to the installer
446
365
  self.install
447
366
  end
448
367
 
449
- # Get a list of all the cookbooks which have newer versions found on the community
450
- # site versus what your current constraints allow
368
+ # List of all the cookbooks which have a newer version found at a source that satisfies
369
+ # the constraints of your dependencies
451
370
  #
452
371
  # @option options [Symbol, Array] :except
453
- # Group(s) to exclude which will cause any sources marked as a member of the
372
+ # Group(s) to exclude which will cause any dependencies marked as a member of the
454
373
  # group to not be installed
455
374
  # @option options [Symbol, Array] :only
456
- # Group(s) to include which will cause any sources marked as a member of the
375
+ # Group(s) to include which will cause any dependencies marked as a member of the
457
376
  # group to be installed and all others to be ignored
458
377
  # @option cookbooks [String, Array] :cookbooks
459
- # Names of the cookbooks to retrieve sources for
378
+ # Whitelist of cookbooks to to check for updated versions for
460
379
  #
461
380
  # @return [Hash]
462
381
  # a hash of cached cookbooks and their latest version. An empty hash is returned
463
- # if there are no newer cookbooks for any of your sources
382
+ # if there are no newer cookbooks for any of your dependencies
464
383
  #
465
384
  # @example
466
- # berksfile.outdated => {
385
+ # berksfile.outdated #=> {
467
386
  # #<CachedCookbook name="artifact"> => "0.11.2"
468
387
  # }
469
388
  def outdated(options = {})
470
- outdated = Hash.new
471
-
472
- sources(options).each do |cookbook|
473
- location = cookbook.location || Location.init(cookbook.name, cookbook.version_constraint, site: :opscode)
474
-
475
- if location.is_a?(SiteLocation)
476
- latest_version = location.latest_version
477
-
478
- unless cookbook.version_constraint.satisfies?(latest_version)
479
- outdated[cookbook] = latest_version
480
- end
481
- end
482
- end
483
-
484
- outdated
389
+ raise RuntimeError, "not yet implemented"
485
390
  end
486
391
 
487
392
  # Upload the cookbooks installed by this Berksfile
@@ -492,66 +397,40 @@ module Berkshelf
492
397
  # @option options [Boolean] :freeze (true)
493
398
  # Freeze the uploaded Cookbook on the Chef Server so that it cannot be overwritten
494
399
  # @option options [Symbol, Array] :except
495
- # Group(s) to exclude which will cause any sources marked as a member of the
400
+ # Group(s) to exclude which will cause any dependencies marked as a member of the
496
401
  # group to not be installed
497
402
  # @option options [Symbol, Array] :only
498
- # Group(s) to include which will cause any sources marked as a member of the
403
+ # Group(s) to include which will cause any dependencies marked as a member of the
499
404
  # group to be installed and all others to be ignored
500
405
  # @option options [String, Array] :cookbooks
501
- # Names of the cookbooks to retrieve sources for
406
+ # Names of the cookbooks to retrieve dependencies for
502
407
  # @option options [Hash] :ssl_verify (true)
503
408
  # Disable/Enable SSL verification during uploads
504
- # @option options [Boolean] :skip_dependencies (false)
505
- # Skip uploading dependent cookbook(s).
506
409
  # @option options [Boolean] :halt_on_frozen (false)
507
410
  # Raise a FrozenCookbook error if one of the cookbooks being uploaded is already located
508
411
  # on the remote Chef Server and frozen.
509
412
  # @option options [String] :server_url
510
413
  # An overriding Chef Server to upload the cookbooks to
511
- # @option options [String] :client_name
512
- # An overriding client name to use for connecting to the chef server
513
- # @option options [String] :client_key
514
- # An overriding client key to use for connecting to the chef server
515
414
  #
516
- # @raise [UploadFailure]
415
+ # @raise [Berkshelf::UploadFailure]
517
416
  # if you are uploading cookbooks with an invalid or not-specified client key
417
+ # @raise [Berkshelf::DependencyNotFound]
418
+ # if one of the given cookbooks is not a dependency defined in the Berksfile
518
419
  # @raise [Berkshelf::FrozenCookbook]
519
420
  # if an attempt to upload a cookbook which has been frozen on the target server is made
520
421
  # and the :halt_on_frozen option was true
521
422
  def upload(options = {})
522
- options = options.reverse_merge(force: false, freeze: true, skip_dependencies: false, halt_on_frozen: false, update_lockfile: false, validate: true)
423
+ options = options.reverse_merge(force: false, freeze: true, halt_on_frozen: false, cookbooks: [])
523
424
 
524
- cached_cookbooks = install(options)
525
- upload_opts = options.slice(:force, :freeze, :validate)
526
- conn = ridley_connection(options)
527
-
528
- cached_cookbooks.each do |cookbook|
529
- Berkshelf.formatter.upload(cookbook.cookbook_name, cookbook.version, conn.server_url)
530
- validate_files!(cookbook)
531
-
532
- begin
533
- conn.cookbook.upload(cookbook.path, upload_opts.merge(name: cookbook.cookbook_name))
534
- rescue Ridley::Errors::FrozenCookbook => ex
535
- if options[:halt_on_frozen]
536
- raise Berkshelf::FrozenCookbook, ex
537
- end
425
+ options[:cookbooks].each do |cookbook|
426
+ unless dependency = find(cookbook)
427
+ raise DependencyNotFound, "Failed to upload cookbook '#{cookbook}'. Not defined in Berksfile."
538
428
  end
539
429
  end
540
430
 
541
- if options[:skip_dependencies]
542
- missing_cookbooks = options.fetch(:cookbooks, nil) - cached_cookbooks.map(&:cookbook_name)
543
- unless missing_cookbooks.empty?
544
- msg = "Unable to upload cookbooks: #{missing_cookbooks.sort.join(', ')}\n"
545
- msg << "Specified cookbooks must be defined within the Berkshelf file when using the"
546
- msg << " `--skip-dependencies` option"
547
- raise ExplicitCookbookNotFound.new(msg)
548
- end
549
- end
550
- rescue Ridley::Errors::RidleyError => ex
551
- log_exception(ex)
552
- raise ChefConnectionError, ex # todo implement
553
- ensure
554
- conn.terminate if conn && conn.alive?
431
+ cached_cookbooks = install(options)
432
+ cached_cookbooks = filter_to_upload(cached_cookbooks, options[:cookbooks]) if options[:cookbooks]
433
+ do_upload(cached_cookbooks, options)
555
434
  end
556
435
 
557
436
  # Resolve this Berksfile and apply the locks found in the generated Berksfile.lock to the
@@ -562,38 +441,24 @@ module Berkshelf
562
441
  # @option options [Hash] :ssl_verify (true)
563
442
  # Disable/Enable SSL verification during uploads
564
443
  #
565
- # @raise [EnvironmentNotFound] if the target environment was not found
566
- # @raise [ChefConnectionError] if you are locking cookbooks with an invalid or not-specified client configuration
567
- # @raise [EnvironmentFileNotFound] if you are uploading environment from local file
444
+ # @raise [EnvironmentNotFound]
445
+ # if the target environment was not found
446
+ # @raise [ChefConnectionError]
447
+ # if you are locking cookbooks with an invalid or not-specified client configuration
568
448
  def apply(environment_name, options = {})
569
- conn = ridley_connection(options)
570
-
571
- if options[:from_file]
572
- environment_path = options[:from_file]
573
- begin
574
- environment = conn.environment.from_file(environment_path)
575
- rescue # Ridley::Errors::RidleyError => ex
576
- raise EnvironmentFileNotFound, "Local environment file #{environment_path} not found."
449
+ ridley_connection(options) do |conn|
450
+ unless environment = conn.environment.find(environment_name)
451
+ raise EnvironmentNotFound.new(environment_name)
577
452
  end
578
- else
579
- environment = conn.environment.find(environment_name)
580
- end
581
453
 
582
- if environment
583
454
  install
584
455
 
585
456
  environment.cookbook_versions = {}.tap do |cookbook_versions|
586
- lockfile.sources.each { |source| cookbook_versions[source.name] = "= #{source.locked_version.to_s}" }
457
+ lockfile.dependencies.each { |dependency| cookbook_versions[dependency.name] = dependency.locked_version }
587
458
  end
588
459
 
589
460
  environment.save
590
- else
591
- raise EnvironmentNotFound.new(environment_name)
592
461
  end
593
- rescue Ridley::Errors::RidleyError => ex
594
- raise ChefConnectionError, ex
595
- ensure
596
- conn.terminate if conn && conn.alive?
597
462
  end
598
463
 
599
464
  # Package the given cookbook for distribution outside of berkshelf. If the
@@ -607,8 +472,6 @@ module Berkshelf
607
472
  #
608
473
  # @option options [String] :output
609
474
  # the path to output the tarball
610
- # @option options [Boolean] :skip_dependencies
611
- # package cookbook dependencies as well
612
475
  # @option options [Boolean] :ignore_chefignore
613
476
  # do not apply the chefignore file to the packed cookbooks
614
477
  #
@@ -616,29 +479,25 @@ module Berkshelf
616
479
  # the path to the package
617
480
  def package(name = nil, options = {})
618
481
  tar_name = "#{name || 'package'}.tar.gz"
619
- output = File.expand_path(File.join(options[:output], tar_name))
482
+ output = File.expand_path(File.join(options[:output], tar_name))
620
483
 
621
- unless name.nil?
622
- source = self.find(name)
623
- raise CookbookNotFound, "Cookbook '#{name}' is not in your Berksfile" unless source
484
+ cached_cookbooks = unless name.nil?
485
+ unless dependency = find(name)
486
+ raise CookbookNotFound, "Cookbook '#{name}' is not in your Berksfile"
487
+ end
624
488
 
625
- package = Berkshelf.ui.mute {
626
- self.resolve(source, options)[:solution]
627
- }
489
+ options[:cookbooks] = name
490
+ Berkshelf.ui.mute { install(options) }
628
491
  else
629
- package = Berkshelf.ui.mute {
630
- self.resolve(sources, options)[:solution]
631
- }
492
+ Berkshelf.ui.mute { install(options) }
632
493
  end
633
494
 
634
- package.each do |cookbook|
635
- validate_files!(cookbook)
636
- end
495
+ cached_cookbooks.each { |cookbook| validate_files!(cookbook) }
637
496
 
638
497
  Dir.mktmpdir do |tmp|
639
- package.each do |cached_cookbook|
640
- path = cached_cookbook.path.to_s
641
- destination = File.join(tmp, cached_cookbook.cookbook_name)
498
+ cached_cookbooks.each do |cookbook|
499
+ path = cookbook.path.to_s
500
+ destination = File.join(tmp, cookbook.cookbook_name)
642
501
 
643
502
  FileUtils.cp_r(path, destination)
644
503
 
@@ -663,25 +522,6 @@ module Berkshelf
663
522
  output
664
523
  end
665
524
 
666
- # Finds a solution for the Berksfile and returns an array of CachedCookbooks.
667
- #
668
- # @param [Array<Berkshelf::CookbookSource>] sources
669
- # Array of cookbook sources to resolve
670
- #
671
- # @option options [Boolean] :skip_dependencies
672
- # Skip resolving of dependencies
673
- #
674
- # @return [Array<Berkshelf::CachedCookbooks>]
675
- def resolve(sources = [], options = {})
676
- resolver = Resolver.new(
677
- self,
678
- sources: sources,
679
- skip_dependencies: options[:skip_dependencies]
680
- )
681
-
682
- { solution: resolver.resolve, sources: resolver.sources }
683
- end
684
-
685
525
  # Get the lockfile corresponding to this Berksfile. This is necessary because
686
526
  # the user can specify a different path to the Berksfile. So assuming the lockfile
687
527
  # is named "Berksfile.lock" is a poor assumption.
@@ -695,12 +535,55 @@ module Berkshelf
695
535
 
696
536
  private
697
537
 
698
- def ridley_connection(options = {})
538
+ def do_upload(cookbooks, options = {})
539
+ upload_opts = options.slice(:force, :freeze)
540
+
541
+ ridley_connection(options) do |conn|
542
+ cookbooks.each do |cookbook|
543
+ Berkshelf.formatter.upload(cookbook.cookbook_name, cookbook.version, conn.server_url)
544
+ validate_files!(cookbook)
545
+
546
+ begin
547
+ conn.cookbook.upload(cookbook.path, upload_opts.merge(name: cookbook.cookbook_name))
548
+ rescue Ridley::Errors::FrozenCookbook => ex
549
+ if options[:halt_on_frozen]
550
+ raise Berkshelf::FrozenCookbook, ex
551
+ end
552
+ end
553
+ end
554
+ end
555
+ end
556
+
557
+ # Filter the cookbooks to upload based on a set of given names. The dependencies of a cookbook
558
+ # will always be included in the filtered results even if the dependency's name is not
559
+ # explicitly provided.
560
+ #
561
+ # @param [Array<Berkshelf::CachedCookbooks>] cookbooks
562
+ # set of cookbooks to filter
563
+ # @param [Array<String>] names
564
+ # names of cookbooks to include in the filtered results
565
+ #
566
+ # @return [Array<Berkshelf::CachedCookbooks]
567
+ def filter_to_upload(cookbooks, names)
568
+ unless names.empty?
569
+ explicit = cookbooks.select { |cookbook| names.include?(cookbook.cookbook_name) }
570
+ explicit.each do |cookbook|
571
+ cookbook.dependencies.each do |name, version|
572
+ explicit += cookbooks.select { |cookbook| cookbook.cookbook_name == name }
573
+ end
574
+ end
575
+ cookbooks = explicit.uniq
576
+ end
577
+ cookbooks
578
+ end
579
+
580
+ # @raise [Berkshelf::ChefConnectionError]
581
+ def ridley_connection(options = {}, &block)
699
582
  ridley_options = options.slice(:ssl)
700
- ridley_options[:server_url] = options[:server_url] || Berkshelf::Config.instance.chef.chef_server_url
701
- ridley_options[:client_name] = options[:client_name] || Berkshelf::Config.instance.chef.node_name
702
- ridley_options[:client_key] = options[:client_key] || Berkshelf::Config.instance.chef.client_key
703
- ridley_options[:ssl] = { verify: (options[:ssl_verify] || Berkshelf::Config.instance.ssl.verify) }
583
+ ridley_options[:server_url] = options[:server_url] || Berkshelf.config.chef.chef_server_url
584
+ ridley_options[:client_name] = Berkshelf.config.chef.node_name
585
+ ridley_options[:client_key] = Berkshelf.config.chef.client_key
586
+ ridley_options[:ssl] = { verify: (options[:ssl_verify] || Berkshelf.config.ssl.verify) }
704
587
 
705
588
  unless ridley_options[:server_url].present?
706
589
  raise ChefConnectionError, 'Missing required attribute in your Berkshelf configuration: chef.server_url'
@@ -714,12 +597,10 @@ module Berkshelf
714
597
  raise ChefConnectionError, 'Missing required attribute in your Berkshelf configuration: chef.client_key'
715
598
  end
716
599
 
717
- Ridley.new(ridley_options)
718
- end
719
-
720
- def descendant_directory?(candidate, parent)
721
- hack = FileUtils::Entry_.new('/tmp')
722
- hack.send(:descendant_diretory?, candidate, parent)
600
+ Ridley.open(ridley_options, &block)
601
+ rescue Ridley::Errors::RidleyError => ex
602
+ log_exception(ex)
603
+ raise ChefConnectionError, ex # todo implement
723
604
  end
724
605
 
725
606
  # Determine if any cookbooks were specified that aren't in our shelf.
@@ -730,52 +611,12 @@ module Berkshelf
730
611
  # @raise [Berkshelf::CookbookNotFound]
731
612
  # if a cookbook name is given that does not exist
732
613
  def validate_cookbook_names!(options = {})
733
- missing = (Array(options[:cookbooks]) - sources.map(&:name))
614
+ missing = (Array(options[:cookbooks]) - dependencies.map(&:name))
734
615
  unless missing.empty?
735
616
  raise Berkshelf::CookbookNotFound,
736
- "Could not find cookbooks #{missing.collect{ |c| "'#{c}'" }.join(', ')} " +
737
- "in any of the sources. #{missing.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
738
- end
739
- end
740
-
741
- # The list of sources "locked" by the lockfile.
742
- #
743
- # @return [Array<Berkshelf::CookbookSource>]
744
- # the list of sources in this lockfile
745
- def locked_sources
746
- lockfile.sources
747
- end
748
-
749
- # Merge the locked sources against the given sources.
750
- #
751
- # For each the given sources, check if there's a locked version that
752
- # still satisfies the version constraint. If it does, "lock" that source
753
- # because we should just use the locked version.
754
- #
755
- # If a locked source exists, but doesn't satisfy the constraint, raise a
756
- # {Berkshelf::OutdatedCookbookSource} and tell the user to run update.
757
- def apply_lockfile(sources = [])
758
- sources.collect do |source|
759
- source_from_lockfile(source) || source
760
- end
761
- end
762
-
763
- def source_from_lockfile(source)
764
- locked_source = lockfile.find(source)
765
-
766
- return nil unless locked_source
767
-
768
- # If there's a locked_version, make sure it's still satisfied
769
- # by the constraint
770
- if locked_source.locked_version
771
- unless source.version_constraint.satisfies?(locked_source.locked_version)
772
- raise Berkshelf::OutdatedCookbookSource.new(locked_source, source)
773
- end
617
+ "Could not find cookbook(s) #{missing.collect{ |c| "'#{c}'" }.join(', ')} " +
618
+ "in any of the configured dependencies. #{missing.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
774
619
  end
775
-
776
- # Update to the new constraint (it might have changed, but still be satisfied)
777
- locked_source.version_constraint = source.version_constraint
778
- locked_source
779
620
  end
780
621
 
781
622
  # Validate that the given cookbook does not have "bad" files. Currently
@@ -793,34 +634,5 @@ module Berkshelf
793
634
 
794
635
  raise Berkshelf::InvalidCookbookFiles.new(cookbook, files) unless files.empty?
795
636
  end
796
-
797
- # Verify that the licenses of all the cached cookbooks fall in the realm of
798
- # allowed licenses from the Berkshelf Config.
799
- #
800
- # @raise [Berkshelf::LicenseNotAllowed]
801
- # if the license is not permitted and `raise_license_exception` is true
802
- def verify_licenses!
803
- licenses = Array(Berkshelf::Config.instance.allowed_licenses)
804
- return if licenses.empty?
805
-
806
- sources.each do |source|
807
- next if source.location.is_a?(Berkshelf::PathLocation)
808
- cached = source.cached_cookbook
809
-
810
- begin
811
- unless licenses.include?(cached.metadata.license)
812
- raise Berkshelf::LicenseNotAllowed.new(cached)
813
- end
814
- rescue Berkshelf::LicenseNotAllowed => e
815
- if Berkshelf::Config.instance.raise_license_exception
816
- FileUtils.rm_rf(cached.path)
817
- raise
818
- end
819
-
820
- Berkshelf.ui.warn(e.to_s)
821
- end
822
- end
823
- end
824
-
825
637
  end
826
638
  end