knife-essentials 0.9.2 → 0.9.3

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.
@@ -28,20 +28,14 @@ module ChefFS
28
28
  super("nodes", parent, nil, ChefFS::DataHandler::NodeDataHandler.new)
29
29
  end
30
30
 
31
- # Override children to respond to environment
32
- # TODO let's not do this mmkay
33
- def children
34
- @children ||= begin
35
- env_api_path = environment ? "environments/#{environment}/#{api_path}" : api_path
36
- rest.get_rest(env_api_path).keys.sort.map { |key| RestListEntry.new("#{key}.json", self, true) }
37
- rescue Net::HTTPServerException
38
- if $!.response.code == "404"
39
- raise ChefFS::FileSystem::NotFoundError.new(self, $!)
40
- else
41
- raise
42
- end
43
- end
44
- end
31
+ # Override to respond to environment
32
+ def chef_collection
33
+ rest.get_rest(env_api_path)
34
+ end
35
+
36
+ def env_api_path
37
+ environment ? "environments/#{environment}/#{api_path}" : api_path
38
+ end
45
39
  end
46
40
  end
47
41
  end
@@ -44,7 +44,7 @@ module ChefFS
44
44
 
45
45
  def children
46
46
  begin
47
- @children ||= rest.get_rest(api_path).keys.sort.map do |key|
47
+ @children ||= chef_collection.keys.sort.map do |key|
48
48
  _make_child_entry("#{key}.json", true)
49
49
  end
50
50
  rescue Net::HTTPServerException => e
@@ -56,6 +56,10 @@ module ChefFS
56
56
  end
57
57
  end
58
58
 
59
+ def chef_collection
60
+ rest.get_rest(api_path)
61
+ end
62
+
59
63
  def identity_key
60
64
  'name'
61
65
  end
@@ -64,7 +68,7 @@ module ChefFS
64
68
  # DataBagDir.create_child as well.
65
69
  def create_child(name, file_contents)
66
70
  begin
67
- object = Chef::JSONCompat.from_json(file_contents).to_hash
71
+ object = JSON.parse(file_contents, :create_additions => false)
68
72
  rescue JSON::ParserError => e
69
73
  raise ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Parse error reading JSON creating child '#{name}': #{e}"
70
74
  end
@@ -74,7 +74,17 @@ module ChefFS
74
74
 
75
75
  def read
76
76
  # Minimize the value so the results don't look terrible
77
- Chef::JSONCompat.to_json_pretty(minimize_value(chef_object.to_hash))
77
+ Chef::JSONCompat.to_json_pretty(minimize_value(chef_hash))
78
+ end
79
+
80
+ def chef_hash
81
+ JSON.parse(raw_request(api_path), :create_additions => false)
82
+ rescue Net::HTTPServerException => e
83
+ if $!.response.code == "404"
84
+ raise ChefFS::FileSystem::NotFoundError.new(self, $!)
85
+ else
86
+ raise ChefFS::FileSystem::OperationFailedError.new(:read, self, e)
87
+ end
78
88
  end
79
89
 
80
90
  def chef_object
@@ -113,7 +123,8 @@ module ChefFS
113
123
  value = minimize_value(value)
114
124
  value_json = Chef::JSONCompat.to_json_pretty(value)
115
125
  begin
116
- other_value = Chef::JSONCompat.from_json(other_value_json, :create_additions => false)
126
+ #other_value = Chef::JSONCompat.from_json(other_value_json, :create_additions => false)
127
+ other_value = JSON.parse(other_value_json, :create_additions => false)
117
128
  rescue JSON::ParserError => e
118
129
  Chef::Log.warn("Parse error reading #{other.path_for_printing} as JSON: #{e}")
119
130
  return [ nil, value_json, other_value_json ]
@@ -130,7 +141,8 @@ module ChefFS
130
141
 
131
142
  def write(file_contents)
132
143
  begin
133
- object = Chef::JSONCompat.from_json(file_contents).to_hash
144
+ #object = Chef::JSONCompat.from_json(file_contents).to_hash
145
+ object = JSON.parse(file_contents, :create_additions => false)
134
146
  rescue JSON::ParserError => e
135
147
  raise ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Parse error reading JSON: #{e}"
136
148
  end
@@ -29,6 +29,11 @@ module ChefFS
29
29
  :long => '--repo-mode MODE',
30
30
  :default => "default",
31
31
  :description => "Specifies the local repository layout. Values: default or full"
32
+
33
+ option :chef_repo_path,
34
+ :long => '--chef-repo-path PATH',
35
+ :default => nil,
36
+ :description => 'Overrides the location of chef repo. Default is specified by chef_repo_paths in the config'
32
37
  end
