berkshelf 2.0.18 → 3.0.0.beta1

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