adding documentation to config service base class

This commit is contained in:
Blake Harnden 2020-01-24 22:15:21 -08:00
parent 22ff48182a
commit 80073dd8c8

View file

@ -28,6 +28,10 @@ class ConfigServiceBootError(Exception):
class ConfigService(abc.ABC): class ConfigService(abc.ABC):
"""
Base class for creating configurable services.
"""
# validation period in seconds, how frequent validation is attempted # validation period in seconds, how frequent validation is attempted
validation_period = 0.5 validation_period = 0.5
@ -35,6 +39,11 @@ class ConfigService(abc.ABC):
validation_timer = 5 validation_timer = 5
def __init__(self, node: CoreNode) -> None: def __init__(self, node: CoreNode) -> None:
"""
Create ConfigService instance.
:param node: node this service is assigned to
"""
self.node = node self.node = node
class_file = inspect.getfile(self.__class__) class_file = inspect.getfile(self.__class__)
templates_path = pathlib.Path(class_file).parent.joinpath(TEMPLATES_DIR) templates_path = pathlib.Path(class_file).parent.joinpath(TEMPLATES_DIR)
@ -47,6 +56,13 @@ class ConfigService(abc.ABC):
@staticmethod @staticmethod
def clean_text(text: str) -> str: def clean_text(text: str) -> str:
"""
Returns space stripped text for string literals, while keeping space
indentations.
:param text: text to clean
:return: cleaned text
"""
return inspect.cleandoc(text) return inspect.cleandoc(text)
@property @property
@ -110,6 +126,13 @@ class ConfigService(abc.ABC):
raise NotImplementedError raise NotImplementedError
def start(self) -> None: def start(self) -> None:
"""
Creates services files/directories, runs startup, and validates based on
validation mode.
:return: nothing
:raises ConfigServiceBootError: when there is an error starting service
"""
logging.info("node(%s) service(%s) starting...", self.node.name, self.name) logging.info("node(%s) service(%s) starting...", self.node.name, self.name)
self.create_dirs() self.create_dirs()
self.create_files() self.create_files()
@ -122,6 +145,11 @@ class ConfigService(abc.ABC):
self.run_validation() self.run_validation()
def stop(self) -> None: def stop(self) -> None:
"""
Stop service using shutdown commands.
:return: nothing
"""
for cmd in self.shutdown: for cmd in self.shutdown:
try: try:
self.node.cmd(cmd) self.node.cmd(cmd)
@ -132,10 +160,21 @@ class ConfigService(abc.ABC):
) )
def restart(self) -> None: def restart(self) -> None:
"""
Restarts service by running stop and then start.
:return: nothing
"""
self.stop() self.stop()
self.start() self.start()
def create_dirs(self) -> None: def create_dirs(self) -> None:
"""
Creates directories for service.
:return: nothing
:raises CoreError: when there is a failure creating a directory
"""
for directory in self.directories: for directory in self.directories:
try: try:
self.node.privatedir(directory) self.node.privatedir(directory)
@ -146,15 +185,39 @@ class ConfigService(abc.ABC):
) )
def data(self) -> Dict[str, Any]: def data(self) -> Dict[str, Any]:
"""
Returns key/value data, used when rendering file templates.
:return: key/value template data
"""
return {} return {}
def set_template(self, name: str, template: str) -> None: def set_template(self, name: str, template: str) -> None:
"""
Store custom template to render for a given file.
:param name: file to store custom template for
:param template: custom template to render
:return: nothing
"""
self.custom_templates[name] = template self.custom_templates[name] = template
def get_text_template(self, name: str) -> str: def get_text_template(self, name: str) -> str:
"""
Retrieves text based template for files that do not have a file based template.
:param name: name of file to get template for
:return: template to render
"""
raise CoreError(f"service({self.name}) unknown template({name})") raise CoreError(f"service({self.name}) unknown template({name})")
def get_templates(self) -> Dict[str, str]: def get_templates(self) -> Dict[str, str]:
"""
Retrieves mapping of file names to templates for all cases, which
includes custom templates, file templates, and text templates.
:return: mapping of files to templates
"""
templates = {} templates = {}
for name in self.files: for name in self.files:
basename = pathlib.Path(name).name basename = pathlib.Path(name).name
@ -170,6 +233,11 @@ class ConfigService(abc.ABC):
return templates return templates
def create_files(self) -> None: def create_files(self) -> None:
"""
Creates service files inside associated node.
:return: nothing
"""
data = self.data() data = self.data()
for name in self.files: for name in self.files:
basename = pathlib.Path(name).name basename = pathlib.Path(name).name
@ -191,6 +259,14 @@ class ConfigService(abc.ABC):
self.node.nodefile(name, rendered) self.node.nodefile(name, rendered)
def run_startup(self, wait: bool) -> None: def run_startup(self, wait: bool) -> None:
"""
Run startup commands for service on node.
:param wait: wait successful command exit status when True, ignore status
otherwise
:return: nothing
:raises ConfigServiceBootError: when a command that waits fails
"""
for cmd in self.startup: for cmd in self.startup:
try: try:
self.node.cmd(cmd, wait=wait) self.node.cmd(cmd, wait=wait)
@ -200,6 +276,12 @@ class ConfigService(abc.ABC):
) )
def run_validation(self) -> None: def run_validation(self) -> None:
"""
Runs validation commands for service on node.
:return: nothing
:raises ConfigServiceBootError: if there is a validation failure
"""
start = time.monotonic() start = time.monotonic()
cmds = self.validate[:] cmds = self.validate[:]
index = 0 index = 0
@ -222,6 +304,13 @@ class ConfigService(abc.ABC):
) )
def _render(self, template: Template, data: Dict[str, Any] = None) -> str: def _render(self, template: Template, data: Dict[str, Any] = None) -> str:
"""
Renders template providing all associated data to template.
:param template: template to render
:param data: service specific defined data for template
:return: rendered template
"""
if data is None: if data is None:
data = {} data = {}
return template.render_unicode( return template.render_unicode(
@ -229,6 +318,13 @@ class ConfigService(abc.ABC):
) )
def render_text(self, text: str, data: Dict[str, Any] = None) -> str: def render_text(self, text: str, data: Dict[str, Any] = None) -> str:
"""
Renders text based template providing all associated data to template.
:param text: text to render
:param data: service specific defined data for template
:return: rendered template
"""
text = self.clean_text(text) text = self.clean_text(text)
try: try:
template = Template(text) template = Template(text)
@ -240,6 +336,13 @@ class ConfigService(abc.ABC):
) )
def render_template(self, basename: str, data: Dict[str, Any] = None) -> str: def render_template(self, basename: str, data: Dict[str, Any] = None) -> str:
"""
Renders file based template providing all associated data to template.
:param basename: base name for file to render
:param data: service specific defined data for template
:return: rendered template
"""
try: try:
template = self.templates.get_template(basename) template = self.templates.get_template(basename)
return self._render(template, data) return self._render(template, data)
@ -250,16 +353,34 @@ class ConfigService(abc.ABC):
) )
def _define_config(self, configs: List[Configuration]) -> None: def _define_config(self, configs: List[Configuration]) -> None:
"""
Initializes default configuration data.
:param configs: configs to initialize
:return: nothing
"""
for config in configs: for config in configs:
self.config[config.id] = config self.config[config.id] = config
def render_config(self) -> Dict[str, str]: def render_config(self) -> Dict[str, str]:
"""
Returns configuration data key/value pairs for rendering a template.
:return: nothing
"""
if self.custom_config: if self.custom_config:
return self.custom_config return self.custom_config
else: else:
return {k: v.default for k, v in self.config.items()} return {k: v.default for k, v in self.config.items()}
def set_config(self, data: Dict[str, str]) -> None: def set_config(self, data: Dict[str, str]) -> None:
"""
Set configuration data from key/value pairs.
:param data: configuration key/values to set
:return: nothing
:raise CoreError: when an unknown configuration value is given
"""
for key, value in data.items(): for key, value in data.items():
if key not in self.config: if key not in self.config:
raise CoreError(f"unknown config: {key}") raise CoreError(f"unknown config: {key}")