knife-essentials 0.9.8 → 1.0.0.beta1

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.
@@ -38,7 +38,7 @@ class Chef
38
38
  error = false
39
39
  if config[:remote_only]
40
40
  pattern_args.each do |pattern|
41
- ChefFS::FileSystem.list(chef_fs, pattern) do |result|
41
+ ChefFS::FileSystem.list(chef_fs, pattern).each do |result|
42
42
  if delete_result(result)
43
43
  error = true
44
44
  end
@@ -46,7 +46,7 @@ class Chef
46
46
  end
47
47
  elsif config[:local_only]
48
48
  pattern_args.each do |pattern|
49
- ChefFS::FileSystem.list(local_fs, pattern) do |result|
49
+ ChefFS::FileSystem.list(local_fs, pattern).each do |result|
50
50
  if delete_result(result)
51
51
  error = true
52
52
  end
@@ -54,7 +54,7 @@ class Chef
54
54
  end
55
55
  else
56
56
  pattern_args.each do |pattern|
57
- ChefFS::FileSystem.list_pairs(pattern, chef_fs, local_fs) do |chef_result, local_result|
57
+ ChefFS::FileSystem.list_pairs(pattern, chef_fs, local_fs).each do |chef_result, local_result|
58
58
  if delete_result(chef_result, local_result)
59
59
  error = true
60
60
  end
@@ -36,7 +36,7 @@ class Chef
36
36
  @root = config[:remote] ? chef_fs : local_fs
37
37
  dependencies = {}
38
38
  pattern_args.each do |pattern|
39
- ChefFS::FileSystem.list(@root, pattern) do |entry|
39
+ ChefFS::FileSystem.list(@root, pattern).each do |entry|
40
40
  if config[:tree]
41
41
  print_dependencies_tree(entry, dependencies)
42
42
  else
@@ -20,7 +20,7 @@ class Chef
20
20
  # Get the matches (recursively)
21
21
  error = false
22
22
  pattern_args.each do |pattern|
23
- ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
23
+ ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result|
24
24
  if result.dir?
25
25
  ui.error "#{format_path(result)}: is a directory" if pattern.exact_path
26
26
  error = true
@@ -43,21 +43,28 @@ class Chef
43
43
  patterns = name_args.length == 0 ? [""] : name_args
44
44
 
45
45
  # Get the matches (recursively)
46
- results = []
47
- dir_results = []
48
- pattern_args_from(patterns).each do |pattern|
49
- ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
50
- if result.dir? && !config[:bare_directories]
51
- dir_results += add_dir_result(result)
52
- elsif result.exists?
53
- results << result
54
- elsif pattern.exact_path
55
- ui.error "#{format_path(result)}: No such file or directory"
56
- self.exit_code = 1
57
- end
46
+ all_results = parallelize(pattern_args_from(patterns), :flatten => true) do |pattern|
47
+ pattern_results = ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern)
48
+ if pattern_results.first && !pattern_results.first.exists? && pattern.exact_path
49
+ ui.error "#{format_path(pattern_results.first)}: No such file or directory"
50
+ self.exit_code = 1
58
51
  end
52
+ pattern_results
59
53
  end
60
54
 
55
+ # Process directories
56
+ if !config[:bare_directories]
57
+ dir_results = parallelize(all_results.select { |result| result.dir? }, :flatten => true) do |result|
58
+ add_dir_result(result)
59
+ end.to_a
60
+ else
61
+ dir_results = []
62
+ end
63
+
64
+ # Process all other results
65
+ results = all_results.select { |result| result.exists? && (!result.dir? || config[:bare_directories]) }.to_a
66
+
67
+ # Flatten out directory results if necessary
61
68
  if config[:flat]
62
69
  dir_results.each do |result, children|
63
70
  results += children
@@ -65,9 +72,11 @@ class Chef
65
72
  dir_results = []
66
73
  end
67
74
 
75
+ # Sort by path for happy output
68
76
  results = results.sort_by { |result| result.path }
69
77
  dir_results = dir_results.sort_by { |result| result[0].path }
70
78
 
79
+ # Print!
71
80
  if results.length == 0 && dir_results.length == 1
72
81
  results = dir_results[0][1]
73
82
  dir_results = []
@@ -98,11 +107,8 @@ class Chef
98
107
 
99
108
  result = [ [ result, children ] ]
100
109
  if config[:recursive]
101
- children.each do |child|
102
- if child.dir?
103
- result += add_dir_result(child)
104
- end
105
- end
110
+ child_dirs = children.select { |child| child.dir? }
111
+ result += parallelize(child_dirs, :flatten => true) { |child| add_dir_result(child) }.to_a
106
112
  end
