Skip to content

Commit

Permalink
set vm ancestry from relationship resource info
Browse files Browse the repository at this point in the history
  • Loading branch information
d-m-u committed Jun 10, 2020
1 parent 3a66ffb commit 9302c94
Show file tree
Hide file tree
Showing 2 changed files with 199 additions and 0 deletions.
61 changes: 61 additions & 0 deletions db/migrate/20200607025146_add_ancestry_to_vm.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
class AddAncestryToVm < ActiveRecord::Migration[5.2]
class VmOrTemplate < ActiveRecord::Base
self.inheritance_column = :_type_disabled
self.table_name = 'vms'
has_many :all_relationships, :class_name => "AddAncestryToVm::Relationship", :dependent => :destroy, :as => :resource
end

class Vm < VmOrTemplate
end

class Relationship < ActiveRecord::Base
belongs_to :vm, :class_name => 'AddAncestryToVm::VmOrTemplate', :foreign_key => :vm_id
end

def up
add_column :vms, :ancestry, :string
add_index :vms, :ancestry

say_with_time("set vm ancestry from existing genealogy relationship resource information") do
connection.execute <<-SQL
UPDATE vms
SET ancestry = new_ancestry
FROM (
SELECT ancestor_resources_for_vms.vm_id AS vm_id,
ARRAY_TO_STRING(ARRAY_AGG(res_rels.resource_id)::VARCHAR[], '/') AS new_ancestry
FROM (
SELECT vms.id AS vm_id, relationships.id AS rel_id
FROM vms
JOIN relationships a_rels ON a_rels.resource_id = vms.id
AND a_rels.relationship = 'genealogy'
AND a_rels.resource_type = 'VmOrTemplate'
LEFT JOIN LATERAL UNNEST(STRING_TO_ARRAY(a_rels.ancestry, '/')::BIGINT[])
WITH ORDINALITY AS relationships(id, indx) ON TRUE
ORDER BY vms.id, relationships.indx
) AS ancestor_resources_for_vms
JOIN relationships res_rels ON res_rels.id = ancestor_resources_for_vms.rel_id AND res_rels.relationship = 'genealogy'
GROUP BY ancestor_resources_for_vms.vm_id
) AS new_ancestors_for_vms
WHERE new_ancestors_for_vms.vm_id = vms.id
SQL
end

connection.execute <<-SQL
DELETE FROM relationships
WHERE relationship = 'genealogy' AND resource_type = 'VmOrTemplate'
AND resource_id
IN (SELECT vms.id FROM vms);
SQL
end

def down
say_with_time("create relationship records from vm ancestry") do
vms_with_ancestry = Vm.select { |vm| vm.ancestry.present? }
vms_with_ancestry.each do |vm|
Relationship.create!(:relationship => 'genealogy', :resource_type => 'VmOrTemplate', :resource_id => vm.id, :ancestry => vm.ancestry)
end

remove_column :vms, :ancestry
end
end
end
138 changes: 138 additions & 0 deletions spec/migrations/20200607025146_add_ancestry_to_vm_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
require_migration

describe AddAncestryToVm do
let(:rel_stub) { migration_stub(:Relationship) }
let(:vm_stub) { migration_stub :VmOrTemplate }
let(:vm) { vm_stub.create! }
let(:vm2) { vm_stub.create! }
let(:vm3) { vm_stub.create! }
let(:vm4) { vm_stub.create! }
let(:vm5) { vm_stub.create! }
let(:vm6) { vm_stub.create! }
let(:root) { vm_stub.create! }

migration_context :up do
context "single parent/child rel" do
it 'updates ancestry' do
parent_rel = rel_stub.create!(:relationship => 'genealogy', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => parent_rel.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)

migrate

expect(vm.reload.ancestry).to eq(root.id.to_s)
expect(root.reload.ancestry).to eq(nil)
expect(AddAncestryToVm::Relationship.count).to eq(0)
end
end

