knife-essentials 1.2 → 1.2.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.
- data/lib/chef/knife/raw_essentials.rb +3 -3
- data/lib/chef/knife/serve_essentials.rb +2 -351
- data/lib/chef_fs/chef_fs_data_store.rb +373 -0
- data/lib/chef_fs/config.rb +193 -0
- data/lib/chef_fs/file_system/chef_repository_file_system_root_dir.rb +17 -0
- data/lib/chef_fs/file_system/chef_server_root_dir.rb +4 -0
- data/lib/chef_fs/file_system/cookbooks_dir.rb +2 -0
- data/lib/chef_fs/knife.rb +23 -151
- data/lib/chef_fs/version.rb +1 -1
- metadata +4 -2
@@ -4,12 +4,12 @@ class Chef
|
|
4
4
|
class Knife
|
5
5
|
remove_const(:Raw) if const_defined?(:Raw) && Raw.name == 'Chef::Knife::Raw' # override Chef's version
|
6
6
|
class Raw < Chef::Knife
|
7
|
-
ChefFS = ::ChefFS
|
8
7
|
banner "knife raw REQUEST_PATH"
|
9
8
|
|
10
9
|
deps do
|
11
10
|
require 'json'
|
12
|
-
require '
|
11
|
+
require 'chef/rest'
|
12
|
+
require 'chef/config'
|
13
13
|
require 'chef_fs/raw_request'
|
14
14
|
end
|
15
15
|
|
@@ -48,7 +48,7 @@ class Chef
|
|
48
48
|
end
|
49
49
|
chef_rest = Chef::REST.new(Chef::Config[:chef_server_url])
|
50
50
|
begin
|
51
|
-
output ChefFS::RawRequest.api_request(chef_rest, config[:method].to_sym, chef_rest.create_url(name_args[0]), {}, data)
|
51
|
+
output ::ChefFS::RawRequest.api_request(chef_rest, config[:method].to_sym, chef_rest.create_url(name_args[0]), {}, data)
|
52
52
|
rescue Timeout::Error => e
|
53
53
|
ui.error "Server timeout"
|
54
54
|
exit 1
|
@@ -19,13 +19,7 @@ ERROR: chef-zero must be installed to run "knife serve"! To install:
|
|
19
19
|
EOM
|
20
20
|
exit(1)
|
21
21
|
end
|
22
|
-
require '
|
23
|
-
require 'chef_fs/file_pattern'
|
24
|
-
require 'chef_fs/file_system'
|
25
|
-
require 'chef_fs/file_system/not_found_error'
|
26
|
-
require 'chef_fs/file_system/memory_root'
|
27
|
-
require 'chef_zero/data_store/data_already_exists_error'
|
28
|
-
require 'chef_zero/data_store/data_not_found_error'
|
22
|
+
require 'chef_fs/chef_fs_data_store'
|
29
23
|
end
|
30
24
|
|
31
25
|
option :remote,
|
@@ -50,7 +44,7 @@ EOM
|
|
50
44
|
|
51
45
|
def run
|
52
46
|
server_options = {}
|
53
|
-
server_options[:data_store] = ChefFSDataStore.new(proc { config[:remote] ? create_chef_fs : create_local_fs })
|
47
|
+
server_options[:data_store] = ChefFS::ChefFSDataStore.new(proc { config[:remote] ? create_chef_fs : create_local_fs })
|
54
48
|
server_options[:log_level] = Chef::Log.level
|
55
49
|
server_options[:host] = config[:host] if config[:host]
|
56
50
|
server_options[:port] = config[:port] ? config[:port].to_i : 4000
|
@@ -58,349 +52,6 @@ EOM
|
|
58
52
|
|
59
53
|
ChefZero::Server.new(server_options).start(:publish => true)
|
60
54
|
end
|
61
|
-
|
62
|
-
class ChefFSDataStore
|
63
|
-
def initialize(chef_fs)
|
64
|
-
@chef_fs = chef_fs
|
65
|
-
@memory_store = ChefZero::DataStore::MemoryStore.new
|
66
|
-
end
|
67
|
-
|
68
|
-
def chef_fs
|
69
|
-
@chef_fs.call
|
70
|
-
end
|
71
|
-
|
72
|
-
MEMORY_PATHS = %w(sandboxes file_store)
|
73
|
-
|
74
|
-
def create_dir(path, name, *options)
|
75
|
-
if use_memory_store?(path)
|
76
|
-
@memory_store.create_dir(path, name, *options)
|
77
|
-
else
|
78
|
-
with_dir(path) do |parent|
|
79
|
-
parent.create_child(chef_fs_filename(path + [name]), nil)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def create(path, name, data, *options)
|
85
|
-
if use_memory_store?(path)
|
86
|
-
@memory_store.create(path, name, data, *options)
|
87
|
-
|
88
|
-
elsif path[0] == 'cookbooks' && path.length == 2
|
89
|
-
# Do nothing. The entry gets created when the cookbook is created.
|
90
|
-
|
91
|
-
else
|
92
|
-
if !data.is_a?(String)
|
93
|
-
raise "set only works with strings"
|
94
|
-
end
|
95
|
-
|
96
|
-
with_dir(path) do |parent|
|
97
|
-
parent.create_child(chef_fs_filename(path + [name]), data)
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def get(path, request=nil)
|
103
|
-
if use_memory_store?(path)
|
104
|
-
@memory_store.get(path)
|
105
|
-
|
106
|
-
elsif path[0] == 'file_store' && path[1] == 'repo'
|
107
|
-
entry = ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join('/'))
|
108
|
-
begin
|
109
|
-
entry.read
|
110
|
-
rescue ChefFS::FileSystem::NotFoundError => e
|
111
|
-
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
112
|
-
end
|
113
|
-
|
114
|
-
else
|
115
|
-
with_entry(path) do |entry|
|
116
|
-
if path[0] == 'cookbooks' && path.length == 3
|
117
|
-
# get /cookbooks/NAME/version
|
118
|
-
result = entry.chef_object.to_hash
|
119
|
-
result.each_pair do |key, value|
|
120
|
-
if value.is_a?(Array)
|
121
|
-
value.each do |file|
|
122
|
-
if file.is_a?(Hash) && file.has_key?('checksum')
|
123
|
-
relative = ['file_store', 'repo', 'cookbooks']
|
124
|
-
if Chef::Config.versioned_cookbooks
|
125
|
-
relative << "#{path[1]}-#{path[2]}"
|
126
|
-
else
|
127
|
-
relative << path[1]
|
128
|
-
end
|
129
|
-
relative = relative + file[:path].split('/')
|
130
|
-
file['url'] = ChefZero::RestBase::build_uri(request.base_uri, relative)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
JSON.pretty_generate(result)
|
136
|
-
|
137
|
-
else
|
138
|
-
entry.read
|
139
|
-
end
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def set(path, data, *options)
|
145
|
-
if use_memory_store?(path)
|
146
|
-
@memory_store.set(path, data, *options)
|
147
|
-
else
|
148
|
-
if !data.is_a?(String)
|
149
|
-
raise "set only works with strings: #{path} = #{data.inspect}"
|
150
|
-
end
|
151
|
-
|
152
|
-
# Write out the files!
|
153
|
-
if path[0] == 'cookbooks' && path.length == 3
|
154
|
-
write_cookbook(path, data, *options)
|
155
|
-
else
|
156
|
-
with_dir(path[0..-2]) do |parent|
|
157
|
-
parent.create_child(chef_fs_filename(path), data)
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
def delete(path)
|
164
|
-
if use_memory_store?(path)
|
165
|
-
@memory_store.delete(path)
|
166
|
-
else
|
167
|
-
with_entry(path) do |entry|
|
168
|
-
if path[0] == 'cookbooks' && path.length >= 3
|
169
|
-
entry.delete(true)
|
170
|
-
else
|
171
|
-
entry.delete
|
172
|
-
end
|
173
|
-
end
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def delete_dir(path, *options)
|
178
|
-
if use_memory_store?(path)
|
179
|
-
@memory_store.delete_dir(path, *options)
|
180
|
-
else
|
181
|
-
with_entry(path) do |entry|
|
182
|
-
entry.delete(options.include?(:recursive))
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
def list(path)
|
188
|
-
if use_memory_store?(path)
|
189
|
-
@memory_store.list(path)
|
190
|
-
|
191
|
-
elsif path[0] == 'cookbooks' && path.length == 1
|
192
|
-
with_entry(path) do |entry|
|
193
|
-
begin
|
194
|
-
if Chef::Config.versioned_cookbooks
|
195
|
-
# /cookbooks/name-version -> /cookbooks/name
|
196
|
-
entry.children.map { |child| split_name_version(child.name)[0] }.uniq
|
197
|
-
else
|
198
|
-
entry.children.map { |child| child.name }
|
199
|
-
end
|
200
|
-
rescue ChefFS::FileSystem::NotFoundError
|
201
|
-
# If the cookbooks dir doesn't exist, we have no cookbooks (not 404)
|
202
|
-
[]
|
203
|
-
end
|
204
|
-
end
|
205
|
-
|
206
|
-
elsif path[0] == 'cookbooks' && path.length == 2
|
207
|
-
if Chef::Config.versioned_cookbooks
|
208
|
-
# list /cookbooks/name = filter /cookbooks/name-version down to name
|
209
|
-
entry.children.map { |child| split_name_version(child.name) }.
|
210
|
-
select { |name, version| name == path[1] }.
|
211
|
-
map { |name, version| version }.to_a
|
212
|
-
else
|
213
|
-
# list /cookbooks/name = <single version>
|
214
|
-
version = get_single_cookbook_version(path)
|
215
|
-
[version]
|
216
|
-
end
|
217
|
-
|
218
|
-
else
|
219
|
-
with_entry(path) do |entry|
|
220
|
-
begin
|
221
|
-
entry.children.map { |c| zero_filename(c) }.sort
|
222
|
-
rescue ChefFS::FileSystem::NotFoundError
|
223
|
-
# /cookbooks, /data, etc. never return 404
|
224
|
-
if path_always_exists?(path)
|
225
|
-
[]
|
226
|
-
else
|
227
|
-
raise
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
def exists?(path)
|
235
|
-
if use_memory_store?(path)
|
236
|
-
@memory_store.exists?(path)
|
237
|
-
else
|
238
|
-
path_always_exists?(path) || ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
|
239
|
-
end
|
240
|
-
end
|
241
|
-
|
242
|
-
def exists_dir?(path)
|
243
|
-
if use_memory_store?(path)
|
244
|
-
@memory_store.exists_dir?(path)
|
245
|
-
elsif path[0] == 'cookbooks' && path.length == 2
|
246
|
-
list([ path[0] ]).include?(path[1])
|
247
|
-
else
|
248
|
-
ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
private
|
253
|
-
|
254
|
-
def use_memory_store?(path)
|
255
|
-
return path[0] == 'sandboxes' || path[0] == 'file_store' && path[1] == 'checksums' || path == [ 'environments', '_default' ]
|
256
|
-
end
|
257
|
-
|
258
|
-
def write_cookbook(path, data, *options)
|
259
|
-
# Create a little ChefFS memory filesystem with the data
|
260
|
-
if Chef::Config.versioned_cookbooks
|
261
|
-
cookbook_path = "cookbooks/#{path[1]}-#{path[2]}"
|
262
|
-
else
|
263
|
-
cookbook_path = "cookbooks/#{path[1]}"
|
264
|
-
end
|
265
|
-
puts "Write cookbook #{cookbook_path}"
|
266
|
-
cookbook_fs = ChefFS::FileSystem::MemoryRoot.new('uploading')
|
267
|
-
cookbook = JSON.parse(data, :create_additions => false)
|
268
|
-
cookbook.each_pair do |key, value|
|
269
|
-
if value.is_a?(Array)
|
270
|
-
value.each do |file|
|
271
|
-
if file.is_a?(Hash) && file.has_key?('checksum')
|
272
|
-
file_data = @memory_store.get(['file_store', 'checksums', file['checksum']])
|
273
|
-
cookbook_fs.add_file("#{cookbook_path}/#{file['path']}", file_data)
|
274
|
-
end
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
# Use the copy/diff algorithm to copy it down so we don't destroy
|
280
|
-
# chefignored data. This is terribly un-thread-safe.
|
281
|
-
ChefFS::FileSystem.copy_to(ChefFS::FilePattern.new("/#{cookbook_path}"), cookbook_fs, chef_fs, nil, {:purge => true})
|
282
|
-
end
|
283
|
-
|
284
|
-
def split_name_version(entry_name)
|
285
|
-
name_version = entry_name.split('-')
|
286
|
-
name = name_version[0..-2].join('-')
|
287
|
-
version = name_version[-1]
|
288
|
-
[name,version]
|
289
|
-
end
|
290
|
-
|
291
|
-
def to_chef_fs_path(path)
|
292
|
-
_to_chef_fs_path(path).join('/')
|
293
|
-
end
|
294
|
-
|
295
|
-
def chef_fs_filename(path)
|
296
|
-
_to_chef_fs_path(path)[-1]
|
297
|
-
end
|
298
|
-
|
299
|
-
def _to_chef_fs_path(path)
|
300
|
-
if path[0] == 'data'
|
301
|
-
path = path.dup
|
302
|
-
path[0] = 'data_bags'
|
303
|
-
if path.length >= 3
|
304
|
-
path[2] = "#{path[2]}.json"
|
305
|
-
end
|
306
|
-
elsif path[0] == 'cookbooks'
|
307
|
-
if path.length == 2
|
308
|
-
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
309
|
-
elsif Chef::Config.versioned_cookbooks
|
310
|
-
if path.length >= 3
|
311
|
-
# cookbooks/name/version -> cookbooks/name-version
|
312
|
-
path = [ path[0], "#{path[1]}-#{path[2]}" ] + path[3..-1]
|
313
|
-
end
|
314
|
-
else
|
315
|
-
if path.length >= 3
|
316
|
-
# cookbooks/name/version/... -> /cookbooks/name/... iff metadata says so
|
317
|
-
version = get_single_cookbook_version(path)
|
318
|
-
if path[2] == version
|
319
|
-
path = path[0..1] + path[3..-1]
|
320
|
-
else
|
321
|
-
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
322
|
-
end
|
323
|
-
end
|
324
|
-
end
|
325
|
-
elsif path.length == 2
|
326
|
-
path = path.dup
|
327
|
-
path[1] = "#{path[1]}.json"
|
328
|
-
end
|
329
|
-
path
|
330
|
-
end
|
331
|
-
|
332
|
-
def to_zero_path(entry)
|
333
|
-
path = entry.path.split('/')[1..-1]
|
334
|
-
if path[0] == 'data_bags'
|
335
|
-
path = path.dup
|
336
|
-
path[0] = 'data'
|
337
|
-
if path.length >= 3
|
338
|
-
path[2] = path[2][0..-6]
|
339
|
-
end
|
340
|
-
|
341
|
-
elsif path[0] == 'cookbooks'
|
342
|
-
if Chef::Config.versioned_cookbooks
|
343
|
-
# cookbooks/name-version/... -> cookbooks/name/version/...
|
344
|
-
if path.length >= 2
|
345
|
-
name, version = split_name_version(path[1])
|
346
|
-
path = [ path[0], name, version ] + path[2..-1]
|
347
|
-
end
|
348
|
-
else
|
349
|
-
if path.length >= 2
|
350
|
-
# cookbooks/name/... -> cookbooks/name/version/...
|
351
|
-
version = get_single_cookbook_version(path)
|
352
|
-
path = path[0..1] + [version] + path[2..-1]
|
353
|
-
end
|
354
|
-
end
|
355
|
-
|
356
|
-
elsif path.length == 2 && path[0] != 'cookbooks'
|
357
|
-
path = path.dup
|
358
|
-
path[1] = path[1][0..-6]
|
359
|
-
end
|
360
|
-
path
|
361
|
-
end
|
362
|
-
|
363
|
-
def zero_filename(entry)
|
364
|
-
to_zero_path(entry)[-1]
|
365
|
-
end
|
366
|
-
|
367
|
-
def path_always_exists?(path)
|
368
|
-
return path.length == 1 && %w(clients cookbooks data environments nodes roles users).include?(path[0])
|
369
|
-
end
|
370
|
-
|
371
|
-
def with_entry(path)
|
372
|
-
begin
|
373
|
-
yield ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path))
|
374
|
-
rescue ChefFS::FileSystem::NotFoundError => e
|
375
|
-
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
376
|
-
end
|
377
|
-
end
|
378
|
-
|
379
|
-
def with_dir(path)
|
380
|
-
begin
|
381
|
-
yield get_dir(_to_chef_fs_path(path), true)
|
382
|
-
rescue ChefFS::FileSystem::NotFoundError => e
|
383
|
-
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
384
|
-
end
|
385
|
-
end
|
386
|
-
|
387
|
-
def get_dir(path, create=false)
|
388
|
-
result = ChefFS::FileSystem.resolve_path(chef_fs, path.join('/'))
|
389
|
-
if result.exists?
|
390
|
-
result
|
391
|
-
elsif create
|
392
|
-
get_dir(path[0..-2], create).create_child(result.name, nil)
|
393
|
-
else
|
394
|
-
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
398
|
-
def get_single_cookbook_version(path)
|
399
|
-
dir = ChefFS::FileSystem.resolve_path(chef_fs, path[0..1].join('/'))
|
400
|
-
metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, [])
|
401
|
-
metadata[:version] || '0.0.0'
|
402
|
-
end
|
403
|
-
end
|
404
55
|
end
|
405
56
|
end
|
406
57
|
end
|
@@ -0,0 +1,373 @@
|
|
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 'chef_zero/data_store/memory_store'
|
20
|
+
require 'chef_zero/data_store/data_already_exists_error'
|
21
|
+
require 'chef_zero/data_store/data_not_found_error'
|
22
|
+
require 'chef_fs/file_pattern'
|
23
|
+
require 'chef_fs/file_system'
|
24
|
+
require 'chef_fs/file_system/not_found_error'
|
25
|
+
require 'chef_fs/file_system/memory_root'
|
26
|
+
|
27
|
+
module ChefFS
|
28
|
+
class ChefFSDataStore
|
29
|
+
def initialize(chef_fs)
|
30
|
+
@chef_fs = chef_fs
|
31
|
+
@memory_store = ChefZero::DataStore::MemoryStore.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def publish_description
|
35
|
+
"Reading and writing data to #{chef_fs.fs_description}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def chef_fs
|
39
|
+
@chef_fs.call
|
40
|
+
end
|
41
|
+
|
42
|
+
MEMORY_PATHS = %w(sandboxes file_store)
|
43
|
+
|
44
|
+
def create_dir(path, name, *options)
|
45
|
+
if use_memory_store?(path)
|
46
|
+
@memory_store.create_dir(path, name, *options)
|
47
|
+
else
|
48
|
+
with_dir(path) do |parent|
|
49
|
+
parent.create_child(chef_fs_filename(path + [name]), nil)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def create(path, name, data, *options)
|
55
|
+
if use_memory_store?(path)
|
56
|
+
@memory_store.create(path, name, data, *options)
|
57
|
+
|
58
|
+
elsif path[0] == 'cookbooks' && path.length == 2
|
59
|
+
# Do nothing. The entry gets created when the cookbook is created.
|
60
|
+
|
61
|
+
else
|
62
|
+
if !data.is_a?(String)
|
63
|
+
raise "set only works with strings"
|
64
|
+
end
|
65
|
+
|
66
|
+
with_dir(path) do |parent|
|
67
|
+
parent.create_child(chef_fs_filename(path + [name]), data)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def get(path, request=nil)
|
73
|
+
if use_memory_store?(path)
|
74
|
+
@memory_store.get(path)
|
75
|
+
|
76
|
+
elsif path[0] == 'file_store' && path[1] == 'repo'
|
77
|
+
entry = ChefFS::FileSystem.resolve_path(chef_fs, path[2..-1].join('/'))
|
78
|
+
begin
|
79
|
+
entry.read
|
80
|
+
rescue ChefFS::FileSystem::NotFoundError => e
|
81
|
+
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
82
|
+
end
|
83
|
+
|
84
|
+
else
|
85
|
+
with_entry(path) do |entry|
|
86
|
+
if path[0] == 'cookbooks' && path.length == 3
|
87
|
+
# get /cookbooks/NAME/version
|
88
|
+
result = entry.chef_object.to_hash
|
89
|
+
result.each_pair do |key, value|
|
90
|
+
if value.is_a?(Array)
|
91
|
+
value.each do |file|
|
92
|
+
if file.is_a?(Hash) && file.has_key?('checksum')
|
93
|
+
relative = ['file_store', 'repo', 'cookbooks']
|
94
|
+
if Chef::Config.versioned_cookbooks
|
95
|
+
relative << "#{path[1]}-#{path[2]}"
|
96
|
+
else
|
97
|
+
relative << path[1]
|
98
|
+
end
|
99
|
+
relative = relative + file[:path].split('/')
|
100
|
+
file['url'] = ChefZero::RestBase::build_uri(request.base_uri, relative)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
JSON.pretty_generate(result)
|
106
|
+
|
107
|
+
else
|
108
|
+
entry.read
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def set(path, data, *options)
|
115
|
+
if use_memory_store?(path)
|
116
|
+
@memory_store.set(path, data, *options)
|
117
|
+
else
|
118
|
+
if !data.is_a?(String)
|
119
|
+
raise "set only works with strings: #{path} = #{data.inspect}"
|
120
|
+
end
|
121
|
+
|
122
|
+
# Write out the files!
|
123
|
+
if path[0] == 'cookbooks' && path.length == 3
|
124
|
+
write_cookbook(path, data, *options)
|
125
|
+
else
|
126
|
+
with_dir(path[0..-2]) do |parent|
|
127
|
+
parent.create_child(chef_fs_filename(path), data)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def delete(path)
|
134
|
+
if use_memory_store?(path)
|
135
|
+
@memory_store.delete(path)
|
136
|
+
else
|
137
|
+
with_entry(path) do |entry|
|
138
|
+
if path[0] == 'cookbooks' && path.length >= 3
|
139
|
+
entry.delete(true)
|
140
|
+
else
|
141
|
+
entry.delete
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def delete_dir(path, *options)
|
148
|
+
if use_memory_store?(path)
|
149
|
+
@memory_store.delete_dir(path, *options)
|
150
|
+
else
|
151
|
+
with_entry(path) do |entry|
|
152
|
+
entry.delete(options.include?(:recursive))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def list(path)
|
158
|
+
if use_memory_store?(path)
|
159
|
+
@memory_store.list(path)
|
160
|
+
|
161
|
+
elsif path[0] == 'cookbooks' && path.length == 1
|
162
|
+
with_entry(path) do |entry|
|
163
|
+
begin
|
164
|
+
if Chef::Config.versioned_cookbooks
|
165
|
+
# /cookbooks/name-version -> /cookbooks/name
|
166
|
+
entry.children.map { |child| split_name_version(child.name)[0] }.uniq
|
167
|
+
else
|
168
|
+
entry.children.map { |child| child.name }
|
169
|
+
end
|
170
|
+
rescue ChefFS::FileSystem::NotFoundError
|
171
|
+
# If the cookbooks dir doesn't exist, we have no cookbooks (not 404)
|
172
|
+
[]
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
elsif path[0] == 'cookbooks' && path.length == 2
|
177
|
+
if Chef::Config.versioned_cookbooks
|
178
|
+
# list /cookbooks/name = filter /cookbooks/name-version down to name
|
179
|
+
entry.children.map { |child| split_name_version(child.name) }.
|
180
|
+
select { |name, version| name == path[1] }.
|
181
|
+
map { |name, version| version }.to_a
|
182
|
+
else
|
183
|
+
# list /cookbooks/name = <single version>
|
184
|
+
version = get_single_cookbook_version(path)
|
185
|
+
[version]
|
186
|
+
end
|
187
|
+
|
188
|
+
else
|
189
|
+
with_entry(path) do |entry|
|
190
|
+
begin
|
191
|
+
entry.children.map { |c| zero_filename(c) }.sort
|
192
|
+
rescue ChefFS::FileSystem::NotFoundError
|
193
|
+
# /cookbooks, /data, etc. never return 404
|
194
|
+
if path_always_exists?(path)
|
195
|
+
[]
|
196
|
+
else
|
197
|
+
raise
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def exists?(path)
|
205
|
+
if use_memory_store?(path)
|
206
|
+
@memory_store.exists?(path)
|
207
|
+
else
|
208
|
+
path_always_exists?(path) || ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
def exists_dir?(path)
|
213
|
+
if use_memory_store?(path)
|
214
|
+
@memory_store.exists_dir?(path)
|
215
|
+
elsif path[0] == 'cookbooks' && path.length == 2
|
216
|
+
list([ path[0] ]).include?(path[1])
|
217
|
+
else
|
218
|
+
ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path)).exists?
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
def use_memory_store?(path)
|
225
|
+
return path[0] == 'sandboxes' || path[0] == 'file_store' && path[1] == 'checksums' || path == [ 'environments', '_default' ]
|
226
|
+
end
|
227
|
+
|
228
|
+
def write_cookbook(path, data, *options)
|
229
|
+
# Create a little ChefFS memory filesystem with the data
|
230
|
+
if Chef::Config.versioned_cookbooks
|
231
|
+
cookbook_path = "cookbooks/#{path[1]}-#{path[2]}"
|
232
|
+
else
|
233
|
+
cookbook_path = "cookbooks/#{path[1]}"
|
234
|
+
end
|
235
|
+
cookbook_fs = ChefFS::FileSystem::MemoryRoot.new('uploading')
|
236
|
+
cookbook = JSON.parse(data, :create_additions => false)
|
237
|
+
cookbook.each_pair do |key, value|
|
238
|
+
if value.is_a?(Array)
|
239
|
+
value.each do |file|
|
240
|
+
if file.is_a?(Hash) && file.has_key?('checksum')
|
241
|
+
file_data = @memory_store.get(['file_store', 'checksums', file['checksum']])
|
242
|
+
cookbook_fs.add_file("#{cookbook_path}/#{file['path']}", file_data)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Use the copy/diff algorithm to copy it down so we don't destroy
|
249
|
+
# chefignored data. This is terribly un-thread-safe.
|
250
|
+
ChefFS::FileSystem.copy_to(ChefFS::FilePattern.new("/#{cookbook_path}"), cookbook_fs, chef_fs, nil, {:purge => true})
|
251
|
+
end
|
252
|
+
|
253
|
+
def split_name_version(entry_name)
|
254
|
+
name_version = entry_name.split('-')
|
255
|
+
name = name_version[0..-2].join('-')
|
256
|
+
version = name_version[-1]
|
257
|
+
[name,version]
|
258
|
+
end
|
259
|
+
|
260
|
+
def to_chef_fs_path(path)
|
261
|
+
_to_chef_fs_path(path).join('/')
|
262
|
+
end
|
263
|
+
|
264
|
+
def chef_fs_filename(path)
|
265
|
+
_to_chef_fs_path(path)[-1]
|
266
|
+
end
|
267
|
+
|
268
|
+
def _to_chef_fs_path(path)
|
269
|
+
if path[0] == 'data'
|
270
|
+
path = path.dup
|
271
|
+
path[0] = 'data_bags'
|
272
|
+
if path.length >= 3
|
273
|
+
path[2] = "#{path[2]}.json"
|
274
|
+
end
|
275
|
+
elsif path[0] == 'cookbooks'
|
276
|
+
if path.length == 2
|
277
|
+
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
278
|
+
elsif Chef::Config.versioned_cookbooks
|
279
|
+
if path.length >= 3
|
280
|
+
# cookbooks/name/version -> cookbooks/name-version
|
281
|
+
path = [ path[0], "#{path[1]}-#{path[2]}" ] + path[3..-1]
|
282
|
+
end
|
283
|
+
else
|
284
|
+
if path.length >= 3
|
285
|
+
# cookbooks/name/version/... -> /cookbooks/name/... iff metadata says so
|
286
|
+
version = get_single_cookbook_version(path)
|
287
|
+
if path[2] == version
|
288
|
+
path = path[0..1] + path[3..-1]
|
289
|
+
else
|
290
|
+
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
elsif path.length == 2
|
295
|
+
path = path.dup
|
296
|
+
path[1] = "#{path[1]}.json"
|
297
|
+
end
|
298
|
+
path
|
299
|
+
end
|
300
|
+
|
301
|
+
def to_zero_path(entry)
|
302
|
+
path = entry.path.split('/')[1..-1]
|
303
|
+
if path[0] == 'data_bags'
|
304
|
+
path = path.dup
|
305
|
+
path[0] = 'data'
|
306
|
+
if path.length >= 3
|
307
|
+
path[2] = path[2][0..-6]
|
308
|
+
end
|
309
|
+
|
310
|
+
elsif path[0] == 'cookbooks'
|
311
|
+
if Chef::Config.versioned_cookbooks
|
312
|
+
# cookbooks/name-version/... -> cookbooks/name/version/...
|
313
|
+
if path.length >= 2
|
314
|
+
name, version = split_name_version(path[1])
|
315
|
+
path = [ path[0], name, version ] + path[2..-1]
|
316
|
+
end
|
317
|
+
else
|
318
|
+
if path.length >= 2
|
319
|
+
# cookbooks/name/... -> cookbooks/name/version/...
|
320
|
+
version = get_single_cookbook_version(path)
|
321
|
+
path = path[0..1] + [version] + path[2..-1]
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
elsif path.length == 2 && path[0] != 'cookbooks'
|
326
|
+
path = path.dup
|
327
|
+
path[1] = path[1][0..-6]
|
328
|
+
end
|
329
|
+
path
|
330
|
+
end
|
331
|
+
|
332
|
+
def zero_filename(entry)
|
333
|
+
to_zero_path(entry)[-1]
|
334
|
+
end
|
335
|
+
|
336
|
+
def path_always_exists?(path)
|
337
|
+
return path.length == 1 && %w(clients cookbooks data environments nodes roles users).include?(path[0])
|
338
|
+
end
|
339
|
+
|
340
|
+
def with_entry(path)
|
341
|
+
begin
|
342
|
+
yield ChefFS::FileSystem.resolve_path(chef_fs, to_chef_fs_path(path))
|
343
|
+
rescue ChefFS::FileSystem::NotFoundError => e
|
344
|
+
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def with_dir(path)
|
349
|
+
begin
|
350
|
+
yield get_dir(_to_chef_fs_path(path), true)
|
351
|
+
rescue ChefFS::FileSystem::NotFoundError => e
|
352
|
+
raise ChefZero::DataStore::DataNotFoundError.new(to_zero_path(e.entry), e)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def get_dir(path, create=false)
|
357
|
+
result = ChefFS::FileSystem.resolve_path(chef_fs, path.join('/'))
|
358
|
+
if result.exists?
|
359
|
+
result
|
360
|
+
elsif create
|
361
|
+
get_dir(path[0..-2], create).create_child(result.name, nil)
|
362
|
+
else
|
363
|
+
raise ChefZero::DataStore::DataNotFoundError.new(path)
|
364
|
+
end
|
365
|
+
end
|
366
|
+
|
367
|
+
def get_single_cookbook_version(path)
|
368
|
+
dir = ChefFS::FileSystem.resolve_path(chef_fs, path[0..1].join('/'))
|
369
|
+
metadata = ChefZero::CookbookData.metadata_from(dir, path[1], nil, [])
|
370
|
+
metadata[:version] || '0.0.0'
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
@@ -0,0 +1,193 @@
|
|
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 'chef/log'
|
20
|
+
require 'chef_fs/path_utils'
|
21
|
+
|
22
|
+
module ChefFS
|
23
|
+
#
|
24
|
+
# Helpers to take Chef::Config and create chef_fs and local_fs from it
|
25
|
+
#
|
26
|
+
class Config
|
27
|
+
def initialize(chef_config = Chef::Config, cwd = Dir.pwd)
|
28
|
+
@chef_config = chef_config
|
29
|
+
@cwd = cwd
|
30
|
+
configure_repo_paths
|
31
|
+
end
|
32
|
+
|
33
|
+
PATH_VARIABLES = %w(acl_path client_path cookbook_path container_path data_bag_path environment_path group_path node_path role_path user_path)
|
34
|
+
|
35
|
+
def chef_fs
|
36
|
+
@chef_fs ||= create_chef_fs
|
37
|
+
end
|
38
|
+
|
39
|
+
def create_chef_fs
|
40
|
+
require 'chef_fs/file_system/chef_server_root_dir'
|
41
|
+
ChefFS::FileSystem::ChefServerRootDir.new("remote", @chef_config)
|
42
|
+
end
|
43
|
+
|
44
|
+
def local_fs
|
45
|
+
@local_fs ||= create_local_fs
|
46
|
+
end
|
47
|
+
|
48
|
+
def create_local_fs
|
49
|
+
require 'chef_fs/file_system/chef_repository_file_system_root_dir'
|
50
|
+
ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the given real path's location relative to the server root.
|
54
|
+
#
|
55
|
+
# If chef_repo is /home/jkeiser/chef_repo,
|
56
|
+
# and pwd is /home/jkeiser/chef_repo/cookbooks,
|
57
|
+
# server_path('blah') == '/cookbooks/blah'
|
58
|
+
# server_path('../roles/blah.json') == '/roles/blah'
|
59
|
+
# server_path('../../readme.txt') == nil
|
60
|
+
# server_path('*/*ab*') == '/cookbooks/*/*ab*'
|
61
|
+
# server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
|
62
|
+
# server_path('/home/*/chef_repo/cookbooks/blah') == nil
|
63
|
+
#
|
64
|
+
# If there are multiple paths (cookbooks, roles, data bags, etc. can all
|
65
|
+
# have separate paths), and cwd+the path reaches into one of them, we will
|
66
|
+
# return a path relative to that. Otherwise we will return a path to
|
67
|
+
# chef_repo.
|
68
|
+
#
|
69
|
+
# Globs are allowed as well, but globs outside server paths are NOT
|
70
|
+
# (presently) supported. See above examples. TODO support that.
|
71
|
+
#
|
72
|
+
# If the path does not reach into ANY specified directory, nil is returned.
|
73
|
+
def server_path(file_path)
|
74
|
+
pwd = File.expand_path(Dir.pwd)
|
75
|
+
absolute_path = ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
|
76
|
+
|
77
|
+
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
|
78
|
+
object_paths.each_pair do |name, paths|
|
79
|
+
paths.each do |path|
|
80
|
+
realest_path = ChefFS::PathUtils.realest_path(path)
|
81
|
+
if absolute_path[0,realest_path.length] == realest_path &&
|
82
|
+
(absolute_path.length == realest_path.length ||
|
83
|
+
absolute_path[realest_path.length,1] =~ /#{PathUtils.regexp_path_separator}/)
|
84
|
+
relative_path = ChefFS::PathUtils::relative_to(absolute_path, realest_path)
|
85
|
+
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Check chef_repo_path
|
91
|
+
Array(@chef_config[:chef_repo_path]).flatten.each do |chef_repo_path|
|
92
|
+
realest_chef_repo_path = ChefFS::PathUtils.realest_path(chef_repo_path)
|
93
|
+
if absolute_path == realest_chef_repo_path
|
94
|
+
return '/'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
101
|
+
# The current directory, relative to server root
|
102
|
+
def base_path
|
103
|
+
@base_path ||= server_path(File.expand_path(@cwd))
|
104
|
+
end
|
105
|
+
|
106
|
+
# Print the given server path, relative to the current directory
|
107
|
+
def format_path(entry)
|
108
|
+
server_path = entry.path
|
109
|
+
if base_path && server_path[0,base_path.length] == base_path
|
110
|
+
if server_path == base_path
|
111
|
+
return "."
|
112
|
+
elsif server_path[base_path.length,1] == "/"
|
113
|
+
return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
|
114
|
+
elsif base_path == "/" && server_path[0,1] == "/"
|
115
|
+
return server_path[1, server_path.length - 1]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
server_path
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def object_paths
|
124
|
+
@object_paths ||= begin
|
125
|
+
if !@chef_config[:chef_repo_path]
|
126
|
+
Chef::Log.error("Must specify either chef_repo_path or cookbook_path in Chef config file")
|
127
|
+
exit(1)
|
128
|
+
end
|
129
|
+
|
130
|
+
result = {}
|
131
|
+
case @chef_config[:repo_mode]
|
132
|
+
when 'static'
|
133
|
+
object_names = %w(cookbooks data_bags environments roles)
|
134
|
+
when 'hosted_everything'
|
135
|
+
object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
|
136
|
+
else
|
137
|
+
object_names = %w(clients cookbooks data_bags environments nodes roles users)
|
138
|
+
end
|
139
|
+
object_names.each do |object_name|
|
140
|
+
variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
|
141
|
+
paths = Array(@chef_config[variable_name]).flatten
|
142
|
+
result[object_name] = paths.map { |path| File.expand_path(path) }
|
143
|
+
end
|
144
|
+
result
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def configure_repo_paths
|
149
|
+
# Smooth out some (for now) inappropriate defaults set by Chef
|
150
|
+
if @chef_config[:cookbook_path] == [ @chef_config.platform_specific_path("/var/chef/cookbooks"),
|
151
|
+
@chef_config.platform_specific_path("/var/chef/site-cookbooks") ]
|
152
|
+
@chef_config[:cookbook_path] = nil
|
153
|
+
end
|
154
|
+
if @chef_config[:data_bag_path] == @chef_config.platform_specific_path('/var/chef/data_bags')
|
155
|
+
@chef_config[:data_bag_path] = nil
|
156
|
+
end
|
157
|
+
if @chef_config[:node_path] == '/var/chef/node'
|
158
|
+
@chef_config[:node_path] = nil
|
159
|
+
end
|
160
|
+
if @chef_config[:role_path] == @chef_config.platform_specific_path('/var/chef/roles')
|
161
|
+
@chef_config[:role_path] = nil
|
162
|
+
end
|
163
|
+
|
164
|
+
# Infer chef_repo_path from cookbook_path if not speciifed
|
165
|
+
if !@chef_config[:chef_repo_path]
|
166
|
+
if @chef_config[:cookbook_path]
|
167
|
+
@chef_config[:chef_repo_path] = Array(@chef_config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Default to getting *everything* from the server.
|
172
|
+
if !@chef_config[:repo_mode]
|
173
|
+
if @chef_config[:chef_server_url] =~ /\/+organizations\/.+/
|
174
|
+
@chef_config[:repo_mode] = 'hosted_everything'
|
175
|
+
else
|
176
|
+
@chef_config[:repo_mode] = 'everything'
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# Infer any *_path variables that are not specified
|
181
|
+
if @chef_config[:chef_repo_path]
|
182
|
+
PATH_VARIABLES.each do |variable_name|
|
183
|
+
chef_repo_paths = Array(@chef_config[:chef_repo_path]).flatten
|
184
|
+
variable = variable_name.to_sym
|
185
|
+
if !@chef_config[variable]
|
186
|
+
# cookbook_path -> cookbooks
|
187
|
+
@chef_config[variable] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
@@ -67,6 +67,23 @@ module ChefFS
|
|
67
67
|
nil
|
68
68
|
end
|
69
69
|
|
70
|
+
# Used to print out the filesystem
|
71
|
+
def fs_description
|
72
|
+
repo_path = File.dirname(child_paths['cookbooks'][0])
|
73
|
+
result = "repository at #{repo_path}\n"
|
74
|
+
if Chef::Config[:versioned_cookbooks]
|
75
|
+
result << " Multiple versions per cookbook\n"
|
76
|
+
else
|
77
|
+
result << " One version per cookbook\n"
|
78
|
+
end
|
79
|
+
child_paths.each_pair do |name, paths|
|
80
|
+
if paths.any? { |path| File.dirname(path) != repo_path }
|
81
|
+
result << " #{name} at #{paths.join(', ')}\n"
|
82
|
+
end
|
83
|
+
end
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
70
87
|
private
|
71
88
|
|
72
89
|
def make_child_entry(name)
|
@@ -49,6 +49,10 @@ module ChefFS
|
|
49
49
|
attr_reader :environment
|
50
50
|
attr_reader :repo_mode
|
51
51
|
|
52
|
+
def fs_description
|
53
|
+
"Chef server at #{chef_server_url} (user #{chef_username}), repo_mode = #{repo_mode}"
|
54
|
+
end
|
55
|
+
|
52
56
|
def rest
|
53
57
|
Chef::REST.new(chef_server_url, chef_username, chef_private_key)
|
54
58
|
end
|
@@ -75,6 +75,8 @@ module ChefFS
|
|
75
75
|
else
|
76
76
|
raise ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "HTTP error writing: #{e}"
|
77
77
|
end
|
78
|
+
rescue Chef::Exceptions::CookbookFrozen => e
|
79
|
+
raise ChefFS::FileSystem::CookbookFrozenError.new(:write, self, e), "Cookbook #{other.name} is frozen"
|
78
80
|
end
|
79
81
|
|
80
82
|
# Knife currently does not understand versioned cookbooks
|
data/lib/chef_fs/knife.rb
CHANGED
@@ -23,14 +23,20 @@ module ChefFS
|
|
23
23
|
# Workaround for CHEF-3932
|
24
24
|
def self.deps
|
25
25
|
super do
|
26
|
-
require '
|
27
|
-
require 'chef_fs/
|
26
|
+
require 'chef/config'
|
27
|
+
require 'chef_fs/parallelizer'
|
28
|
+
require 'chef_fs/config'
|
28
29
|
require 'chef_fs/file_pattern'
|
29
30
|
require 'chef_fs/path_utils'
|
30
|
-
require 'chef_fs/parallelizer'
|
31
|
-
require 'chef/config'
|
32
31
|
yield
|
33
32
|
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.inherited(c)
|
36
|
+
super
|
37
|
+
# Ensure we always get to do our includes, whether subclass calls deps or not
|
38
|
+
c.deps do
|
39
|
+
end
|
34
40
|
|
35
41
|
option :repo_mode,
|
36
42
|
:long => '--repo-mode MODE',
|
@@ -45,176 +51,38 @@ module ChefFS
|
|
45
51
|
:description => 'Maximum number of simultaneous requests to send (default: 10)'
|
46
52
|
end
|
47
53
|
|
48
|
-
def self.inherited(c)
|
49
|
-
super
|
50
|
-
# Ensure we always get to do our includes, whether subclass calls deps or not
|
51
|
-
c.deps do
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
54
|
def configure_chef
|
56
55
|
super
|
57
56
|
Chef::Config[:repo_mode] = config[:repo_mode] if config[:repo_mode]
|
58
57
|
Chef::Config[:concurrency] = config[:concurrency].to_i if config[:concurrency]
|
59
58
|
|
60
59
|
# --chef-repo-path overrides all other paths
|
61
|
-
path_variables = %w(acl_path client_path cookbook_path container_path data_bag_path environment_path group_path node_path role_path user_path)
|
62
60
|
if config[:chef_repo_path]
|
63
61
|
Chef::Config[:chef_repo_path] = config[:chef_repo_path]
|
64
|
-
|
65
|
-
Chef::Config[variable_name.to_sym] =
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Infer chef_repo_path from cookbook_path if not speciifed
|
70
|
-
if !Chef::Config[:chef_repo_path]
|
71
|
-
if Chef::Config[:cookbook_path]
|
72
|
-
Chef::Config[:chef_repo_path] = Array(Chef::Config[:cookbook_path]).flatten.map { |path| File.expand_path('..', path) }
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Smooth out some (for now) inappropriate defaults set by Chef
|
77
|
-
if Chef::Config[:data_bag_path] == Chef::Config.platform_specific_path('/var/chef/data_bags')
|
78
|
-
Chef::Config[:data_bag_path] = nil
|
79
|
-
end
|
80
|
-
if Chef::Config[:node_path] == '/var/chef/node'
|
81
|
-
Chef::Config[:node_path] = nil
|
82
|
-
end
|
83
|
-
if Chef::Config[:role_path] == Chef::Config.platform_specific_path('/var/chef/roles')
|
84
|
-
Chef::Config[:role_path] = nil
|
85
|
-
end
|
86
|
-
|
87
|
-
# Default to getting *everything* from the server.
|
88
|
-
if !Chef::Config[:repo_mode]
|
89
|
-
if Chef::Config[:chef_server_url] =~ /\/+organizations\/.+/
|
90
|
-
Chef::Config[:repo_mode] = 'hosted_everything'
|
91
|
-
else
|
92
|
-
Chef::Config[:repo_mode] = 'everything'
|
62
|
+
ChefFS::Config::PATH_VARIABLES.each do |variable_name|
|
63
|
+
Chef::Config[variable_name.to_sym] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
|
93
64
|
end
|
94
65
|
end
|
95
66
|
|
96
|
-
|
97
|
-
if Chef::Config[:chef_repo_path]
|
98
|
-
path_variables.each do |variable_name|
|
99
|
-
chef_repo_paths = Array(Chef::Config[:chef_repo_path]).flatten
|
100
|
-
variable = variable_name.to_sym
|
101
|
-
if !Chef::Config[variable]
|
102
|
-
# cookbook_path -> cookbooks
|
103
|
-
Chef::Config[variable] = chef_repo_paths.map { |path| File.join(path, "#{variable_name[0..-6]}s") }
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
67
|
+
@chef_fs_config = ChefFS::Config.new(Chef::Config)
|
107
68
|
|
108
69
|
ChefFS::Parallelizer.threads = (Chef::Config[:concurrency] || 10) - 1
|
109
70
|
end
|
110
71
|
|
111
72
|
def chef_fs
|
112
|
-
@chef_fs
|
73
|
+
@chef_fs_config.chef_fs
|
113
74
|
end
|
114
75
|
|
115
76
|
def create_chef_fs
|
116
|
-
|
117
|
-
end
|
118
|
-
|
119
|
-
def object_paths
|
120
|
-
@object_paths ||= begin
|
121
|
-
if !Chef::Config[:chef_repo_path]
|
122
|
-
Chef::Log.error("Must specify either chef_repo_path or cookbook_path in Chef config file")
|
123
|
-
exit(1)
|
124
|
-
end
|
125
|
-
|
126
|
-
result = {}
|
127
|
-
case Chef::Config[:repo_mode]
|
128
|
-
when 'static'
|
129
|
-
object_names = %w(cookbooks data_bags environments roles)
|
130
|
-
when 'hosted_everything'
|
131
|
-
object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
|
132
|
-
else
|
133
|
-
object_names = %w(clients cookbooks data_bags environments nodes roles users)
|
134
|
-
end
|
135
|
-
object_names.each do |object_name|
|
136
|
-
variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
|
137
|
-
paths = Array(Chef::Config[variable_name]).flatten
|
138
|
-
result[object_name] = paths.map { |path| File.expand_path(path) }
|
139
|
-
end
|
140
|
-
result
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Returns the given real path's location relative to the server root.
|
145
|
-
#
|
146
|
-
# If chef_repo is /home/jkeiser/chef_repo,
|
147
|
-
# and pwd is /home/jkeiser/chef_repo/cookbooks,
|
148
|
-
# server_path('blah') == '/cookbooks/blah'
|
149
|
-
# server_path('../roles/blah.json') == '/roles/blah'
|
150
|
-
# server_path('../../readme.txt') == nil
|
151
|
-
# server_path('*/*ab*') == '/cookbooks/*/*ab*'
|
152
|
-
# server_path('/home/jkeiser/chef_repo/cookbooks/blah') == '/cookbooks/blah'
|
153
|
-
# server_path('/home/*/chef_repo/cookbooks/blah') == nil
|
154
|
-
#
|
155
|
-
# If there are multiple paths (cookbooks, roles, data bags, etc. can all
|
156
|
-
# have separate paths), and cwd+the path reaches into one of them, we will
|
157
|
-
# return a path relative to that. Otherwise we will return a path to
|
158
|
-
# chef_repo.
|
159
|
-
#
|
160
|
-
# Globs are allowed as well, but globs outside server paths are NOT
|
161
|
-
# (presently) supported. See above examples. TODO support that.
|
162
|
-
#
|
163
|
-
# If the path does not reach into ANY specified directory, nil is returned.
|
164
|
-
def server_path(file_path)
|
165
|
-
pwd = File.expand_path(Dir.pwd)
|
166
|
-
absolute_path = ChefFS::PathUtils.realest_path(File.expand_path(file_path, pwd))
|
167
|
-
|
168
|
-
# Check all object paths (cookbooks_dir, data_bags_dir, etc.)
|
169
|
-
object_paths.each_pair do |name, paths|
|
170
|
-
paths.each do |path|
|
171
|
-
realest_path = ChefFS::PathUtils.realest_path(path)
|
172
|
-
if absolute_path[0,realest_path.length] == realest_path &&
|
173
|
-
(absolute_path.length == realest_path.length ||
|
174
|
-
absolute_path[realest_path.length,1] =~ /#{PathUtils.regexp_path_separator}/)
|
175
|
-
relative_path = ChefFS::PathUtils::relative_to(absolute_path, realest_path)
|
176
|
-
return relative_path == '.' ? "/#{name}" : "/#{name}/#{relative_path}"
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
# Check chef_repo_path
|
182
|
-
Array(Chef::Config[:chef_repo_path]).flatten.each do |chef_repo_path|
|
183
|
-
realest_chef_repo_path = ChefFS::PathUtils.realest_path(chef_repo_path)
|
184
|
-
if absolute_path == realest_chef_repo_path
|
185
|
-
return '/'
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
nil
|
190
|
-
end
|
191
|
-
|
192
|
-
# The current directory, relative to server root
|
193
|
-
def base_path
|
194
|
-
@base_path ||= server_path(File.expand_path(Dir.pwd))
|
195
|
-
end
|
196
|
-
|
197
|
-
# Print the given server path, relative to the current directory
|
198
|
-
def format_path(entry)
|
199
|
-
server_path = entry.path
|
200
|
-
if base_path && server_path[0,base_path.length] == base_path
|
201
|
-
if server_path == base_path
|
202
|
-
return "."
|
203
|
-
elsif server_path[base_path.length,1] == "/"
|
204
|
-
return server_path[base_path.length + 1, server_path.length - base_path.length - 1]
|
205
|
-
elsif base_path == "/" && server_path[0,1] == "/"
|
206
|
-
return server_path[1, server_path.length - 1]
|
207
|
-
end
|
208
|
-
end
|
209
|
-
server_path
|
77
|
+
@chef_fs_config.create_chef_fs
|
210
78
|
end
|
211
79
|
|
212
80
|
def local_fs
|
213
|
-
@local_fs
|
81
|
+
@chef_fs_config.local_fs
|
214
82
|
end
|
215
83
|
|
216
84
|
def create_local_fs
|
217
|
-
|
85
|
+
@chef_fs_config.create_local_fs
|
218
86
|
end
|
219
87
|
|
220
88
|
def pattern_args
|
@@ -225,14 +93,18 @@ module ChefFS
|
|
225
93
|
# TODO support absolute file paths and not just patterns? Too much?
|
226
94
|
# Could be super useful in a world with multiple repo paths
|
227
95
|
args.map do |arg|
|
228
|
-
if
|
96
|
+
if !@chef_fs_config.base_path && !ChefFS::PathUtils.is_absolute?(arg)
|
229
97
|
ui.error("Attempt to use relative path '#{arg}' when current directory is outside the repository path")
|
230
98
|
exit(1)
|
231
99
|
end
|
232
|
-
ChefFS::FilePattern
|
100
|
+
ChefFS::FilePattern.relative_to(@chef_fs_config.base_path, arg)
|
233
101
|
end
|
234
102
|
end
|
235
103
|
|
104
|
+
def format_path(entry)
|
105
|
+
@chef_fs_config.format_path(entry)
|
106
|
+
end
|
107
|
+
|
236
108
|
def parallelize(inputs, options = {}, &block)
|
237
109
|
ChefFS::Parallelizer.parallelize(inputs, options, &block)
|
238
110
|
end
|
data/lib/chef_fs/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: knife-essentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-06-
|
12
|
+
date: 2013-06-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: chef
|
@@ -129,7 +129,9 @@ files:
|
|
129
129
|
- lib/chef/knife/show_essentials.rb
|
130
130
|
- lib/chef/knife/upload_essentials.rb
|
131
131
|
- lib/chef/knife/xargs_essentials.rb
|
132
|
+
- lib/chef_fs/chef_fs_data_store.rb
|
132
133
|
- lib/chef_fs/command_line.rb
|
134
|
+
- lib/chef_fs/config.rb
|
133
135
|
- lib/chef_fs/data_handler/acl_data_handler.rb
|
134
136
|
- lib/chef_fs/data_handler/client_data_handler.rb
|
135
137
|
- lib/chef_fs/data_handler/container_data_handler.rb
|