Module: Rightscale::RightGogridInterface

Included in:
Gogrid
Defined in:
lib/gogrid_base.rb

Constant Summary collapse

DEFAULT_GOGRID_URL =
'https://api.gogrid.com/api'
DEFAULT_VERSION =
'1.0'
DEFAULT_FORMAT =
'json'
GOGRID_PROBLEMS =

If found in an error message returned by Gogrid, these phrases indicate a transient error. Transient errors are automatically retried with exponential back-off.

[ #'Forbidden',
 'internal service error',
 'is currently unavailable',
 'no response from',
 'Please try again',
 'InternalError',
 'ServiceUnavailable', 
 'Unavailable',
 'This application is not currently available',
 'InsufficientInstanceCapacity'
]
@@gogrid_problems =

TODO: gather more Gogrid errors here

GOGRID_PROBLEMS
@@caching =
false
@@bench =
GogridBenchmarkingBlock.new

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#cacheObject (readonly)

Cache



115
116
117
# File 'lib/gogrid_base.rb', line 115

def cache
  @cache
end

#connectionObject (readonly)

RightHttpConnection instance



113
114
115
# File 'lib/gogrid_base.rb', line 113

def connection
  @connection
end

#gogrid_api_keyObject (readonly)

Current Gogrid API key



99
100
101
# File 'lib/gogrid_base.rb', line 99

def gogrid_api_key
  @gogrid_api_key
end

#gogrid_secretObject (readonly)

Current Gogrid secret key



101
102
103
# File 'lib/gogrid_base.rb', line 101

def gogrid_secret
  @gogrid_secret
end

#last_errorsObject

Last Gogrid errors list (used by GogridErrorHandler)



107
108
109
# File 'lib/gogrid_base.rb', line 107

def last_errors
  @last_errors
end

#last_requestObject (readonly)

Last HTTP request object



103
104
105
# File 'lib/gogrid_base.rb', line 103

def last_request
  @last_request
end

#last_responseObject (readonly)

Last HTTP response object



105
106
107
# File 'lib/gogrid_base.rb', line 105

def last_response
  @last_response
end

#loggerObject

Logger object



109
110
111
# File 'lib/gogrid_base.rb', line 109

def logger
  @logger
end

#paramsObject

Initial params hash



111
112
113
# File 'lib/gogrid_base.rb', line 111

def params
  @params
end

Class Method Details

.bench_gogridObject



94
95
96
# File 'lib/gogrid_base.rb', line 94

def self.bench_gogrid
  @@bench.service
end

.bench_parserObject



91
92
93
# File 'lib/gogrid_base.rb', line 91

def self.bench_parser
  @@bench.parser
end

.cachingObject



83
84
85
# File 'lib/gogrid_base.rb', line 83

def self.caching
  @@caching
end

.caching=(caching) ⇒ Object



86
87
88
# File 'lib/gogrid_base.rb', line 86

def self.caching=(caching)
  @@caching = caching
end

.gogrid_problemsObject

Returns a list of Gogrid responses which are known to be transient problems. We have to re-request if we get any of them, because the problem will probably disappear. By default this method returns the same value as the GOGRID_PROBLEMS const.



78
79
80
# File 'lib/gogrid_base.rb', line 78

def self.gogrid_problems
  @@gogrid_problems
end

Instance Method Details

#cache_hits?(function, response, do_raise = :raise) ⇒ Boolean

Check if the gogrid function response hits the cache or not. If the cache hits:

  • raises an GogridNoChange exception if do_raise == :raise.

  • returnes parsed response from the cache if it exists or true otherwise.

If the cache miss or the caching is off then returns false.

Returns:

  • (Boolean)


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
# File 'lib/gogrid_base.rb', line 276

def cache_hits?(function, response, do_raise=:raise) # :nodoc:
  result = false
  if caching?
    function     = function.to_sym
    response_md5 = MD5.md5(response).to_s
    # well, the response is new, reset cache data
    unless @cache[function] && @cache[function][:response_md5] == response_md5
      update_cache(function, {:response_md5 => response_md5,
                              :timestamp    => Time.now,
                              :hits         => 0,
                              :parsed       => nil})
    else
      # aha, cache hits, update the data and throw an exception if needed
      @cache[function][:hits] += 1
      if do_raise == :raise
        raise(GogridNoChange, "Cache hit: #{function} response has not changed since "+
                              "#{@cache[function][:timestamp].strftime('%Y-%m-%d %H:%M:%S')}, "+
                              "hits: #{@cache[function][:hits]}.")
      else
        result = @cache[function][:parsed] || true
      end
    end
  end
  result
end

#caching?Boolean

Returns true if the describe_xxx responses are being cached

Returns:

  • (Boolean)


267
268
269
# File 'lib/gogrid_base.rb', line 267

def caching?
  @params.key?(:cache) ? @params[:cache] : @@caching
end

#cgi_escape_params(params) ⇒ Object

:nodoc:



160
161
162
# File 'lib/gogrid_base.rb', line 160

def cgi_escape_params(params) # :nodoc:
  params.map {|k,v| "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}" }.join("&")
end

#do_request(path, params = {}, non_unique_params = {}) ⇒ Object


request generation and processing




172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/gogrid_base.rb', line 172

def do_request(path, params={}, non_unique_params={}) # :nodoc:
  request_hash = generate_request(path, params, non_unique_params)
  # create a dafault response parser
  case request_hash[:format]
  when 'json' then parser = GogridJsonParser.new
#      when 'xml'
#      when 'csv'
  else             raise "Unsupported request format: #{params['format']}"
  end
  # perform a request
  request_info(request_hash, parser)
end

#generate_request(path, params = {}, non_unique_params = {}) ⇒ Object

