librarian-chef-nochef 0.1.0

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.2"
4
+ end
5
+ end
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = "librarian-chef-nochef"
7
+ gem.version = "0.1.0"
8
+ gem.authors = ["Emiliano Ticci", "Jay Feldblum"]
9
+ gem.email = ["emiticci@gmail.com", "y_feldblum@yahoo.com"]
10
+ gem.summary = %q{A Bundler for your Chef Cookbooks that does not depends on chef.}
11
+ gem.description = %q{A Bundler for your Chef Cookbooks that does not depends on chef.}
12
+ gem.homepage = "https://github.com/emyl/librarian-chef-nochef"
13
+ gem.license = "MIT"
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"
21
+ gem.add_dependency "minitar", ">= 0.5.2"
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "rspec"
25
+ gem.add_development_dependency "webmock"
26
+ 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,409 @@
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
+ require 'support/project_path'
11
+
12
+ module Librarian
13
+ module Chef
14
+ module Source
15
+ describe Site do
16
+
17
+ include WebMock::API
18
+
19
+ let(:project_path) { ::Support::ProjectPath.project_path }
20
+ let(:tmp_path) { project_path.join("tmp/spec/functional/chef/source/site") }
21
+ after { tmp_path.rmtree if tmp_path && tmp_path.exist? }
22
+
23
+ let(:api_url) { "http://site.cookbooks.com" }
24
+
25
+ let(:repo_path) { tmp_path.join("repo") }
26
+ let(:env) { repo_path.mkpath ; Environment.new(:project_path => repo_path) }
27
+
28
+ after { WebMock.reset! }
29
+
30
+ describe "lint" do
31
+ it "lints" do
32
+ Librarian::Linter::SourceLinter.lint! described_class
33
+ end
34
+ end
35
+
36
+ describe "class methods" do
37
+
38
+ describe ".lock_name" do
39
+ specify { described_class.lock_name.should == "SITE" }
40
+ end
41
+
42
+ describe ".from_spec_args" do
43
+ it "gives the expected source" do
44
+ args = { }
45
+ source = described_class.from_spec_args(env, api_url, args)
46
+ source.uri.should == api_url
47
+ end
48
+
49
+ it "raises on unexpected args" do
50
+ args = {:k => 3}
51
+ expect { described_class.from_spec_args(env, api_url, args) }.
52
+ to raise_error Librarian::Error, "unrecognized options: k"
53
+ end
54
+ end
55
+
56
+ describe ".from_lock_options" do
57
+ it "gives the expected source" do
58
+ options = {:remote => api_url}
59
+ source = described_class.from_lock_options(env, options)
60
+ source.uri.should == api_url
61
+ end
62
+
63
+ it "roundtrips" do
64
+ options = {:remote => api_url}
65
+ source = described_class.from_lock_options(env, options)
66
+ source.to_lock_options.should == options
67
+ end
68
+ end
69
+
70
+ end
71
+
72
+ describe "instance methods" do
73
+
74
+ let(:sample_metadata) do
75
+ Helpers.strip_heredoc(<<-METADATA)
76
+ version "0.6.5"
77
+ METADATA
78
+ end
79
+
80
+ let(:sample_index_data) do
81
+ {
82
+ "name" => "sample",
83
+ "versions" => [
84
+ "#{api_url}/cookbooks/sample/versions/0_6_5"
85
+ ]
86
+ }
87
+ end
88
+ let(:sample_0_6_5_data) do
89
+ {
90
+ "version" => "0.6.5",
91
+ "file" => "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz"
92
+ }
93
+ end
94
+ let(:sample_0_6_5_package) do
95
+ s = StringIO.new
96
+ z = Zlib::GzipWriter.new(s, Zlib::NO_COMPRESSION)
97
+ t = Archive::Tar::Minitar::Output.new(z)
98
+ t.tar.add_file_simple("sample/metadata.rb", :mode => 0700,
99
+ :size => sample_metadata.bytesize){|io| io.write(sample_metadata)}
100
+ t.close
101
+ z.close unless z.closed?
102
+ s.string
103
+ end
104
+
105
+ before do
106
+ stub_request(:get, "#{api_url}/cookbooks/sample").
107
+ to_return(:body => JSON.dump(sample_index_data))
108
+
109
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5").
110
+ to_return(:body => JSON.dump(sample_0_6_5_data))
111
+
112
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz").
113
+ to_return(:body => sample_0_6_5_package)
114
+ end
115
+
116
+ let(:source) { described_class.new(env, api_url) }
117
+
118
+ describe "#manifests" do
119
+ it "gives a list of all manifests" do
120
+ manifests = source.manifests("sample")
121
+ manifests.should have(1).item
122
+ manifest = manifests.first
123
+ manifest.source.should be source
124
+ manifest.version.should == Manifest::Version.new("0.6.5")
125
+ manifest.dependencies.should be_empty
126
+ end
127
+ end
128
+
129
+ describe "#fetch_version" do
130
+ it "fetches the version based on extra" do
131
+ extra = "#{api_url}/cookbooks/sample/versions/0_6_5"
132
+ source.fetch_version("sample", extra).should == "0.6.5"
133
+ end
134
+ end
135
+
136
+ describe "#fetch_dependencies" do
137
+ it "fetches the dependencies based on extra" do
138
+ extra = "#{api_url}/cookbooks/sample/versions/0_6_5"
139
+ source.fetch_dependencies("sample", "0.6.5", extra).should == [ ]
140
+ end
141
+ end
142
+
143
+ describe "#pinned?" do
144
+ it "returns false" do
145
+ source.should_not be_pinned
146
+ end
147
+ end
148
+
149
+ describe "#unpin!" do
150
+ it "is a no-op" do
151
+ source.unpin!
152
+ end
153
+ end
154
+
155
+ describe "#install!" do
156
+ before { env.install_path.mkpath }
157
+
158
+ context "directly" do
159
+ it "installs the manifest" do
160
+ manifest = Manifest.new(source, "sample")
161
+ manifest.version = "0.6.5"
162
+ source.install!(manifest)
163
+ text = env.install_path.join("sample/metadata.rb").read
164
+ text.should == sample_metadata
165
+ end
166
+ end
167
+
168
+ context "indirectly" do
169
+ it "installs the manifest" do
170
+ manifest = source.manifests("sample").first
171
+ source.install!(manifest)
172
+ text = env.install_path.join("sample/metadata.rb").read
173
+ text.should == sample_metadata
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "#to_spec_args" do
179
+ it "gives the expected spec args" do
180
+ source.to_spec_args.should == [api_url, { }]
181
+ end
182
+ end
183
+
184
+ describe "#to_lock_options" do
185
+ it "gives the expected lock options" do
186
+ source.to_lock_options.should == {:remote => api_url}
187
+ end
188
+
189
+ it "roundtrips" do
190
+ options = source.to_lock_options
191
+ described_class.from_lock_options(env, options).should == source
192
+ end
193
+ end
194
+
195
+ end
196
+
197
+ describe "following http redirects" do
198
+ let(:source) { described_class.new(env, api_url) }
199
+
200
+ let(:sample_metadata) do
201
+ Helpers.strip_heredoc(<<-METADATA)
202
+ version "0.6.5"
203
+ METADATA
204
+ end
205
+
206
+ let(:sample_index_data) do
207
+ {
208
+ "name" => "sample",
209
+ "versions" => [
210
+ "#{api_url}/cookbooks/sample/versions/0_6_5"
211
+ ]
212
+ }
213
+ end
214
+ let(:sample_0_6_5_data) do
215
+ {
216
+ "version" => "0.6.5",
217
+ "file" => "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz"
218
+ }
219
+ end
220
+ let(:sample_0_6_5_package) do
221
+ s = StringIO.new
222
+ z = Zlib::GzipWriter.new(s, Zlib::NO_COMPRESSION)
223
+ t = Archive::Tar::Minitar::Output.new(z)
224
+ t.tar.add_file_simple("sample/metadata.rb", :mode => 0700,
225
+ :size => sample_metadata.bytesize){|io| io.write(sample_metadata)}
226
+ t.close
227
+ z.close unless z.closed?
228
+ s.string
229
+ end
230
+
231
+ def redirect_to(url)
232
+ {:status => 302, :headers => {"Location" => url}}
233
+ end
234
+
235
+ context "with a sequence of http redirects" do
236
+ before do
237
+ stub_request(:get, "#{api_url}/cookbooks/sample").
238
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
239
+ stub_request(:get, "#{api_url}/cookbooks/sample-1").
240
+ to_return redirect_to "#{api_url}/cookbooks/sample-2"
241
+ stub_request(:get, "#{api_url}/cookbooks/sample-2").
242
+ to_return(:body => JSON.dump(sample_index_data))
243
+
244
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5").
245
+ to_return(:body => JSON.dump(sample_0_6_5_data))
246
+
247
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar.gz").
248
+ to_return(:body => sample_0_6_5_package)
249
+ end
250
+
251
+ it "follows a sequence of redirects" do
252
+ manifest = source.manifests("sample").first
253
+ manifest.version.to_s.should == "0.6.5"
254
+ end
255
+ end
256
+
257
+ context "with too many http redirects" do
258
+ before do
259
+ stub_request(:get, "#{api_url}/cookbooks/sample").
260
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
261
+ (1 .. 11).each do |i|
262
+ stub_request(:get, "#{api_url}/cookbooks/sample-#{i}").
263
+ to_return redirect_to "#{api_url}/cookbooks/sample-#{i + 1}"
264
+ end
265
+ stub_request(:get, "#{api_url}/cookbooks/sample-12").
266
+ to_return(:body => JSON.dump(sample_index_data))
267
+ end
268
+
269
+ it "raises, warning of too many redirects" do
270
+ expect { source.manifests("sample").first }.
271
+ to raise_error Librarian::Error, /because too many redirects!/
272
+ end
273
+ end
274
+
275
+ context "with a redirect cycle" do
276
+ before do
277
+ stub_request(:get, "#{api_url}/cookbooks/sample").
278
+ to_return redirect_to "#{api_url}/cookbooks/sample-1"
279
+ (1 .. 8).each do |i|
280
+ stub_request(:get, "#{api_url}/cookbooks/sample-#{i}").
281
+ to_return redirect_to "#{api_url}/cookbooks/sample-#{i + 1}"
282
+ end
283
+ stub_request(:get, "#{api_url}/cookbooks/sample-9").
284
+ to_return redirect_to "#{api_url}/cookbooks/sample-6"
285
+ end
286
+
287
+ it "raises, warning of a redirect cycle" do
288
+ expect { source.manifests("sample").first }.
289
+ to raise_error Librarian::Error, /because redirect cycle!/
290
+ end
291
+ end
292
+ end
293
+
294
+ describe "extracting .tar packages" do
295
+ let(:source) { described_class.new(env, api_url) }
296
+
297
+ let(:sample_metadata) do
298
+ Helpers.strip_heredoc(<<-METADATA)
299
+ version "0.6.5"
300
+ METADATA
301
+ end
302
+
303
+ let(:sample_index_data) do
304
+ {
305
+ "name" => "sample",
306
+ "versions" => [
307
+ "#{api_url}/cookbooks/sample/versions/0_6_5"
308
+ ]
309
+ }
310
+ end
311
+ let(:sample_0_6_5_data) do
312
+ {
313
+ "version" => "0.6.5",
314
+ "file" => "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar"
315
+ }
316
+ end
317
+ let(:sample_0_6_5_package) do
318
+ s = StringIO.new
319
+ t = Archive::Tar::Minitar::Output.new(s)
320
+ t.tar.add_file_simple("sample/metadata.rb", :mode => 0700,
321
+ :size => sample_metadata.bytesize){|io| io.write(sample_metadata)}
322
+ t.close
323
+ s.string
324
+ end
325
+
326
+ before do
327
+ stub_request(:get, "#{api_url}/cookbooks/sample").
328
+ to_return(:body => JSON.dump(sample_index_data))
329
+
330
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5").
331
+ to_return(:body => JSON.dump(sample_0_6_5_data))
332
+
333
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar").
334
+ to_return(:body => sample_0_6_5_package)
335
+ end
336
+
337
+ it "installs the manifest" do
338
+ env.install_path.mkpath
339
+ manifest = source.manifests("sample").first
340
+ source.install!(manifest)
341
+ text = env.install_path.join("sample/metadata.rb").read
342
+ text.should == sample_metadata
343
+ end
344
+
345
+ end
346
+
347
+ describe "extracting .tar packages with long filenames" do
348
+ let(:source) { described_class.new(env, api_url) }
349
+
350
+ before { tmp_path.mkpath unless tmp_path.exist? }
351
+ let(:bad_name_size) { Librarian::Posix.run!(%W[getconf NAME_MAX #{tmp_path}]).to_i }
352
+ let(:bad_name_path) { SecureRandom.hex((bad_name_size.to_f/2).ceil)[0...bad_name_size] }
353
+
354
+ before { expect(bad_name_path.size).to eq bad_name_size }
355
+
356
+ let(:sample_metadata) do
357
+ Helpers.strip_heredoc(<<-METADATA)
358
+ version "0.6.5"
359
+ METADATA
360
+ end
361
+
362
+ let(:sample_index_data) do
363
+ {
364
+ "name" => "sample",
365
+ "versions" => [
366
+ "#{api_url}/cookbooks/sample/versions/0_6_5"
367
+ ]
368
+ }
369
+ end
370
+ let(:sample_0_6_5_data) do
371
+ {
372
+ "version" => "0.6.5",
373
+ "file" => "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar"
374
+ }
375
+ end
376
+ let(:sample_0_6_5_package) do
377
+ tar_path = tmp_path + "tar-source"
378
+ tar_path.mkpath
379
+ tar_path.join("sample").mkpath
380
+ tar_path.join("sample/metadata.rb").open("w"){|io| io.write(sample_metadata)}
381
+ tar_path.join("sample/#{bad_name_path}").open("w"){|io| io.write("?")}
382
+ Librarian::Posix.run!(%W[tar cz -C #{tar_path} . ])
383
+ end
384
+
385
+ before do
386
+ stub_request(:get, "#{api_url}/cookbooks/sample").
387
+ to_return(:body => JSON.dump(sample_index_data))
388
+
389
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5").
390
+ to_return(:body => JSON.dump(sample_0_6_5_data))
391
+
392
+ stub_request(:get, "#{api_url}/cookbooks/sample/versions/0_6_5/file.tar").
393
+ to_return(:body => sample_0_6_5_package)
394
+ end
395
+
396
+ it "installs the manifest" do
397
+ env.install_path.mkpath
398
+ manifest = source.manifests("sample").first
399
+ source.install!(manifest)
400
+ text = env.install_path.join("sample/#{bad_name_path}").read
401
+ text.should == "?"
402
+ end
403
+
404
+ end
405
+
406
+ end
407
+ end
408
+ end
409
+ end