diff options
9 files changed, 175 insertions, 2 deletions
diff --git a/lib/chef/formatters/error_inspectors/api_error_formatting.rb b/lib/chef/formatters/error_inspectors/api_error_formatting.rb index 652d478b40..ec25f4e903 100644 --- a/lib/chef/formatters/error_inspectors/api_error_formatting.rb +++ b/lib/chef/formatters/error_inspectors/api_error_formatting.rb @@ -16,6 +16,8 @@ # limitations under the License. # +require 'chef/http/authenticator' + class Chef module Formatters @@ -65,6 +67,21 @@ E error_description.section("Server Response:",format_rest_error) end + def describe_406_error(error_description, response) + if Chef::JSONCompat.from_json(response.body)["error"] == "invalid-x-ops-server-api-version" + min_version = Chef::JSONCompat.from_json(response.body)["min_version"] + max_version = Chef::JSONCompat.from_json(response.body)["max_version"] + error_description.section("Incompatible server API version:",<<-E) +This version of Chef is not supported by the Chef server you sent this request to +This version of Chef requires a server API version of #{Chef::HTTP::Authenticator::SERVER_API_VERSION} +The Chef server you sent the request to supports a min API version of #{min_version} and a max API version of #{max_version} +Please either update your Chef client or server to be a compatible set +E + else + describe_http_error(error_description) + end + end + def describe_500_error(error_description) error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load the node data. diff --git a/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb b/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb index aa5eb8485d..e011fa9d9b 100644 --- a/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb @@ -72,6 +72,8 @@ E describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable describe_503_error(error_description) + when Net::HTTPNotAcceptable + describe_406_error(error_description, response) else describe_http_error(error_description) end diff --git a/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb b/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb index 0cb849a17f..971dbd664e 100644 --- a/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/cookbook_sync_error_inspector.rb @@ -67,6 +67,8 @@ class Chef describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable, Net::HTTPGatewayTimeOut describe_503_error(error_description) + when Net::HTTPNotAcceptable + describe_406_error(error_description, response) else describe_http_error(error_description) end diff --git a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb index e257ee30c0..d81a9f7cc8 100644 --- a/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/node_load_error_inspector.rb @@ -84,6 +84,8 @@ E describe_500_error(error_description) when Net::HTTPBadGateway, Net::HTTPServiceUnavailable describe_503_error(error_description) + when Net::HTTPNotAcceptable + describe_406_error(error_description, response) else describe_http_error(error_description) end diff --git a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb index f31b348278..dbd23f4a52 100644 --- a/lib/chef/formatters/error_inspectors/registration_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/registration_error_inspector.rb @@ -9,6 +9,8 @@ class Chef # TODO: Lots of duplication with the node_load_error_inspector, just # slightly tweaked to talk about validation keys instead of other keys. class RegistrationErrorInspector + include APIErrorFormatting + attr_reader :exception attr_reader :node_name attr_reader :config @@ -94,6 +96,8 @@ E error_description.section("Relevant Config Settings:",<<-E) chef_server_url "#{server_url}" E + when Net::HTTPNotAcceptable + describe_406_error(error_description, response) when Net::HTTPInternalServerError error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load the node data. diff --git a/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb b/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb index ac19a983af..818228276e 100644 --- a/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb +++ b/lib/chef/formatters/error_inspectors/run_list_expansion_error_inspector.rb @@ -98,6 +98,8 @@ E error_description.section("Possible Causes:",<<-E) * Your client (#{username}) may have misconfigured authorization permissions. E + when Net::HTTPNotAcceptable + describe_406_error(error_description, response) when Net::HTTPInternalServerError error_description.section("Unknown Server Error:",<<-E) The server had a fatal error attempting to load a role. diff --git a/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb new file mode 100644 index 0000000000..4b3b8bff44 --- /dev/null +++ b/spec/unit/formatters/error_inspectors/api_error_formatting_spec.rb @@ -0,0 +1,75 @@ +# +# Author:: Tyler Cloke (<tyler@chef.io>) +# Copyright:: Copyright (c) 2015 Chef Software, 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 'spec_helper' +require 'chef/formatters/error_inspectors/api_error_formatting' + +describe Chef::Formatters::APIErrorFormatting do + let(:class_instance) { (Class.new { include Chef::Formatters::APIErrorFormatting }).new } + let(:error_description) { instance_double(Chef::Formatters::ErrorDescription) } + let(:response) { double("response") } + before do + allow(response).to receive(:body) + end + + + context "when describe_406_error is called" do + context "when response.body['error'] == 'invalid-x-ops-server-api-version'" do + let(:min_version) { "2" } + let(:max_version) { "5" } + let(:return_hash) { + { + "error" => "invalid-x-ops-server-api-version", + "min_version" => min_version, + "max_version" => max_version + } + } + + before do + allow(Chef::JSONCompat).to receive(:from_json).and_return(return_hash) + end + + it "prints an error about client and server API version incompatibility with a min API version" do + expect(error_description).to receive(:section).with("Incompatible server API version:",/a min API version of #{min_version}/) + class_instance.describe_406_error(error_description, response) + end + + it "prints an error about client and server API version incompatibility with a max API version" do + expect(error_description).to receive(:section).with("Incompatible server API version:",/a max API version of #{max_version}/) + class_instance.describe_406_error(error_description, response) + end + end + + context "when response.body['error'] != 'invalid-x-ops-server-api-version'" do + let(:return_hash) { + { + "error" => "some-other-error" + } + } + + before do + allow(Chef::JSONCompat).to receive(:from_json).and_return(return_hash) + end + + it "forwards the error_description to describe_http_error" do + expect(class_instance).to receive(:describe_http_error).with(error_description) + class_instance.describe_406_error(error_description, response) + end + end + end +end diff --git a/spec/unit/http/authenticator_spec.rb b/spec/unit/http/authenticator_spec.rb new file mode 100644 index 0000000000..82d38d6c2b --- /dev/null +++ b/spec/unit/http/authenticator_spec.rb @@ -0,0 +1,69 @@ +# +# Author:: Tyler Cloke (<tyler@chef.io>) +# Copyright:: Copyright (c) 2015 Chef Software, 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 'spec_helper' +require 'chef/http/authenticator' + +describe Chef::HTTP::Authenticator do + let(:class_instance) { Chef::HTTP::Authenticator.new } + let(:method) { double("method") } + let(:url) { double("url") } + let(:headers) { Hash.new } + let(:data) { double("data") } + + before do + allow(class_instance).to receive(:authentication_headers).and_return({}) + end + + context "when handle_request is called" do + shared_examples_for "merging the server API version into the headers" do + it "merges X-Ops-Server-API-Version into the headers" do + # headers returned + expect(class_instance.handle_request(method, url, headers, data)[2]). + to include({'X-Ops-Server-API-Version' => Chef::HTTP::Authenticator::SERVER_API_VERSION}) + end + end + + context "when !sign_requests?" do + before do + allow(class_instance).to receive(:sign_requests?).and_return(false) + end + + it_behaves_like "merging the server API version into the headers" + + it "authentication_headers is not called" do + expect(class_instance).to_not receive(:authentication_headers) + class_instance.handle_request(method, url, headers, data) + end + + end + + context "when sign_requests?" do + before do + allow(class_instance).to receive(:sign_requests?).and_return(true) + end + + it_behaves_like "merging the server API version into the headers" + + it "calls authentication_headers with the proper input" do + expect(class_instance).to receive(:authentication_headers).with(method, url, data).and_return({}) + class_instance.handle_request(method, url, headers, data) + end + end + end +end diff --git a/spec/unit/knife_spec.rb b/spec/unit/knife_spec.rb index a0afafb6b9..7cb16aae2a 100644 --- a/spec/unit/knife_spec.rb +++ b/spec/unit/knife_spec.rb @@ -381,8 +381,8 @@ describe Chef::Knife do allow(response).to receive(:body).and_return(Chef::JSONCompat.to_json(:error => "sad trombone", :min_version => "0", :max_version => "1")) allow(knife).to receive(:run).and_raise(Net::HTTPServerException.new("406 Not Acceptable", response)) knife.run_with_pretty_exceptions - expect(stderr.string).to match(%r[The version of Chef that Knife is using is not supported by the Chef server you sent this request to]) - expect(stderr.string).to match(%r[This version of Chef requires a server API version of #{Chef::HTTP::Authenticator::SERVER_API_VERSION}]) + expect(stderr.string).to include('The version of Chef that Knife is using is not supported by the Chef server you sent this request to') + expect(stderr.string).to include("This version of Chef requires a server API version of #{Chef::HTTP::Authenticator::SERVER_API_VERSION}") end it "formats 500s nicely" do |