33
38
 
34
39
  def chef_fs
@@ -37,16 +42,16 @@ module ChefFS
37
42
 
38
43
  def chef_repo_paths
39
44
  @chef_repo_paths ||= begin
40
- result = config_paths(:chef_repo_path)
41
- if result
42
- result
43
- else
44
- if Chef::Config[:cookbook_path]
45
- Array(Chef::Config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
46
- else
47
- nil
48
- end
49
- end
45
+ result = config_paths(:chef_repo_path)
46
+ if result
47
+ result
48
+ else
49
+ if Chef::Config[:cookbook_path]
50
+ Array(Chef::Config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
51
+ else
52
+ nil
53
+ end
54
+ end
50
55
  end
51
56
  end
52
57
 
@@ -78,14 +83,20 @@ module ChefFS
78
83
  object_names = %w(cookbooks data_bags environments roles)
79
84
  end
80
85
  object_names.each do |object_name|
81
- variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
82
- paths = config_paths(variable_name.to_sym)
83
- if !paths
84
- if !chef_repo_paths
85
- Chef::Log.error("Must specify either chef_repo_path or #{variable_name} in Chef config file")
86
- exit(1)
86
+ # If --chef-repo-path is passed in the command line, then override *everything*
87
+ # and assume all the subdirectory is contained in that directory.
88
+ if config[:chef_repo_path]
89
+ paths = [ "#{config[:chef_repo_path]}/#{object_name}" ]
90
+ else
91
+ variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
92
+ paths = config_paths(variable_name.to_sym)
93
+ if !paths
94
+ if !chef_repo_paths
95
+ Chef::Log.error("Must specify either chef_repo_path or #{variable_name} in Chef config file")
96
+ exit(1)
97
+ end
98
+ paths = chef_repo_paths.map { |path| File.join(path, object_name) }
87
99
  end
88
- paths = chef_repo_paths.map { |path| File.join(path, object_name) }
89
100
  end
90
101
  paths = paths.flatten.map { |path| File.expand_path(path) }
91
102
  result[object_name] = paths
@@ -1,3 +1,3 @@
1
1
  module ChefFS
2
- VERSION = "0.9.2"
2
+ VERSION = "0.9.3"
3
3
  end
@@ -20,6 +20,24 @@ require 'support/spec_helper'
20
20
  require 'chef_fs/file_system/chef_server_root_dir'
21
21
 
22
22
  describe ChefFS::FileSystem::ChefServerRootDir do
23
+
24
+ let(:should_receive_children) { endpoint.should_receive(:chef_collection).once.and_return(chef_collection) }
25
+ let(:should_receive_read) { endpoint_leaf.should_receive(:chef_hash).once.and_return(chef_hash) }
26
+ let(:should_throw_404) do
27
+ nonexistent_child.should_receive(:raw_request).
28
+ with("#{endpoint_name}/blah").
29
+ once.and_raise(Net::HTTPServerException.new(nil,Net::HTTPResponse.new(nil,'404',nil)))
30
+ end
31
+
32
+ let(:chef_collection) do
33
+ {
34
+ "achild" => "http://opscode.com/achild",
35
+ "bchild" => "http://opscode.com/bchild"
36
+ }
37
+ end
38
+
39
+ let(:chef_hash) { { 'a' => 'b' } }
40
+
23
41
  shared_examples 'a json endpoint dir leaf' do
24
42
  it 'parent is endpoint' do
25
43
  endpoint_leaf.parent.should == endpoint
@@ -41,10 +59,7 @@ describe ChefFS::FileSystem::ChefServerRootDir do
41
59
  endpoint_leaf.exists?.should be_true
42
60
  end
43
61
  it 'read returns content' do
44
- @rest.should_receive(:get_rest).with("#{endpoint_name}/#{endpoint_leaf_name}").once.and_return(
45
- {
46
- 'a' => 'b'
47
- })
62
+ should_receive_read
48
63
  endpoint_leaf.read.should == '{
49
64
  "name": "achild",
50
65
  "a": "b"
@@ -52,6 +67,7 @@ describe ChefFS::FileSystem::ChefServerRootDir do
52
67
  end
53
68
  end
54
69
 
70
+
55
71
  shared_examples 'a json rest endpoint dir' do
56
72
  it 'parent is root' do
57
73
  endpoint.parent.should == root_dir
@@ -81,17 +97,13 @@ describe ChefFS::FileSystem::ChefServerRootDir do
81
97
  endpoint.can_have_child?('blah', true).should be_false
82
98
  endpoint.can_have_child?('blah.json', true).should be_false
83
99
  end
84
- let(:should_receive_children) {
85
- @rest.should_receive(:get_rest).with(endpoint_name).once.and_return(
86
- {
87
- "achild" => "http://opscode.com/achild",
88
- "bchild" => "http://opscode.com/bchild"
89
- })
90
- }
100
+
101
+
91
102
  it 'has correct children' do
92
103
  should_receive_children
93
104
  endpoint.children.map { |child| child.name }.should =~ %w(achild.json bchild.json)
94
105
  end
106
+
95
107
  context 'achild in endpoint.children' do
96
108
  let(:endpoint_leaf_name) { 'achild' }
97
109
  let(:endpoint_leaf) do
@@ -121,7 +133,7 @@ describe ChefFS::FileSystem::ChefServerRootDir do
121
133
  nonexistent_child.dir?.should be_false
122
134
  end
123
135
  it 'read returns NotFoundError' do
124
- @rest.should_receive(:get_rest).with("#{endpoint_name}/blah").once.and_raise(Net::HTTPServerException.new(nil,Net::HTTPResponse.new(nil,'404',nil)))
136
+ should_throw_404
125
137
  expect { nonexistent_child.read }.to raise_error(ChefFS::FileSystem::NotFoundError)
126
138
  end
127
139
  end
@@ -135,10 +147,12 @@ describe ChefFS::FileSystem::ChefServerRootDir do
135
147
  :client_key => 'key'
136
148
  }, 'everything')
