librarian-chef 0.0.1.beta.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.
@@ -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