-
Notifications
You must be signed in to change notification settings - Fork 80
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
Plugin installer migration #173
Plugin installer migration #173
Conversation
Signed-off-by: Chase Engelbrecht <[email protected]>
Signed-off-by: Chase Engelbrecht <[email protected]>
…dd unit tests for PluginInstaller Signed-off-by: Chase Engelbrecht <[email protected]>
Signed-off-by: Chase Engelbrecht <[email protected]>
@@ -26,9 +26,10 @@ def __init__(self, provision_config_instance, executor, hook_handler_class=Boots | |||
self.config_applier = ConfigApplier(executor, self.template_renderer, self.path_manager) | |||
self.host_cleaner = HostCleaner(self.path_manager) | |||
|
|||
def install(self, host, binaries, all_node_ips): | |||
# pylint: disable=arguments-differ | |||
def install(self, host, binaries, all_node_ips, config_vars=None): |
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.
Should config_vars
just be added to the Installer.install
interface?
Is there a reason that disabling the linter is preferred?
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.
config_vars
shouldn't be part of this method's signature at all IMO. An installer should be responsible for owning and defining its own config_vars rather than having some passed into it.
The trouble comes when both OS and plugin(s) are set to be installed. In this case, the config_vars from all sources must be considered. Adding config_vars
to the signature allows the CompositeInstaller to pass in the finalized config_vars from all sources without changing the general flow of the sub-installers. OpenSearchInstaller can be consumed as a standalone Installer implementation or utilized by the CompositeInstaller via the same install method.
I feel that I am missing a design pattern that would eliminate the need to add config_vars
to this method. I will think on it some more and try to come up with a better solution.
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 agree with your assessment that config_vars
shouldn't be a part of this method. Its essentially an implementation detail that the user of this method shouldn't care about. Also, that's why the Installer
interface shouldn't & doesn't have it.
Please correct me if I'm wrong, but it seems like each installer already just makes a single install call in its lifetime. Constructor is instance specific and can be different for classes implementing the same interface, have you considered having the config_vars
as an optional constructor param for OpenSearchInstaller
and making it an class level variable?
I believe this way the CompositeInstaller can have multiple sub-installers, and not break their general flow. However, if you mean the CompositeInstaller needs results from sub-installers to come up with the final config_vars
, I'd still recommend the same approach, but using a factory class the generates the OS installer instance after the config_vars
are ready.
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.
The CompositeInstaller does need results from the sub-installers to come up with the final config_vars
. Specifically, it needs the Node object generated by the OpenSearchInstaller's install
method.
With the current implementation there's no way to generate the config_vars
without first instantiating an instance of OpenSearchInstaller and invoking the install
method to get a Node. I think a further layer of abstraction to separate preparing the installation and applying the config data is the solution here: create new OpenSearchPreparer and PluginPreparer classes with prepare
and get_config_vars
methods.
Then the OpenSearch and PluginInstallers would take in a respective instance of these Preparer classes and perform the whole installation flow (including applying the config) within the install method. The CompositeInstaller would have instances of both OpenSearchPreparer and PluginPreparer, with the flow being: call prepare methods -> call get_config_vars
methods -> generate final config_vars
-> apply configs
I plan to make heavy use of the factory pattern when selecting the correct builder components for the ClusterBuilder
:)
@@ -26,9 +26,10 @@ def __init__(self, provision_config_instance, executor, hook_handler_class=Boots | |||
self.config_applier = ConfigApplier(executor, self.template_renderer, self.path_manager) | |||
self.host_cleaner = HostCleaner(self.path_manager) | |||
|
|||
def install(self, host, binaries, all_node_ips): | |||
# pylint: disable=arguments-differ | |||
def install(self, host, binaries, all_node_ips, config_vars=None): |
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 agree with your assessment that config_vars
shouldn't be a part of this method. Its essentially an implementation detail that the user of this method shouldn't care about. Also, that's why the Installer
interface shouldn't & doesn't have it.
Please correct me if I'm wrong, but it seems like each installer already just makes a single install call in its lifetime. Constructor is instance specific and can be different for classes implementing the same interface, have you considered having the config_vars
as an optional constructor param for OpenSearchInstaller
and making it an class level variable?
I believe this way the CompositeInstaller can have multiple sub-installers, and not break their general flow. However, if you mean the CompositeInstaller needs results from sub-installers to come up with the final config_vars
, I'd still recommend the same approach, but using a factory class the generates the OS installer instance after the config_vars
are ready.
if self.hook_handler.can_load(): | ||
self.hook_handler.load() |
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.
Does this need to happen during the object construction itself? Would it make sense to move this to the install logic?
I'm not fundamentally against it but prefer lightweight constructors. Think of scenarios where these fail and what stack trace they leave - I feel it makes more sense if we say the plugin failed to install /load. Instead if the constructor throws an exception, I suspect there's things like class not found exception or something which isn't a good description of what happened.
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 could go in the install
logic. The rationale for having it here is to fail fast if an exception is thrown when loading the hook_handler
.
If this logic is moved to the install
method, then the exception it creates on failure would only be thrown after the infrastructure has been provisioned and the necessary data downloaded and installed on the host. By failing in the constructor, no resources are created or work done for a scenario that couldn't possibly succeed.
Perhaps wrapping the exception with some documentation on the failure would be a solution to the vague description. Given that this hook_handler
logic is used in multiple Installers, adding that handling into the factory generating Installer instances would be a good approach.
Signed-off-by: Chase Engelbrecht <[email protected]>
Signed-off-by: Chase Engelbrecht <[email protected]>
Signed-off-by: Chase Engelbrecht <[email protected]>
After thinking more on it, there's no need to distinguish between a SingularBareInstaller and a CompositeBareInstaller as the flow can be the exact same via looping. Instead, I've opted to create a BareInstaller that handles both scenarios |
def _get_node(self, preparer_to_node): | ||
nodes_list = list(filter(lambda node: node is not None, preparer_to_node.values())) | ||
|
||
assert len(nodes_list) == 1, "Exactly one node must be provisioned per host, but found nodes: {}".format(nodes_list) |
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.
You have preparers_to_nodes as a list, so I guess that one node per host may be changed in the future?
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.
preparers_to_nodes
is a map of a Preparer
to the return value of its prepare
method, which can be either a Node
object or None
. Only 1 Preparer
should be generating a Node
, the others are used to install additional components (i.e. plugins) to that same Node
Allowing more than one node per host could be implemented in the future, but there doesn't seem to be much justification for supporting it at the moment.
from osbenchmark.builder.utils.template_renderer import TemplateRenderer | ||
|
||
|
||
class BareInstaller(Installer): |
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.
The naming is fine, but hope to understand what installations are attributed to "bare"..?
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.
Bare essentially means "not in a container". The original implementation used this name and I carried forward since I can't think of a better one. Open to suggestions if anyone has ideas
Description
Migrates the PluginInstaller. Also slight refactors to OpenSearchInstaller to rename
provisioner_defaults
->installer_defaults
and make config_vars an optional param for theinstall
method. The CompositeInstaller coming in the next PR will use this new parameter to pass inconfig_vars
overrides.Issues Resolved
#134
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.