librarian-chef-nochef 0.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.travis.yml +16 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +396 -0
- data/Rakefile +1 -0
- data/bin/librarian-chef +7 -0
- data/lib/librarian-chef.rb +1 -0
- data/lib/librarian/chef.rb +1 -0
- data/lib/librarian/chef/cli.rb +47 -0
- data/lib/librarian/chef/dsl.rb +16 -0
- data/lib/librarian/chef/environment.rb +32 -0
- data/lib/librarian/chef/extension.rb +9 -0
- data/lib/librarian/chef/integration/knife.rb +46 -0
- data/lib/librarian/chef/manifest_reader.rb +56 -0
- data/lib/librarian/chef/metadata.rb +70 -0
- data/lib/librarian/chef/source.rb +4 -0
- data/lib/librarian/chef/source/git.rb +25 -0
- data/lib/librarian/chef/source/github.rb +27 -0
- data/lib/librarian/chef/source/local.rb +74 -0
- data/lib/librarian/chef/source/path.rb +12 -0
- data/lib/librarian/chef/source/site.rb +481 -0
- data/lib/librarian/chef/templates/Cheffile +15 -0
- data/lib/librarian/chef/version.rb +5 -0
- data/librarian-chef-nochef.gemspec +26 -0
- data/spec/functional/chef/cli_spec.rb +195 -0
- data/spec/functional/chef/source/site_spec.rb +409 -0
- data/spec/integration/chef/source/git_spec.rb +449 -0
- data/spec/integration/chef/source/site_spec.rb +215 -0
- data/spec/support/project_path.rb +27 -0
- data/spec/unit/metadata_spec.rb +85 -0
- metadata +156 -0
@@ -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,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
|