-
Notifications
You must be signed in to change notification settings - Fork 897
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
Add hash_expression to MiqAlert #15315
Conversation
@simon3z @moolitayer @cben @dkorn Please review |
@miq-bot add_label enhancement, alerts |
app/models/miq_alert.rb
Outdated
@@ -73,10 +74,14 @@ def set_responds_to_events | |||
self.responds_to_events = events unless events.nil? | |||
end | |||
|
|||
def set_expression_type | |||
self.expression_type = self.expression.class unless self.expression_type || self.expression.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a need to add this column over, say, memoizing this result? i.e.
def expression_type
@expression_type ||= expression.class
end
I'm not sure I follow the rationale for adding an |
I don't understand this PR or the related #14979, what feature is missing originally? I'm concerned because |
I think we all agree "MiqExpression is already complicated" :-|
@moolitayer @dkorn are/will these used for anything beyond hawkular-evaluated alerts? Our concrete problem is the hash ones can't be round-tripped through the API. (API returns either as json hash, losing the distinction; it converts any incoming hash to MiqExpression.) This is one option out several ideas how to begin untangling this: #14979 (comment) Talking of code smells, see also #14979 (comment) how miq_alert.rb is full of |
This change was agreed upon[1] as a perquisite for the alert definitions PR. That happened after a very long time of trying to get feedback in various channels and makes perfect sense.
Yes, all the [1] #14979 (comment)
|
@imtayadeway @abellotti Please review and let us know how to proceed with this and #14979 as soon as possible |
This seems to be the root of the problem. I'd like to know what is preventing us from fixing this?
If I understand this correctly, subclassing MIqAlert would create a Liskov substitution problem. To me, there is no getting around the fact that this column is storing two totally different things, that don't share an interface and can't be used interchangeably. I can't approve a change that preserves this behavior, at least without understanding why we can't address root problem above. |
That goes against what was explicitly requested of us in #14979 (comment) If you disagree with that please discuss it with @abellotti and let us know what you two decide. |
@@ -0,0 +1,23 @@ | |||
class ::MiqExpression; end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a major no-no!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! We suspected so but weren't sure what's a right way.
It's a serialized column, without a stub accessing it will instantiate lib/miq_expression.rb
(uncaught by good_migrations BTW, though it does fail when that (wrongly?) leads to requiring app/models/tag.rb.)
And namespacing the stub inside the migration serialize
doesn't pick it. Is there a way to deserialize using custom class namespace?
Or manipulate the YAML with regexps like https://github.com/ManageIQ/manageiq/blob/master/db/migrate/20150625220141_fix_serialized_reports_for_rails_four.rb ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you can try serializing manually or specify a coder which will use your MiqExpression stub within the migration namespace:
https://www.viget.com/articles/how-i-used-activerecord-serialize-with-a-custom-data-type
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just remembered where we stubbed classes for deserialization (if that turns out to be needed):
manageiq/spec/migrations/20150625220141_fix_serialized_reports_for_rails_four_spec.rb
Lines 6 to 10 in 9fdcde9
stub_const("MiqReport", Class.new(ActiveRecord::Base)) | |
stub_const("Ruport", Module.new) | |
stub_const("Ruport::Data", Module.new) | |
stub_const("Ruport::Data::Table", Class.new) | |
stub_const("Ruport::Data::Record", Class.new) |
say_with_time('Add expression type to MiqAlert for existing alerts') do | ||
MiqAlert.all.each do |alert| | ||
alert.expression_type = Hash if alert.expression.kind_of?(Hash) | ||
alert.expression_type = MiqExpression if alert.expression.kind_of?(MiqExpression) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what is this expression_type in SQL? A string? If so, I expect above 2 lines to set it this value to a string, and not a Ruby class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we have Ruby objects stored inside the serialized YAML, those are hard to solve in migrations. Easiest solution tends to be to treat the column like a regular string in the stub, and then just look for keywords in the string. For example:
class AddExpressionTypeToMiqAlert < ActiveRecord::Migration[5.0]
class MiqAlert < ActiveRecord::Base
end
def up
...
alert.expression_type = alert.expression.include?("MiqExpression") : "MiqExpression" : "Hash"
...
end
If you end up doing the string inclusion style, then you can do this with update_all in two calls:
def up
...
MiqAlert.where("expression LIKE ?", "%MiqExpression%").update_all(:expression_type => "MiqExpression")
MiqAlert.where(:expression_type => nil).update_all(:expression_type => "Hash")
end
@imtayadeway @abellotti if you don't have any further input, I'm continuing as agreed in #14979 (comment) |
@imtayadeway I'd like for you to do the last review on this, and I'll do a final review before merge. |
end | ||
|
||
it 'alert with miq_expression has expression_type MiqExpression' do | ||
miq_expression = YAML.load '--- !ruby/object:MiqExpression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This forces the object to be deserialized, which isn't necessary. You can just write this string directly into the column if you don't serialize in the stub in the first place a la https://github.com/ManageIQ/manageiq/pull/15315/files#r121777567
@zakiva I asked above in #15315 (comment) what was preventing us from fixing the root problem that this PR is attempting to accommodate. Are you able to provide details of that, regardless of what was agreed upon with other parties? That would help enormously, thanks! |
@imtayadeway can you please explain what change you want to see here? |
Database migrations have now been moved to the https://github.com/ManageIQ/manageiq-schema repo. Please see http://talk.manageiq.org/t/new-split-repo-manageiq-schema/2478 for instructions on how to transfer your database migrations. If this PR contains only migrations, I will leave it open for a short time during the transition, after which I will close this if it has not been moved over. |
@lucasponce fyi - does this change/add anything for hawkular alerts? |
@abonas on a quick look I think this change has not impact on the MiddlewareServer alerts evaluation, middleware classes are handled separately on |
This change should be backward compatible (if not it is a bug). The implementation is going to change (we will have two separate columns for miq_expression and hash typed expressions) expression is already sometimes an expression and sometimes a Hash (like for hawkular alerts) this PR is going to make the distinction clearer. |
@zakiva I discussed this with @imtayadeway. What we want to see here at this point is two new columns in In another PR we will try to better encapsulate these types, we can start with the migration of that part. @imtayadeway is that accurate? This work will allow us to continue with #14979 which will use to seed policies using ansible. |
@moolitayer @imtayadeway I updated the PR according to this approach, please review. |
e951ce1
to
204559a
Compare
@miq-bot rm_label sql migration |
app/models/miq_alert.rb
Outdated
def validate_single_expression | ||
unless miq_expression.nil? || hash_expression.nil? | ||
errors.add("Alert", "must not have both miq_expression and hash_expression set") | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
It's worth validating that there is exactly one value. That would allow simplifying some code in this PR
(line 555 for example)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Validating that there is exactly one value means that you will not be able to have a nil
expression which was possible until now, so I preferred not to change that behavior.
@zakiva I like that the outside interface has not changed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
@imtayadeway @Fryguy please review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM 👍
Ping @imtayadeway @Fryguy |
I re-reviewed the schema change and found some problems with it. Would like those resolve first. |
app/models/miq_alert.rb
Outdated
@@ -89,6 +103,12 @@ def validate_automate_expressions | |||
valid | |||
end | |||
|
|||
def validate_single_expression | |||
unless miq_expression.nil? || hash_expression.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer if miq_expression && hash_expression
, which to me is a lot clearer in that it is saying "if both things are set", as opposed to "if neither thing is not set" (double-negatives and all that)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
changed :)
when "internal" then return evaluate_internal(target, inputs) | ||
when "automate" then return evaluate_in_automate(target, inputs) | ||
when "script" then return evaluate_script | ||
else raise "unable to evaluate expression: [#{expression.inspect}], unknown mode" | ||
else raise "unable to evaluate expression: [#{hash_expression.inspect}], unknown mode" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe not for this PR, but we are mixing evaluation of 2 different expression types into a single method, and that is causing confusion. I think this would be a LOT clearer if the two evaluation strategies were split into methods, with eval_expression branching between those
def eval_expression(target, inputs = {})
case expression
when MiqExpression then eval_miq_expression(target, inputs = {})
when Hash then eval_hash_expression(target, inputs = {})
else raise "unable to evaluate expression: [#{expression.inspect}], unknown format"
end
end
I think I'm also leaning towards using expression
where possible in the places where it has been changed to individually check miq_expression
, then hash_expression
, then nil
. Checking for each one in turn is sort of the purpose of the expression
method itself.
However, in a method like my suggested eval_hash_expression
, where you know you are working with hash_expression, then yes, you would access it directly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Fryguy This is defiantly the direction we are aiming at. As @imtayadeway pointed in an earlier discussion, having both expression types in the same column was the root problem here, which we hope to handle in this PR (and its related schema change). The next step will be trying to better encapsulate these types and their evaluation, maybe with two new tables each with one column (it will probably require more discussion). However, since this PR blocks some other items we are working on we would like to move forward with the current implementation as an initial solution if you are OK with it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds great @zakiva
Checked commit zakiva@97c94f6 with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0 app/models/miq_alert.rb
|
Merged schema in ManageIQ/manageiq-schema#49 |
Merging 🔴 to fix db seeding on master |
Depends on: ManageIQ/manageiq-schema#49
As discussed in #14979, we want to allow a hash typed expression in the alert definition api.
We will have 2 separate columns, one for miq expression and another for hash expression. I'm keeping the interface of writing and reading the
expression
attribute to keep backward compatibility (so we can avoid refactoring major areas right now).