From ecd3953c80f0cf49c721441b8e22dead2c6e5918 Mon Sep 17 00:00:00 2001 From: Adam Grare Date: Thu, 24 Feb 2022 09:45:46 -0500 Subject: [PATCH 01/16] Fix EmsAutomation alias pointing to AnsibleTower This should be a generic ExternalAutomationManager not the AnsibleTower provider specifically. --- app/models/aliases/ems_automation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/aliases/ems_automation.rb b/app/models/aliases/ems_automation.rb index 5580892ba7e2..000262634b82 100644 --- a/app/models/aliases/ems_automation.rb +++ b/app/models/aliases/ems_automation.rb @@ -1 +1 @@ -::EmsAutomation = ::ManageIQ::Providers::AnsibleTower::AutomationManager +::EmsAutomation = ::ManageIQ::Providers::ExternalAutomationManager From 5f8f3dca9d84405e2a4dc0f3e5f81ca7fd5e566e Mon Sep 17 00:00:00 2001 From: Adam Grare Date: Thu, 24 Feb 2022 10:56:09 -0500 Subject: [PATCH 02/16] Add Embedded and External Automation Manager locales --- locale/en.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/locale/en.yml b/locale/en.yml index 0190426f2d4d..3f9b0fbdbcd9 100644 --- a/locale/en.yml +++ b/locale/en.yml @@ -1464,6 +1464,7 @@ en: LoadBalancer: Load Balancer ManageIQ::Providers::AutomationManager: Automation Manager ManageIQ::Providers::BaseManager: Provider + ManageIQ::Providers::EmbeddedAutomationManager: Embedded Automation Manager ManageIQ::Providers::EmbeddedAutomationManager::Authentication: Credential ManageIQ::Providers::EmbeddedAutomationManager::ConfigurationScriptSource: Repository ManageIQ::Providers::EmbeddedAnsible::AutomationManager::AmazonCredential: Credential (Amazon) @@ -1480,6 +1481,7 @@ en: ManageIQ::Providers::EmbeddedAnsible::AutomationManager::VmwareCredential: Credential (VMware) ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook: Playbook (Embedded Ansible) ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScriptSource: Repository (Embedded Ansible) + ManageIQ::Providers::ExternalAutomationManager: Automation Manager ManageIQ::Providers::AutomationManager::Authentication: Credential ManageIQ::Providers::AnsibleTower::AutomationManager::AmazonCredential: Credential (Amazon) ManageIQ::Providers::AnsibleTower::AutomationManager::AzureCredential: Credential (Microsoft Azure) From 8730bf2a77915299214ea551630c8d5a864846dc Mon Sep 17 00:00:00 2001 From: Joe Rafaniello Date: Thu, 7 Apr 2022 11:35:37 -0400 Subject: [PATCH 03/16] Bump minimum to 6.0.4.7 for CVE-2022-21831 --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 94bcce25f0e0..a54ffb26707e 100644 --- a/Gemfile +++ b/Gemfile @@ -63,7 +63,7 @@ gem "pg", :require => false gem "pg-dsn_parser", "~>0.1.0", :require => false gem "query_relation", "~>0.1.0", :require => false gem "rack-attack", "~>6.5.0", :require => false -gem "rails", "~>6.0.4", ">=6.0.4.6" +gem "rails", "~>6.0.4", ">=6.0.4.7" gem "rails-i18n", "~>6.x" gem "rake", ">=12.3.3", :require => false gem "rest-client", "~>2.1.0", :require => false From 41010025caee538255ee78cb2ee08e96115c1ecc Mon Sep 17 00:00:00 2001 From: nasark Date: Thu, 7 Apr 2022 10:49:42 -0400 Subject: [PATCH 04/16] add delete cloud database methods --- app/models/cloud_database.rb | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/app/models/cloud_database.rb b/app/models/cloud_database.rb index c0f3a74ce778..f90dd0ed6840 100644 --- a/app/models/cloud_database.rb +++ b/app/models/cloud_database.rb @@ -1,6 +1,7 @@ class CloudDatabase < ApplicationRecord include NewWithTypeStiMixin include ProviderObjectMixin + include SupportsFeatureMixin belongs_to :ext_management_system, :foreign_key => :ems_id, :class_name => "ManageIQ::Providers::CloudManager" belongs_to :cloud_tenant @@ -8,4 +9,33 @@ class CloudDatabase < ApplicationRecord belongs_to :resource_group serialize :extra_attributes + + supports_not :delete + + def delete_cloud_database_queue(userid) + task_opts = { + :action => "deleting Cloud Database for user #{userid}", + :userid => userid + } + + queue_opts = { + :class_name => self.class.name, + :method_name => 'delete_cloud_database', + :instance_id => id, + :role => 'ems_operations', + :queue_name => ext_management_system.queue_name_for_ems_operations, + :zone => ext_management_system.my_zone, + :args => [] + } + + MiqTask.generic_action_with_callback(task_opts, queue_opts) + end + + def delete_cloud_database + raw_delete_cloud_database + end + + def raw_delete_cloud_database + raise NotImplementedError, _("raw_delete_cloud_database must be implemented in a subclass") + end end From 278762b3d4cc28fc24b99c59a9b512f252e4c904 Mon Sep 17 00:00:00 2001 From: Brandon Dunne Date: Tue, 5 Apr 2022 17:29:12 -0400 Subject: [PATCH 05/16] Add a /ping for the remote console worker This will be used for liveness and readiness probes --- lib/remote_console/rack_server.rb | 7 +++++-- spec/lib/remote_console/rack_server_spec.rb | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/lib/remote_console/rack_server.rb b/lib/remote_console/rack_server.rb index 37536709be2b..808994bc7739 100644 --- a/lib/remote_console/rack_server.rb +++ b/lib/remote_console/rack_server.rb @@ -27,8 +27,9 @@ module RemoteConsole class RackServer attr_accessor :logger - RACK_404 = [404, {'Content-Type' => 'text/plain'}, ['Not found']].freeze - RACK_YAY = [-1, {}, []].freeze + RACK_404 = [404, {'Content-Type' => 'text/plain'}, ['Not found']].freeze + RACK_PONG = [200, {'Content-Type' => 'text/plain'}, ['pong']].freeze + RACK_YAY = [-1, {}, []].freeze def initialize(options = {}) @logger = options.fetch(:logger, $remote_console_log) @@ -61,6 +62,8 @@ def call(env) if WebSocket::Driver.websocket?(env) && same_origin_as_host?(env) && exp.present? @logger.info("RemoteConsole connection initiated") init_proxy(env, exp[1]) + elsif same_origin_as_host?(env) && env['REQUEST_URI'].to_s.match?(%r{^/ping$}) + RACK_PONG else @logger.error('Invalid RemoteConsole request or URL') RACK_404 diff --git a/spec/lib/remote_console/rack_server_spec.rb b/spec/lib/remote_console/rack_server_spec.rb index 33e98fc662e2..885a82fc1f1b 100644 --- a/spec/lib/remote_console/rack_server_spec.rb +++ b/spec/lib/remote_console/rack_server_spec.rb @@ -14,7 +14,8 @@ let(:right) { pipes.last } let(:hijack) { double } - let(:env) { {'REQUEST_URI' => "/ws/#{url}", 'rack.hijack' => hijack} } + let(:path) { "/ws/#{url}" } + let(:env) { {'REQUEST_URI' => path, 'rack.hijack' => hijack} } describe '#call' do context 'remote console' do @@ -30,6 +31,14 @@ end end + context 'any other URL' do + let(:path) { '/ping' } + + it 'returns with 200' do + expect(subject.call(env)).to eq(described_class::RACK_PONG) + end + end + context 'any other URL' do let(:url) { 'haha' } From d8cf6fbc800c7377c31cc78fc8e984abc767a79a Mon Sep 17 00:00:00 2001 From: Brandon Dunne Date: Wed, 6 Apr 2022 10:39:32 -0400 Subject: [PATCH 06/16] Don't limit ping requests to the same origin --- lib/remote_console/rack_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote_console/rack_server.rb b/lib/remote_console/rack_server.rb index 808994bc7739..addb74ce410d 100644 --- a/lib/remote_console/rack_server.rb +++ b/lib/remote_console/rack_server.rb @@ -62,7 +62,7 @@ def call(env) if WebSocket::Driver.websocket?(env) && same_origin_as_host?(env) && exp.present? @logger.info("RemoteConsole connection initiated") init_proxy(env, exp[1]) - elsif same_origin_as_host?(env) && env['REQUEST_URI'].to_s.match?(%r{^/ping$}) + elsif env['REQUEST_URI'].to_s.match?(%r{^/ping$}) RACK_PONG else @logger.error('Invalid RemoteConsole request or URL') From f835bd9e2fd9f150880e1e86d7162e3e68a62fe7 Mon Sep 17 00:00:00 2001 From: Brandon Dunne Date: Wed, 6 Apr 2022 15:58:08 -0400 Subject: [PATCH 07/16] Set container port to 3001 to not conflict with HTTPD --- app/models/miq_remote_console_worker.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/models/miq_remote_console_worker.rb b/app/models/miq_remote_console_worker.rb index ac634dc72067..5fc07b6f450e 100644 --- a/app/models/miq_remote_console_worker.rb +++ b/app/models/miq_remote_console_worker.rb @@ -16,4 +16,8 @@ def friendly_name def self.kill_priority MiqWorkerType::KILL_PRIORITY_REMOTE_CONSOLE_WORKERS end + + def container_port + 3001 + end end From ec8e3f5d38500d9b8aa7e25c51fa3c5e6db1afce Mon Sep 17 00:00:00 2001 From: Brandon Dunne Date: Wed, 6 Apr 2022 15:58:37 -0400 Subject: [PATCH 08/16] Enable SSL to the remote console pods --- app/models/miq_remote_console_worker.rb | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/models/miq_remote_console_worker.rb b/app/models/miq_remote_console_worker.rb index 5fc07b6f450e..b6fc146856f8 100644 --- a/app/models/miq_remote_console_worker.rb +++ b/app/models/miq_remote_console_worker.rb @@ -20,4 +20,16 @@ def self.kill_priority def container_port 3001 end + + def configure_service_worker_deployment(definition) + super + + definition[:spec][:template][:spec][:containers].first[:volumeMounts] << {:name => "remote-console-httpd-config", :mountPath => "/etc/httpd/conf.d"} + definition[:spec][:template][:spec][:volumes] << {:name => "remote-console-httpd-config", :configMap => {:name => "remote-console-httpd-configs", :defaultMode => 420}} + + if ENV["REMOTE_CONSOLE_SSL_SECRET_NAME"].present? + definition[:spec][:template][:spec][:containers].first[:volumeMounts] << {:name => "remote-console-httpd-ssl", :mountPath => "/etc/pki/tls"} + definition[:spec][:template][:spec][:volumes] << {:name => "remote-console-httpd-ssl", :secret => {:secretName => ENV["REMOTE_CONSOLE_SSL_SECRET_NAME"], :items => [{:key => "remote_console_crt", :path => "certs/server.crt"}, {:key => "remote_console_key", :path => "private/server.key"}], :defaultMode => 400}} + end + end end From 97fdb8d724c49e1b64cae7cd3e946e4910fafd7a Mon Sep 17 00:00:00 2001 From: Brandon Dunne Date: Wed, 6 Apr 2022 17:22:57 -0400 Subject: [PATCH 09/16] Rework based on discussion in: https://github.com/ManageIQ/manageiq/pull/21809/files#r844048614 --- lib/remote_console/rack_server.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/remote_console/rack_server.rb b/lib/remote_console/rack_server.rb index addb74ce410d..2372a9947808 100644 --- a/lib/remote_console/rack_server.rb +++ b/lib/remote_console/rack_server.rb @@ -58,7 +58,7 @@ def initialize(options = {}) # Rack entrypoint def call(env) - exp = %r{^/ws/console/([a-zA-Z0-9]+)/?$}.match(env['REQUEST_URI']) + exp = env['REQUEST_URI'].to_s.match(%r{^/ws/console/([a-zA-Z0-9]+)/?$}) if WebSocket::Driver.websocket?(env) && same_origin_as_host?(env) && exp.present? @logger.info("RemoteConsole connection initiated") init_proxy(env, exp[1]) From 203ced2c8e18b033b507699a0039be2ce8f16d16 Mon Sep 17 00:00:00 2001 From: nasark Date: Fri, 8 Apr 2022 15:59:57 -0400 Subject: [PATCH 10/16] add create cloud database support --- app/models/cloud_database.rb | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/models/cloud_database.rb b/app/models/cloud_database.rb index f90dd0ed6840..f795c36870b7 100644 --- a/app/models/cloud_database.rb +++ b/app/models/cloud_database.rb @@ -10,8 +10,39 @@ class CloudDatabase < ApplicationRecord serialize :extra_attributes + supports_not :create supports_not :delete + def self.create_cloud_database_queue(userid, ext_management_system, options = {}) + task_opts = { + :action => "creating Cloud Database for user #{userid}", + :userid => userid + } + + queue_opts = { + :class_name => name, + :method_name => 'create_cloud_database', + :role => 'ems_operations', + :queue_name => ext_management_system.queue_name_for_ems_operations, + :zone => ext_management_system.my_zone, + :args => [ext_management_system.id, options] + } + + MiqTask.generic_action_with_callback(task_opts, queue_opts) + end + + def self.create_cloud_database(ems_id, options = {}) + raise ArgumentError, _("ems_id cannot be nil") if ems_id.nil? + + ext_management_system = ExtManagementSystem.find(ems_id) + klass = ext_management_system.class_by_ems(:CloudDatabase) + klass.raw_create_cloud_database(ext_management_system, options) + end + + def self.raw_create_cloud_database(_ext_management_system, _options = {}) + raise NotImplementedError, _("raw_create_cloud_database must be implemented in a subclass") + end + def delete_cloud_database_queue(userid) task_opts = { :action => "deleting Cloud Database for user #{userid}", From fa20307f0ef35576b9f9a50427ba18460cd2d4ec Mon Sep 17 00:00:00 2001 From: nasark Date: Mon, 11 Apr 2022 15:38:33 -0400 Subject: [PATCH 11/16] update cloud database support --- app/models/cloud_database.rb | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/app/models/cloud_database.rb b/app/models/cloud_database.rb index f795c36870b7..0a9d4c3e5846 100644 --- a/app/models/cloud_database.rb +++ b/app/models/cloud_database.rb @@ -12,6 +12,7 @@ class CloudDatabase < ApplicationRecord supports_not :create supports_not :delete + supports_not :update def self.create_cloud_database_queue(userid, ext_management_system, options = {}) task_opts = { @@ -69,4 +70,31 @@ def delete_cloud_database def raw_delete_cloud_database raise NotImplementedError, _("raw_delete_cloud_database must be implemented in a subclass") end + + def update_cloud_database_queue(userid, options = {}) + task_opts = { + :action => "updating Cloud Database for user #{userid}", + :userid => userid + } + + queue_opts = { + :class_name => self.class.name, + :method_name => 'update_cloud_database', + :instance_id => id, + :role => 'ems_operations', + :queue_name => ext_management_system.queue_name_for_ems_operations, + :zone => ext_management_system.my_zone, + :args => [options] + } + + MiqTask.generic_action_with_callback(task_opts, queue_opts) + end + + def update_cloud_database(options = {}) + raw_update_cloud_database(options) + end + + def raw_update_cloud_database(_options = {}) + raise NotImplementedError, _("raw_update_cloud_database must be implemented in a subclass") + end end From 803b5c42e5eb865646542b6cdcf3ceadac8a31a2 Mon Sep 17 00:00:00 2001 From: Adam Grare Date: Mon, 11 Apr 2022 13:38:05 -0400 Subject: [PATCH 12/16] Add common physical_infra manager collections --- .../builder/physical_infra_manager.rb | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/app/models/manageiq/providers/inventory/persister/builder/physical_infra_manager.rb b/app/models/manageiq/providers/inventory/persister/builder/physical_infra_manager.rb index 94c26cf56d5c..261b6b7bb84e 100644 --- a/app/models/manageiq/providers/inventory/persister/builder/physical_infra_manager.rb +++ b/app/models/manageiq/providers/inventory/persister/builder/physical_infra_manager.rb @@ -14,6 +14,13 @@ def physical_chassis add_common_default_values end + def physical_disks + add_properties( + :manager_ref => %i[physical_storage ems_ref], + :parent_inventory_collections => %i[physical_storages] + ) + end + def physical_storages add_common_default_values end @@ -68,6 +75,30 @@ def physical_chassis_hardwares add_hardware_properties(:computer_system, :physical_chassis) end + def physical_server_network_ports + add_properties( + :model_class => ::PhysicalNetworkPort, + :manager_ref => %i[port_type uid_ems], + :parent_inventory_collections => %i[physical_servers] + ) + end + + def physical_storage_network_ports + add_properties( + :model_class => ::PhysicalNetworkPort, + :manager_ref => %i[port_type port_name guest_device], + :parent_inventory_collections => %i[physical_storages] + ) + end + + def physical_switch_network_ports + add_properties( + :model_class => ::PhysicalNetworkPort, + :manager_ref => %i[port_type port_name physical_switch], + :parent_inventory_collections => %i[physical_switches] + ) + end + def physical_storage_hardwares add_hardware_properties(:computer_system, :physical_storages) end From ce6d18334f6adfa6e2407eb3b0edf2c192a48aa1 Mon Sep 17 00:00:00 2001 From: kavyanekkalapu Date: Thu, 14 Apr 2022 15:05:29 -0400 Subject: [PATCH 13/16] update csp configuraton --- config/initializers/secure_headers.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/initializers/secure_headers.rb b/config/initializers/secure_headers.rb index 6725a1edad58..15ca44a72bb1 100644 --- a/config/initializers/secure_headers.rb +++ b/config/initializers/secure_headers.rb @@ -12,13 +12,15 @@ # X-Permitted-Cross-Domain-Policies config.x_xss_protection = "1; mode=block" # Content-Security-Policy + # Need google fonts in fonts_src for https://fonts.googleapis.com/css?family=IBM+Plex+Sans+Condensed%7CIBM+Plex+Sans:400,600&display=swap (For carbon-charts download) config.csp = { :report_only => false, :default_src => ["'self'"], :frame_src => ["'self'"], - :font_src => ["'self'", 'https://fonts.gstatic.com'], + :font_src => ["'self'", 'https://fonts.gstatic.com', "https://fonts.googleapis.com"], + :img_src => ["'self'", "data:"], :connect_src => ["'self'"], - :style_src => ["'unsafe-inline'", "'self'"], + :style_src => ["'unsafe-inline'", "'self'", "https://fonts.googleapis.com", "https://fonts.gstatic.com"], :script_src => ["'unsafe-eval'", "'unsafe-inline'", "'self'"], :report_uri => ["/dashboard/csp_report"] } From 9e52d0d34971aa7a60dc6fd87ea04f522bda41f2 Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Tue, 19 Apr 2022 10:41:38 -0400 Subject: [PATCH 14/16] move create_volume_snapshot to master from providers --- app/models/cloud_volume.rb | 4 ++++ app/models/cloud_volume_snapshot.rb | 30 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/app/models/cloud_volume.rb b/app/models/cloud_volume.rb index 2349618f5775..66bd201d5ca2 100644 --- a/app/models/cloud_volume.rb +++ b/app/models/cloud_volume.rb @@ -189,4 +189,8 @@ def raw_safe_delete_volume def available_vms raise NotImplementedError, _("available_vms must be implemented in a subclass") end + + def create_volume_snapshot_queue(userid, options = {}) + ext_management_system.class_by_ems(:CloudVolumeSnapshot)&.create_snapshot_queue(userid, self, options) + end end diff --git a/app/models/cloud_volume_snapshot.rb b/app/models/cloud_volume_snapshot.rb index 3a11ba436058..b1f5a34048a3 100644 --- a/app/models/cloud_volume_snapshot.rb +++ b/app/models/cloud_volume_snapshot.rb @@ -26,6 +26,36 @@ def my_zone self.class.my_zone(ext_management_system) end + def self.create_snapshot_queue(userid, cloud_volume, options = {}) + raise ArgumentError, "Must provide a cloud volume with a provider" if cloud_volume&.ext_management_system.nil? + + ext_management_system = cloud_volume.ext_management_system + task_opts = { + :action => "creating volume snapshot in #{ext_management_system.inspect} for #{cloud_volume.inspect} with #{options.inspect}", + :userid => userid + } + + queue_opts = { + :class_name => cloud_volume.class.name, + :instance_id => cloud_volume.id, + :method_name => 'create_volume_snapshot', + :role => 'ems_operations', + :queue_name => ext_management_system.queue_name_for_ems_operations, + :zone => my_zone(ext_management_system), + :args => [options] + } + + MiqTask.generic_action_with_callback(task_opts, queue_opts) + end + + def self.create_snapshot(cloud_volume, options) + raw_create_snapshot(cloud_volume, options) + end + + def self.raw_create_snapshot(_cloud_volume, _options) + raise NotImplementedError, _("raw_create_snapshot must be implemented in a subclass") + end + # Delete a cloud volume snapshot as a queued task and return the task id. The # queue name and the queue zone are derived from the EMS. The userid is # optional and defaults to 'system'. From c64f3865a0e151e4ff87c96ee2f17a330ba8e818 Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Wed, 30 Mar 2022 20:05:33 -0400 Subject: [PATCH 15/16] move update_volume_snapshot to master from providers --- app/models/cloud_volume_snapshot.rb | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/models/cloud_volume_snapshot.rb b/app/models/cloud_volume_snapshot.rb index b1f5a34048a3..4614913d4335 100644 --- a/app/models/cloud_volume_snapshot.rb +++ b/app/models/cloud_volume_snapshot.rb @@ -56,6 +56,33 @@ def self.raw_create_snapshot(_cloud_volume, _options) raise NotImplementedError, _("raw_create_snapshot must be implemented in a subclass") end + def update_snapshot_queue(userid = "system", options = {}) + task_opts = { + :action => "updating volume snapshot #{inspect} in #{ext_management_system.inspect} with #{options.inspect}", + :userid => userid + } + + queue_opts = { + :class_name => self.class.name, + :instance_id => id, + :method_name => 'update_snapshot', + :role => 'ems_operations', + :queue_name => ext_management_system.queue_name_for_ems_operations, + :zone => my_zone, + :args => [options] + } + + MiqTask.generic_action_with_callback(task_opts, queue_opts) + end + + def update_snapshot(options = {}) + raw_update_snapshot(options) + end + + def raw_update_snapshot(_options = {}) + raise NotImplementedError, _("update_snapshot must be implemented in a subclass") + end + # Delete a cloud volume snapshot as a queued task and return the task id. The # queue name and the queue zone are derived from the EMS. The userid is # optional and defaults to 'system'. @@ -72,7 +99,6 @@ def delete_snapshot_queue(userid = "system", _options = {}) :class_name => self.class.name, :instance_id => id, :method_name => 'delete_snapshot', - :priority => MiqQueue::HIGH_PRIORITY, :role => 'ems_operations', :queue_name => ext_management_system.queue_name_for_ems_operations, :zone => my_zone, From 77ec69077c44178ba8f79da861290bd844b8bd95 Mon Sep 17 00:00:00 2001 From: Keenan Brock Date: Thu, 31 Mar 2022 17:47:52 -0400 Subject: [PATCH 16/16] default reason for stub_supports stub supports was returning nil for the reason of not supporting a feature This resulted in api tests that were expecting an "unsupported" in the reason but was just nil --- spec/support/supports_helper.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/spec/support/supports_helper.rb b/spec/support/supports_helper.rb index 94210693fa42..dbdd895b2aaa 100644 --- a/spec/support/supports_helper.rb +++ b/spec/support/supports_helper.rb @@ -15,11 +15,10 @@ def stub_supports_not(model, feature = :update, reason = nil) stub_supports(model, feature, :supported => false) - if reason - receive_reason = receive(:unsupported_reason).with(feature).and_return(reason) - allow(model).to(receive_reason) - allow_any_instance_of(model).to(receive_reason) - end + reason ||= SupportsFeatureMixin.reason_or_default(reason) + receive_reason = receive(:unsupported_reason).with(feature).and_return(reason) + allow(model).to(receive_reason) + allow_any_instance_of(model).to(receive_reason) end end end