Class: Chef::REST
Defined Under Namespace
Classes: CookieJar
Instance Attribute Summary collapse
-
#client_name ⇒ Object
Returns the value of attribute client_name.
-
#cookies ⇒ Object
Returns the value of attribute cookies.
-
#sign_on_redirect ⇒ Object
Returns the value of attribute sign_on_redirect.
-
#sign_request(http_method, private_key, user_id, body = "", host = "localhost") ⇒ Object
Returns the value of attribute sign_request.
-
#signing_key ⇒ Object
Returns the value of attribute signing_key.
-
#signing_key_filename ⇒ Object
Returns the value of attribute signing_key_filename.
-
#url ⇒ Object
Returns the value of attribute url.
Instance Method Summary collapse
- #create_url(path) ⇒ Object
-
#delete_rest(path, headers = {}) ⇒ Object
Send an HTTP DELETE request to the path.
-
#get_rest(path, raw = false, headers = {}) ⇒ Object
Send an HTTP GET request to the path.
-
#initialize(url, client_name = Chef::Config[:node_name], signing_key_filename = Chef::Config[:client_key]) ⇒ REST
constructor
A new instance of REST.
- #load_signing_key(key) ⇒ Object
-
#post_rest(path, json, headers = {}) ⇒ Object
Send an HTTP POST request to the path.
-
#put_rest(path, json, headers = {}) ⇒ Object
Send an HTTP PUT request to the path.
-
#register(name = Chef::Config[:node_name], destination = Chef::Config[:client_key]) ⇒ Object
Register the client.
-
#run_request(method, url, headers = {}, data = false, limit = 10, raw = false) ⇒ Object
Actually run an HTTP request.
Constructor Details
#initialize(url, client_name = Chef::Config[:node_name], signing_key_filename = Chef::Config[:client_key]) ⇒ REST
Returns a new instance of REST.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/chef/rest.rb', line 42 def initialize(url, client_name=Chef::Config[:node_name], signing_key_filename=Chef::Config[:client_key]) @url = url @cookies = CookieJar.instance @client_name = client_name if signing_key_filename @signing_key_filename = signing_key_filename @signing_key = load_signing_key(signing_key_filename) @sign_request = true else @signing_key = nil @sign_request = false end @sign_on_redirect = true end |
Instance Attribute Details
#client_name ⇒ Object
Returns the value of attribute client_name.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def client_name @client_name end |
#cookies ⇒ Object
Returns the value of attribute cookies.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def @cookies end |
#sign_on_redirect ⇒ Object
Returns the value of attribute sign_on_redirect.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def sign_on_redirect @sign_on_redirect end |
#sign_request(http_method, private_key, user_id, body = "", host = "localhost") ⇒ Object
Returns the value of attribute sign_request.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def sign_request @sign_request end |
#signing_key ⇒ Object
Returns the value of attribute signing_key.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def signing_key @signing_key end |
#signing_key_filename ⇒ Object
Returns the value of attribute signing_key_filename.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def signing_key_filename @signing_key_filename end |
#url ⇒ Object
Returns the value of attribute url.
40 41 42 |
# File 'lib/chef/rest.rb', line 40 def url @url end |
Instance Method Details
#create_url(path) ⇒ Object
117 118 119 120 121 122 123 |
# File 'lib/chef/rest.rb', line 117 def create_url(path) if path =~ /^(http|https):\/\// URI.parse(path) else URI.parse("#{@url}/#{path}") end end |
#delete_rest(path, headers = {}) ⇒ Object
Send an HTTP DELETE request to the path
103 104 105 |
# File 'lib/chef/rest.rb', line 103 def delete_rest(path, headers={}) run_request(:DELETE, create_url(path), headers) end |
#get_rest(path, raw = false, headers = {}) ⇒ Object
Send an HTTP GET request to the path
Parameters
- path
-
The path to GET
- raw
-
Whether you want the raw body returned, or JSON inflated. Defaults
to JSON inflated.
98 99 100 |
# File 'lib/chef/rest.rb', line 98 def get_rest(path, raw=false, headers={}) run_request(:GET, create_url(path), headers, false, 10, raw) end |
#load_signing_key(key) ⇒ Object
57 58 59 60 61 62 63 |
# File 'lib/chef/rest.rb', line 57 def load_signing_key(key) if File.exists?(key) && File.readable?(key) IO.read(key) else raise Chef::Exceptions::PrivateKeyMissing, "I cannot find #{key}, which you told me to use to sign requests!" end end |
#post_rest(path, json, headers = {}) ⇒ Object
Send an HTTP POST request to the path
108 109 110 |
# File 'lib/chef/rest.rb', line 108 def post_rest(path, json, headers={}) run_request(:POST, create_url(path), headers, json) end |
#put_rest(path, json, headers = {}) ⇒ Object
Send an HTTP PUT request to the path
113 114 115 |
# File 'lib/chef/rest.rb', line 113 def put_rest(path, json, headers={}) run_request(:PUT, create_url(path), headers, json) end |
#register(name = Chef::Config[:node_name], destination = Chef::Config[:client_key]) ⇒ Object
Register the client
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/chef/rest.rb', line 66 def register(name=Chef::Config[:node_name], destination=Chef::Config[:client_key]) if File.exists?(destination) raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination} - check permissions?" unless File.writable?(destination) end nc = Chef::ApiClient.new nc.name(name) response = nc.save(true, true) Chef::Log.debug("Registration response: #{response.inspect}") raise Chef::Exceptions::CannotWritePrivateKey, "The response from the server did not include a private key!" unless response.has_key?("private_key") begin # Write out the private key file = File.open(destination, "w") file.print(response["private_key"]) file.close rescue raise Chef::Exceptions::CannotWritePrivateKey, "I cannot write your private key to #{destination}" end true end |
#run_request(method, url, headers = {}, data = false, limit = 10, raw = false) ⇒ Object
Actually run an HTTP request. First argument is the HTTP method, which should be one of :GET, :PUT, :POST or :DELETE. Next is the URL, then an object to include in the body (which will be converted with .to_json) and finally, the limit of HTTP Redirects to follow (10).
Typically, you won’t use this method – instead, you’ll use one of the helper methods (get_rest, post_rest, etc.)
Will return the body of the response on success.
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 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 211 212 213 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 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 |
# File 'lib/chef/rest.rb', line 146 def run_request(method, url, headers={}, data=false, limit=10, raw=false) http_retry_delay = Chef::Config[:http_retry_delay] http_retry_count = Chef::Config[:http_retry_count] raise ArgumentError, 'HTTP redirect too deep' if limit == 0 http = Net::HTTP.new(url.host, url.port) if url.scheme == "https" http.use_ssl = true if Chef::Config[:ssl_verify_mode] == :verify_none http.verify_mode = OpenSSL::SSL::VERIFY_NONE elsif Chef::Config[:ssl_verify_mode] == :verify_peer http.verify_mode = OpenSSL::SSL::VERIFY_PEER end if Chef::Config[:ssl_ca_path] and File.exists?(Chef::Config[:ssl_ca_path]) http.ca_path = Chef::Config[:ssl_ca_path] elsif Chef::Config[:ssl_ca_file] and File.exists?(Chef::Config[:ssl_ca_file]) http.ca_file = Chef::Config[:ssl_ca_file] end if Chef::Config[:ssl_client_cert] && File.exists?(Chef::Config[:ssl_client_cert]) http.cert = OpenSSL::X509::Certificate.new(File.read(Chef::Config[:ssl_client_cert])) http.key = OpenSSL::PKey::RSA.new(File.read(Chef::Config[:ssl_client_key])) end end http.read_timeout = Chef::Config[:rest_timeout] unless raw headers = headers.merge({ 'Accept' => "application/json", }) end if @cookies.has_key?("#{url.host}:#{url.port}") headers['Cookie'] = @cookies["#{url.host}:#{url.port}"] end json_body = data ? data.to_json : nil if @sign_request Chef::Log.debug("Signing the request as #{@client_name}") if json_body headers.merge!(sign_request(method, OpenSSL::PKey::RSA.new(@signing_key), @client_name, json_body, "#{url.host}:#{url.port}")) else headers.merge!(sign_request(method, OpenSSL::PKey::RSA.new(@signing_key), @client_name, "", "#{url.host}:#{url.port}")) end end req = nil case method when :GET req_path = "#{url.path}" req_path << "?#{url.query}" if url.query req = Net::HTTP::Get.new(req_path, headers) when :POST headers["Content-Type"] = 'application/json' if data req_path = "#{url.path}" req_path << "?#{url.query}" if url.query req = Net::HTTP::Post.new(req_path, headers) req.body = json_body if json_body when :PUT headers["Content-Type"] = 'application/json' if data req_path = "#{url.path}" req_path << "?#{url.query}" if url.query req = Net::HTTP::Put.new(req_path, headers) req.body = json_body if json_body when :DELETE req_path = "#{url.path}" req_path << "?#{url.query}" if url.query req = Net::HTTP::Delete.new(req_path, headers) else raise ArgumentError, "You must provide :GET, :PUT, :POST or :DELETE as the method" end Chef::Log.debug("Sending HTTP Request via #{req.method} to #{url.host}:#{url.port}#{req.path}") # Optionally handle HTTP Basic Authentication req.basic_auth(url.user, url.password) if url.user res = nil tf = nil http_retries = 1 begin res = http.request(req) do |response| if raw tf = Tempfile.new("chef-rest") # Stolen from http://www.ruby-forum.com/topic/166423 # Kudos to _why! size, total = 0, response.header['Content-Length'].to_i response.read_body do |chunk| tf.write(chunk) size += chunk.size if size == 0 Chef::Log.debug("#{req.path} done (0 length file)") elsif total == 0 Chef::Log.debug("#{req.path} (zero content length)") else Chef::Log.debug("#{req.path}" + " %d%% done (%d of %d)" % [(size * 100) / total, size, total]) end end tf.close tf else response.read_body end response end rescue Errno::ECONNREFUSED => e Chef::Log.error("Connection refused connecting to #{url.host}:#{url.port} for #{req.path} #{http_retries}/#{http_retry_count}") sleep(http_retry_delay) retry if (http_retries += 1) < http_retry_count raise Errno::ECONNREFUSED, "Connection refused connecting to #{url.host}:#{url.port} for #{req.path}, giving up" rescue Timeout::Error Chef::Log.error("Timeout connecting to #{url.host}:#{url.port} for #{req.path}, retry #{http_retries}/#{http_retry_count}") sleep(http_retry_delay) retry if (http_retries += 1) < http_retry_count raise Timeout::Error, "Timeout connecting to #{url.host}:#{url.port} for #{req.path}, giving up" end if res.kind_of?(Net::HTTPSuccess) if res['set-cookie'] @cookies["#{url.host}:#{url.port}"] = res['set-cookie'] end if res['content-type'] =~ /json/ response_body = res.body.chomp JSON.parse(response_body) else if raw tf else res.body end end elsif res.kind_of?(Net::HTTPFound) or res.kind_of?(Net::HTTPMovedPermanently) if res['set-cookie'] @cookies["#{url.host}:#{url.port}"] = res['set-cookie'] end @sign_request = false if @sign_on_redirect == false run_request(:GET, create_url(res['location']), {}, false, limit - 1, raw) else if res['content-type'] =~ /json/ exception = JSON.parse(res.body) Chef::Log.warn("HTTP Request Returned #{res.code} #{res.}: #{exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"]}") end res.error! end end |