berkshelf 0.3.7 → 0.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/.gitignore +2 -1
- data/README.md +8 -0
- data/Thorfile +2 -2
- data/berkshelf.gemspec +1 -1
- data/features/install.feature +102 -2
- data/features/lockfile.feature +1 -1
- data/features/step_definitions/chef_server_steps.rb +8 -0
- data/features/step_definitions/cli_steps.rb +1 -1
- data/features/step_definitions/filesystem_steps.rb +12 -0
- data/features/support/env.rb +8 -10
- data/features/update.feature +1 -1
- data/lib/berkshelf.rb +19 -1
- data/lib/berkshelf/berksfile.rb +18 -6
- data/lib/berkshelf/cli.rb +3 -8
- data/lib/berkshelf/cookbook_source.rb +108 -23
- data/lib/berkshelf/cookbook_source/chef_api_location.rb +256 -0
- data/lib/berkshelf/cookbook_source/git_location.rb +14 -2
- data/lib/berkshelf/cookbook_source/location.rb +119 -2
- data/lib/berkshelf/cookbook_source/path_location.rb +6 -1
- data/lib/berkshelf/cookbook_source/site_location.rb +36 -105
- data/lib/berkshelf/cookbook_store.rb +7 -12
- data/lib/berkshelf/dsl.rb +1 -1
- data/lib/berkshelf/errors.rb +2 -0
- data/lib/berkshelf/init_generator.rb +6 -6
- data/lib/berkshelf/resolver.rb +8 -34
- data/lib/berkshelf/uploader.rb +7 -7
- data/lib/berkshelf/version.rb +1 -1
- data/spec/fixtures/reset.pem +27 -0
- data/spec/knife.rb.sample +12 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/support/chef_api.rb +32 -0
- data/spec/support/knife.rb +18 -0
- data/spec/unit/berkshelf/cookbook_source/chef_api_location_spec.rb +243 -0
- data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +2 -2
- data/spec/unit/berkshelf/cookbook_source/location_spec.rb +130 -2
- data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +2 -2
- data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +22 -105
- data/spec/unit/berkshelf/cookbook_source_spec.rb +140 -71
- data/spec/unit/berkshelf/cookbook_store_spec.rb +6 -6
- data/spec/unit/berkshelf/resolver_spec.rb +9 -30
- data/spec/unit/berkshelf/uploader_spec.rb +1 -10
- data/spec/unit/berkshelf_spec.rb +21 -1
- metadata +19 -15
- data/features/config.sample.yml +0 -3
@@ -3,9 +3,6 @@ require 'fileutils'
|
|
3
3
|
module Berkshelf
|
4
4
|
# @author Jamie Winsor <jamie@vialstudios.com>
|
5
5
|
class CookbookStore
|
6
|
-
include DepSelector
|
7
|
-
include DepSelector::Exceptions
|
8
|
-
|
9
6
|
attr_reader :storage_path
|
10
7
|
|
11
8
|
# Create a new instance of CookbookStore with the given
|
@@ -69,27 +66,25 @@ module Berkshelf
|
|
69
66
|
# constraint. Nil is returned if no matching CachedCookbook is found.
|
70
67
|
#
|
71
68
|
# @param [#to_s] name
|
72
|
-
# @param [
|
69
|
+
# @param [Solve::Constraint] constraint
|
73
70
|
#
|
74
71
|
# @return [Berkshelf::CachedCookbook, nil]
|
75
72
|
def satisfy(name, constraint)
|
76
|
-
graph
|
77
|
-
|
78
|
-
|
79
|
-
solution_constraints = [ SolutionConstraint.new(graph.package(name), constraint) ]
|
73
|
+
graph = Solve::Graph.new
|
74
|
+
cookbooks(name).each { |cookbook| graph.artifacts(name, cookbook.version) }
|
75
|
+
graph.demands(name, constraint)
|
80
76
|
|
81
|
-
|
82
|
-
name, version = quietly { selector.find_solution(solution_constraints).first }
|
77
|
+
name, version = Solve.it!(graph).first
|
83
78
|
|
84
79
|
cookbook(name, version)
|
85
|
-
rescue
|
80
|
+
rescue Solve::NoSolutionError
|
86
81
|
nil
|
87
82
|
end
|
88
83
|
|
89
84
|
private
|
90
85
|
|
91
86
|
def initialize_filesystem
|
92
|
-
FileUtils.mkdir_p(storage_path, :
|
87
|
+
FileUtils.mkdir_p(storage_path, mode: 0755)
|
93
88
|
end
|
94
89
|
end
|
95
90
|
end
|
data/lib/berkshelf/dsl.rb
CHANGED
data/lib/berkshelf/errors.rb
CHANGED
@@ -11,6 +11,7 @@ module Berkshelf
|
|
11
11
|
alias_method :message, :to_s
|
12
12
|
end
|
13
13
|
|
14
|
+
class InternalError < BerkshelfError; status_code(99); end
|
14
15
|
class BerksfileNotFound < BerkshelfError; status_code(100); end
|
15
16
|
class NoVersionForConstraints < BerkshelfError; status_code(101); end
|
16
17
|
class DownloadFailure < BerkshelfError; status_code(102); end
|
@@ -58,4 +59,5 @@ module Berkshelf
|
|
58
59
|
end
|
59
60
|
|
60
61
|
class ConstraintNotSatisfied < BerkshelfError; status_code(111); end
|
62
|
+
class InvalidChefAPILocation < BerkshelfError; status_code(112); end
|
61
63
|
end
|
@@ -12,16 +12,16 @@ module Berkshelf
|
|
12
12
|
include Thor::Actions
|
13
13
|
|
14
14
|
argument :path,
|
15
|
-
:
|
16
|
-
:
|
15
|
+
type: :string,
|
16
|
+
required: true
|
17
17
|
|
18
18
|
class_option :metadata_entry,
|
19
|
-
:
|
20
|
-
:
|
19
|
+
type: :boolean,
|
20
|
+
default: false
|
21
21
|
|
22
22
|
class_option :chefignore,
|
23
|
-
:
|
24
|
-
:
|
23
|
+
type: :boolean,
|
24
|
+
default: false
|
25
25
|
|
26
26
|
def generate
|
27
27
|
target_path = File.expand_path(path)
|
data/lib/berkshelf/resolver.rb
CHANGED
@@ -2,16 +2,14 @@ module Berkshelf
|
|
2
2
|
# @author Jamie Winsor <jamie@vialstudios.com>
|
3
3
|
class Resolver
|
4
4
|
extend Forwardable
|
5
|
-
include DepSelector
|
6
5
|
|
7
|
-
|
8
|
-
def_delegator :@graph, :packages
|
6
|
+
attr_reader :graph
|
9
7
|
|
10
8
|
# @param [Downloader] downloader
|
11
9
|
# @param [Array<CookbookSource>, CookbookSource] sources
|
12
10
|
def initialize(downloader, sources = Array.new)
|
13
11
|
@downloader = downloader
|
14
|
-
@graph =
|
12
|
+
@graph = Solve::Graph.new
|
15
13
|
@sources = Hash.new
|
16
14
|
|
17
15
|
# Dependencies need to be added AFTER the sources. If they are
|
@@ -46,8 +44,7 @@ module Berkshelf
|
|
46
44
|
set_source(source)
|
47
45
|
use_source(source) || install_source(source)
|
48
46
|
|
49
|
-
|
50
|
-
package_version = add_version(package, Version.new(source.cached_cookbook.version))
|
47
|
+
graph.artifacts(source.name, source.cached_cookbook.version)
|
51
48
|
|
52
49
|
if include_dependencies
|
53
50
|
add_source_dependencies(source)
|
@@ -84,7 +81,11 @@ module Berkshelf
|
|
84
81
|
#
|
85
82
|
# @return [Array<Berkshelf::CachedCookbook>]
|
86
83
|
def resolve
|
87
|
-
|
84
|
+
graph.artifacts.each do |artifact|
|
85
|
+
graph.demands(artifact.name)
|
86
|
+
end
|
87
|
+
|
88
|
+
solution = Solve.it!(graph)
|
88
89
|
|
89
90
|
[].tap do |cached_cookbooks|
|
90
91
|
solution.each do |name, version|
|
@@ -114,7 +115,6 @@ module Berkshelf
|
|
114
115
|
private
|
115
116
|
|
116
117
|
attr_reader :downloader
|
117
|
-
attr_reader :graph
|
118
118
|
|
119
119
|
# @param [CookbookSource] source
|
120
120
|
def set_source(source)
|
@@ -164,31 +164,5 @@ module Berkshelf
|
|
164
164
|
|
165
165
|
true
|
166
166
|
end
|
167
|
-
|
168
|
-
def selector
|
169
|
-
Selector.new(graph)
|
170
|
-
end
|
171
|
-
|
172
|
-
def solution_constraints
|
173
|
-
constraints = graph.packages.collect do |name, package|
|
174
|
-
SolutionConstraint.new(package)
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
# @param [String] name
|
179
|
-
# name of the package to add to the graph
|
180
|
-
def add_package(name)
|
181
|
-
graph.package(name)
|
182
|
-
end
|
183
|
-
|
184
|
-
# Add a version to a package
|
185
|
-
#
|
186
|
-
# @param [DepSelector::Package] package
|
187
|
-
# the package to add a version to
|
188
|
-
# @param [DepSelector::Version] version
|
189
|
-
# the version to add the the package
|
190
|
-
def add_version(package, version)
|
191
|
-
package.add_version(version)
|
192
|
-
end
|
193
167
|
end
|
194
168
|
end
|
data/lib/berkshelf/uploader.rb
CHANGED
@@ -71,7 +71,7 @@ module Berkshelf
|
|
71
71
|
memo
|
72
72
|
end
|
73
73
|
|
74
|
-
rest.post_rest("sandboxes", :
|
74
|
+
rest.post_rest("sandboxes", checksums: massaged_sums)
|
75
75
|
end
|
76
76
|
|
77
77
|
def commit_sandbox(sandbox)
|
@@ -107,13 +107,13 @@ module Berkshelf
|
|
107
107
|
file_contents = File.open(file, "rb") {|f| f.read}
|
108
108
|
|
109
109
|
sign_obj = Mixlib::Authentication::SignedHeaderAuth.signing_object(
|
110
|
-
:
|
111
|
-
:
|
112
|
-
:
|
113
|
-
:
|
114
|
-
:
|
110
|
+
http_method: :put,
|
111
|
+
path: URI.parse(url).path,
|
112
|
+
body: file_contents,
|
113
|
+
timestamp: timestamp,
|
114
|
+
user_id: rest.client_name
|
115
115
|
)
|
116
|
-
headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, :
|
116
|
+
headers = { 'content-type' => 'application/x-binary', 'content-md5' => checksum64, accept: 'application/json' }
|
117
117
|
headers.merge!(sign_obj.sign(OpenSSL::PKey::RSA.new(rest.signing_key)))
|
118
118
|
|
119
119
|
begin
|
data/lib/berkshelf/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
-----BEGIN RSA PRIVATE KEY-----
|
2
|
+
MIIEpQIBAAKCAQEAyyUMqrTh1IzKOyE0fvXEWC7m0AdMI8/dr9JJMUKtK9vhhP0w
|
3
|
+
rm6m95GoybFM2IRryukFsAxpcir3M1ungTU3Smq4MshhMJ7H9FbvZVfQoknTbCsR
|
4
|
+
w6scg2fBepxT2+fcGRufr8nAh92M3uUkN9bMMTAkt18D4br6035YvdmvHDJERxYq
|
5
|
+
ByA/720AdI9VNSIvw+x8oqsIkXLEdF6dgT9MpG5iWZT66pbFsnNZpRrd4/bFNWBY
|
6
|
+
+13aOqdmjiTL08/EdgQFKMT5qimpos1TuQhA7mwInOjQgzVu9uCDkMiYejaLbUz0
|
7
|
+
lGyS8y4uxu6z2hA900Jg/z+JJuXymH5QAX3GZQIDAQABAoIBAQCtFXkwbYPI1Nht
|
8
|
+
/wG6du5+8B9K+hy+mppY9wPTy+q+Zs9Ev3Fd/fuXDm1QxBckl9c8AMUO1dR2KPOM
|
9
|
+
t7gFl/DvH/SnmCFvCqp1nijFIUgrLlnMXPn6zG0z7RBlxpKQ2IGohufNIEpBuNwR
|
10
|
+
Ag2U4hgChPGTp4ooJ2cVEh7MS5AupYPDbC62dWEdW68aRTWhh2BCGAWBb6s16yl9
|
11
|
+
aZ7+OcxW2eeRJVbRfLkLQEDutJZi5TfOEn5QPc86ZgxcCmnvwulnpnhpz6QCkgQt
|
12
|
+
OP/+KRqDhWSDVCFREVT30fUIj1EWvK7NFWASZQxueZStuIvMEKeFebYfrbHxRFzJ
|
13
|
+
UmaxJnWVAoGBAPbKLpeky6ClccBaHHrCgjzakoDfGgyNKDQ9g753lJxB8nn7d9X4
|
14
|
+
HQpkWpfqAGFRZp1hI2H+VxyUXLh2Ob5OUeTm0OZJll35vycOaQEtfgIScXTcvzn0
|
15
|
+
16J9eX2YY4wIHEEMh85nKk8BEGgiNP5nuEviHocCeYXoi/Zq3+qj6v63AoGBANK5
|
16
|
+
4nyi6LBQFs1CUc7Sh7vjtOE3ia7KeRmOr7gS6QhS3iK3Oa8FzBLJ6ETjN2a9Bw8N
|
17
|
+
cF7I/+cr4s7DUJjxdb53D/J6TVSYORNNCUVnpF/uB2LqqdXDYmpO0PvFkXFoYTnJ
|
18
|
+
kaLAN8uCoLKr6JH9tq3DfXIfDIHiZ+BOIvI070fDAoGBAMDyzEDFmGruTyRLj66u
|
19
|
+
+rJnVVmqlKwxhLhrS+CTj74nlVOnt0a0KMhiM65IRqnPwcHUG5zXBPaUTHXwAS93
|
20
|
+
/nFPwQ37hLPOupPnoVNJZRZrowbyPBQtCJbDMURv64ylHqoBCQDoCd0hANnZvMMX
|
21
|
+
BrFVhfaaibaXXS542r6SD/27AoGAECadHE5kJTdOOBcwK/jo3Fa8g1J9Y/8yvum3
|
22
|
+
wBT69V9clS6T5j08geglvDnqAh7UzquKBEnFi1NKw+wmXkKLcrivaTdEfApavYb3
|
23
|
+
AfHKoGue907jC3Y5Mcquq81ds2J7qTEwz1eKLzfo1yjj32ShvrmwALIuhDn1GjUC
|
24
|
+
6qtx938CgYEApEqvu0nocR1jmVVlLe5uKQBj949dh6NGq0R5Lztz6xufaTYzMC3d
|
25
|
+
AZG9XPPjRqSLs+ylSXJpwHEwoeyLFDaJcO+GgW1/ut4MC2HppOx6aImwDdXMHUWR
|
26
|
+
KYGIFF4AU/IYoBcanAm4s078EH/Oz01B2c7tR2TqabisPgLYe7PXSCw=
|
27
|
+
-----END RSA PRIVATE KEY-----
|
@@ -0,0 +1,12 @@
|
|
1
|
+
current_dir = File.expand_path(File.dirname(__FILE__))
|
2
|
+
|
3
|
+
log_level :info
|
4
|
+
log_location STDOUT
|
5
|
+
node_name "berkshelf"
|
6
|
+
client_key "#{current_dir}/berkshelf.pem"
|
7
|
+
validation_client_name "chef-validator"
|
8
|
+
validation_key "#{current_dir}/chef-validator.pem"
|
9
|
+
chef_server_url "http://localhost:4000"
|
10
|
+
cache_type 'BasicFile'
|
11
|
+
cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
|
12
|
+
cookbook_path []
|
data/spec/spec_helper.rb
CHANGED
@@ -22,10 +22,11 @@ Spork.prefork do
|
|
22
22
|
RSpec.configure do |config|
|
23
23
|
config.include Berkshelf::RSpec::FileSystemMatchers
|
24
24
|
config.include JsonSpec::Helpers
|
25
|
+
config.include Berkshelf::RSpec::ChefAPI
|
25
26
|
|
26
27
|
config.mock_with :rspec
|
27
28
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
28
|
-
config.filter_run :
|
29
|
+
config.filter_run focus: true
|
29
30
|
config.run_all_when_everything_filtered = true
|
30
31
|
|
31
32
|
config.around do |example|
|
@@ -85,6 +86,8 @@ Spork.prefork do
|
|
85
86
|
FileUtils.rm_rf(tmp_path)
|
86
87
|
FileUtils.mkdir_p(tmp_path)
|
87
88
|
end
|
89
|
+
|
90
|
+
Berkshelf::RSpec::Knife.load_knife_config(File.join(APP_ROOT, 'spec/knife.rb'))
|
88
91
|
end
|
89
92
|
|
90
93
|
Spork.each_run do
|
data/spec/support/chef_api.rb
CHANGED
@@ -5,6 +5,33 @@ require 'chef/cookbook_version'
|
|
5
5
|
module Berkshelf
|
6
6
|
module RSpec
|
7
7
|
module ChefAPI
|
8
|
+
# Return an array of Hashes containing cookbooks and their information
|
9
|
+
#
|
10
|
+
# @return [Array]
|
11
|
+
def get_cookbooks
|
12
|
+
rest.get_rest("cookbooks")
|
13
|
+
end
|
14
|
+
|
15
|
+
def upload_cookbook(path)
|
16
|
+
cached = CachedCookbook.from_store_path(path)
|
17
|
+
uploader.upload!(cached)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Remove all versions of all cookbooks from the Chef Server defined in your
|
21
|
+
# Knife config.
|
22
|
+
def purge_cookbooks
|
23
|
+
get_cookbooks.each do |name, info|
|
24
|
+
info["versions"].each do |version_info|
|
25
|
+
rest.delete_rest("cookbooks/#{name}/#{version_info["version"]}?purge=true")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Remove the version of the given cookbook from the Chef Server defined
|
31
|
+
# in your Knife config.
|
32
|
+
#
|
33
|
+
# @param [#to_s] name
|
34
|
+
# @param [#to_s] version
|
8
35
|
def purge_cookbook(name, version)
|
9
36
|
rest.delete_rest("cookbooks/#{name}/#{version}?purge=true")
|
10
37
|
rescue Net::HTTPServerException => e
|
@@ -57,6 +84,7 @@ EOF
|
|
57
84
|
end
|
58
85
|
|
59
86
|
File.write(cookbook_path.join("metadata.rb"), metadata)
|
87
|
+
cookbook_path
|
60
88
|
end
|
61
89
|
|
62
90
|
private
|
@@ -64,6 +92,10 @@ EOF
|
|
64
92
|
def rest
|
65
93
|
quietly { Chef::REST.new(Chef::Config[:chef_server_url]) }
|
66
94
|
end
|
95
|
+
|
96
|
+
def uploader
|
97
|
+
@uploader ||= Berkshelf::Uploader.new(Chef::Config[:chef_server_url])
|
98
|
+
end
|
67
99
|
end
|
68
100
|
end
|
69
101
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'chef/config'
|
2
|
+
|
3
|
+
module Berkshelf
|
4
|
+
module RSpec
|
5
|
+
module Knife
|
6
|
+
class << self
|
7
|
+
def load_knife_config(path)
|
8
|
+
if File.exist?(path)
|
9
|
+
Chef::Config.from_file(path)
|
10
|
+
ENV["CHEF_CONFIG"] = path
|
11
|
+
else
|
12
|
+
raise "Cannot continue; '#{path}' must exist and have testing credentials."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,243 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Berkshelf
|
4
|
+
describe CookbookSource::ChefAPILocation do
|
5
|
+
let(:test_chef_api) { "https://chefserver:8081" }
|
6
|
+
|
7
|
+
describe "ClassMethods" do
|
8
|
+
subject { CookbookSource::ChefAPILocation }
|
9
|
+
let(:valid_uri) { test_chef_api }
|
10
|
+
let(:invalid_uri) { "notauri" }
|
11
|
+
let(:constraint) { double('constraint') }
|
12
|
+
|
13
|
+
describe "::initialize" do
|
14
|
+
let(:node_name) { "reset" }
|
15
|
+
let(:client_key) { fixtures_path.join("reset.pem").to_s }
|
16
|
+
|
17
|
+
before(:each) do
|
18
|
+
@location = subject.new("nginx",
|
19
|
+
constraint,
|
20
|
+
chef_api: test_chef_api,
|
21
|
+
node_name: node_name,
|
22
|
+
client_key: client_key
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "sets the uri attribute to the value of the chef_api option" do
|
27
|
+
@location.uri.should eql(test_chef_api)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "sets the node_name attribute to the value of the node_name option" do
|
31
|
+
@location.node_name.should eql(node_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "sets the client_key attribute to the value of the client_key option" do
|
35
|
+
@location.client_key.should eql(client_key)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "sets the downloaded status to false" do
|
39
|
+
@location.should_not be_downloaded
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when an invalid Chef API URI is given" do
|
43
|
+
it "raises InvalidChefAPILocation" do
|
44
|
+
lambda {
|
45
|
+
subject.new("nginx", constraint, chef_api: invalid_uri, node_name: node_name, client_key: client_key)
|
46
|
+
}.should raise_error(InvalidChefAPILocation, "'notauri' is not a valid Chef API URI.")
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "when no option for node_name is supplied" do
|
51
|
+
it "raises InvalidChefAPILocation" do
|
52
|
+
lambda {
|
53
|
+
subject.new("nginx", constraint, chef_api: invalid_uri, client_key: client_key)
|
54
|
+
}.should raise_error(InvalidChefAPILocation)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when no option for client_key is supplied" do
|
59
|
+
it "raises InvalidChefAPILocation" do
|
60
|
+
lambda {
|
61
|
+
subject.new("nginx", constraint, chef_api: invalid_uri, node_name: node_name)
|
62
|
+
}.should raise_error(InvalidChefAPILocation)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "given the symbol :knife for the value of chef_api:" do
|
67
|
+
before(:each) { @loc = subject.new("nginx", constraint, chef_api: :knife) }
|
68
|
+
|
69
|
+
it "uses the value of Chef::Config[:chef_server_url] for the uri attribute" do
|
70
|
+
@loc.uri.should eql(Chef::Config[:chef_server_url])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "uses the value of Chef::Config[:node_name] for the node_name attribute" do
|
74
|
+
@loc.node_name.should eql(Chef::Config[:node_name])
|
75
|
+
end
|
76
|
+
|
77
|
+
it "uses the value of Chef::Config[:client_key] for the client_key attribute" do
|
78
|
+
@loc.client_key.should eql(Chef::Config[:client_key])
|
79
|
+
end
|
80
|
+
|
81
|
+
it "attempts to load the config file with no arguments" do
|
82
|
+
Berkshelf.should_receive(:load_config).with(no_args)
|
83
|
+
|
84
|
+
subject.new("nginx", constraint, chef_api: :knife)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe "::validate_uri" do
|
90
|
+
it "returns false if the given URI is invalid" do
|
91
|
+
subject.validate_uri(invalid_uri).should be_false
|
92
|
+
end
|
93
|
+
|
94
|
+
it "returns true if the given URI is valid" do
|
95
|
+
subject.validate_uri(valid_uri).should be_true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "::validate_uri!" do
|
100
|
+
it "raises InvalidChefAPILocation if the given URI is invalid" do
|
101
|
+
lambda {
|
102
|
+
subject.validate_uri!(invalid_uri)
|
103
|
+
}.should raise_error(InvalidChefAPILocation, "'notauri' is not a valid Chef API URI.")
|
104
|
+
end
|
105
|
+
|
106
|
+
it "returns true if the given URI is valid" do
|
107
|
+
subject.validate_uri!(valid_uri).should be_true
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
subject do
|
113
|
+
loc = CookbookSource::ChefAPILocation.new("nginx",
|
114
|
+
double('constraint', satisfies?: true),
|
115
|
+
chef_api: :knife
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
let(:rest) { double('rest') }
|
120
|
+
|
121
|
+
before(:each) do
|
122
|
+
subject.stub(:rest) { rest }
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "#download" do
|
126
|
+
let(:latest) { ["0.101.2", "http://chef/cookbook/nginx/0.101.2"] }
|
127
|
+
let(:versions) do
|
128
|
+
versions = {
|
129
|
+
"#{subject.name}" => {
|
130
|
+
"versions" => [
|
131
|
+
{
|
132
|
+
"url" => "https://api.opscode.com/organizations/vialstudios/cookbooks/#{subject.name}/0.101.2",
|
133
|
+
"version" => "0.101.2"
|
134
|
+
},
|
135
|
+
{
|
136
|
+
"url" => "https://api.opscode.com/organizations/vialstudios/cookbooks/#{subject.name}/0.99.0",
|
137
|
+
"version" => "0.99.0"
|
138
|
+
}
|
139
|
+
],
|
140
|
+
"url" => "https://api.opscode.com/organizations/vialstudios/cookbooks/#{subject.name}"
|
141
|
+
}
|
142
|
+
}
|
143
|
+
end
|
144
|
+
|
145
|
+
before(:each) do
|
146
|
+
subject.stub(:latest_version) { latest }
|
147
|
+
rest.should_receive(:get_rest).with("cookbooks/#{subject.name}").and_return(versions)
|
148
|
+
end
|
149
|
+
|
150
|
+
context "given a constraint that matches an available cookbook" do
|
151
|
+
before(:each) do
|
152
|
+
cookbook_version = double('cookbook-version')
|
153
|
+
cookbook_version.stub(:manifest).and_return({})
|
154
|
+
subject.stub(:version_constraint) { Solve::Constraint.new("= 0.99.0") }
|
155
|
+
rest.should_receive(:get_rest).with("https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.99.0").and_return(cookbook_version)
|
156
|
+
subject.should_receive(:download_files).with(cookbook_version.manifest).and_return(
|
157
|
+
generate_cookbook(Dir.mktmpdir, subject.name, "0.99.0")
|
158
|
+
)
|
159
|
+
end
|
160
|
+
|
161
|
+
it "returns an instance of Berkshelf::CachedCookbook" do
|
162
|
+
subject.download(tmp_path).should be_a(Berkshelf::CachedCookbook)
|
163
|
+
end
|
164
|
+
|
165
|
+
it "sets the downloaded status to true" do
|
166
|
+
subject.download(tmp_path)
|
167
|
+
|
168
|
+
subject.should be_downloaded
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "given a wildcard '>= 0.0.0' version constraint is specified" do
|
173
|
+
before(:each) do
|
174
|
+
subject.stub(:version_constraint) { Solve::Constraint.new(">= 0.0.0") }
|
175
|
+
end
|
176
|
+
|
177
|
+
it "downloads the manifest of the latest cookbook version of the cookbook" do
|
178
|
+
cookbook_version = double('cookbook-version')
|
179
|
+
cookbook_version.stub(:manifest).and_return({})
|
180
|
+
rest.should_receive(:get_rest).with("https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2").and_return(cookbook_version)
|
181
|
+
subject.should_receive(:download_files).with(cookbook_version.manifest).and_return(
|
182
|
+
generate_cookbook(Dir.mktmpdir, subject.name, subject.latest_version[0])
|
183
|
+
)
|
184
|
+
|
185
|
+
subject.download(tmp_path)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "#versions" do
|
191
|
+
before(:each) do
|
192
|
+
rest = double('rest')
|
193
|
+
subject.stub(:rest) { rest }
|
194
|
+
response = {"nginx"=>{"versions"=>[{"url"=>"https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2", "version"=>"0.101.2"}], "url"=>"https://api.opscode.com/organizations/vialstudios/cookbooks/nginx"}}
|
195
|
+
rest.stub(:get_rest).and_return(response)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "returns a hash containing a string containing the version number of each cookbook version as the keys" do
|
199
|
+
subject.versions.should have_key("0.101.2")
|
200
|
+
end
|
201
|
+
|
202
|
+
it "returns a hash containing a string containing the download URL for each cookbook version as the values" do
|
203
|
+
subject.versions["0.101.2"].should eql("https://api.opscode.com/organizations/vialstudios/cookbooks/nginx/0.101.2")
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe "#latest_version" do
|
208
|
+
before(:each) do
|
209
|
+
subject.stub(:versions).and_return(
|
210
|
+
"0.0.1" => "https://chef/nginx/0.0.1",
|
211
|
+
"1.0.0" => "https://chef/nginx/1.0.0",
|
212
|
+
"0.100.0" => "https://chef/nginx/0.100.0"
|
213
|
+
)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "returns an array with two elements" do
|
217
|
+
subject.latest_version.should be_a(Array)
|
218
|
+
subject.latest_version.should have(2).items
|
219
|
+
end
|
220
|
+
|
221
|
+
it "returns an array containing the latest version at index 0" do
|
222
|
+
subject.latest_version[0].should eql("1.0.0")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "returns an array containing the URL to the latest version at index 1" do
|
226
|
+
subject.latest_version[1].should eql("https://chef/nginx/1.0.0")
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
describe "#to_s" do
|
231
|
+
subject do
|
232
|
+
CookbookSource::ChefAPILocation.new('nginx',
|
233
|
+
double('constraint'),
|
234
|
+
chef_api: :knife
|
235
|
+
)
|
236
|
+
end
|
237
|
+
|
238
|
+
it "returns a string containing the location key and the Chef API URI" do
|
239
|
+
subject.to_s.should eql("chef_api: '#{Chef::Config[:chef_server_url]}'")
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|