From b3b5cc2435a66c9c2f7cedecc72a8cb123a19545 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miha=20Ple=C5=A1ko?= Date: Fri, 17 Feb 2017 15:13:32 +0100 Subject: [PATCH] Support operations on CloudObjectStoreContainer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CloudObjectStoreContainer model had no support for operations (e.g. delete) since they were not supported by backend. Well, now the backend supports deleting bucket hence we add "Configuration" menu option for this. This commit brings a new mid-page menu item "Configuration->Remove Object Storage Container" to the following pages: * cloud object container details page * cloud object container full list * cloud object container list per StorageManager Signed-off-by: Miha Pleško --- .../application_controller/ci_processing.rb | 50 ++++++ ...cloud_object_store_container_controller.rb | 11 +- app/controllers/ems_common.rb | 8 +- .../cloud_object_store_container_center.rb | 24 +++ .../cloud_object_store_containers_center.rb | 26 +++ .../ci_processing_spec.rb | 156 ++++++++++++++++++ .../cloud_object_store_container_spec.rb | 68 +++++++- .../controllers/ems_common_controller_spec.rb | 5 + 8 files changed, 342 insertions(+), 6 deletions(-) diff --git a/app/controllers/application_controller/ci_processing.rb b/app/controllers/application_controller/ci_processing.rb index 6c3c0dc3c7d..edeeca64c59 100644 --- a/app/controllers/application_controller/ci_processing.rb +++ b/app/controllers/application_controller/ci_processing.rb @@ -1840,6 +1840,49 @@ def vm_button_operation(method, display_name, partial_after_single_selection = n vms.count end + def process_cloud_object_storage_buttons(pressed) + assert_privileges(pressed) + + klass = get_rec_cls + task = pressed.sub("#{klass.name.underscore.to_sym}_", "") + + return tag(klass) if task == "tag" + + cloud_object_store_button_operation(klass, task) + end + + def cloud_object_store_button_operation(klass, task) + method = "#{task}_#{klass.name.underscore.to_sym}" + display_name = _(task.capitalize) + + items = [] + + # Either a list or coming from a different controller + if @lastaction == "show_list" || !%w(cloud_object_store_container).include?(request.parameters["controller"]) + items = find_checked_items + if items.empty? + add_flash(_("No %{model} were selected for %{task}") % + {:model => ui_lookup(:models => klass.name), :task => display_name}, :error) + elsif klass.find(items).any? { |item| !item.supports?(task) } + add_flash(_("%{task} does not apply to at least one of the selected items") % + {:task => display_name}, :error) + else + process_objects(items, method, display_name) + end + elsif params[:id].nil? || klass.find_by(:id => params[:id]).nil? + add_flash(_("%{record} no longer exists") % + {:record => ui_lookup(:table => request.parameters["controller"])}, :error) + show_list unless @explorer + @refresh_partial = "layouts/gtl" + elsif !klass.find_by(:id => params[:id]).supports?(task) + add_flash(_("%{task} does not apply to this item") % + {:task => display_name}, :error) + else + items.push(params[:id]) + process_objects(items, method, display_name) unless items.empty? + end + end + def get_rec_cls case request.parameters["controller"] when "miq_template" @@ -1848,6 +1891,10 @@ def get_rec_cls return OrchestrationStack when "service" return Service + when "cloud_object_store_container" + CloudObjectStoreContainer + when "ems_storage" + CloudObjectStoreContainer else return VmOrTemplate end @@ -1864,6 +1911,9 @@ def process_objects(objs, task, display_name = nil) when "VmOrTemplate" objs, _objs_out_reg = filter_ids_in_region(objs, "VM") unless VmOrTemplate::REMOTE_REGION_TASKS.include?(task) klass = Vm + when "CloudObjectStoreContainer" + objs, _objs_out_reg = filter_ids_in_region(objs, "CloudObjectStoreContainer") + klass = CloudObjectStoreContainer end assert_rbac(current_user, get_rec_cls, objs) diff --git a/app/controllers/cloud_object_store_container_controller.rb b/app/controllers/cloud_object_store_container_controller.rb index 97b0ae9643e..580b8e3077c 100644 --- a/app/controllers/cloud_object_store_container_controller.rb +++ b/app/controllers/cloud_object_store_container_controller.rb @@ -16,7 +16,16 @@ def breadcrumb_name(_model) def button @edit = session[:edit] # Restore @edit for adv search box params[:page] = @current_page unless @current_page.nil? # Save current page for list refresh - return tag("CloudObjectStoreContainer") if params[:pressed] == 'cloud_object_store_container_tag' + + process_cloud_object_storage_buttons(params[:pressed]) + + if !@flash_array.nil? && params[:pressed].ends_with?("delete") + javascript_redirect :action => 'show_list', + :flash_msg => @flash_array[0][:message], + :flash_error => @flash_array[0][:level] == :error + elsif !@flash_array.nil? + render_flash unless performed? + end end def self.display_methods diff --git a/app/controllers/ems_common.rb b/app/controllers/ems_common.rb index 1c6845252b1..d1bfdfe5754 100644 --- a/app/controllers/ems_common.rb +++ b/app/controllers/ems_common.rb @@ -332,7 +332,6 @@ def button # Handle buttons from sub-items screen if params[:pressed].starts_with?("availability_zone_", "cloud_network_", - "cloud_object_store_container_", "cloud_subnet_", "cloud_tenant_", "cloud_volume_", @@ -380,7 +379,6 @@ def button # Edit Tags for Network Manager Relationship pages when "availability_zone_tag" then tag(AvailabilityZone) when "cloud_network_tag" then tag(CloudNetwork) - when "cloud_object_store_container_tag" then tag(CloudObjectStoreContainer) when "cloud_subnet_tag" then tag(CloudSubnet) when "cloud_tenant_tag" then tag(CloudTenant) when "cloud_volume_tag" then tag(CloudVolume) @@ -416,6 +414,8 @@ def button show # Handle EMS buttons end end + elsif params[:pressed].starts_with?("cloud_object_store_") + process_cloud_object_storage_buttons(params[:pressed]) else @refresh_div = "main_div" # Default div for button.rjs to refresh redirect_to :action => "new" if params[:pressed] == "new" @@ -496,7 +496,9 @@ def button end if !@flash_array.nil? && params[:pressed] == "#{@table_name}_delete" && @single_delete - javascript_redirect :action => 'show_list', :flash_msg => @flash_array[0][:message] # redirect to build the retire screen + javascript_redirect :action => 'show_list', + :flash_msg => @flash_array[0][:message], + :flash_error => @flash_array[0][:level] == :error elsif params[:pressed] == "host_aggregate_edit" javascript_redirect :controller => "host_aggregate", :action => "edit", :id => find_checked_items[0] elsif params[:pressed] == "cloud_tenant_edit" diff --git a/app/helpers/application_helper/toolbar/cloud_object_store_container_center.rb b/app/helpers/application_helper/toolbar/cloud_object_store_container_center.rb index 025df8b0a19..810ba005c54 100644 --- a/app/helpers/application_helper/toolbar/cloud_object_store_container_center.rb +++ b/app/helpers/application_helper/toolbar/cloud_object_store_container_center.rb @@ -1,4 +1,28 @@ class ApplicationHelper::Toolbar::CloudObjectStoreContainerCenter < ApplicationHelper::Toolbar::Basic + button_group( + 'cloud_object_store_container_vmdb', + [ + select( + :cloud_object_store_container_vmdb_choice, + 'fa fa-cog fa-lg', + t = N_('Configuration'), + t, + :items => [ + button( + :cloud_object_store_container_delete, + 'pficon pficon-delete fa-lg', + N_('Remove Object Storage Container'), + N_('Remove Object Storage Container'), + :url_parms => "main_div", + :confirm => N_("Warning: The selected Object Storage Container and ALL related Objects will be "\ + "permanently removed!"), + :klass => ApplicationHelper::Button::GenericFeatureButtonWithDisable, + :options => {:feature => :delete} + ), + ] + ), + ] + ) button_group('cloud_object_store_container_policy', [ select( :cloud_object_store_container_policy_choice, diff --git a/app/helpers/application_helper/toolbar/cloud_object_store_containers_center.rb b/app/helpers/application_helper/toolbar/cloud_object_store_containers_center.rb index 8cf9959fab6..1d475c7b5aa 100644 --- a/app/helpers/application_helper/toolbar/cloud_object_store_containers_center.rb +++ b/app/helpers/application_helper/toolbar/cloud_object_store_containers_center.rb @@ -1,4 +1,30 @@ class ApplicationHelper::Toolbar::CloudObjectStoreContainersCenter < ApplicationHelper::Toolbar::Basic + button_group( + 'cloud_object_store_container_vmdb', + [ + select( + :cloud_object_store_container_vmdb_choice, + 'fa fa-cog fa-lg', + t = N_('Configuration'), + t, + :enabled => false, + :onwhen => "1+", + :items => [ + button( + :cloud_object_store_container_delete, + 'pficon pficon-delete fa-lg', + N_('Remove selected Object Storage Containers'), + N_('Remove Object Storage Containers'), + :url_parms => "main_div", + :confirm => N_("Warning: The selected Object Storage Containers and ALL related Objects will be "\ + "permanently removed!"), + :enabled => false, + :onwhen => "1+" + ), + ] + ), + ] + ) button_group('cloud_object_store_container_policy', [ select( :cloud_object_store_container_policy_choice, diff --git a/spec/controllers/application_controller/ci_processing_spec.rb b/spec/controllers/application_controller/ci_processing_spec.rb index 40aa7754c42..dedd3b9de08 100644 --- a/spec/controllers/application_controller/ci_processing_spec.rb +++ b/spec/controllers/application_controller/ci_processing_spec.rb @@ -18,6 +18,162 @@ end end + context "Delete object store container" do + before do + allow(controller).to receive(:assert_rbac).and_return(nil) + allow_any_instance_of(CloudObjectStoreContainer).to receive(:supports?).and_return(true) + end + + let :container1 do + FactoryGirl.create(:cloud_object_store_container) + end + + let :container2 do + FactoryGirl.create(:cloud_object_store_container) + end + + context "from list view" do + before do + controller.params[:pressed] = "cloud_object_store_container_delete" + request.parameters["controller"] = "ems_storage" + end + + it "get_rec_cls" do + expect(controller.send(:get_rec_cls)).to eq(CloudObjectStoreContainer) + end + + it "invokes cloud_object_store_button_operation" do + expect(controller).to receive(:cloud_object_store_button_operation).with( + CloudObjectStoreContainer, + 'delete' + ) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + end + + it "invokes process_objects" do + controller.params[:miq_grid_checks] = "#{container1.id}, #{container2.id}" + expect(controller).to receive(:process_objects).with( + [container1.id, container2.id], + 'delete_cloud_object_store_container', + 'Delete' + ) + controller.send(:cloud_object_store_button_operation, CloudObjectStoreContainer, 'delete') + end + + it "invokes process_tasks on container class" do + expect(CloudObjectStoreContainer).to receive(:process_tasks).with( + :ids => [container1.id, container2.id], + :task => 'delete_cloud_object_store_container', + :userid => anything + ) + controller.send(:process_objects, [container1.id, container2.id], 'delete_cloud_object_store_container', + 'delete') + end + + it "invokes process_tasks overall (when selected)" do + controller.params[:miq_grid_checks] = "#{container1.id}, #{container2.id}" + expect(CloudObjectStoreContainer).to receive(:process_tasks).with( + :ids => [container1.id, container2.id], + :task => 'delete_cloud_object_store_container', + :userid => anything + ) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + end + + it "does not invoke process_tasks overall when nothing selected" do + controller.params[:miq_grid_checks] = '' + expect(CloudObjectStoreContainer).not_to receive(:process_tasks) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + end + + it "flash - nothing selected" do + controller.params[:miq_grid_checks] = '' + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + expect(assigns(:flash_array).first[:message]).to include( + "No Cloud Object Store Containers were selected for Delete" + ) + end + + it "flash - task not supported" do + controller.params[:miq_grid_checks] = "#{container1.id}, #{container2.id}" + allow_any_instance_of(CloudObjectStoreContainer).to receive(:supports?).and_return(false) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + expect(assigns(:flash_array).first[:message]).to include( + "Delete does not apply to at least one of the selected items" + ) + end + end + + context "from details view" do + before do + allow(controller).to receive(:show_list).and_return(nil) + controller.params[:pressed] = "cloud_object_store_container_delete" + request.parameters["controller"] = "cloud_object_store_container" + end + + let :container do + FactoryGirl.create(:cloud_object_store_container) + end + + it "get_rec_cls" do + expect(controller.send(:get_rec_cls)).to eq(CloudObjectStoreContainer) + end + + it "invokes cloud_object_store_button_operation" do + expect(controller).to receive(:cloud_object_store_button_operation).with( + CloudObjectStoreContainer, + 'delete' + ) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + end + + it "invokes process_objects" do + controller.params[:id] = container.id + expect(controller).to receive(:process_objects).with( + [container.id], + 'delete_cloud_object_store_container', + 'Delete' + ) + controller.send(:cloud_object_store_button_operation, CloudObjectStoreContainer, 'delete') + end + + it "invokes process_tasks on container class" do + expect(CloudObjectStoreContainer).to receive(:process_tasks).with( + :ids => [container.id], + :task => 'delete_cloud_object_store_container', + :userid => anything + ) + controller.send(:process_objects, [container.id], 'delete_cloud_object_store_container', 'delete') + end + + it "invokes process_tasks overall" do + controller.params[:id] = container.id + expect(CloudObjectStoreContainer).to receive(:process_tasks).with( + :ids => [container.id], + :task => 'delete_cloud_object_store_container', + :userid => anything + ) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + end + + it "flash - container no longer exists" do + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + expect(assigns(:flash_array).first[:message]).to include( + "Cloud Object Store Container no longer exists" + ) + end + + it "flash - task not supported" do + controller.params[:id] = container.id + allow_any_instance_of(CloudObjectStoreContainer).to receive(:supports?).and_return(false) + controller.send(:process_cloud_object_storage_buttons, "cloud_object_store_container_delete") + expect(assigns(:flash_array).first[:message]).to include( + "Delete does not apply to this item" + ) + end + end + end + # some methods should not be accessible through the legacy routes # either by being private or through the hide_action mechanism it 'should not allow call of hidden/private actions' do diff --git a/spec/controllers/cloud_object_store_container_spec.rb b/spec/controllers/cloud_object_store_container_spec.rb index cf69e6e057e..353002454a2 100644 --- a/spec/controllers/cloud_object_store_container_spec.rb +++ b/spec/controllers/cloud_object_store_container_spec.rb @@ -1,9 +1,13 @@ describe CloudObjectStoreContainerController do + before do + EvmSpecHelper.create_guid_miq_server_zone + @container = FactoryGirl.create(:cloud_object_store_container, :name => "cloud-object-store-container-01") + allow_any_instance_of(CloudObjectStoreContainer).to receive(:supports?).and_return(true) + end + context "#tags_edit" do let!(:user) { stub_user(:features => :all) } before(:each) do - EvmSpecHelper.create_guid_miq_server_zone - @container = FactoryGirl.create(:cloud_object_store_container, :name => "cloud-object-store-container-01") allow(@container).to receive(:tagged_with).with(:cat => user.userid).and_return("my tags") classification = FactoryGirl.create(:classification, :name => "department", :description => "D epartment") @tag1 = FactoryGirl.create(:classification_tag, @@ -48,5 +52,65 @@ end end + context "delete object store container" do + before do + login_as FactoryGirl.create(:user, :features => "everything") + request.parameters["controller"] = "cloud_object_store_container" + allow(controller).to receive(:role_allows?).and_return(true) + end + + it "delete invokes process_cloud_object_storage_buttons" do + expect(controller).to receive(:process_cloud_object_storage_buttons) + post :button, :params => { + :pressed => "cloud_object_store_container_delete", :format => :js, :id => @container.id + } + end + + it "delete triggers delete" do + expect(controller).to receive(:cloud_object_store_button_operation).with( + CloudObjectStoreContainer, + 'delete' + ) + post :button, :params => { + :pressed => "cloud_object_store_container_delete", :format => :js, :id => @container.id + } + end + + it "delete redirects to show_list" do + expect(controller).to receive(:javascript_redirect).with( + :action => 'show_list', + :flash_msg => anything, + :flash_error => false + ) + post :button, :params => { + :pressed => "cloud_object_store_container_delete", :format => :js, :id => @container.id + } + end + + it "delete shows expected flash" do + post :button, :params => { + :pressed => "cloud_object_store_container_delete", :format => :js, :id => @container.id + } + + expect(assigns(:flash_array).first[:message]).to include( + "Delete initiated for 1 Cloud Object Store Container from the ManageIQ Database" + ) + expect(response.status).to eq(200) + end + + it "delete shows expected flash (non-existing container)" do + @container.destroy + + post :button, :params => { + :pressed => "cloud_object_store_container_delete", :format => :js, :id => @container.id + } + + expect(assigns(:flash_array).first[:message]).to include( + "Cloud Object Store Container no longer exists" + ) + expect(response.status).to eq(200) + end + end + include_examples '#download_summary_pdf', :cloud_object_store_container end diff --git a/spec/controllers/ems_common_controller_spec.rb b/spec/controllers/ems_common_controller_spec.rb index 68187dba753..265cf306ac2 100644 --- a/spec/controllers/ems_common_controller_spec.rb +++ b/spec/controllers/ems_common_controller_spec.rb @@ -153,6 +153,11 @@ expect(response.status).to eq 200 expect(response.body).to include('orchestration_stack/retire') end + + it "when Delete Button is pressed for CloudObjectStoreContainer" do + expect(controller).to receive(:process_cloud_object_storage_buttons) + post :button, :params => { :pressed => "cloud_object_store_container_delete" } + end end end