berkshelf 0.1.1

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.
Files changed (93) hide show
  1. data/.gitignore +20 -0
  2. data/.rbenv-version +1 -0
  3. data/Gemfile +3 -0
  4. data/Guardfile +23 -0
  5. data/LICENSE +22 -0
  6. data/README.rdoc +102 -0
  7. data/Thorfile +47 -0
  8. data/berkshelf.gemspec +39 -0
  9. data/features/config.sample.yml +3 -0
  10. data/features/init_command.feature +40 -0
  11. data/features/install.feature +55 -0
  12. data/features/lockfile.feature +22 -0
  13. data/features/step_definitions/chef_server_steps.rb +13 -0
  14. data/features/step_definitions/cli_steps.rb +56 -0
  15. data/features/step_definitions/filesystem_steps.rb +79 -0
  16. data/features/support/env.rb +55 -0
  17. data/features/update.feature +23 -0
  18. data/features/upload_command.feature +43 -0
  19. data/features/without.feature +25 -0
  20. data/lib/berkshelf.rb +90 -0
  21. data/lib/berkshelf/berksfile.rb +170 -0
  22. data/lib/berkshelf/cached_cookbook.rb +253 -0
  23. data/lib/berkshelf/cookbook_source.rb +139 -0
  24. data/lib/berkshelf/cookbook_source/git_location.rb +54 -0
  25. data/lib/berkshelf/cookbook_source/path_location.rb +27 -0
  26. data/lib/berkshelf/cookbook_source/site_location.rb +206 -0
  27. data/lib/berkshelf/cookbook_store.rb +77 -0
  28. data/lib/berkshelf/core_ext.rb +3 -0
  29. data/lib/berkshelf/core_ext/file.rb +14 -0
  30. data/lib/berkshelf/core_ext/kernel.rb +33 -0
  31. data/lib/berkshelf/core_ext/pathname.rb +24 -0
  32. data/lib/berkshelf/downloader.rb +92 -0
  33. data/lib/berkshelf/dsl.rb +39 -0
  34. data/lib/berkshelf/errors.rb +20 -0
  35. data/lib/berkshelf/generator_files/Berksfile.erb +3 -0
  36. data/lib/berkshelf/generator_files/chefignore +44 -0
  37. data/lib/berkshelf/git.rb +70 -0
  38. data/lib/berkshelf/init_generator.rb +38 -0
  39. data/lib/berkshelf/lockfile.rb +42 -0
  40. data/lib/berkshelf/resolver.rb +176 -0
  41. data/lib/berkshelf/tx_result.rb +12 -0
  42. data/lib/berkshelf/tx_result_set.rb +37 -0
  43. data/lib/berkshelf/uploader.rb +153 -0
  44. data/lib/berkshelf/version.rb +3 -0
  45. data/lib/chef/knife/berks_init.rb +29 -0
  46. data/lib/chef/knife/berks_install.rb +27 -0
  47. data/lib/chef/knife/berks_update.rb +23 -0
  48. data/lib/chef/knife/berks_upload.rb +39 -0
  49. data/spec/fixtures/Berksfile +3 -0
  50. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/README.md +12 -0
  51. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/metadata.rb +6 -0
  52. data/spec/fixtures/cookbooks/example_cookbook-0.5.0/recipes/default.rb +8 -0
  53. data/spec/fixtures/cookbooks/example_cookbook/README.md +12 -0
  54. data/spec/fixtures/cookbooks/example_cookbook/metadata.rb +6 -0
  55. data/spec/fixtures/cookbooks/example_cookbook/recipes/default.rb +8 -0
  56. data/spec/fixtures/cookbooks/invalid_ruby_files-1.0.0/recipes/default.rb +1 -0
  57. data/spec/fixtures/cookbooks/invalid_template_files-1.0.0/templates/default/broken.erb +1 -0
  58. data/spec/fixtures/cookbooks/nginx-0.100.5/README.md +77 -0
  59. data/spec/fixtures/cookbooks/nginx-0.100.5/attributes/default.rb +65 -0
  60. data/spec/fixtures/cookbooks/nginx-0.100.5/definitions/nginx_site.rb +35 -0
  61. data/spec/fixtures/cookbooks/nginx-0.100.5/files/default/mime.types +73 -0
  62. data/spec/fixtures/cookbooks/nginx-0.100.5/files/ubuntu/mime.types +73 -0
  63. data/spec/fixtures/cookbooks/nginx-0.100.5/libraries/nginxlib.rb +1 -0
  64. data/spec/fixtures/cookbooks/nginx-0.100.5/metadata.rb +91 -0
  65. data/spec/fixtures/cookbooks/nginx-0.100.5/providers/defprovider.rb +1 -0
  66. data/spec/fixtures/cookbooks/nginx-0.100.5/recipes/default.rb +59 -0
  67. data/spec/fixtures/cookbooks/nginx-0.100.5/resources/defresource.rb +1 -0
  68. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/nginx.pill.erb +15 -0
  69. data/spec/fixtures/cookbooks/nginx-0.100.5/templates/default/plugins/nginx.rb.erb +66 -0
  70. data/spec/fixtures/lockfile_spec/with_lock/Berksfile +1 -0
  71. data/spec/fixtures/lockfile_spec/without_lock/Berksfile.lock +5 -0
  72. data/spec/spec_helper.rb +92 -0
  73. data/spec/support/chef_api.rb +27 -0
  74. data/spec/support/matchers/file_system_matchers.rb +115 -0
  75. data/spec/support/matchers/filepath_matchers.rb +19 -0
  76. data/spec/unit/berkshelf/cached_cookbook_spec.rb +420 -0
  77. data/spec/unit/berkshelf/cookbook_source/git_location_spec.rb +59 -0
  78. data/spec/unit/berkshelf/cookbook_source/path_location_spec.rb +34 -0
  79. data/spec/unit/berkshelf/cookbook_source/site_location_spec.rb +166 -0
  80. data/spec/unit/berkshelf/cookbook_source_spec.rb +194 -0
  81. data/spec/unit/berkshelf/cookbook_store_spec.rb +71 -0
  82. data/spec/unit/berkshelf/cookbookfile_spec.rb +160 -0
  83. data/spec/unit/berkshelf/downloader_spec.rb +82 -0
  84. data/spec/unit/berkshelf/dsl_spec.rb +42 -0
  85. data/spec/unit/berkshelf/git_spec.rb +63 -0
  86. data/spec/unit/berkshelf/init_generator_spec.rb +52 -0
  87. data/spec/unit/berkshelf/lockfile_spec.rb +25 -0
  88. data/spec/unit/berkshelf/resolver_spec.rb +126 -0
  89. data/spec/unit/berkshelf/tx_result_set_spec.rb +77 -0
  90. data/spec/unit/berkshelf/tx_result_spec.rb +21 -0
  91. data/spec/unit/berkshelf/uploader_spec.rb +71 -0
  92. data/spec/unit/berkshelf_spec.rb +29 -0
  93. metadata +411 -0
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe CookbookSource::GitLocation do
5
+ subject { CookbookSource::GitLocation.new("nginx", :git => "git://github.com/opscode-cookbooks/nginx.git") }
6
+
7
+ describe "#download" do
8
+ it "downloads the cookbook to the given destination" do
9
+ subject.download(tmp_path)
10
+ # have to set outside of custom rspec matcher block
11
+ name, branch = subject.name, subject.branch
12
+
13
+ tmp_path.should have_structure {
14
+ directory "#{name}-#{branch}" do
15
+ file "metadata.rb"
16
+ end
17
+ }
18
+ end
19
+
20
+ it "returns the path to the cookbook" do
21
+ result = subject.download(tmp_path)
22
+ # have to set outside of custom rspec matcher block
23
+ name, branch = subject.name, subject.branch
24
+
25
+ result.should eql(tmp_path.join("#{name}-#{branch}").to_s)
26
+ end
27
+
28
+ context "given no ref/branch/tag options is given" do
29
+ subject { CookbookSource::GitLocation.new("nginx", :git => "git://github.com/opscode-cookbooks/nginx.git") }
30
+
31
+ it "sets the branch attribute to the HEAD revision of the cloned repo" do
32
+ subject.download(tmp_path)
33
+
34
+ subject.branch.should_not be_nil
35
+ end
36
+ end
37
+
38
+ context "given a git repo that does not exist" do
39
+ subject { CookbookSource::GitLocation.new("doesnot_exist", :git => "git://github.com/RiotGames/thisrepo_does_not_exist.git") }
40
+
41
+ it "raises a CookbookNotFound error" do
42
+ lambda {
43
+ subject.download(tmp_path)
44
+ }.should raise_error(CookbookNotFound)
45
+ end
46
+ end
47
+
48
+ context "given a git repo that does not contain a cookbook" do
49
+ subject { CookbookSource::GitLocation.new("doesnot_exist", :git => "git://github.com/RiotGames/berkshelf.git") }
50
+
51
+ it "raises a CookbookNotFound error" do
52
+ lambda {
53
+ subject.download(tmp_path)
54
+ }.should raise_error(CookbookNotFound)
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe CookbookSource::PathLocation do
5
+ let(:path) { fixtures_path.join("cookbooks", "example_cookbook").to_s }
6
+ subject { CookbookSource::PathLocation.new("nginx", :path => path) }
7
+
8
+ describe "#download" do
9
+ it "returns the path to the cookbook" do
10
+ subject.download(tmp_path).should eql(path)
11
+ end
12
+
13
+ context "given a path that does not exist" do
14
+ subject { CookbookSource::PathLocation.new("doesnot_exist", :path => tmp_path.join("doesntexist_noway")) }
15
+
16
+ it "raises a CookbookNotFound error" do
17
+ lambda {
18
+ subject.download(tmp_path)
19
+ }.should raise_error(CookbookNotFound)
20
+ end
21
+ end
22
+
23
+ context "given a path that does not contain a cookbook" do
24
+ subject { CookbookSource::PathLocation.new("doesnot_exist", :path => fixtures_path) }
25
+
26
+ it "raises a CookbookNotFound error" do
27
+ lambda {
28
+ subject.download(tmp_path)
29
+ }.should raise_error(CookbookNotFound)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,166 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe CookbookSource::SiteLocation do
5
+ describe "ClassMethods" do
6
+ subject { CookbookSource::SiteLocation }
7
+ let(:constraint) { DepSelector::VersionConstraint.new("~> 0.101.2") }
8
+ let(:versions) do
9
+ {
10
+ DepSelector::Version.new("0.101.2") => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_2",
11
+ DepSelector::Version.new("0.101.0") => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_101_0",
12
+ DepSelector::Version.new("0.100.2") => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_100_2",
13
+ DepSelector::Version.new("0.100.0") => "http://cookbooks.opscode.com/api/v1/cookbooks/nginx/versions/0_100_0"
14
+ }
15
+ end
16
+
17
+ describe "#solve_for_constraint" do
18
+ it "returns an array containing a DepSelector::Version at index 0" do
19
+ result = subject.solve_for_constraint(constraint, versions)
20
+
21
+ result[0].should be_a(DepSelector::Version)
22
+ end
23
+
24
+ it "returns an array containing a URI at index 0" do
25
+ result = subject.solve_for_constraint(constraint, versions)
26
+
27
+ result[1].should match(URI.regexp)
28
+ end
29
+
30
+ it "should return the best match for the constraint and versions given" do
31
+ subject.solve_for_constraint(constraint, versions)[0].to_s.should eql("0.101.2")
32
+ end
33
+ end
34
+ end
35
+
36
+ subject { CookbookSource::SiteLocation.new("nginx") }
37
+
38
+ describe "#download" do
39
+ it "returns the path to the cookbook" do
40
+ result = subject.download(tmp_path)
41
+ name = subject.name
42
+ ver, uri = subject.latest_version
43
+
44
+ result.should eql(tmp_path.join("#{name}-#{ver}").to_s)
45
+ end
46
+
47
+ context "when no version constraint is specified" do
48
+ it "the latest version of the cookbook is downloaded to the given destination" do
49
+ subject.download(tmp_path)
50
+ name = subject.name
51
+ ver, uri = subject.latest_version
52
+
53
+ tmp_path.should have_structure {
54
+ directory "#{name}-#{ver}" do
55
+ file "metadata.rb"
56
+ end
57
+ }
58
+ end
59
+ end
60
+
61
+ context "given an explicit version_constraint" do
62
+ subject { CookbookSource::SiteLocation.new("nginx", :version_constraint => DepSelector::VersionConstraint.new("= 0.101.2")) }
63
+
64
+ it "downloads the cookbook with the version matching the version_constraint to the given destination" do
65
+ subject.download(tmp_path)
66
+ name = subject.name
67
+
68
+ tmp_path.should have_structure {
69
+ directory "#{name}-0.101.2" do
70
+ file "metadata.rb"
71
+ end
72
+ }
73
+ end
74
+ end
75
+
76
+ context "given a more broad version_constraint" do
77
+ subject { CookbookSource::SiteLocation.new("nginx", :version_constraint => DepSelector::VersionConstraint.new("~> 0.99.0")) }
78
+
79
+ it "downloads the best matching cookbook version for the constraint to the given destination" do
80
+ subject.download(tmp_path)
81
+ name = subject.name
82
+
83
+ tmp_path.should have_structure {
84
+ directory "#{name}-0.99.2" do
85
+ file "metadata.rb"
86
+ end
87
+ }
88
+ end
89
+ end
90
+
91
+ context "given an explicit :site location key" do
92
+ subject { CookbookSource::SiteLocation.new("nginx", :site => "http://cookbooks.opscode.com/api/v1/cookbooks") }
93
+
94
+ it "downloads the cookbook to the given destination" do
95
+ subject.download(tmp_path)
96
+ name = subject.name
97
+ ver, uri = subject.latest_version
98
+
99
+ tmp_path.should have_structure {
100
+ directory "#{name}-#{ver}" do
101
+ file "metadata.rb"
102
+ end
103
+ }
104
+ end
105
+ end
106
+
107
+ context "given a cookbook that does not exist on the specified site" do
108
+ subject { CookbookSource::SiteLocation.new("nowaythis_exists", :site => "http://cookbooks.opscode.com/api/v1/cookbooks") }
109
+
110
+ it "raises a CookbookNotFound error" do
111
+ lambda {
112
+ subject.download(tmp_path)
113
+ }.should raise_error(CookbookNotFound)
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "#versions" do
119
+ it "returns a hash containing versions for keys" do
120
+ subject.versions.each do |key, val|
121
+ key.should be_a(DepSelector::Version)
122
+ end
123
+ end
124
+
125
+ it "returns a hash containing uris for values" do
126
+ subject.versions.each do |key, val|
127
+ val.should match(URI.regexp)
128
+ end
129
+ end
130
+ end
131
+
132
+ describe "#version" do
133
+ it "returns an array containing a DepSelector::Version at index 0" do
134
+ result = subject.version("0.101.2")
135
+
136
+ result[0].should be_a(DepSelector::Version)
137
+ end
138
+
139
+ it "returns an array containing a URI at index 0" do
140
+ result = subject.version("0.101.2")
141
+
142
+ result[1].should match(URI.regexp)
143
+ end
144
+
145
+ it "returns a DepSelector::Version that matches the given version" do
146
+ result = subject.version("0.101.2")
147
+
148
+ result[0].to_s.should eql("0.101.2")
149
+ end
150
+ end
151
+
152
+ describe "#latest_version" do
153
+ it "returns an array containing a DepSelector::Version at index 0" do
154
+ result = subject.latest_version
155
+
156
+ result[0].should be_a(DepSelector::Version)
157
+ end
158
+
159
+ it "returns an array containing a URI at index 0" do
160
+ result = subject.latest_version
161
+
162
+ result[1].should match(URI.regexp)
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,194 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe CookbookSource do
5
+ let(:cookbook_name) { "nginx" }
6
+
7
+ describe "#initialize" do
8
+ subject { CookbookSource }
9
+
10
+ context "given no location key (i.e. :git, :path, :site)" do
11
+ let(:source) { subject.new(cookbook_name) }
12
+
13
+ it "uses a default SiteLocation pointing to the opscode community api" do
14
+ source.location.api_uri.should eql(subject::SiteLocation::OPSCODE_COMMUNITY_API)
15
+ end
16
+ end
17
+
18
+ context "given no value for constraint" do
19
+ let(:source) { subject.new(cookbook_name) }
20
+
21
+ it "returns a wildcard match for any version" do
22
+ source.version_constraint.to_s.should eql(">= 0.0.0")
23
+ end
24
+ end
25
+
26
+ context "given a value for constraint" do
27
+ let(:source) { subject.new(cookbook_name, "~> 1.0.84") }
28
+
29
+ it "returns a DepSelector::VersionConstraint for the given version for version_constraint" do
30
+ source.version_constraint.to_s.should eql("~> 1.0.84")
31
+ end
32
+ end
33
+
34
+ context "given a location key :git" do
35
+ let(:url) { "git://url_to_git" }
36
+ let(:source) { subject.new(cookbook_name, :git => url) }
37
+
38
+ it "initializes a GitLocation for location" do
39
+ source.location.should be_a(subject::GitLocation)
40
+ end
41
+
42
+ it "points to the given Git URL" do
43
+ source.location.uri.should eql(url)
44
+ end
45
+ end
46
+
47
+ context "given a location key :path" do
48
+ let(:path) { "/Path/To/Cookbook" }
49
+ let(:source) { subject.new(cookbook_name, :path => path) }
50
+
51
+ it "initializes a PathLocation for location" do
52
+ source.location.should be_a(subject::PathLocation)
53
+ end
54
+
55
+ it "points to the specified path" do
56
+ source.location.path.should eql(path)
57
+ end
58
+ end
59
+
60
+ context "given a location key :site" do
61
+ let(:url) { "http://path_to_api/v1" }
62
+ let(:source) { subject.new(cookbook_name, :site => url) }
63
+
64
+ it "initializes a SiteLocation for location" do
65
+ source.location.should be_a(subject::SiteLocation)
66
+ end
67
+
68
+ it "points to the specified URI" do
69
+ source.location.api_uri.should eql(url)
70
+ end
71
+ end
72
+
73
+ context "given multiple location options" do
74
+ it "raises with an ArgumentError" do
75
+ lambda {
76
+ subject.new(cookbook_name, :site => "something", :git => "something")
77
+ }.should raise_error(ArgumentError)
78
+ end
79
+ end
80
+
81
+ context "given a group option containing a single group" do
82
+ let(:group) { :production }
83
+ let(:source) { subject.new(cookbook_name, :group => group) }
84
+
85
+ it "assigns the single group to the groups attribute" do
86
+ source.groups.should include(group)
87
+ end
88
+ end
89
+
90
+ context "given a group option containing an array of groups" do
91
+ let(:groups) { [ :development, :test ] }
92
+ let(:source) { subject.new(cookbook_name, :group => groups) }
93
+
94
+ it "assigns all the groups to the group attribute" do
95
+ source.groups.should eql(groups)
96
+ end
97
+ end
98
+
99
+ context "given no group option" do
100
+ let(:source) { subject.new(cookbook_name) }
101
+
102
+ it "should have the default group assigned" do
103
+ source.groups.should include(:default)
104
+ end
105
+ end
106
+ end
107
+
108
+ subject { CookbookSource.new(cookbook_name) }
109
+
110
+ describe '#add_group' do
111
+ it "should store strings as symbols" do
112
+ subject.add_group "foo"
113
+ subject.groups.should == [:default, :foo]
114
+ end
115
+
116
+ it "should not store duplicate groups" do
117
+ subject.add_group "bar"
118
+ subject.add_group "bar"
119
+ subject.add_group :bar
120
+ subject.groups.should == [:default, :bar]
121
+ end
122
+
123
+ it "should add multiple groups" do
124
+ subject.add_group "baz", "quux"
125
+ subject.groups.should == [:default, :baz, :quux]
126
+ end
127
+
128
+ it "should handle multiple groups as an array" do
129
+ subject.add_group ["baz", "quux"]
130
+ subject.groups.should == [:default, :baz, :quux]
131
+ end
132
+ end
133
+
134
+ describe "#download" do
135
+ context "when download is successful" do
136
+ it "writes a value to local_path" do
137
+ subject.download(tmp_path)
138
+
139
+ subject.local_path.should_not be_nil
140
+ end
141
+
142
+ it "writes a value to local_version" do
143
+ subject.download(tmp_path)
144
+
145
+ subject.local_version.should_not be_nil
146
+ end
147
+
148
+ it "returns an array containing the symbol :ok and the local_path" do
149
+ result = subject.download(tmp_path)
150
+
151
+ result.should be_a(Array)
152
+ result[0].should eql(:ok)
153
+ result[1].should eql(subject.local_path)
154
+ end
155
+ end
156
+
157
+ context "when the download fails" do
158
+ let(:bad_cb_name) { "NOWAYTHISEXISTS" }
159
+ subject { CookbookSource.new(bad_cb_name) }
160
+
161
+ it "returns an array containing the symbol :error and the error message" do
162
+ result = subject.download(tmp_path)
163
+
164
+ result.should be_a(Array)
165
+ result[0].should eql(:error)
166
+ result[1].should eql("Cookbook '#{bad_cb_name}' not found at site: 'http://cookbooks.opscode.com/api/v1/cookbooks'")
167
+ end
168
+ end
169
+ end
170
+
171
+ describe "#metadata" do
172
+ it "should return the metadata of a CookbookSource that has been downloaded" do
173
+ subject.download(tmp_path)
174
+
175
+ subject.metadata.should be_a(Chef::Cookbook::Metadata)
176
+ end
177
+
178
+ it "should return nil if the CookbookSource has not been downloaded" do
179
+ subject.metadata.should be_nil
180
+ end
181
+ end
182
+
183
+ describe "#downloaded?" do
184
+ let(:path) { fixtures_path.join("cookbooks", "example_cookbook").to_s }
185
+ subject { CookbookSource.new("example_cookbook", :path => path) }
186
+
187
+ it "returns true if the local_path has been set" do
188
+ subject.stub(:local_path) { path }
189
+
190
+ subject.downloaded?.should be_true
191
+ end
192
+ end
193
+ end
194
+ end