From 4bd43156ec8e849ca97ce716f4798f17951cf593 Mon Sep 17 00:00:00 2001 From: d-m-u Date: Thu, 14 Feb 2019 14:30:12 -0500 Subject: [PATCH] Add method to copy service templates --- app/models/configuration_script_base.rb | 4 + app/models/ext_management_system.rb | 4 + app/models/miq_provision_request_template.rb | 4 + app/models/miq_request.rb | 4 + app/models/orchestration_template.rb | 4 + app/models/service_template.rb | 2 + app/models/service_template/copy.rb | 17 +++ spec/factories/service_template.rb | 4 +- spec/models/service_template_spec.rb | 109 +++++++++++++++++++ 9 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 app/models/service_template/copy.rb diff --git a/app/models/configuration_script_base.rb b/app/models/configuration_script_base.rb index 1273da01af13..db6bda2fd519 100644 --- a/app/models/configuration_script_base.rb +++ b/app/models/configuration_script_base.rb @@ -22,4 +22,8 @@ class ConfigurationScriptBase < ApplicationRecord scope :with_manager, ->(manager_id) { where(:manager_id => manager_id) } include ProviderObjectMixin + + def service_template_resource_copy + self + end end diff --git a/app/models/ext_management_system.rb b/app/models/ext_management_system.rb index 02cb74ee67df..d9741970852e 100644 --- a/app/models/ext_management_system.rb +++ b/app/models/ext_management_system.rb @@ -20,6 +20,10 @@ def self.supported_subclasses end end + def service_template_resource_copy + self + end + def self.supported_types_and_descriptions_hash supported_subclasses.each_with_object({}) do |klass, hash| if Vmdb::PermissionStores.instance.supported_ems_type?(klass.ems_type) diff --git a/app/models/miq_provision_request_template.rb b/app/models/miq_provision_request_template.rb index 6905a37f07cc..92c2cd358af9 100644 --- a/app/models/miq_provision_request_template.rb +++ b/app/models/miq_provision_request_template.rb @@ -21,6 +21,10 @@ def request_task_class MiqProvision end + def service_template_resource_copy + dup.tap(&:save!) + end + def execute # Should not be called. raise _("Provision Request Templates do not support the execute method.") diff --git a/app/models/miq_request.rb b/app/models/miq_request.rb index af56a64c7d4d..fc2fa7961279 100644 --- a/app/models/miq_request.rb +++ b/app/models/miq_request.rb @@ -160,6 +160,10 @@ def resource self end + def service_template_copy + self + end + def miq_request self end diff --git a/app/models/orchestration_template.rb b/app/models/orchestration_template.rb index 38427f73e645..9189e48817ca 100644 --- a/app/models/orchestration_template.rb +++ b/app/models/orchestration_template.rb @@ -31,6 +31,10 @@ def self.find_with_content(template_content) where(:draft => false).find_by(:md5 => calc_md5(with_universal_newline(template_content))) end + def service_template_resource_copy + self + end + # Find only by template content. Here we only compare md5 considering the table is expected # to be small and the chance of md5 collision is minimal. # diff --git a/app/models/service_template.rb b/app/models/service_template.rb index 227ff13cccff..9fb161268cd8 100644 --- a/app/models/service_template.rb +++ b/app/models/service_template.rb @@ -43,7 +43,9 @@ class ServiceTemplate < ApplicationRecord include ArchivedMixin include CiFeatureMixin include_concern 'Filter' + include_concern 'Copy' + validates :name, :presence => true belongs_to :tenant # # These relationships are used to specify children spawned from a parent service # has_many :child_services, :class_name => "ServiceTemplate", :foreign_key => :service_template_id diff --git a/app/models/service_template/copy.rb b/app/models/service_template/copy.rb new file mode 100644 index 000000000000..4ec62de64300 --- /dev/null +++ b/app/models/service_template/copy.rb @@ -0,0 +1,17 @@ +module ServiceTemplate::Copy + extend ActiveSupport::Concern + + def copy(new_name = "Copy of " + name + Time.zone.now.to_s) + if template_valid? && type != 'ServiceTemplateAnsiblePlaybook' + ActiveRecord::Base.transaction do + dup.tap do |template| + template.update_attributes(:name => new_name, :guid => nil) + service_resources.each do |sr| + template.add_resource(sr.resource_type.constantize.find(sr.resource_id).try(:service_template_resource_copy)) + end + end.save! + # new_srs.each { |sr| sr.try(:service_template_copy) ? sr.service_template_copy : "Service Template copy not supported for #{sr.class}" } + end + end + end +end diff --git a/spec/factories/service_template.rb b/spec/factories/service_template.rb index 0172d10149ed..78ea20c1a124 100644 --- a/spec/factories/service_template.rb +++ b/spec/factories/service_template.rb @@ -1,5 +1,7 @@ FactoryBot.define do - factory :service_template + factory :service_template do + sequence(:name) { |n| "service_template_#{seq_padded_for_sorting(n)}" } + end factory :service_template_orchestration, :class => 'ServiceTemplateOrchestration', :parent => :service_template factory :service_template_ansible_playbook, :class => 'ServiceTemplateAnsiblePlaybook', :parent => :service_template factory :service_template_container_template, :class => 'ServiceTemplateContainerTemplate', :parent => :service_template diff --git a/spec/models/service_template_spec.rb b/spec/models/service_template_spec.rb index 2ddb4910dec2..df1b501f799f 100644 --- a/spec/models/service_template_spec.rb +++ b/spec/models/service_template_spec.rb @@ -268,6 +268,115 @@ end end + context "#copy" do + before do + @st1 = FactoryBot.create(:service_template) + end + + context "with given name" do + it "without resource " do + expect(ServiceTemplate.count).to eq(1) + @st1.copy("drew") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "drew")).not_to be(nil) + expect(ServiceTemplate.find_by(:name => "drew").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (configuration script base)" do + @st1.add_resource(FactoryBot.create(:configuration_script_base)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ConfigurationScriptBase.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (ext management system)" do + @st1.add_resource(FactoryBot.create(:ext_management_system)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ExtManagementSystem.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (orchestration template)" do + @st1.add_resource(FactoryBot.create(:orchestration_template)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy("thing") + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing").service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(OrchestrationTemplate.count).to eq(1) + expect(ServiceTemplate.find_by(:name => "thing").guid).not_to eq(@st1.guid) + end + + it "with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + @st1.add_resource(ptr) + expect(ServiceTemplate.count).to eq(1) + @st1.copy("thing1") + expect(ServiceTemplate.count).to eq(2) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by(:name => "thing1").guid).not_to eq(@st1.guid) + end + end + + context "without given name" do + it "without resource" do + expect(ServiceTemplate.count).to eq(1) + @st1.copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (configuration_script_base)" do + @st1.add_resource(FactoryBot.create(:configuration_script_base)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ConfigurationScriptBase.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (ext management system)" do + @st1.add_resource(FactoryBot.create(:ext_management_system)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(ExtManagementSystem.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + + it "with non-copyable resource (orchestration template)" do + @st1.add_resource(FactoryBot.create(:orchestration_template)) + expect(ServiceTemplate.count).to eq(1) + @st1.copy + expect(ServiceTemplate.count).to eq(2) + expect(ServiceTemplate.where("name ILIKE ?", "Copy of service%").first.service_resources.first.resource_id).to eq(@st1.service_resources.first.resource_id) + expect(OrchestrationTemplate.count).to eq(1) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + + it "with copyable resource" do + admin = FactoryBot.create(:user_admin) + vm_template = FactoryBot.create(:vm_openstack, :ext_management_system => FactoryBot.create(:ext_management_system)) + ptr = FactoryBot.create(:miq_provision_request_template, :requester => admin, :src_vm_id => vm_template.id) + @st1.add_resource(ptr) + expect(ServiceTemplate.count).to eq(1) + @st1.copy + expect(ServiceTemplate.count).to eq(2) + expect(MiqProvisionRequestTemplate.count).to eq(2) + expect(ServiceTemplate.find_by("name ILIKE ?", "Copy of service%").guid).not_to eq(@st1.guid) + end + end + end + context "#composite?" do before do @st1 = FactoryBot.create(:service_template)