diff options
author | John Keiser <jkeiser@opscode.com> | 2013-10-11 13:57:07 -0700 |
---|---|---|
committer | John Keiser <jkeiser@opscode.com> | 2013-10-11 13:57:07 -0700 |
commit | 12b902d5aa539f14ee2e25f1a1ca702f8fb81377 (patch) | |
tree | 6db3aef50f1f3e823cd928491796a09c40a822e0 /lib/chef | |
parent | 6d58ff931dda2d5bfa0eb8b7feadf5cd0fb37c8e (diff) | |
parent | 5e5f5b1b1e431612a40bf235c4081dc5ff885bef (diff) | |
download | chef-12b902d5aa539f14ee2e25f1a1ca702f8fb81377.tar.gz |
Use Chef::HTTP for knife-essentials raw requests, fix CHEF-4515 in the balance
Diffstat (limited to 'lib/chef')
22 files changed, 262 insertions, 170 deletions
diff --git a/lib/chef/chef_fs/file_system/acl_entry.rb b/lib/chef/chef_fs/file_system/acl_entry.rb index 0be9076038..8edc02d5c5 100644 --- a/lib/chef/chef_fs/file_system/acl_entry.rb +++ b/lib/chef/chef_fs/file_system/acl_entry.rb @@ -40,7 +40,7 @@ class Chef acls = data_handler.normalize(JSON.parse(file_contents, :create_additions => false), self) PERMISSIONS.each do |permission| begin - rest.put_rest("#{api_path}/#{permission}", { permission => acls[permission] }) + rest.put("#{api_path}/#{permission}", { permission => acls[permission] }) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Timeout writing: #{e}" rescue Net::HTTPServerException => e diff --git a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb index 98f90e3a68..0083ee4cfa 100644 --- a/lib/chef/chef_fs/file_system/chef_server_root_dir.rb +++ b/lib/chef/chef_fs/file_system/chef_server_root_dir.rb @@ -16,6 +16,7 @@ # limitations under the License. # +require 'chef/server_api' require 'chef/chef_fs/file_system/acls_dir' require 'chef/chef_fs/file_system/base_fs_dir' require 'chef/chef_fs/file_system/rest_list_dir' @@ -23,7 +24,6 @@ require 'chef/chef_fs/file_system/cookbooks_dir' require 'chef/chef_fs/file_system/data_bags_dir' require 'chef/chef_fs/file_system/nodes_dir' require 'chef/chef_fs/file_system/environments_dir' -require 'chef/rest' require 'chef/chef_fs/data_handler/client_data_handler' require 'chef/chef_fs/data_handler/role_data_handler' require 'chef/chef_fs/data_handler/user_data_handler' @@ -57,6 +57,14 @@ class Chef end def rest + Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key, :raw_output => true) + end + + def get_json(path) + Chef::ServerAPI.new(chef_server_url, :client_name => chef_username, :signing_key_filename => chef_private_key).get(path) + end + + def chef_rest Chef::REST.new(chef_server_url, chef_username, chef_private_key) end diff --git a/lib/chef/chef_fs/file_system/cookbook_dir.rb b/lib/chef/chef_fs/file_system/cookbook_dir.rb index 5751328b02..d7411e1c74 100644 --- a/lib/chef/chef_fs/file_system/cookbook_dir.rb +++ b/lib/chef/chef_fs/file_system/cookbook_dir.rb @@ -126,7 +126,7 @@ class Chef def delete(recurse) if recurse begin - rest.delete_rest(api_path) + rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException @@ -191,7 +191,7 @@ class Chef old_retry_count = Chef::Config[:http_retry_count] begin Chef::Config[:http_retry_count] = 0 - @chef_object ||= Chef::CookbookVersion.json_create(Chef::ChefFS::RawRequest.raw_json(rest, api_path)) + @chef_object ||= Chef::CookbookVersion.json_create(root.get_json(api_path)) ensure Chef::Config[:http_retry_count] = old_retry_count end diff --git a/lib/chef/chef_fs/file_system/cookbook_file.rb b/lib/chef/chef_fs/file_system/cookbook_file.rb index e05c4aa614..6673ad73f4 100644 --- a/lib/chef/chef_fs/file_system/cookbook_file.rb +++ b/lib/chef/chef_fs/file_system/cookbook_file.rb @@ -17,6 +17,7 @@ # require 'chef/chef_fs/file_system/base_fs_object' +require 'chef/http/simple' require 'digest/md5' class Chef @@ -35,16 +36,12 @@ class Chef end def read - old_sign_on_redirect = rest.sign_on_redirect - rest.sign_on_redirect = false begin - tmpfile = rest.get_rest(file[:url], true) + tmpfile = Chef::HTTP::Simple.new(file[:url]).streaming_request(file[:url]) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "Timeout reading #{file[:url]}: #{e}" rescue Net::HTTPServerException => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "#{e.message} retrieving #{file[:url]}" - ensure - rest.sign_on_redirect = old_sign_on_redirect end begin diff --git a/lib/chef/chef_fs/file_system/cookbooks_dir.rb b/lib/chef/chef_fs/file_system/cookbooks_dir.rb index 73684450b3..a58bfdd1f2 100644 --- a/lib/chef/chef_fs/file_system/cookbooks_dir.rb +++ b/lib/chef/chef_fs/file_system/cookbooks_dir.rb @@ -18,7 +18,6 @@ require 'chef/chef_fs/file_system/rest_list_dir' require 'chef/chef_fs/file_system/cookbook_dir' -require 'chef/chef_fs/raw_request' require 'chef/chef_fs/file_system/operation_failed_error' require 'chef/chef_fs/file_system/cookbook_frozen_error' require 'chef/chef_fs/file_system/chef_repository_file_system_cookbook_dir' @@ -54,13 +53,13 @@ class Chef @children ||= begin if Chef::Config[:versioned_cookbooks] result = [] - Chef::ChefFS::RawRequest.raw_json(rest, "#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks| + root.get_json("#{api_path}/?num_versions=all").each_pair do |cookbook_name, cookbooks| cookbooks['versions'].each do |cookbook_version| result << CookbookDir.new("#{cookbook_name}-#{cookbook_version['version']}", self, :exists => true) end end else - result = Chef::ChefFS::RawRequest.raw_json(rest, api_path).keys.map { |cookbook_name| CookbookDir.new(cookbook_name, self, :exists => true) } + result = root.get_json(api_path).keys.map { |cookbook_name| CookbookDir.new(cookbook_name, self, :exists => true) } end result.sort_by(&:name) end @@ -107,7 +106,7 @@ class Chef cookbook_to_upload.freeze_version if options[:freeze] # Instantiate a new uploader based on the proxy loader - uploader = Chef::CookbookUploader.new(cookbook_to_upload, proxy_cookbook_path, :force => options[:force], :rest => rest) + uploader = Chef::CookbookUploader.new(cookbook_to_upload, proxy_cookbook_path, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(temp_cookbooks_path) do upload_cookbook!(uploader) @@ -129,7 +128,7 @@ class Chef def upload_unversioned_cookbook(other, options) cookbook_to_upload = other.chef_object cookbook_to_upload.freeze_version if options[:freeze] - uploader = Chef::CookbookUploader.new(cookbook_to_upload, other.parent.file_path, :force => options[:force], :rest => rest) + uploader = Chef::CookbookUploader.new(cookbook_to_upload, other.parent.file_path, :force => options[:force], :rest => root.chef_rest) with_actual_cookbooks_dir(other.parent.file_path) do upload_cookbook!(uploader) diff --git a/lib/chef/chef_fs/file_system/data_bag_dir.rb b/lib/chef/chef_fs/file_system/data_bag_dir.rb index 3814b94fac..212f76fdb9 100644 --- a/lib/chef/chef_fs/file_system/data_bag_dir.rb +++ b/lib/chef/chef_fs/file_system/data_bag_dir.rb @@ -52,7 +52,7 @@ class Chef raise MustDeleteRecursivelyError.new(self), "#{path_for_printing} must be deleted recursively" end begin - rest.delete_rest(api_path) + rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException => e diff --git a/lib/chef/chef_fs/file_system/data_bags_dir.rb b/lib/chef/chef_fs/file_system/data_bags_dir.rb index d0404c284b..6d0685d3b7 100644 --- a/lib/chef/chef_fs/file_system/data_bags_dir.rb +++ b/lib/chef/chef_fs/file_system/data_bags_dir.rb @@ -34,7 +34,7 @@ class Chef def children begin - @children ||= Chef::ChefFS::RawRequest.raw_json(rest, api_path).keys.sort.map do |entry| + @children ||= root.get_json(api_path).keys.sort.map do |entry| DataBagDir.new(entry, self, true) end rescue Timeout::Error => e @@ -54,7 +54,7 @@ class Chef def create_child(name, file_contents) begin - rest.post_rest(api_path, { 'name' => name }) + rest.post(api_path, { 'name' => name }) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Timeout creating child '#{name}': #{e}" rescue Net::HTTPServerException => e diff --git a/lib/chef/chef_fs/file_system/nodes_dir.rb b/lib/chef/chef_fs/file_system/nodes_dir.rb index 82683e81ac..c3c48377cd 100644 --- a/lib/chef/chef_fs/file_system/nodes_dir.rb +++ b/lib/chef/chef_fs/file_system/nodes_dir.rb @@ -32,7 +32,7 @@ class Chef # Identical to RestListDir.children, except supports environments def children begin - @children ||= Chef::ChefFS::RawRequest.raw_json(rest, env_api_path).keys.sort.map do |key| + @children ||= root.get_json(env_api_path).keys.sort.map do |key| _make_child_entry("#{key}.json", true) end rescue Timeout::Error => e diff --git a/lib/chef/chef_fs/file_system/rest_list_dir.rb b/lib/chef/chef_fs/file_system/rest_list_dir.rb index 7e4a33813b..b7ee51d284 100644 --- a/lib/chef/chef_fs/file_system/rest_list_dir.rb +++ b/lib/chef/chef_fs/file_system/rest_list_dir.rb @@ -45,7 +45,7 @@ class Chef def children begin - @children ||= Chef::ChefFS::RawRequest.raw_json(rest, api_path).keys.sort.map do |key| + @children ||= root.get_json(api_path).keys.sort.map do |key| _make_child_entry("#{key}.json", true) end rescue Timeout::Error => e @@ -76,7 +76,7 @@ class Chef end begin - rest.post_rest(api_path, object) + rest.post(api_path, object) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:create_child, self, e), "Timeout creating '#{name}': #{e}" rescue Net::HTTPServerException => e diff --git a/lib/chef/chef_fs/file_system/rest_list_entry.rb b/lib/chef/chef_fs/file_system/rest_list_entry.rb index 6e6ad12438..0d5557de1d 100644 --- a/lib/chef/chef_fs/file_system/rest_list_entry.rb +++ b/lib/chef/chef_fs/file_system/rest_list_entry.rb @@ -19,7 +19,6 @@ require 'chef/chef_fs/file_system/base_fs_object' require 'chef/chef_fs/file_system/not_found_error' require 'chef/chef_fs/file_system/operation_failed_error' -require 'chef/chef_fs/raw_request' require 'chef/role' require 'chef/node' @@ -68,7 +67,7 @@ class Chef def delete(recurse) begin - rest.delete_rest(api_path) + rest.delete(api_path) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:delete, self, e), "Timeout deleting: #{e}" rescue Net::HTTPServerException => e @@ -86,7 +85,8 @@ class Chef def _read_hash begin - json = Chef::ChefFS::RawRequest.raw_request(rest, api_path) + # Minimize the value (get rid of defaults) so the results don't look terrible + minimize_value(root.get_json(api_path)) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "Timeout reading: #{e}" rescue Net::HTTPServerException => e @@ -96,8 +96,6 @@ class Chef raise Chef::ChefFS::FileSystem::OperationFailedError.new(:read, self, e), "HTTP error reading: #{e}" end end - # Minimize the value (get rid of defaults) so the results don't look terrible - minimize_value(JSON.parse(json, :create_additions => false)) end def chef_object @@ -160,7 +158,7 @@ class Chef end begin - rest.put_rest(api_path, object) + rest.put(api_path, object) rescue Timeout::Error => e raise Chef::ChefFS::FileSystem::OperationFailedError.new(:write, self, e), "Timeout writing: #{e}" rescue Net::HTTPServerException => e diff --git a/lib/chef/chef_fs/raw_request.rb b/lib/chef/chef_fs/raw_request.rb deleted file mode 100644 index e017589fa4..0000000000 --- a/lib/chef/chef_fs/raw_request.rb +++ /dev/null @@ -1,83 +0,0 @@ -class Chef - module ChefFS - module RawRequest - def self.raw_json(chef_rest, api_path) - api_request(chef_rest, :GET, chef_rest.create_url(api_path), {}, nil, :parse_json => true) - end - - def self.raw_request(chef_rest, api_path) - api_request(chef_rest, :GET, chef_rest.create_url(api_path)) - end - - def self.api_request(chef_rest, method, url, headers={}, data=nil, options = {}) - json_body = data || nil - # json_body = data ? Chef::JSONCompat.to_json(data) : nil - # Force encoding to binary to fix SSL related EOFErrors - # cf. http://tickets.opscode.com/browse/CHEF-2363 - # http://redmine.ruby-lang.org/issues/5233 - # json_body.force_encoding(Encoding::BINARY) if json_body.respond_to?(:force_encoding) - headers = build_headers(chef_rest, method, url, headers, json_body) - - chef_rest.retriable_rest_request(method, url, json_body, headers) do |rest_request| - response = rest_request.call {|r| r.read_body} - - response_body = chef_rest.decompress_body(response) - - if response.kind_of?(Net::HTTPSuccess) - if options[:parse_json] && response['content-type'] =~ /json/ - JSON.parse(response_body, :create_additions => false) - else - response_body - end - elsif redirect_location = redirected_to(response) - if [:GET, :HEAD].include?(method) - chef_rest.follow_redirect do - api_request(chef_rest, method, chef_rest.create_url(redirect_location), headers, nil, options) - end - else - raise Exceptions::InvalidRedirect, "#{method} request was redirected from #{url} to #{redirect_location}. Only GET and HEAD support redirects." - end - else - # have to decompress the body before making an exception for it. But the body could be nil. - response.body.replace(chef_rest.decompress_body(response)) if response.body.respond_to?(:replace) - - if response['content-type'] =~ /json/ - exception = response_body - msg = "HTTP Request Returned #{response.code} #{response.message}: " - msg << (exception["error"].respond_to?(:join) ? exception["error"].join(", ") : exception["error"].to_s) - Chef::Log.info(msg) - end - response.error! - end - end - end - - private - - # Copied so that it does not automatically inflate an object - # This is also used by knife raw_essentials - - ACCEPT_ENCODING = "Accept-Encoding".freeze - ENCODING_GZIP_DEFLATE = "gzip;q=1.0,deflate;q=0.6,identity;q=0.3".freeze - - def self.redirected_to(response) - return nil unless response.kind_of?(Net::HTTPRedirection) - # Net::HTTPNotModified is undesired subclass of Net::HTTPRedirection so test for this - return nil if response.kind_of?(Net::HTTPNotModified) - response['location'] - end - - def self.build_headers(chef_rest, method, url, headers={}, json_body=nil, raw=nil) - # headers = @default_headers.merge(headers) - #headers['Accept'] = "application/json" unless raw - headers['Accept'] = "application/json" unless raw - headers['Content-Type'] = 'application/json' if json_body - headers['Content-Length'] = json_body.bytesize.to_s if json_body - headers[Chef::REST::RESTRequest::ACCEPT_ENCODING] = Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE - headers.merge!(chef_rest.authentication_headers(method, url, json_body)) if chef_rest.sign_requests? - headers.merge!(Chef::Config[:custom_http_headers]) if Chef::Config[:custom_http_headers] - headers - end - end - end -end diff --git a/lib/chef/http.rb b/lib/chef/http.rb index 77faa686a0..4852a3eb8a 100644 --- a/lib/chef/http.rb +++ b/lib/chef/http.rb @@ -94,7 +94,7 @@ class Chef # === Parameters # path:: path part of the request URL def head(path, headers={}) - api_request(:HEAD, create_url(path), headers) + request(:HEAD, path, headers) end # Send an HTTP GET request to the path @@ -102,7 +102,7 @@ class Chef # === Parameters # path:: The path to GET def get(path, headers={}) - api_request(:GET, create_url(path), headers) + request(:GET, path, headers) end # Send an HTTP PUT request to the path @@ -110,7 +110,7 @@ class Chef # === Parameters # path:: path part of the request URL def put(path, json, headers={}) - api_request(:PUT, create_url(path), headers, json) + request(:PUT, path, headers, json) end # Send an HTTP POST request to the path @@ -118,7 +118,7 @@ class Chef # === Parameters # path:: path part of the request URL def post(path, json, headers={}) - api_request(:POST, create_url(path), headers, json) + request(:POST, path, headers, json) end # Send an HTTP DELETE request to the path @@ -126,13 +126,13 @@ class Chef # === Parameters # path:: path part of the request URL def delete(path, headers={}) - api_request(:DELETE, create_url(path), headers) + request(:DELETE, path, headers) end - # Makes an HTTP request to +url+ with the given +method+, +headers+, and + # Makes an HTTP request to +path+ with the given +method+, +headers+, and # +data+ (if applicable). - def request(method, url, headers={}, data=false) - + def request(method, path, headers={}, data=false) + url = create_url(path) method, url, headers, data = apply_request_middleware(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) @@ -154,7 +154,8 @@ class Chef # # If no block is given, the tempfile is returned, which means it's up to # you to unlink the tempfile when you're done with it. - def streaming_request(url, headers, &block) + def streaming_request(path, headers={}, &block) + url = create_url(path) response, rest_request, return_value = nil, nil, nil tempfile = nil @@ -185,13 +186,15 @@ class Chef raise end - def http_client - BasicClient.new(create_url(url)) + def http_client(base_url=nil) + base_url ||= url + BasicClient.new(base_url) end protected def create_url(path) + return path if path.is_a?(URI) if path =~ /^(http|https):\/\// URI.parse(path) else @@ -228,10 +231,11 @@ class Chef headers = build_headers(method, url, headers, body) retrying_http_errors(url) do + client = http_client(url) if block_given? - request, response = http_client.request(method, url, body, headers, &response_handler) + request, response = client.request(method, url, body, headers, &response_handler) else - request, response = http_client.request(method, url, body, headers) {|r| r.read_body } + request, response = client.request(method, url, body, headers) {|r| r.read_body } end @last_response = response @@ -320,7 +324,6 @@ class Chef yield ensure @redirects_followed = 0 - @authenticator.sign_request = true end private diff --git a/lib/chef/http/cookie_manager.rb b/lib/chef/http/cookie_manager.rb index 067dc3ed22..f6dcf9aa32 100644 --- a/lib/chef/http/cookie_manager.rb +++ b/lib/chef/http/cookie_manager.rb @@ -32,16 +32,16 @@ class Chef end def handle_request(method, url, headers={}, data=false) - host, port = url.host, url.port - if @cookies.has_key?("#{host}:#{port}") - headers['Cookie'] = @cookies["#{host}:#{port}"] + @host, @port = url.host, url.port + if @cookies.has_key?("#{@host}:#{@port}") + headers['Cookie'] = @cookies["#{@host}:#{@port}"] end [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) if http_response['set-cookie'] - @cookies["#{host}:#{port}"] = http_response['set-cookie'] + @cookies["#{@host}:#{@port}"] = http_response['set-cookie'] end [http_response, rest_request, return_value] end diff --git a/lib/chef/http/json_input.rb b/lib/chef/http/json_input.rb new file mode 100644 index 0000000000..741c48f5f6 --- /dev/null +++ b/lib/chef/http/json_input.rb @@ -0,0 +1,53 @@ +#-- +# Author:: Daniel DeLeo (<dan@opscode.com>) +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/json_compat' + +class Chef + class HTTP + + # Middleware that takes json input and turns it into raw text + class JSONInput + + def initialize(opts={}) + end + + def handle_request(method, url, headers={}, data=false) + if data + headers["Content-Type"] = 'application/json' + data = Chef::JSONCompat.to_json(data) + # Force encoding to binary to fix SSL related EOFErrors + # cf. http://tickets.opscode.com/browse/CHEF-2363 + # http://redmine.ruby-lang.org/issues/5233 + data.force_encoding(Encoding::BINARY) if data.respond_to?(:force_encoding) + end + [method, url, headers, data] + end + + def handle_response(http_response, rest_request, return_value) + [http_response, rest_request, return_value] + end + + def stream_response_handler(response) + nil + end + + end + end +end diff --git a/lib/chef/http/json_to_model_inflater.rb b/lib/chef/http/json_output.rb index 0b46054510..48407d933f 100644 --- a/lib/chef/http/json_to_model_inflater.rb +++ b/lib/chef/http/json_output.rb @@ -1,5 +1,6 @@ #-- # Author:: Daniel DeLeo (<dan@opscode.com>) +# Author:: John Keiser (<jkeiser@opscode.com>) # Copyright:: Copyright (c) 2013 Opscode, Inc. # License:: Apache License, Version 2.0 # @@ -17,29 +18,25 @@ # require 'chef/json_compat' +require 'chef/log' + class Chef class HTTP - # A Middleware-ish thing that takes an HTTP response, parses it as JSON if - # possible, and converts it into an appropriate model object if it contains - # a `json_class` key. - class JSONToModelInflater + # Middleware that takes an HTTP response, parses it as JSON if possible. + class JSONOutput def initialize(opts={}) + @raw_output = opts[:raw_output] + @inflate_json_class = opts[:inflate_json_class] end def handle_request(method, url, headers={}, data=false) # Ideally this should always set Accept to application/json, but # Chef::REST is sometimes used to make non-JSON requests, so it sets # Accept to the desired value before middlewares get called. - headers['Accept'] ||= "application/json" - headers["Content-Type"] = 'application/json' if data - json_body = data ? Chef::JSONCompat.to_json(data) : nil - # Force encoding to binary to fix SSL related EOFErrors - # cf. http://tickets.opscode.com/browse/CHEF-2363 - # http://redmine.ruby-lang.org/issues/5233 - json_body.force_encoding(Encoding::BINARY) if json_body.respond_to?(:force_encoding) - [method, url, headers, json_body] + headers['Accept'] ||= 'application/json' + [method, url, headers, data] end def handle_response(http_response, rest_request, return_value) @@ -47,7 +44,16 @@ class Chef # needed to keep conditional get stuff working correctly. return [http_response, rest_request, return_value] if return_value == false if http_response['content-type'] =~ /json/ - [http_response, rest_request, Chef::JSONCompat.from_json(http_response.body.chomp)] + if @raw_output + return_value = http_response.body.to_s + else + if @inflate_json_class + return_value = Chef::JSONCompat.from_json(http_response.body.chomp) + else + return_value = Chef::JSONCompat.from_json(http_response.body.chomp, :create_additions => false) + end + end + [http_response, rest_request, return_value] else Chef::Log.warn("Expected JSON response, but got content-type '#{http_response['content-type']}'") return [http_response, rest_request, http_response.body.to_s] diff --git a/lib/chef/http/json_to_model_output.rb b/lib/chef/http/json_to_model_output.rb new file mode 100644 index 0000000000..9bc90a52ae --- /dev/null +++ b/lib/chef/http/json_to_model_output.rb @@ -0,0 +1,34 @@ +#-- +# Author:: Daniel DeLeo (<dan@opscode.com>) +# Copyright:: Copyright (c) 2013 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/http/json_output' + +class Chef + class HTTP + + # A Middleware-ish thing that takes an HTTP response, parses it as JSON if + # possible, and converts it into an appropriate model object if it contains + # a `json_class` key. + class JSONToModelOutput < JSONOutput + def initialize(opts={}) + opts[:inflate_json_class] = true if !opts.has_key?(:inflate_json_class) + super + end + end + end +end diff --git a/lib/chef/http/simple.rb b/lib/chef/http/simple.rb new file mode 100644 index 0000000000..0ecb28846c --- /dev/null +++ b/lib/chef/http/simple.rb @@ -0,0 +1,16 @@ +require 'chef/http' +require 'chef/http/authenticator' +require 'chef/http/decompressor' + + +class Chef + class HTTP + + class Simple < HTTP + + use Decompressor + use CookieManager + + end + end +end diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb index 5f357c6f1e..2756de1a5a 100644 --- a/lib/chef/knife/raw.rb +++ b/lib/chef/knife/raw.rb @@ -6,10 +6,13 @@ class Chef banner "knife raw REQUEST_PATH" deps do - require 'json' - require 'chef/rest' + require 'chef/json_compat' require 'chef/config' - require 'chef/chef_fs/raw_request' + require 'chef/http' + require 'chef/http/authenticator' + require 'chef/http/cookie_manager' + require 'chef/http/decompressor' + require 'chef/http/json_output' end option :method, @@ -29,6 +32,18 @@ class Chef :short => '-i FILE', :description => "Name of file to use for PUT or POST" + class RawInputServerAPI < Chef::HTTP + def initialize(options = {}) + options[:client_name] ||= Chef::Config[:node_name] + options[:signing_key_filename] ||= Chef::Config[:client_key] + super(Chef::Config[:chef_server_url], options) + end + use Chef::HTTP::JSONOutput + use Chef::HTTP::CookieManager + use Chef::HTTP::Decompressor + use Chef::HTTP::Authenticator + end + def run if name_args.length == 0 show_usage @@ -45,13 +60,18 @@ class Chef if config[:input] data = IO.read(config[:input]) end - chef_rest = Chef::REST.new(Chef::Config[:chef_server_url]) begin method = config[:method].to_sym - url = chef_rest.create_url(name_args[0]) - result = Chef::ChefFS::RawRequest.api_request(chef_rest, method, url, {}, data, :parse_json => config[:pretty]) - if result.is_a?(Hash) || result.is_a?(Array) - result = Chef::JSONCompat.to_json_pretty(result) + + if config[:pretty] + chef_rest = RawInputServerAPI.new + result = chef_rest.request(method, name_args[0], {'Content-Type' => 'application/json'}, data) + unless result.is_a?(String) + result = Chef::JSONCompat.to_json_pretty(result) + end + else + chef_rest = RawInputServerAPI.new(:raw_output => true) + result = chef_rest.request(method, name_args[0], {'Content-Type' => 'application/json'}, data) end output result rescue Timeout::Error => e diff --git a/lib/chef/provider/http_request.rb b/lib/chef/provider/http_request.rb index a5bc3b5e04..1e0aa8b4a0 100644 --- a/lib/chef/provider/http_request.rb +++ b/lib/chef/provider/http_request.rb @@ -17,26 +17,27 @@ # require 'tempfile' +require 'chef/http/simple' class Chef class Provider class HttpRequest < Chef::Provider - attr_accessor :rest + attr_accessor :http def whyrun_supported? true end def load_current_resource - @rest = Chef::REST.new(@new_resource.url, nil, nil) + @http = Chef::HTTP::Simple.new(@new_resource.url) end # Send a HEAD request to @new_resource.url, with ?message=@new_resource.message def action_head message = check_message(@new_resource.message) # returns true from Chef::REST if returns 2XX (Net::HTTPSuccess) - modified = @rest.head( + modified = @http.head( "#{@new_resource.url}?message=#{message}", @new_resource.headers ) @@ -53,9 +54,8 @@ class Chef converge_by("#{@new_resource} GET to #{@new_resource.url}") do message = check_message(@new_resource.message) - body = @rest.get( + body = @http.get( "#{@new_resource.url}?message=#{message}", - false, @new_resource.headers ) Chef::Log.info("#{@new_resource} GET to #{@new_resource.url} successful") @@ -67,7 +67,7 @@ class Chef def action_put converge_by("#{@new_resource} PUT to #{@new_resource.url}") do message = check_message(@new_resource.message) - body = @rest.put( + body = @http.put( "#{@new_resource.url}", message, @new_resource.headers @@ -81,7 +81,7 @@ class Chef def action_post converge_by("#{@new_resource} POST to #{@new_resource.url}") do message = check_message(@new_resource.message) - body = @rest.post( + body = @http.post( "#{@new_resource.url}", message, @new_resource.headers @@ -94,7 +94,7 @@ class Chef # Send a DELETE request to @new_resource.url def action_delete converge_by("#{@new_resource} DELETE to #{@new_resource.url}") do - body = @rest.delete( + body = @http.delete( "#{@new_resource.url}", @new_resource.headers ) diff --git a/lib/chef/provider/remote_file/http.rb b/lib/chef/provider/remote_file/http.rb index 9b65d87895..7eb2cda108 100644 --- a/lib/chef/provider/remote_file/http.rb +++ b/lib/chef/provider/remote_file/http.rb @@ -17,7 +17,7 @@ # limitations under the License. # -require 'chef/rest' +require 'chef/http/simple' require 'chef/digester' require 'chef/provider/remote_file' require 'chef/provider/remote_file/cache_control_data' @@ -58,9 +58,9 @@ class Chef def fetch tempfile = nil begin - rest = Chef::REST.new(uri, nil, nil, http_client_opts) - tempfile = rest.streaming_request(uri, headers) - update_cache_control_data(tempfile, rest.last_response) + http = Chef::HTTP::Simple.new(uri, http_client_opts) + tempfile = http.streaming_request(uri, headers) + update_cache_control_data(tempfile, http.last_response) rescue Net::HTTPRetriableError => e if e.response.is_a? Net::HTTPNotModified tempfile = nil diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb index b264f96336..1a60fa093e 100644 --- a/lib/chef/rest.rb +++ b/lib/chef/rest.rb @@ -29,7 +29,8 @@ end require 'chef/http/authenticator' require 'chef/http/decompressor' -require 'chef/http/json_to_model_inflater' +require 'chef/http/json_input' +require 'chef/http/json_to_model_output' require 'chef/http/cookie_manager' require 'chef/config' require 'chef/exceptions' @@ -58,10 +59,14 @@ class Chef options[:signing_key_filename] = signing_key_filename super(url, options) - @chef_json_inflater = JSONToModelInflater.new(options) - @cookie_manager = CookieManager.new(options) @decompressor = Decompressor.new(options) @authenticator = Authenticator.new(options) + + @middlewares << JSONInput.new(options) + @middlewares << JSONToModelOutput.new(options) + @middlewares << CookieManager.new(options) + @middlewares << @decompressor + @middlewares << @authenticator end def signing_key_filename @@ -94,9 +99,9 @@ class Chef # to JSON inflated. def get(path, raw=false, headers={}) if raw - streaming_request(create_url(path), headers) + streaming_request(path, headers) else - api_request(:GET, create_url(path), headers) + request(:GET, path, headers) end end @@ -117,11 +122,6 @@ class Chef streaming_request(create_url(path), headers) {|tmp_file| yield tmp_file } end - # Chef::REST doesn't define middleware in the normal way for backcompat reasons, so it's hardcoded here. - def middlewares - [@chef_json_inflater, @cookie_manager, @decompressor, @authenticator] - end - alias :api_request :request alias :raw_http_request :send_http_request diff --git a/lib/chef/server_api.rb b/lib/chef/server_api.rb new file mode 100644 index 0000000000..e9e7593dd6 --- /dev/null +++ b/lib/chef/server_api.rb @@ -0,0 +1,41 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright (c) 2012 Opscode, Inc. +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require 'chef/http' +require 'chef/http/authenticator' +require 'chef/http/cookie_manager' +require 'chef/http/decompressor' +require 'chef/http/json_input' +require 'chef/http/json_output' + +class Chef + class ServerAPI < Chef::HTTP + + def initialize(url = Chef::Config[:chef_server_url], options = {}) + options[:client_name] ||= Chef::Config[:node_name] + options[:signing_key_filename] ||= Chef::Config[:client_key] + super(url, options) + end + + use Chef::HTTP::JSONInput + use Chef::HTTP::JSONOutput + use Chef::HTTP::CookieManager + use Chef::HTTP::Decompressor + use Chef::HTTP::Authenticator + end +end
\ No newline at end of file |