diff --git a/app/models/firmware_binary.rb b/app/models/firmware_binary.rb new file mode 100644 index 000000000000..1a8772fcee72 --- /dev/null +++ b/app/models/firmware_binary.rb @@ -0,0 +1,15 @@ +class FirmwareBinary < ApplicationRecord + validates :name, :presence => true + has_many :endpoints, :dependent => :destroy, :as => :resource, :inverse_of => :resource + has_many :firmware_binary_firmware_constraints, :dependent => :destroy + has_many :firmware_constraints, :through => :firmware_binary_firmware_constraints + belongs_to :firmware_registry + + def allow_duplicate_endpoint_url? + true + end + + def urls + endpoints.map(&:url) + end +end diff --git a/app/models/firmware_binary_firmware_constraint.rb b/app/models/firmware_binary_firmware_constraint.rb new file mode 100644 index 000000000000..25b567f9ea85 --- /dev/null +++ b/app/models/firmware_binary_firmware_constraint.rb @@ -0,0 +1,6 @@ +class FirmwareBinaryFirmwareConstraint < ApplicationRecord + self.table_name = 'firmware_binaries_firmware_constraints' + + belongs_to :firmware_binary + belongs_to :firmware_constraint +end diff --git a/app/models/firmware_constraint.rb b/app/models/firmware_constraint.rb new file mode 100644 index 000000000000..c68e80a2488b --- /dev/null +++ b/app/models/firmware_constraint.rb @@ -0,0 +1,9 @@ +class FirmwareConstraint < ApplicationRecord + has_many :firmware_binary_firmware_constraints, :dependent => :destroy + has_many :firmware_binaries, :through => :firmware_binary_firmware_constraints + + # Firmware constraints are nameless, but we need to show something on the UI. + def name + _('Firmware Constraint') + end +end diff --git a/app/models/firmware_registry.rb b/app/models/firmware_registry.rb new file mode 100644 index 000000000000..87ebc7529dc1 --- /dev/null +++ b/app/models/firmware_registry.rb @@ -0,0 +1,37 @@ +class FirmwareRegistry < ApplicationRecord + include NewWithTypeStiMixin + + has_many :firmware_binaries, :dependent => :destroy + has_one :endpoint, :as => :resource, :dependent => :destroy, :inverse_of => :resource + has_one :authentication, :as => :resource, :dependent => :destroy, :inverse_of => :resource + + validates :name, :presence => true, :uniqueness => true + + def sync_fw_binaries_queue + MiqQueue.put_unless_exists( + :class_name => self.class.name, + :instance_id => id, + :method_name => 'sync_fw_binaries' + ) + end + + def sync_fw_binaries + _log.info("Synchronizing FirmwareBinaries from #{self.class.name} [#{id}|#{name}]...") + sync_fw_binaries_raw + self.last_refresh_error = nil + _log.info("Synchronizing FirmwareBinaries from #{self.class.name} [#{id}|#{name}]... Complete") + rescue MiqException::Error => e + self.last_refresh_error = e + ensure + self.last_refresh_on = Time.now.utc + save! + end + + def sync_fw_binaries_raw + raise NotImplementedError, 'Must be implemented in subclass' + end + + def self.display_name(number = 1) + n_('Firmware Registry', 'Firmware Registries', number) + end +end diff --git a/app/models/firmware_registry_simple.rb b/app/models/firmware_registry_simple.rb new file mode 100644 index 000000000000..3e345dadf354 --- /dev/null +++ b/app/models/firmware_registry_simple.rb @@ -0,0 +1,61 @@ +class FirmwareRegistrySimple < FirmwareRegistry + CONSTRAINTS = %i[manufacturer model].freeze + + def sync_fw_binaries_raw + remote_binaries.each do |binary_hash| + binary = FirmwareBinary.find_or_create_by(:firmware_registry => self, :external_id => binary_hash['id']) + _log.info("Updating FirmwareBinary [#{binary.id} | #{binary.name}]...") + + binary.name = binary_hash['filename'] || binary_hash['id'] + binary.description = binary_hash['description'] + binary.version = binary_hash['version'] + binary.save! + + unless binary.urls.sort == binary_hash['urls'].sort + _log.info("Updating FirmwareBinary [#{binary.id} | #{binary.name}] endpoints...") + binary.endpoints.destroy_all + binary.endpoints = binary_hash['urls'].map { |url| Endpoint.create(:url => url) } + end + + currents = binary.firmware_constraints.map { |c| c.attributes.slice(*CONSTRAINTS) } + remotes = binary_hash['compatible_server_models'].map { |r| r.symbolize_keys.slice(*CONSTRAINTS) } + unless currents.map(&:to_a).sort == remotes.map(&:to_a).sort + _log.info("Updating FirmwareBinary [#{binary.id} | #{binary.name}] constraints...") + binary.firmware_constraints = remotes.map do |remote| + FirmwareConstraint.find_or_create_by(remote) + end + end + + _log.info("Updating FirmwareBinary [#{binary.id} | #{binary.name}]... completed.") + end + end + + def remote_binaries + self.class.fetch_from_remote( + endpoint.url, + authentication.userid, + authentication.password, + :verify_ssl => endpoint.security_protocol + ) + end + + def self.fetch_from_remote(url, username, password, verify_ssl: OpenSSL::SSL::VERIFY_PEER) + uri = URI.parse(url) + request = Net::HTTP::Get.new(uri.request_uri) + request['Content-Type'] = 'application/json' + request.basic_auth(username, password) + response = Net::HTTP.new(uri.host, uri.port).tap do |http| + http.open_timeout = 5.seconds + http.use_ssl = url.start_with?('https') + http.verify_mode = verify_ssl + end.request(request) + + raise MiqException::Error, "Bad status returned: #{response.code}" if response.code.to_i != 200 + + JSON.parse(response.body) + rescue SocketError => e + raise MiqException::Error, e + rescue JSON::ParserError => e + raise MiqException::Error, e + end +end diff --git a/spec/factories/firmware_binary.rb b/spec/factories/firmware_binary.rb new file mode 100644 index 000000000000..8ff728787f94 --- /dev/null +++ b/spec/factories/firmware_binary.rb @@ -0,0 +1,14 @@ +FactoryBot.define do + factory :firmware_binary do + sequence(:name) { |n| "firmware_binary_#{seq_padded_for_sorting(n)}" } + sequence(:description) { 'Firmware Binary Description' } + sequence(:version) { 'v1.2.3' } + + trait :with_endpoints do + after(:create) do |binary| + binary.endpoints << FactoryBot.create(:endpoint, :url => 'http://test.binary.1', :resource => binary) + binary.endpoints << FactoryBot.create(:endpoint, :url => 'http://test.binary.2', :resource => binary) + end + end + end +end diff --git a/spec/factories/firmware_constraint.rb b/spec/factories/firmware_constraint.rb new file mode 100644 index 000000000000..5b1b9faaee0d --- /dev/null +++ b/spec/factories/firmware_constraint.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :firmware_constraint do + sequence(:manufacturer) { |n| "manufacturer_#{seq_padded_for_sorting(n)}" } + sequence(:model) { |n| "model_#{seq_padded_for_sorting(n)}" } + end +end diff --git a/spec/factories/firmware_registry.rb b/spec/factories/firmware_registry.rb new file mode 100644 index 000000000000..908e4be8710b --- /dev/null +++ b/spec/factories/firmware_registry.rb @@ -0,0 +1,9 @@ +FactoryBot.define do + factory :firmware_registry do + sequence(:name) { |n| "firmware_registry_#{seq_padded_for_sorting(n)}" } + endpoint { |n| FactoryBot.create(:endpoint, :url => "http://test.registry.#{n}") } + authentication { FactoryBot.create(:authentication) } + end + + factory :firmware_registry_simple, :parent => :firmware_registry, :class => 'FirmwareRegistrySimple' +end diff --git a/spec/models/firmware_binary_spec.rb b/spec/models/firmware_binary_spec.rb new file mode 100644 index 000000000000..2e7e773cdafd --- /dev/null +++ b/spec/models/firmware_binary_spec.rb @@ -0,0 +1,49 @@ +describe FirmwareBinary do + subject { FactoryBot.create(:firmware_binary) } + + describe '#allow_duplicate_endpoint_url?' do + let(:binary1) { FactoryBot.create(:firmware_binary) } + let(:binary2) { FactoryBot.create(:firmware_binary) } + + it 'has the flag set' do + expect(subject.allow_duplicate_endpoint_url?).to eq(true) + end + + it 'two different binaries are allowed to have same url' do + expect do + binary1.endpoints << FactoryBot.create(:endpoint, :url => 'same-url', :resource => binary1) + binary2.endpoints << FactoryBot.create(:endpoint, :url => 'same-url', :resource => binary2) + end.not_to raise_error + end + end + + describe '#urls' do + it 'lists from all endpoints' do + subject.endpoints << FactoryBot.create(:endpoint, :url => 'url1') + subject.endpoints << FactoryBot.create(:endpoint, :url => 'url2') + expect(subject.urls).to eq(%w[url1 url2]) + end + end + + describe '#has_many endpoints' do + before { subject.endpoints = [endpoint1, endpoint2] } + let(:endpoint1) { FactoryBot.create(:endpoint) } + let(:endpoint2) { FactoryBot.create(:endpoint) } + + it 'one to many connection exists' do + expect(subject.endpoints).to match_array([endpoint1, endpoint2]) + end + end + + describe '#has_many firmware_constraints' do + before { subject.firmware_constraints = [constraint1, constraint2] } + let(:constraint1) { FactoryBot.create(:firmware_constraint) } + let(:constraint2) { FactoryBot.create(:firmware_constraint) } + + it 'many to many connection exists' do + expect(subject.firmware_constraints).to match_array([constraint1, constraint2]) + expect(constraint1.firmware_binaries).to match_array([subject]) + expect(constraint2.firmware_binaries).to match_array([subject]) + end + end +end diff --git a/spec/models/firmware_registry_simple/data/sample_firmware_binaries.json b/spec/models/firmware_registry_simple/data/sample_firmware_binaries.json new file mode 100644 index 000000000000..3fe585050abe --- /dev/null +++ b/spec/models/firmware_registry_simple/data/sample_firmware_binaries.json @@ -0,0 +1,54 @@ +[ + { + "description": "DELL BIOS update", + "urls": [ + "http://172.16.93.126:8080/files/dell-bios-2019-03-23.bin", + "https://172.16.93.126:8443/files/dell-bios-2019-03-23.bin" + ], + "compatible_server_models": [ + { + "model": "Common Model", + "manufacturer": "Common Manufacturer" + } + ], + "id": 1, + "version": "10.4.3", + "filename": "dell-bios-2019-03-23.bin" + }, + { + "description": "DELL BIOS update", + "urls": [ + "http://172.16.93.126:8080/files/dell-bios-2019-04-13.bin", + "https://172.16.93.126:8443/files/dell-bios-2019-04-13.bin" + ], + "compatible_server_models": [ + { + "model": "Common Model", + "manufacturer": "Common Manufacturer" + } + ], + "id": 2, + "version": "10.4.4", + "filename": "dell-bios-2019-04-13.bin" + }, + { + "description": "SuperMicro NIC firmware", + "urls": [ + "http://172.16.93.126:8080/files/nic-update-3.exe", + "https://172.16.93.126:8443/files/nic-update-3.exe" + ], + "compatible_server_models": [ + { + "model": "SYS-6033A-GHJ7Z", + "manufacturer": "SuperMicro" + }, + { + "model": "SYS-5019D-FN8TP", + "manufacturer": "SuperMicro" + } + ], + "id": 3, + "version": "4.3.2-101-z", + "filename": "nic-update-3.exe" + } +] diff --git a/spec/models/firmware_registry_simple/firmware_registry_simple_spec.rb b/spec/models/firmware_registry_simple/firmware_registry_simple_spec.rb new file mode 100644 index 000000000000..e9052005cae4 --- /dev/null +++ b/spec/models/firmware_registry_simple/firmware_registry_simple_spec.rb @@ -0,0 +1,159 @@ +describe FirmwareRegistrySimple do + before do + VCR.configure do |config| + config.filter_sensitive_data('AUTHORIZATION') { Base64.encode64("#{user}:#{pass}").chomp } + config.before_record { |interaction| interaction.request.uri.downcase! } + end + end + + let(:host) { Rails.application.secrets.firmware_registry_simple.try(:[], 'host') || 'firmware_registry_simple_host' } + let(:user) { Rails.application.secrets.firmware_registry_simple.try(:[], 'userid') || 'username' } + let(:pass) { Rails.application.secrets.firmware_registry_simple.try(:[], 'password') || 'password' } + let(:url) { "http://#{host}/images/" } + + describe '.fetch_from_remote', :vcr do + context 'when 200' do + it 'list of firmware binaries is returned' do + expect(described_class.fetch_from_remote(url, user, pass)).to be_an Array + end + end + + context 'when bad credentials' do + it 'managed error is raised' do + expect { described_class.fetch_from_remote(url, 'bad-username', 'bad-password') }.to raise_error(MiqException::Error) + end + end + + context 'when bad host' do + it 'managed error is raised' do + expect { described_class.fetch_from_remote('http://bad.host', 'user', 'pass') }.to raise_error(MiqException::Error) + end + end + + context 'when bad json' do + it 'managed error is raised' do + expect { described_class.fetch_from_remote(url, user, pass) }.to raise_error(MiqException::Error) + end + end + end + + describe '#sync_fw_binaries_raw' do + before { allow(described_class).to receive(:fetch_from_remote).and_return(json) } + let(:json) { [] } + subject { FactoryBot.create(:firmware_registry_simple) } + + context 'when on empty database' do + before { assert_counts(:firmware_registry => 0, :firmware_binary => 0, :firmware_constraint => 0) } + + context 'using simple inline fixture' do + let(:json) do + [ + { + 'description' => 'Some Binary Description', + 'urls' => [ + 'http://url.net =>1000', + 'https://url.net =>1443', + ], + 'compatible_server_models' => [ + { + 'model' => 'Common Model', + 'manufacturer' => 'Common Manufacturer' + } + ], + 'version' => 'v1.2.3', + 'filename' => 'some.binary.name' + } + ] + end + + it 'binary is inventoried' do + subject.sync_fw_binaries_raw + subject.reload + assert_counts(:firmware_binary => 1) + expect(FirmwareBinary.first).to have_attributes( + :name => 'some.binary.name', + :description => 'Some Binary Description', + :version => 'v1.2.3', + :firmware_registry => subject, + :firmware_constraints => [FirmwareConstraint.first] + ) + end + + it 'constraint is inventoried' do + subject.sync_fw_binaries_raw + subject.reload + assert_counts(:firmware_constraint => 1) + expect(FirmwareConstraint.first).to have_attributes( + :manufacturer => 'Common Manufacturer', + :model => 'Common Model', + :firmware_binaries => [FirmwareBinary.first] + ) + end + + it 'refresh twice' do + 2.times { subject.sync_fw_binaries_raw } + assert_counts(:firmware_registry => 1, :firmware_binary => 1, :firmware_constraint => 1) + end + end + + context 'using json fixture' do + let(:json) { JSON.parse(File.read(File.join(File.dirname(__FILE__), 'data', 'sample_firmware_binaries.json'))) } + + it 'inventories specific entities' do + subject.sync_fw_binaries_raw + subject.reload + assert_counts(:firmware_registry => 1, :firmware_binary => json.size, :firmware_constraint => 3) + expect(FirmwareBinary.find_by(:name => 'dell-bios-2019-03-23.bin')).to have_attributes( + :description => 'DELL BIOS update', + :version => '10.4.3', + :firmware_registry => subject, + :firmware_constraints => [FirmwareConstraint.find_by(:model => 'Common Model')] + ) + end + end + end + + context 'when on existing database' do + let!(:binary) { FactoryBot.create(:firmware_binary, :firmware_registry => subject) } + let!(:constraint) { FactoryBot.create(:firmware_constraint, :firmware_binaries => [binary]) } + let(:json) do + [ + { + 'description' => 'Updated Description', + 'urls' => [ + 'http://url.net =>1000', + 'https://url.net =>1443', + ], + 'compatible_server_models' => [ + { + 'model' => 'Updated Model', + 'manufacturer' => 'Updated Manufacturer' + } + ], + 'version' => 'v1.2.3', + 'filename' => binary.name + } + ] + end + + it 'binary is updated' do + subject.sync_fw_binaries_raw + subject.reload + assert_counts(:firmware_binary => 1) + expect(FirmwareBinary.first).to have_attributes( + :name => binary.name, + :description => 'Updated Description', + :version => 'v1.2.3', + :firmware_registry => subject, + :firmware_constraints => [FirmwareConstraint.find_by(:model => 'Updated Model')] + ) + end + end + + def assert_counts(counts) + expect(FirmwareRegistry.count).to eq(counts[:firmware_registry]) if counts.include?(:firmware_registry) + expect(FirmwareBinary.count).to eq(counts[:firmware_binary]) if counts.include?(:firmware_binary) + expect(FirmwareConstraint.count).to eq(counts[:firmware_constraint]) if counts.include?(:firmware_constraint) + end + end +end diff --git a/spec/models/firmware_registry_spec.rb b/spec/models/firmware_registry_spec.rb new file mode 100644 index 000000000000..354c724ba1d7 --- /dev/null +++ b/spec/models/firmware_registry_spec.rb @@ -0,0 +1,64 @@ +describe FirmwareRegistry do + before { EvmSpecHelper.create_guid_miq_server_zone } + subject { FactoryBot.create(:firmware_registry) } + + describe '#destroy' do + let!(:binary) { FactoryBot.create(:firmware_binary, :with_endpoints, :firmware_registry => subject) } + let!(:constraint) { FactoryBot.create(:firmware_constraint, :firmware_binaries => [binary]) } + + it 'deletes repository in cascade' do + expect(FirmwareRegistry.count).to eq(1) + expect(FirmwareBinary.count).to eq(1) + expect(Authentication.count).to eq(1) + expect(Endpoint.count).to eq(1 + 2) # registry endpoint + 2*binary endpoint + expect(FirmwareBinaryFirmwareConstraint.count).to eq(1) + expect(FirmwareConstraint.count).to eq(1) + + subject.destroy + + expect(FirmwareRegistry.count).to eq(0) + expect(FirmwareBinary.count).to eq(0) + expect(Endpoint.count).to eq(0) + expect(Authentication.count).to eq(0) + expect(FirmwareBinaryFirmwareConstraint.count).to eq(0) + expect(FirmwareConstraint.count).to eq(1) + end + end + + it '#sync_fw_binaries_raw' do + expect { subject.sync_fw_binaries_raw }.to raise_error(NotImplementedError) + end + + it '#sync_fw_binaries_queue' do + subject.sync_fw_binaries_queue + expect(MiqQueue.count).to eq(1) + expect(MiqQueue.find_by(:method_name => 'sync_fw_binaries')).to have_attributes( + :class_name => described_class.name, + :instance_id => subject.id + ) + end + + describe '#sync_fw_binaries' do + context 'when sync succeeds' do + before { allow(subject).to receive(:sync_fw_binaries_raw).and_return(nil) } + + it 'last_refresh_[on|error] is updated' do + subject.sync_fw_binaries + subject.reload + expect(subject.last_refresh_error).to be_nil + expect(subject.last_refresh_on).to be_within(5.seconds).of(Time.now.utc) + end + end + + context 'when sync errors' do + before { allow(subject).to receive(:sync_fw_binaries_raw).and_raise(MiqException::Error.new('MESSAGE')) } + + it 'last_refresh_[on|error] is updated' do + subject.sync_fw_binaries + subject.reload + expect(subject.last_refresh_error).to eq('MESSAGE') + expect(subject.last_refresh_on).to be_within(5.seconds).of(Time.now.utc) + end + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 03ccd695233a..acda09ee25a0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -90,6 +90,7 @@ c.default_cassette_options = { :allow_unused_http_interactions => false } + c.configure_rspec_metadata! # Set your config/secrets.yml file secrets = Rails.application.secrets diff --git a/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_200/list_of_firmware_binaries_is_returned.yml b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_200/list_of_firmware_binaries_is_returned.yml new file mode 100644 index 000000000000..573c788baae0 --- /dev/null +++ b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_200/list_of_firmware_binaries_is_returned.yml @@ -0,0 +1,89 @@ +--- +http_interactions: +- request: + method: get + uri: http://firmware_registry_simple_host/images/ + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Content-Type: + - application/json + Authorization: + - Basic AUTHORIZATION + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json + Content-Length: + - '1423' + Server: + - Werkzeug/0.15.4 Python/3.5.2 + Date: + - Tue, 21 May 2019 15:03:46 GMT + body: + encoding: UTF-8 + string: | + [ + { + "description": "DELL BIOS update", + "urls": [ + "http://172.16.93.126:8080/files/dell-bios-2019-03-23.bin", + "https://172.16.93.126:8443/files/dell-bios-2019-03-23.bin" + ], + "compatible_server_models": [ + { + "model": "PowerEdge R740", + "manufacturer": "Dell" + } + ], + "id": 1, + "version": "10.4.3" + }, + { + "description": "DELL BIOS update", + "urls": [ + "http://172.16.93.126:8080/files/dell-bios-2019-04-13.bin", + "https://172.16.93.126:8443/files/dell-bios-2019-04-13.bin" + ], + "compatible_server_models": [ + { + "model": "PowerEdge R740", + "manufacturer": "Dell" + } + ], + "id": 2, + "version": "10.4.4" + }, + { + "description": "SuperMicro NIC firmware", + "urls": [ + "http://172.16.93.126:8080/files/nic-update-3.exe", + "https://172.16.93.126:8443/files/nic-update-3.exe" + ], + "compatible_server_models": [ + { + "model": "SYS-5019D-FN8TP", + "manufacturer": "SuperMicro" + }, + { + "model": "SYS-6033A-GHJ7Z", + "manufacturer": "SuperMicro" + } + ], + "id": 3, + "version": "4.3.2-101-z" + } + ] + http_version: + recorded_at: Tue, 21 May 2019 15:03:46 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_credentials/managed_error_is_raised.yml b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_credentials/managed_error_is_raised.yml new file mode 100644 index 000000000000..dc7366261cba --- /dev/null +++ b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_credentials/managed_error_is_raised.yml @@ -0,0 +1,41 @@ +--- +http_interactions: +- request: + method: get + uri: http://firmware_registry_simple_host/images/ + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Content-Type: + - application/json + Authorization: + - Basic YmFkLXVzZXJuYW1lOmJhZC1wYXNzd29yZA== + response: + status: + code: 401 + message: UNAUTHORIZED + headers: + Content-Type: + - application/json + Content-Length: + - '242' + Server: + - Werkzeug/0.15.4 Python/3.5.2 + Date: + - Tue, 21 May 2019 15:05:18 GMT + body: + encoding: UTF-8 + string: | + { + "message": "The server could not verify that you are authorized to access the URL requested. You either supplied the wrong credentials (e.g. a bad FIRMWARE_REGISTRY_SIMPLE_PASSWORDword), or your browser doesn't understand how to supply the credentials required." + } + http_version: + recorded_at: Tue, 21 May 2019 15:05:18 GMT +recorded_with: VCR 3.0.3 diff --git a/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_json/managed_error_is_raised.yml b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_json/managed_error_is_raised.yml new file mode 100644 index 000000000000..f63bc055ed1c --- /dev/null +++ b/spec/vcr_cassettes/FirmwareRegistrySimple/_fetch_from_remote/when_bad_json/managed_error_is_raised.yml @@ -0,0 +1,41 @@ +--- +http_interactions: +- request: + method: get + uri: http://firmware_registry_simple_host/images/ + body: + encoding: US-ASCII + string: '' + headers: + Accept-Encoding: + - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 + Accept: + - "*/*" + User-Agent: + - Ruby + Content-Type: + - application/json + Authorization: + - Basic AUTHORIZATION + response: + status: + code: 200 + message: OK + headers: + Content-Type: + - application/json + Content-Length: + - '1423' + Server: + - Werkzeug/0.15.4 Python/3.5.2 + Date: + - Tue, 21 May 2019 15:05:17 GMT + body: + encoding: UTF-8 + string: | + [ + INVALID JSON!!! + ] + http_version: + recorded_at: Tue, 21 May 2019 15:05:18 GMT +recorded_with: VCR 3.0.3