berkshelf 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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,71 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe CookbookStore do
5
+ subject { CookbookStore.new(tmp_path.join("cbstore_rspec")) }
6
+
7
+ describe "#initialize" do
8
+ it "creates the storage_path" do
9
+ storage_path = tmp_path.join("random_storage")
10
+ subject.class.new(storage_path)
11
+
12
+ storage_path.should exist
13
+ end
14
+ end
15
+
16
+ describe "#cookbook_path" do
17
+ let(:cookbook_name) { "nginx" }
18
+ let(:cookbook_version) { "0.101.2" }
19
+
20
+ before(:each) do
21
+ @cb_path = subject.cookbook_path(cookbook_name, cookbook_version)
22
+ end
23
+
24
+ it "returns an instance of Pathname" do
25
+ @cb_path.should be_a(Pathname)
26
+ end
27
+
28
+ it "returns a Cookbook Version's filepath within the storage path" do
29
+ @cb_path.dirname.should eql(subject.storage_path)
30
+ end
31
+
32
+ it "returns a basename containing the cookbook name and version separated by a dash" do
33
+ @cb_path.basename.to_s.should eql("#{cookbook_name}-#{cookbook_version}")
34
+ end
35
+ end
36
+
37
+ describe "#downloaded?" do
38
+ it "returns true if the store contains a Cookbook of the given name and version" do
39
+ CookbookSource.new("nginx", "0.101.2").download(subject.storage_path)
40
+
41
+ subject.downloaded?("nginx", "0.101.2").should be_true
42
+ end
43
+
44
+ it "returns false if the store does not contain a Cookbook of the given name and version" do
45
+ subject.downloaded?("notthere", "0.0.0").should be_false
46
+ end
47
+ end
48
+
49
+ describe "#cookbook" do
50
+ subject { CookbookStore.new(fixtures_path.join("cookbooks")) }
51
+
52
+ it "returns a CachedCookbook if the specified cookbook version exists" do
53
+ subject.cookbook("example_cookbook", "0.5.0").should be_a(CachedCookbook)
54
+ end
55
+
56
+ it "returns nil if the specified cookbook version does not exist" do
57
+ subject.cookbook("doesnotexist", "0.1.0").should be_nil
58
+ end
59
+ end
60
+
61
+ describe "#cookbooks" do
62
+ it "returns a list of CachedCookbooks" do
63
+ CookbookSource.new("nginx", "0.101.2").download(subject.storage_path)
64
+
65
+ subject.cookbooks.each do |cb|
66
+ cb.should be_a(CachedCookbook)
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,160 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe Berksfile do
5
+ describe "ClassMethods" do
6
+ subject { Berksfile }
7
+
8
+ let(:content) do
9
+ <<-EOF
10
+ cookbook 'ntp', '<= 1.0.0'
11
+ cookbook 'mysql'
12
+ cookbook 'nginx', '< 0.101.2'
13
+ cookbook 'ssh_known_hosts2', :git => 'https://github.com/erikh/chef-ssh_known_hosts2.git'
14
+ EOF
15
+ end
16
+
17
+ describe "#read" do
18
+ it "reads the content of a Berksfile and adds the sources to the Shelf" do
19
+ cbfile = subject.read(content)
20
+
21
+ ['ntp', 'mysql', 'nginx', 'ssh_known_hosts2'].each do |name|
22
+ cbfile.should have_source(name)
23
+ end
24
+ end
25
+
26
+ it "returns an instance of Berksfile" do
27
+ subject.read(content).should be_a(Berksfile)
28
+ end
29
+ end
30
+
31
+ describe "#from_file" do
32
+ let(:cookbook_file) { fixtures_path.join('lockfile_spec', 'with_lock', 'Berksfile') }
33
+
34
+ it "reads a Berksfile and returns an instance Berksfile" do
35
+ subject.from_file(cookbook_file).should be_a(Berksfile)
36
+ end
37
+
38
+ context "when Berksfile does not exist at given path" do
39
+ let(:bad_path) { tmp_path.join("thisdoesnotexist") }
40
+
41
+ it "raises BerksfileNotFound" do
42
+ lambda {
43
+ subject.from_file(bad_path)
44
+ }.should raise_error(BerksfileNotFound)
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "#filter_sources" do
50
+ context "given one of the sources is a member of one of the excluded groups" do
51
+ let(:excluded_groups) { [:nautilus, :skarner] }
52
+ let(:source_one) { double('source_one') }
53
+ let(:source_two) { double('source_two') }
54
+
55
+ before(:each) do
56
+ source_one.stub(:groups) { [:nautilus] }
57
+ source_two.stub(:groups) { [:riven] }
58
+ @sources = [source_one, source_two]
59
+ end
60
+
61
+ it "returns an array without sources that were members of the excluded groups" do
62
+ result = subject.filter_sources(@sources, excluded_groups)
63
+
64
+ result.should_not include(source_one)
65
+ end
66
+
67
+ it "does not remove sources that were not a member of the excluded groups" do
68
+ result = subject.filter_sources(@sources, excluded_groups)
69
+
70
+ result.should include(source_two)
71
+ end
72
+ end
73
+ end
74
+ end
75
+
76
+ let(:source_one) { double('source_one', name: "nginx") }
77
+ let(:source_two) { double('source_two', name: "mysql") }
78
+
79
+ subject do
80
+ cbf = Berksfile.new
81
+ cbf.add_source(source_one)
82
+ cbf.add_source(source_two)
83
+ cbf
84
+ end
85
+
86
+ describe "#sources" do
87
+ it "returns all CookbookSources added to the instance of Berksfile" do
88
+ result = subject.sources
89
+
90
+ result.should have(2).items
91
+ result.should include(source_one)
92
+ result.should include(source_two)
93
+ end
94
+
95
+ context "given the option :exclude" do
96
+ it "filters the sources before returning them" do
97
+ subject.class.should_receive(:filter_sources).with(subject.sources, :nautilus)
98
+
99
+ subject.sources(exclude: :nautilus)
100
+ end
101
+ end
102
+ end
103
+
104
+ describe "#groups" do
105
+ before(:each) do
106
+ source_one.stub(:groups) { [:nautilus, :skarner] }
107
+ source_two.stub(:groups) { [:nautilus, :riven] }
108
+ end
109
+
110
+ it "returns a hash containing keys for every group a source is a member of" do
111
+ subject.groups.keys.should have(3).items
112
+ subject.groups.should have_key(:nautilus)
113
+ subject.groups.should have_key(:skarner)
114
+ subject.groups.should have_key(:riven)
115
+ end
116
+
117
+ it "returns an Array of CookbookSources who are members of the group for value" do
118
+ subject.groups[:nautilus].should include(source_one)
119
+ subject.groups[:nautilus].should include(source_two)
120
+ subject.groups[:riven].should_not include(source_one)
121
+ end
122
+ end
123
+
124
+ describe "#install" do
125
+ let(:resolver) { double('resolver') }
126
+ before(:each) { Berkshelf::Resolver.stub(:new) { resolver } }
127
+
128
+ context "when a lockfile is not present" do
129
+ before(:each) do
130
+ subject.should_receive(:lockfile_present?).and_return(false)
131
+ resolver.should_receive(:sources).and_return([])
132
+ end
133
+
134
+ it "creates a new resolver and finds a solution by calling resolve on the resolver" do
135
+ resolver.should_receive(:resolve)
136
+
137
+ subject.install
138
+ end
139
+
140
+ it "writes a lockfile with the resolvers sources" do
141
+ resolver.should_receive(:resolve)
142
+ subject.should_receive(:write_lockfile).with([])
143
+
144
+ subject.install
145
+ end
146
+ end
147
+
148
+ context "when a lockfile is present" do
149
+ before(:each) { subject.should_receive(:lockfile_present?).and_return(true) }
150
+
151
+ it "does not write a new lock file" do
152
+ resolver.should_receive(:resolve)
153
+ subject.should_not_receive(:write_lockfile)
154
+
155
+ subject.install
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe Downloader do
5
+ subject { Downloader.new(CookbookStore.new(tmp_path)) }
6
+ let(:source) { CookbookSource.new("sparkle_motion") }
7
+
8
+ describe "#enqueue" do
9
+ it "should add a source to the queue" do
10
+ subject.enqueue(source)
11
+
12
+ subject.queue.should have(1).source
13
+ end
14
+
15
+ it "should not allow you to add an invalid source" do
16
+ lambda {
17
+ subject.enqueue("a string, not a source")
18
+ }.should raise_error(ArgumentError)
19
+ end
20
+ end
21
+
22
+ describe "#dequeue" do
23
+ before(:each) { subject.enqueue(source) }
24
+
25
+ it "should remove a source from the queue" do
26
+ subject.dequeue(source)
27
+
28
+ subject.queue.should be_empty
29
+ end
30
+ end
31
+
32
+ describe "#download_all" do
33
+ let(:source_one) { CookbookSource.new("nginx") }
34
+ let(:source_two) { CookbookSource.new("mysql") }
35
+
36
+ before(:each) do
37
+ subject.enqueue source_one
38
+ subject.enqueue source_two
39
+ end
40
+
41
+ it "should remove each item from the queue after a successful download" do
42
+ subject.download_all
43
+
44
+ subject.queue.should be_empty
45
+ end
46
+
47
+ it "should not remove the item from the queue if the download failed" do
48
+ subject.enqueue CookbookSource.new("does_not_exist_no_way")
49
+ subject.download_all
50
+
51
+ subject.queue.should have(1).sources
52
+ end
53
+
54
+ it "should return a TXResultSet" do
55
+ results = subject.download_all
56
+
57
+ results.should be_a(TXResultSet)
58
+ end
59
+ end
60
+
61
+ describe "#download" do
62
+ let(:source) { CookbookSource.new("nginx") }
63
+ let(:bad_source) { CookbookSource.new("donowaytexists") }
64
+
65
+ it "returns a TXResult" do
66
+ subject.download(source).should be_a(TXResult)
67
+ end
68
+
69
+ context "when successful" do
70
+ it "returns a successesful TXResult" do
71
+ subject.download(source).should be_success
72
+ end
73
+ end
74
+
75
+ context "when failure" do
76
+ it "returns a failed TXResult" do
77
+ subject.download(bad_source).should be_failed
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+ require 'berkshelf/dsl'
3
+
4
+ module Berkshelf
5
+ describe DSL do
6
+ subject do
7
+ Class.new do
8
+ include Berkshelf::DSL
9
+ end.new
10
+ end
11
+
12
+ describe "#cookbook" do
13
+ it "calls add source to the instance of the implementing class with a CookbookSource" do
14
+ subject.should_receive(:add_source).with(kind_of(CookbookSource))
15
+
16
+ subject.cookbook "ntp"
17
+ end
18
+ end
19
+
20
+ describe '#group' do
21
+ it "calls add source to the instance of the implementing class with a CookbookSource" do
22
+ subject.should_receive(:add_source).with(kind_of(CookbookSource))
23
+
24
+ subject.group "awesome" do
25
+ subject.cookbook "ntp"
26
+ end
27
+ end
28
+ end
29
+
30
+ describe "#metadata" do
31
+ before(:each) do
32
+ Dir.chdir fixtures_path.join('cookbooks/example_cookbook')
33
+ end
34
+
35
+ it "calls add source to the instance of the implementing class with a CookbookSource" do
36
+ subject.should_receive(:add_source).with(kind_of(CookbookSource))
37
+
38
+ subject.metadata
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe Git do
5
+ describe "ClassMethods" do
6
+ subject { Git }
7
+
8
+ describe "#find_git" do
9
+ it "should find git" do
10
+ subject.find_git.should_not be_nil
11
+ end
12
+
13
+ it "should raise if it can't find git" do
14
+ begin
15
+ path = ENV["PATH"]
16
+ ENV["PATH"] = ""
17
+
18
+ lambda { subject.find_git }.should raise_error
19
+ ensure
20
+ ENV["PATH"] = path
21
+ end
22
+ end
23
+ end
24
+
25
+ describe "#clone" do
26
+ let(:target) { tmp_path.join("nginx") }
27
+
28
+ it "clones the repository to the target path" do
29
+ subject.clone("git://github.com/opscode-cookbooks/nginx.git", target)
30
+
31
+ target.should exist
32
+ target.should be_directory
33
+ end
34
+ end
35
+
36
+ describe "#checkout" do
37
+ let(:repo_path) { tmp_path.join("nginx") }
38
+ let(:repo) { subject.clone("git://github.com/opscode-cookbooks/nginx.git", repo_path) }
39
+ let(:tag) { "0.101.2" }
40
+
41
+ it "checks out the specified path of the given repository" do
42
+ subject.checkout(repo, tag)
43
+
44
+ Dir.chdir repo_path do
45
+ %x[git rev-parse #{tag}].should == %x[git rev-parse HEAD]
46
+ end
47
+ end
48
+ end
49
+
50
+ describe "#rev_parse" do
51
+ let(:repo_path) { tmp_path.join("nginx") }
52
+ before(:each) do
53
+ subject.clone("git://github.com/opscode-cookbooks/nginx.git", repo_path)
54
+ subject.checkout(repo_path, "0e4887d9eef8cb83972f974a85890983c8204c3b")
55
+ end
56
+
57
+ it "returns the ref for HEAD" do
58
+ subject.rev_parse(repo_path).should eql("0e4887d9eef8cb83972f974a85890983c8204c3b")
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module Berkshelf
4
+ describe InitGenerator do
5
+ subject { InitGenerator }
6
+
7
+ let(:target_root) { tmp_path.join("some_cookbook") }
8
+
9
+ context "with default options" do
10
+ before do
11
+ generator = subject.new([], :path => target_root)
12
+ capture(:stdout) { generator.invoke_all }
13
+ end
14
+
15
+ specify do
16
+ target_root.should have_structure {
17
+ file "Berksfile"
18
+ no_file ".chefignore"
19
+ }
20
+ end
21
+ end
22
+
23
+ context "with a .chefignore" do
24
+ before do
25
+ generator = subject.new([], :path => target_root, :chefignore => true)
26
+ capture(:stdout) { generator.invoke_all }
27
+ end
28
+
29
+ specify do
30
+ target_root.should have_structure {
31
+ file "Berksfile"
32
+ file ".chefignore"
33
+ }
34
+ end
35
+ end
36
+
37
+ context "with a metadata entry in the Berksfile" do
38
+ before do
39
+ generator = subject.new([], :path => target_root, :metadata_entry => true)
40
+ capture(:stdout) { generator.invoke_all }
41
+ end
42
+
43
+ specify do
44
+ target_root.should have_structure {
45
+ file "Berksfile" do
46
+ contains "metadata"
47
+ end
48
+ }
49
+ end
50
+ end
51
+ end
52
+ end