Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrations for suspending provider #222

Closed
wants to merge 10 commits into from

Conversation

slemrmartin
Copy link
Contributor

@slemrmartin slemrmartin commented Jun 18, 2018

BZ: https://bugzilla.redhat.com/show_bug.cgi?id=1455145

Issue: ManageIQ/manageiq#17489

Adds Zone visibility and EMS's backup zone relation

@miq-bot miq-bot added the wip label Jun 18, 2018
@slemrmartin slemrmartin changed the title [WIP] Migrations for suspending provider Migrations for suspending provider Jun 20, 2018
@miq-bot miq-bot removed the wip label Jun 20, 2018
@slemrmartin slemrmartin changed the title Migrations for suspending provider [WIP] Migrations for suspending provider Jun 20, 2018
@miq-bot miq-bot added the wip label Jun 20, 2018
@slemrmartin slemrmartin force-pushed the suspend-provider branch 2 times, most recently from 69d2243 to 2085d39 Compare June 27, 2018 13:51
@slemrmartin slemrmartin changed the title [WIP] Migrations for suspending provider Migrations for suspending provider Jun 27, 2018
@slemrmartin
Copy link
Contributor Author

Cc @Fryguy

@@ -0,0 +1,5 @@
class AddVisibleToZone < ActiveRecord::Migration[5.0]
def change
add_column :zones, :visible, :boolean, :default => true
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer default_value_for at the model level over a database-level default.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That being said, you will need a data migration to update all existing zones to visible => true.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fryguy exactly, so why is preferred more difficult solution? database-level default, at least for boolean, was standard for every project I seen before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about it, what about compromise solution? keep :default => true as better alternative to data migration and add default_value_for to keep your code standards

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...ok, I disagree with your approach, but we need to finish this RFE, so as you wish

@@ -0,0 +1,5 @@
class AddBackupZoneIdToExtManagementSystem < ActiveRecord::Migration[5.0]
def change
add_reference :ext_management_systems, :backup_zone, :type => :bigint, :index => true, :after => :zone_id
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the :after bit...it's not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it makes db structure more clear, if you group logical related columns together

Zone.where(:name => 'maintenance').where(:visible => false).destroy_all
end
end
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This entire migration is unnecessary, because this can be handled in Zone.seed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fryguy - are you sure that Zone.seed is called after migrations in all existing installations?

@slemrmartin slemrmartin force-pushed the suspend-provider branch 2 times, most recently from f85d7e2 to c8b53f3 Compare July 10, 2018 12:12
class MakeMaintenanceZoneRecord < ActiveRecord::Migration[5.0]
class Zone < ActiveRecord::Base
validates_presence_of :name
validates_uniqueness_of :name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These must be removed. In a global region, since zones are transferred from other regions, there are multiple "default" zones, for example. As such, this migration will fail to migrate a global database.


def up
say_with_time("Creating Maintenance Zone") do
zone = Zone.where(:name => Zone::MAINTENANCE_ZONE_NAME).first
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It just occurred to me that this is not region aware. Please change this to Zone.in_my_region.where(...), to allow migrating a global database. You can also create a spec showing this, but will need to give each zone an id within a region. There should be other migrations that have examples showing how to do that.

@slemrmartin
Copy link
Contributor Author

slemrmartin commented Aug 1, 2018

@Fryguy I'm not sure, how exactly migrations work on global database so please look if in_my_region occurences are enough


migration_context :up do
before(:each) do
zone_stub.delete_all
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why you need this...the database should be empty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is not cleaned between specs in migration_context when I use zone_stub.create!. I don't know if it is some bad local settings, but specs are failing without this line.

zone.save
end

Zone.create_with(:description => "Maintenance Zone", :visible => false).find_or_create_by!(:name => Zone::MAINTENANCE_ZONE_NAME)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We typically don't create seed data in migrations, instead doing that in the .seed method. Typically a migrated empty database should still be empty in the end.

What I recommend here is to keep the bit where we rename any existing __maintenance__ zone, but get rid of the part that actually creates the maintenance zone, instead moving that to .seed in the core.


migration_context :up do
before(:each) do
zone_stub.delete_all
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Fryguy my respose was hidden after commit...
I think it should be cleared, but it's not, create! in spec below is creating more and more records when running repeatedly and spec doesn't work then.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hm, this should not happen, something must be off, because it should all just a transaction that gets rolled back

Delete like this will not work if the specs will run in parallel. So we need to find a way to to properly fix this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Ladas probably some bad before :all in the past in my local db so it work without it normally.

@agrare
Copy link
Member

agrare commented Aug 24, 2018

@carbonin 👍 works for me

@slemrmartin
Copy link
Contributor Author

@carbonin I'm not sure what you mean by it - what does it mean "just fail the migration"?
Migration only renames possible existing zone, it doesn't create our __maintenance__ zone (because this code is in Zone.seed) - so this conflict can happen in runtime.

And creating of zone in migration doesn't want @Fryguy, so it seems to me that we are in loop :)

@agrare
Copy link
Member

agrare commented Aug 28, 2018

@slemrmartin Just leave the rename as is but add an exception handler that prints a message like "Failed to rename maintenance, check for zone conflicts" and re-raise the exception. It will fail the upgrade and the customer will have to remediate it.

class Zone < ActiveRecord::Base
include ActiveRecord::IdRegions

validates_presence_of :name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this necessary?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it is, it need to be multi-region aware.


def down
say_with_time("Renaming user-defined Maintenance Zone") do
Zone.in_my_region.where(:name => Zone::MAINTENANCE_ZONE_NAME).where(:visible => false).destroy_all
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The wheres can be merged

