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

Create event hook infrastructure #952

Closed
wants to merge 1 commit into from
Closed

Conversation

vuntz
Copy link
Member

@vuntz vuntz commented Jan 4, 2017

There are several cases where we have events that should trigger some activity in some other part of Crowbar. For instance:

  • when the public name of a node is saved, it may impact the endpoint of an OpenStack service
  • when the public name of the VIP of haproxy is changed, it impacts the endpoint of OpenStack services
  • when the keystone proposal is applied, we may want to reapply all proposals that depend on keystone

What we need for this is the ability to notify about the events in the rails application and then to dispatch the notifications to hooks listen listening to them that will decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't want to do that in the foreground of the rails application. But now that we have delayed_job, we can send the notifications and run the hooks in the background.

In this commit, we add the simple infrastructure about notifications and hooks:

  • the events are defined with a name and a hash that contains the details of the event. The structure of the hash depends on the event.

  • we have two events that are generated and notified: node_changed (when a node is saved and some specific attributes have been changed) and proposal_applied (when a proposal is successfully applied).

  • a simple dispatcher exists that simply connects the hooks to the events.

  • the hooks only exist for service objects for the time being; a service object simply needs to have a event_hook method to register the hook, and will need to filter for the events it cares about. The signature of event_hook is as follows: def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific events (hence moving the filter to the event dispatcher), but it's not worth the complexity for now.

We could add many more events but for the time being, we only add the events that we know are useful.

def trigger_hooks(event, details)
BarclampCatalog.barclamps.keys.each do |barclamp|
begin
cls = eval("#{barclamp.camelize}Service")

Choose a reason for hiding this comment

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

Lint/Eval: The use of eval is a serious security risk.

Copy link
Contributor

@matelakat matelakat left a comment

Choose a reason for hiding this comment

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

Thanks for the clarification! Please see my comment about the @notify_on_save hash.

@@ -259,6 +259,7 @@ def initialize(node)
end
# deep clone of @role.default_attributes, used when saving node
@attrs_last_saved = @role.default_attributes.deep_dup
@notify_on_save = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

This variable really holds the notifications to be sent on the next save, if understand it correctly. Also looking at the point where the notifications are sent out, it seems, that we are not using the value in the hash, only the keys. I think a better name for this variable might help, and also making it an array/set would also help. I would name it something like: on_save_events. Thus the protocol would look like: the client adds/queues up items to the on_save_events collection, and those are being sent and flushed on save.

@@ -479,6 +483,7 @@ def validate_public_name(value, unique_check = true)
def update_public_name(value)
unless value.nil?
crowbar["crowbar"]["public_name"] = value
@notify_on_save["public_name"] = true
Copy link
Contributor

Choose a reason for hiding this comment

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

It might be a stupid question: Are we sure that update_public_name is only called whenever the value of it has changed? I'm not seeing the same check-if-changed mechanism as earlier.

Copy link
Member Author

Choose a reason for hiding this comment

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

See https://github.com/crowbar/crowbar-core/pull/952/files#diff-496ddf4ffc5694a5fcdddbff8206dc8dR441 (there's also force_public_name= where the check is missing, but I think it's fine to still send the event in that case since it's forced).


details = {
node: @node.name,
attributes: @notify_on_save.keys
Copy link
Contributor

Choose a reason for hiding this comment

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

As I mentioned earlier, we only send the keys, not the values. So we might want to send the values as well, or use a single set as mentioned earlier.

Copy link
Member Author

Choose a reason for hiding this comment

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

I can move to a set, sure. FWIW, in ruby, a set is implemented (behind the scenes) exactly the way I did, so won't be too much different but I guess it's slightly more readable.

@vuntz vuntz force-pushed the event-hooks branch 2 times, most recently from 66792a7 to 2ee29ec Compare January 4, 2017 12:20
def trigger_hooks(event, details)
BarclampCatalog.barclamps.keys.each do |barclamp|
begin
cls = eval("#{barclamp.camelize}Service")

Choose a reason for hiding this comment

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

Lint/Eval: The use of eval is a serious security risk.

Copy link
Member

Choose a reason for hiding this comment

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

Calculating the class name and obtaining the class surely belongs in methods in BarclampCatalog. Doing it here sounds like privacy violation / feature envy.

Copy link
Member

Choose a reason for hiding this comment

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

Isnt the way of doing this better with Kernel.const_get ?*

*not a ruby expert

Copy link
Member Author

Choose a reason for hiding this comment

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

@Itxaka you're right.

Copy link
Member Author

Choose a reason for hiding this comment

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

We actually have ServiceObject.get_service exactly for this...

@vuntz
Copy link
Member Author

vuntz commented Jan 4, 2017

@matelakat I addressed your comments. I also renamed the small library to EventDispatcher as Notify was too generic...

end
end
end
handle_asynchronously :trigger_hooks
Copy link
Member

Choose a reason for hiding this comment

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

Should be a blank line before this.

Copy link
Member

Choose a reason for hiding this comment

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

Oh, maybe not. It's kind of like a decorator.

proposals.each do |proposal|
role = proposal.role
next if role.nil?
service.event_hook(role, event, details)
Copy link
Member Author

Choose a reason for hiding this comment

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

We need some exception catching here.

@@ -289,7 +291,10 @@ def availability_zone
def availability_zone=(value)
@node["crowbar_wall"] ||= {}
@node["crowbar_wall"]["openstack"] ||= {}
@node["crowbar_wall"]["openstack"]["availability_zone"] = value
if @node["crowbar_wall"]["openstack"]["availability_zone"] != value
Copy link
Contributor

Choose a reason for hiding this comment

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

@vuntz as I mentioned it in the "eliminate saves" PR earlier, this pattern might worth extracting to a helper, something like: set_value_and_call_function_if_changed or I believe ruby must have better ways to express it.

}
Crowbar::EventDispatcher.trigger_hooks(:node_changed, details)

@on_save_events = Set.new
Copy link
Contributor

Choose a reason for hiding this comment

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

Can this be called in parallel by different puma threads? If so isn't this a race and we could miss save events when cleaning the Set?

Copy link
Member Author

Choose a reason for hiding this comment

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

The node object is not shared among puma threads, so it should be fine. When we create threads ourselves, we should be careful of course and use proper locking mechanisms, but it's not specific to this change.

Copy link
Member

@aspiers aspiers left a comment

Choose a reason for hiding this comment

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

Once the variable is renamed and comment addressed, I'm good with this PR.

@@ -259,6 +260,7 @@ def initialize(node)
end
# deep clone of @role.default_attributes, used when saving node
@attrs_last_saved = @role.default_attributes.deep_dup
@attributes_changed_for_event = Set.new
Copy link
Member

Choose a reason for hiding this comment

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

As discussed could you rename this to @attributes_changed and then add a comment explaining what it's used for?

@aspiers
Copy link
Member

aspiers commented Jan 19, 2017

Well, of course the merge conflict and Travis failures have to be fixed first :)

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then to dispatch the notifications to hooks listen
listening to them that will decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - we have two events that are generated and notified: node_changed
   (when a node is saved and some specific attributes have been changed)
   and proposal_applied (when a proposal is successfully applied).

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.

We could add many more events but for the time being, we only add the
events that we know are useful.
@vuntz
Copy link
Member Author

vuntz commented Jan 26, 2017

@aspiers rebased

@aspiers aspiers removed their assignment Feb 5, 2017
@aspiers aspiers assigned vuntz and unassigned rsalevsky Feb 5, 2017
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 23, 2017
Rebase of PR crowbar#952

Only committing the infrastructure changes
For usage example(s), see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 23, 2017
Rebase of PR crowbar#952 but only including the new infrastructure class
For usage examples, see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 23, 2017
Rebase of PR crowbar#952 but only including the new infrastructure class

For usage examples, see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 29, 2017
Rebase of PR crowbar#952 but only including the new infrastructure class

For usage examples, see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 29, 2017
Rebase of PR crowbar#952 but only including the new infrastructure class

For usage examples, see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
scottwulf added a commit to scottwulf/crowbar-core that referenced this pull request Aug 30, 2017
Rebase of PR crowbar#952 but only including the new infrastructure class

For usage examples, see:
crowbar#952
crowbar/crowbar-ha#171
crowbar/crowbar-openstack#717

There are several cases where we have events that should trigger some
activity in some other part of Crowbar. For instance:

 - when the public name of a node is saved, it may impact the endpoint
   of an OpenStack service
 - when the public name of the VIP of haproxy is changed, it impacts the
   endpoint of OpenStack services
 - when the keystone proposal is applied, we may want to reapply all
   proposals that depend on keystone

What we need for this is the ability to notify about the events in the
rails application and then dispatch the notifications to hooks which
listen for them and decide if some action should be triggered.

The main reason we didn't have this in the past is that we likely don't
want to do that in the foreground of the rails application. But now that
we have delayed_job, we can send the notifications and run the hooks in
the background.

In this commit, we add the simple infrastructure about notifications and
hooks:

 - the events are defined with a name and a hash that contains the
   details of the event. The structure of the hash depends on the event.

 - a simple dispatcher exists that simply connects the hooks to the
   events.

 - the hooks only exist for service objects for the time being; a
   service object simply needs to have a event_hook method to register
   the hook, and will need to filter for the events it cares about. The
   signature of event_hook is as follows:
     def event_hook(role, event, details)

It could be argued that the hooks should be registered for some specific
events (hence moving the filter to the event dispatcher), but it's not
worth the complexity for now.
@vuntz
Copy link
Member Author

vuntz commented Oct 9, 2017

Completed in #1308

@vuntz vuntz closed this Oct 9, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
9 participants