librarian-chef 0.0.1.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ #^syntax detection
3
+
4
+ site 'http://community.opscode.com/api/v1'
5
+
6
+ # cookbook 'chef-client'
7
+
8
+ # cookbook 'apache2', '>= 1.0.0'
9
+
10
+ # cookbook 'rvm',
11
+ # :git => 'https://github.com/fnichol/chef-rvm'
12
+
13
+ # cookbook 'postgresql',
14
+ # :git => 'https://github.com/findsyou/cookbooks',
15
+ # :ref => 'postgresql-improvements'
@@ -0,0 +1,5 @@
1
+ module Librarian
2
+ module Chef
3
+ VERSION = "0.0.1.beta.1"
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'librarian/chef/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "librarian-chef"
8
+ gem.version = Librarian::Chef::VERSION
9
+ gem.authors = ["Jay Feldblum"]
10
+ gem.email = ["y_feldblum@yahoo.com"]
11
+ gem.description = %q{Librarian::Chef}
12
+ gem.summary = %q{Librarian::Chef}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency "librarian", ">= 0.1.0.beta.1"
21
+ gem.add_dependency "chef", ">= 0.10"
22
+ gem.add_dependency "archive-tar-minitar", ">= 0.5.2"
23
+
24
+ gem.add_development_dependency "rake"
25
+ gem.add_development_dependency "rspec"
26
+ gem.add_development_dependency "webmock"
27
+ end
@@ -0,0 +1,195 @@
1
+ require "securerandom"
2
+
3
+ require "librarian/rspec/support/cli_macro"
4
+
5
+ require "librarian/chef/cli"
6
+
7
+ module Librarian
8
+ module Chef
9
+ describe Cli do
10
+ include Librarian::RSpec::Support::CliMacro
11
+
12
+ describe "init" do
13
+ before do
14
+ cli! "init"
15
+ end
16
+
17
+ it "should create a file named Cheffile" do
18
+ pwd.should have_file "Cheffile"
19
+ end
20
+ end
21
+
22
+ describe "version" do
23
+ before do
24
+ cli! "version"
25
+ end
26
+
27
+ it "should print the version" do
28
+ stdout.should == strip_heredoc(<<-STDOUT)
29
+ librarian-#{Librarian::VERSION}
30
+ librarian-chef-#{Librarian::Chef::VERSION}
31
+ STDOUT
32
+ end
33
+ end
34
+
35
+ describe "install" do
36
+
37
+ context "a simple Cheffile with one cookbook" do
38
+ let(:metadata) { {
39
+ "name" => "apt",
40
+ "version" => "1.0.0",
41
+ "dependencies" => { },
42
+ } }
43
+
44
+ before do
45
+ write_json_file! "cookbook-sources/apt/metadata.json", metadata
46
+ write_file! "Cheffile", strip_heredoc(<<-CHEFFILE)
47
+ cookbook 'apt',
48
+ :path => 'cookbook-sources'
49
+ CHEFFILE
50
+
51
+ cli! "install"
52
+ end
53
+
54
+ it "should write a lockfile" do
55
+ pwd.should have_file "Cheffile.lock"
56
+ end
57
+
58
+ it "should install the cookbook" do
59
+ pwd.should have_json_file "cookbooks/apt/metadata.json", metadata
60
+ end
61
+ end
62
+
63
+ context "a simple Cheffile with one cookbook with one dependency" do
64
+ let(:main_metadata) { {
65
+ "name" => "main",
66
+ "version" => "1.0.0",
67
+ "dependencies" => {
68
+ "sub" => "1.0.0",
69
+ }
70
+ } }
71
+ let(:sub_metadata) { {
72
+ "name" => "sub",
73
+ "version" => "1.0.0",
74
+ "dependencies" => { },
75
+ } }
76
+
77
+ before do
78
+ write_json_file! "cookbook-sources/main/metadata.json", main_metadata
79
+ write_json_file! "cookbook-sources/sub/metadata.json", sub_metadata
80
+ write_file! "Cheffile", strip_heredoc(<<-CHEFFILE)
81
+ path 'cookbook-sources'
82
+ cookbook 'main'
83
+ CHEFFILE
84
+
85
+ cli! "install"
86
+ end
87
+
88
+ it "should write a lockfile" do
89
+ pwd.should have_file "Cheffile.lock"
90
+ end
91
+
92
+ it "should install the dependant cookbook" do
93
+ pwd.should have_json_file "cookbooks/main/metadata.json", main_metadata
94
+ end
95
+
96
+ it "should install the independent cookbook" do
97
+ pwd.should have_json_file "cookbooks/sub/metadata.json", sub_metadata
98
+ end
99
+ end
100
+
101
+ end
102
+
103
+ describe "show" do
104
+ let(:main_metadata) { {
105
+ "name" => "main",
106
+ "version" => "1.0.0",
107
+ "dependencies" => {
108
+ "sub" => "1.0.0",
109
+ }
110
+ } }
111
+ let(:sub_metadata) { {
112
+ "name" => "sub",
113
+ "version" => "1.0.0",
114
+ "dependencies" => { },
115
+ } }
116
+
117
+ before do
118
+ write_json_file! "cookbook-sources/main/metadata.json", main_metadata
119
+ write_json_file! "cookbook-sources/sub/metadata.json", sub_metadata
120
+ write_file! "Cheffile", strip_heredoc(<<-CHEFFILE)
121
+ path 'cookbook-sources'
122
+ cookbook 'main'
123
+ CHEFFILE
124
+
125
+ cli! "install"
126
+ end
127
+
128
+ context "showing all without a lockfile" do
129
+ before do
130
+ pwd.join("Cheffile.lock").delete
131
+
132
+ cli! "show"
133
+ end
134
+
135
+ specify { exit_status.should == 1 }
136
+
137
+ it "should print a warning" do
138
+ stdout.should == strip_heredoc(<<-STDOUT)
139
+ Be sure to install first!
140
+ STDOUT
141
+ end
142
+ end
143
+
144
+ context "showing all" do
145
+ before do
146
+ cli! "show"
147
+ end
148
+
149
+ specify { exit_status.should == 0 }
150
+
151
+ it "should print a summary" do
152
+ stdout.should == strip_heredoc(<<-STDOUT)
153
+ main (1.0.0)
154
+ sub (1.0.0)
155
+ STDOUT
156
+ end
157
+ end
158
+
159
+ context "showing one without dependencies" do
160
+ before do
161
+ cli! "show", "sub"
162
+ end
163
+
164
+ specify { exit_status.should == 0 }
165
+
166
+ it "should print the details" do
167
+ stdout.should == strip_heredoc(<<-STDOUT)
168
+ sub (1.0.0)
169
+ source: cookbook-sources
170
+ STDOUT
171
+ end
172
+ end
173
+
174
+ context "showing one with dependencies" do
175
+ before do
176
+ cli! "show", "main"
177
+ end
178
+
179
+ specify { exit_status.should == 0 }
180
+
181
+ it "should print the details" do
182
+ stdout.should == strip_heredoc(<<-STDOUT)
183
+ main (1.0.0)
184
+ source: cookbook-sources
185
+ dependencies:
186
+ sub (= 1.0.0)
187
+ STDOUT
188
+ end
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,266 @@
1
+ require 'pathname'
2
+ require 'json'
3
+ require 'webmock'
4
+
5
+ require 'librarian'
6
+ require 'librarian/helpers'
7
+ require 'librarian/chef'
8
+ require 'librarian/linter/source_linter'
9
+
10
+ module Librarian
11
+ module Chef
12
+ module Source
13
+ describe Site do
14
+
15
+ include WebMock::API
16
+
17
+ let(:project_path) do
18
+ project_path = Pathname.new(__FILE__).expand_path
19
+ project_path = project_path.dirname until project_path.join("Rakefile").exist?
20
+ project_path
21
+ end
22
+ let(:tmp_path) { project_path.join("tmp/spec/functional/chef/source/site") }
23
+ after { tmp_path.rmtree if tmp_path && tmp_path.exist? }
24
+ let(:sample_path) { tmp_path.join("sample") }
25
+ let(:sample_metadata) do
26
+ Helpers.strip_heredoc(<<-METADATA)
27
+ version "0.6.5"
28
+ METADATA
29
+ end
30
+
31
+ let(:api_url) { "http://site.cookbooks.com" }
32
+
33
+ let(:sample_index_data) do
34
+ {
35
+ "name" => "sample",
36
+ "versions" => [
37
+ "#{api_url}/cookbooks/sample/versions/0_6_5"
38
+ ]
39
+ }
40
+ end
41
+ let(:sample_0_6_5_data) do
42
+ {
43
+ "version" => "0.6.5",
44
+ "file" => "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz"
45
+ }
46
+ end
47
+ let(:sample_0_6_5_package) do
48
+ s = StringIO.new
49
+ z = Zlib::GzipWriter.new(s, Zlib::NO_COMPRESSION)
50
+ t = Archive::Tar::Minitar::Output.new(z)
51
+ t.tar.add_file_simple("sample/metadata.rb", :mode => 0700,
52
+ :size => sample_metadata.bytesize){|io| io.write(sample_metadata)}
53
+ t.close
54
+ z.close unless z.closed?
55
+ s.string
56
+ end
57
+
58
+ # depends on repo_path being defined in each context
59
+ let(:env) { Environment.new(:project_path => repo_path) }
60
+
61
+ before do
62
+ stub_request(:get, "#{api_url}/cookbooks/sample").
63
+ to_return(:body => JSON.dump(sample_index_data))
64
+
65
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5").
66
+ to_return(:body => JSON.dump(sample_0_6_5_data))
67
+
68
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz").
69
+ to_return(:body => sample_0_6_5_package)
70
+ end
71
+
72
+ after do
73
+ WebMock.reset!
74
+ end
75
+
76
+ let(:repo_path) { tmp_path.join("methods") }
77
+ before { repo_path.mkpath }
78
+
79
+ describe "lint" do
80
+ it "lints" do
81
+ Librarian::Linter::SourceLinter.lint! described_class
82
+ end
83
+ end
84
+
85
+ describe "class methods" do
86
+
87
+ describe ".lock_name" do
88
+ specify { described_class.lock_name.should == "SITE" }
89
+ end
90
+
91
+ describe ".from_spec_args" do
92
+ it "gives the expected source" do
93
+ args = { }
94
+ source = described_class.from_spec_args(env, api_url, args)
95
+ source.uri.should == api_url
96
+ end
97
+
98
+ it "raises on unexpected args" do
99
+ args = {:k => 3}
100
+ expect { described_class.from_spec_args(env, api_url, args) }.
101
+ to raise_error Librarian::Error, "unrecognized options: k"
102
+ end
103
+ end
104
+
105
+ describe ".from_lock_options" do
106
+ it "gives the expected source" do
107
+ options = {:remote => api_url}
108
+ source = described_class.from_lock_options(env, options)
109
+ source.uri.should == api_url
110
+ end
111
+
112
+ it "roundtrips" do
113
+ options = {:remote => api_url}
114
+ source = described_class.from_lock_options(env, options)
115
+ source.to_lock_options.should == options
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ describe "instance methods" do
122
+ let(:source) { described_class.new(env, api_url) }
123
+
124
+ describe "#manifests" do
125
+ it "gives a list of all manifests" do
126
+ manifests = source.manifests("sample")
127
+ manifests.should have(1).item
128
+ manifest = manifests.first
129
+ manifest.source.should be source
130
+ manifest.version.should == Manifest::Version.new("0.6.5")
131
+ manifest.dependencies.should be_empty
132
+ end
133
+ end
134
+
135
+ describe "#fetch_version" do
136
+ it "fetches the version based on extra" do
137
+ extra = "#{api_url}/cookbooks/sample/versions/0_6_5"
138
+ source.fetch_version("sample", extra).should == "0.6.5"
139
+ end
140
+ end
141
+
142
+ describe "#fetch_dependencies" do
143
+ it "fetches the dependencies based on extra" do
144
+ extra = "#{api_url}/cookbooks/sample/versions/0_6_5"
145
+ source.fetch_dependencies("sample", "0.6.5", extra).should == [ ]
146
+ end
147
+ end
148
+
149
+ describe "#pinned?" do
150
+ it "returns false" do
151
+ source.should_not be_pinned
152
+ end
153
+ end
154
+
155
+ describe "#unpin!" do
156
+ it "is a no-op" do
157
+ source.unpin!
158
+ end
159
+ end
160
+
161
+ describe "#install!" do
162
+ before { env.install_path.mkpath }
163
+
164
+ context "directly" do
165
+ it "installs the manifest" do
166
+ manifest = Manifest.new(source, "sample")
167
+ manifest.version = "0.6.5"
168
+ source.install!(manifest)
169
+ text = env.install_path.join("sample/metadata.rb").read
170
+ text.should == sample_metadata
171
+ end
172
+ end
173
+
174
+ context "indirectly" do
175
+ it "installs the manifest" do
176
+ manifest = source.manifests("sample").first
177
+ source.install!(manifest)
178
+ text = env.install_path.join("sample/metadata.rb").read
179
+ text.should == sample_metadata
180
+ end
181
+ end
182
+ end
183
+
184
+ describe "#to_spec_args" do
185
+ it "gives the expected spec args" do
186
+ source.to_spec_args.should == [api_url, { }]
187
+ end
188
+ end
189
+
190
+ describe "#to_lock_options" do
191
+ it "gives the expected lock options" do
192
+ source.to_lock_options.should == {:remote => api_url}
193
+ end
194
+
195
+ it "roundtrips" do
196
+ options = source.to_lock_options
197
+ described_class.from_lock_options(env, options).should == source
198
+ end
199
+ end
200
+
201
+ end
202
+
203
+ describe "following http redirects" do
204
+ let(:source) { described_class.new(env, api_url) }
205
+
206
+ def redirect_to(url)
207
+ {:status => 302, :headers => {"Location" => url}}
208
+ end
209
+
210
+ context "with a sequence of http redirects" do
211
+ before do
212
+ stub_request(:get, "#{api_url}/cookbooks/sample").
213
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
214
+ stub_request(:get, "#{api_url}/cookbooks/sample-1").
215
+ to_return redirect_to "#{api_url}/cookbooks/sample-2"
216
+ stub_request(:get, "#{api_url}/cookbooks/sample-2").
217
+ to_return(:body => JSON.dump(sample_index_data))
218
+ end
219
+
220
+ it "follows a sequence of redirects" do
221
+ manifest = source.manifests("sample").first
222
+ manifest.version.to_s.should == "0.6.5"
223
+ end
224
+ end
225
+
226
+ context "with too many http redirects" do
227
+ before do
228
+ stub_request(:get, "#{api_url}/cookbooks/sample").
229
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
230
+ (1 .. 11).each do |i|
231
+ stub_request(:get, "#{api_url}/cookbooks/sample-#{i}").
232
+ to_return redirect_to "#{api_url}/cookbooks/sample-#{i + 1}"
233
+ end
234
+ stub_request(:get, "#{api_url}/cookbooks/sample-12").
235
+ to_return(:body => JSON.dump(sample_index_data))
236
+ end
237
+
238
+ it "raises, warning of too many redirects" do
239
+ expect { source.manifests("sample").first }.
240
+ to raise_error Librarian::Error, /because too many redirects!/
241
+ end
242
+ end
243
+
244
+ context "with a redirect cycle" do
245
+ before do
246
+ stub_request(:get, "#{api_url}/cookbooks/sample").
247
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
248
+ (1 .. 8).each do |i|
249
+ stub_request(:get, "#{api_url}/cookbooks/sample-#{i}").
250
+ to_return redirect_to "#{api_url}/cookbooks/sample-#{i + 1}"
251
+ end
252
+ stub_request(:get, "#{api_url}/cookbooks/sample-9").
253
+ to_return redirect_to "#{api_url}/cookbooks/sample-6"
254
+ end
255
+
256
+ it "raises, warning of a redirect cycle" do
257
+ expect { source.manifests("sample").first }.
258
+ to raise_error Librarian::Error, /because redirect cycle!/
259
+ end
260
+ end
261
+ end
262
+
263
+ end
264
+ end
265
+ end
266
+ end