berkshelf 1.3.1 → 1.4.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/CHANGELOG.md +6 -0
- data/berkshelf.gemspec +1 -1
- data/lib/berkshelf.rb +5 -12
- data/lib/berkshelf/berksfile.rb +104 -70
- data/lib/berkshelf/cached_cookbook.rb +1 -1
- data/lib/berkshelf/chef/cookbook/chefignore.rb +15 -0
- data/lib/berkshelf/cli.rb +19 -33
- data/lib/berkshelf/cookbook_source.rb +111 -27
- data/lib/berkshelf/errors.rb +3 -1
- data/lib/berkshelf/locations/git_location.rb +19 -0
- data/lib/berkshelf/locations/path_location.rb +2 -1
- data/lib/berkshelf/logger.rb +9 -0
- data/lib/berkshelf/mixin/logging.rb +18 -0
- data/lib/berkshelf/version.rb +1 -1
- data/spec/support/chef_api.rb +2 -2
- data/spec/unit/berkshelf/berksfile_spec.rb +275 -279
- data/spec/unit/berkshelf/chef/cookbook/chefignore_spec.rb +23 -0
- data/spec/unit/berkshelf/cookbook_source_spec.rb +32 -8
- data/spec/unit/berkshelf/locations/git_location_spec.rb +14 -0
- data/spec/unit/berkshelf/logger_spec.rb +29 -0
- data/spec/unit/berkshelf/mixin/logging_spec.rb +25 -0
- data/spec/unit/berkshelf_spec.rb +2 -2
- metadata +17 -12
data/lib/berkshelf/errors.rb
CHANGED
@@ -65,7 +65,6 @@ module Berkshelf
|
|
65
65
|
class DuplicateSourceDefined < BerkshelfError; status_code(105); end
|
66
66
|
class NoSolution < BerkshelfError; status_code(106); end
|
67
67
|
class CookbookSyntaxError < BerkshelfError; status_code(107); end
|
68
|
-
class UploadFailure < BerkshelfError; status_code(108); end
|
69
68
|
class BerksConfigNotFound < BerkshelfError; status_code(109); end
|
70
69
|
|
71
70
|
class InvalidGitURI < BerkshelfError
|
@@ -150,4 +149,7 @@ module Berkshelf
|
|
150
149
|
class CommunitySiteError < BerkshelfError; status_code(123); end
|
151
150
|
class CookbookValidationFailure < BerkshelfError; status_code(124); end
|
152
151
|
class ClientKeyFileNotFound < BerkshelfError; status_code(125); end
|
152
|
+
|
153
|
+
class UploadFailure < BerkshelfError; end
|
154
|
+
class FrozenCookbook < UploadFailure; status_code(126); end
|
153
155
|
end
|
@@ -20,6 +20,7 @@ module Berkshelf
|
|
20
20
|
attr_accessor :uri
|
21
21
|
attr_accessor :branch
|
22
22
|
attr_accessor :rel
|
23
|
+
attr_reader :options
|
23
24
|
|
24
25
|
alias_method :ref, :branch
|
25
26
|
alias_method :tag, :branch
|
@@ -52,6 +53,8 @@ module Berkshelf
|
|
52
53
|
#
|
53
54
|
# @return [Berkshelf::CachedCookbook]
|
54
55
|
def download(destination)
|
56
|
+
return local_revision(destination) if cached?(destination)
|
57
|
+
|
55
58
|
::Berkshelf::Git.checkout(clone, branch) if branch
|
56
59
|
unless branch
|
57
60
|
self.branch = ::Berkshelf::Git.rev_parse(clone)
|
@@ -104,5 +107,21 @@ module Berkshelf
|
|
104
107
|
|
105
108
|
tmp_clone
|
106
109
|
end
|
110
|
+
|
111
|
+
def cached?(destination)
|
112
|
+
revision_path(destination) && File.exists?(revision_path(destination))
|
113
|
+
end
|
114
|
+
|
115
|
+
def local_revision(destination)
|
116
|
+
path = revision_path(destination)
|
117
|
+
cached = Berkshelf::CachedCookbook.from_store_path(path)
|
118
|
+
validate_cached(cached)
|
119
|
+
return cached
|
120
|
+
end
|
121
|
+
|
122
|
+
def revision_path(destination)
|
123
|
+
return unless branch
|
124
|
+
File.join(destination, "#{name}-#{branch}")
|
125
|
+
end
|
107
126
|
end
|
108
127
|
end
|
@@ -25,6 +25,7 @@ module Berkshelf
|
|
25
25
|
set_location_key :path
|
26
26
|
|
27
27
|
attr_accessor :path
|
28
|
+
attr_reader :name
|
28
29
|
|
29
30
|
# @param [#to_s] name
|
30
31
|
# @param [Solve::Constraint] version_constraint
|
@@ -43,7 +44,7 @@ module Berkshelf
|
|
43
44
|
#
|
44
45
|
# @return [Berkshelf::CachedCookbook]
|
45
46
|
def download(destination)
|
46
|
-
cached = CachedCookbook.from_path(
|
47
|
+
cached = CachedCookbook.from_path(path, name: name)
|
47
48
|
validate_cached(cached)
|
48
49
|
|
49
50
|
set_downloaded_status(true)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Berkshelf
|
2
|
+
module Mixin
|
3
|
+
# @author Jamie Winsor <reset@riotgames.com>
|
4
|
+
module Logging
|
5
|
+
def log
|
6
|
+
Berkshelf::Logger
|
7
|
+
end
|
8
|
+
|
9
|
+
# Log an exception and it's backtrace to FATAL
|
10
|
+
#
|
11
|
+
# @param [Exception] ex
|
12
|
+
def log_exception(ex)
|
13
|
+
log.fatal("#{ex.class}: #{ex}")
|
14
|
+
log.fatal(ex.backtrace.join("\n")) unless ex.backtrace.nil?
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/berkshelf/version.rb
CHANGED
data/spec/support/chef_api.rb
CHANGED
@@ -20,9 +20,9 @@ module Berkshelf
|
|
20
20
|
# @param [#to_s] version
|
21
21
|
def purge_cookbook(name, version = nil)
|
22
22
|
if version.nil?
|
23
|
-
ridley.cookbook.delete_all(name
|
23
|
+
ridley.cookbook.delete_all(name)
|
24
24
|
else
|
25
|
-
ridley.cookbook.delete(name, version
|
25
|
+
ridley.cookbook.delete(name, version)
|
26
26
|
end
|
27
27
|
rescue Ridley::Errors::HTTPNotFound
|
28
28
|
true
|
@@ -1,411 +1,407 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
let(:content) do
|
3
|
+
describe Berkshelf::Berksfile do
|
4
|
+
let(:content) do
|
6
5
|
<<-EOF
|
7
6
|
cookbook 'ntp', '<= 1.0.0'
|
8
7
|
cookbook 'mysql'
|
9
8
|
cookbook 'nginx', '< 0.101.2'
|
10
9
|
cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_hosts2.git'
|
11
10
|
EOF
|
12
|
-
|
11
|
+
end
|
13
12
|
|
14
|
-
|
15
|
-
|
13
|
+
describe "ClassMethods" do
|
14
|
+
subject { described_class }
|
16
15
|
|
17
|
-
|
18
|
-
|
16
|
+
describe "::from_file" do
|
17
|
+
let(:cookbook_file) { fixtures_path.join('lockfile_spec', 'with_lock', 'Berksfile') }
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
19
|
+
it "reads a Berksfile and returns an instance Berksfile" do
|
20
|
+
subject.from_file(cookbook_file).should be_a(Berkshelf::Berksfile)
|
21
|
+
end
|
23
22
|
|
24
|
-
|
25
|
-
|
23
|
+
context "when Berksfile does not exist at given path" do
|
24
|
+
let(:bad_path) { tmp_path.join("thisdoesnotexist") }
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
26
|
+
it "raises BerksfileNotFound" do
|
27
|
+
lambda {
|
28
|
+
subject.from_file(bad_path)
|
29
|
+
}.should raise_error(Berkshelf::BerksfileNotFound)
|
32
30
|
end
|
33
31
|
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "::vendor" do
|
35
|
+
let(:cached_cookbooks) { [] }
|
36
|
+
let(:tmpdir) { Dir.mktmpdir(nil, tmp_path) }
|
34
37
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
+
it "returns the expanded filepath of the vendor directory" do
|
39
|
+
subject.vendor(cached_cookbooks, tmpdir).should eql(tmpdir)
|
40
|
+
end
|
38
41
|
|
39
|
-
|
40
|
-
|
42
|
+
context "with a chefignore" do
|
43
|
+
it "finds a chefignore file" do
|
44
|
+
Berkshelf::Chef::Cookbook::Chefignore.should_receive(:find_relative_to).and_return(File.expand_path('chefignore'))
|
45
|
+
subject.vendor(cached_cookbooks, tmpdir)
|
41
46
|
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
|
49
|
-
it "finds a chefignore file" do
|
50
|
-
Berkshelf::Chef::Cookbook::Chefignore.should_receive(:new).with(File.expand_path('chefignore'))
|
51
|
-
subject.vendor(cached_cookbooks, tmpdir)
|
52
|
-
end
|
53
|
-
|
54
|
-
it "removes files in chefignore" do
|
55
|
-
cached_cookbooks = [ CachedCookbook.from_path(fixtures_path.join('cookbooks/example_cookbook')) ]
|
56
|
-
FileUtils.should_receive(:cp_r).with(['metadata.rb'], anything()).exactly(1).times
|
57
|
-
FileUtils.should_receive(:cp_r).with(anything(), anything(), anything()).once
|
58
|
-
subject.vendor(cached_cookbooks, tmpdir)
|
59
|
-
end
|
48
|
+
it "ignores files specified by a chefignore if a chefignore is present" do
|
49
|
+
Berkshelf::Chef::Cookbook::Chefignore.should_receive(:find_relative_to).and_return(File.expand_path('chefignore'))
|
50
|
+
Berkshelf::Chef::Cookbook::Chefignore.any_instance.stub(:remove_ignores_from).and_return(['metadata.rb'])
|
51
|
+
subject.vendor(cached_cookbooks, tmpdir)
|
60
52
|
end
|
61
53
|
end
|
62
54
|
end
|
55
|
+
end
|
63
56
|
|
64
|
-
|
65
|
-
|
57
|
+
let(:source_one) { double('source_one', name: "nginx") }
|
58
|
+
let(:source_two) { double('source_two', name: "mysql") }
|
66
59
|
|
67
|
-
|
60
|
+
subject { described_class.new(tmp_path.join("Berksfile")) }
|
68
61
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
62
|
+
describe "#cookbook" do
|
63
|
+
let(:name) { "artifact" }
|
64
|
+
let(:constraint) { double('constraint') }
|
65
|
+
let(:default_options) { { group: [] } }
|
73
66
|
|
74
|
-
|
75
|
-
|
67
|
+
it "sends the add_source message with the name, constraint, and options to the instance of the includer" do
|
68
|
+
subject.should_receive(:add_source).with(name, constraint, default_options)
|
76
69
|
|
77
|
-
|
78
|
-
|
70
|
+
subject.cookbook name, constraint, default_options
|
71
|
+
end
|
79
72
|
|
80
|
-
|
81
|
-
|
73
|
+
it "merges the default options into specified options" do
|
74
|
+
subject.should_receive(:add_source).with(name, constraint, path: "/Users/reset", group: [])
|
82
75
|
|
83
|
-
|
84
|
-
|
76
|
+
subject.cookbook name, constraint, path: "/Users/reset"
|
77
|
+
end
|
85
78
|
|
86
|
-
|
87
|
-
|
79
|
+
it "converts a single specified group option into an array of groups" do
|
80
|
+
subject.should_receive(:add_source).with(name, constraint, group: [:production])
|
88
81
|
|
89
|
-
|
90
|
-
|
82
|
+
subject.cookbook name, constraint, group: :production
|
83
|
+
end
|
91
84
|
|
92
|
-
|
93
|
-
|
94
|
-
|
85
|
+
context "when no constraint specified" do
|
86
|
+
it "sends the add_source message with a nil value for constraint" do
|
87
|
+
subject.should_receive(:add_source).with(name, nil, default_options)
|
95
88
|
|
96
|
-
|
97
|
-
end
|
89
|
+
subject.cookbook name, default_options
|
98
90
|
end
|
91
|
+
end
|
99
92
|
|
100
|
-
|
101
|
-
|
102
|
-
|
93
|
+
context "when no options specified" do
|
94
|
+
it "sends the add_source message with an empty Hash for the value of options" do
|
95
|
+
subject.should_receive(:add_source).with(name, constraint, default_options)
|
103
96
|
|
104
|
-
|
105
|
-
end
|
97
|
+
subject.cookbook name, constraint
|
106
98
|
end
|
107
99
|
end
|
100
|
+
end
|
108
101
|
|
109
|
-
|
110
|
-
|
111
|
-
|
102
|
+
describe '#group' do
|
103
|
+
let(:name) { "artifact" }
|
104
|
+
let(:group) { "production" }
|
112
105
|
|
113
|
-
|
114
|
-
|
106
|
+
it "sends the add_source message with an array of groups determined by the parameter passed to the group block" do
|
107
|
+
subject.should_receive(:add_source).with(name, nil, group: [group])
|
115
108
|
|
116
|
-
|
117
|
-
|
118
|
-
end
|
109
|
+
subject.group group do
|
110
|
+
subject.cookbook name
|
119
111
|
end
|
120
112
|
end
|
113
|
+
end
|
121
114
|
|
122
|
-
|
123
|
-
|
124
|
-
|
115
|
+
describe "#metadata" do
|
116
|
+
let(:cb_path) { fixtures_path.join('cookbooks/example_cookbook') }
|
117
|
+
subject { described_class.new(cb_path.join("Berksfile")) }
|
125
118
|
|
126
|
-
|
119
|
+
before(:each) { Dir.chdir(cb_path) }
|
127
120
|
|
128
|
-
|
129
|
-
|
121
|
+
it "sends the add_source message with an explicit version constraint and the path to the cookbook" do
|
122
|
+
subject.should_receive(:add_source).with("example_cookbook", "= 0.5.0", path: cb_path.to_s)
|
130
123
|
|
131
|
-
|
132
|
-
end
|
124
|
+
subject.metadata
|
133
125
|
end
|
126
|
+
end
|
134
127
|
|
135
|
-
|
136
|
-
|
128
|
+
describe "#site" do
|
129
|
+
let(:uri) { "http://opscode/v1" }
|
137
130
|
|
138
|
-
|
139
|
-
|
131
|
+
it "sends the add_location to the instance of the implementing class with a SiteLocation" do
|
132
|
+
subject.should_receive(:add_location).with(:site, uri)
|
140
133
|
|
141
|
-
|
142
|
-
|
134
|
+
subject.site(uri)
|
135
|
+
end
|
143
136
|
|
144
|
-
|
145
|
-
|
146
|
-
|
137
|
+
context "given the symbol :opscode" do
|
138
|
+
it "sends an add_location message with the default Opscode Community API as the first parameter" do
|
139
|
+
subject.should_receive(:add_location).with(:site, :opscode)
|
147
140
|
|
148
|
-
|
149
|
-
end
|
141
|
+
subject.site(:opscode)
|
150
142
|
end
|
151
143
|
end
|
144
|
+
end
|
152
145
|
|
153
|
-
|
154
|
-
|
146
|
+
describe "#chef_api" do
|
147
|
+
let(:uri) { "http://chef:8080/" }
|
155
148
|
|
156
|
-
|
157
|
-
|
149
|
+
it "sends and add_location message with the type :chef_api and the given URI" do
|
150
|
+
subject.should_receive(:add_location).with(:chef_api, uri, {})
|
158
151
|
|
159
|
-
|
160
|
-
|
152
|
+
subject.chef_api(uri)
|
153
|
+
end
|
161
154
|
|
162
|
-
|
163
|
-
|
164
|
-
|
155
|
+
it "also sends any options passed" do
|
156
|
+
options = { node_name: "reset", client_key: "/Users/reset/.chef/reset.pem" }
|
157
|
+
subject.should_receive(:add_location).with(:chef_api, uri, options)
|
165
158
|
|
166
|
-
|
167
|
-
|
159
|
+
subject.chef_api(uri, options)
|
160
|
+
end
|
168
161
|
|
169
|
-
|
170
|
-
|
171
|
-
|
162
|
+
context "given the symbol :config" do
|
163
|
+
it "sends an add_location message with the the type :chef_api and the URI :config" do
|
164
|
+
subject.should_receive(:add_location).with(:chef_api, :config, {})
|
172
165
|
|
173
|
-
|
174
|
-
end
|
166
|
+
subject.chef_api(:config)
|
175
167
|
end
|
176
168
|
end
|
169
|
+
end
|
177
170
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
171
|
+
describe "#sources" do
|
172
|
+
let(:groups) do
|
173
|
+
[
|
174
|
+
:nautilus,
|
175
|
+
:skarner
|
176
|
+
]
|
177
|
+
end
|
185
178
|
|
186
|
-
|
187
|
-
|
188
|
-
|
179
|
+
it "returns all CookbookSources added to the instance of Berksfile" do
|
180
|
+
subject.add_source(source_one.name)
|
181
|
+
subject.add_source(source_two.name)
|
189
182
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
183
|
+
subject.sources.should have(2).items
|
184
|
+
subject.should have_source(source_one.name)
|
185
|
+
subject.should have_source(source_two.name)
|
186
|
+
end
|
194
187
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
188
|
+
context "given the option :except" do
|
189
|
+
before(:each) do
|
190
|
+
source_one.stub(:groups) { [:default, :skarner] }
|
191
|
+
source_two.stub(:groups) { [:default, :nautilus] }
|
192
|
+
end
|
200
193
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
194
|
+
it "returns all of the sources except the ones in the given groups" do
|
195
|
+
subject.add_source(source_one.name, nil, group: [:default, :skarner])
|
196
|
+
subject.add_source(source_two.name, nil, group: [:default, :nautilus])
|
197
|
+
filtered = subject.sources(except: :nautilus)
|
205
198
|
|
206
|
-
|
207
|
-
|
208
|
-
end
|
199
|
+
filtered.should have(1).item
|
200
|
+
filtered.first.name.should eql(source_one.name)
|
209
201
|
end
|
202
|
+
end
|
210
203
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
204
|
+
context "given the option :only" do
|
205
|
+
before(:each) do
|
206
|
+
source_one.stub(:groups) { [:default, :skarner] }
|
207
|
+
source_two.stub(:groups) { [:default, :nautilus] }
|
208
|
+
end
|
216
209
|
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
210
|
+
it "returns only the sources in the givne groups" do
|
211
|
+
subject.add_source(source_one.name, nil, group: [:default, :skarner])
|
212
|
+
subject.add_source(source_two.name, nil, group: [:default, :nautilus])
|
213
|
+
filtered = subject.sources(only: :nautilus)
|
221
214
|
|
222
|
-
|
223
|
-
|
224
|
-
end
|
215
|
+
filtered.should have(1).item
|
216
|
+
filtered.first.name.should eql(source_two.name)
|
225
217
|
end
|
218
|
+
end
|
226
219
|
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
end
|
220
|
+
context "when a value for :only and :except is given" do
|
221
|
+
it "raises an ArgumentError" do
|
222
|
+
lambda {
|
223
|
+
subject.sources(only: [:default], except: [:other])
|
224
|
+
}.should raise_error(Berkshelf::ArgumentError, "Cannot specify both :except and :only")
|
233
225
|
end
|
234
226
|
end
|
227
|
+
end
|
235
228
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
229
|
+
describe "#groups" do
|
230
|
+
before(:each) do
|
231
|
+
subject.stub(:sources) { [source_one, source_two] }
|
232
|
+
source_one.stub(:groups) { [:nautilus, :skarner] }
|
233
|
+
source_two.stub(:groups) { [:nautilus, :riven] }
|
234
|
+
end
|
242
235
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
236
|
+
it "returns a hash containing keys for every group a source is a member of" do
|
237
|
+
subject.groups.keys.should have(3).items
|
238
|
+
subject.groups.should have_key(:nautilus)
|
239
|
+
subject.groups.should have_key(:skarner)
|
240
|
+
subject.groups.should have_key(:riven)
|
241
|
+
end
|
249
242
|
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
end
|
243
|
+
it "returns an Array of CookbookSources who are members of the group for value" do
|
244
|
+
subject.groups[:nautilus].should have(2).items
|
245
|
+
subject.groups[:riven].should have(1).item
|
254
246
|
end
|
247
|
+
end
|
255
248
|
|
256
|
-
|
257
|
-
|
258
|
-
|
249
|
+
describe "#resolve" do
|
250
|
+
let(:resolver) { double('resolver') }
|
251
|
+
before(:each) { Berkshelf::Resolver.stub(:new) { resolver } }
|
259
252
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
end
|
253
|
+
it "resolves the Berksfile" do
|
254
|
+
resolver.should_receive(:resolve).and_return([double('cached_cookbook_one'), double('cached_cookbook_two')])
|
255
|
+
solution = subject.resolve
|
256
|
+
solution.should have(2).items
|
265
257
|
end
|
258
|
+
end
|
266
259
|
|
267
|
-
|
268
|
-
|
269
|
-
|
260
|
+
describe "#install" do
|
261
|
+
let(:resolver) { double('resolver') }
|
262
|
+
before(:each) { Berkshelf::Resolver.stub(:new) { resolver } }
|
270
263
|
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
264
|
+
context "when a lockfile is not present" do
|
265
|
+
before(:each) do
|
266
|
+
subject.should_receive(:lockfile_present?).and_return(false)
|
267
|
+
resolver.should_receive(:sources).and_return([])
|
268
|
+
end
|
276
269
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
270
|
+
let(:cached_cookbooks) do
|
271
|
+
[
|
272
|
+
double('cached_one'),
|
273
|
+
double('cached_two')
|
274
|
+
]
|
275
|
+
end
|
283
276
|
|
284
|
-
|
285
|
-
|
277
|
+
it "returns the result from sending the message resolve to resolver" do
|
278
|
+
resolver.should_receive(:resolve).and_return(cached_cookbooks)
|
286
279
|
|
287
|
-
|
288
|
-
|
280
|
+
subject.install.should eql(cached_cookbooks)
|
281
|
+
end
|
289
282
|
|
290
|
-
|
291
|
-
|
292
|
-
|
283
|
+
it "sets a value for self.cached_cookbooks equivalent to the return value" do
|
284
|
+
resolver.should_receive(:resolve).and_return(cached_cookbooks)
|
285
|
+
subject.install
|
293
286
|
|
294
|
-
|
295
|
-
|
287
|
+
subject.cached_cookbooks.should eql(cached_cookbooks)
|
288
|
+
end
|
296
289
|
|
297
|
-
|
298
|
-
|
290
|
+
it "creates a new resolver and finds a solution by calling resolve on the resolver" do
|
291
|
+
resolver.should_receive(:resolve)
|
299
292
|
|
300
|
-
|
301
|
-
|
293
|
+
subject.install
|
294
|
+
end
|
302
295
|
|
303
|
-
|
304
|
-
|
305
|
-
|
296
|
+
it "writes a lockfile with the resolvers sources" do
|
297
|
+
resolver.should_receive(:resolve)
|
298
|
+
subject.should_receive(:write_lockfile).with([])
|
306
299
|
|
307
|
-
|
308
|
-
end
|
300
|
+
subject.install
|
309
301
|
end
|
302
|
+
end
|
310
303
|
|
311
|
-
|
312
|
-
|
304
|
+
context "when a lockfile is present" do
|
305
|
+
before(:each) { subject.should_receive(:lockfile_present?).and_return(true) }
|
313
306
|
|
314
|
-
|
315
|
-
|
316
|
-
|
307
|
+
it "does not write a new lock file" do
|
308
|
+
resolver.should_receive(:resolve)
|
309
|
+
subject.should_not_receive(:write_lockfile)
|
317
310
|
|
318
|
-
|
319
|
-
end
|
311
|
+
subject.install
|
320
312
|
end
|
313
|
+
end
|
321
314
|
|
322
|
-
|
323
|
-
|
315
|
+
context "when a value for :path is given" do
|
316
|
+
before(:each) { resolver.should_receive(:resolve) }
|
324
317
|
|
325
|
-
|
326
|
-
|
327
|
-
|
318
|
+
it "sends the message 'vendor' to Berksfile with the value for :path" do
|
319
|
+
path = double('path')
|
320
|
+
subject.class.should_receive(:vendor).with(subject.cached_cookbooks, path)
|
328
321
|
|
329
|
-
|
330
|
-
end
|
322
|
+
subject.install(path: path)
|
331
323
|
end
|
324
|
+
end
|
332
325
|
|
333
|
-
|
334
|
-
|
326
|
+
context "when a value for :except is given" do
|
327
|
+
before(:each) { resolver.should_receive(:resolve) }
|
335
328
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
329
|
+
it "filters the sources and gives the results to the Resolver initializer" do
|
330
|
+
filtered = double('sources')
|
331
|
+
subject.should_receive(:sources).with(except: [:skip_me]).and_return(filtered)
|
332
|
+
Berkshelf::Resolver.should_receive(:new).with(anything, sources: filtered)
|
340
333
|
|
341
|
-
|
342
|
-
end
|
334
|
+
subject.install(except: [:skip_me])
|
343
335
|
end
|
336
|
+
end
|
344
337
|
|
345
|
-
|
346
|
-
|
338
|
+
context "when a value for :only is given" do
|
339
|
+
before(:each) { resolver.should_receive(:resolve) }
|
347
340
|
|
348
|
-
|
349
|
-
|
350
|
-
|
341
|
+
it "filters the sources and gives the results to the Resolver initializer" do
|
342
|
+
filtered = double('sources')
|
343
|
+
subject.should_receive(:sources).with(only: [:skip_me]).and_return(filtered)
|
351
344
|
|
352
|
-
|
353
|
-
end
|
345
|
+
subject.install(only: [:skip_me])
|
354
346
|
end
|
355
347
|
end
|
348
|
+
end
|
356
349
|
|
357
|
-
|
358
|
-
|
359
|
-
|
350
|
+
describe "#load" do
|
351
|
+
it "reads the content of a Berksfile and adds the sources to the Shelf" do
|
352
|
+
subject.load(content)
|
360
353
|
|
361
|
-
|
362
|
-
|
363
|
-
end
|
354
|
+
['ntp', 'mysql', 'nginx', 'ssh_known_hosts2'].each do |name|
|
355
|
+
subject.should have_source(name)
|
364
356
|
end
|
357
|
+
end
|
365
358
|
|
366
|
-
|
367
|
-
|
368
|
-
end
|
359
|
+
it "returns an instance of Berksfile" do
|
360
|
+
subject.load(content).should be_a(described_class)
|
369
361
|
end
|
362
|
+
end
|
370
363
|
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
364
|
+
describe "#add_source" do
|
365
|
+
let(:name) { "cookbook_one" }
|
366
|
+
let(:constraint) { "= 1.2.0" }
|
367
|
+
let(:location) { { site: "http://site" } }
|
375
368
|
|
376
|
-
|
377
|
-
|
378
|
-
|
369
|
+
before(:each) do
|
370
|
+
subject.add_source(name, constraint, location)
|
371
|
+
end
|
379
372
|
|
380
|
-
|
381
|
-
|
382
|
-
|
373
|
+
it "adds new cookbook source to the list of sources" do
|
374
|
+
subject.sources.should have(1).source
|
375
|
+
end
|
383
376
|
|
384
|
-
|
385
|
-
|
386
|
-
|
377
|
+
it "adds a cookbook source with a 'name' of the given name" do
|
378
|
+
subject.sources.first.name.should eql(name)
|
379
|
+
end
|
387
380
|
|
388
|
-
|
389
|
-
|
390
|
-
|
381
|
+
it "adds a cookbook source with a 'version_constraint' of the given constraint" do
|
382
|
+
subject.sources.first.version_constraint.to_s.should eql(constraint)
|
383
|
+
end
|
391
384
|
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
end
|
385
|
+
it "raises DuplicateSourceDefined if multiple sources of the same name are found" do
|
386
|
+
lambda {
|
387
|
+
subject.add_source(name)
|
388
|
+
}.should raise_error(Berkshelf::DuplicateSourceDefined)
|
397
389
|
end
|
390
|
+
end
|
398
391
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
392
|
+
describe "#add_location" do
|
393
|
+
let(:type) { :site }
|
394
|
+
let(:value) { double('value') }
|
395
|
+
let(:options) { double('options') }
|
403
396
|
|
404
|
-
|
405
|
-
|
397
|
+
it "delegates 'add_location' to the downloader" do
|
398
|
+
subject.downloader.should_receive(:add_location).with(type, value, options)
|
406
399
|
|
407
|
-
|
408
|
-
end
|
400
|
+
subject.add_location(type, value, options)
|
409
401
|
end
|
410
402
|
end
|
403
|
+
|
404
|
+
describe "#upload" do
|
405
|
+
pending
|
406
|
+
end
|
411
407
|
end
|