X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/d8277401f758f9a45dfa166205e3b77708ac142d..3657c576451610bc6e7f269cbf4f13cb18ff35ba:/sdk/cli/bin/arv?ds=sidebyside diff --git a/sdk/cli/bin/arv b/sdk/cli/bin/arv index 4ce0940f6e..e84150a35d 100755 --- a/sdk/cli/bin/arv +++ b/sdk/cli/bin/arv @@ -56,14 +56,20 @@ class Google::APIClient return @discovery_documents["#{api}:#{version}"] ||= begin # fetch new API discovery doc if stale - cached_doc = File.expand_path '~/.cache/arvados/discovery_uri.json' - if not File.exist?(cached_doc) or (Time.now - File.mtime(cached_doc)) > 86400 + cached_doc = File.expand_path '~/.cache/arvados/discovery_uri.json' rescue nil + + if cached_doc.nil? or not File.exist?(cached_doc) or (Time.now - File.mtime(cached_doc)) > 86400 response = self.execute!(:http_method => :get, :uri => self.discovery_uri(api, version), :authenticated => false) - FileUtils.makedirs(File.dirname cached_doc) - File.open(cached_doc, 'w') do |f| - f.puts response.body + + begin + FileUtils.makedirs(File.dirname cached_doc) + File.open(cached_doc, 'w') do |f| + f.puts response.body + end + rescue + return JSON.load response.body end end @@ -85,8 +91,8 @@ end def init_config # read authentication data from arvados configuration file if present lineno = 0 - config_file = File.expand_path('~/.config/arvados/settings.conf') - if File.exist? config_file then + config_file = File.expand_path('~/.config/arvados/settings.conf') rescue nil + if not config_file.nil? and File.exist? config_file then File.open(config_file, 'r').each do |line| lineno = lineno + 1 # skip comments @@ -125,7 +131,15 @@ def check_subcommands client, arvados, subcommand, global_opts, remaining_opts end abort when 'pipeline' - exec `which arv-run-pipeline-instance`.strip, *remaining_opts + sub = remaining_opts.shift + if sub == 'run' + exec `which arv-run-pipeline-instance`.strip, *remaining_opts + else + puts "Usage: arv pipeline [method] [--parameters]\n" + puts "Use 'arv pipeline [method] --help' to get more information about specific methods.\n\n" + puts "Available methods: run" + end + abort when 'tag' exec `which arv-tag`.strip, *remaining_opts when 'ws' @@ -135,14 +149,19 @@ def check_subcommands client, arvados, subcommand, global_opts, remaining_opts end end +def arv_edit_save_tmp tmp + FileUtils::cp tmp.path, tmp.path + ".saved" + puts "Saved contents to " + tmp.path + ".saved" +end + def arv_edit client, arvados, global_opts, remaining_opts - n = remaining_opts.shift - if n.nil? or n == "-h" or n == "--help" + uuid = remaining_opts.shift + if uuid.nil? or uuid == "-h" or uuid == "--help" puts head_banner puts "Usage: arv edit [uuid] [fields...]\n\n" - puts "Fetchs the specified Arvados object, select the specified fields, and\n" + puts "Fetch the specified Arvados object, select the specified fields, \n" puts "open an interactive text editor on a text representation (json or\n" - puts "yaml, use --format) and then updates the object. Will use 'nano'\n" + puts "yaml, use --format) and then update the object. Will use 'nano'\n" puts "by default, customize with the EDITOR or VISUAL environment variable.\n" exit 255 end @@ -154,9 +173,13 @@ def arv_edit client, arvados, global_opts, remaining_opts # determine controller - m = /([a-z0-9]{5})-([a-z0-9]{5})-([a-z0-9]{15})/.match n + m = /([a-z0-9]{5})-([a-z0-9]{5})-([a-z0-9]{15})/.match uuid if !m - abort puts "#{n} does not appear to be an arvados uuid" + if /^[a-f0-9]{32}/.match uuid + abort "Arvados collections are not editable." + else + abort "#{n} does not appear to be an Arvados uuid" + end end rsc = nil @@ -175,7 +198,7 @@ def arv_edit client, arvados, global_opts, remaining_opts api_method = 'arvados.' + rsc + '.get' result = client.execute(:api_method => eval(api_method), - :parameters => {"uuid" => n}, + :parameters => {"uuid" => uuid}, :authenticated => false, :headers => { authorization: 'OAuth2 '+ENV['ARVADOS_API_TOKEN'] @@ -201,42 +224,79 @@ def arv_edit client, arvados, global_opts, remaining_opts require 'tempfile' - tmp = Tempfile.new(n) + tmp = Tempfile.new([uuid, "." + global_opts[:format]]) tmp.write(content) tmp.close - pid = Process::fork - if pid.nil? - editor ||= ENV["VISUAL"] - editor ||= ENV["EDITOR"] - editor ||= "nano" - exec editor, tmp.path - else - Process.wait pid - end + need_edit = true + + while need_edit + pid = Process::fork + if pid.nil? + editor ||= ENV["VISUAL"] + editor ||= ENV["EDITOR"] + editor ||= "nano" + exec editor, tmp.path + else + Process.wait pid + end - if $?.exitstatus == 0 - tmp.open - newcontent = tmp.read() + if $?.exitstatus == 0 + tmp.open + newcontent = tmp.read() - newobj = {} - case global_opts[:format] - when 'json' - newobj = Oj.load(newcontent) - when 'yaml' - newobj = YAML.load(newcontent) + newobj = {} + begin + case global_opts[:format] + when 'json' + newobj = Oj.load(newcontent) + when 'yaml' + newobj = YAML.load(newcontent) + end + need_edit = false + rescue Exception => e + puts "Parse error! " + e.to_s + n = 1 + newcontent.each_line do |line| + puts "#{n.to_s.rjust 4} #{line}" + n += 1 + end + puts "\nTry again (y/n)? " + yn = "X" + while not ["y", "Y", "n", "N"].include?(yn) + yn = $stdin.read 1 + end + if yn == 'n' or yn == 'N' + arv_edit_save_tmp tmp + abort + end + end + else + puts "Editor exited with status #{$?.exitstatus}" + exit $?.exitstatus end - tmp.close - tmp.unlink + end + begin if newobj != results api_method = 'arvados.' + rsc + '.update' - result = client.execute(:api_method => eval(api_method), - :parameters => {"uuid" => n, rsc.singularize => Oj.dump(newobj)}, - :authenticated => false, - :headers => { - authorization: 'OAuth2 '+ENV['ARVADOS_API_TOKEN'] - }) + dumped = Oj.dump(newobj) + + begin + result = client.execute(:api_method => eval(api_method), + :parameters => {"uuid" => uuid}, + :body => { rsc.singularize => dumped }, + :authenticated => false, + :headers => { + authorization: 'OAuth2 '+ENV['ARVADOS_API_TOKEN'] + }) + rescue Exception => e + puts "Error communicating with server, error was #{e}" + puts "Update body was:" + puts dumped + arv_edit_save_tmp tmp + abort + end begin results = JSON.parse result.body @@ -246,10 +306,16 @@ def arv_edit client, arvados, global_opts, remaining_opts if result.response.status != 200 puts "Update failed. Server responded #{result.response.status}: #{results['errors']} " + puts "Update body was:" + puts dumped + arv_edit_save_tmp tmp + abort end else puts "Object is unchanged, did not update." end + ensure + tmp.close(true) end exit 0 @@ -352,7 +418,8 @@ def parse_arguments(discovery_document, subcommands) resource = ARGV.shift if not subcommands.include? resource - if global_opts[:resources] or not resource_types.include?(resource) + if not resource_types.include?(resource) + puts "Resource or subcommand '#{resource}' is not recognized.\n\n" if !resource.nil? help_resources(option_parser, discovery_document, resource) end