summaryrefslogtreecommitdiff
path: root/lib/chef/knife/xargs.rb
diff options
context:
space:
mode:
authorJohn Keiser <jkeiser@opscode.com>2013-04-05 23:07:33 -0700
committerJohn Keiser <jkeiser@opscode.com>2013-06-07 13:12:30 -0700
commit1eacecda5bd07ba45f38f291d73cc3708ca82b82 (patch)
tree9ab75a0cb629d8aa304b10896cf309c0109b9fd4 /lib/chef/knife/xargs.rb
parent14fddb7dcb0ee9e936acc059afb96489bdbe73a0 (diff)
downloadchef-1eacecda5bd07ba45f38f291d73cc3708ca82b82.tar.gz
Add xargs -s SIZE for command line size limit
Diffstat (limited to 'lib/chef/knife/xargs.rb')
-rw-r--r--lib/chef/knife/xargs.rb200
1 files changed, 136 insertions, 64 deletions
diff --git a/lib/chef/knife/xargs.rb b/lib/chef/knife/xargs.rb
index f4c80468b7..46471f2252 100644
--- a/lib/chef/knife/xargs.rb
+++ b/lib/chef/knife/xargs.rb
@@ -50,6 +50,10 @@ class Chef
:short => '-n MAXARGS',
:description => "Maximum number of arguments per command line."
+ option :max_command_line,
+ :short => '-s LENGTH',
+ :description => "Maximum size of command line, in characters"
+
def run
error = false
# Get the matches (recursively)
@@ -61,16 +65,59 @@ class Chef
ui.warn "#{format_path(result)}: is a directory. Will not run #{command} on it."
else
files << result
- if config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i
- error = true if xargs_files(files)
- files = []
+ ran = false
+
+ # If the command would be bigger than max command line, back it off a bit
+ # and run a slightly smaller command (with one less arg)
+ if config[:max_command_line]
+ command, tempfiles = create_command(files)
+ begin
+ if command.length > config[:max_command_line].to_i
+ if files.length > 1
+ command, tempfiles_minus_one = create_command(files[0..-2])
+ begin
+ error = true if xargs_files(command, tempfiles_minus_one)
+ files = [ files[-1] ]
+ ran = true
+ ensure
+ destroy_tempfiles(tempfiles)
+ end
+ else
+ error = true if xargs_files(command, tempfiles)
+ files = [ ]
+ ran = true
+ end
+ end
+ ensure
+ destroy_tempfiles(tempfiles)
+ end
+ end
+
+ # If the command has hit the limit for the # of arguments, run it
+ if !ran && config[:max_arguments_per_command] && files.size >= config[:max_arguments_per_command].to_i
+ command, tempfiles = create_command(files)
+ begin
+ error = true if xargs_files(command, tempfiles)
+ files = []
+ ran = true
+ ensure
+ destroy_tempfiles(tempfiles)
+ end
end
end
end
end
+
+ # Any leftovers commands shall be run
if files.size > 0
- error = true if xargs_files(files)
+ command, tempfiles = create_command(files)
+ begin
+ error = true if xargs_files(command, tempfiles)
+ ensure
+ destroy_tempfiles(tempfiles)
+ end
end
+
if error
exit 1
end
@@ -84,80 +131,105 @@ class Chef
end
end
- def xargs_files(files)
+ def create_command(files)
command = name_args.join(' ')
+ # Create the (empty) tempfiles
tempfiles = {}
begin
- # Create and fill in the temporary files
+ # Create 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
+ tempfile = Tempfile.new(file.name)
+ tempfiles[tempfile] = { :file => file }
end
+ rescue
+ destroy_tempfiles(files)
+ raise
+ end
+
+ # Create the 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
+
+ [final_command, tempfiles]
+ end
+
+ def destroy_tempfiles(tempfiles)
+ # Unlink the files now that we're done with them
+ tempfiles.keys.each { |tempfile| tempfile.close! }
+ 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}"
+ def xargs_files(command, tempfiles)
+ error = false
+ # Create the temporary files
+ tempfiles.each_pair do |tempfile, file|
+ begin
+ value = file[:file].read
+ file[:value] = value
+ tempfile.open
+ tempfile.write(value)
+ tempfile.close
+ rescue Chef::ChefFS::FileSystem::OperationNotAllowedError => e
+ ui.error "#{format_path(e.entry)}: #{e.reason}."
+ error = true
+ tempfile.close!
+ tempfiles.delete(tempfile)
+ next
+ rescue Chef::ChefFS::FileSystem::NotFoundError => e
+ ui.error "#{format_path(e.entry)}: No such file or directory"
+ error = true
+ tempfile.close!
+ tempfiles.delete(tempfile)
+ next
end
+ 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
+ return error if error && tempfiles.size == 0
- # 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
+ # Run the command
+ output sub_filenames(command, tempfiles)
+ command_output = `#{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
- ensure
- # Unlink the files now that we're done with them
- tempfiles.keys.each { |tempfile| tempfile.close! }
+ # 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, "#{format_path(file[:file])} (old)")
+ diff.gsub!(tempfile.path, "#{format_path(file[:file])} (new)")
+ output diff
+ ensure
+ old_file.close!
+ end
+ end
end
+
+ error
end
def sub_filenames(str, tempfiles)