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

Composition over inheritance in probes #454

Merged
merged 15 commits into from
Mar 31, 2021
Merged

Composition over inheritance in probes #454

merged 15 commits into from
Mar 31, 2021

Conversation

kmazurek
Copy link
Contributor

@kmazurek kmazurek commented Mar 18, 2021

Resolves: #433

Overview of changes!

Probe components

This PR introduces the concept of probe components (ProbeComponent), replacing certain functionalities previously realised through mixins (composition over inheritance). With these changes the Probe class now includes two types of components:

  • agents, which are subclasses of AgentComponent. These replace AgentMixin and allow for having multiple agents running as part of a single probe. In the current implementation ProviderProbe uses this logic for its ProviderAgentComponent.
  • api, which is of type RestApiComponent. Since this is now part of the base Probe class, both provider and requestor probes can now use the yagna REST APIs.

Flattening probe hierarchy

With these changes there are now only two concrete probe classes: ProviderProbe and RequestorProbe (so no more RequestorProbeWithApiSteps and ProviderProbeWithLogSteps). All high-level test step functions are now placed in domain-specific mixins, some of which are shared between the probe classes (see goth/runner/probe/mixin.py for more details).

@kmazurek kmazurek self-assigned this Mar 18, 2021
@kmazurek kmazurek changed the title Move REST API functionality to base probe class Composition over inheritance in probes Mar 24, 2021
@kmazurek kmazurek marked this pull request as ready for review March 25, 2021 13:01
@kmazurek kmazurek requested a review from azawlocki March 25, 2021 13:02
goth/runner/__init__.py Outdated Show resolved Hide resolved
Copy link
Contributor

@azawlocki azawlocki left a comment

Choose a reason for hiding this comment

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

Good job, our class diagram looks much nicer with these changes!

I've left some remarks and questions, I think those in goth/runner/probe/agent.py need to be addressed before merging.

Comment on lines 72 to 73
agents: Set[AgentComponent]
"""Set of agent components that will be started as part of this probe."""
Copy link
Contributor

Choose a reason for hiding this comment

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

It it were a list, not a set, we could guarantee that the agents are started in the order they appear on the list. Maybe this would be a useful feature? Just thinking aloud.

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 agree, this could be useful. Please see my comment on AgentComponent.__hash__. We could replace this with an OrderedSet (though that would require pulling in an additional dependency, i.e. https://pypi.org/project/ordered-set/) or an OrderedDict.

Copy link
Contributor Author

@kmazurek kmazurek Mar 30, 2021

Choose a reason for hiding this comment

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

After your suggestion on AgentComponent.__hash__ I'm going to change this to a list.

for probe in self.get_probes(probe_type=AgentMixin):
awaitables.append(probe.start_agent())
for probe in self.probes:
awaitables.extend([a.start() for a in probe.agents])
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it would be cleaner to have a start_agents() method in Probe:

awaitables = [probe.start_agents() for probe in self.probes]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added start_agents method in 5a4a7b8

log_config.base_dir = probe.container.log_config.base_dir

self.log_monitor = LogEventMonitor(self.name, log_config)
self._last_checked_line = -1
Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need this line anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed in b365b29

self.log_monitor = LogEventMonitor(self.name, log_config)
self._last_checked_line = -1

async def wait_for_log(self, pattern: str, timeout: float = 1000) -> LogEvent:
Copy link
Contributor

Choose a reason for hiding this comment

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

Use None as default value.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated in b365b29

return entry

def __hash__(self):
return hash(self.name)
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems dangerous, why not use default __hash__() here which is based on object identity for custom classes
(https://docs.python.org/3/glossary.html#term-hashable)?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This, together with changing Probe.agents from a list to a set, was done to prevent adding the same agent type (not necessarily the same class instance) twice to a single Probe object (I assumed the same agent type would yield the same name).

We could replace this set + custom hash approach with a dict (that is: make Probe.agents a dict in which the keys are names assigned to agents). My problem with this approach is that it makes iterating over agents unnecessarily complex. Also, I think it still makes sense to keep the name property on agents, since it's used in AgentComponent itself for creating the agent's log file.

Copy link
Contributor

@azawlocki azawlocki Mar 30, 2021

Choose a reason for hiding this comment

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

Why forbid two agent components with the same type in a probe? I can imagine a test scenario with two requestor agents running in one node. Sure, they would have different names but the same type.

Moreover, to guarantee no two agent components have the same type I think it would be more natural to define __hash__() based on the type, not only the name.

Finally, nothing prevents one from adding two agent components with the same type/name, one of them will be simply silently ignored (probably the second one, but I'm not sure). Maybe instead of overriding __hash__() for AgentComponent, Probe should have a method like this:

   def add_agent(agent: AgentComponent):
       if any(a.name == agent.name for a in self.agents):
           raise SomeError(f"The probe already has an agent component named `{agent.name}`")         
      ....

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the suggestion, I like this idea. I'll add an add_agent method to Probe.



def test_agent_duplicate_names():
"""Test if two agent objects with the same name will be treated as equal."""
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we need this property?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Please see my comment on AgentComponent.__hash__.

Copy link
Contributor

@azawlocki azawlocki left a comment

Choose a reason for hiding this comment

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

LGTM!

@kmazurek kmazurek merged commit 2edc941 into master Mar 31, 2021
@kmazurek kmazurek deleted the km/probe-modules branch March 31, 2021 08:28
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.

Enable high-level yagna API calls in provider probes
2 participants