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

Event parser can parse new format of target #160

Merged

Conversation

Ladas
Copy link
Contributor

@Ladas Ladas commented Mar 3, 2017

Depends on:

Event parser can parse new format of target + specs

@Ladas
Copy link
Contributor Author

Ladas commented Mar 3, 2017

@durandom @kbrock @agrare @blomquisg @Fryguy so this is how the new target format could look like, so we don;t require a Target being created in our DB

{"Vm" => {:manager_id => ems_id, :manager_ref => Set.new({:ems_ref => xy}, {:ems_ref => zz}) }}

It would be probably worth to pack this to ManagerRefresh::Target, where .serialize would return the ^ hash, so it can be queued to the refresh and we could then get it back with ManagerRefresh::Target.new(class, manager_id:, manager_ref: , ..)

then you could always get the records connected to the Target with:
target.manager_ref.map { |ref| target.class.find_by(ref.merge(:ems_id => target.manager_id)) }

or any other way, which would be more effective, though we should not need to really instantiate it ourselves

@durandom durandom changed the title Event parser can parse new format of target [WIP] Event parser can parse new format of target Mar 7, 2017
@durandom durandom added the wip label Mar 7, 2017
Copy link
Member

@durandom durandom left a comment

Choose a reason for hiding this comment

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

I think it would be great if you could extract the parsing stuff into its own class.

For starters it could live here and be called Target although this is probably not the final name.

Then you can write a spec for that object and not convolute the other specs

_guid, _server, zone = EvmSpecHelper.create_guid_miq_server_zone
@ems = FactoryGirl.create(:ems_amazon, :zone => zone)

allow_any_instance_of(EmsEvent).to receive(:handle_event)
Copy link
Member

Choose a reason for hiding this comment

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

allow_any_instance_of is the root of all evil. Chris and JoeR try hard to get eliminate it. Can do without it? Maybe inject an instance of EmsEvent into somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sure we can get rid of that if we will rewrite the code. The EmsEvent.add needs to have submethods, now it's all entangled together.


case event.full_data["event_source"]
when :cloud_watch_api
collect_cloudwatch_api_references!(available_targets, event.full_data.fetch_path("detail", "requestParameters") || {})
Copy link
Member

Choose a reason for hiding this comment

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

all those bang methods. It's hard to tell what they actually modify.
This screams for extraction of an object 🛠

Copy link
Contributor Author

Choose a reason for hiding this comment

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

well they do build available_targets

raise "Unsupported event source #{event["event_source"]}"
end

send("parse_#{event["event_source"]}_event!", event, event_hash)
Copy link
Member

Choose a reason for hiding this comment

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

chilly bang bang. What is modified by this method?
It would be much easier to follow if you return the modified version and assign it.

event_hash = send("parse_#{event["event_source"]}_event", event)

Copy link
Member

Choose a reason for hiding this comment

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

but I just saw, this is already in the code :/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah this would be probably nicer

Copy link
Member

Choose a reason for hiding this comment

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

sigh

@durandom
Copy link
Member

durandom commented Mar 7, 2017

I like the .serialize idea 👍

Could you explain a bit, when and how this logic is triggered?
So, when is a refresh scheduled for such a non existent target?

@Ladas
Copy link
Contributor Author

Ladas commented Mar 7, 2017

@durandom so this logic is not trigered yet, but the workflow should be

  1. parse event so we can store EmsEvent to the DB

  2. if there is a targeted refresh, queue for refresh all targets we get from parser_class.parse_targets(ems_event) and optionally limit the targets parsed

  • so the new automate method would be something like:
    2.1) /System/event_handlers/graph_refresh # queues all available targets for refresh
    2.2) /System/event_handlers/graph_refresh?limit_targets=Vm # queues only target of type Vm, if available
    2.3) /System/event_handlers/graph_refresh?with_relations=Vm # queues Vm targeted refresh, with options to scan the Vm and queue also all it's relations for refresh, so it's a Vm full refresh
  1. Save EmsEvent with all the targets as M:N relations, this needs to be invoked after the refresh, so all the targets are in the DB