@bdunne
Copy link
Member

bdunne commented Aug 28, 2018

"just fail the migration"

Doesn't seem acceptable to me. If it was only developers I could be okay with it, but not for customers. I know it's an edge case, but they're not going to know what went wrong or how to fix it and it's going to lead to an unnecessary support call on a weekend (since that's when they do upgrades). Maybe we could talk about something like #222 (comment) instead?

@carbonin
Copy link
Member

@bdunne they should know exactly what to do to fix it if we include the remediation steps in the failure message. It should be straight forward enough.

@bdunne
Copy link
Member

bdunne commented Aug 28, 2018

Okay, so @carbonin and I were just discussing this some more...

I think renaming an existing zone in a migration is just out of the question. Too many things don't use real relations and point to zones by name (mistakes were made), that's why there is a uniqueness validation on Zone#name.

That said, it really doesn't matter what the name of our maintenance zone is as long as we can find it when we want to use it. I think we should generate a random zone name and create that as our maintenance zone (and if it conflicts with an existing one, generate a new one until it doesn't conflict). Then we can put a reference to that zone on MiqRegion so that we can find it in the future.

Is there any case where we would want multiple maintenance zones?

@slemrmartin
Copy link
Contributor Author

@bdunne good point
maintenance zone is identified by unique name (just like default zone, I wanted to be consistent) and created outside migration in Zone.seed

Answer is: No, we don't want multiple maintenance zones - that's another reason why it isn't identified by some boolean flag.

I consider your solution as more robust, but it requires new migration to MiqRegion - I hope for choosing any solution, it was customer request (urgent, I suppose) 3 months ago, I think ;))

@Fryguy
Copy link
Member

Fryguy commented Sep 4, 2018

Reading back, I'm on the fence about renaming maintenance zone. On one hand, having a hard-coded name is nice because that's what we have for the default zone. However the difference is that the default zone is seeded way before a customer gets a chance to create their own zone. Additionally, as @bdunne mentioned, the data migration would also have to rename every column where we store zone as a name, such as in MiqQueue, and in the worst case some deep yaml options column (I really hope this isn't a thing, but you never know).

That said, it really doesn't matter what the name of our maintenance zone is

It does matter if we have to show it somewhere. We can probably easily hide it from the Zone configuration page, but my concern is there's going to some random locations in the UI we haven't considered and the user is going to see __mainenance__525face0-927b-0136-588e-3c15c2c7fbb8 .

Is there any case where we would want multiple maintenance zones?

I don't think we want multiple zones for any particular purpose, but there will be multiple maintenance zones upon replication. For the default zone we deal with this just fine, but the default zone isn't "special"; it's just a normal zone we seed. For maintenance, if we need to "hide" it, then you get into having to hide multiple zones in the UI.

@carbonin
Copy link
Member

carbonin commented Sep 4, 2018

@Fryguy For what it's worth, we don't show the region accordion for anything other than the current region. So we don't really display the zone anywhere in the UI.

But while thinking about this, I'm realizing that the provider (and its relation to its zone) would be replicated. That makes me think that the global will need the information about which zone is is the maintenance zone for each of the remote regions to display the provider correctly (as suspended).

@slemrmartin
Copy link
Contributor Author

I would prefer to keep identification by name __maintenance__. We searched for all occurences in UI and API and hide this zone (and others) identified by visible? boolean.
But there could be always some random place, of course.

If we need to rename existing __maintenance__, I can search schema.rb for all "zone" columns which are string and add it to this migration. This can solve remaining problems, right?

Like Job,MiqTask,MiqQueue + raises error when renaming existing zone to another existing.
@miq-bot
Copy link
Member

miq-bot commented Sep 10, 2018

Checked commits slemrmartin/manageiq-schema@8767c08~...87bbb6a with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0
6 files checked, 2 offenses detected

db/migrate/20180618084054_init_zones_visibility.rb

say_with_time("Renaming user-defined Maintenance Zone") do
zone = Zone.in_my_region.find_by(:name => Zone::MAINTENANCE_ZONE_NAME)
if zone.present?
# STEP 1 - rename zone
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still think this is the wrong approach

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bdunne you were worried about string associations

  1. possibility that user has __maintenance__ as name is almost zero
  2. if so, zone and associations are renamed to name with possibility of duplication zero^2.

could you be more specific? When this fails (with significant probability).
Is it not acceptable for @Fryguy, @agrare, @carbonin too?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. possibility that user has maintenance as name is almost zero

I agree, but while unlikely, not impossible

  1. if so, zone and associations are renamed to name with possibility of duplication zero^2.

Right now you're only updating Job and MiqQueue. The following models have a zone column:

=> ["MiqQueue", "Job", "MiqTask", "ManageIQ::Providers::Kubernetes::ContainerManager::Scanning::Job", "ManageIQ::Providers::EmbeddedAnsible::AutomationManager::PlaybookRunner", "ManageIQ::Providers::EmsRefreshWorkflow", "ManageIQ::Providers::AnsibleRunnerWorkflow", "VmScan", "ManageIQ::Providers::NativeOperationWorkflow", "Service::LinkingWorkflow"]

I also believe it's in Settings and who knows how many other places it's buried in a serialized column. I just don't think it's practical to try renaming a zone

@slemrmartin
Copy link
Contributor Author

I prepared alternative PR with region association:
#275

@agrare
Copy link
Member

agrare commented Sep 24, 2018

Closing in favor of #275 now that it is merged

@agrare agrare closed this Sep 24, 2018
@slemrmartin slemrmartin deleted the suspend-provider branch September 24, 2018 18:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants