diff options
author | PrajaktaPurohit <prajakta@opscode.com> | 2014-01-19 18:07:17 -0800 |
---|---|---|
committer | Prajakta Purohit <prajakta@opscode.com> | 2014-02-07 15:31:10 -0800 |
commit | a4cdce667a1bc1828eea875bdf3f49af6d96da70 (patch) | |
tree | ccf5fb1bf44f0eda5ad0470f0957dd31864c62f0 | |
parent | 29e732d97ec7e28b2111aca9f93edfd1bc257c2d (diff) | |
download | chef-a4cdce667a1bc1828eea875bdf3f49af6d96da70.tar.gz |
- Adding X-Remote-Request-Id to the set of headers for every request from CCR and
knife that will be sent to erchef
- Each knife request has a different X-Remote-Request-Id, where as it
remains the same for all requests originating from the same chef-client
run.
- Adding and fixing tests
-rw-r--r-- | lib/chef/client.rb | 6 | ||||
-rw-r--r-- | lib/chef/http/remote_request_id.rb | 46 | ||||
-rw-r--r-- | lib/chef/knife/raw.rb | 1 | ||||
-rw-r--r-- | lib/chef/request_id.rb | 37 | ||||
-rw-r--r-- | lib/chef/resource_reporter.rb | 11 | ||||
-rw-r--r-- | lib/chef/rest.rb | 6 | ||||
-rw-r--r-- | lib/chef/run_status.rb | 5 | ||||
-rw-r--r-- | lib/chef/server_api.rb | 4 | ||||
-rw-r--r-- | spec/unit/client_spec.rb | 3 | ||||
-rw-r--r-- | spec/unit/knife_spec.rb | 55 | ||||
-rw-r--r-- | spec/unit/resource_reporter_spec.rb | 2 | ||||
-rw-r--r-- | spec/unit/rest_spec.rb | 51 |
12 files changed, 205 insertions, 22 deletions
diff --git a/lib/chef/client.rb b/lib/chef/client.rb index 722c9915e9..638047331a 100644 --- a/lib/chef/client.rb +++ b/lib/chef/client.rb @@ -44,6 +44,7 @@ require 'chef/version' require 'chef/resource_reporter' require 'chef/run_lock' require 'chef/policy_builder' +require 'chef/request_id' require 'ohai' require 'rbconfig' @@ -391,10 +392,12 @@ class Chef # don't add code that may fail before entering this section to be sure to release lock begin runlock.save_pid + request_id = Chef::RequestID.instance.request_id run_context = nil @events.run_start(Chef::VERSION) Chef::Log.info("*** Chef #{Chef::VERSION} ***") Chef::Log.info "Chef-client pid: #{Process.pid}" + Chef::Log.debug("Chef-client request_id: #{request_id}") enforce_path_sanity run_ohai @events.ohai_completed(node) @@ -404,6 +407,7 @@ class Chef build_node + run_status.run_id = request_id run_status.start_clock Chef::Log.info("Starting Chef Run for #{node.name}") run_started @@ -434,6 +438,8 @@ class Chef @events.run_failed(e) raise ensure + Chef::RequestID.instance.reset_request_id + request_id = nil @run_status = nil run_context = nil runlock.release diff --git a/lib/chef/http/remote_request_id.rb b/lib/chef/http/remote_request_id.rb new file mode 100644 index 0000000000..6bec5dba4f --- /dev/null +++ b/lib/chef/http/remote_request_id.rb @@ -0,0 +1,46 @@ +# Author:: Prajakta Purohit (<prajakta@opscode.com>) +# Copyright:: Copyright (c) 2009, 2010, 2013, 2014 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/request_id' + +class Chef + class HTTP + class RemoteRequestID + + def initialize(opts={}) + end + + def handle_request(method, url, headers={}, data=false) + headers.merge!({'X-REMOTE-REQUEST-ID' => Chef::RequestID.instance.request_id}) + [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 + + def handle_stream_complete(http_response, rest_request, return_value) + [http_response, rest_request, return_value] + end + + end + end +end diff --git a/lib/chef/knife/raw.rb b/lib/chef/knife/raw.rb index 2756de1a5a..954d46beee 100644 --- a/lib/chef/knife/raw.rb +++ b/lib/chef/knife/raw.rb @@ -42,6 +42,7 @@ class Chef use Chef::HTTP::CookieManager use Chef::HTTP::Decompressor use Chef::HTTP::Authenticator + use Chef::HTTP::RemoteRequestID end def run diff --git a/lib/chef/request_id.rb b/lib/chef/request_id.rb new file mode 100644 index 0000000000..7fc177c633 --- /dev/null +++ b/lib/chef/request_id.rb @@ -0,0 +1,37 @@ +# Author:: Prajakta Purohit (<prajakta@opscode.com>) +# Copyright:: Copyright (c) 2009, 2010, 2013, 2014 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/monkey_patches/securerandom' +require 'singleton' + +class Chef + class RequestID + include Singleton + + def reset_request_id + @request_id = nil + end + + def request_id + @request_id ||= generate_request_id + end + + def generate_request_id + SecureRandom.uuid + end + end +end diff --git a/lib/chef/resource_reporter.rb b/lib/chef/resource_reporter.rb index 04f4ee26de..d191710cb4 100644 --- a/lib/chef/resource_reporter.rb +++ b/lib/chef/resource_reporter.rb @@ -107,7 +107,6 @@ class Chef @pending_update = nil @status = "success" @exception = nil - @run_id = SecureRandom.uuid @rest_client = rest_client @error_descriptions = {} end @@ -118,7 +117,7 @@ class Chef if reporting_enabled? begin resource_history_url = "reports/nodes/#{node_name}/runs" - server_response = @rest_client.post_rest(resource_history_url, {:action => :start, :run_id => @run_id, + server_response = @rest_client.post_rest(resource_history_url, {:action => :start, :run_id => run_id, :start_time => start_time.to_s}, headers) rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError => e handle_error_starting_run(e, resource_history_url) @@ -158,6 +157,10 @@ class Chef @reporting_enabled = false end + def run_id + @run_status.run_id + end + def resource_current_state_loaded(new_resource, action, current_resource) unless nested_resource?(new_resource) @pending_update = ResourceReport.new_with_current_state(new_resource, action, current_resource) @@ -214,8 +217,8 @@ class Chef def post_reporting_data if reporting_enabled? run_data = prepare_run_data - resource_history_url = "reports/nodes/#{node_name}/runs/#{@run_id}" - Chef::Log.info("Sending resource update report (run-id: #{@run_id})") + resource_history_url = "reports/nodes/#{node_name}/runs/#{run_id}" + Chef::Log.info("Sending resource update report (run-id: #{run_id})") Chef::Log.debug run_data.inspect compressed_data = encode_gzip(run_data.to_json) begin diff --git a/lib/chef/rest.rb b/lib/chef/rest.rb index a1139d7fa2..73ca4b3293 100644 --- a/lib/chef/rest.rb +++ b/lib/chef/rest.rb @@ -36,6 +36,7 @@ require 'chef/http/validate_content_length' require 'chef/config' require 'chef/exceptions' require 'chef/platform/query_helpers' +require 'chef/http/remote_request_id' class Chef # == Chef::REST @@ -62,6 +63,7 @@ class Chef @decompressor = Decompressor.new(options) @authenticator = Authenticator.new(options) + @request_id = RemoteRequestID.new(options) @middlewares << ValidateContentLength.new(options) @middlewares << JSONInput.new(options) @@ -69,6 +71,8 @@ class Chef @middlewares << CookieManager.new(options) @middlewares << @decompressor @middlewares << @authenticator + @middlewares << @request_id + end def signing_key_filename @@ -132,7 +136,7 @@ class Chef def raw_http_request(method, path, headers, data) url = create_url(path) method, url, headers, data = @authenticator.handle_request(method, url, headers, data) - + method, url, headers, data = @request_id.handle_request(method, url, headers, data) response, rest_request, return_value = send_http_request(method, url, headers, data) response.error! unless success_response?(response) return_value diff --git a/lib/chef/run_status.rb b/lib/chef/run_status.rb index 9354f7872a..0f181426b0 100644 --- a/lib/chef/run_status.rb +++ b/lib/chef/run_status.rb @@ -37,6 +37,8 @@ class Chef::RunStatus attr_writer :exception + attr_accessor :run_id + def initialize(node, events) @node = node @events = events @@ -112,7 +114,8 @@ class Chef::RunStatus :all_resources => all_resources, :updated_resources => updated_resources, :exception => formatted_exception, - :backtrace => backtrace} + :backtrace => backtrace, + :run_id => run_id} end # Returns a string of the format "ExceptionClass: message" or +nil+ if no diff --git a/lib/chef/server_api.rb b/lib/chef/server_api.rb index e9e7593dd6..8cdcd7a09d 100644 --- a/lib/chef/server_api.rb +++ b/lib/chef/server_api.rb @@ -22,6 +22,7 @@ require 'chef/http/cookie_manager' require 'chef/http/decompressor' require 'chef/http/json_input' require 'chef/http/json_output' +require 'chef/http/remote_request_id' class Chef class ServerAPI < Chef::HTTP @@ -37,5 +38,6 @@ class Chef use Chef::HTTP::CookieManager use Chef::HTTP::Decompressor use Chef::HTTP::Authenticator + use Chef::HTTP::RemoteRequestID end -end
\ No newline at end of file +end diff --git a/spec/unit/client_spec.rb b/spec/unit/client_spec.rb index 58a643d403..bd80d39237 100644 --- a/spec/unit/client_spec.rb +++ b/spec/unit/client_spec.rb @@ -183,7 +183,8 @@ shared_examples_for Chef::Client do # Client.register will then turn around create another # Chef::REST object, this time with the client key it got from the # previous step. - Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key]).exactly(1).and_return(mock_chef_rest_for_node) + Chef::REST.should_receive(:new).with(Chef::Config[:chef_server_url], @fqdn, Chef::Config[:client_key] + ).exactly(1).and_return(mock_chef_rest_for_node) # --Client#build_node # looks up the node, which we will return, then later saves it. diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index daace18106..8bf85bf604 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -22,6 +22,7 @@ module KnifeSpecs end require 'spec_helper' +require 'uri' describe Chef::Knife do before(:each) do @@ -141,6 +142,60 @@ describe Chef::Knife do end + describe "the headers include X-Remote-Request-Id" do + + let(:headers) {{"Accept"=>"application/json", + "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", + 'X-Chef-Version' => Chef::VERSION, + "Host"=>"api.opscode.piab:443", + "X-REMOTE-REQUEST-ID"=>request_id}} + + let(:request_id) {"1234"} + + let(:request_mock) { {} } + + let(:rest) do + Net::HTTP.stub(:new).and_return(http_client) + Chef::RequestID.instance.stub(:request_id).and_return(request_id) + Chef::Config.stub(:chef_server_url).and_return("https://api.opscode.piab") + command = Chef::Knife.run(%w{test yourself}) + rest = command.noauth_rest + rest + end + + let!(:http_client) do + http_client = Net::HTTP.new(url.host, url.port) + http_client.stub(:request).and_yield(http_response).and_return(http_response) + http_client + end + + let(:url) { URI.parse("https://api.opscode.piab") } + + let(:http_response) do + http_response = Net::HTTPSuccess.new("1.1", "200", "successful rest req") + http_response.stub(:read_body) + http_response.stub(:body).and_return(body) + http_response["Content-Length"] = body.bytesize.to_s + http_response + end + + let(:body) { "ninja" } + + before(:each) do + Chef::Config[:chef_server_url] = "https://api.opscode.piab" + if KnifeSpecs.const_defined?(:TestYourself) + KnifeSpecs.send :remove_const, :TestYourself + end + Kernel.load(File.join(CHEF_SPEC_DATA, 'knife_subcommand', 'test_yourself.rb')) + Chef::Knife.subcommands.each { |name, klass| Chef::Knife.subcommands.delete(name) unless klass.kind_of?(Class) } + end + + it "confirms that the headers include X-Remote-Request-Id" do + Net::HTTP::Get.should_receive(:new).with("/monkey", headers).and_return(request_mock) + rest.get_rest("monkey") + end + end + describe "when running a command" do before(:each) do if KnifeSpecs.const_defined?(:TestYourself) diff --git a/spec/unit/resource_reporter_spec.rb b/spec/unit/resource_reporter_spec.rb index 52fd44e692..d412234596 100644 --- a/spec/unit/resource_reporter_spec.rb +++ b/spec/unit/resource_reporter_spec.rb @@ -38,7 +38,6 @@ describe Chef::ResourceReporter do @rest_client = double("Chef::REST (mock)") @rest_client.stub(:post_rest).and_return(true) @resource_reporter = Chef::ResourceReporter.new(@rest_client) - @run_id = @resource_reporter.run_id @new_resource = Chef::Resource::File.new("/tmp/a-file.txt") @new_resource.cookbook_name = "monkey" @cookbook_version = double("Cookbook::Version", :version => "1.2.3") @@ -49,6 +48,7 @@ describe Chef::ResourceReporter do @events = Chef::EventDispatch::Dispatcher.new @run_context = Chef::RunContext.new(@node, {}, @events) @run_status = Chef::RunStatus.new(@node, @events) + @run_id = @run_status.run_id Time.stub(:now).and_return(@start_time, @end_time) end diff --git a/spec/unit/rest_spec.rb b/spec/unit/rest_spec.rb index a53b4c9507..3d6144ab35 100644 --- a/spec/unit/rest_spec.rb +++ b/spec/unit/rest_spec.rb @@ -59,13 +59,19 @@ describe Chef::REST do let(:log_stringio) { StringIO.new } + let(:request_id) {"1234"} + let(:rest) do Chef::REST::CookieJar.stub(:instance).and_return({}) + Chef::RequestID.instance.stub(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) Chef::REST::CookieJar.instance.clear rest end + let(:standard_read_headers) {{"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}} + let(:standard_write_headers) {{"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3", "X-REMOTE-REQUEST-ID"=>request_id}} + before(:each) do Chef::Log.init(log_stringio) end @@ -82,7 +88,7 @@ describe Chef::REST do it "makes a :GET request with the composed url object" do rest.should_receive(:send_http_request). - with(:GET, monkey_uri, STANDARD_READ_HEADERS, false). + with(:GET, monkey_uri, standard_read_headers, false). and_return([1,2,3]) rest.should_receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) @@ -94,12 +100,9 @@ describe Chef::REST do rest.get_rest("monkey", true) end - STANDARD_READ_HEADERS = {"Accept"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3"} - STANDARD_WRITE_HEADERS = {"Accept"=>"application/json", "Content-Type"=>"application/json", "Accept"=>"application/json", "Accept-Encoding"=>"gzip;q=1.0,deflate;q=0.6,identity;q=0.3"} - it "makes a :DELETE request with the composed url object" do rest.should_receive(:send_http_request). - with(:DELETE, monkey_uri, STANDARD_READ_HEADERS, false). + with(:DELETE, monkey_uri, standard_read_headers, false). and_return([1,2,3]) rest.should_receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) @@ -108,7 +111,7 @@ describe Chef::REST do it "makes a :POST request with the composed url object and data" do rest.should_receive(:send_http_request). - with(:POST, monkey_uri, STANDARD_WRITE_HEADERS, "\"data\""). + with(:POST, monkey_uri, standard_write_headers, "\"data\""). and_return([1,2,3]) rest.should_receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) @@ -117,7 +120,7 @@ describe Chef::REST do it "makes a :PUT request with the composed url object and data" do rest.should_receive(:send_http_request). - with(:PUT, monkey_uri, STANDARD_WRITE_HEADERS, "\"data\""). + with(:PUT, monkey_uri, standard_write_headers, "\"data\""). and_return([1,2,3]) rest.should_receive(:apply_response_middleware).with(1,2,3).and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) @@ -142,27 +145,27 @@ describe Chef::REST do it 'calls the authn middleware' do data = "\"secure data\"" - auth_headers = STANDARD_WRITE_HEADERS.merge({"auth_done"=>"yep"}) + auth_headers = standard_write_headers.merge({"auth_done"=>"yep"}) rest.authenticator.should_receive(:handle_request). - with(:POST, monkey_uri, STANDARD_WRITE_HEADERS, data). + with(:POST, monkey_uri, standard_write_headers, data). and_return([:POST, monkey_uri, auth_headers, data]) rest.should_receive(:send_http_request). with(:POST, monkey_uri, auth_headers, data). and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) - rest.raw_http_request(:POST, monkey_uri, STANDARD_WRITE_HEADERS, data) + rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data) end it 'sets correct authn headers' do data = "\"secure data\"" - method, uri, auth_headers, d = rest.authenticator.handle_request(:POST, monkey_uri, STANDARD_WRITE_HEADERS, data) + method, uri, auth_headers, d = rest.authenticator.handle_request(:POST, monkey_uri, standard_write_headers, data) rest.should_receive(:send_http_request). with(:POST, monkey_uri, auth_headers, data). and_return([1,2,3]) rest.should_receive('success_response?'.to_sym).with(1).and_return(true) - rest.raw_http_request(:POST, monkey_uri, STANDARD_WRITE_HEADERS, data) + rest.raw_http_request(:POST, monkey_uri, standard_write_headers, data) end end @@ -244,6 +247,7 @@ describe Chef::REST do let(:rest) do Net::HTTP.stub(:new).and_return(http_client) Chef::REST::CookieJar.stub(:instance).and_return({}) + Chef::RequestID.instance.stub(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) Chef::REST::CookieJar.instance.clear rest @@ -254,6 +258,7 @@ describe Chef::REST do 'Accept' => 'application/json', 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, + 'X-REMOTE-REQUEST-ID' => request_id } end @@ -275,6 +280,7 @@ describe Chef::REST do 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, 'Host' => host_header, + 'X-REMOTE-REQUEST-ID' => request_id } end @@ -287,6 +293,11 @@ describe Chef::REST do rest.request(:GET, url, {}) end + it "should always include the X-Remote-Request-Id header" do + Net::HTTP::Get.should_receive(:new).with("/?foo=bar", base_headers).and_return(request_mock) + rest.request(:GET, url, {}) + end + it "sets the user agent to chef-client" do # XXX: must reset to default b/c knife changes the UA Chef::REST::RESTRequest.user_agent = Chef::REST::RESTRequest::DEFAULT_UA @@ -342,6 +353,7 @@ describe Chef::REST do let(:rest) do Net::HTTP.stub(:new).and_return(http_client) Chef::REST::CookieJar.instance["#{url.host}:#{url.port}"] = "cookie monster" + Chef::RequestID.instance.stub(:request_id).and_return(request_id) rest = Chef::REST.new(base_url, nil, nil) rest end @@ -542,7 +554,20 @@ describe Chef::REST do expected_headers = {'Accept' => "*/*", 'X-Chef-Version' => Chef::VERSION, 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, - 'Host' => host_header} + 'Host' => host_header, + 'X-REMOTE-REQUEST-ID'=> request_id + } + Net::HTTP::Get.should_receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock) + rest.streaming_request(url, {}) + end + + it "build a new HTTP GET request with the X-Remote-Request-Id header" do + expected_headers = {'Accept' => "*/*", + 'X-Chef-Version' => Chef::VERSION, + 'Accept-Encoding' => Chef::REST::RESTRequest::ENCODING_GZIP_DEFLATE, + 'Host' => host_header, + 'X-REMOTE-REQUEST-ID'=> request_id + } Net::HTTP::Get.should_receive(:new).with("/?foo=bar", expected_headers).and_return(request_mock) rest.streaming_request(url, {}) end |