berkshelf 0.4.0 → 0.5.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/berkshelf.gemspec +8 -3
- data/bin/berks +1 -1
- data/features/groups_install.feature +67 -0
- data/features/install.feature +1 -71
- data/features/json_formatter.feature +0 -17
- data/features/step_definitions/filesystem_steps.rb +1 -3
- data/features/update.feature +3 -6
- data/features/vendor_install.feature +20 -0
- data/generator_files/Vagrantfile.erb +25 -3
- data/lib/berkshelf.rb +27 -17
- data/lib/berkshelf/base_generator.rb +5 -0
- data/lib/berkshelf/berksfile.rb +82 -77
- data/lib/berkshelf/cli.rb +37 -26
- data/lib/berkshelf/cookbook_source.rb +14 -4
- data/lib/berkshelf/errors.rb +4 -1
- data/lib/berkshelf/formatters.rb +69 -5
- data/lib/berkshelf/formatters/human_readable.rb +26 -8
- data/lib/berkshelf/formatters/json.rb +51 -23
- data/lib/berkshelf/init_generator.rb +4 -4
- data/lib/berkshelf/location.rb +29 -6
- data/lib/berkshelf/locations/chef_api_location.rb +23 -5
- data/lib/berkshelf/locations/git_location.rb +13 -6
- data/lib/berkshelf/locations/path_location.rb +8 -4
- data/lib/berkshelf/locations/site_location.rb +9 -3
- data/lib/berkshelf/ui.rb +34 -0
- data/lib/berkshelf/uploader.rb +37 -103
- data/lib/berkshelf/vagrant.rb +65 -0
- data/lib/berkshelf/vagrant/action/clean.rb +24 -0
- data/lib/berkshelf/vagrant/action/install.rb +47 -0
- data/lib/berkshelf/vagrant/action/set_ui.rb +17 -0
- data/lib/berkshelf/vagrant/action/upload.rb +40 -0
- data/lib/berkshelf/vagrant/config.rb +70 -0
- data/lib/berkshelf/vagrant/middleware.rb +52 -0
- data/lib/berkshelf/version.rb +1 -1
- data/lib/thor/monkies.rb +3 -0
- data/lib/thor/monkies/hash_with_indifferent_access.rb +13 -0
- data/lib/vagrant_init.rb +2 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/chef_api.rb +6 -1
- data/spec/unit/berkshelf/berksfile_spec.rb +93 -55
- data/spec/unit/berkshelf/formatters_spec.rb +99 -1
- data/spec/unit/berkshelf/init_generator_spec.rb +1 -1
- data/spec/unit/berkshelf/location_spec.rb +44 -7
- data/spec/unit/berkshelf/lockfile_spec.rb +2 -2
- data/spec/unit/berkshelf/uploader_spec.rb +2 -20
- data/spec/unit/berkshelf_spec.rb +2 -2
- metadata +100 -14
- data/features/without.feature +0 -26
- data/lib/berkshelf/core_ext/fileutils.rb +0 -90
- data/lib/berkshelf/core_ext/kernel.rb +0 -33
- data/spec/unit/berkshelf/core_ext/fileutils_spec.rb +0 -20
@@ -0,0 +1,24 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
module Action
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
class Clean
|
6
|
+
attr_reader :shelf
|
7
|
+
|
8
|
+
def initialize(app, env)
|
9
|
+
@app = app
|
10
|
+
@shelf = Berkshelf::Vagrant.shelf_for(env)
|
11
|
+
end
|
12
|
+
|
13
|
+
def call(env)
|
14
|
+
if Berkshelf::Vagrant.chef_solo?(env[:global_config])
|
15
|
+
Berkshelf.formatter.msg "cleaning Vagrant's shelf"
|
16
|
+
FileUtils.remove_dir(self.shelf, fore: true)
|
17
|
+
end
|
18
|
+
|
19
|
+
@app.call(env)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
module Action
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
# @author Andrew Garson <andrew.garson@gmail.com>
|
6
|
+
class Install
|
7
|
+
attr_reader :config
|
8
|
+
attr_reader :shelf
|
9
|
+
attr_reader :berksfile
|
10
|
+
|
11
|
+
def initialize(app, env)
|
12
|
+
@app = app
|
13
|
+
@shelf = Berkshelf::Vagrant.shelf_for(env)
|
14
|
+
@config = env[:global_config].berkshelf
|
15
|
+
Berkshelf.config_path = @config.config_path
|
16
|
+
Berkshelf.load_config
|
17
|
+
@berksfile = Berksfile.from_file(@config.berksfile_path)
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
if Berkshelf::Vagrant.chef_solo?(env[:global_config])
|
22
|
+
configure_cookbooks_path(env)
|
23
|
+
install(env)
|
24
|
+
end
|
25
|
+
|
26
|
+
@app.call(env)
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def install(env)
|
32
|
+
Berkshelf.formatter.msg "installing cookbooks..."
|
33
|
+
opts = {
|
34
|
+
path: self.shelf
|
35
|
+
}.merge(self.config.to_hash).symbolize_keys!
|
36
|
+
berksfile.install(opts)
|
37
|
+
end
|
38
|
+
|
39
|
+
def configure_cookbooks_path(env)
|
40
|
+
Berkshelf::Vagrant.provisioners(:chef_solo, env[:global_config]).each do |provisioner|
|
41
|
+
provisioner.config.cookbooks_path.unshift(self.shelf)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
module Action
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
class SetUI
|
6
|
+
def initialize(app, env)
|
7
|
+
@app = app
|
8
|
+
end
|
9
|
+
|
10
|
+
def call(env)
|
11
|
+
Berkshelf.ui = ::Vagrant::UI::Colored.new("Berkshelf")
|
12
|
+
@app.call(env)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
module Action
|
4
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
|
+
class Upload
|
6
|
+
attr_reader :berksfile
|
7
|
+
attr_reader :node_name
|
8
|
+
attr_reader :client_key
|
9
|
+
|
10
|
+
def initialize(app, env)
|
11
|
+
@app = app
|
12
|
+
@node_name = env[:global_config].berkshelf.node_name
|
13
|
+
@client_key = env[:global_config].berkshelf.client_key
|
14
|
+
@berksfile = Berksfile.from_file(env[:global_config].berkshelf.berksfile_path)
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
if Berkshelf::Vagrant.chef_client?(env[:global_config])
|
19
|
+
upload(env)
|
20
|
+
end
|
21
|
+
|
22
|
+
@app.call(env)
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def upload(env)
|
28
|
+
Berkshelf::Vagrant.provisioners(:chef_client, env[:global_config]).each do |provisioner|
|
29
|
+
Berkshelf.formatter.msg "uploading cookbooks to '#{provisioner.config.chef_server_url}'"
|
30
|
+
berksfile.upload(
|
31
|
+
provisioner.config.chef_server_url,
|
32
|
+
node_name: self.node_name,
|
33
|
+
client_key: self.client_key
|
34
|
+
)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
4
|
+
# @author Andrew Garson <andrew.garson@gmail.com>
|
5
|
+
class Config < ::Vagrant::Config::Base
|
6
|
+
# return [String]
|
7
|
+
# path to a knife configuration file
|
8
|
+
attr_reader :config_path
|
9
|
+
|
10
|
+
# @return [String]
|
11
|
+
# path to the Berksfile to use with Vagrant
|
12
|
+
attr_reader :berksfile_path
|
13
|
+
|
14
|
+
# @return [String]
|
15
|
+
# A path to a client key on disk to use with the Chef Client provisioner to
|
16
|
+
# upload cookbooks installed by Berkshelf.
|
17
|
+
attr_reader :client_key
|
18
|
+
|
19
|
+
# @return [String]
|
20
|
+
# A client name (node_name) to use with the Chef Client provisioner to upload
|
21
|
+
# cookbooks installed by Berkshelf.
|
22
|
+
attr_accessor :node_name
|
23
|
+
|
24
|
+
# @return [Array<Symbol>]
|
25
|
+
# cookbooks in all other groups except for these will be installed
|
26
|
+
# and copied to Vagrant's shelf
|
27
|
+
attr_accessor :except
|
28
|
+
|
29
|
+
# @return [Array<Symbol>]
|
30
|
+
# only cookbooks in these groups will be installed and copied to
|
31
|
+
# Vagrant's shelf
|
32
|
+
attr_accessor :only
|
33
|
+
|
34
|
+
def initialize
|
35
|
+
@config_path = Berkshelf::DEFAULT_CONFIG
|
36
|
+
@berksfile_path = File.join(Dir.pwd, Berkshelf::DEFAULT_FILENAME)
|
37
|
+
@only = Array.new
|
38
|
+
@except = Array.new
|
39
|
+
end
|
40
|
+
|
41
|
+
def config_path=(value)
|
42
|
+
@config_path = File.expand_path(value)
|
43
|
+
end
|
44
|
+
|
45
|
+
def berksfile_path=(value)
|
46
|
+
@berksfile_path = File.expand_path(value)
|
47
|
+
end
|
48
|
+
|
49
|
+
def client_key=(value)
|
50
|
+
@client_key = File.expand_path(value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate(env, errors)
|
54
|
+
if !except.empty? && !only.empty?
|
55
|
+
errors.add("A value for berkshelf.empty and berkshelf.only cannot both be defined.")
|
56
|
+
end
|
57
|
+
|
58
|
+
if Berkshelf::Vagrant.chef_client?(env.config.global)
|
59
|
+
if node_name.nil?
|
60
|
+
errors.add("A value for berkshelf.node_name is required when using the chef_client provisioner.")
|
61
|
+
end
|
62
|
+
|
63
|
+
if client_key.nil?
|
64
|
+
errors.add("A value for berkshelf.client_key is required when using the chef_client provisioner.")
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Vagrant
|
3
|
+
# @author Jamie Winsor <jamie@vialstudios.com>
|
4
|
+
#
|
5
|
+
# Middleware stacks for use with Vagrant
|
6
|
+
module Middleware
|
7
|
+
class << self
|
8
|
+
# Return the Berkshelf install middleware stack. When placed in the action chain
|
9
|
+
# this stack will find retrieve and resolve the cookbook dependencies describe
|
10
|
+
# in your configured Berksfile.
|
11
|
+
#
|
12
|
+
# Cookbooks will installed into a temporary directory, called a Shelf, and mounted
|
13
|
+
# into the VM. This mounted path will be appended to the chef_solo.cookbooks_path value.
|
14
|
+
#
|
15
|
+
# @return [::Vagrant::Action::Builder]
|
16
|
+
def install
|
17
|
+
@install ||= ::Vagrant::Action::Builder.new do
|
18
|
+
use Berkshelf::Vagrant::Action::SetUI
|
19
|
+
use Berkshelf::Vagrant::Action::Install
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the Berkshelf upload middleware stack. When placed in the action chain
|
24
|
+
# this stack will upload cookbooks to a Chef Server if the Chef-Client provisioner
|
25
|
+
# is used. The Chef Server where the cookbooks will be uploaded to is the same Chef
|
26
|
+
# Server used in the Chef-Client provisioner.
|
27
|
+
#
|
28
|
+
# Nothing will be done if the Chef-Solo provisioner is used.
|
29
|
+
#
|
30
|
+
# @return [::Vagrant::Action::Builder]
|
31
|
+
def upload
|
32
|
+
@upload ||= ::Vagrant::Action::Builder.new do
|
33
|
+
use Berkshelf::Vagrant::Action::SetUI
|
34
|
+
use Berkshelf::Vagrant::Action::Upload
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the Berkshelf clean middleware stack. When placed in the action chain
|
39
|
+
# this stack will clean up any temporary directories or files created by the other
|
40
|
+
# middleware stacks.
|
41
|
+
#
|
42
|
+
# @return [::Vagrant::Action::Builder]
|
43
|
+
def clean
|
44
|
+
@clean ||= ::Vagrant::Action::Builder.new do
|
45
|
+
use Berkshelf::Vagrant::Action::SetUI
|
46
|
+
use Berkshelf::Vagrant::Action::Clean
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/berkshelf/version.rb
CHANGED
data/lib/thor/monkies.rb
ADDED
data/lib/vagrant_init.rb
ADDED
data/spec/spec_helper.rb
CHANGED
data/spec/support/chef_api.rb
CHANGED
@@ -100,7 +100,12 @@ EOF
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def uploader
|
103
|
-
@uploader ||= Berkshelf::Uploader.new(
|
103
|
+
@uploader ||= Berkshelf::Uploader.new(
|
104
|
+
server_url: Chef::Config[:chef_server_url],
|
105
|
+
client_name: Chef::Config[:node_name],
|
106
|
+
client_key: Chef::Config[:client_key],
|
107
|
+
organization: Berkshelf::ChefAPILocation.extract_organization(Chef::Config[:chef_server_url])
|
108
|
+
)
|
104
109
|
end
|
105
110
|
end
|
106
111
|
end
|
@@ -14,7 +14,7 @@ EOF
|
|
14
14
|
describe "ClassMethods" do
|
15
15
|
subject { Berksfile }
|
16
16
|
|
17
|
-
describe "
|
17
|
+
describe "::from_file" do
|
18
18
|
let(:cookbook_file) { fixtures_path.join('lockfile_spec', 'with_lock', 'Berksfile') }
|
19
19
|
|
20
20
|
it "reads a Berksfile and returns an instance Berksfile" do
|
@@ -32,29 +32,12 @@ EOF
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
describe "
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
before(:each) do
|
42
|
-
source_one.stub(:groups) { [:nautilus] }
|
43
|
-
source_two.stub(:groups) { [:riven] }
|
44
|
-
@sources = [source_one, source_two]
|
45
|
-
end
|
46
|
-
|
47
|
-
it "returns an array without sources that were members of the excluded groups" do
|
48
|
-
result = subject.filter_sources(@sources, excluded_groups)
|
49
|
-
|
50
|
-
result.should_not include(source_one)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "does not remove sources that were not a member of the excluded groups" do
|
54
|
-
result = subject.filter_sources(@sources, excluded_groups)
|
55
|
-
|
56
|
-
result.should include(source_two)
|
57
|
-
end
|
35
|
+
describe "::vendor" do
|
36
|
+
it "returns the expanded filepath of the vendor directory" do
|
37
|
+
cached_cookbooks = Array.new
|
38
|
+
tmpdir = Dir.mktmpdir(nil, tmp_path)
|
39
|
+
|
40
|
+
subject.vendor(cached_cookbooks, tmpdir).should eql(tmpdir)
|
58
41
|
end
|
59
42
|
end
|
60
43
|
end
|
@@ -174,6 +157,13 @@ EOF
|
|
174
157
|
end
|
175
158
|
|
176
159
|
describe "#sources" do
|
160
|
+
let(:groups) do
|
161
|
+
[
|
162
|
+
:nautilus,
|
163
|
+
:skarner
|
164
|
+
]
|
165
|
+
end
|
166
|
+
|
177
167
|
it "returns all CookbookSources added to the instance of Berksfile" do
|
178
168
|
subject.add_source(source_one.name)
|
179
169
|
subject.add_source(source_two.name)
|
@@ -183,11 +173,43 @@ EOF
|
|
183
173
|
subject.should have_source(source_two.name)
|
184
174
|
end
|
185
175
|
|
186
|
-
context "given the option :
|
187
|
-
|
188
|
-
|
176
|
+
context "given the option :except" do
|
177
|
+
before(:each) do
|
178
|
+
source_one.stub(:groups) { [:default, :skarner] }
|
179
|
+
source_two.stub(:groups) { [:default, :nautilus] }
|
180
|
+
end
|
181
|
+
|
182
|
+
it "returns all of the sources except the ones in the given groups" do
|
183
|
+
subject.add_source(source_one.name, nil, group: [:default, :skarner])
|
184
|
+
subject.add_source(source_two.name, nil, group: [:default, :nautilus])
|
185
|
+
filtered = subject.sources(except: :nautilus)
|
186
|
+
|
187
|
+
filtered.should have(1).item
|
188
|
+
filtered.first.name.should eql(source_one.name)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "given the option :only" do
|
193
|
+
before(:each) do
|
194
|
+
source_one.stub(:groups) { [:default, :skarner] }
|
195
|
+
source_two.stub(:groups) { [:default, :nautilus] }
|
196
|
+
end
|
197
|
+
|
198
|
+
it "returns only the sources in the givne groups" do
|
199
|
+
subject.add_source(source_one.name, nil, group: [:default, :skarner])
|
200
|
+
subject.add_source(source_two.name, nil, group: [:default, :nautilus])
|
201
|
+
filtered = subject.sources(only: :nautilus)
|
189
202
|
|
190
|
-
|
203
|
+
filtered.should have(1).item
|
204
|
+
filtered.first.name.should eql(source_two.name)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "when a value for :only and :except is given" do
|
209
|
+
it "raises an ArgumentError" do
|
210
|
+
lambda {
|
211
|
+
subject.sources(only: [:default], except: [:other])
|
212
|
+
}.should raise_error(Berkshelf::ArgumentError, "Cannot specify both :except and :only")
|
191
213
|
end
|
192
214
|
end
|
193
215
|
end
|
@@ -233,6 +255,26 @@ EOF
|
|
233
255
|
resolver.should_receive(:sources).and_return([])
|
234
256
|
end
|
235
257
|
|
258
|
+
let(:cached_cookbooks) do
|
259
|
+
[
|
260
|
+
double('cached_one'),
|
261
|
+
double('cached_two')
|
262
|
+
]
|
263
|
+
end
|
264
|
+
|
265
|
+
it "returns the result from sending the message resolve to resolver" do
|
266
|
+
resolver.should_receive(:resolve).and_return(cached_cookbooks)
|
267
|
+
|
268
|
+
subject.install.should eql(cached_cookbooks)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "sets a value for self.cached_cookbooks equivalent to the return value" do
|
272
|
+
resolver.should_receive(:resolve).and_return(cached_cookbooks)
|
273
|
+
subject.install
|
274
|
+
|
275
|
+
subject.cached_cookbooks.should eql(cached_cookbooks)
|
276
|
+
end
|
277
|
+
|
236
278
|
it "creates a new resolver and finds a solution by calling resolve on the resolver" do
|
237
279
|
resolver.should_receive(:resolve)
|
238
280
|
|
@@ -258,42 +300,38 @@ EOF
|
|
258
300
|
end
|
259
301
|
end
|
260
302
|
|
261
|
-
context "when
|
262
|
-
|
263
|
-
let(:cached_two) { double('cached_two', cookbook_name: 'example_cookbook', path: fixtures_path.join("cookbooks", "example_cookbook-0.5.0")) }
|
264
|
-
let(:shims_path) { tmp_path.join("cookbook_shims") }
|
303
|
+
context "when a value for :path is given" do
|
304
|
+
before(:each) { resolver.should_receive(:resolve) }
|
265
305
|
|
266
|
-
|
267
|
-
|
268
|
-
|
306
|
+
it "sends the message 'vendor' to Berksfile with the value for :path" do
|
307
|
+
path = double('path')
|
308
|
+
subject.class.should_receive(:vendor).with(subject.cached_cookbooks, path)
|
269
309
|
|
270
|
-
|
271
|
-
subject.should_receive(:write_shims).with(shims_path, [cached_one, cached_two])
|
272
|
-
subject.install(shims: shims_path)
|
310
|
+
subject.install(path: path)
|
273
311
|
end
|
274
312
|
end
|
275
|
-
end
|
276
313
|
|
277
|
-
|
278
|
-
|
279
|
-
let(:cached_two) { double('cached_two', cookbook_name: 'example_cookbook', path: fixtures_path.join("cookbooks", "example_cookbook-0.5.0")) }
|
280
|
-
let(:shims_path) { tmp_path.join("cookbook_shims") }
|
314
|
+
context "when a value for :except is given" do
|
315
|
+
before(:each) { resolver.should_receive(:resolve) }
|
281
316
|
|
282
|
-
|
317
|
+
it "filters the sources and gives the results to the Resolver initializer" do
|
318
|
+
filtered = double('sources')
|
319
|
+
subject.should_receive(:sources).with(except: [:skip_me]).and_return(filtered)
|
320
|
+
Resolver.should_receive(:new).with(anything, sources: filtered)
|
283
321
|
|
284
|
-
|
285
|
-
|
286
|
-
shims_path.should be_directory
|
322
|
+
subject.install(except: [:skip_me])
|
323
|
+
end
|
287
324
|
end
|
288
325
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
326
|
+
context "when a value for :only is given" do
|
327
|
+
before(:each) { resolver.should_receive(:resolve) }
|
328
|
+
|
329
|
+
it "filters the sources and gives the results to the Resolver initializer" do
|
330
|
+
filtered = double('sources')
|
331
|
+
subject.should_receive(:sources).with(only: [:skip_me]).and_return(filtered)
|
332
|
+
|
333
|
+
subject.install(only: [:skip_me])
|
334
|
+
end
|
297
335
|
end
|
298
336
|
end
|
299
337
|
|