summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrajaktaPurohit <prajakta@opscode.com>2014-01-19 18:07:17 -0800
committerPrajakta Purohit <prajakta@opscode.com>2014-02-07 15:31:10 -0800
commita4cdce667a1bc1828eea875bdf3f49af6d96da70 (patch)
treeccf5fb1bf44f0eda5ad0470f0957dd31864c62f0
parent29e732d97ec7e28b2111aca9f93edfd1bc257c2d (diff)
downloadchef-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.rb6
-rw-r--r--lib/chef/http/remote_request_id.rb46
-rw-r--r--lib/chef/knife/raw.rb1
-rw-r--r--lib/chef/request_id.rb37
-rw-r--r--lib/chef/resource_reporter.rb11
-rw-r--r--lib/chef/rest.rb6
-rw-r--r--lib/chef/run_status.rb5
-rw-r--r--lib/chef/server_api.rb4
-rw-r--r--spec/unit/client_spec.rb3
-rw-r--r--spec/unit/knife_spec.rb55
-rw-r--r--spec/unit/resource_reporter_spec.rb2
-rw-r--r--spec/unit/rest_spec.rb51
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