107
113
  result
108
114
  end
@@ -19,26 +19,33 @@ class Chef
19
19
  def run
20
20
  # Get the matches (recursively)
21
21
  error = false
22
- pattern_args.each do |pattern|
23
- ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
24
- if result.dir?
25
- ui.error "#{format_path(result)}: is a directory" if pattern.exact_path
22
+ entry_values = parallelize(pattern_args, :flatten => true) do |pattern|
23
+ parallelize(ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern)) do |entry|
24
+ if entry.dir?
25
+ ui.error "#{format_path(entry)}: is a directory" if pattern.exact_path
26
26
  error = true
27
+ nil
27
28
  else
28
29
  begin
29
- value = result.read
30
- output "#{format_path(result)}:"
31
- output(format_for_display(value))
30
+ [entry, entry.read]
32
31
  rescue ChefFS::FileSystem::OperationNotAllowedError => e
33
32
  ui.error "#{format_path(e.entry)}: #{e.reason}."
34
33
  error = true
34
+ nil
35
35
  rescue ChefFS::FileSystem::NotFoundError => e
36
36
  ui.error "#{format_path(e.entry)}: No such file or directory"
37
37
  error = true
38
+ nil
38
39
  end
39
40
  end
40
41
  end
41
42
  end
43
+ entry_values.each do |entry, value|
44
+ if entry
45
+ output "#{format_path(entry)}:"
46
+ output(format_for_display(value))
47
+ end
48
+ end
42
49
  if error
43
50
  exit 1
44
51
  end
@@ -74,7 +74,7 @@ class Chef
74
74
  # Get the matches (recursively)
75
75
  files = []
76
76
  pattern_args_from(get_patterns).each do |pattern|
77
- ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
77
+ ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern).each do |result|
78
78
  if result.dir?
79
79
  # TODO option to include directories
80
80
  ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it."
@@ -30,7 +30,7 @@ module ChefFS
30
30
 
31
31
  get_content = (output_mode != :name_only && output_mode != :name_status)
32
32
  found_match = false
33
- diff(pattern, a_root, b_root, recurse_depth, get_content) do |type, old_entry, new_entry, old_value, new_value, error|
33
+ diff(pattern, a_root, b_root, recurse_depth, get_content).each do |type, old_entry, new_entry, old_value, new_value, error|
34
34
  found_match = true unless type == :both_nonexistent
35
35
  old_path = format_path.call(old_entry)
36
36
  new_path = format_path.call(new_entry)
@@ -124,112 +124,128 @@ module ChefFS
124
124
  error
125
125
  end
126
126
 
127
- def self.diff(pattern, a_root, b_root, recurse_depth, get_content)
128
- ChefFS::FileSystem.list_pairs(pattern, a_root, b_root) do |a, b|
129
- diff_entries(a, b, recurse_depth, get_content) do |diff|
130
- yield diff
131
- end
127
+ def self.diff(pattern, old_root, new_root, recurse_depth, get_content)
128
+ ChefFS::Parallelizer.parallelize(ChefFS::FileSystem.list_pairs(pattern, old_root, new_root), :flatten => true) do |old_entry, new_entry|
129
+ diff_entries(old_entry, new_entry, recurse_depth, get_content)
132
130
  end
133
131
  end
134
132
 
135
133
  # Diff two known entries (could be files or dirs)
136
134
  def self.diff_entries(old_entry, new_entry, recurse_depth, get_content)
137
- begin
138
- # If both are directories
139
- if old_entry.dir?
140
- if new_entry.dir?
141
- if recurse_depth == 0
142
- yield [ :common_subdirectories, old_entry, new_entry ]
143
- else
144
- ChefFS::FileSystem.child_pairs(old_entry, new_entry).each do |old_child,new_child|
145
- diff_entries(old_child, new_child,
146
- recurse_depth ? recurse_depth - 1 : nil, get_content) do |diff|
147
- yield diff
148
- end
149
- end
135
+ # If both are directories
136
+ if old_entry.dir?
137
+ if new_entry.dir?
138
+ if recurse_depth == 0
139
+ return [ [ :common_subdirectories, old_entry, new_entry ] ]
140
+ else
141
+ return ChefFS::Parallelizer.parallelize(ChefFS::FileSystem.child_pairs(old_entry, new_entry), :flatten => true) do |old_child, new_child|
142
+ ChefFS::CommandLine.diff_entries(old_child, new_child, recurse_depth ? recurse_depth - 1 : nil, get_content)
150
143
  end
144
+ end
151
145
 
152
146
  # If old is a directory and new is a file
153
- elsif new_entry.exists?
154
- yield [ :directory_to_file, old_entry, new_entry ]
147
+ elsif new_entry.exists?
148
+ return [ [ :directory_to_file, old_entry, new_entry ] ]
155
149
 
156
150
  # If old is a directory and new does not exist
157
- elsif new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
158
- yield [ :deleted, old_entry, new_entry ]
159
- end
151
+ elsif new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
152
+ return [ [ :deleted, old_entry, new_entry ] ]
160
153
 
161
- # If new is a directory and old is a file
162
- elsif new_entry.dir?
163
- if old_entry.exists?
164
- yield [ :file_to_directory, old_entry, new_entry ]
154
+ # If the new entry does not and *cannot* exist, report that.
155
+ else
156
+ return [ [ :new_cannot_upload, old_entry, new_entry ] ]
157
+ end
158
+
159
+ # If new is a directory and old is a file
160
+ elsif new_entry.dir?
161
+ if old_entry.exists?
162
+ return [ [ :file_to_directory, old_entry, new_entry ] ]
165
163
 
166
164
  # If new is a directory and old does not exist
167
- elsif old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
168
- yield [ :added, old_entry, new_entry ]
169
- end
165
+ elsif old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
166
+ return [ [ :added, old_entry, new_entry ] ]
170
167
 
171
- # Neither is a directory, so they are diffable with file diff
168
+ # If the new entry does not and *cannot* exist, report that.
172
169
  else
173
- are_same, old_value, new_value = ChefFS::FileSystem.compare(old_entry, new_entry)
174
- if are_same
175
- if old_value == :none
176
- yield [ :both_nonexistent, old_entry, new_entry ]
177
- else
178
- yield [ :same, old_entry, new_entry ]
179
- end
170
+ return [ [ :old_cannot_upload, old_entry, new_entry ] ]
171
+ end
172
+
173
+ # Neither is a directory, so they are diffable with file diff
174
+ else
175
+ are_same, old_value, new_value = ChefFS::FileSystem.compare(old_entry, new_entry)
176
+ if are_same
177
+ if old_value == :none
178
+ return [ [ :both_nonexistent, old_entry, new_entry ] ]
180
179
  else
181
- if old_value == :none
182
- old_exists = false
183
- elsif old_value.nil?
184
- old_exists = old_entry.exists?
185
- else
186
- old_exists = true
187
- end
180
+ return [ [ :same, old_entry, new_entry ] ]
181
+ end
182
+ else
183
+ if old_value == :none
184
+ old_exists = false
185
+ elsif old_value.nil?
186
+ old_exists = old_entry.exists?
187
+ else
188
+ old_exists = true
189
+ end
188
190
 
189
- if new_value == :none
190
- new_exists = false
191
- elsif new_value.nil?
192
- new_exists = new_entry.exists?
193
- else
194
- new_exists = true
195
- end
191
+ if new_value == :none
192
+ new_exists = false
193
+ elsif new_value.nil?
194
+ new_exists = new_entry.exists?
195
+ else
196
+ new_exists = true
197
+ end
196
198
 
197
- # If one of the files doesn't exist, we only want to print the diff if the
198
- # other file *could be uploaded/downloaded*.
199
- if !old_exists && !old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
200
- yield [ :old_cannot_upload, old_entry, new_entry ]
201
- return
202
- end
203
- if !new_exists && !new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
204
- yield [ :new_cannot_upload, old_entry, new_entry ]
205
- return
206
- end
199
+ # If one of the files doesn't exist, we only want to print the diff if the
200
+ # other file *could be uploaded/downloaded*.
201
+ if !old_exists && !old_entry.parent.can_have_child?(new_entry.name, new_entry.dir?)
202
+ return [ [ :old_cannot_upload, old_entry, new_entry ] ]
203
+ end
204
+ if !new_exists && !new_entry.parent.can_have_child?(old_entry.name, old_entry.dir?)
205
+ return [ [ :new_cannot_upload, old_entry, new_entry ] ]
206
+ end
207
207
 
208
- if get_content
209
- # If we haven't read the values yet, get them now so that they can be diffed
210
- begin
211
- old_value = old_entry.read if old_value.nil?
212
- rescue ChefFS::FileSystem::NotFoundError
213
- old_value = :none
214
- end
215
- begin
216
- new_value = new_entry.read if new_value.nil?
217
- rescue ChefFS::FileSystem::NotFoundError
218
- new_value = :none
219
- end
208
+ if get_content
209
+ # If we haven't read the values yet, get them now so that they can be diffed
210
+ begin
211
+ old_value = old_entry.read if old_value.nil?
212
+ rescue ChefFS::FileSystem::NotFoundError
213
+ old_value = :none
220
214
  end