137
149
  }
150
+
138
151
  before(:each) do
139
152
  @rest = double("rest")
140
153
  Chef::REST.stub(:new).with('url','username','key') { @rest }
141
154
  end
155
+
142
156
  context 'the root directory' do
143
157
  it 'has no parent' do
144
158
  root_dir.parent.should == nil
@@ -0,0 +1,582 @@
1
+ #
2
+ # Author:: John Keiser (<jkeiser@opscode.com>)
3
+ # Copyright:: Copyright (c) 2012 Opscode, Inc.
4
+ # License:: Apache License, Version 2.0
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ require 'support/spec_helper'
20
+ require 'chef_fs/file_system/chef_server_root_dir'
21
+ require 'chef_fs/file_system'
22
+
23
+ describe ChefFS::FileSystem::CookbookDir do
24
+ let(:root_dir) {
25
+ ChefFS::FileSystem::ChefServerRootDir.new('remote',
26
+ {
27
+ :chef_server_url => 'url',
28
+ :node_name => 'username',
29
+ :client_key => 'key'
30
+ },
31
+ 'everything')
32
+ }
33
+
34
+ let(:cookbook_response) do
35
+ {
36
+ "achild" => {
37
+ "url" => "http://example.com/cookbooks/achild",
38
+ 'versions' => [
39
+ { "version" => '2.0.0', 'url' => 'http://example.com/cookbooks/achild/2.0.0' },
40
+ { "version" => '1.0.0', 'url' => 'http://example.com/cookbooks/achild/2.0.0' }, ] },
41
+ "bchild" => {
42
+ "url" => "http://example.com/cookbokks/bchild",
43
+ 'versions' => [ { "version" => '1.0.0', 'url' => 'http://example.com/cookbooks/achild/2.0.0' }, ] },
44
+
45
+ }
46
+ end
47
+
48
+ let(:cookbooks_dir) { root_dir.child('cookbooks') }
49
+ let(:should_list_cookbooks) { rest.should_receive(:get_rest).with('cookbooks').once.and_return(cookbook_response) }
50
+
51
+ let(:rest) { double 'rest' }
52
+ before(:each) { Chef::REST.stub(:new).with('url','username','key') { rest } }
53
+
54
+ # Cookbook dir (/cookbooks/<blah>)
55
+ shared_examples_for 'a segment directory' do
56
+ it 'has cookbook as parent' do
57
+ segment_dir.parent.should == cookbook_dir
58
+ end
59
+ it 'exists' do
60
+ segment_dir.exists?.should be_true
61
+ end
62
+ it 'is a directory' do
63
+ segment_dir.dir?.should be_true
64
+ end
65
+ it 'name is correct' do
66
+ segment_dir.name.should == segment_dir_name
67
+ end
68
+ it 'path is correct' do
69
+ segment_dir.path.should == "/cookbooks/#{cookbook_dir_name}/#{segment_dir_name}"
70
+ end
71
+ it 'path_for_printing is correct' do
72
+ segment_dir.path_for_printing.should == "remote/cookbooks/#{cookbook_dir_name}/#{segment_dir_name}"
73
+ end
74
+ it 'has the right children' do
75
+ segment_dir.children =~ %w(a.rb b.txt subdir)
76
+ end
77
+ it 'children are identical to child()' do
78
+ segment_dir.child('a.rb').should == segment_dir.children.select { |child| child.name == 'a.rb' }.first
79
+ segment_dir.child('b.txt').should == segment_dir.children.select { |child| child.name == 'b.txt' }.first
80
+ segment_dir.child('subdir').should == segment_dir.children.select { |child| child.name == 'subdir' }.first
81
+ end
82
+ context 'subdirectory' do
83
+ it 'has segment as a parent' do
84
+ segment_dir.child('subdir').parent.should == segment_dir
85
+ end
86
+ it 'exists' do
87
+ segment_dir.child('subdir').exists?.should be_true
88
+ end
89
+ it 'is a directory' do
90
+ segment_dir.child('subdir').dir?.should be_true
91
+ end
92
+ it 'name is subdir' do
93
+ segment_dir.child('subdir').name.should == 'subdir'
94
+ end
95
+ it 'path is correct' do
96
+ segment_dir.child('subdir').path.should == "/cookbooks/#{cookbook_dir_name}/#{segment_dir_name}/subdir"
97
+ end
98
+ it 'path_for_printing is correct' do
99
+ segment_dir.child('subdir').path_for_printing.should == "remote/cookbooks/#{cookbook_dir_name}/#{segment_dir_name}/subdir"
100
+ end
101
+ it 'has the right children' do
102
+ segment_dir.child('subdir').children =~ %w(a.rb b.txt)
103
+ end
104
+ it 'children are identical to child()' do
105
+ segment_dir.child('subdir').child('a.rb').should == segment_dir.child('subdir').children.select { |child| child.name == 'a.rb' }.first
106
+ segment_dir.child('subdir').child('b.txt').should == segment_dir.child('subdir').children.select { |child| child.name == 'b.txt' }.first
107
+ end
108
+ end
109
+ end
110
+
111
+ shared_examples_for 'a cookbook' do
112
+ it 'has cookbooks as parent' do
113
+ cookbook_dir.parent == cookbooks_dir
114
+ end
115
+ it 'is a directory' do
116
+ should_list_cookbooks
117
+ cookbook_dir.dir?.should be_true
118
+ end
119
+ it 'exists' do
120
+ should_list_cookbooks
121
+ cookbook_dir.exists?.should be_true
122
+ end
123
+ it 'has name <cookbook name>' do
124
+ cookbook_dir.name.should == cookbook_dir_name
125
+ end
126
+ it 'has path /cookbooks/<cookbook name>' do
127
+ cookbook_dir.path.should == "/cookbooks/#{cookbook_dir_name}"
128
+ end
129
+ it 'has path_for_printing remote/cookbooks/<cookbook name>' do
130
+ cookbook_dir.path_for_printing.should == "remote/cookbooks/#{cookbook_dir_name}"
131
+ end
132
+ it 'can have segment directories as children' do
133
+ cookbook_dir.can_have_child?('attributes', true).should be_true
134
+ cookbook_dir.can_have_child?('definitions', true).should be_true
135
+ cookbook_dir.can_have_child?('recipes', true).should be_true
136
+ cookbook_dir.can_have_child?('libraries', true).should be_true
137
+ cookbook_dir.can_have_child?('templates', true).should be_true
138
+ cookbook_dir.can_have_child?('files', true).should be_true
139
+ cookbook_dir.can_have_child?('resources', true).should be_true
140
+ cookbook_dir.can_have_child?('providers', true).should be_true
141
+ end
142
+ it 'cannot have arbitrary directories as children' do
143
+ cookbook_dir.can_have_child?('blah', true).should be_false
144
+ cookbook_dir.can_have_child?('root_files', true).should be_false
145
+ end
146
+ it 'can have files as children' do
147
+ cookbook_dir.can_have_child?('blah', false).should be_true
148
+ cookbook_dir.can_have_child?('root_files', false).should be_true
149
+ cookbook_dir.can_have_child?('attributes', false).should be_true
150
+ cookbook_dir.can_have_child?('definitions', false).should be_true
151
+ cookbook_dir.can_have_child?('recipes', false).should be_true
152
+ cookbook_dir.can_have_child?('libraries', false).should be_true
153
+ cookbook_dir.can_have_child?('templates', false).should be_true
154
+ cookbook_dir.can_have_child?('files', false).should be_true
155
+ cookbook_dir.can_have_child?('resources', false).should be_true
156
+ cookbook_dir.can_have_child?('providers', false).should be_true
157
+ end
158
+ # TODO test empty parts, cross-contamination (root_files named templates/x.txt, libraries named recipes/blah.txt)
159
+ context 'with a full directory structure' do
160
+
161
+ let(:manifest) do
162
+ {
163
+ :attributes => json_files('attributes'),
164
+ :definitions => json_files('definitions'),
165
+ :files => json_files('files'),
166
+ :libraries => json_files('libraries'),
167
+ :providers => json_files('providers'),
168
+ :recipes => json_files('recipes'),
169
+ :resources => json_files('resources'),
170
+ :templates => json_files('templates'),
171
+ :root_files => root_files.map { |f| json_file(f, file_checksums[f]) }
172
+ }
173
+ end
174
+
175
+ def json_file(path, checksum)
176
+ {
177
+ :name => basename(path),
178
+ :url => "cookbook_file:#{path}",
179
+ :checksum => checksum,
180
+ :path => path,
181
+ :specificity => "default"
182
+ }
183
+ end
184
+
185
+ def json_files(segment)
186
+ files.
187
+ select { |f| /^#{segment}\//.match(f) }.
188
+ map { |f| json_file(f, file_checksums[f]) }
189
+ end
190
+
191
+ def basename(path)
192
+ ChefFS::PathUtils.split(path)[-1]
193
+ end
194
+
195
+ let(:segments) { %w(attributes definitions files libraries providers recipes resources templates) }
196
+ let(:some_filenames) { %w(a.rb b.txt subdir/a.rb subdir/b.txt) }
197
+ let(:root_files) { ['root_files'] + some_filenames }
198
+
199
+ # Generate a sample cookbook
200
+ let(:files) { [root_files, segment_files].flatten }
201
+ let(:segment_files) { segments.map { |s| some_filenames.map { |f| "#{s}/#{f}" } } }
202
+
203
+ # Make a hash where { filename => checksum }
204
+ let(:file_checksums) { Hash[*(files.each_with_index.map(&filename_with_checksum).flatten)] }
205
+ let(:filename_with_checksum) { lambda { |f, i| [f, i.to_s(16)] } }
206
+
207
+ let(:cookbook) { double('cookbook').tap { |c| c.should_receive(:manifest).and_return(manifest) } }
208
+ let(:api_url) do
209
+ if Chef::Config[:versioned_cookbooks]
210
+ "cookbooks/?num_versions=all"
211
+ else
212
+ "cookbooks/#{cookbook_dir_name}/_latest"
213
+ end
214
+ end
215
+
216
+ let(:should_get_cookbook) do
217
+ rest.should_receive(:get_rest).with(api_url).once.and_return(cookbook)
218
+ end
219
+
220
+ it 'has correct children' do
221
+ should_get_cookbook
222
+ cookbook_dir.children.map { |child| child.name }.should =~ %w(attributes definitions files libraries providers recipes resources templates a.rb b.txt subdir root_files)
223
+ end
224
+ it 'children and child() yield the exact same objects' do
225
+ should_get_cookbook
226
+ cookbook_dir.children.each { |child| child.should == cookbook_dir.child(child.name) }
227
+ end
228
+ it 'all files exist (recursive) and have correct parent, path, path_for_printing, checksum and type' do
229
+ should_get_cookbook
230
+ file_checksums.each do |path, checksum|
231
+ file = ChefFS::FileSystem.resolve_path(cookbook_dir, path)
232
+ file_parts = path.split('/')
233
+ if file_parts.length == 3
234
+ file.parent.parent.parent.should == cookbook_dir
235
+ elsif file_parts.length == 2
236
+ file.parent.parent.should == cookbook_dir
237
+ else
238
+ file.parent.should == cookbook_dir
239
+ end
240
+ file.exists?.should be_true
241
+ file.dir?.should be_false
242
+ file.name.should == file_parts[-1]
243
+ file.path.should == "/cookbooks/#{cookbook_dir_name}/#{path}"
244
+ file.path_for_printing.should == "remote/cookbooks/#{cookbook_dir_name}/#{path}"
245
+ file.checksum.should == checksum
246
+ end
247
+ end
248
+ it 'all files can be read' do
249
+ should_get_cookbook
250
+ files.each do |path|
251
+ cookbook_file = double(path)
252
+ cookbook_file.should_receive(:open).with(no_args()).once
253
+ cookbook_file.should_receive(:read).with(no_args()).once.and_return("This is #{path}'s content")
254
+ cookbook_file.should_receive(:close!).with(no_args()).once
255
+ rest.should_receive(:get_rest).with("cookbook_file:#{path}", true).once.and_return(cookbook_file)
256
+ rest.should_receive(:sign_on_redirect).with(no_args()).once.and_return(true)
257
+ rest.should_receive(:sign_on_redirect=).with(false).once
258
+ rest.should_receive(:sign_on_redirect=).with(true).once
259
+ file = ChefFS::FileSystem.resolve_path(cookbook_dir, path)
260
+ file.read.should == "This is #{path}'s content"
261
+ end
262
+ end
263
+
264
+ context 'the attributes segment' do
265
+ let(:segment_dir) { cookbook_dir.child('attributes') }
266
+ let(:segment_dir_name) { 'attributes' }
267
+ it_behaves_like 'a segment directory'
268
+
269
+ before(:each) do
270
+ should_get_cookbook
271
+ end
272
+
273
+ it 'can have ruby files' do
274
+ should_get_cookbook
275
+ segment_dir.can_have_child?('blah.rb', false).should be_true
276
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
277
+ end
278
+ it 'cannot have non-ruby files' do
279
+ should_get_cookbook
280
+ segment_dir.can_have_child?('blah.txt', false).should be_false
281
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
282
+ end
283
+ it 'cannot have subdirectories' do
284
+ should_get_cookbook
285
+ segment_dir.can_have_child?('blah', true).should be_false
286
+ end
287
+ end
288
+
289
+ context 'the definitions segment' do
290
+ let(:segment_dir) { cookbook_dir.child('definitions') }
291
+ let(:segment_dir_name) { 'definitions' }
292
+ it_behaves_like 'a segment directory'
293
+
294
+ before(:each) do
295
+ should_get_cookbook
296
+ end
297
+
298
+ it 'can have ruby files' do
299
+ segment_dir.can_have_child?('blah.rb', false).should be_true
300
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
301
+ end
302
+ it 'cannot have non-ruby files' do
303
+ segment_dir.can_have_child?('blah.txt', false).should be_false
304
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
305
+ end
306
+ it 'cannot have subdirectories' do
307
+ segment_dir.can_have_child?('blah', true).should be_false
308
+ end
309
+ end
310
+
311
+ context 'the files segment' do
312
+ let(:segment_dir) { cookbook_dir.child('files') }
313
+ let(:segment_dir_name) { 'files' }
314
+ it_behaves_like 'a segment directory'
315
+
316
+ before(:each) do
317
+ should_get_cookbook
318
+ end
319
+
320
+ it 'can have ruby files' do
321
+ segment_dir.can_have_child?('blah.rb', false).should be_true
322
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
323
+ end
324
+ it 'can have non-ruby files' do
325
+ segment_dir.can_have_child?('blah.txt', false).should be_true
326
+ segment_dir.can_have_child?('.blah.txt', false).should be_true
327
+ end
328
+ it 'can have subdirectories' do
329
+ segment_dir.can_have_child?('blah', true).should be_true
330
+ end
331
+ it 'subdirectories can have ruby files' do
332
+ segment_dir.child('subdir').can_have_child?('blah.rb', false).should be_true
333
+ segment_dir.child('subdir').can_have_child?('.blah.rb', false).should be_true
334
+ end
335
+ it 'subdirectories can have non-ruby files' do
336
+ segment_dir.child('subdir').can_have_child?('blah.txt', false).should be_true
337
+ segment_dir.child('subdir').can_have_child?('.blah.txt', false).should be_true
338
+ end
339
+ it 'subdirectories can have subdirectories' do
340
+ segment_dir.child('subdir').can_have_child?('blah', true).should be_true
341
+ end
342
+ end
343
+
344
+ context 'the libraries segment' do
345
+ let(:segment_dir) { cookbook_dir.child('libraries') }
346
+ let(:segment_dir_name) { 'libraries' }
347
+ it_behaves_like 'a segment directory'
348
+
349
+ before(:each) do
350
+ should_get_cookbook
351
+ end
352
+
353
+ it 'can have ruby files' do
354
+ segment_dir.can_have_child?('blah.rb', false).should be_true
355
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
356
+ end
357
+ it 'cannot have non-ruby files' do
358
+ segment_dir.can_have_child?('blah.txt', false).should be_false
359
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
360
+ end
361
+ it 'cannot have subdirectories' do
362
+ segment_dir.can_have_child?('blah', true).should be_false
363
+ end
364
+ end
365
+
366
+ context 'the providers segment' do
367
+ let(:segment_dir) { cookbook_dir.child('providers') }
368
+ let(:segment_dir_name) { 'providers' }
369
+ it_behaves_like 'a segment directory'
370
+
371
+ before(:each) do
372
+ should_get_cookbook
373
+ end
374
+
375
+ it 'can have ruby files' do
376
+ segment_dir.can_have_child?('blah.rb', false).should be_true
377
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
378
+ end
379
+ it 'cannot have non-ruby files' do
380
+ segment_dir.can_have_child?('blah.txt', false).should be_false
381
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
382
+ end
383
+ it 'can have subdirectories' do
384
+ segment_dir.can_have_child?('blah', true).should be_true
385
+ end
386
+ it 'subdirectories can have ruby files' do
387
+ segment_dir.child('subdir').can_have_child?('blah.rb', false).should be_true
388
+ segment_dir.child('subdir').can_have_child?('.blah.rb', false).should be_true
389
+ end
390
+ it 'subdirectories cannot have non-ruby files' do
391
+ segment_dir.child('subdir').can_have_child?('blah.txt', false).should be_false
392
+ segment_dir.child('subdir').can_have_child?('.blah.txt', false).should be_false
393
+ end
394
+ it 'subdirectories can have subdirectories' do
395
+ segment_dir.child('subdir').can_have_child?('blah', true).should be_true
396
+ end
397
+ end
398
+
399
+ context 'the recipes segment' do
400
+ let(:segment_dir) { cookbook_dir.child('recipes') }
401
+ let(:segment_dir_name) { 'recipes' }
402
+ it_behaves_like 'a segment directory'
403
+
404
+ before(:each) do
405
+ should_get_cookbook
406
+ end
407
+
408
+ it 'can have ruby files' do
409
+ segment_dir.can_have_child?('blah.rb', false).should be_true
410
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
411
+ end
412
+ it 'cannot have non-ruby files' do
413
+ segment_dir.can_have_child?('blah.txt', false).should be_false
414
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
415
+ end
416
+ it 'cannot have subdirectories' do
417
+ segment_dir.can_have_child?('blah', true).should be_false
418
+ end
419
+ end
420
+
421
+ context 'the resources segment' do
422
+ let(:segment_dir) { cookbook_dir.child('resources') }
423
+ let(:segment_dir_name) { 'resources' }
424
+ it_behaves_like 'a segment directory'
425
+
426
+ before(:each) do
427
+ should_get_cookbook
428
+ end
429
+
430
+ it 'can have ruby files' do
431
+ segment_dir.can_have_child?('blah.rb', false).should be_true
432
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
433
+ end
434
+ it 'cannot have non-ruby files' do
435
+ segment_dir.can_have_child?('blah.txt', false).should be_false
436
+ segment_dir.can_have_child?('.blah.txt', false).should be_false
437
+ end
438
+ it 'can have subdirectories' do
439
+ segment_dir.can_have_child?('blah', true).should be_true
440
+ end
441
+ it 'subdirectories can have ruby files' do
442
+ segment_dir.child('subdir').can_have_child?('blah.rb', false).should be_true
443
+ segment_dir.child('subdir').can_have_child?('.blah.rb', false).should be_true
444
+ end
445
+ it 'subdirectories cannot have non-ruby files' do
446
+ segment_dir.child('subdir').can_have_child?('blah.txt', false).should be_false
447
+ segment_dir.child('subdir').can_have_child?('.blah.txt', false).should be_false
448
+ end
449
+ it 'subdirectories can have subdirectories' do
450
+ segment_dir.child('subdir').can_have_child?('blah', true).should be_true
451
+ end
452
+ end
453
+
454
+ context 'the templates segment' do
455
+ let(:segment_dir) { cookbook_dir.child('templates') }
456
+ let(:segment_dir_name) { 'templates' }
457
+ it_behaves_like 'a segment directory'
458
+
459
+ before(:each) do
460
+ should_get_cookbook
461
+ end
462
+
463
+ it 'can have ruby files' do
464
+ segment_dir.can_have_child?('blah.rb', false).should be_true
465
+ segment_dir.can_have_child?('.blah.rb', false).should be_true
466
+ end
467
+ it 'can have non-ruby files' do
468
+ segment_dir.can_have_child?('blah.txt', false).should be_true
469
+ segment_dir.can_have_child?('.blah.txt', false).should be_true
470
+ end
471
+ it 'can have subdirectories' do
472
+ segment_dir.can_have_child?('blah', true).should be_true
473
+ end
474
+ it 'subdirectories can have ruby files' do
475
+ segment_dir.child('subdir').can_have_child?('blah.rb', false).should be_true
476
+ segment_dir.child('subdir').can_have_child?('.blah.rb', false).should be_true
477
+ end
478
+ it 'subdirectories can have non-ruby files' do
479
+ segment_dir.child('subdir').can_have_child?('blah.txt', false).should be_true
480
+ segment_dir.child('subdir').can_have_child?('.blah.txt', false).should be_true
481
+ end
482
+ it 'subdirectories can have subdirectories' do
483
+ segment_dir.child('subdir').can_have_child?('blah', true).should be_true
484
+ end
485
+ end
486
+
487
+ context 'root subdirectories' do
488
+ let(:root_subdir) { cookbook_dir.child('subdir') }
489
+
490
+ before(:each) do
491
+ should_get_cookbook
492
+ end
493
+
494
+ # Really, since these shouldn't exist in the first place,
495
+ # it doesn't matter; but these REALLY shouldn't be able to
496
+ # have any files in them at all.
497
+ it 'can have ruby files' do
498
+ root_subdir.can_have_child?('blah.rb', false).should be_true
499
+ root_subdir.can_have_child?('.blah.rb', false).should be_true
500
+ end
501
+ it 'can have non-ruby files' do
502
+ root_subdir.can_have_child?('blah.txt', false).should be_true
503
+ root_subdir.can_have_child?('.blah.txt', false).should be_true
504
+ end
505
+ it 'cannot have subdirectories' do
506
+ root_subdir.can_have_child?('blah', true).should be_false
507
+ end
508
+ end
509
+ end
510
+ end
511
+
512
+ context 'achild from cookbooks_dir.children' do
513
+ let(:cookbook_dir_name) { 'achild' }
514
+ let(:cookbook_dir) do
515
+ should_list_cookbooks
516
+ cookbooks_dir.children.select { |child| child.name == 'achild' }.first
517
+ end
518
+ it_behaves_like 'a cookbook'
519
+ end
520
+
521
+ context 'cookbooks_dir.child(achild)' do
522
+ let(:cookbook_dir_name) { 'achild' }
523
+ let(:cookbook_dir) { cookbooks_dir.child('achild') }
524
+ it_behaves_like 'a cookbook'
525
+ end
526
+ context 'nonexistent cookbooks_dir.child()' do
527
+ let(:nonexistent_child) { cookbooks_dir.child('blah') }
528
+ it 'has correct parent, name, path and path_for_printing' do
529
+ nonexistent_child.parent.should == cookbooks_dir
530
+ nonexistent_child.name.should == "blah"
531
+ nonexistent_child.path.should == "/cookbooks/blah"
532
+ nonexistent_child.path_for_printing.should == "remote/cookbooks/blah"
533
+ end
534
+ it 'does not exist' do
535
+ should_list_cookbooks
536
+ nonexistent_child.exists?.should be_false
537
+ end
538
+ it 'is a directory' do
539
+ should_list_cookbooks
540
+ nonexistent_child.dir?.should be_false
541
+ end
542
+ it 'read returns NotFoundError' do
543
+ should_list_cookbooks
544
+ expect { nonexistent_child.read }.to raise_error(ChefFS::FileSystem::NotFoundError)
545
+ end
546
+ end
547
+
548
+ describe 'VALID_VERSIONED_COOKBOOK_NAME' do
549
+ subject { valid_versioned_cookbook_name.match(cookbook_name) }
550
+ let(:valid_versioned_cookbook_name) { ChefFS::FileSystem::CookbookDir::VALID_VERSIONED_COOKBOOK_NAME }
551
+
552
+ def self.should_accept(_cookbook_name)
553
+ context "with a cookbook name of '#{_cookbook_name}'" do
554
+ let(:cookbook_name) { _cookbook_name }
555
+ it 'should accept' do
556
+ should_not be_nil
557
+ end
558
+ end
559
+ end
560
+
561
+ def self.should_reject(_cookbook_name)
562
+ context "with a cookbook name of '#{_cookbook_name}'" do
563
+ let(:cookbook_name) { _cookbook_name }
564
+ it 'should reject' do
565
+ should be_nil
566
+ end
567
+ end
568
+ end
569
+
570
+ should_accept 'apt-1.8.4'
571
+ should_accept 'APT-1.8.4'
572
+ should_accept 'apt-100.83.4'
573
+ should_accept 'apt-2.0.0-1.8.4'
574
+ should_accept 'apt---1.8.4'
575
+
576
+ should_reject 'apt'
577
+ should_reject 'apt-1'
578
+ should_reject 'apt-1.2'
579
+ should_reject 'apt-1.2.x'
580
+
581
+ end
582
+ end