Ladas added 2 commits March 7, 2017 17:43
Adding class for parsing targets from an Event
Adding specs for EmsEvent Target parsing
@Ladas Ladas force-pushed the event_parser_can_parse_new_format_of_target branch from c7a8076 to a84469f Compare March 7, 2017 16:44
@durandom
Copy link
Member

durandom commented Mar 8, 2017

@Ladas 🤘 for creating all in new classes

Copy link
Member

@durandom durandom left a comment

Choose a reason for hiding this comment

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

great iteration

next step should be the EventTarget class extraction

And I still need to understand how this will be used when picked later from the event queue


context "AWS Config Event" do
it "parses vm_ems_ref into event" do
ems_event = create_ems_event("sqs_message.json")
Copy link
Member

Choose a reason for hiding this comment

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

Still, I would create an EmsEvent via a factory here and not test the whole EmsEvent Stream stack.

But I get your integration spec needs. But shouldnt those parts of the code already be tested somewhere else?

Copy link
Contributor Author

@Ladas Ladas Mar 9, 2017

Choose a reason for hiding this comment

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

I really want to test this on real events, so using the actual jsons of the event. I don't really want to stub the middlelayers. I want to add like 40-80 main events for testing, would be dull and error prone to do the double stubbing

@@ -0,0 +1,139 @@
class ManageIQ::Providers::Amazon::CloudManager::EventTargetParser
Copy link
Member

Choose a reason for hiding this comment

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

👍 for this class

Now extract another class, e.g. EventTarget which holds add_reference and the available_targets data. Then you have fully encapsulated that data you want to transport into a class and moved away from hashes.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

done, ManagerRefresh::Target and ManagerRefresh::TargetCollection are being introduced as a dependency

Ladas added 2 commits March 9, 2017 17:51
Change EventTargetParser to use the ManagerRefresh target abstractions
Use ManagerRefresh::TargetCollection instead of the Amazon one
Copy link
Member

@kbrock kbrock left a comment

Choose a reason for hiding this comment

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

this is looking nice.

I got the general idea behind this. My comments are only cosmetic

def collect_config_references!(target_collection, event_data)
resource_type = event_data.fetch_path("configurationItem", "resourceType")
resource_id = event_data.fetch_path("configurationItem", "resourceId")
target_class = case resource_type
Copy link
Member

Choose a reason for hiding this comment

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

would it make sense to do a hash lookup here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

possibly, but maybe the case is enough? Should be still pretty quick

end
byebug
Copy link
Member

Choose a reason for hiding this comment

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

heh

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yaay :-)

collect_cloudwatch_api_references!(target_collection, x, depth + 1)
end
(event_data.fetch_path("instances") || []).each do |x|
collect_cloudwatch_api_references!(target_collection, x, depth + 1)
Copy link
Member

Choose a reason for hiding this comment

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

would it make sense to expand this api? collect_cloud_watch_api_referenceses!

of course, naming is hard. Then again, do you ever collect_cloudwatch_api_references with a single array?

collect_cloud_watch_api_referenceses!(target_collection, event_data.fetch_path("instances") || [], depth + 1)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

no usually the cloudwatch events have a nested arrays that contain references

@@ -30,49 +30,21 @@ def initialize(_manager, _target)

def initialize_inventory_sources
@instances = []
@instances_refs = 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.

yay.

I'm assuming this doesn't add any O(N) lookups

Copy link
Contributor Author

Choose a reason for hiding this comment

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

the refs were moved to ManagerRefresh::TargetCollection

Copy link
Member

Choose a reason for hiding this comment

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

Ooh, then that is very cool

end

