module Chef::ChefFS::FileSystem
Public Class Methods
Get entries for children of either a or b, with matching pairs matched up.
Returns¶ ↑
An array of child pairs.
[ [ a_child, b_child ], ... ]
If a child is only in a or only in b, the other child entry will be retrieved by name (and will most likely be a “nonexistent child”).
Example¶ ↑
Chef::ChefFS::FileSystem.child_pairs(a, b).length
# File lib/chef/chef_fs/file_system.rb, line 220 def self.child_pairs(a, b) # If both are directories, recurse into them and diff the children instead of returning ourselves. result = [] a_children_names = Set.new a.children.each do |a_child| a_children_names << a_child.bare_name result << [ a_child, b.child(a_child.bare_name) ] end # Check b for children that aren't in a b.children.each do |b_child| if !a_children_names.include?(b_child.bare_name) result << [ a.child(b_child.bare_name), b_child ] end end result end
# File lib/chef/chef_fs/file_system.rb, line 238 def self.compare(a, b) are_same, a_value, b_value = a.compare_to(b) if are_same.nil? are_same, b_value, a_value = b.compare_to(a) end if are_same.nil? # TODO these reads can be parallelized begin a_value = a.read if a_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError a_value = :none end begin b_value = b.read if b_value.nil? rescue Chef::ChefFS::FileSystem::NotFoundError b_value = :none end are_same = (a_value == b_value) end [ are_same, a_value, b_value ] end
Copy everything matching the given pattern from src to dest.
After this method completes, everything in dest matching the given pattern will look identical to src.
Attributes¶ ↑
-
pattern
-Chef::ChefFS::FilePattern
to match children under -
src_root
- the root from which things will be copied -
dest_root
- the root to which things will be copied -
recurse_depth
- the maximum depth to copy things.nil
means infinite depth. 0 means no recursion. -
options
- hash of options:-
purge
- iftrue
, items indest
that are not insrc
will be deleted from
dest
. Iffalse
, these items will be left alone.-
force
- iftrue
, matching files are always copied fromsrc
todest
. Iffalse
, they will only be copied if actually different (which will take time to determine). -
dry_run
- iftrue
, action will not actually be taken; things will be printed out instead.
-
Examples¶ ↑
Chef::ChefFS::FileSystem.copy_to(FilePattern.new('/cookbooks'), chef_fs, local_fs, nil, true) do |message| puts message end
# File lib/chef/chef_fs/file_system.rb, line 137 def self.copy_to(pattern, src_root, dest_root, recurse_depth, options, ui = nil, format_path = nil) found_result = false error = false parallel_do(list_pairs(pattern, src_root, dest_root)) do |src, dest| found_result = true new_dest_parent = get_or_create_parent(dest, options, ui, format_path) child_error = copy_entries(src, dest, new_dest_parent, recurse_depth, options, ui, format_path) error ||= child_error end if !found_result && pattern.exact_path ui.error "#{pattern}: No such file or directory on remote or local" if ui error = true end error end
Returns a list of all things under (and including) this entry that match the given pattern.
Attributes¶ ↑
-
root
- Entry to start listing under -
pattern
-Chef::ChefFS::FilePattern
to match children under
# File lib/chef/chef_fs/file_system.rb, line 34 def self.list(root, pattern) Lister.new(root, pattern) end
Yield entries for children that are in either a_root
or b_root
, with matching pairs matched up.
Yields¶ ↑
Yields matching entries in pairs:
[ a_entry, b_entry ]
Example¶ ↑
Chef::ChefFS::FileSystem.list_pairs(FilePattern.new('**x.txt', a_root, b_root)).each do |a, b| ... end
# File lib/chef/chef_fs/file_system.rb, line 168 def self.list_pairs(pattern, a_root, b_root) PairLister.new(pattern, a_root, b_root) end
Resolve the given path against the entry, returning the entry at the end of the path.
Attributes¶ ↑
-
entry
- the entry to start looking under. Relative paths will be resolved from here. -
path
- the path to resolve. If it starts with/
, the path will be resolved starting fromentry.root
.
Examples¶ ↑
Chef::ChefFS::FileSystem.resolve_path(root_path, 'cookbooks/java/recipes/default.rb')
# File lib/chef/chef_fs/file_system.rb, line 94 def self.resolve_path(entry, path) return entry if path.length == 0 return resolve_path(entry.root, path) if path[0, 1] == "/" && entry.root != entry if path[0, 1] == "/" path = path[1, path.length - 1] end result = entry Chef::ChefFS::PathUtils.split(path).each do |part| result = result.child(part) end result end
Private Class Methods
Copy two entries (could be files or dirs)
# File lib/chef/chef_fs/file_system.rb, line 264 def copy_entries(src_entry, dest_entry, new_dest_parent, recurse_depth, options, ui, format_path) # A NOTE about this algorithm: # There are cases where this algorithm does too many network requests. # knife upload with a specific filename will first check if the file # exists (a "dir" in the parent) before deciding whether to POST or # PUT it. If we just tried PUT (or POST) and then tried the other if # the conflict failed, we wouldn't need to check existence. # On the other hand, we may already have DONE the request, in which # case we shouldn't waste time trying PUT if we know the file doesn't # exist. # Will need to decide how that works with checksums, though. error = false begin dest_path = format_path.call(dest_entry) if ui src_path = format_path.call(src_entry) if ui if !src_entry.exists? if options[:purge] # If we would not have uploaded it, we will not purge it. if src_entry.parent.can_have_child?(dest_entry.name, dest_entry.dir?) if options[:dry_run] ui.output "Would delete #{dest_path}" if ui else begin dest_entry.delete(true) ui.output "Deleted extra entry #{dest_path} (purge is on)" if ui rescue Chef::ChefFS::FileSystem::NotFoundError ui.output "Entry #{dest_path} does not exist. Nothing to do. (purge is on)" if ui end end else ui.output ("Not deleting extra entry #{dest_path} (purge is off)") if ui end end elsif !dest_entry.exists? if new_dest_parent.can_have_child?(src_entry.name, src_entry.dir?) # If the entry can do a copy directly from filesystem, do that. if new_dest_parent.respond_to?(:create_child_from) if options[:dry_run] ui.output "Would create #{dest_path}" if ui else new_dest_parent.create_child_from(src_entry) ui.output "Created #{dest_path}" if ui end return end if src_entry.dir? if options[:dry_run] ui.output "Would create #{dest_path}" if ui new_dest_dir = new_dest_parent.child(src_entry.name) else new_dest_dir = new_dest_parent.create_child(src_entry.name, nil) ui.output "Created #{dest_path}" if ui end # Directory creation is recursive. if recurse_depth != 0 parallel_do(src_entry.children) do |src_child| new_dest_child = new_dest_dir.child(src_child.name) child_error = copy_entries(src_child, new_dest_child, new_dest_dir, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) error ||= child_error end end else if options[:dry_run] ui.output "Would create #{dest_path}" if ui else child = new_dest_parent.create_child(src_entry.name, src_entry.read) ui.output "Created #{format_path.call(child)}" if ui end end end else # Both exist. # If the entry can do a copy directly, do that. if dest_entry.respond_to?(:copy_from) if options[:force] || compare(src_entry, dest_entry)[0] == false if options[:dry_run] ui.output "Would update #{dest_path}" if ui else dest_entry.copy_from(src_entry, options) ui.output "Updated #{dest_path}" if ui end end return end # If they are different types, log an error. if src_entry.dir? if dest_entry.dir? # If both are directories, recurse into their children if recurse_depth != 0 parallel_do(child_pairs(src_entry, dest_entry)) do |src_child, dest_child| child_error = copy_entries(src_child, dest_child, dest_entry, recurse_depth ? recurse_depth - 1 : recurse_depth, options, ui, format_path) error ||= child_error end end else # If they are different types. ui.error("File #{src_path} is a directory while file #{dest_path} is a regular file\n") if ui return end else if dest_entry.dir? ui.error("File #{src_path} is a regular file while file #{dest_path} is a directory\n") if ui return else # Both are files! Copy them unless we're sure they are the same.' if options[:diff] == false should_copy = false elsif options[:force] should_copy = true src_value = nil else are_same, src_value, _dest_value = compare(src_entry, dest_entry) should_copy = !are_same end if should_copy if options[:dry_run] ui.output "Would update #{dest_path}" if ui else src_value = src_entry.read if src_value.nil? dest_entry.write(src_value) ui.output "Updated #{dest_path}" if ui end end end end end rescue RubyFileError => e ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui rescue DefaultEnvironmentCannotBeModifiedError => e ui.warn "#{format_path.call(e.entry)} #{e.reason}." if ui rescue OperationFailedError => e ui.error "#{format_path.call(e.entry)} failed to #{e.operation}: #{e.message}" if ui error = true rescue OperationNotAllowedError => e ui.error "#{format_path.call(e.entry)} #{e.reason}." if ui error = true end error end
# File lib/chef/chef_fs/file_system.rb, line 410 def get_or_create_parent(entry, options, ui, format_path) parent = entry.parent if parent && !parent.exists? parent_path = format_path.call(parent) if ui parent_parent = get_or_create_parent(parent, options, ui, format_path) if options[:dry_run] ui.output "Would create #{parent_path}" if ui else parent = parent_parent.create_child(parent.name, nil) ui.output "Created #{parent_path}" if ui end end parent end
# File lib/chef/chef_fs/file_system.rb, line 425 def parallel_do(enum, options = {}, &block) Chef::ChefFS::Parallelizer.parallel_do(enum, options, &block) end