-
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
Changes from all commits
2f0f38f
c000e33
c4b8094
61acc8b
6c0b5c4
e639f1b
dd56c2d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
from osbenchmark.builder.installers.installer import Installer | ||
from osbenchmark.builder.installers.preparers.plugin_preparer import PluginPreparer | ||
from osbenchmark.builder.provision_config import BootstrapPhase | ||
from osbenchmark.builder.utils.config_applier import ConfigApplier | ||
from osbenchmark.builder.utils.java_home_resolver import JavaHomeResolver | ||
from osbenchmark.builder.utils.path_manager import PathManager | ||
from osbenchmark.builder.utils.template_renderer import TemplateRenderer | ||
|
||
|
||
class BareInstaller(Installer): | ||
def __init__(self, provision_config_instance, executor, preparers): | ||
super().__init__(executor) | ||
self.provision_config_instance = provision_config_instance | ||
if isinstance(preparers, list): | ||
self.preparers = preparers | ||
else: | ||
self.preparers = [preparers] | ||
self.template_renderer = TemplateRenderer() | ||
self.path_manager = PathManager(executor) | ||
self.config_applier = ConfigApplier(executor, self.template_renderer, self.path_manager) | ||
self.java_home_resolver = JavaHomeResolver(executor) | ||
|
||
def install(self, host, binaries, all_node_ips): | ||
preparer_to_node = self._prepare_nodes(host, binaries) | ||
config_vars = self._get_config_vars(host, preparer_to_node, all_node_ips) | ||
self._apply_configs(host, preparer_to_node, config_vars) | ||
self._invoke_install_hooks(host, config_vars) | ||
|
||
return self._get_node(preparer_to_node) | ||
|
||
def _prepare_nodes(self, host, binaries): | ||
preparer_to_node = {} | ||
for preparer in self.preparers: | ||
preparer_to_node[preparer] = preparer.prepare(host, binaries) | ||
|
||
return preparer_to_node | ||
|
||
def _get_config_vars(self, host, preparer_to_node, all_node_ips): | ||
config_vars = {} | ||
|
||
for preparer, node in preparer_to_node.items(): | ||
config_vars.update(preparer.get_config_vars(host, node, all_node_ips)) | ||
|
||
plugin_names = [preparer.get_plugin_name() for preparer in self.preparers if isinstance(preparer, PluginPreparer)] | ||
if plugin_names: | ||
# as a safety measure, prevent the cluster to startup if something went wrong during plugin installation | ||
config_vars["cluster_settings"] = {} | ||
config_vars["cluster_settings"]["plugin.mandatory"] = plugin_names | ||
|
||
return config_vars | ||
|
||
def _apply_configs(self, host, preparer_to_node, config_vars): | ||
for preparer, node in preparer_to_node.items(): | ||
self.config_applier.apply_configs(host, node, preparer.get_config_paths(), config_vars) | ||
|
||
def _invoke_install_hooks(self, host, config_vars): | ||
_, java_home = self.java_home_resolver.resolve_java_home(host, self.provision_config_instance) | ||
|
||
env = {} | ||
if java_home: | ||
env["JAVA_HOME"] = java_home | ||
|
||
config_vars_copy = config_vars.copy() | ||
for preparer in self.preparers: | ||
preparer.invoke_install_hook(host, BootstrapPhase.post_install, config_vars_copy, env) | ||
|
||
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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
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. |
||
|
||
return nodes_list[0] | ||
|
||
def cleanup(self, host): | ||
for preparer in self.preparers: | ||
preparer.cleanup(host) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import logging | ||
import os | ||
|
||
from osbenchmark.builder.installers.preparers.preparer import Preparer | ||
|
||
|
||
class PluginPreparer(Preparer): | ||
def __init__(self, plugin, executor, hook_handler_class): | ||
super().__init__(executor) | ||
self.logger = logging.getLogger(__name__) | ||
self.plugin = plugin | ||
self.hook_handler = hook_handler_class(self.plugin) | ||
if self.hook_handler.can_load(): | ||
self.hook_handler.load() | ||
|
||
def prepare(self, host, binaries): | ||
install_cmd = self._get_install_command(host, binaries) | ||
self.executor.execute(host, install_cmd) | ||
|
||
def _get_install_command(self, host, binaries): | ||
installer_binary_path = os.path.join(host.node.binary_path, "bin", "opensearch-plugin") | ||
plugin_binary_path = binaries.get(self.plugin.name) | ||
|
||
if plugin_binary_path: | ||
self.logger.info("Installing [%s] into [%s] from [%s]", self.plugin.name, host.node.binary_path, plugin_binary_path) | ||
return '%s install --batch "%s"' % (installer_binary_path, plugin_binary_path) | ||
else: | ||
self.logger.info("Installing [%s] into [%s]", self.plugin.name, host.node.binary_path) | ||
return '%s install --batch "%s"' % (installer_binary_path, self.plugin.name) | ||
|
||
def get_config_vars(self, host, node, all_node_ips): | ||
return self.plugin.variables | ||
|
||
def get_plugin_name(self): | ||
return self.plugin.name | ||
|
||
def get_config_paths(self): | ||
return self.plugin.config_paths | ||
|
||
def invoke_install_hook(self, host, phase, variables, env): | ||
self.hook_handler.invoke(phase.name, variables=variables, env=env) | ||
|
||
def cleanup(self, host): | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from abc import ABC, abstractmethod | ||
|
||
|
||
class Preparer(ABC): | ||
""" | ||
A preparer is used for preparing the installation of a node by setting up the filesystem, binaries, and install hooks | ||
""" | ||
|
||
def __init__(self, executor): | ||
self.executor = executor | ||
|
||
@abstractmethod | ||
def prepare(self, host, binaries): | ||
""" | ||
Prepares the filesystem and binaries on a node | ||
|
||
;param host: A Host object defining the host on which to prepare the data | ||
;param binaries: A map of components to download paths on the host | ||
;return node: A Node object detailing the installation data of the node on the host. May be None if no Node was generated | ||
""" | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def get_config_vars(self, host, node, all_node_ips): | ||
""" | ||
Gets the config file(s) variables associated with the given preparer | ||
|
||
;param host: A Host object defining a machine within a cluster | ||
;param node: A Node object defining the node on a host | ||
;param all_node_ips: A list of the ips for each node in the cluster. Used for cluster formation | ||
;return dict: A key value pair of the config variables | ||
""" | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def get_config_paths(self): | ||
""" | ||
Returns the config paths list | ||
""" | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def invoke_install_hook(self, host, phase, variables, env): | ||
""" | ||
Invokes the associated install hook | ||
|
||
;param host: A Host object defining the host on which to invoke the install hook | ||
;param phase: The BoostrapPhase of install hook | ||
;param variables: Key value pairs to be passed to the install hook | ||
;param env: Key value pairs of environment variables to be passed ot the install hook | ||
;return None | ||
""" | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
def cleanup(self, host): | ||
""" | ||
Removes the data that was downloaded, installed, and created on a given host during the test execution | ||
|
||
;param host: A Host object defining the host on which to remove the data | ||
;return None | ||
""" | ||
raise NotImplementedError |
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