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.
- data/lib/chef/knife/raw_essentials.rb +3 -61
- data/lib/chef_fs/data_handler/data_handler_base.rb +2 -1
- data/lib/chef_fs/file_system/base_fs_object.rb +75 -1
- data/lib/chef_fs/file_system/chef_repository_file_system_entry.rb +23 -0
- data/lib/chef_fs/file_system/cookbook_dir.rb +27 -13
- data/lib/chef_fs/file_system/cookbooks_dir.rb +89 -30
- data/lib/chef_fs/file_system/data_bags_dir.rb +5 -1
- data/lib/chef_fs/file_system/nodes_dir.rb +8 -14
- data/lib/chef_fs/file_system/rest_list_dir.rb +6 -2
- data/lib/chef_fs/file_system/rest_list_entry.rb +15 -3
- data/lib/chef_fs/knife.rb +28 -17
- data/lib/chef_fs/version.rb +1 -1
- data/spec/chef_fs/file_system/chef_server_root_dir_spec.rb +26 -12
- data/spec/chef_fs/file_system/cookbook_dir_spec.rb +582 -0
- data/spec/chef_fs/file_system/cookbooks_dir_spec.rb +81 -489
- data/spec/chef_fs/file_system/data_bags_dir_spec.rb +63 -49
- data/spec/integration/deps_spec.rb +32 -32
- data/spec/integration/diff_spec.rb +425 -133
- data/spec/integration/download_spec.rb +743 -211
- data/spec/integration/upload_spec.rb +814 -244
- data/spec/support/integration_helper.rb +33 -6
- data/spec/support/knife_support.rb +4 -2
- metadata +19 -2
@@ -28,20 +28,14 @@ module ChefFS
|
|
28
28
|
super("nodes", parent, nil, ChefFS::DataHandler::NodeDataHandler.new)
|
29
29
|
end
|
30
30
|
|
31
|
-
# Override
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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 ||=
|
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 =
|
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(
|
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
|
data/lib/chef_fs/knife.rb
CHANGED
@@ -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
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
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
|
-
|
82
|
-
|
83
|
-
if
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
data/lib/chef_fs/version.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
85
|
-
|
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
|
-
|
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
|