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.
- data/.ruby-version +1 -1
- data/.travis.yml +4 -1
- data/CHANGELOG.md +2 -26
- data/Gemfile +12 -2
- data/README.md +9 -1
- data/berkshelf.gemspec +9 -18
- data/bin/berks +3 -13
- data/features/apply_command.feature +11 -9
- data/features/berksfile.feature +8 -10
- data/features/config.feature +1 -2
- data/features/configure_command.feature +13 -14
- data/features/contingent_command.feature +13 -1
- data/features/cookbook_command.feature +2 -4
- data/features/groups_install.feature +10 -2
- data/features/help.feature +1 -1
- data/features/init_command.feature +5 -7
- data/features/install_command.feature +157 -228
- data/features/json_formatter.feature +27 -15
- data/features/licenses.feature +18 -12
- data/features/list_command.feature +6 -1
- data/features/lockfile.feature +116 -72
- data/features/outdated_command.feature +3 -47
- data/features/package_command.feature +10 -7
- data/features/shelf/show.feature +2 -2
- data/features/shelf/uninstall.feature +2 -2
- data/features/show_command.feature +10 -3
- data/features/step_definitions/chef/config_steps.rb +12 -0
- data/features/step_definitions/chef_server_steps.rb +16 -16
- data/features/step_definitions/cli_steps.rb +3 -79
- data/features/step_definitions/config_steps.rb +43 -0
- data/features/step_definitions/environment_steps.rb +7 -0
- data/features/step_definitions/filesystem_steps.rb +12 -57
- data/features/step_definitions/gem_steps.rb +1 -2
- data/features/step_definitions/json_steps.rb +3 -1
- data/features/step_definitions/lockfile_steps.rb +4 -0
- data/features/step_definitions/utility_steps.rb +0 -19
- data/features/support/aruba.rb +12 -0
- data/features/support/env.rb +52 -57
- data/features/update_command.feature +37 -23
- data/features/upload_command.feature +96 -160
- data/generator_files/Berksfile.erb +2 -1
- data/generator_files/Vagrantfile.erb +3 -0
- data/generator_files/default_test.rb.erb +1 -1
- data/generator_files/helpers.rb.erb +1 -1
- data/lib/berkshelf.rb +43 -24
- data/lib/berkshelf/api_client.rb +67 -0
- data/lib/berkshelf/api_client/remote_cookbook.rb +42 -0
- data/lib/berkshelf/berksfile.rb +232 -420
- data/lib/berkshelf/cached_cookbook.rb +22 -10
- data/lib/berkshelf/chef/config.rb +1 -0
- data/lib/berkshelf/cli.rb +66 -68
- data/lib/berkshelf/commands/shelf.rb +1 -1
- data/lib/berkshelf/community_rest.rb +10 -17
- data/lib/berkshelf/config.rb +23 -27
- data/lib/berkshelf/cookbook_generator.rb +3 -4
- data/lib/berkshelf/cookbook_store.rb +74 -17
- data/lib/berkshelf/core_ext/file.rb +2 -2
- data/lib/berkshelf/core_ext/pathname.rb +7 -5
- data/lib/berkshelf/{cookbook_source.rb → dependency.rb} +47 -67
- data/lib/berkshelf/downloader.rb +49 -106
- data/lib/berkshelf/errors.rb +64 -71
- data/lib/berkshelf/formatters.rb +11 -9
- data/lib/berkshelf/formatters/human_readable.rb +9 -9
- data/lib/berkshelf/formatters/json.rb +14 -4
- data/lib/berkshelf/init_generator.rb +3 -3
- data/lib/berkshelf/installer.rb +136 -0
- data/lib/berkshelf/location.rb +91 -131
- data/lib/berkshelf/locations/git_location.rb +9 -11
- data/lib/berkshelf/locations/github_location.rb +1 -1
- data/lib/berkshelf/locations/path_location.rb +10 -27
- data/lib/berkshelf/lockfile.rb +92 -70
- data/lib/berkshelf/logger.rb +4 -7
- data/lib/berkshelf/mixin/config.rb +21 -4
- data/lib/berkshelf/resolver.rb +60 -150
- data/lib/berkshelf/resolver/graph.rb +44 -0
- data/lib/berkshelf/source.rb +55 -0
- data/lib/berkshelf/source_uri.rb +38 -0
- data/lib/berkshelf/version.rb +1 -1
- data/spec/config/knife.rb +1 -1
- data/spec/fixtures/cassettes/Berkshelf_Resolver/_initialize/adds_the_dependencies_of_the_dependency_as_dependencies.yml +3694 -0
- data/spec/fixtures/cookbooks/example_cookbook/Berksfile.lock +1 -1
- data/spec/fixtures/lockfiles/default.lock +1 -1
- data/spec/spec_helper.rb +20 -121
- data/spec/support/chef_api.rb +3 -4
- data/spec/support/chef_server.rb +20 -11
- data/spec/support/git.rb +127 -0
- data/spec/support/kitchen.rb +12 -0
- data/spec/support/path_helpers.rb +69 -0
- data/spec/unit/berkshelf/api_client/remote_cookbook_spec.rb +23 -0
- data/spec/unit/berkshelf/api_client_spec.rb +57 -0
- data/spec/unit/berkshelf/berksfile_spec.rb +206 -324
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +73 -38
- data/spec/unit/berkshelf/community_rest_spec.rb +30 -71
- data/spec/unit/berkshelf/config_spec.rb +3 -14
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +1 -2
- data/spec/unit/berkshelf/cookbook_store_spec.rb +12 -7
- data/spec/unit/berkshelf/dependency_spec.rb +285 -0
- data/spec/unit/berkshelf/downloader_spec.rb +4 -183
- data/spec/unit/berkshelf/formatters/null_spec.rb +1 -1
- data/spec/unit/berkshelf/formatters_spec.rb +4 -2
- data/spec/unit/berkshelf/git_spec.rb +15 -15
- data/spec/unit/berkshelf/installer_spec.rb +39 -0
- data/spec/unit/berkshelf/location_spec.rb +87 -114
- data/spec/unit/berkshelf/locations/git_location_spec.rb +41 -53
- data/spec/unit/berkshelf/locations/path_location_spec.rb +13 -23
- data/spec/unit/berkshelf/lockfile_spec.rb +38 -40
- data/spec/unit/berkshelf/resolver/graph_spec.rb +44 -0
- data/spec/unit/berkshelf/resolver_spec.rb +34 -83
- data/spec/unit/berkshelf/source_spec.rb +23 -0
- data/spec/unit/berkshelf/source_uri_spec.rb +29 -0
- metadata +149 -188
- checksums.yaml +0 -7
- data/features/default_locations.feature +0 -127
- data/features/step_definitions/berksfile_steps.rb +0 -8
- data/features/step_definitions/configure_cli_steps.rb +0 -19
- data/features/vendor_install.feature +0 -19
- data/lib/berkshelf/core_ext/openuri.rb +0 -36
- data/lib/berkshelf/core_ext/rbzip2.rb +0 -8
- data/lib/berkshelf/locations/chef_api_location.rb +0 -228
- data/lib/berkshelf/locations/site_location.rb +0 -92
- data/lib/berkshelf/test.rb +0 -35
- data/spec/knife.rb.sample +0 -12
- data/spec/support/test_generators.rb +0 -27
- data/spec/unit/berkshelf/cli_spec.rb +0 -16
- data/spec/unit/berkshelf/cookbook_source_spec.rb +0 -358
- data/spec/unit/berkshelf/core_ext/pathname_spec.rb +0 -46
- data/spec/unit/berkshelf/locations/chef_api_location_spec.rb +0 -139
- data/spec/unit/berkshelf/locations/site_location_spec.rb +0 -19
@@ -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.
|
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
|
data/lib/berkshelf.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
require '
|
1
|
+
require 'buff/extensions'
|
2
2
|
require 'archive/tar/minitar'
|
3
|
-
require '
|
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
|
-
|
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
|
-
|
72
|
+
Berkshelf::Chef::Config.instance
|
61
73
|
end
|
62
74
|
|
63
|
-
#
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
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/
|
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
|
data/lib/berkshelf/berksfile.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
-
@
|
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
|
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
|
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
|
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
|
-
|
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
|
183
|
-
#
|
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
|
-
|
128
|
+
add_dependency(name, nil, path: path, metadata: true)
|
199
129
|
end
|
200
130
|
|
201
|
-
# Add a
|
202
|
-
#
|
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
|
-
#
|
210
|
-
#
|
136
|
+
# source "http://api.berkshelf.com"
|
137
|
+
# source "http://berks-api.riotgames.com"
|
211
138
|
#
|
212
|
-
# @param [String
|
139
|
+
# @param [String] api_url
|
140
|
+
# url for the api to add
|
213
141
|
#
|
214
|
-
# @
|
215
|
-
|
216
|
-
|
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
|
-
#
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
#
|
225
|
-
#
|
226
|
-
# @
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
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
|
-
# @
|
237
|
-
def chef_api(
|
238
|
-
|
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
|
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
|
185
|
+
# the name of the dependency to add
|
245
186
|
# @param [String, Solve::Constraint] constraint
|
246
|
-
# the constraint to lock the
|
247
|
-
#
|
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 [
|
250
|
-
# with a
|
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::
|
253
|
-
def
|
254
|
-
if
|
255
|
-
# Only raise an exception if the
|
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 !(@
|
258
|
-
raise
|
259
|
-
"Berksfile contains multiple
|
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
|
-
@
|
216
|
+
@dependencies[name] = Berkshelf::Dependency.new(self, name, options)
|
270
217
|
end
|
271
218
|
|
272
|
-
# @param [#to_s]
|
273
|
-
# the
|
219
|
+
# @param [#to_s] dependency
|
220
|
+
# the dependency to remove
|
274
221
|
#
|
275
|
-
# @return [Berkshelf::
|
276
|
-
def
|
277
|
-
@
|
222
|
+
# @return [Berkshelf::Dependency]
|
223
|
+
def remove_dependency(dependency)
|
224
|
+
@dependencies.delete(dependency.to_s)
|
278
225
|
end
|
279
226
|
|
280
|
-
# @param [#to_s]
|
281
|
-
# the
|
227
|
+
# @param [#to_s] dependency
|
228
|
+
# the dependency to check presence of
|
282
229
|
#
|
283
230
|
# @return [Boolean]
|
284
|
-
def
|
285
|
-
@
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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::
|
306
|
-
|
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
|
-
|
257
|
+
@dependencies.values.select { |dependency| cookbooks.include?(dependency.name) }
|
322
258
|
when !except.empty?
|
323
|
-
|
259
|
+
@dependencies.values.select { |dependency| (except & dependency.groups).empty? }
|
324
260
|
when !only.empty?
|
325
|
-
|
261
|
+
@dependencies.values.select { |dependency| !(only & dependency.groups).empty? }
|
326
262
|
else
|
327
|
-
|
263
|
+
@dependencies.values
|
328
264
|
end
|
329
265
|
end
|
330
266
|
|
331
|
-
# Find a
|
267
|
+
# Find a dependency defined in this berksfile by name.
|
332
268
|
#
|
333
269
|
# @param [String] name
|
334
|
-
# the name of the cookbook
|
335
|
-
# @return [Berkshelf::
|
336
|
-
# the cookbook
|
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
|
-
@
|
274
|
+
@dependencies[name]
|
339
275
|
end
|
340
276
|
|
341
277
|
# @return [Hash]
|
342
|
-
# a hash containing group names as keys and an array of
|
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::
|
349
|
-
# #<Berkshelf::
|
284
|
+
# #<Berkshelf::Dependency: nginx (~> 1.0.0)>,
|
285
|
+
# #<Berkshelf::Dependency: mysql (~> 1.2.4)>
|
350
286
|
# ],
|
351
287
|
# skarner: [
|
352
|
-
# #<Berkshelf::
|
288
|
+
# #<Berkshelf::Dependency: nginx (~> 1.0.0)>
|
353
289
|
# ]
|
354
290
|
# }
|
355
291
|
def groups
|
356
292
|
{}.tap do |groups|
|
357
|
-
|
358
|
-
|
293
|
+
dependencies.each do |dependency|
|
294
|
+
dependency.groups.each do |group|
|
359
295
|
groups[group] ||= []
|
360
|
-
groups[group] <<
|
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
|
303
|
+
# name of the dependency to return
|
368
304
|
#
|
369
|
-
# @return [Berkshelf::
|
305
|
+
# @return [Berkshelf::Dependency]
|
370
306
|
def [](name)
|
371
|
-
@
|
307
|
+
@dependencies[name]
|
372
308
|
end
|
373
|
-
alias_method :
|
309
|
+
alias_method :get_dependency, :[]
|
374
310
|
|
375
|
-
# Install the
|
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
|
-
#
|
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
|
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
|
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
|
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
|
404
|
-
#
|
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::
|
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
|
-
|
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
|
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
|
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
|
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
|
-
|
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
|
-
#
|
450
|
-
#
|
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
|
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
|
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
|
-
#
|
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
|
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
|
-
|
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
|
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
|
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
|
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,
|
423
|
+
options = options.reverse_merge(force: false, freeze: true, halt_on_frozen: false, cookbooks: [])
|
523
424
|
|
524
|
-
|
525
|
-
|
526
|
-
|
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
|
-
|
542
|
-
|
543
|
-
|
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]
|
566
|
-
#
|
567
|
-
# @raise [
|
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
|
-
|
570
|
-
|
571
|
-
|
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.
|
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
|
482
|
+
output = File.expand_path(File.join(options[:output], tar_name))
|
620
483
|
|
621
|
-
unless name.nil?
|
622
|
-
|
623
|
-
|
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
|
-
|
626
|
-
|
627
|
-
}
|
489
|
+
options[:cookbooks] = name
|
490
|
+
Berkshelf.ui.mute { install(options) }
|
628
491
|
else
|
629
|
-
|
630
|
-
self.resolve(sources, options)[:solution]
|
631
|
-
}
|
492
|
+
Berkshelf.ui.mute { install(options) }
|
632
493
|
end
|
633
494
|
|
634
|
-
|
635
|
-
validate_files!(cookbook)
|
636
|
-
end
|
495
|
+
cached_cookbooks.each { |cookbook| validate_files!(cookbook) }
|
637
496
|
|
638
497
|
Dir.mktmpdir do |tmp|
|
639
|
-
|
640
|
-
path
|
641
|
-
destination = File.join(tmp,
|
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
|
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
|
701
|
-
ridley_options[:client_name] =
|
702
|
-
ridley_options[:client_key] =
|
703
|
-
ridley_options[:ssl] = { verify: (options[:ssl_verify] || Berkshelf
|
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.
|
718
|
-
|
719
|
-
|
720
|
-
|
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]) -
|
614
|
+
missing = (Array(options[:cookbooks]) - dependencies.map(&:name))
|
734
615
|
unless missing.empty?
|
735
616
|
raise Berkshelf::CookbookNotFound,
|
736
|
-
"Could not find
|
737
|
-
"in any of the
|
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
|