Project

General

Profile

« Previous | Next » 

Revision c7bfc159

Added by Partha Aji almost 10 years ago

Fixes #6815,#6187/bz1125398,1125358 - Environment Destroy Dynflow

This commit adds code to orchestrate lifecycle env destroy via dynflow.
In addition to that it adds code to prevent environment deletion if any
of the lifecycle env associations like content hosts and activation keys

View differences:

app/controllers/katello/api/v2/environments_controller.rb
param :organization_id, :number, :desc => N_("organization identifier")
def destroy
if @environment.is_deletable?
@environment.destroy
sync_task(::Actions::Katello::Environment::Destroy, @environment)
respond_for_destroy
else
fail HttpErrors::BadRequest, @environment.errors.full_messages.join(" ")
app/lib/actions/katello/environment/destroy.rb
#
# Copyright 2014 Red Hat, Inc.
#
# This software is licensed to you under the GNU General Public
# License as published by the Free Software Foundation; either version
# 2 of the License (GPLv2) or (at your option) any later version.
# There is NO WARRANTY for this software, express or implied,
# including the implied warranties of MERCHANTABILITY,
# NON-INFRINGEMENT, or FITNESS FOR A PARTICULAR PURPOSE. You should
# have received a copy of GPLv2 along with this software; if not, see
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
module Actions
module Katello
module Environment
class Destroy < Actions::EntryAction
def plan(env)
sequence do
env.disable_auto_reindex!
action_subject(env)
concurrence do
env.content_view_environments.each do |cve|
plan_action(ContentView::Remove, cve.content_view, :content_view_environments => [cve])
end
end
env.reload.destroy!
plan_action(ElasticSearch::Reindex, env)
end
end
def humanized_name
_("Delete Lifecycle Environment")
end
end
end
end
end
app/lib/actions/katello/organization/destroy.rb
remove_content_views(organization)
remove_products(organization)
remove_default_content_view(organization)
remove_environments(organization)
organization.reload
organization.destroy!
end
......
end
end
def remove_environments(organization)
organization.promotion_paths.each do |path|
path.reverse.each do |env|
plan_action(Katello::Environment::Destroy, env)
end
end
plan_action(Katello::Environment::Destroy, organization.library)
end
def remove_content_view_environments(organization)
organization.content_view_environments.non_default.each do |cv_env|
remove_content_view_environment(cv_env)
app/models/katello/concerns/organization_extensions.rb
include Glue::ElasticSearch::Organization if Katello.config.use_elasticsearch
include Ext::LabelFromName
before_destroy :remove_environments # must be before has_many :kt_environments
has_many :activation_keys, :class_name => "Katello::ActivationKey", :dependent => :destroy
has_many :providers, :class_name => "Katello::Provider", :dependent => :destroy
has_many :products, :class_name => "Katello::Product", :dependent => :destroy, :inverse_of => :organization
# has_many :environments is already defined in Foreman taxonomy.rb
has_many :kt_environments, :class_name => "Katello::KTEnvironment", :dependent => :destroy, :inverse_of => :organization
has_many :kt_environments, :class_name => "Katello::KTEnvironment", :dependent => :restrict, :inverse_of => :organization
has_one :library, :class_name => "Katello::KTEnvironment", :conditions => {:library => true}, :dependent => :destroy
has_many :gpg_keys, :class_name => "Katello::GpgKey", :dependent => :destroy, :inverse_of => :organization
has_many :sync_plans, :class_name => "Katello::SyncPlan", :dependent => :destroy, :inverse_of => :organization
......
fail ::Foreman::Exception.new(N_("You cannot set an organization's parent_id. This feature is disabled."))
end
def remove_environments
# start at the end of each promotion path
promotion_paths.each do |path|
path.reverse.each { |env| env.destroy! }
end
library.destroy!
end
private
def start_discovery_task(url, notify = false)
app/models/katello/glue/elastic_search/environment.rb
base.class_eval do
include Ext::IndexedModel
after_save :update_related_index
after_destroy :delete_related_index
index_options :extended_json => :extended_index_attrs,
:json => {:only => [:id, :name, :description, :organization_id]},
......
end
end
def delete_related_index
self.organization.update_index if self.organization
end
end
end
app/models/katello/glue/elastic_search/organization.rb
base.class_eval do
index_options :extended_json => :extended_index_attrs,
:json => {:except => [:debug_cert, :events]},
:display_attrs => [:name, :description, :environment]
:display_attrs => [:name, :description]
mapping do
indexes :name, :type => 'string', :analyzer => :kt_name_analyzer
......
end
def extended_index_attrs
{:name_sort => name.downcase, :environment => self.kt_environments.collect{|e| e.name}}
{:name_sort => name.downcase}
end
end
end
app/models/katello/kt_environment.rb
module Katello
class KTEnvironment < Katello::Model
self.include_root_in_json = false
include ForemanTasks::Concerns::ActionSubject
include Authorization::LifecycleEnvironment
include Glue::ElasticSearch::Environment if Katello.config.use_elasticsearch
self.table_name = "katello_environments"
include Ext::LabelFromName
# RAILS3458: before_destroys before associations. see http://tinyurl.com/rails3458
before_destroy :is_deletable?
belongs_to :organization, :class_name => "Organization", :inverse_of => :environments
has_many :activation_keys, :class_name => "Katello::ActivationKey",
:dependent => :destroy, :foreign_key => :environment_id
:dependent => :restrict, :foreign_key => :environment_id
# rubocop:disable HasAndBelongsToMany
# TODO: change these into has_many associations
has_and_belongs_to_many :priors, { :class_name => "Katello::KTEnvironment", :foreign_key => :environment_id,
......
has_many :repositories, :class_name => "Katello::Repository", dependent: :destroy, foreign_key: :environment_id
has_many :systems, :class_name => "Katello::System", :inverse_of => :environment,
:dependent => :destroy, :foreign_key => :environment_id
:dependent => :restrict, :foreign_key => :environment_id
has_many :distributors, :class_name => "Katello::Distributor", :inverse_of => :environment,
:dependent => :destroy, :foreign_key => :environment_id
has_many :content_view_environments, :class_name => "Katello::ContentViewEnvironment",
:foreign_key => :environment_id, :inverse_of => :environment, :dependent => :destroy
:foreign_key => :environment_id, :inverse_of => :environment, :dependent => :restrict
has_many :content_view_puppet_environments, :class_name => "Katello::ContentViewPuppetEnvironment",
:foreign_key => :environment_id, :inverse_of => :environment, :dependent => :destroy
:foreign_key => :environment_id, :inverse_of => :environment, :dependent => :restrict
has_many :content_view_versions, :through => :content_view_environments, :inverse_of => :environments
has_many :content_views, :through => :content_view_environments, :inverse_of => :environments
has_many :content_view_histories, :class_name => "Katello::ContentViewHistory", :dependent => :destroy,
......
has_many :capsule_lifecycle_environments, :foreign_key => :lifecycle_environment_id,
:dependent => :destroy, :inverse_of => :lifecycle_environment
# RAILS3458: before_destroys before associations. see http://tinyurl.com/rails3458
before_destroy :is_deletable?, :prepend => true
scope(:not_in_capsule,
lambda do |capsule|
select("DISTINCT #{KTEnvironment.table_name}.*").
......
return true if self.organization.nil? || self.organization.being_deleted?
if library?
errors.add :base, _("Library environments may not be deleted.")
return false
errors.add :base, _("Library lifecycle environments may not be deleted.")
elsif !successor.nil?
errors.add :base, _("Environment %s has a successor. Only the last environment on a path can be deleted") % self.name
return false
errors.add :base, _("Lifecycle Environment %s has a successor. Only the last lifecycle environment on a path can be deleted") % self.name
end
if systems.any?
errors.add(:base,
_("Lifecycle Environment %s has associated Content Hosts." +
" Please unregister or move the associated Content Hosts before trying to delete this lifecycle environment.") % self.name)
end
if activation_keys.any?
errors.add(:base,
_("Lifecycle Environment %s has associated Activation Keys." +
" Please change or remove the associated Activation Keys before trying to delete this lifecycle environment.") % self.name)
end
return true
return errors.empty?
end
#Unlike path which only gives the path from this environment going forward
spec/models/environment_spec.rb
end
describe "delete an environment" do
it "should delete the environment" do
id = @environment.id
@environment.destroy
User.current.remote_id = User.current.login
env = KTEnvironment.create!(:name=>"Boooo1224",
:organization => @organization,
:prior => @organization.library)
id = env.id
env.destroy!
lambda { KTEnvironment.find(id)}.must_raise(ActiveRecord::RecordNotFound)
end
end
spec/models/organization_spec.rb
end
end
describe "delete an organization" do
it "can delete the org" do
id = @organization.id
Organization.any_instance.stubs(:being_deleted?).returns(true)
@organization.destroy
@organization.must_be :destroyed?
lambda{Organization.find(id)}.must_raise(ActiveRecord::RecordNotFound)
end
it "can delete the org and envs are deleted" do
org_id = @organization.id
env_name = "prod"
@env = KTEnvironment.new(:name=>env_name, :label=> env_name, :library => false, :prior => @organization.library)
@organization.kt_environments << @env
@env.save!
Organization.any_instance.stubs(:being_deleted?).returns(true)
@organization.reload.destroy
lambda{Organization.find(org_id)}.must_raise(ActiveRecord::RecordNotFound)
KTEnvironment.where(:name => env_name).all.must_be_empty
end
it "can delete the org and env of a different org exist" do
env_name = "prod"
@org2 = Organization.create!(:name=>"foobar", :label=> "foobar")
@env1 = KTEnvironment.new(:name=>env_name, :label=> env_name, :organization => @organization, :prior => @organization.library)
@organization.kt_environments << @env1
@env1.save!
@env2 = KTEnvironment.new(:name=>env_name, :label=> env_name, :organization => @org2, :prior => @organization.library)
@org2.kt_environments << @env2
@env2.save!
id1 = @organization.id
Organization.any_instance.stubs(:being_deleted?).returns(true)
@organization.reload.destroy
lambda{Organization.find(id1)}.must_raise(ActiveRecord::RecordNotFound)
KTEnvironment.where(:name => env_name).first.must_equal(@env2)
KTEnvironment.where(:name => env_name).size.must_equal(1)
end
it "can delete an org where there is a full environment path" do
dev = create_environment(:name=>"Dev-34343", :label=> "Dev", :organization => @organization, :prior => @organization.library)
qa = create_environment(:name=>"QA", :label=> "QA", :organization => @organization, :prior => dev)
prod = create_environment(:name=>"prod", :label=> "prod", :organization => @organization, :prior => qa)
Organization.any_instance.stubs(:being_deleted?).returns(true)
@organization = @organization.reload
@organization.destroy
lambda{Organization.find(@organization.id)}.must_raise(ActiveRecord::RecordNotFound)
KTEnvironment.where(:name =>'Dev-34343').size.must_equal(0)
end
end
describe "it can retrieve manifest history" do
test 'test manifest history should be successful' do
@organization = @organization.reload
test/actions/katello/environment_test.rb
library, content_view)
end
end
class DestroyTest < TestBase
let(:action_class) { ::Actions::Katello::Environment::Destroy }
let(:action) { create_action action_class }
let(:environment) { stub }
it 'plans' do
content_view = stub
cve = mock(:content_view => content_view)
action.stubs(:action_subject).with(environment)
environment.expects(:reload).returns(environment)
environment.expects(:destroy!).returns(true)
environment.expects(:disable_auto_reindex!)
environment.expects(:content_view_environments).returns([cve])
plan_action(action, environment)
assert_action_planed_with(action, ::Actions::ElasticSearch::Reindex, environment)
assert_action_planed_with(action, ::Actions::Katello::ContentView::Remove, content_view, :content_view_environments => [cve])
end
end
end
test/actions/katello/organization_test.rb
let(:organization) { stub }
it 'plans' do
env = stub
library = stub
action.stubs(:action_subject).with(organization)
default_view = stub(:content_view_environments => [])
library = stub(:destroy! => true)
......
organization.expects(:content_view_environments).returns(stub(:non_default => []))
organization.expects(:reload)
organization.expects(:destroy!).returns(true)
organization.expects(:promotion_paths).returns([[env]])
organization.expects(:library).returns(library)
plan_action(action, organization)
......
::Actions::Candlepin::Owner::Destroy,
label: "ACME_Corporation")
assert_action_planed_with(action, ::Actions::Katello::ContentView::Destroy, default_view)
assert_action_planed_with(action, ::Actions::Katello::Environment::Destroy, env)
end
end
end
test/controllers/api/v2/environments_controller_test.rb
module Katello
class Api::V2::EnvironmentsControllerTest < ActionController::TestCase
include Support::ForemanTasks::Task
def self.before_suite
models = ["KTEnvironment", "ContentViewEnvironment", "Organization"]
disable_glue_layers(["Candlepin", "Pulp", "ElasticSearch"], models)
......
end
def test_destroy
destroyable_env = KTEnvironment.create!(:name => "DestroyAble",
:organization => @staging.organization,
:prior => @staging)
assert_sync_task(::Actions::Katello::Environment::Destroy, destroyable_env)
delete :destroy, :organization_id => @organization.id,
:id => @staging.id
:id => destroyable_env.id
assert_response :success
end
test/models/content_view_test.rb
assert_includes @library.content_views, @library_view
end
def test_environment_content_view_env_destroy
def test_environment_content_view_env_destroy_should_fail
User.current = User.find(users(:admin))
ContentViewPuppetEnvironment.any_instance.stubs(:clear_content_indices)
env = @dev
cve = env.content_views.first.content_view_environments.where(:environment_id=>env.id).first
env.destroy
assert_nil ContentViewEnvironment.find_by_id(cve.id)
assert_raises(RuntimeError) do
env.destroy!
end
refute_nil ContentViewEnvironment.find_by_id(cve.id)
end
def test_promote
test/models/kt_environment_test.rb
assert_nil env.default_content_view_version
end
def test_destroy_content_view_environment
env = @staging
cve = env.content_views.first.content_view_environments.where(:environment_id=>env.id).first
cve_cp_id = cve.cp_id
env.destroy
assert_empty ContentViewEnvironment.where(:cp_id=>cve_cp_id)
def test_destroy_env_with_systems_should_fail
env = KTEnvironment.create!(:name => "batman", :organization => @acme_corporation, :prior => @library)
env.expects(:systems).returns([stub])
assert_raises(RuntimeError) do
env.destroy!
end
end
def test_destroy_env_with_activation_keys_should_fail
env = KTEnvironment.create!(:name => "batman", :organization => @acme_corporation, :prior => @library)
env.stubs(:activation_keys).returns([stub])
assert_raises(RuntimeError) do
env.destroy!
end
end
def test_destroy_library
test/models/organization_test.rb
refute_empty org.default_content_view.content_view_environments
end
def test_org_being_deleted
Organization.any_instance.stubs(:being_deleted?).returns(true)
User.current = User.find(users(:admin))
org = Organization.create!(:name=>"TestOrg", :label=>'test_org')
org.content_view_environments.first.destroy!
org.reload.library.destroy!
id = org.id
org.destroy!
assert_nil Organization.find_by_id(id)
end
end
end

Also available in: Unified diff