diff --git a/db/migrate/20220114155819_remove_vim_types_from_binary_blob_parts.rb b/db/migrate/20220114155819_remove_vim_types_from_binary_blob_parts.rb new file mode 100644 index 000000000..9d82c113b --- /dev/null +++ b/db/migrate/20220114155819_remove_vim_types_from_binary_blob_parts.rb @@ -0,0 +1,35 @@ +class RemoveVimTypesFromBinaryBlobParts < ActiveRecord::Migration[6.0] + class BinaryBlob < ActiveRecord::Base + include ActiveRecord::IdRegions + end + + class BinaryBlobPart < ActiveRecord::Base + include ActiveRecord::IdRegions + end + + def up + say_with_time("Removing VimTypes from BinaryBlobParts") do + # Only consider BinaryBlobs which are a YAML data type, this means we are safe to + # ENCODE the bytea type to text + yaml_binary_blobs = BinaryBlob.in_my_region.select(:id).where(:data_type => "YAML") + + # Since binary_blob_parts#data is a binary data type we have to encode it to + # allow REPLACE() to function, then cast it back to a binary type + BinaryBlobPart.in_my_region + .where(:binary_blob_id => yaml_binary_blobs) + .where("ENCODE(data, 'escape') LIKE ?", "%!ruby/string:VimString%") + .update_all("data = REPLACE(ENCODE(data, 'escape'), '!ruby/string:VimString', '!ruby/string:String')::bytea") + end + end + + def down + say_with_time("Resetting VimTypes from BinaryBlobParts") do + yaml_binary_blobs = BinaryBlob.in_my_region.select(:id).where(:data_type => "YAML") + + BinaryBlobPart.in_my_region + .where(:binary_blob_id => yaml_binary_blobs) + .where("ENCODE(data, 'escape') LIKE ?", "%!ruby/string:String%") + .update_all("data = REPLACE(ENCODE(data, 'escape'), '!ruby/string:String', '!ruby/string:VimString')::bytea") + end + end +end diff --git a/spec/migrations/20220114155819_remove_vim_types_from_binary_blob_parts_spec.rb b/spec/migrations/20220114155819_remove_vim_types_from_binary_blob_parts_spec.rb new file mode 100644 index 000000000..8f5eaefdb --- /dev/null +++ b/spec/migrations/20220114155819_remove_vim_types_from_binary_blob_parts_spec.rb @@ -0,0 +1,68 @@ +require_migration + +describe RemoveVimTypesFromBinaryBlobParts do + let(:binary_blob_stub) { migration_stub(:BinaryBlob) } + let(:binary_blob_part_stub) { migration_stub(:BinaryBlobPart) } + + migration_context :up do + it "removes VimStrings from BinaryBlobParts" do + binary_blob = binary_blob_stub.create(:data_type => "YAML") + binary_blob_part = binary_blob_part_stub.create( + :data => "---\n:ticket: !ruby/string:VimString\n str: MY_TICKET\n xsiType: :SOAP::SOAPString\n vimType:\n:remote_url: vmrc://clone:MY_TICKET@vsphere.local:443/?moid=vm-1234\n:proto: remote\n", + :binary_blob_id => binary_blob.id + ) + + migrate + + # ensure the string doesn't include VimString + expect(binary_blob_part.reload.data).not_to include("VimString") + + # ensure the data loads as valid YAML + require "yaml" + expect(YAML.load(binary_blob_part.data)).to include( + :ticket => "MY_TICKET", + :proto => "remote" + ) + end + + it "doesn't impact other BinaryBlobParts" do + binary_blob = binary_blob_stub.create(:data_type => "YAML") + binary_blob_part = binary_blob_part_stub.create( + :data => "---\n:ticket: MY_TICKET\n", + :binary_blob_id => binary_blob.id + ) + + migrate + + require "yaml" + expect(YAML.load(binary_blob_part.data)).to include(:ticket => "MY_TICKET") + end + end + + migration_context :down do + it "resets ruby/string:String back to VimString" do + binary_blob = binary_blob_stub.create(:data_type => "YAML") + binary_blob_part = binary_blob_part_stub.create( + :data => "---\n:ticket: !ruby/string:String\n str: MY_TICKET\n xsiType: :SOAP::SOAPString\n vimType:\n:remote_url: vmrc://clone:MY_TICKET@vsphere.local:443/?moid=vm-1234\n:proto: remote\n", + :binary_blob_id => binary_blob.id + ) + + migrate + + expect(binary_blob_part.reload.data).to include("ruby/string:VimString") + end + + it "doesn't impact other BinaryBlobParts" do + binary_blob = binary_blob_stub.create(:data_type => "YAML") + binary_blob_part = binary_blob_part_stub.create( + :data => "---\n:ticket: MY_TICKET\n", + :binary_blob_id => binary_blob.id + ) + + migrate + + require "yaml" + expect(YAML.load(binary_blob_part.data)).to include(:ticket => "MY_TICKET") + end + end +end