X-Git-Url: https://git.arvados.org/arvados.git/blobdiff_plain/b1c303302dbac2ea6e9ab6775aecaf661b5da9c8..c51e59e03bc721de2837db7958415766bd7b46c8:/sdk/ruby/lib/arvados.rb diff --git a/sdk/ruby/lib/arvados.rb b/sdk/ruby/lib/arvados.rb index 429777e73f..63550cd37c 100644 --- a/sdk/ruby/lib/arvados.rb +++ b/sdk/ruby/lib/arvados.rb @@ -1,31 +1,45 @@ +# Copyright (C) The Arvados Authors. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + require 'rubygems' -require 'google/api_client' require 'active_support/inflector' require 'json' require 'fileutils' require 'andand' +require 'net/http' + +require 'arvados/google_api_client' ActiveSupport::Inflector.inflections do |inflect| inflect.irregular 'specimen', 'specimens' inflect.irregular 'human', 'humans' end -module Kernel - def suppress_warnings - original_verbosity = $VERBOSE - $VERBOSE = nil - result = yield - $VERBOSE = original_verbosity - return result - end -end - class Arvados + class ArvadosClient < Google::APIClient + attr_reader :request_id + + def execute(*args) + @request_id = "req-" + Random.new.rand(2**128).to_s(36)[0..19] + if args.last.is_a? Hash + args.last[:headers] ||= {} + args.last[:headers]['X-Request-Id'] = @request_id + end + begin + super(*args) + rescue => e + if !e.message.match(/.*req-[0-9a-zA-Z]{20}.*/) + raise $!, "#{$!} (Request ID: #{@request_id})", $!.backtrace + end + raise e + end + end + end class TransactionFailedError < StandardError end - @@config = nil @@debuglevel = 0 class << self attr_accessor :debuglevel @@ -37,12 +51,16 @@ class Arvados @arvados_api_version = opts[:api_version] || 'v1' - @arvados_api_host = opts[:api_host] || - config['ARVADOS_API_HOST'] or - raise "#{$0}: no :api_host or ENV[ARVADOS_API_HOST] provided." - @arvados_api_token = opts[:api_token] || - config['ARVADOS_API_TOKEN'] or - raise "#{$0}: no :api_token or ENV[ARVADOS_API_TOKEN] provided." + @config = nil + [[:api_host, 'ARVADOS_API_HOST'], + [:api_token, 'ARVADOS_API_TOKEN']].each do |op, en| + if opts[op] + config[en] = opts[op] + end + if !config[en] + raise "#{$0}: no :#{op} or ENV[#{en}] provided." + end + end if (opts[:suppress_ssl_warnings] or %w(1 true yes).index(config['ARVADOS_API_HOST_INSECURE']. @@ -58,7 +76,7 @@ class Arvados _arvados = self namespace_class = Arvados.const_set "A#{self.object_id}", Class.new self.arvados_api.schemas.each do |classname, schema| - next if classname.match /List$/ + next if classname.match(/List$/) klass = Class.new(Arvados::Model) do def self.arvados @arvados @@ -97,39 +115,15 @@ class Arvados # result looks like Arvados::A26949680::Job. namespace_class.const_set classname, klass - self.class.class_eval do - define_method classname.underscore do - klass - end + self.define_singleton_method classname.underscore do + klass end end end - class Google::APIClient - def discovery_document(api, version) - api = api.to_s - 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 - 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 - end - end - - File.open(cached_doc) { |f| JSON.load f } - end - end - end - def client - @client ||= Google::APIClient. - new(:host => @arvados_api_host, + @client ||= ArvadosClient. + new(:host => config["ARVADOS_API_HOST"], :application_name => @application_name, :application_version => @application_version.to_s) end @@ -143,11 +137,11 @@ class Arvados end def debuglog *args - self.class.debuglog *args + self.class.debuglog(*args) end def config(config_file_path="~/.config/arvados/settings.conf") - return @@config if @@config + return @config if @config # Initialize config settings with environment variables. config = {} @@ -165,7 +159,7 @@ class Arvados # Note: If we start using additional configuration settings from # this file in the future, we might have to read the file anyway # instead of returning here. - return (@@config = config) + return (@config = config) end begin @@ -192,7 +186,16 @@ class Arvados debuglog "Ignoring error reading #{config_file_path}: #{e}", 0 end - @@config = config + @config = config + end + + def cluster_config + return @cluster_config if @cluster_config + + uri = URI("https://#{config()["ARVADOS_API_HOST"]}/arvados/v1/config") + cc = JSON.parse(Net::HTTP.get(uri)) + + @cluster_config = cc end class Model @@ -203,10 +206,10 @@ class Arvados arvados.client end def self.debuglog(*args) - arvados.class.debuglog *args + arvados.class.debuglog(*args) end def debuglog(*args) - self.class.arvados.class.debuglog *args + self.class.arvados.class.debuglog(*args) end def self.api_exec(method, parameters={}) api_method = arvados_api.send(api_models_sym).send(method.name.to_sym) @@ -228,17 +231,20 @@ class Arvados execute(:api_method => api_method, :authenticated => false, :parameters => parameters, - :body => body, + :body_object => body, :headers => { - authorization: 'OAuth2 '+arvados.config['ARVADOS_API_TOKEN'] + :authorization => 'Bearer '+arvados.config['ARVADOS_API_TOKEN'] }) resp = JSON.parse result.body, :symbolize_names => true if resp[:errors] + if !resp[:errors][0].match(/.*req-[0-9a-zA-Z]{20}.*/) + resp[:errors][0] += " (#{result.headers['X-Request-Id'] or client.request_id})" + end raise Arvados::TransactionFailedError.new(resp[:errors]) elsif resp[:uuid] and resp[:etag] self.new(resp) elsif resp[:items].is_a? Array - resp.merge(items: resp[:items].collect do |i| + resp.merge(:items => resp[:items].collect do |i| self.new(i) end) else @@ -283,4 +289,16 @@ class Arvados @attributes = j end end + + protected + + def suppress_warnings + original_verbosity = $VERBOSE + begin + $VERBOSE = nil + yield + ensure + $VERBOSE = original_verbosity + end + end end