diff options
author | John Keiser <jkeiser@opscode.com> | 2013-04-05 22:22:12 -0700 |
---|---|---|
committer | John Keiser <jkeiser@opscode.com> | 2013-06-07 13:12:30 -0700 |
commit | 14fddb7dcb0ee9e936acc059afb96489bdbe73a0 (patch) | |
tree | 5d6c06899960f590f6a5fe0cdd97f6ff909d8caf /lib/chef/knife/xargs.rb | |
parent | 9d5cd97ea8de3fe98d7ae75da9c6b7b529a70c55 (diff) | |
download | chef-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.rb | 156 |
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 |