berkshelf 1.4.6 → 2.0.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG.md +1 -5
- data/CONTRIBUTING.md +3 -1
- data/Gemfile +11 -1
- data/README.md +16 -0
- data/Thorfile +3 -1
- data/berkshelf.gemspec +26 -38
- data/features/apply_command.feature +32 -0
- data/features/configure_command.feature +31 -0
- data/features/contingent_command.feature +5 -5
- data/features/default_locations.feature +2 -2
- data/features/groups_install.feature +19 -20
- data/features/info_command.feature +13 -13
- data/features/install_command.feature +86 -83
- data/features/json_formatter.feature +60 -23
- data/features/list_command.feature +5 -11
- data/features/lockfile.feature +286 -6
- data/features/open_command.feature +8 -4
- data/features/outdated_command.feature +8 -15
- data/features/package_command.feature +39 -0
- data/features/show_command.feature +8 -9
- data/features/step_definitions/chef_server_steps.rb +20 -2
- data/features/step_definitions/cli_steps.rb +10 -2
- data/features/step_definitions/configure_cli_steps.rb +7 -0
- data/features/step_definitions/filesystem_steps.rb +19 -14
- data/features/step_definitions/json_steps.rb +22 -5
- data/features/step_definitions/utility_steps.rb +13 -1
- data/features/support/env.rb +10 -23
- data/features/update_command.feature +105 -24
- data/features/upload_command.feature +0 -14
- data/features/vendor_install.feature +3 -3
- data/generator_files/Vagrantfile.erb +11 -11
- data/lib/berkshelf.rb +6 -5
- data/lib/berkshelf/berksfile.rb +267 -99
- data/lib/berkshelf/cli.rb +70 -34
- data/lib/berkshelf/cli_commands/test_command.rb +11 -0
- data/lib/berkshelf/community_rest.rb +1 -1
- data/lib/berkshelf/config.rb +19 -2
- data/lib/berkshelf/cookbook_source.rb +41 -12
- data/lib/berkshelf/cookbook_store.rb +8 -4
- data/lib/berkshelf/errors.rb +61 -29
- data/lib/berkshelf/formatters.rb +13 -19
- data/lib/berkshelf/formatters/human_readable.rb +8 -0
- data/lib/berkshelf/formatters/json.rb +12 -1
- data/lib/berkshelf/formatters/null.rb +23 -0
- data/lib/berkshelf/init_generator.rb +22 -11
- data/lib/berkshelf/location.rb +8 -10
- data/lib/berkshelf/locations/chef_api_location.rb +4 -5
- data/lib/berkshelf/locations/git_location.rb +14 -12
- data/lib/berkshelf/locations/path_location.rb +16 -1
- data/lib/berkshelf/locations/site_location.rb +1 -3
- data/lib/berkshelf/lockfile.rb +230 -33
- data/lib/berkshelf/resolver.rb +2 -1
- data/lib/berkshelf/ui.rb +4 -8
- data/lib/berkshelf/version.rb +1 -1
- data/lib/thor/monkies/shell.rb +2 -5
- data/spec/fixtures/cassettes/Berkshelf_Resolver/{ClassMethods/_initialize → _initialize}/adds_the_dependencies_of_the_source_as_sources.yml +0 -0
- data/spec/fixtures/cookbooks/example_cookbook/.gitignore +2 -0
- data/spec/fixtures/cookbooks/example_cookbook/.kitchen.yml +26 -0
- data/spec/fixtures/cookbooks/example_cookbook/Berksfile.lock +5 -0
- data/spec/fixtures/lockfiles/default.lock +11 -0
- data/spec/{config/knife.rb → knife.rb.sample} +2 -2
- data/spec/spec_helper.rb +15 -3
- data/spec/support/chef_api.rb +19 -5
- data/spec/support/chef_server.rb +4 -3
- data/spec/support/knife.rb +18 -0
- data/spec/unit/berkshelf/berksfile_spec.rb +332 -235
- data/spec/unit/berkshelf/cached_cookbook_spec.rb +40 -42
- data/spec/unit/berkshelf/chef/cookbook/chefignore_spec.rb +11 -15
- data/spec/unit/berkshelf/community_rest_spec.rb +16 -14
- data/spec/unit/berkshelf/config_spec.rb +36 -20
- data/spec/unit/berkshelf/cookbook_generator_spec.rb +6 -10
- data/spec/unit/berkshelf/cookbook_source_spec.rb +244 -183
- data/spec/unit/berkshelf/cookbook_store_spec.rb +72 -76
- data/spec/unit/berkshelf/core_ext/file_utils_spec.rb +2 -2
- data/spec/unit/berkshelf/downloader_spec.rb +137 -157
- data/spec/unit/berkshelf/errors_spec.rb +1 -1
- data/spec/unit/berkshelf/formatters/null_spec.rb +17 -0
- data/spec/unit/berkshelf/formatters_spec.rb +83 -90
- data/spec/unit/berkshelf/git_spec.rb +219 -207
- data/spec/unit/berkshelf/init_generator_spec.rb +73 -73
- data/spec/unit/berkshelf/location_spec.rb +143 -162
- data/spec/unit/berkshelf/locations/chef_api_location_spec.rb +94 -89
- data/spec/unit/berkshelf/locations/git_location_spec.rb +75 -59
- data/spec/unit/berkshelf/locations/path_location_spec.rb +46 -30
- data/spec/unit/berkshelf/locations/site_location_spec.rb +4 -4
- data/spec/unit/berkshelf/lockfile_spec.rb +185 -1
- data/spec/unit/berkshelf/logger_spec.rb +6 -24
- data/spec/unit/berkshelf/mixin/logging_spec.rb +6 -8
- data/spec/unit/berkshelf/resolver_spec.rb +36 -38
- data/spec/unit/berkshelf/ui_spec.rb +9 -10
- data/spec/unit/berkshelf_spec.rb +41 -40
- data/spec/unit/chef/config_spec.rb +9 -11
- metadata +55 -203
- data/spec/config/berkshelf.pem +0 -27
- data/spec/config/validator.pem +0 -27
@@ -1,55 +1,136 @@
|
|
1
|
-
Feature: update
|
1
|
+
Feature: update command
|
2
2
|
As a user
|
3
3
|
I want a way to update the versions without clearing out the files I've downloaded
|
4
4
|
So that I can update faster than a clean install
|
5
5
|
|
6
|
-
Scenario:
|
6
|
+
Scenario: updating with the old lockfile format
|
7
7
|
Given I write to "Berksfile" with:
|
8
8
|
"""
|
9
|
-
|
9
|
+
site :opscode
|
10
|
+
cookbook 'berkshelf-cookbook-fixture', '~> 0.1'
|
10
11
|
"""
|
11
12
|
Given I write to "Berksfile.lock" with:
|
12
13
|
"""
|
13
|
-
cookbook '
|
14
|
+
cookbook 'berkshelf-cookbook-fixture', :locked_version => '0.1.0'
|
14
15
|
"""
|
15
16
|
When I successfully run `berks update`
|
16
|
-
Then the
|
17
|
-
|
18
|
-
|
17
|
+
Then the output should contain "You are using the old lockfile format. Attempting to convert..."
|
18
|
+
Then the file "Berksfile.lock" should contain JSON:
|
19
|
+
"""
|
20
|
+
{
|
21
|
+
"sha":"b2714a4f9bdf500cb20267067160a0b3c1d8404c",
|
22
|
+
"sources":{
|
23
|
+
"berkshelf-cookbook-fixture":{
|
24
|
+
"locked_version":"0.2.0",
|
25
|
+
"constraint":"~> 0.1"
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
19
29
|
"""
|
20
30
|
|
21
|
-
Scenario:
|
31
|
+
Scenario: Updating all cookbooks
|
22
32
|
Given I write to "Berksfile" with:
|
23
33
|
"""
|
24
|
-
|
25
|
-
cookbook
|
34
|
+
site :opscode
|
35
|
+
cookbook 'berkshelf-cookbook-fixture', '~> 0.1'
|
36
|
+
cookbook 'hostsfile', '~> 1.0.0'
|
26
37
|
"""
|
27
38
|
Given I write to "Berksfile.lock" with:
|
28
39
|
"""
|
29
|
-
|
30
|
-
|
40
|
+
{
|
41
|
+
"sha":"9d10199aa2652f9e965149c4346db20c78e97553",
|
42
|
+
"sources":{
|
43
|
+
"berkshelf-cookbook-fixture":{
|
44
|
+
"locked_version":"0.1.0",
|
45
|
+
"constraint":"~> 0.1"
|
46
|
+
},
|
47
|
+
"hostsfile":{
|
48
|
+
"locked_version":"1.0.1",
|
49
|
+
"constraint":"= 1.0.1"
|
50
|
+
}
|
51
|
+
}
|
52
|
+
}
|
53
|
+
"""
|
54
|
+
When I successfully run `berks update`
|
55
|
+
Then the file "Berksfile.lock" should contain JSON:
|
56
|
+
"""
|
57
|
+
{
|
58
|
+
"sha":"69b2e00e970d2bb6a9b1d09aeb3e6a17ef3df955",
|
59
|
+
"sources":{
|
60
|
+
"berkshelf-cookbook-fixture":{
|
61
|
+
"locked_version":"0.2.0",
|
62
|
+
"constraint":"~> 0.1"
|
63
|
+
},
|
64
|
+
"hostsfile":{
|
65
|
+
"locked_version":"1.0.1",
|
66
|
+
"constraint":"~> 1.0.0"
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
31
70
|
"""
|
32
|
-
|
33
|
-
|
71
|
+
|
72
|
+
Scenario: Updating a single cookbook
|
73
|
+
Given I write to "Berksfile" with:
|
34
74
|
"""
|
35
|
-
|
36
|
-
cookbook '
|
75
|
+
site :opscode
|
76
|
+
cookbook 'berkshelf-cookbook-fixture', '~> 0.1'
|
77
|
+
cookbook 'hostsfile', '~> 1.0.0'
|
78
|
+
"""
|
79
|
+
And I write to "Berksfile.lock" with:
|
80
|
+
"""
|
81
|
+
{
|
82
|
+
"sha":"9d10199aa2652f9e965149c4346db20c78e97553",
|
83
|
+
"sources":{
|
84
|
+
"berkshelf-cookbook-fixture":{
|
85
|
+
"locked_version":"0.1.0",
|
86
|
+
"constraint":"~> 0.1"
|
87
|
+
},
|
88
|
+
"hostsfile":{
|
89
|
+
"locked_version":"1.0.0",
|
90
|
+
"constraint":"~> 1.0.0"
|
91
|
+
}
|
92
|
+
}
|
93
|
+
}
|
94
|
+
"""
|
95
|
+
And I successfully run `berks update berkshelf-cookbook-fixture`
|
96
|
+
Then the file "Berksfile.lock" should contain JSON:
|
97
|
+
"""
|
98
|
+
{
|
99
|
+
"sha":"69b2e00e970d2bb6a9b1d09aeb3e6a17ef3df955",
|
100
|
+
"sources":{
|
101
|
+
"berkshelf-cookbook-fixture":{
|
102
|
+
"locked_version":"0.2.0",
|
103
|
+
"constraint":"~> 0.1"
|
104
|
+
},
|
105
|
+
"hostsfile":{
|
106
|
+
"locked_version":"1.0.0",
|
107
|
+
"constraint":"~> 1.0.0"
|
108
|
+
}
|
109
|
+
}
|
110
|
+
}
|
37
111
|
"""
|
38
112
|
|
39
|
-
Scenario:
|
113
|
+
Scenario: Update a cookbook that doesn't exist
|
40
114
|
Given I write to "Berksfile" with:
|
41
115
|
"""
|
42
|
-
|
43
|
-
cookbook
|
116
|
+
site :opscode
|
117
|
+
cookbook 'berkshelf-cookbook-fixture', '~> 0.1'
|
44
118
|
"""
|
45
119
|
Given I write to "Berksfile.lock" with:
|
46
120
|
"""
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
121
|
+
{
|
122
|
+
"sha":"23150cfe61b7b86882013c8664883058560b899d",
|
123
|
+
"sources":{
|
124
|
+
"berkshelf-cookbook-fixture":{
|
125
|
+
"locked_version":"0.1.0",
|
126
|
+
"constraint":"~> 0.1"
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
130
|
+
"""
|
131
|
+
When I run `berks update non-existent-cookbook`
|
51
132
|
Then the output should contain:
|
52
133
|
"""
|
53
|
-
Could not find cookbooks '
|
134
|
+
Could not find cookbooks 'non-existent-cookbook' in any of the sources. Is it in your Berksfile?
|
54
135
|
"""
|
55
136
|
And the CLI should exit with the status code for error "CookbookNotFound"
|
@@ -216,17 +216,3 @@ Feature: upload command
|
|
216
216
|
| ntp |
|
217
217
|
| vim |
|
218
218
|
And the exit status should be 0
|
219
|
-
|
220
|
-
Scenario: Raise exception uploading an invalid cookbook
|
221
|
-
Given a cookbook named "cookbook with spaces"
|
222
|
-
And I write to "Berksfile" with:
|
223
|
-
"""
|
224
|
-
cookbook 'cookbook with spaces', path: './cookbook with spaces'
|
225
|
-
"""
|
226
|
-
When I run `berks upload`
|
227
|
-
Then the output should contain:
|
228
|
-
"""
|
229
|
-
The cookbook 'cookbook with spaces' has invalid filenames:
|
230
|
-
"""
|
231
|
-
And the CLI should exit with the status code for error "InvalidCookbookFiles"
|
232
|
-
|
@@ -7,13 +7,13 @@ Feature: install cookbooks to a given vendor path
|
|
7
7
|
Given I write to "Berksfile" with:
|
8
8
|
"""
|
9
9
|
site :opscode
|
10
|
-
cookbook
|
10
|
+
cookbook 'berkshelf-cookbook-fixture', '1.0.0'
|
11
11
|
"""
|
12
12
|
When I run the install command with flags:
|
13
13
|
| --path vendor/cookbooks |
|
14
14
|
Then the cookbook store should have the cookbooks:
|
15
|
-
|
|
15
|
+
| berkshelf-cookbook-fixture | 1.0.0 |
|
16
16
|
Then the following directories should exist:
|
17
17
|
| vendor/cookbooks |
|
18
|
-
| vendor/cookbooks/
|
18
|
+
| vendor/cookbooks/berkshelf-cookbook-fixture |
|
19
19
|
And the exit status should be 0
|
@@ -9,18 +9,18 @@ Vagrant.configure("2") do |config|
|
|
9
9
|
config.vm.hostname = "<%= "#{cookbook_name.gsub('_','-')}-berkshelf" %>"
|
10
10
|
|
11
11
|
# Every Vagrant virtual environment requires a box to build off of.
|
12
|
-
config.vm.box = "<%=
|
12
|
+
config.vm.box = "<%= berkshelf_config.vagrant.vm.box %>"
|
13
13
|
|
14
14
|
# The url from where the 'config.vm.box' box will be fetched if it
|
15
15
|
# doesn't already exist on the user's system.
|
16
|
-
config.vm.box_url = "<%=
|
16
|
+
config.vm.box_url = "<%= berkshelf_config.vagrant.vm.box_url %>"
|
17
17
|
|
18
18
|
# Assign this VM to a host-only network IP, allowing you to access it
|
19
19
|
# via the IP. Host-only networks can talk to the host machine as well as
|
20
20
|
# any other machines on the same network, but cannot be accessed (through this
|
21
21
|
# network interface) by any external networks.
|
22
|
-
<% if
|
23
|
-
config.vm.network :private_network, ip: "<%=
|
22
|
+
<% if berkshelf_config.vagrant.vm.network.hostonly.present? -%>
|
23
|
+
config.vm.network :private_network, ip: "<%= berkshelf_config.vagrant.vm.network.hostonly %>"
|
24
24
|
<% else %>
|
25
25
|
config.vm.network :private_network, ip: "192.168.33.10"
|
26
26
|
<% end -%>
|
@@ -28,7 +28,7 @@ Vagrant.configure("2") do |config|
|
|
28
28
|
# Create a public network, which generally matched to bridged network.
|
29
29
|
# Bridged networks make the machine appear as another physical device on
|
30
30
|
# your network.
|
31
|
-
<% if
|
31
|
+
<% if berkshelf_config.vagrant.vm.network.bridged -%>
|
32
32
|
config.vm.network :public_network
|
33
33
|
<% else %>
|
34
34
|
# config.vm.network :public_network
|
@@ -37,7 +37,7 @@ Vagrant.configure("2") do |config|
|
|
37
37
|
# Create a forwarded port mapping which allows access to a specific port
|
38
38
|
# within the machine from a port on the host machine. In the example below,
|
39
39
|
# accessing "localhost:8080" will access port 80 on the guest machine.
|
40
|
-
<%
|
40
|
+
<% berkshelf_config.vagrant.vm.forward_port.each do |guest, host| %>
|
41
41
|
config.vm.network :forwarded_port, guest: <%= guest %>, host: <%= host %>
|
42
42
|
<% end -%>
|
43
43
|
|
@@ -80,11 +80,11 @@ Vagrant.configure("2") do |config|
|
|
80
80
|
# to skip installing and copying to Vagrant's shelf.
|
81
81
|
# config.berkshelf.except = []
|
82
82
|
|
83
|
-
<% if
|
83
|
+
<% if berkshelf_config.vagrant.vm.provision == "chef_client" -%>
|
84
84
|
config.vm.provision :chef_client do |chef|
|
85
|
-
chef.chef_server_url = "<%=
|
86
|
-
chef.validation_client_name = "<%=
|
87
|
-
chef.validation_key_path = "<%=
|
85
|
+
chef.chef_server_url = "<%= berkshelf_config.chef.chef_server_url %>"
|
86
|
+
chef.validation_client_name = "<%= berkshelf_config.chef.validation_client_name %>"
|
87
|
+
chef.validation_key_path = "<%= berkshelf_config.chef.validation_key_path %>"
|
88
88
|
|
89
89
|
chef.run_list = [
|
90
90
|
<% if options[:chef_minitest] -%>
|
@@ -93,7 +93,7 @@ Vagrant.configure("2") do |config|
|
|
93
93
|
"recipe[<%= cookbook_name %>::default]"
|
94
94
|
]
|
95
95
|
end
|
96
|
-
<% elsif
|
96
|
+
<% elsif berkshelf_config.vagrant.vm.provision == "chef_solo" -%>
|
97
97
|
config.vm.provision :chef_solo do |chef|
|
98
98
|
chef.json = {
|
99
99
|
:mysql => {
|
data/lib/berkshelf.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
|
-
require 'chozo/core_ext'
|
2
1
|
require 'active_support/core_ext'
|
3
2
|
require 'archive/tar/minitar'
|
3
|
+
require 'celluloid'
|
4
|
+
require 'chozo/core_ext'
|
4
5
|
require 'forwardable'
|
5
6
|
require 'hashie'
|
6
|
-
require '
|
7
|
+
require 'json'
|
7
8
|
require 'pathname'
|
8
9
|
require 'ridley'
|
9
10
|
require 'solve'
|
@@ -11,7 +12,6 @@ require 'thor'
|
|
11
12
|
require 'tmpdir'
|
12
13
|
require 'uri'
|
13
14
|
require 'zlib'
|
14
|
-
require 'celluloid'
|
15
15
|
|
16
16
|
require 'berkshelf/core_ext'
|
17
17
|
require 'berkshelf/errors'
|
@@ -56,9 +56,10 @@ module Berkshelf
|
|
56
56
|
@root ||= Pathname.new(File.expand_path('../', File.dirname(__FILE__)))
|
57
57
|
end
|
58
58
|
|
59
|
-
# @return [::Thor::Shell::
|
59
|
+
# @return [Thor::Shell::Color, Thor::Shell::Basic]
|
60
|
+
# A basic shell on Windows, colored everywhere else
|
60
61
|
def ui
|
61
|
-
@ui ||=
|
62
|
+
@ui ||= Thor::Base.shell.new
|
62
63
|
end
|
63
64
|
|
64
65
|
# Returns the filepath to the location Berskhelf will use for
|
data/lib/berkshelf/berksfile.rb
CHANGED
@@ -83,6 +83,12 @@ module Berkshelf
|
|
83
83
|
@cached_cookbooks = nil
|
84
84
|
end
|
85
85
|
|
86
|
+
# @return [String]
|
87
|
+
# the shasum for the Berksfile
|
88
|
+
def sha
|
89
|
+
@sha ||= Digest::SHA1.hexdigest File.read(filepath.to_s)
|
90
|
+
end
|
91
|
+
|
86
92
|
# Add a cookbook source to the Berksfile to be retrieved and have it's dependencies recursively retrieved
|
87
93
|
# and resolved.
|
88
94
|
#
|
@@ -283,24 +289,31 @@ module Berkshelf
|
|
283
289
|
@sources.has_key?(source.to_s)
|
284
290
|
end
|
285
291
|
|
292
|
+
# The list of cookbook sources specified in this Berksfile
|
293
|
+
#
|
294
|
+
# @param [Array] sources
|
295
|
+
# the list of sources to filter
|
296
|
+
#
|
286
297
|
# @option options [Symbol, Array] :except
|
287
|
-
#
|
298
|
+
# group(s) to exclude to exclude from the returned Array of sources
|
288
299
|
# group to not be installed
|
289
300
|
# @option options [Symbol, Array] :only
|
290
|
-
#
|
301
|
+
# group(s) to include which will cause any sources marked as a member of the
|
291
302
|
# group to be installed and all others to be ignored
|
292
303
|
# @option cookbooks [String, Array] :cookbooks
|
293
|
-
#
|
304
|
+
# names of the cookbooks to retrieve sources for
|
294
305
|
#
|
295
|
-
# @raise [Berkshelf::ArgumentError]
|
306
|
+
# @raise [Berkshelf::ArgumentError]
|
307
|
+
# if a value for both :except and :only is provided
|
296
308
|
#
|
297
309
|
# @return [Array<Berkshelf::CookbookSource>]
|
310
|
+
# the list of cookbook sources that match the given options
|
298
311
|
def sources(options = {})
|
299
|
-
l_sources = @sources.
|
312
|
+
l_sources = @sources.values
|
300
313
|
|
301
|
-
cookbooks = Array(options
|
302
|
-
except = Array(options
|
303
|
-
only = Array(options
|
314
|
+
cookbooks = Array(options[:cookbooks])
|
315
|
+
except = Array(options[:except]).collect(&:to_sym)
|
316
|
+
only = Array(options[:only]).collect(&:to_sym)
|
304
317
|
|
305
318
|
case
|
306
319
|
when !except.empty? && !only.empty?
|
@@ -309,7 +322,7 @@ module Berkshelf
|
|
309
322
|
if !except.empty? && !only.empty?
|
310
323
|
Berkshelf.ui.warn "Cookbooks were specified, ignoring :except and :only"
|
311
324
|
end
|
312
|
-
l_sources.select { |source|
|
325
|
+
l_sources.select { |source| cookbooks.include?(source.name) }
|
313
326
|
when !except.empty?
|
314
327
|
l_sources.select { |source| (except & source.groups).empty? }
|
315
328
|
when !only.empty?
|
@@ -319,6 +332,16 @@ module Berkshelf
|
|
319
332
|
end
|
320
333
|
end
|
321
334
|
|
335
|
+
# Find a source defined in this berksfile by name.
|
336
|
+
#
|
337
|
+
# @param [String] name
|
338
|
+
# the name of the cookbook source to search for
|
339
|
+
# @return [Berkshelf::CookbookSource, nil]
|
340
|
+
# the cookbook source, or nil if one does not exist
|
341
|
+
def find(name)
|
342
|
+
@sources[name]
|
343
|
+
end
|
344
|
+
|
322
345
|
# @return [Hash]
|
323
346
|
# a hash containing group names as keys and an array of CookbookSources
|
324
347
|
# that are a member of that group as values
|
@@ -353,6 +376,33 @@ module Berkshelf
|
|
353
376
|
end
|
354
377
|
alias_method :get_source, :[]
|
355
378
|
|
379
|
+
# Install the sources listed in the Berksfile, respecting the locked
|
380
|
+
# versions in the Berksfile.lock.
|
381
|
+
#
|
382
|
+
# 1. Check that a lockfile exists. If a lockfile does not exist, all
|
383
|
+
# sources are considered to be "unlocked". If a lockfile is specified, a
|
384
|
+
# definition is created via the following algorithm:
|
385
|
+
#
|
386
|
+
# - Compare the SHA of the current Berksfile with the last-known SHA.
|
387
|
+
# - If the SHAs match, the Berksfile has not been updated, so we rely
|
388
|
+
# solely on the locked sources.
|
389
|
+
# - If the SHAs don't match, then the Berksfile has diverged from the
|
390
|
+
# lockfile, which means some sources are outdated. For each unlocked
|
391
|
+
# source, see if there exists a locked version that still satisfies
|
392
|
+
# the version constraint in the Berksfile. If there exists such a
|
393
|
+
# source, remove it from the list of unlocked sources. If not, then
|
394
|
+
# either a version constraint has changed, or a new source has been
|
395
|
+
# added to the Berksfile. In the event that a locked_source exists,
|
396
|
+
# but it no longer satisfies the constraint, this method will raise
|
397
|
+
# a {Berkshelf::OutdatedCookbookSource}, and inform the user to run
|
398
|
+
# <tt>berks update COOKBOOK</tt> to remedy the issue.
|
399
|
+
# - Remove any locked sources that no longer exist in the Berksfile
|
400
|
+
# (i.e. a cookbook source was removed from the Berksfile).
|
401
|
+
#
|
402
|
+
# 2. Resolve the collection of locked and unlocked sources.
|
403
|
+
#
|
404
|
+
# 3. Write out a new lockfile.
|
405
|
+
#
|
356
406
|
# @option options [Symbol, Array] :except
|
357
407
|
# Group(s) to exclude which will cause any sources marked as a member of the
|
358
408
|
# group to not be installed
|
@@ -363,16 +413,26 @@ module Berkshelf
|
|
363
413
|
# a path to "vendor" the cached_cookbooks resolved by the resolver. Vendoring
|
364
414
|
# is a technique for packaging all cookbooks resolved by a Berksfile.
|
365
415
|
#
|
416
|
+
# @raise [Berkshelf::OutdatedCookbookSource]
|
417
|
+
# if the lockfile constraints do not satisfy the Berskfile constraints
|
418
|
+
# @raise [Berkshelf::ArgumentError]
|
419
|
+
# if there are missing or conflicting options
|
420
|
+
#
|
366
421
|
# @return [Array<Berkshelf::CachedCookbook>]
|
367
422
|
def install(options = {})
|
368
|
-
|
423
|
+
if self.sha == lockfile.sha
|
424
|
+
local_sources = locked_sources
|
425
|
+
else
|
426
|
+
local_sources = apply_lockfile(sources(options))
|
427
|
+
end
|
369
428
|
|
370
|
-
|
371
|
-
|
429
|
+
resolver = resolve(local_sources)
|
430
|
+
@cached_cookbooks = resolver[:solution]
|
431
|
+
local_sources = resolver[:sources]
|
372
432
|
|
373
|
-
if options[:path]
|
374
|
-
|
375
|
-
|
433
|
+
self.class.vendor(@cached_cookbooks, options[:path]) if options[:path]
|
434
|
+
|
435
|
+
lockfile.update(local_sources, sha: self.sha)
|
376
436
|
|
377
437
|
self.cached_cookbooks
|
378
438
|
end
|
@@ -386,25 +446,15 @@ module Berkshelf
|
|
386
446
|
# @option cookbooks [String, Array] :cookbooks
|
387
447
|
# Names of the cookbooks to retrieve sources for
|
388
448
|
def update(options = {})
|
389
|
-
|
390
|
-
|
391
|
-
cookbooks = resolver.resolve
|
392
|
-
sources = resolver.sources
|
393
|
-
missing_cookbooks = (options[:cookbooks] - cookbooks.map(&:cookbook_name))
|
449
|
+
validate_cookbook_names!(options)
|
394
450
|
|
395
|
-
|
396
|
-
|
397
|
-
msg << " in any of the sources. #{missing_cookbooks.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
|
398
|
-
raise Berkshelf::CookbookNotFound, msg
|
399
|
-
end
|
451
|
+
# Unlock any/all specified cookbooks
|
452
|
+
sources(options).each { |source| lockfile.unlock(source) }
|
400
453
|
|
401
|
-
|
454
|
+
lockfile.reset_sha!
|
402
455
|
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
|
407
|
-
cookbooks
|
456
|
+
# NOTE: We intentionally do NOT pass options to the installer
|
457
|
+
self.install
|
408
458
|
end
|
409
459
|
|
410
460
|
# Get a list of all the cookbooks which have newer versions found on the community
|
@@ -431,7 +481,7 @@ module Berkshelf
|
|
431
481
|
outdated = Hash.new
|
432
482
|
|
433
483
|
sources(options).each do |cookbook|
|
434
|
-
location = cookbook.location || Location.init(cookbook.name, cookbook.version_constraint)
|
484
|
+
location = cookbook.location || Location.init(cookbook.name, cookbook.version_constraint, site: :opscode)
|
435
485
|
|
436
486
|
if location.is_a?(SiteLocation)
|
437
487
|
latest_version = location.latest_version
|
@@ -476,38 +526,17 @@ module Berkshelf
|
|
476
526
|
options = options.reverse_merge(
|
477
527
|
force: false,
|
478
528
|
freeze: true,
|
479
|
-
ssl_verify: Berkshelf::Config.instance.ssl.verify,
|
480
529
|
skip_dependencies: false,
|
481
530
|
halt_on_frozen: false
|
482
531
|
)
|
483
532
|
|
484
|
-
|
485
|
-
ridley_options[:server_url] = options[:server_url] || Berkshelf::Config.instance.chef.chef_server_url
|
486
|
-
ridley_options[:client_name] = Berkshelf::Config.instance.chef.node_name
|
487
|
-
ridley_options[:client_key] = Berkshelf::Config.instance.chef.client_key
|
488
|
-
ridley_options[:ssl] = { verify: options[:ssl_verify] }
|
489
|
-
|
490
|
-
unless ridley_options[:server_url].present?
|
491
|
-
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.server_url"
|
492
|
-
end
|
493
|
-
|
494
|
-
unless ridley_options[:client_name].present?
|
495
|
-
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.node_name"
|
496
|
-
end
|
497
|
-
|
498
|
-
unless ridley_options[:client_key].present?
|
499
|
-
raise UploadFailure, "Missing required attribute in your Berkshelf configuration: chef.client_key"
|
500
|
-
end
|
501
|
-
|
502
|
-
solution = resolve(options)
|
533
|
+
solution = resolve(sources(options), options)[:solution]
|
503
534
|
upload_opts = options.slice(:force, :freeze)
|
504
|
-
conn =
|
535
|
+
conn = ridley_connection(options)
|
505
536
|
|
506
537
|
solution.each do |cb|
|
507
538
|
Berkshelf.formatter.upload(cb.cookbook_name, cb.version, conn.server_url)
|
508
539
|
|
509
|
-
validate_files!(cb)
|
510
|
-
|
511
540
|
begin
|
512
541
|
conn.cookbook.upload(cb.path, upload_opts.merge(name: cb.cookbook_name))
|
513
542
|
rescue Ridley::Errors::FrozenCookbook => ex
|
@@ -528,27 +557,122 @@ module Berkshelf
|
|
528
557
|
end
|
529
558
|
rescue Ridley::Errors::RidleyError => ex
|
530
559
|
log_exception(ex)
|
531
|
-
raise
|
560
|
+
raise ChefConnectionError, ex # todo implement
|
532
561
|
ensure
|
533
562
|
conn.terminate if conn && conn.alive?
|
534
563
|
end
|
535
564
|
|
565
|
+
# Resolve this Berksfile and apply the locks found in the generated Berksfile.lock to the
|
566
|
+
# target Chef environment
|
567
|
+
#
|
568
|
+
# @param [String] environment_name
|
569
|
+
#
|
570
|
+
# @option options [Hash] :ssl_verify (true)
|
571
|
+
# Disable/Enable SSL verification during uploads
|
572
|
+
#
|
573
|
+
# @raise [EnvironmentNotFound] if the target environment was not found
|
574
|
+
# @raise [ChefConnectionError] if you are locking cookbooks with an invalid or not-specified client configuration
|
575
|
+
def apply(environment_name, options = {})
|
576
|
+
conn = ridley_connection(options)
|
577
|
+
environment = conn.environment.find(environment_name)
|
578
|
+
|
579
|
+
if environment
|
580
|
+
install
|
581
|
+
|
582
|
+
environment.cookbook_versions = {}.tap do |cookbook_versions|
|
583
|
+
lockfile.sources.each { |source| cookbook_versions[source.name] = source.locked_version }
|
584
|
+
end
|
585
|
+
|
586
|
+
environment.save
|
587
|
+
else
|
588
|
+
raise EnvironmentNotFound.new(environment_name)
|
589
|
+
end
|
590
|
+
rescue Ridley::Errors::RidleyError => ex
|
591
|
+
raise ChefConnectionError, ex
|
592
|
+
ensure
|
593
|
+
conn.terminate if conn && conn.alive?
|
594
|
+
end
|
595
|
+
|
596
|
+
# Package the given cookbook for distribution outside of berkshelf. If the
|
597
|
+
# name attribute is not given, all cookbooks in the Berksfile will be
|
598
|
+
# packaged.
|
599
|
+
#
|
600
|
+
# @param [String] name
|
601
|
+
# the name of the cookbook to package
|
602
|
+
# @param [Hash] options
|
603
|
+
# a list of options
|
604
|
+
#
|
605
|
+
# @option options [String] :output
|
606
|
+
# the path to output the tarball
|
607
|
+
# @option options [Boolean] :skip_dependencies
|
608
|
+
# package cookbook dependencies as well
|
609
|
+
# @option options [Boolean] :ignore_chefignore
|
610
|
+
# do not apply the chefignore file to the packed cookbooks
|
611
|
+
#
|
612
|
+
# @return [String]
|
613
|
+
# the path to the package
|
614
|
+
def package(name = nil, options = {})
|
615
|
+
tar_name = "#{name || 'package'}.tar.gz"
|
616
|
+
output = File.expand_path(File.join(options[:output], tar_name))
|
617
|
+
|
618
|
+
unless name.nil?
|
619
|
+
source = self.find(name)
|
620
|
+
raise CookbookNotFound, "Cookbook '#{name}' is not in your Berksfile" unless source
|
621
|
+
|
622
|
+
package = Berkshelf.ui.mute {
|
623
|
+
self.resolve(source, options)[:solution]
|
624
|
+
}
|
625
|
+
else
|
626
|
+
package = Berkshelf.ui.mute {
|
627
|
+
self.resolve(sources, options)[:solution]
|
628
|
+
}
|
629
|
+
end
|
630
|
+
|
631
|
+
Dir.mktmpdir do |tmp|
|
632
|
+
package.each do |cached_cookbook|
|
633
|
+
path = cached_cookbook.path.to_s
|
634
|
+
destination = File.join(tmp, cached_cookbook.cookbook_name)
|
635
|
+
|
636
|
+
FileUtils.cp_r(path, destination)
|
637
|
+
|
638
|
+
unless options[:ignore_chefignore]
|
639
|
+
if ignore_file = Berkshelf::Chef::Cookbook::Chefignore.find_relative_to(path)
|
640
|
+
chefignore = Berkshelf::Chef::Cookbook::Chefignore.new(ignore_file)
|
641
|
+
chefignore.remove_ignores_from(destination) if chefignore
|
642
|
+
end
|
643
|
+
end
|
644
|
+
end
|
645
|
+
|
646
|
+
FileUtils.mkdir_p(options[:output])
|
647
|
+
|
648
|
+
Dir.chdir(tmp) do |dir|
|
649
|
+
tgz = Zlib::GzipWriter.new(File.open(output, 'wb'))
|
650
|
+
Archive::Tar::Minitar.pack('.', tgz)
|
651
|
+
end
|
652
|
+
end
|
653
|
+
|
654
|
+
Berkshelf.formatter.package(name, output)
|
655
|
+
|
656
|
+
output
|
657
|
+
end
|
658
|
+
|
536
659
|
# Finds a solution for the Berksfile and returns an array of CachedCookbooks.
|
537
660
|
#
|
538
|
-
# @
|
539
|
-
#
|
540
|
-
#
|
541
|
-
# @option options [Symbol, Array] :only
|
542
|
-
# Group(s) to include which will cause any sources marked as a member of the
|
543
|
-
# group to be installed and all others to be ignored
|
544
|
-
# @option cookbooks [String, Array] :cookbooks
|
545
|
-
# Names of the cookbooks to retrieve sources for
|
661
|
+
# @param [Array<Berkshelf::CookbookSource>] sources
|
662
|
+
# Array of cookbook sources to resolve
|
663
|
+
#
|
546
664
|
# @option options [Boolean] :skip_dependencies
|
547
665
|
# Skip resolving of dependencies
|
548
666
|
#
|
549
|
-
# @return [Array<Berkshelf::CachedCookbooks]
|
550
|
-
def resolve(options = {})
|
551
|
-
resolver(
|
667
|
+
# @return [Array<Berkshelf::CachedCookbooks>]
|
668
|
+
def resolve(sources = [], options = {})
|
669
|
+
resolver = Resolver.new(
|
670
|
+
self,
|
671
|
+
sources: sources,
|
672
|
+
skip_dependencies: options[:skip_dependencies]
|
673
|
+
)
|
674
|
+
|
675
|
+
{ solution: resolver.resolve, sources: resolver.sources }
|
552
676
|
end
|
553
677
|
|
554
678
|
# Reload this instance of Berksfile with the given content. The content
|
@@ -568,56 +692,100 @@ module Berkshelf
|
|
568
692
|
self
|
569
693
|
end
|
570
694
|
|
695
|
+
# Get the lockfile corresponding to this Berksfile. This is necessary because
|
696
|
+
# the user can specify a different path to the Berksfile. So assuming the lockfile
|
697
|
+
# is named "Berksfile.lock" is a poor assumption.
|
698
|
+
#
|
699
|
+
# @return [Berkshelf::Lockfile]
|
700
|
+
# the lockfile corresponding to this berksfile, or a new Lockfile if one does
|
701
|
+
# not exist
|
702
|
+
def lockfile
|
703
|
+
@lockfile ||= Berkshelf::Lockfile.new(self)
|
704
|
+
end
|
705
|
+
|
571
706
|
private
|
572
707
|
|
708
|
+
def ridley_connection(options = {})
|
709
|
+
ridley_options = options.slice(:ssl)
|
710
|
+
ridley_options[:server_url] = options[:server_url] || Berkshelf::Config.instance.chef.chef_server_url
|
711
|
+
ridley_options[:client_name] = Berkshelf::Config.instance.chef.node_name
|
712
|
+
ridley_options[:client_key] = Berkshelf::Config.instance.chef.client_key
|
713
|
+
ridley_options[:ssl] = { verify: (options[:ssl_verify] || Berkshelf::Config.instance.ssl.verify) }
|
714
|
+
|
715
|
+
unless ridley_options[:server_url].present?
|
716
|
+
raise ChefConnectionError, "Missing required attribute in your Berkshelf configuration: chef.server_url"
|
717
|
+
end
|
718
|
+
|
719
|
+
unless ridley_options[:client_name].present?
|
720
|
+
raise ChefConnectionError, "Missing required attribute in your Berkshelf configuration: chef.node_name"
|
721
|
+
end
|
722
|
+
|
723
|
+
unless ridley_options[:client_key].present?
|
724
|
+
raise ChefConnectionError, "Missing required attribute in your Berkshelf configuration: chef.client_key"
|
725
|
+
end
|
726
|
+
|
727
|
+
Ridley.new(ridley_options)
|
728
|
+
end
|
729
|
+
|
573
730
|
def descendant_directory?(candidate, parent)
|
574
731
|
hack = FileUtils::Entry_.new('/tmp')
|
575
732
|
hack.send(:descendant_diretory?, candidate, parent)
|
576
733
|
end
|
577
734
|
|
578
|
-
|
579
|
-
File.exist?(Berkshelf::Lockfile::DEFAULT_FILENAME)
|
580
|
-
end
|
581
|
-
|
582
|
-
# Builds a Resolver instance
|
735
|
+
# Determine if any cookbooks were specified that aren't in our shelf.
|
583
736
|
#
|
584
|
-
# @option options [
|
585
|
-
#
|
586
|
-
# group to not be installed
|
587
|
-
# @option options [Symbol, Array] :only
|
588
|
-
# Group(s) to include which will cause any sources marked as a member of the
|
589
|
-
# group to be installed and all others to be ignored
|
590
|
-
# @option options [String, Array] :cookbooks
|
591
|
-
# Names of the cookbooks to retrieve sources for
|
592
|
-
# @option options [Boolean] :skip_dependencies
|
593
|
-
# Skip resolving of dependencies
|
737
|
+
# @option options [Array<String>] :cookbooks
|
738
|
+
# a list of strings of cookbook names
|
594
739
|
#
|
595
|
-
# @
|
596
|
-
|
597
|
-
|
740
|
+
# @raise [Berkshelf::CookbookNotFound]
|
741
|
+
# if a cookbook name is given that does not exist
|
742
|
+
def validate_cookbook_names!(options = {})
|
743
|
+
missing = (Array(options[:cookbooks]) - sources.map(&:name))
|
744
|
+
unless missing.empty?
|
745
|
+
raise Berkshelf::CookbookNotFound,
|
746
|
+
"Could not find cookbooks #{missing.collect{ |c| "'#{c}'" }.join(', ')} " +
|
747
|
+
"in any of the sources. #{missing.size == 1 ? 'Is it' : 'Are they' } in your Berksfile?"
|
748
|
+
end
|
598
749
|
end
|
599
750
|
|
600
|
-
|
601
|
-
|
751
|
+
# The list of sources "locked" by the lockfile.
|
752
|
+
#
|
753
|
+
# @return [Array<Berkshelf::CookbookSource>]
|
754
|
+
# the list of sources in this lockfile
|
755
|
+
def locked_sources
|
756
|
+
lockfile.sources
|
602
757
|
end
|
603
758
|
|
604
|
-
|
605
|
-
|
759
|
+
# Merge the locked sources against the given sources.
|
760
|
+
#
|
761
|
+
# For each the given sources, check if there's a locked version that
|
762
|
+
# still satisfies the version constraint. If it does, "lock" that source
|
763
|
+
# because we should just use the locked version.
|
764
|
+
#
|
765
|
+
# If a locked source exists, but doesn't satisfy the constraint, raise a
|
766
|
+
# {Berkshelf::OutdatedCookbookSource} and tell the user to run update.
|
767
|
+
def apply_lockfile(sources = [])
|
768
|
+
sources.collect do |source|
|
769
|
+
source_from_lockfile(source) || source
|
770
|
+
end
|
606
771
|
end
|
607
772
|
|
608
|
-
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
# the Cookbook to validate
|
613
|
-
def validate_files!(cookbook)
|
614
|
-
path = cookbook.path.to_s
|
773
|
+
def source_from_lockfile(source)
|
774
|
+
locked_source = lockfile.find(source)
|
775
|
+
|
776
|
+
return nil unless locked_source
|
615
777
|
|
616
|
-
|
617
|
-
|
778
|
+
# If there's a locked_version, make sure it's still satisfied
|
779
|
+
# by the constraint
|
780
|
+
if locked_source.locked_version
|
781
|
+
unless source.version_constraint.satisfies?(locked_source.locked_version)
|
782
|
+
raise Berkshelf::OutdatedCookbookSource.new(locked_source, source)
|
783
|
+
end
|
618
784
|
end
|
619
785
|
|
620
|
-
|
786
|
+
# Update to the new constraint (it might have changed, but still be satisfied)
|
787
|
+
locked_source.version_constraint = source.version_constraint
|
788
|
+
locked_source
|
621
789
|
end
|
622
790
|
end
|
623
791
|
end
|