221
-
222
- if old_value == :none || (old_value == nil && !old_entry.exists?)
223
- yield [ :added, old_entry, new_entry, old_value, new_value ]
224
- elsif new_value == :none
225
- yield [ :deleted, old_entry, new_entry, old_value, new_value ]
226
- else
227
- yield [ :modified, old_entry, new_entry, old_value, new_value ]
215
+ begin
216
+ new_value = new_entry.read if new_value.nil?
217
+ rescue ChefFS::FileSystem::NotFoundError
218
+ new_value = :none
228
219
  end
229
220
  end
221
+
222
+ if old_value == :none || (old_value == nil && !old_entry.exists?)
223
+ return [ [ :added, old_entry, new_entry, old_value, new_value ] ]
224
+ elsif new_value == :none
225
+ return [ [ :deleted, old_entry, new_entry, old_value, new_value ] ]
226
+ else
227
+ return [ [ :modified, old_entry, new_entry, old_value, new_value ] ]
228
+ end
230
229
  end
231
- rescue ChefFS::FileSystem::FileSystemError => e
232
- yield [ :error, old_entry, new_entry, nil, nil, e ]
230
+ end
231
+ rescue ChefFS::FileSystem::FileSystemError => e
232
+ return [ [ :error, old_entry, new_entry, nil, nil, e ] ]
233
+ end
234
+
235
+ class Differ
236
+ def initialize(old_entry, new_entry, recurse_depth, get_content)
237
+ @old_entry = old_entry
238
+ @new_entry = new_entry
239
+ @recurse_depth = recurse_depth
240
+ @get_content = get_content
241
+ end
242
+
243
+ attr_reader :old_entry
244
+ attr_reader :new_entry
245
+ attr_reader :recurse_depth
246
+ attr_reader :get_content
247
+
248
+ def each
233
249
  end
234
250
  end
235
251
 
@@ -20,39 +20,59 @@ require 'chef_fs/path_utils'
20
20
  require 'chef_fs/file_system/default_environment_cannot_be_modified_error'
21
21
  require 'chef_fs/file_system/operation_failed_error'
22
22
  require 'chef_fs/file_system/operation_not_allowed_error'
23
+ require 'chef_fs/parallelizer'
23
24
 
24
25
  module ChefFS
25
26
  module FileSystem
26
- # Yields a list of all things under (and including) this entry that match the
27
+ # Returns a list of all things under (and including) this entry that match the
27
28
  # given pattern.
28
29
  #
29
30
  # ==== Attributes
30
31
  #
31
- # * +entry+ - Entry to start listing under
32
+ # * +root+ - Entry to start listing under
32
33
  # * +pattern+ - ChefFS::FilePattern to match children under
33
34
  #
34
- def self.list(entry, pattern, &block)
35
- # Include self in results if it matches
36
- if pattern.match?(entry.path)
37
- block.call(entry)
35
+ def self.list(root, pattern)
36
+ Lister.new(root, pattern)
37
+ end
38
+
39
+ class Lister
40
+ include Enumerable
41
+
42
+ def initialize(root, pattern)
43
+ @root = root
44
+ @pattern = pattern
38
45
  end
39
46
 
40
- if pattern.could_match_children?(entry.path)
41
- # If it's possible that our children could match, descend in and add matches.
42
- exact_child_name = pattern.exact_child_name_under(entry.path)
47
+ attr_reader :root
48
+ attr_reader :pattern
43
49
 
44
- # If we've got an exact name, don't bother listing children; just grab the
45
- # child with the given name.
46
- if exact_child_name
47
- exact_child = entry.child(exact_child_name)
48
- if exact_child
49
- list(exact_child, pattern, &block)
50
- end
50
+ def each(&block)
51
+ list_from(root, &block)
52
+ end
53
+
54
+ def list_from(entry, &block)
55
+ # Include self in results if it matches
56
+ if pattern.match?(entry.path)
57
+ block.call(entry)
58
+ end
59
+
60
+ if pattern.could_match_children?(entry.path)
61
+ # If it's possible that our children could match, descend in and add matches.
62
+ exact_child_name = pattern.exact_child_name_under(entry.path)
63
+
64
+ # If we've got an exact name, don't bother listing children; just grab the
65
+ # child with the given name.
66
+ if exact_child_name
67
+ exact_child = entry.child(exact_child_name)
68
+ if exact_child
69
+ list_from(exact_child, &block)
70
+ end
51
71
 
