Project

General

Profile

Download (7.08 KB) Statistics
| Branch: | Tag: | Revision:

runcible / lib / runcible / base.rb @ ba8278b5

1
# Copyright (c) 2012 Red Hat
2
#
3
# MIT License
4
#
5
# Permission is hereby granted, free of charge, to any person obtaining
6
# a copy of this software and associated documentation files (the
7
# "Software"), to deal in the Software without restriction, including
8
# without limitation the rights to use, copy, modify, merge, publish,
9
# distribute, sublicense, and/or sell copies of the Software, and to
10
# permit persons to whom the Software is furnished to do so, subject to
11
# the following conditions:
12
#
13
# The above copyright notice and this permission notice shall be
14
# included in all copies or substantial portions of the Software.
15
#
16
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23

    
24
require 'rest_client'
25
require 'oauth'
26
require 'json'
27
require 'thread'
28

    
29

    
30
module Runcible
31
  class Base
32

    
33
    def initialize(config={})
34
      @mutex = Mutex.new
35
      @config = config
36
    end
37

    
38
    def lazy_config=(blk)
39
      @mutex.synchronize { @lazy_config = blk }
40
    end
41

    
42
    def config
43
      @mutex.synchronize do
44
        @config = @lazy_config.call if defined?(@lazy_config)
45
        raise Runcible::ConfigurationUndefinedError, Runcible::ConfigurationUndefinedError.message unless @config
46
        @config
47
      end
48
    end
49

    
50
    def path(*args)
51
      self.class.path(*args)
52
    end
53

    
54
    def call(method, path, options={})
55
      clone_config = self.config.clone
56
      #on occation path will already have prefix (sync cancel)
57
      path = clone_config[:api_path] + path if !path.start_with?(clone_config[:api_path])
58

    
59
      RestClient.log    = []
60
      logger            = clone_config[:logging][:logger]
61
      debug_logging     = clone_config[:logging][:debug]
62
      exception_logging = clone_config[:logging][:exception]
63

    
64
      headers = clone_config[:headers].clone
65

    
66
      get_params = options[:params] if options[:params]
67
      path = combine_get_params(path, get_params) if get_params
68

    
69
      client_options = {}
70
      client_options[:timeout] =  clone_config[:timeout] if clone_config[:timeout]
71
      client_options[:open_timeout] =  clone_config[:open_timeout] if clone_config[:open_timeout]
72

    
73
      if clone_config[:oauth]
74
        headers = add_oauth_header(method, path, headers) if clone_config[:oauth]
75
        headers["pulp-user"] = clone_config[:user]
76
        client = RestClient::Resource.new(clone_config[:url], client_options)
77
      else
78
        client_options[:user] =  clone_config[:user]
79
        client_options[:password] = config[:http_auth][:password]
80
        client = RestClient::Resource.new(clone_config[:url], client_options)
81
      end
82

    
83
      args = [method]
84
      args << generate_payload(options) if [:post, :put].include?(method)
85
      args << headers 
86

    
87
      response = get_response(client, path, *args)
88
      process_response(response)
89

    
90
    rescue => e
91
      log_exception
92
      raise e
93
    end
94

    
95
    def get_response(client, path, *args)
96
      client[path].send(*args) do |response, request, result, &block|
97
        resp = response.return!(request, result)
98
        log_debug
99
        return resp
100
      end
101
    end
102

    
103
    def combine_get_params(path, params)
104
      query_string  = params.collect do |k, v|
105
        if v.is_a? Array
106
          v.collect{|y| "#{k.to_s}=#{y.to_s}" }.join('&')
107
        else
108
          "#{k.to_s}=#{v.to_s}"
109
        end
110
      end.flatten().join('&')
111
      path + "?#{query_string}"
112
    end
113

    
114
    def generate_payload(options)
115
      if options[:payload]
116
        if options[:payload][:optional]
117
          if options[:payload][:required]
118
            payload = options[:payload][:required].merge(options[:payload][:optional])
119
          else
120
            payload = options[:payload][:optional]
121
          end
122
        elsif options[:payload][:delta]
123
          payload = options[:payload]
124
        else
125
          payload = options[:payload][:required]
126
        end
127
      else
128
        payload = {}
129
      end
130

    
131
      return payload.to_json
132
    end
133

    
134
    def process_response(response)
135
      begin
136
        body = JSON.parse(response.body)
137
        if body.respond_to? :with_indifferent_access
138
          body = body.with_indifferent_access
139
        elsif body.is_a? Array
140
          body = body.collect  do |i|
141
            i.respond_to?(:with_indifferent_access) ? i.with_indifferent_access : i
142
          end
143
        end
144
        response = RestClient::Response.create(body, response.net_http_res, response.args)
145
      rescue JSON::ParserError
146
      end
147

    
148
      return response
149
    end
150

    
151
    def required_params(local_names, binding, keys_to_remove=[])
152
      local_names = local_names.reduce({}) do |acc, v|
153
        value = binding.eval(v.to_s) unless v == :_
154
        acc[v] = value unless value.nil?
155
        acc
156
      end
157

    
158
      #The double delete is to support 1.8.7 and 1.9.3
159
      local_names.delete(:payload)
160
      local_names.delete(:optional)
161
      local_names.delete("payload")
162
      local_names.delete("optional")
163
      keys_to_remove.each do |key|
164
        local_names.delete(key)
165
        local_names.delete(key.to_sym)
166
      end
167

    
168
      return local_names
169
    end
170

    
171
    def add_http_auth_header
172
      return {:user => config[:user], :password => config[:http_auth][:password]}
173
    end
174

    
175
    def add_oauth_header(method, path, headers)
176
      default_options = { :site               => config[:url],
177
                          :http_method        => method,
178
                          :request_token_path => "",
179
                          :authorize_path     => "",
180
                          :access_token_path  => "" }
181

    
182
      default_options[:ca_file] = config[:ca_cert_file] unless config[:ca_cert_file].nil?
183
      consumer = OAuth::Consumer.new(config[:oauth][:oauth_key], config[:oauth][:oauth_secret], default_options)
184

    
185
      method_to_http_request = { :get    => Net::HTTP::Get,
186
                                 :post   => Net::HTTP::Post,
187
                                 :put    => Net::HTTP::Put,
188
                                 :delete => Net::HTTP::Delete }
189

    
190
      http_request = method_to_http_request[method].new(path)
191
      consumer.sign!(http_request)
192

    
193
      headers['Authorization'] = http_request['Authorization']
194
      return headers
195
    end
196

    
197
    def log_debug
198
      if self.config[:logging][:debug]
199
        log_message = generate_log_message                  
200
        self.config[:logging][:logger].debug(log_message)
201
      end
202
    end
203

    
204
    def log_exception
205
      if self.config[:logging][:exception]
206
        log_message = generate_log_message
207
        self.config[:logging][:logger].error(log_message)
208
      end
209
    end
210

    
211
    def generate_log_message
212
      RestClient.log.join('\n')
213
    end
214

    
215
    def logger
216
      self.config[:logging][:logger]
217
    end
218

    
219
  end 
220

    
221
  class ConfigurationUndefinedError < StandardError
222

    
223
    def self.message
224
      # override me to change the error message
225
      "Configuration not set. Runcible::Base.config= must be called before Runcible::Base.config."
226
    end
227
  end
228
end