context "slightly more complicated tree" do
it 'updates ancestry' do
parent_rel = rel_stub.create!(:relationship => 'genealogy', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
child_rel = rel_stub.create!(:relationship => 'genealogy', :ancestry => parent_rel.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => child_rel.id.to_s + '/' + parent_rel.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm2.id)

migrate

expect(vm.reload.ancestry).to eq(root.id.to_s)
expect(vm2.reload.ancestry).to eq("#{vm.id}/#{root.id}")
expect(root.reload.ancestry).to eq(nil)
expect(AddAncestryToVm::Relationship.count).to eq(0)
end
end

context "complicated tree" do
# a
# b c
# d g
# e f
it 'updates ancestry' do
rel_a = rel_stub.create!(:relationship => 'genealogy', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
rel_c = rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_a.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_c.id.to_s + '/' + rel_a.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm2.id)
rel_b = rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_a.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm3.id)
rel_d = rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_b.id.to_s + '/' + rel_a.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm4.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_d.id.to_s + '/' + rel_b.id.to_s + '/' + rel_a.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm5.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => rel_d.id.to_s + '/' + rel_b.id.to_s + '/' + rel_a.id.to_s, :resource_type => 'VmOrTemplate', :resource_id => vm6.id)

migrate

expect(vm5.reload.ancestry).to eq("#{vm4.id}/#{vm3.id}/#{root.id}")
expect(vm6.reload.ancestry).to eq("#{vm4.id}/#{vm3.id}/#{root.id}")
expect(vm3.reload.ancestry).to eq(root.id.to_s)
expect(vm.reload.ancestry).to eq(root.id.to_s)
expect(vm2.reload.ancestry).to eq("#{vm.id}/#{root.id}")
expect(root.reload.ancestry).to eq(nil)
expect(AddAncestryToVm::Relationship.count).to eq(0)
end
end

context "vm without rels" do
it 'nil ancestry' do
migrate

expect(AddAncestryToVm::Vm.find(vm.id).ancestry).to eq(nil)
end
end

context "with only ems_metadata relationship tree" do
it 'sets nothing' do
parent_rel = rel_stub.create!(:relationship => 'ems_metadata', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
rel_stub.create!(:relationship => 'ems_metadata', :ancestry => parent_rel.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)

migrate

expect(vm.reload.ancestry).to eq(nil)
expect(root.reload.ancestry).to eq(nil)
expect(AddAncestryToVm::Relationship.count).to eq(2)
end
end

context "with both genealogy and ems_metadata rels" do
it 'only sets ancestry from genealogy rels' do
invalid_parent_rel = rel_stub.create!(:relationship => 'ems_metadata', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
rel_stub.create!(:relationship => 'ems_metadata', :ancestry => invalid_parent_rel.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)
parent_rel = rel_stub.create!(:relationship => 'genealogy', :ancestry => nil, :resource_type => 'VmOrTemplate', :resource_id => root.id)
rel_stub.create!(:relationship => 'genealogy', :ancestry => parent_rel.id, :resource_type => 'VmOrTemplate', :resource_id => vm.id)

migrate

expect(vm.reload.ancestry).to eq(root.id.to_s)
expect(root.reload.ancestry).to eq(nil)
expect(AddAncestryToVm::Relationship.count).to eq(2)
end
end
end


migration_context :down do
context "multiple rels" do
let(:vm) { vm_stub.create!(:ancestry => '6/5/4') }
it 'creates rel and removes ancestry' do
vm

migrate

rel = AddAncestryToVm::Relationship.first
expect(rel.relationship).to eq('genealogy')
expect(rel.ancestry).to eq('6/5/4')
expect(rel.resource_type).to eq('VmOrTemplate')
expect(rel.resource_id).to eq(vm.id)
end
end

context "single rel" do
let(:vm) { vm_stub.create!(:ancestry => '645') }
it 'creates rel and removes ancestry' do
vm

migrate

rel = AddAncestryToVm::Relationship.first
expect(rel.relationship).to eq('genealogy')
expect(rel.ancestry).to eq('645')
expect(rel.resource_type).to eq('VmOrTemplate')
expect(rel.resource_id).to eq(vm.id)
end
end
end
end

0 comments on commit 9302c94

Please sign in to comment.