52
- # Otherwise, go through all children and find any matches
53
- elsif entry.dir?
54
- entry.children.each do |child|
55
- list(child, pattern, &block)
72
+ # Otherwise, go through all children and find any matches
73
+ elsif entry.dir?
74
+ results = Parallelizer::parallelize(entry.children, :flatten => true) { |child| ChefFS::FileSystem.list(child, pattern) }
75
+ results.each(&block)
56
76
  end
57
77
  end
58
78
  end
@@ -118,7 +138,7 @@ module ChefFS
118
138
  def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui, format_path)
119
139
  found_result = false
120
140
  error = false
121
- list_pairs(pattern, src_root, dest_root) do |src, dest|
141
+ parallel_do(list_pairs(pattern, src_root, dest_root)) do |src, dest|
122
142
  found_result = true
123
143
  new_dest_parent = get_or_create_parent(dest, options, ui, format_path)
124
144
  child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path)
@@ -142,26 +162,44 @@ module ChefFS
142
162
  #
143
163
  # ==== Example
144
164
  #
145
- # ChefFS::FileSystem.list_pairs(FilePattern.new('**x.txt', a_root, b_root)) do |a, b|
165
+ # ChefFS::FileSystem.list_pairs(FilePattern.new('**x.txt', a_root, b_root)).each do |a, b|
146
166
  # ...
147
167
  # end
148
168
  #
149
169
  def self.list_pairs(pattern, a_root, b_root)
150
- # Make sure everything on the server is also on the filesystem, and diff
151
- found_paths = Set.new
152
- ChefFS::FileSystem.list(a_root, pattern) do |a|
153
- found_paths << a.path
154
- b = ChefFS::FileSystem.resolve_path(b_root, a.path)
155
- yield [ a, b ]
170
+ PairLister.new(pattern, a_root, b_root)
171
+ end
172
+
173
+ class PairLister
174
+ include Enumerable
175
+
176
+ def initialize(pattern, a_root, b_root)
177
+ @pattern = pattern
178
+ @a_root = a_root
179
+ @b_root = b_root
156
180
  end
157
181
 
158
- # Check the outer regex pattern to see if it matches anything on the
159
- # filesystem that isn't on the server
160
- ChefFS::FileSystem.list(b_root, pattern) do |b|
161
- if !found_paths.include?(b.path)
162
- a = ChefFS::FileSystem.resolve_path(a_root, b.path)
182
+ attr_reader :pattern
183
+ attr_reader :a_root
184
+ attr_reader :b_root
185
+
186
+ def each
187
+ # Make sure everything on the server is also on the filesystem, and diff
188
+ found_paths = Set.new
189
+ ChefFS::FileSystem.list(a_root, pattern).each do |a|
190
+ found_paths << a.path
191
+ b = ChefFS::FileSystem.resolve_path(b_root, a.path)
163
192
  yield [ a, b ]
164
193
  end
194
+
195
+ # Check the outer regex pattern to see if it matches anything on the
196
+ # filesystem that isn't on the server
197
+ ChefFS::FileSystem.list(b_root, pattern).each do |b|
198
+ if !found_paths.include?(b.path)
199
+ a = ChefFS::FileSystem.resolve_path(a_root, b.path)
200
+ yield [ a, b ]
201
+ end
202
+ end
165
203
  end
166
204
  end
167
205
 
@@ -204,6 +242,7 @@ module ChefFS
204
242
  are_same, b_value, a_value = b.compare_to(a)
205
243
  end
206
244
  if are_same.nil?
245
+ # TODO these reads can be parallelized
207
246
  begin
208
247
  a_value = a.read if a_value.nil?
209
248
  rescue ChefFS::FileSystem::NotFoundError
@@ -276,7 +315,7 @@ module ChefFS
276
315
  end
277
316
  # Directory creation is recursive.
278
317
  if recurse_depth != 0
279
- src_entry.children.each do |src_child|
318
+ parallel_do(src_entry.children) do |src_child|
280
319
  new_dest_child = new_dest_dir.child(src_child.name)
281
320
  child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
282
321
  error ||= child_error
@@ -313,7 +352,7 @@ module ChefFS
313
352
  if dest_entry.dir?
314
353
  # If both are directories, recurse into their children
315
354
  if recurse_depth != 0
316
- child_pairs(src_entry, dest_entry).each do |src_child, dest_child|
355
+ parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child|
317
356
  child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path)
318
357
  error ||= child_error
319
358
  end
@@ -376,5 +415,8 @@ module ChefFS
376
415
  return parent
377
416
  end
378
417
 
418
+ def self.parallel_do(enum, options = {}, &block)
419
+ ChefFS::Parallelizer.parallelize(enum, options, &block).to_a
420
+ end
379
421
  end
380
422
  end