Generate a handy request hash.



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/gogrid_base.rb', line 186

def generate_request(path, params={}, non_unique_params={}) # :nodoc:
  # default request params
  params = { 'format'  => DEFAULT_FORMAT,
             'v'       => DEFAULT_VERSION,
             'sig'     => signature,
             'api_key' => @gogrid_api_key}.merge(params)
  # encode key/values
  normal_params = cgi_escape_params(params)
  # add the non_unique params at the end if we've received some
  other_params = non_unique_params.collect do |i|
    k = i.keys[0];
    v = i.values[0];
    "#{CGI.escape(k.to_s)}=#{CGI.escape(v.to_s)}"
  end.join("&")
  other_params = "&#{other_params}" unless other_params.blank?
  # create url and request
  request_url = "#{@params[:service]}/#{path}?#{normal_params}#{other_params}"
  request     = Net::HTTP::Get.new(request_url)
  # prepare output hash
  { :request  => request,
    :server   => @params[:server],
    :port     => @params[:port],
    :protocol => @params[:protocol],
    :format   => params['format'] }
end

#init(gogrid_api_key, gogrid_secret, params = {}) ⇒ Object

Params:

:gogrid_url
:logger
:multi_thread


124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/gogrid_base.rb', line 124

def init(gogrid_api_key, gogrid_secret, params={}) #:nodoc:
  @params = params
  @cache  = {}
  @error_handler = nil
  # deny working without credentials
  if gogrid_api_key.blank? || gogrid_secret.blank?
    raise GogridError.new("GoGrid api and secret keys are required to operate on GoGrid API service")
  end
  @gogrid_api_key = gogrid_api_key
  @gogrid_secret  = gogrid_secret
  # parse Gogrid URL
  @params[:gogrid_url] ||= ENV['GOGRID_URL'] || DEFAULT_GOGRID_URL
  @params[:server]       = URI.parse(@params[:gogrid_url]).host
  @params[:port]         = URI.parse(@params[:gogrid_url]).port
  @params[:service]      = URI.parse(@params[:gogrid_url]).path
  @params[:protocol]     = URI.parse(@params[:gogrid_url]).scheme
  # other params
  @params[:multi_thread] ||= defined?(GOGRID_DAEMON)
  @logger = @params[:logger] || (defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER) || Logger.new(STDOUT)
  @logger.info "New #{self.class.name} using #{@params[:multi_thread] ? 'multi' : 'single'}-threaded mode"
end

#multi_threadObject

Return true if this instance works in multi_thread mode and false otherwise.



156
157
158
# File 'lib/gogrid_base.rb', line 156

def multi_thread
  @params[:multi_thread]
end

#on_exception(options = {:raise=>true, :log=>true}) ⇒ Object

:nodoc:



146
147
148
149
# File 'lib/gogrid_base.rb', line 146

def on_exception(options={:raise=>true, :log=>true}) # :nodoc:
  raise if $!.is_a?(GogridNoChange)
  GogridError::on_gogrid_exception(self, options)
end

#request_cache_or_info(method, request_hash, parser_class, use_cache = true) ⇒ Object

Perform a request. Skips a response parsing if caching is used.



250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
# File 'lib/gogrid_base.rb', line 250

def request_cache_or_info(method, request_hash, parser_class, use_cache=true) #:nodoc:
  # We do not want to break the logic of parsing hence will use a dummy parser to process all the standard
  # steps (errors checking etc). The dummy parser does nothig - just returns back the params it received.
  # If the caching is enabled and hit then throw  GogridNoChange.
  # P.S. caching works for the whole images list only! (when the list param is blank)
  response = request_info(request_hash, GogridDummyParser.new)
  # check cache
  cache_hits?(method.to_sym, response.body) if use_cache
  result = nil
  @@bench.parser.add!{ result = parser_class.new.parse(response) }
  result = yield(result) if block_given?
  # update parsed data
  update_cache(method.to_sym, :parsed => result) if use_cache
  result
end

#request_info(request, parser) ⇒ Object

Perform a request. (4xx and 5xx error handling is being made through GogridErrorHandler)



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/gogrid_base.rb', line 214

def request_info(request, parser)  #:nodoc:
  # check single/multi threading mode
  thread = @params[:multi_thread] ? Thread.current : Thread.main
  # create a connection if needed
  thread[:ec2_connection] ||= Rightscale::HttpConnection.new(:exception => GogridError, :logger => @logger)
  @connection    = thread[:ec2_connection]
  @last_request  = request[:request]
  @last_response = nil
  # perform a request
  @@bench.service.add!{ @last_response = @connection.request(request) }
  # check response for success...
  if @last_response.is_a?(Net::HTTPSuccess)
    @error_handler = nil
    result         = nil
    @@bench.parser.add! { result = parser.parse(@last_response) }
    return result
  else
    @error_handler ||= GogridErrorHandler.new(self, parser, :errors_list => @@gogrid_problems)
    check_result     = @error_handler.check(request)
    if check_result
      @error_handler = nil
      return check_result
    end
    raise GogridError.new(@last_errors, @last_response.code)
  end
rescue
  @error_handler = nil
  raise
end

#signatureObject

:nodoc:



164
165
166
# File 'lib/gogrid_base.rb', line 164

def signature # :nodoc:
  Digest::MD5.hexdigest("#{@gogrid_api_key}#{@gogrid_secret}#{'%.0f'%Time.new.to_f}")
end

#update_cache(function, hash) ⇒ Object

:nodoc:



302
303
304
# File 'lib/gogrid_base.rb', line 302

def update_cache(function, hash) # :nodoc:
  (@cache[function.to_sym] ||= {}).merge!(hash) if caching?
end