summaryrefslogtreecommitdiff
path: root/lib/chef/knife/xargs.rb
diff options
context:
space:
mode:
authorJohn Keiser <jkeiser@opscode.com>2013-04-05 22:22:12 -0700
committerJohn Keiser <jkeiser@opscode.com>2013-06-07 13:12:30 -0700
commit14fddb7dcb0ee9e936acc059afb96489bdbe73a0 (patch)
tree5d6c06899960f590f6a5fe0cdd97f6ff909d8caf /lib/chef/knife/xargs.rb
parent9d5cd97ea8de3fe98d7ae75da9c6b7b529a70c55 (diff)
downloadchef-14fddb7dcb0ee9e936acc059afb96489bdbe73a0.tar.gz
Add xargs -n MAXARGS to allow command lines with more args
Diffstat (limited to 'lib/chef/knife/xargs.rb')
-rw-r--r--lib/chef/knife/xargs.rb156
1 files changed, 98 insertions, 58 deletions
diff --git a/lib/chef/knife/xargs.rb b/lib/chef/knife/xargs.rb
index 5a531b57d9..f4c80468b7 100644
--- a/lib/chef/knife/xargs.rb
+++ b/lib/chef/knife/xargs.rb
@@ -39,82 +39,38 @@ class Chef
:description => "Force upload of files even if they are not changed (quicker and harmless, but doesn't print out what it changed)"
option :replace_first,
- :short => '-J replacestr',
+ :short => '-J REPLACESTR',
:description => "String to replace with filenames. -J will only replace the FIRST occurrence of the replacement string."
option :replace_all,
- :short => '-I replacestr',
+ :short => '-I REPLACESTR',
:description => "String to replace with filenames. -I will replace ALL occurrence of the replacement string."
+ option :max_arguments_per_command,
+ :short => '-n MAXARGS',
+ :description => "Maximum number of arguments per command line."
+
def run
error = false
- command = name_args.join(' ')
# Get the matches (recursively)
+ files = []
pattern_args_from(get_patterns).each do |pattern|
Chef::ChefFS::FileSystem.list(config[:local] ? local_fs : chef_fs, pattern) do |result|
if result.dir?
# TODO option to include directories
ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it."
else
- begin
- value = result.read
- tmpfile = Tempfile.open(result.name)
- begin
- tmpfile.write(value)
- tmpfile.close
-
- # Determine the command name
- if config[:replace_all]
- final_command = command.gsub(config[:replace_all], tmpfile.path)
- elsif config[:replace_first]
- final_command = command.sub(config[:replace_first], tmpfile.path)
- else
- final_command = "#{command} #{tmpfile.path}"
- end
-
- # Run the command
- output final_command.gsub(tmpfile.path, format_path(result))
- command_output = `#{final_command}`
- command_output.gsub!(tmpfile.path, format_path(result))
-
- # Check if the output is different
- tmpfile.open
- new_value = tmpfile.read
- tmpfile.close
- if config[:force] || new_value != value
- if config[:dry_run]
- output "Would update #{format_path(result)}"
- else
- result.write(new_value)
- output "Updated #{format_path(result)}"
- end
- end
-
- if config[:diff] && new_value != value
- old_file = Tempfile.open(result.name)
- begin
- old_file.write(value)
- old_file.close
-
- diff = `diff -u #{old_file.path} #{tmpfile.path}`
- ensure
- old_file.close!
- end
- end
- ensure
- tmpfile.close! # Unlink the file now that we're done with it
- end
-
- rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
- ui.error "#{format_path(e.entry)}: #{e.reason}."
- error = true
- rescue Chef::ChefFS::FileSystem::NotFoundError => e
- ui.error "#{format_path(e.entry)}: No such file or directory"
- error = true
+ files << result
+ if config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i
+ error = true if xargs_files(files)
+ files = []
end
end
end
end
+ if files.size > 0
+ error = true if xargs_files(files)
+ end
if error
exit 1
end
@@ -127,6 +83,90 @@ class Chef
stdin.lines.map { |line| line.chomp }
end
end
+
+ def xargs_files(files)
+ command = name_args.join(' ')
+
+ tempfiles = {}
+ begin
+ # Create and fill in the temporary files
+ files.each do |file|
+ begin
+ value = file.read
+ tempfile = Tempfile.open(file.name)
+ tempfiles[tempfile] = { :file => file, :value => value }
+ tempfile.write(value)
+ tempfile.close
+ rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
+ ui.error "#{format_path(e.entry)}: #{e.reason}."
+ error = true
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ ui.error "#{format_path(e.entry)}: No such file or directory"
+ error = true
+ next
+ end
+ end
+
+ # Determine the full command
+ paths = tempfiles.keys.map { |tempfile| tempfile.path }.join(' ')
+ if config[:replace_all]
+ final_command = command.gsub(config[:replace_all], paths)
+ elsif config[:replace_first]
+ final_command = command.sub(config[:replace_first], paths)
+ else
+ final_command = "#{command} #{paths}"
+ end
+
+ # Run the command
+ output sub_filenames(final_command, tempfiles)
+ command_output = `#{final_command}`
+ command_output = sub_filenames(command_output, tempfiles)
+
+ # Check if the output is different
+ tempfiles.each_pair do |tempfile, file|
+ # Read the new output
+ new_value = IO.binread(tempfile.path)
+
+ # Upload the output if different
+ if config[:force] || new_value != file[:value]
+ if config[:dry_run]
+ output "Would update #{format_path(file[:file])}"
+ else
+ file[:file].write(new_value)
+ output "Updated #{format_path(file[:file])}"
+ end
+ end
+
+ # Print a diff of what was uploaded
+ if config[:diff] && new_value != file[:value]
+ old_file = Tempfile.open(file[:file].name)
+ begin
+ old_file.write(file[:value])
+ old_file.close
+
+ diff = `diff -u #{old_file.path} #{tempfile.path}`
+ diff.gsub!(old_file.path, "#{file[:file].name} (old)")
+ diff.gsub!(tempfile.path, "#{file[:file].name} (new)")
+ output diff
+ ensure
+ old_file.close!
+ end
+ end
+ end
+
+ ensure
+ # Unlink the files now that we're done with them
+ tempfiles.keys.each { |tempfile| tempfile.close! }
+ end
+ end
+
+ def sub_filenames(str, tempfiles)
+ tempfiles.each_pair do |tempfile, file|
+ str.gsub!(tempfile.path, format_path(file[:file]))
+ end
+ str
+ end
+
end
end
end