berkshelf 0.3.7 → 0.4.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|