def infer_related_ems_refs!
# We have a list of instances_refs collected from events. Now we want to look into our DB and API, and collect
# ems_refs of every related object. Now this is not very nice fro ma design point of view, but we really want
# to see changes in VM's associated objects, so the VM view is always consistent and have fresh data. The partial
# reason for this is, that AWS doesn't send all the objects state change,
changed_vms = manager.vms.where(:ems_ref => instances_refs.to_a).includes(:key_pairs, :network_ports, :floating_ips,
:orchestration_stack)
byebug
Copy link
Member

Choose a reason for hiding this comment

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

heh

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hehe

@@ -24,6 +24,10 @@ def initialize_inventory_collections

private

def references(collection)
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 a generic method?
Can you get define it in somewhere like ManagerRefresh::Inventory::Collector or highter?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

right, it's not really generic since it states :ems_ref, but maybe I could pack part of it to the TargetCollection

Ladas added 2 commits March 10, 2017 09:25
…Persister

Use ManagerRefresh::TargetCollection collection for TargetCollection Persister
…Collector

Use ManagerRefresh::TargetCollection collection for TargetCollection Collector
@Ladas Ladas force-pushed the event_parser_can_parse_new_format_of_target branch from 2d68ec5 to f67259d Compare March 10, 2017 08:26
add_target! method was renamed to add_target
@Ladas Ladas closed this Mar 10, 2017
@Ladas Ladas reopened this Mar 10, 2017
Fix EventTargetParser specs, it's broken due to core changes
@miq-bot
Copy link
Member

miq-bot commented Mar 13, 2017

Some comments on commits Ladas/manageiq-providers-amazon@308e47b~...50ee99e

spec/models/manageiq/providers/amazon/cloud_manager/event_target_parser_spec.rb

  • ⚠️ - 6 - Detected allow_any_instance_of. This RSpec method is highly discouraged, please only use when absolutely necessary.

@miq-bot
Copy link
Member

miq-bot commented Mar 13, 2017

Checked commits Ladas/manageiq-providers-amazon@308e47b~...50ee99e with ruby 2.2.6, rubocop 0.47.1, and haml-lint 0.20.0
7 files checked, 0 offenses detected
Everything looks good. 👍

@Ladas Ladas changed the title [WIP] Event parser can parse new format of target Event parser can parse new format of target Mar 13, 2017
@Ladas Ladas removed the wip label Mar 13, 2017
Copy link
Member

@durandom durandom left a comment

Choose a reason for hiding this comment

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

👍 except for some refactoring suggestions

#
# @return [Array] Array of ManagerRefresh::Target objects
def parse
parse_ems_event_targets(ems_event)
Copy link
Member

Choose a reason for hiding this comment

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

Now that you add ems_event as a state (instance variable), I think you can also create target_collection as an instance var and let collect_cloudwatch_api_references operate on that.

attr_reader :load_balancers, :load_balancers_refs, :load_balancers_deleted
attr_reader :stacks, :stacks_refs, :stacks_deleted
attr_reader :cloud_volumes, :cloud_volumes_refs
attr_reader :cloud_volume_snapshots, :cloud_volume_snapshots_refs
Copy link
Member

Choose a reason for hiding this comment

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

👏 for less attr_readers

@@ -3,24 +3,30 @@ def initialize(_manager, _target)
super
parse_targets!
infer_related_ems_refs!

target.manager_refs_by_association_reset
Copy link
Member

Choose a reason for hiding this comment

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

This would be a perfect ! method

def parse_vm_target!(t)
instances_refs << t.ems_ref if t.ems_ref
target.add_target(:association => :vms, :manager_ref => {:ems_ref => t.ems_ref}) if t.ems_ref
Copy link
Member

Choose a reason for hiding this comment

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

If this is mostly always target.add_target, you might also allow target.add syntax

Copy link
Member

Choose a reason for hiding this comment

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

How about renaming this to targets or target_collection

@durandom durandom merged commit 578c455 into ManageIQ:master Mar 13, 2017
@durandom durandom added this to the Sprint 56 Ending Mar 13, 2017 milestone Mar 13, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants