parent
85f7d8f792
commit
7a64eeff09
65 changed files with 991 additions and 4285 deletions
@ -1 +0,0 @@ |
|||||||
6.8.33 |
|
@ -0,0 +1 @@ |
|||||||
|
7.0.0-a |
@ -1 +1 @@ |
|||||||
6.8.33 |
7.0.0 |
@ -1,84 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
import logging |
|
||||||
from importlib import import_module |
|
||||||
from .constants import LOGGER_NAME |
|
||||||
from .library.commands import ItemizedCommand |
|
||||||
|
|
||||||
log = logging.getLogger(LOGGER_NAME) |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"Factory", |
|
||||||
) |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Factory(object): |
|
||||||
"""A command factory.""" |
|
||||||
|
|
||||||
def __init__(self, overlay): |
|
||||||
"""Initialize the factory. |
|
||||||
|
|
||||||
:param overlay: The name of the overlay to use for generating commands. |
|
||||||
:type overlay: str |
|
||||||
|
|
||||||
""" |
|
||||||
self.is_loaded = False |
|
||||||
self.overlay = None |
|
||||||
self._overlay = overlay |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
return "<%s %s>" % (self.__class__.__name__, self._overlay) |
|
||||||
|
|
||||||
def get_command(self, name, *args, **kwargs): |
|
||||||
"""Get a command. |
|
||||||
|
|
||||||
:param name: The name of the command. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
args and kwargs are passed to the initialize the command. |
|
||||||
|
|
||||||
:rtype: scripttease.library.commands.Command | scripttease.library.commands.ItemizedCommand |
|
||||||
|
|
||||||
:raise: RuntimeError |
|
||||||
:raises: ``RuntimeError`` if the factory has not yet been loaded. |
|
||||||
|
|
||||||
""" |
|
||||||
if not self.is_loaded: |
|
||||||
raise RuntimeError("Factory has not been loaded, so no commands are available. Call load() method first!") |
|
||||||
|
|
||||||
if not self.overlay.command_exists(name): |
|
||||||
log.warning("Command does not exist in %s overlay: %s" % (self._overlay, name)) |
|
||||||
return None |
|
||||||
|
|
||||||
callback = self.overlay.MAPPINGS[name] |
|
||||||
|
|
||||||
try: |
|
||||||
items = kwargs.pop("items", None) |
|
||||||
if items is not None: |
|
||||||
return ItemizedCommand(callback, items, *args, name=name, **kwargs) |
|
||||||
|
|
||||||
command = callback(*args, **kwargs) |
|
||||||
command.name = name |
|
||||||
return command |
|
||||||
except (KeyError, NameError, TypeError, ValueError) as e: |
|
||||||
log.critical("Failed to load %s command: %s" % (name, e)) |
|
||||||
return None |
|
||||||
|
|
||||||
def load(self): |
|
||||||
"""Load the factory. |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
try: |
|
||||||
self.overlay = import_module("scripttease.library.overlays.%s" % self._overlay) |
|
||||||
self.is_loaded = True |
|
||||||
except ImportError as e: |
|
||||||
log.error("The %s overlay could not be imported: %s" % (self._overlay, str(e))) |
|
||||||
pass |
|
||||||
|
|
||||||
return self.is_loaded |
|
@ -1 +0,0 @@ |
|||||||
from .commands.posix import POSIX_MAPPINGS |
|
@ -1,3 +0,0 @@ |
|||||||
from .base import Command, ItemizedCommand |
|
||||||
from .templates import Template |
|
||||||
# from .factory import command_factory |
|
@ -1,309 +0,0 @@ |
|||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Command(object): |
|
||||||
"""A command line statement.""" |
|
||||||
|
|
||||||
def __init__(self, statement, comment=None, condition=None, cd=None, environments=None, function=None, name=None, |
|
||||||
prefix=None, register=None, shell=None, stop=False, sudo=None, tags=None, **kwargs): |
|
||||||
"""Initialize a command. |
|
||||||
|
|
||||||
:param statement: The statement to be executed. |
|
||||||
:type statement: str |
|
||||||
|
|
||||||
:param comment: A comment regarding the statement. |
|
||||||
:type comment: str |
|
||||||
|
|
||||||
:param condition: A (system-specific) condition for the statement to be executed. |
|
||||||
:type condition: str |
|
||||||
|
|
||||||
:param cd: The direction from which the statement should be executed. |
|
||||||
:type cd: str |
|
||||||
|
|
||||||
:param environments: A list of target environments where the statement should be executed. |
|
||||||
:type environments: list[str] |
|
||||||
|
|
||||||
:param function: The name of the function in which the statement is executed. |
|
||||||
:type function: str |
|
||||||
|
|
||||||
:param name: The name of the command from the mapping. Not used and not required for programmatic use, but |
|
||||||
automatically assigned during factory instantiation. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param prefix: A statement to execute before the main statement is executed. |
|
||||||
:type prefix: str |
|
||||||
|
|
||||||
:param register: A variable name to use for capture the success for failure of the statement's execution. |
|
||||||
:type register: str |
|
||||||
|
|
||||||
:param shell: The shell execute through which the statement is executed. |
|
||||||
:type shell: str |
|
||||||
|
|
||||||
:param stop: Indicates process should stop if the statement fails to execute. |
|
||||||
:type stop: bool | None |
|
||||||
|
|
||||||
:param sudo: Indicates whether sudo should be invoked for the statement. Given as a bool or user name or |
|
||||||
:py:class:`scripttease.library.commands.base.Sudo` instance. |
|
||||||
:type sudo: bool | str | Sudo |
|
||||||
|
|
||||||
:param tags: A list of tags describing the statement. |
|
||||||
:type tags: list[str] |
|
||||||
|
|
||||||
Additional kwargs are available as dynamic attributes of the Command instance. |
|
||||||
|
|
||||||
""" |
|
||||||
self.comment = comment |
|
||||||
self.condition = condition |
|
||||||
self.cd = cd |
|
||||||
self.environments = environments or list() |
|
||||||
self.function = function |
|
||||||
self.name = name |
|
||||||
self.prefix = prefix |
|
||||||
self.register = register |
|
||||||
self.shell = shell |
|
||||||
self.statement = statement |
|
||||||
self.stop = stop |
|
||||||
self.tags = tags or list() |
|
||||||
|
|
||||||
if isinstance(sudo, Sudo): |
|
||||||
self.sudo = sudo |
|
||||||
elif type(sudo) is str: |
|
||||||
self.sudo = Sudo(enabled=True, user=sudo) |
|
||||||
elif sudo is True: |
|
||||||
self.sudo = Sudo(enabled=True) |
|
||||||
else: |
|
||||||
self.sudo = Sudo() |
|
||||||
|
|
||||||
self._attributes = kwargs |
|
||||||
|
|
||||||
def __getattr__(self, item): |
|
||||||
return self._attributes.get(item) |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
if self.comment is not None: |
|
||||||
return "<%s %s>" % (self.__class__.__name__, self.comment) |
|
||||||
|
|
||||||
return "<%s>" % self.__class__.__name__ |
|
||||||
|
|
||||||
def get_statement(self, cd=False, suppress_comment=False): |
|
||||||
"""Get the full statement. |
|
||||||
|
|
||||||
:param cd: Include the directory change, if given. |
|
||||||
:type cd: bool |
|
||||||
|
|
||||||
:param suppress_comment: Don't include the comment. |
|
||||||
:type suppress_comment: bool |
|
||||||
|
|
||||||
:rtype: str |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
if cd and self.cd is not None: |
|
||||||
a.append("( cd %s &&" % self.cd) |
|
||||||
|
|
||||||
if self.prefix is not None: |
|
||||||
a.append("%s &&" % self.prefix) |
|
||||||
|
|
||||||
if self.sudo: |
|
||||||
statement = "%s %s" % (self.sudo, self._get_statement()) |
|
||||||
else: |
|
||||||
statement = self._get_statement() |
|
||||||
|
|
||||||
a.append("%s" % statement) |
|
||||||
|
|
||||||
if cd and self.cd is not None: |
|
||||||
a.append(")") |
|
||||||
|
|
||||||
b = list() |
|
||||||
if self.comment is not None and not suppress_comment: |
|
||||||
b.append("# %s" % self.comment) |
|
||||||
|
|
||||||
if self.condition is not None: |
|
||||||
b.append("if [[ %s ]]; then %s; fi;" % (self.condition, " ".join(a))) |
|
||||||
else: |
|
||||||
b.append(" ".join(a)) |
|
||||||
|
|
||||||
if self.register is not None: |
|
||||||
b.append("%s=$?;" % self.register) |
|
||||||
|
|
||||||
if self.stop: |
|
||||||
b.append("if [[ $%s -gt 0 ]]; exit 1; fi;" % self.register) |
|
||||||
elif self.stop: |
|
||||||
b.append("if [[ $? -gt 0 ]]; exit 1; fi;") |
|
||||||
else: |
|
||||||
pass |
|
||||||
|
|
||||||
return "\n".join(b) |
|
||||||
|
|
||||||
def has_attribute(self, name): |
|
||||||
"""Indicates whether the command has the named, dynamic attribute. |
|
||||||
|
|
||||||
:param name: The name of the attribute to be checked. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
return name in self._attributes |
|
||||||
|
|
||||||
@property |
|
||||||
def is_itemized(self): |
|
||||||
"""Always returns ``False``.""" |
|
||||||
return False |
|
||||||
|
|
||||||
def set_attribute(self, name, value): |
|
||||||
"""Set the value of a dynamic attribute. |
|
||||||
|
|
||||||
:param name: The name of the attribute. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param value: The value of the attribute. |
|
||||||
|
|
||||||
""" |
|
||||||
self._attributes[name] = value |
|
||||||
|
|
||||||
def _get_statement(self): |
|
||||||
"""By default, get the statement passed upon command initialization. |
|
||||||
|
|
||||||
:rtype: str |
|
||||||
|
|
||||||
""" |
|
||||||
return self.statement |
|
||||||
|
|
||||||
|
|
||||||
class ItemizedCommand(object): |
|
||||||
"""An itemized command represents multiple commands of with the same statement but different parameters.""" |
|
||||||
|
|
||||||
def __init__(self, callback, items, *args, name=None, **kwargs): |
|
||||||
"""Initialize the command. |
|
||||||
|
|
||||||
:param callback: The function to be used to generate the command. |
|
||||||
|
|
||||||
:param items: The command arguments. |
|
||||||
:type items: list[str] |
|
||||||
|
|
||||||
:param name: The name of the command from the mapping. Not used and not required for programmatic use, but |
|
||||||
automatically assigned during factory instantiation. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param args: The itemized arguments. ``$item`` should be included. |
|
||||||
|
|
||||||
Keyword arguments are passed to the command class upon instantiation. |
|
||||||
|
|
||||||
""" |
|
||||||
self.args = args |
|
||||||
self.callback = callback |
|
||||||
self.items = items |
|
||||||
self.kwargs = kwargs |
|
||||||
self.name = name |
|
||||||
|
|
||||||
# Set defaults for when ItemizedCommand is referenced directly before individual commands are instantiated. For |
|
||||||
# example, when command filtering occurs. |
|
||||||
self.kwargs.setdefault("environments", list()) |
|
||||||
self.kwargs.setdefault("tags", list()) |
|
||||||
|
|
||||||
def __getattr__(self, item): |
|
||||||
return self.kwargs.get(item) |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
return "<%s %s>" % (self.__class__.__name__, self.callback.__name__) |
|
||||||
|
|
||||||
def get_commands(self): |
|
||||||
"""Get the commands to be executed. |
|
||||||
|
|
||||||
:rtype: list[BaseType(Command)] |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs = self.kwargs.copy() |
|
||||||
|
|
||||||
a = list() |
|
||||||
for item in self.items: |
|
||||||
args = list() |
|
||||||
for arg in self.args: |
|
||||||
args.append(arg.replace("$item", item)) |
|
||||||
|
|
||||||
command = self.callback(*args, **kwargs) |
|
||||||
a.append(command) |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
def get_statement(self, cd=False, suppress_comment=False): |
|
||||||
"""Override to get multiple commands.""" |
|
||||||
kwargs = self.kwargs.copy() |
|
||||||
comment = kwargs.pop("comment", "execute multiple commands") |
|
||||||
|
|
||||||
a = list() |
|
||||||
# a.append("# %s" % comment) |
|
||||||
|
|
||||||
commands = self.get_commands() |
|
||||||
for c in commands: |
|
||||||
a.append(c.get_statement(cd=cd, suppress_comment=suppress_comment)) |
|
||||||
a.append("") |
|
||||||
|
|
||||||
# for item in self.items: |
|
||||||
# args = list() |
|
||||||
# for arg in self.args: |
|
||||||
# args.append(arg.replace("$item", item)) |
|
||||||
# |
|
||||||
# command = self.command_class(*args, **kwargs) |
|
||||||
# a.append(command.preview(cwd=cwd)) |
|
||||||
# a.append("") |
|
||||||
|
|
||||||
return "\n".join(a) |
|
||||||
|
|
||||||
def has_attribute(self, name): |
|
||||||
"""Indicates whether the command has the named, dynamic attribute. |
|
||||||
|
|
||||||
:param name: The name of the attribute to be checked. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
return name in self.kwargs |
|
||||||
|
|
||||||
@property |
|
||||||
def is_itemized(self): |
|
||||||
"""Always returns ``True``.""" |
|
||||||
return True |
|
||||||
|
|
||||||
def set_attribute(self, name, value): |
|
||||||
"""Set the value of a dynamic attribute. |
|
||||||
|
|
||||||
:param name: The name of the attribute. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param value: The value of the attribute. |
|
||||||
|
|
||||||
.. note:: |
|
||||||
This is applied to all command in the itemized list. |
|
||||||
|
|
||||||
""" |
|
||||||
self.kwargs[name] = value |
|
||||||
|
|
||||||
|
|
||||||
class Sudo(object): |
|
||||||
"""Helper class for defining sudo options.""" |
|
||||||
|
|
||||||
def __init__(self, enabled=False, user="root"): |
|
||||||
"""Initialize the helper. |
|
||||||
|
|
||||||
:param enabled: Indicates sudo is enabled. |
|
||||||
:type enabled: bool |
|
||||||
|
|
||||||
:param user: The user to be invoked. |
|
||||||
:type user: str |
|
||||||
|
|
||||||
""" |
|
||||||
self.enabled = enabled |
|
||||||
self.user = user |
|
||||||
|
|
||||||
def __bool__(self): |
|
||||||
return self.enabled |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
if self.enabled: |
|
||||||
return "sudo -u %s" % self.user |
|
||||||
|
|
||||||
return "" |
|
@ -1,197 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import parse_jinja_template, read_file |
|
||||||
from jinja2.exceptions import TemplateError, TemplateNotFound |
|
||||||
import logging |
|
||||||
import os |
|
||||||
from ...constants import LOGGER_NAME |
|
||||||
from .base import Command |
|
||||||
|
|
||||||
log = logging.getLogger(LOGGER_NAME) |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"Template", |
|
||||||
) |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Template(Command): |
|
||||||
"""Parse a template.""" |
|
||||||
|
|
||||||
PARSER_JINJA = "jinja2" |
|
||||||
PARSER_SIMPLE = "simple" |
|
||||||
|
|
||||||
def __init__(self, source, target, backup=True, lines=False, parser=PARSER_JINJA, pythonic=False, **kwargs): |
|
||||||
"""Initialize the command. |
|
||||||
|
|
||||||
:param source: The template source file. |
|
||||||
:type source: str |
|
||||||
|
|
||||||
:param target: The path to the output file. |
|
||||||
:type target: str |
|
||||||
|
|
||||||
:param backup: Indicates a copy of an existing file should be madee. |
|
||||||
:type backup: bool |
|
||||||
|
|
||||||
:param parser: The parser to use. |
|
||||||
:type parser: str |
|
||||||
|
|
||||||
:param pythonic: Use a Python one-liner to write the file. Requires Python installation, obviously. This is |
|
||||||
useful when the content of the file cannot be handled with a cat command; for example, shell |
|
||||||
script templates. |
|
||||||
:type pythonic: bool |
|
||||||
|
|
||||||
""" |
|
||||||
# Base parameters need to be captured, because all others are assumed to be switches for the management command. |
|
||||||
self._kwargs = { |
|
||||||
'comment': kwargs.pop("comment", None), |
|
||||||
'cd': kwargs.pop("cd", None), |
|
||||||
'environments': kwargs.pop("environments", None), |
|
||||||
'function': kwargs.pop("function", None), |
|
||||||
# 'local': kwargs.pop("local", False), |
|
||||||
'name': "template", |
|
||||||
'prefix': kwargs.pop("prefix", None), |
|
||||||
'shell': kwargs.pop("shell", "/bin/bash"), |
|
||||||
'stop': kwargs.pop("stop", False), |
|
||||||
'sudo': kwargs.pop('sudo', False), |
|
||||||
'tags': kwargs.pop("tags", None), |
|
||||||
} |
|
||||||
|
|
||||||
self.backup_enabled = backup |
|
||||||
self.context = kwargs.pop("context", dict()) |
|
||||||
self.parser = parser or self.PARSER_JINJA |
|
||||||
self.pythonic = pythonic |
|
||||||
self.line_by_line = lines |
|
||||||
self.locations = kwargs.pop("locations", list()) |
|
||||||
self.source = os.path.expanduser(source) |
|
||||||
self.target = target |
|
||||||
|
|
||||||
# Remaining kwargs are added to the context. |
|
||||||
# print(_kwargs['comment'], kwargs) |
|
||||||
self.context.update(kwargs) |
|
||||||
|
|
||||||
super().__init__("# template: %s" % source, **self._kwargs) |
|
||||||
|
|
||||||
def get_content(self): |
|
||||||
"""Parse the template. |
|
||||||
|
|
||||||
:rtype: str | None |
|
||||||
|
|
||||||
""" |
|
||||||
template = self.get_template() |
|
||||||
|
|
||||||
if self.parser == self.PARSER_SIMPLE: |
|
||||||
content = read_file(template) |
|
||||||
for key, value in self.context.items(): |
|
||||||
replace = "$%s$" % key |
|
||||||
content = content.replace(replace, str(value)) |
|
||||||
|
|
||||||
return content |
|
||||||
|
|
||||||
try: |
|
||||||
return parse_jinja_template(template, self.context) |
|
||||||
except TemplateNotFound: |
|
||||||
log.error("Template not found: %s" % template) |
|
||||||
return None |
|
||||||
except TemplateError as e: |
|
||||||
log.error("Could not parse %s template: %s" % (template, e)) |
|
||||||
return None |
|
||||||
|
|
||||||
def get_statement(self, cd=False, suppress_comment=False): |
|
||||||
"""Override to get the statement based on the parser.""" |
|
||||||
if self.parser == self.PARSER_JINJA: |
|
||||||
return self._get_jinja2_statement(cd=cd).statement |
|
||||||
elif self.parser == self.PARSER_SIMPLE: |
|
||||||
return self._get_simple_statement(cd=cd).statement |
|
||||||
else: |
|
||||||
log.error("Unknown or unsupported template parser: %s" % self.parser) |
|
||||||
return None |
|
||||||
|
|
||||||
def get_template(self): |
|
||||||
"""Get the template path. |
|
||||||
|
|
||||||
:rtype: str |
|
||||||
|
|
||||||
""" |
|
||||||
source = self.source |
|
||||||
for location in self.locations: |
|
||||||
_source = os.path.join(location, self.source) |
|
||||||
if os.path.exists(_source): |
|
||||||
return _source |
|
||||||
|
|
||||||
return source |
|
||||||
|
|
||||||
def _get_command(self, content): |
|
||||||
"""Get the cat command.""" |
|
||||||
output = list() |
|
||||||
|
|
||||||
# TODO: Template backup is not system safe, but is specific to bash. |
|
||||||
if self.backup_enabled: |
|
||||||
output.append('if [[ -f "%s" ]]; then mv %s %s.b; fi;' % (self.target, self.target, self.target)) |
|
||||||
|
|
||||||
if content.startswith("#!"): |
|
||||||
_content = content.split("\n") |
|
||||||
first_line = _content.pop(0) |
|
||||||
output.append('echo "%s" > %s' % (first_line, self.target)) |
|
||||||
output.append("cat >> %s << EOF" % self.target) |
|
||||||
output.append("\n".join(_content)) |
|
||||||
output.append("EOF") |
|
||||||
else: |
|
||||||
output.append("cat > %s << EOF" % self.target) |
|
||||||
output.append(content) |
|
||||||
output.append("EOF") |
|
||||||
|
|
||||||
statement = "\n".join(output) |
|
||||||
|
|
||||||
return Command(statement, **self._kwargs) |
|
||||||
|
|
||||||
# # BUG: This still does not seem to work, possibly because a shell script includes EOF? The work around is to use |
|
||||||
# # get_content(), self.target, and write the file manually. |
|
||||||
# if self.line_by_line: |
|
||||||
# a = list() |
|
||||||
# a.append('touch %s' % self.target) |
|
||||||
# for i in content.split("\n"): |
|
||||||
# i = i.replace('"', r'\"') |
|
||||||
# a.append('echo "%s" >> %s' % (i, self.target)) |
|
||||||
# |
|
||||||
# output.append("\n".join(a)) |
|
||||||
# elif self.pythonic: |
|
||||||
# target_file = File(self.target) |
|
||||||
# script_file = "write_%s_template.py" % target_file.name.replace("-", "_") |
|
||||||
# |
|
||||||
# a = list() |
|
||||||
# a.append('content = """%s' % content) |
|
||||||
# a.append('"""') |
|
||||||
# a.append("") |
|
||||||
# a.append('with open("%s", "w") as f:' % self.target) |
|
||||||
# a.append(' f.write(content)') |
|
||||||
# a.append(' f.close()') |
|
||||||
# a.append('') |
|
||||||
# output.append('cat > %s <<EOF' % script_file) |
|
||||||
# output.append("\n".join(a)) |
|
||||||
# output.append('EOF') |
|
||||||
# output.append("") |
|
||||||
# output.append("rm %s" % script_file) |
|
||||||
# else: |
|
||||||
# output.append("cat > %s << EOF" % self.target) |
|
||||||
# output.append(content) |
|
||||||
# output.append("EOF") |
|
||||||
# |
|
||||||
# statement = "\n".join(output) |
|
||||||
# return Command(statement, **self._kwargs) |
|
||||||
|
|
||||||
# noinspection PyUnusedLocal |
|
||||||
def _get_jinja2_statement(self, cd=False): |
|
||||||
"""Parse a Jinja2 template.""" |
|
||||||
content = self.get_content() |
|
||||||
return self._get_command(content) |
|
||||||
|
|
||||||
# noinspection PyUnusedLocal |
|
||||||
def _get_simple_statement(self, cd=False): |
|
||||||
"""Parse a "simple" template.""" |
|
||||||
content = self.get_content() |
|
||||||
|
|
||||||
return self._get_command(content) |
|
@ -1,281 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import split_csv |
|
||||||
from ..commands import Command, Template |
|
||||||
from .common import COMMON_MAPPINGS |
|
||||||
from .django import DJANGO_MAPPINGS |
|
||||||
from .mysql import MYSQL_MAPPINGS |
|
||||||
from .pgsql import PGSQL_MAPPINGS |
|
||||||
from .posix import POSIX_MAPPINGS, Function |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"MAPPINGS", |
|
||||||
"apache", |
|
||||||
"apache_reload", |
|
||||||
"apache_restart", |
|
||||||
"apache_start", |
|
||||||
"apache_stop", |
|
||||||
"apache_test", |
|
||||||
"command_exists", |
|
||||||
"service_reload", |
|
||||||
"service_restart", |
|
||||||
"service_start", |
|
||||||
"service_stop", |
|
||||||
"system", |
|
||||||
"system_install", |
|
||||||
"system_reboot", |
|
||||||
"system_update", |
|
||||||
"system_upgrade", |
|
||||||
"system_uninstall", |
|
||||||
"template", |
|
||||||
"user", |
|
||||||
"Function", |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
def command_exists(name): |
|
||||||
"""Indicates whether a given command exists in this overlay. |
|
||||||
|
|
||||||
:param name: The name of the command. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
return name in MAPPINGS |
|
||||||
|
|
||||||
|
|
||||||
def apache(op, **kwargs): |
|
||||||
"""Execute an Apache-related command. |
|
||||||
|
|
||||||
- op (str): The operation to perform; reload, restart, start, stop, test. |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "reload": |
|
||||||
return apache_reload(**kwargs) |
|
||||||
elif op == "restart": |
|
||||||
return apache_restart(**kwargs) |
|
||||||
elif op == "start": |
|
||||||
return apache_start(**kwargs) |
|
||||||
elif op == "stop": |
|
||||||
return apache_stop(**kwargs) |
|
||||||
elif op == "test": |
|
||||||
return apache_test(**kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported apache operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
def apache_reload(**kwargs): |
|
||||||
kwargs.setdefault("comment", "reload apache") |
|
||||||
kwargs.setdefault("register", "apache_reloaded") |
|
||||||
|
|
||||||
return Command("apachectl –k reload", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_restart(**kwargs): |
|
||||||
kwargs.setdefault("comment", "restart apache") |
|
||||||
kwargs.setdefault("register", "apache_restarted") |
|
||||||
|
|
||||||
return Command("apachectl –k restart", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_start(**kwargs): |
|
||||||
kwargs.setdefault("comment", "start apache") |
|
||||||
kwargs.setdefault("register", "apache_started") |
|
||||||
|
|
||||||
return Command("apachectl –k start", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_stop(**kwargs): |
|
||||||
kwargs.setdefault("comment", "stop apache") |
|
||||||
|
|
||||||
return Command("apachectl –k stop", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_test(**kwargs): |
|
||||||
kwargs.setdefault("comment", "check apache configuration") |
|
||||||
kwargs.setdefault("register", "apache_checks_out") |
|
||||||
|
|
||||||
return Command("apachectl configtest", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_reload(name, **kwargs): |
|
||||||
"""Reload a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "reload %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_reloaded" % name) |
|
||||||
|
|
||||||
return Command("systemctl reload %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_restart(name, **kwargs): |
|
||||||
"""Restart a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "restart %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_restarted" % name) |
|
||||||
|
|
||||||
return Command("ssystemctl restart %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_start(name, **kwargs): |
|
||||||
"""Start a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "start %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_started" % name) |
|
||||||
|
|
||||||
return Command("systemctl start %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_stop(name, **kwargs): |
|
||||||
"""Stop a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "stop %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_stopped" % name) |
|
||||||
|
|
||||||
return Command("systemctl stop %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system(op, **kwargs): |
|
||||||
"""Perform a system operation. |
|
||||||
|
|
||||||
- op (str): The operation to perform; reboot, update, upgrade. |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "reboot": |
|
||||||
return system_reboot(**kwargs) |
|
||||||
elif op == "update": |
|
||||||
return system_update(**kwargs) |
|
||||||
elif op == "upgrade": |
|
||||||
return system_upgrade(**kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported system operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
def system_install(name, **kwargs): |
|
||||||
"""Install a system-level package. |
|
||||||
|
|
||||||
- name (str): The name of the package to install. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "install system package %s" % name) |
|
||||||
|
|
||||||
return Command("yum install -y %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_reboot(**kwargs): |
|
||||||
kwargs.setdefault("comment", "reboot the system") |
|
||||||
|
|
||||||
return Command("reboot", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_uninstall(name, **kwargs): |
|
||||||
"""Uninstall a system-level package. |
|
||||||
|
|
||||||
- name (str): The name of the package to uninstall. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "remove system package %s" % name) |
|
||||||
|
|
||||||
return Command("yum remove -y %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_update(**kwargs): |
|
||||||
kwargs.setdefault("comment", "update system package info") |
|
||||||
|
|
||||||
return Command("yum check-update", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_upgrade(**kwargs): |
|
||||||
kwargs.setdefault("comment", "upgrade the system") |
|
||||||
|
|
||||||
return Command("yum update -y", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def template(source, target, backup=True, parser=None, **kwargs): |
|
||||||
"""Create a file from a template. |
|
||||||
|
|
||||||
- source (str): The path to the template file. |
|
||||||
- target (str): The path to where the new file should be created. |
|
||||||
- backup (bool): Indicates whether a backup should be made if the target file already exists. |
|
||||||
- parser (str): The parser to use ``jinja`` (the default) or ``simple``. |
|
||||||
|
|
||||||
""" |
|
||||||
return Template(source, target, backup=backup, parser=parser, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def user(name, groups=None, home=None, op="add", password=None, **kwargs): |
|
||||||
"""Create or remove a user. |
|
||||||
|
|
||||||
- name (str): The user name. |
|
||||||
- groups (str | list): A list of groups to which the user should belong. |
|
||||||
- home (str): The path to the user's home directory. |
|
||||||
- op (str); The operation to perform; ``add`` or ``remove``. |
|
||||||
- password (str): The user's password. (NOT IMPLEMENTED) |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "add": |
|
||||||
kwargs.setdefault("comment", "create a user named %s" % name) |
|
||||||
|
|
||||||
commands = list() |
|
||||||
|
|
||||||
a = list() |
|
||||||
a.append('adduser %s' % name) |
|
||||||
if home is not None: |
|
||||||
a.append("--home %s" % home) |
|
||||||
|
|
||||||
commands.append(Command(" ".join(a), **kwargs)) |
|
||||||
|
|
||||||
if type(groups) is str: |
|
||||||
groups = split_csv(groups, smart=False) |
|
||||||
|
|
||||||
if type(groups) in [list, tuple]: |
|
||||||
for group in groups: |
|
||||||
commands.append(Command("gpasswd -a %s %s" % (name, group), **kwargs)) |
|
||||||
|
|
||||||
a = list() |
|
||||||
for c in commands: |
|
||||||
a.append(c.get_statement(suppress_comment=True)) |
|
||||||
|
|
||||||
return Command("\n".join(a), **kwargs) |
|
||||||
elif op == "remove": |
|
||||||
kwargs.setdefault("comment", "remove a user named %s" % name) |
|
||||||
return Command("userdel -r %s" % name, **kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unsupported or unrecognized operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
MAPPINGS = { |
|
||||||
'apache': apache, |
|
||||||
'install': system_install, |
|
||||||
'reboot': system_reboot, |
|
||||||
'reload': service_reload, |
|
||||||
'restart': service_restart, |
|
||||||
'start': service_start, |
|
||||||
'stop': service_stop, |
|
||||||
'system': system, |
|
||||||
'template': template, |
|
||||||
'update': system_update, |
|
||||||
'uninstall': system_uninstall, |
|
||||||
'upgrade': system_upgrade, |
|
||||||
'user': user, |
|
||||||
} |
|
||||||
|
|
||||||
MAPPINGS.update(COMMON_MAPPINGS) |
|
||||||
MAPPINGS.update(DJANGO_MAPPINGS) |
|
||||||
MAPPINGS.update(MYSQL_MAPPINGS) |
|
||||||
MAPPINGS.update(PGSQL_MAPPINGS) |
|
||||||
MAPPINGS.update(POSIX_MAPPINGS) |
|
@ -1,150 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from ..commands import Command |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"COMMON_MAPPINGS", |
|
||||||
"python_pip", |
|
||||||
"python_virtualenv", |
|
||||||
"run", |
|
||||||
"slack", |
|
||||||
"twist", |
|
||||||
"udf", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def python_pip(name, op="install", upgrade=False, venv=None, version=3, **kwargs): |
|
||||||
"""Use pip to install or uninstall a Python package. |
|
||||||
|
|
||||||
- name (str): The name of the package. |
|
||||||
- op (str): The operation to perform; install, uninstall |
|
||||||
- upgrade (bool): Upgrade an installed package. |
|
||||||
- venv (str): The name of the virtual environment to load. |
|
||||||
- version (int): The Python version to use, e.g. ``2`` or ``3``. |
|
||||||
|
|
||||||
""" |
|
||||||
manager = "pip" |
|
||||||
if version == 3: |
|
||||||
manager = "pip3" |
|
||||||
|
|
||||||
if upgrade: |
|
||||||
statement = "%s install --upgrade %s" % (manager, name) |
|
||||||
else: |
|
||||||
statement = "%s %s %s" % (manager, op, name) |
|
||||||
|
|
||||||
if venv is not None: |
|
||||||
kwargs['prefix'] = "source %s/bin/activate" % venv |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "%s %s" % (op, name)) |
|
||||||
|
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def python_virtualenv(name, **kwargs): |
|
||||||
"""Create a Python virtual environment. |
|
||||||
|
|
||||||
- name (str): The name of the environment to create. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "create %s virtual environment" % name) |
|
||||||
|
|
||||||
return Command("virtualenv %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def run(statement, **kwargs): |
|
||||||
"""Run any statement. |
|
||||||
|
|
||||||
- statement (str): The statement to be executed. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "run statement") |
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def slack(message, url=None, **kwargs): |
|
||||||
"""Send a message to Slack. |
|
||||||
|
|
||||||
- message (str): The message to be sent. |
|
||||||
- url (str): The webhook URL. This is required. See documentation. |
|
||||||
|
|
||||||
""" |
|
||||||
if url is None: |
|
||||||
raise ValueError("A url is required to use the slack command.") |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "send a message to slack") |
|
||||||
|
|
||||||
a = list() |
|
||||||
|
|
||||||
a.append("curl -X POST -H 'Content-type: application/json' --data") |
|
||||||
a.append("'" + '{"text": "%s"}' % message + "'") |
|
||||||
a.append(url) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def twist(message, title="Notice", url=None, **kwargs): |
|
||||||
"""Send a message to Twist. |
|
||||||
|
|
||||||
- message (str): The message to be sent. |
|
||||||
- title (str): The message title. |
|
||||||
- url (str): The webhook URL. This is required. See documentation. |
|
||||||
|
|
||||||
""" |
|
||||||
if url is None: |
|
||||||
raise ValueError("A url is required to use the twist command.") |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "send a message to twist") |
|
||||||
|
|
||||||
a = list() |
|
||||||
|
|
||||||
# curl -X POST -H 'Content-type: application/json' --data '{"content": "This is the message.", "title": "Message Title"}' "https://twist.com/api/v3/integration_incoming/post_data?install_id=116240&install_token=116240_bfb05bde51ecd0f728b4b161bee6fcee" |
|
||||||
a.append("curl -X POST -H 'Content-type: application/json' --data") |
|
||||||
|
|
||||||
data = '{"content": "%s", "title": "%s"}' % (message, title) |
|
||||||
a.append("'%s'" % data) |
|
||||||
a.append('"%s"' % url) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def udf(name, default=None, example=None, label=None, **kwargs): |
|
||||||
"""Create a UDF prompt for a StackScript. |
|
||||||
|
|
||||||
- name (str): The name of the variable. |
|
||||||
- default: The default value. |
|
||||||
- example: An example value, instead of a default. |
|
||||||
- label (str): The label for the variable. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("prompt for %s in stackscript" % name) |
|
||||||
|
|
||||||
label = label or name.replace("_", " ").title() |
|
||||||
|
|
||||||
a = ['# <UDF name="%s" label="%s"' % (name, label)] |
|
||||||
|
|
||||||
if default is not None: |
|
||||||
a.append('default="%s"' % default) |
|
||||||
elif example is not None: |
|
||||||
a.append('example="%s"' % example) |
|
||||||
else: |
|
||||||
pass |
|
||||||
|
|
||||||
a.append("/>") |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
# Mappings |
|
||||||
|
|
||||||
COMMON_MAPPINGS = { |
|
||||||
'pip': python_pip, |
|
||||||
'run': run, |
|
||||||
'slack': slack, |
|
||||||
'twist': twist, |
|
||||||
'udf': udf, |
|
||||||
'virtualenv': python_virtualenv, |
|
||||||
} |
|
@ -1,188 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
import os |
|
||||||
from ..commands import Command |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"DJANGO_MAPPINGS", |
|
||||||
"django", |
|
||||||
"django_check", |
|
||||||
"django_collect_static", |
|
||||||
"django_dumpdata", |
|
||||||
"django_loaddata", |
|
||||||
"django_migrate", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def _django(name, *args, venv=None, **kwargs): |
|
||||||
"""Process a django-based command. |
|
||||||
|
|
||||||
:param name: The name of the management command. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param venv: The virtual environment to use. |
|
||||||
:type venv: str |
|
||||||
|
|
||||||
args and kwargs are used to instantiate the command instance. |
|
||||||
|
|
||||||
This exists because we need ``django()`` to serve as an interface for any management command. |
|
||||||
|
|
||||||
""" |
|
||||||
if venv is not None: |
|
||||||
kwargs['prefix'] = "source %s/bin/activate" % venv |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "run %s django management command" % name) |
|
||||||
|
|
||||||
# Base parameters need to be captured, because all others are assumed to be switches for the management command. |
|
||||||
_kwargs = { |
|
||||||
'comment': kwargs.pop("comment", None), |
|
||||||
'condition': kwargs.pop("condition", None), |
|
||||||
'cd': kwargs.pop("cd", None), |
|
||||||
'environments': kwargs.pop("environments", None), |
|
||||||
'function': kwargs.pop("function", None), |
|
||||||
# 'local': kwargs.pop("local", False), |
|
||||||
'prefix': kwargs.pop("prefix", None), |
|
||||||
'register': kwargs.pop("register", None), |
|
||||||
'shell': kwargs.pop("shell", "/bin/bash"), |
|
||||||
'stop': kwargs.pop("stop", False), |
|
||||||
'sudo': kwargs.pop('sudo', False), |
|
||||||
'tags': kwargs.pop("tags", None), |
|
||||||
} |
|
||||||
|
|
||||||
statement = list() |
|
||||||
statement.append("./manage.py %s" % name) |
|
||||||
|
|
||||||
# Remaining kwargs are assumed to be switches. |
|
||||||
for key, value in kwargs.items(): |
|
||||||
key = key.replace("_", "-") |
|
||||||
if type(value) is bool: |
|
||||||
if value is True: |
|
||||||
statement.append("--%s" % key) |
|
||||||
else: |
|
||||||
statement.append("--%s=%s" % (key, value)) |
|
||||||
|
|
||||||
if len(args) > 0: |
|
||||||
statement.append(" ".join(args)) |
|
||||||
|
|
||||||
return Command(" ".join(statement), **_kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def django(name, *args, venv=None, **kwargs): |
|
||||||
"""Run any Django management command. |
|
||||||
|
|
||||||
- name (str): The name of the management command. |
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
args are passed as positional arguments, while kwargs are given as switches. |
|
||||||
|
|
||||||
""" |
|
||||||
if name == "check": |
|
||||||
return django_check(venv=venv, **kwargs) |
|
||||||
elif name in ("collectstatic", "static"): |
|
||||||
return django_collect_static(venv=venv, **kwargs) |
|
||||||
elif name == "migrate": |
|
||||||
return django_migrate(venv=venv, **kwargs) |
|
||||||
else: |
|
||||||
return _django(name, *args, venv=venv, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def django_check(venv=None, **kwargs): |
|
||||||
"""Run the Django check command. |
|
||||||
|
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "run django checks") |
|
||||||
kwargs.setdefault("register", "django_checks_out") |
|
||||||
|
|
||||||
return _django("check", venv=venv, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def django_collect_static(venv=None, **kwargs): |
|
||||||
"""Collect static files. |
|
||||||
|
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "collect static files") |
|
||||||
|
|
||||||
return _django("collectstatic", venv=venv, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def django_dumpdata(app_name, base_path="local", file_name="initial", indent=4, natural_foreign=False, |
|
||||||
natural_primary=False, path=None, venv=None, **kwargs): |
|
||||||
"""Dump data from the database. |
|
||||||
|
|
||||||
- app_name (str): The name (app label) of the app. ``app_label.ModelName`` may also be given. |
|
||||||
- base_path (str): The path under which apps are located in source. |
|
||||||
- file_name (str): The file name to which the data will be dumped. |
|
||||||
- indent (int): Indentation of the exported fixtures. |
|
||||||
- natural_foreign (bool): Use the natural foreign parameter. |
|
||||||
- natural_primary (bool): Use the natural primary parameter. |
|
||||||
- path (str): The path to the data file. |
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "export fixtures for %s" % app_name) |
|
||||||
|
|
||||||
output_format = kwargs.pop("format", "json") |
|
||||||
|
|
||||||
_path = path or os.path.join(base_path, app_name, "fixtures", "%s.%s" % (file_name, output_format)) |
|
||||||
|
|
||||||
return _django( |
|
||||||
"dumpdata", |
|
||||||
app_name, |
|
||||||
"> %s" % _path, |
|
||||||
format=output_format, |
|
||||||
indent=indent, |
|
||||||
natural_foreign=natural_foreign, |
|
||||||
natural_primary=natural_primary, |
|
||||||
venv=venv, |
|
||||||
**kwargs |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
def django_loaddata(app_name, base_path="local", file_name="initial", path=None, venv=None, **kwargs): |
|
||||||
"""Load data into the database. |
|
||||||
|
|
||||||
- app_name (str): The name (app label) of the app. ``app_label.ModelName`` may also be given. |
|
||||||
- base_path (str): The path under which apps are located in source. |
|
||||||
- file_name (str): The file name to which the data will be dumped. |
|
||||||
- path (str): The path to the data file. |
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "load fixtures for %s" % app_name) |
|
||||||
|
|
||||||
output_format = kwargs.pop("format", "json") |
|
||||||
|
|
||||||
_path = path or os.path.join(base_path, app_name, "fixtures", "%s.%s" % (file_name, output_format)) |
|
||||||
|
|
||||||
return _django("loaddata", _path, venv=venv, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def django_migrate(venv=None, **kwargs): |
|
||||||
"""Apply database migrations. |
|
||||||
|
|
||||||
- venv (str): The of the virtual environment to use. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "run django database migrations") |
|
||||||
|
|
||||||
return _django("migrate", venv=venv, **kwargs) |
|
||||||
|
|
||||||
# Mapping |
|
||||||
|
|
||||||
|
|
||||||
DJANGO_MAPPINGS = { |
|
||||||
'django': django, |
|
||||||
'django.check': django_check, |
|
||||||
'django.collect_static': django_collect_static, |
|
||||||
'django.dumpdata': django_dumpdata, |
|
||||||
'django.loaddata': django_loaddata, |
|
||||||
'django.migrate': django_migrate, |
|
||||||
} |
|
@ -1,262 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from ..commands import Command |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"MYSQL_MAPPINGS", |
|
||||||
"mysql_create", |
|
||||||
"mysql_drop", |
|
||||||
"mysql_dump", |
|
||||||
"mysql_exec", |
|
||||||
"mysql_exists", |
|
||||||
"mysql_grant", |
|
||||||
"mysql_user", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def _get_mysql_command(host="localhost", name="mysql", password=None, port=3306, user="root"): |
|
||||||
a = list() |
|
||||||
a.append("%s --user=%s" % (name, user)) |
|
||||||
a.append("--host=%s" % host) |
|
||||||
a.append("--port=%s" % port) |
|
||||||
|
|
||||||
if password: |
|
||||||
a.append('--password="%s"' % password) |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
|
|
||||||
def mysql_create(database, host="localhost", owner=None, password=None, port=3306, user="root", **kwargs): |
|
||||||
"""Create a MySQL database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- owner (str): The owner (user/role name) of the new database. |
|
||||||
- port (int): The TCP port number of the MySQL service running on the host. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "create the %s mysql database" % database) |
|
||||||
|
|
||||||
# MySQL commands always run without sudo because the --user may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
a = _get_mysql_command(host=host, name="mysqladmin", password=password, port=port, user=user) |
|
||||||
a.append("create %s" % database) |
|
||||||
|
|
||||||
if owner: |
|
||||||
grant = mysql_grant( |
|
||||||
owner, |
|
||||||
database=database, |
|
||||||
host=host, |
|
||||||
password=password, |
|
||||||
port=port, |
|
||||||
user=user, |
|
||||||
) |
|
||||||
a.append("&& %s" % grant.get_statement(suppress_comment=True)) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_drop(database, host="localhost", password=None, port=3306, user="root", **kwargs): |
|
||||||
"""Drop (remove) a MySQL database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number of the MySQL service running on the host. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "remove the %s mysql database" % database) |
|
||||||
|
|
||||||
# MySQL commands always run without sudo because the --user may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
a = _get_mysql_command(host=host, name="mysqladmin", password=password, port=port, user=user) |
|
||||||
a.append("drop %s" % database) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_dump(database, file_name=None, host="localhost", inserts=False, password=None, port=3306, user="root", |
|
||||||
**kwargs): |
|
||||||
"""Dump (export) a MySQL database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number of the MySQL service running on the host. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "dump the %s mysql database" % database) |
|
||||||
|
|
||||||
# MySQL commands always run without sudo because the --user may be provided. |
|
||||||
# kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
a = _get_mysql_command(host=host, name="mysqldump", password=password, port=port, user=user) |
|
||||||
|
|
||||||
# if data_only: |
|
||||||
# a.append("--no-create-info") |
|
||||||
# elif schema_only: |
|
||||||
# a.append("--no-data") |
|
||||||
# else: |
|
||||||
# pass |
|
||||||
|
|
||||||
if inserts: |
|
||||||
a.append("--complete-inserts") |
|
||||||
|
|
||||||
a.append(database) |
|
||||||
|
|
||||||
_file_name = file_name or "%s.sql" % database |
|
||||||
a.append("> %s" % _file_name) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_exec(sql, database="default", host="localhost", password=None, port=3306, user="root", **kwargs): |
|
||||||
"""Execute a MySQL statement. |
|
||||||
|
|
||||||
- sql (str): The SQL to run. |
|
||||||
- database (str): The name of the database. |
|
||||||
- host (str): The host name. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs['sudo'] = False |
|
||||||
kwargs.setdefault("comment", "execute mysql statement") |
|
||||||
|
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
a.append(database) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_exists(database, host="localhost", password=None, port=3306, user="root", **kwargs): |
|
||||||
"""Determine if a MySQL database exists. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number of the MySQL service running on the host. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "determine if the %s mysql database exists" % database) |
|
||||||
kwargs.setdefault("register", "mysql_database_exists") |
|
||||||
|
|
||||||
# MySQL commands always run without sudo because the --user may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
|
|
||||||
sql = "SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = '%s'" % database |
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_grant(to, database=None, host="localhost", password=None, port=3306, privileges="ALL", user="root", **kwargs): |
|
||||||
"""Grant privileges to a user. |
|
||||||
|
|
||||||
- to (str): The user name to which privileges are granted. |
|
||||||
- database (str): The database name. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number of the MySQL service running on the host. |
|
||||||
- privileges (str): The privileges to be granted. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "grant mysql privileges to %s" % to) |
|
||||||
|
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
|
|
||||||
# See https://dev.mysql.com/doc/refman/5.7/en/grant.html |
|
||||||
_database = database or "*" |
|
||||||
sql = "GRANT %(privileges)s ON %(database)s.* TO '%(user)s'@'%(host)s'" % { |
|
||||||
'database': _database, |
|
||||||
'host': host, |
|
||||||
'privileges': privileges, |
|
||||||
'user': to, |
|
||||||
} |
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(a)) |
|
||||||
|
|
||||||
|
|
||||||
def mysql_user(name, host="localhost", op="create", passwd=None, password=None, port=3306, user="root", **kwargs): |
|
||||||
"""Work with a MySQL user. |
|
||||||
|
|
||||||
- name (str): The user name. |
|
||||||
- host (str): The host name. |
|
||||||
- op (str): The operation to perform: ``create``, ``drop``, ``exists``. |
|
||||||
- passwd (str): The password for a new user. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs['sudo'] = False |
|
||||||
if op == "create": |
|
||||||
kwargs.setdefault("comment", "create %s mysql user" % name) |
|
||||||
|
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
sql = "CREATE USER IF NOT EXISTS '%(user)s'@'%(host)s'" % { |
|
||||||
'host': host, |
|
||||||
'user': name, |
|
||||||
} |
|
||||||
if passwd: |
|
||||||
sql += " IDENTIFIED BY PASSWORD('%s')" % passwd |
|
||||||
|
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
elif op == "drop": |
|
||||||
kwargs.setdefault("comment", "drop %s mysql user" % name) |
|
||||||
|
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
|
|
||||||
sql = "DROP USER IF EXISTS '%(user)s'@'%(host)s'" % { |
|
||||||
'host': host, |
|
||||||
'user': name, |
|
||||||
} |
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
elif op == "exists": |
|
||||||
kwargs.setdefault("comment", "determine if %s mysql user exists" % name) |
|
||||||
kwargs.setdefault("register", "mysql_user_exists") |
|
||||||
|
|
||||||
a = _get_mysql_command(host=host, password=password, port=port, user=user) |
|
||||||
|
|
||||||
sql = "SELECT EXISTS(SELECT 1 FROM mysql.user WHERE user = '%s')" % name |
|
||||||
a.append('--execute="%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported MySQL user operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
MYSQL_MAPPINGS = { |
|
||||||
'mysql.create': mysql_create, |
|
||||||
'mysql.drop': mysql_drop, |
|
||||||
'mysql.dump': mysql_dump, |
|
||||||
'mysql.exists': mysql_exists, |
|
||||||
'mysql.grant': mysql_grant, |
|
||||||
'mysql.sql': mysql_exec, |
|
||||||
'mysql.user': mysql_user, |
|
||||||
} |
|
@ -1,226 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from ..commands import Command |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"PGSQL_MAPPINGS", |
|
||||||
"pgsql_create", |
|
||||||
"pgsql_drop", |
|
||||||
"pgsql_dump", |
|
||||||
"pgsql_exec", |
|
||||||
"pgsql_exists", |
|
||||||
"pgsql_user", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def _get_pgsql_command(name, host="localhost", password=None, port=5432, user="postgres"): |
|
||||||
"""Get a postgres-related command using commonly required parameters. |
|
||||||
|
|
||||||
:param name: The name of the command. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param host: The host name. |
|
||||||
:type host: str |
|
||||||
|
|
||||||
:param password: The password to use. |
|
||||||
:type password: str |
|
||||||
|
|
||||||
:param port: The TCP port number. |
|
||||||
:type port: int |
|
||||||
|
|
||||||
:param user: The user name that will be used to execute the command. |
|
||||||
|
|
||||||
:rtype: list[str] |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
if password: |
|
||||||
a.append('export PGPASSWORD="%s" &&' % password) |
|
||||||
|
|
||||||
a.append(name) |
|
||||||
|
|
||||||
a.append("--host=%s" % host) |
|
||||||
a.append("--port=%s" % port) |
|
||||||
a.append("--username=%s" % user) |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_create(database, admin_pass=None, admin_user="postgres", host="localhost", owner=None, port=5432, template=None, |
|
||||||
**kwargs): |
|
||||||
"""Create a PostgreSQL database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- admin_pass (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- admin_user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- owner (str): The owner (user/role name) of the new database. |
|
||||||
- port (int): The port number of the Postgres service running on the host. |
|
||||||
- template (str): The database template name to use, if any. |
|
||||||
|
|
||||||
""" |
|
||||||
_owner = owner or admin_user |
|
||||||
|
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
base = _get_pgsql_command("createdb", host=host, password=admin_pass, port=port) |
|
||||||
base.append("--owner=%s" % _owner) |
|
||||||
|
|
||||||
if template is not None: |
|
||||||
base.append("--template=%s" % template) |
|
||||||
|
|
||||||
base.append(database) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_drop(database, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
|
||||||
"""Remove a PostgreSQL database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- admin_pass (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- admin_user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- port (int): The port number of the Postgres service running on the host. |
|
||||||
|
|
||||||
""" |
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
base = _get_pgsql_command("dropdb", host=host, password=admin_pass, port=port, user=admin_user) |
|
||||||
base.append(database) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_dump(database, admin_pass=None, admin_user="postgres", file_name=None, host="localhost", port=5432, **kwargs): |
|
||||||
"""Export a Postgres database. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- admin_pass (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- admin_user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
- file_name (str): The name/path of the export file. Defaults the database name plus ``.sql``. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- port (int): The port number of the Postgres service running on the host. |
|
||||||
|
|
||||||
""" |
|
||||||
_file_name = file_name or "%s.sql" % database |
|
||||||
|
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
# kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
base = _get_pgsql_command("pg_dump", host=host, password=admin_pass, port=port, user=admin_user) |
|
||||||
base.append("--column-inserts") |
|
||||||
base.append("--file=%s" % _file_name) |
|
||||||
base.append(database) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_exec(sql, database="template1", host="localhost", password=None, port=5432, user="postgres", **kwargs): |
|
||||||
"""Execute a psql command. |
|
||||||
|
|
||||||
- sql (str): The SQL to be executed. |
|
||||||
- database (str): The database name. |
|
||||||
- admin_pass (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- admin_user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- port (int): The port number of the Postgres service running on the host. |
|
||||||
|
|
||||||
""" |
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
# Assemble the command. |
|
||||||
base = _get_pgsql_command("psql", host=host, password=password, port=port, user=user) |
|
||||||
base.append("--dbname=%s" % database) |
|
||||||
base.append('-c "%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_exists(database, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
|
||||||
"""Determine if a Postgres database exists. |
|
||||||
|
|
||||||
- database (str): The database name. |
|
||||||
- admin_pass (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- admin_user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
- host (str): The database host name or IP address. |
|
||||||
- owner (str): The owner (user/role name) of the new database. |
|
||||||
- port (int): The port number of the Postgres service running on the host. |
|
||||||
|
|
||||||
""" |
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
kwargs.setdefault("register", "pgsql_db_exists") |
|
||||||
|
|
||||||
base = _get_pgsql_command("psql", host=host, password=admin_pass, port=port, user=admin_user) |
|
||||||
base.append(r"-lqt | cut -d \| -f 1 | grep -qw %s" % database) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def pgsql_user(name, admin_pass=None, admin_user="postgres", host="localhost", op="create", password=None, port=5432, **kwargs): |
|
||||||
"""Work with a PostgreSQL user. |
|
||||||
|
|
||||||
- name (str): The user name. |
|
||||||
- host (str): The host name. |
|
||||||
- op (str): The operation to perform: ``create``, ``drop``, ``exists``. |
|
||||||
- passwd (str): The password for a new user. |
|
||||||
- password (str): The password for the user with sufficient access privileges to execute the command. |
|
||||||
- port (int): The TCP port number. |
|
||||||
- user (str): The name of the user with sufficient access privileges to execute the command. |
|
||||||
|
|
||||||
""" |
|
||||||
# Postgres commands always run without sudo because the -U may be provided. |
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
if op == "create": |
|
||||||
kwargs.setdefault("comment", "create %s postgres user" % name) |
|
||||||
# Assemble the command. |
|
||||||
base = _get_pgsql_command("createuser", host=host, password=admin_pass, port=port) |
|
||||||
base.append("-DRS") |
|
||||||
base.append(name) |
|
||||||
|
|
||||||
if password is not None: |
|
||||||
base.append("&& psql -h %s -U %s" % (host, admin_user)) |
|
||||||
base.append("-c \"ALTER USER %s WITH ENCRYPTED PASSWORD '%s';\"" % (name, password)) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
elif op == "drop": |
|
||||||
kwargs.setdefault("comment", "drop %s postgres user" % name) |
|
||||||
base = _get_pgsql_command("dropuser", host=host, password=admin_pass, port=port, user=admin_user) |
|
||||||
base.append(name) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
elif op == "exists": |
|
||||||
kwargs.setdefault("comment", "determine if %s postgres user exits" % name) |
|
||||||
kwargs.setdefault("register", "pgsql_use_exists") |
|
||||||
|
|
||||||
base = _get_pgsql_command("psql", host=host, password=admin_pass, port=port, user=admin_user) |
|
||||||
|
|
||||||
sql = "SELECT 1 FROM pgsql_roles WHERE rolname='%s'" % name |
|
||||||
base.append('-c "%s"' % sql) |
|
||||||
|
|
||||||
return Command(" ".join(base), **kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported Postgres user operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
PGSQL_MAPPINGS = { |
|
||||||
'pgsql.create': pgsql_create, |
|
||||||
'pgsql.drop': pgsql_drop, |
|
||||||
'pgsql.dump': pgsql_dump, |
|
||||||
'pgsql.exists': pgsql_exists, |
|
||||||
'pgsql.sql': pgsql_exec, |
|
||||||
'pgsql.user': pgsql_user, |
|
||||||
} |
|
@ -1,758 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import indent, split_csv |
|
||||||
import os |
|
||||||
from ..commands import Command |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"POSIX_MAPPINGS", |
|
||||||
"archive", |
|
||||||
"certbot", |
|
||||||
"dialog", |
|
||||||
"echo", |
|
||||||
"extract", |
|
||||||
"file_append", |
|
||||||
"file_copy", |
|
||||||
"file_write", |
|
||||||
"mkdir", |
|
||||||
"move", |
|
||||||
"perms", |
|
||||||
"prompt", |
|
||||||
"remove", |
|
||||||
"rename", |
|
||||||
"rsync", |
|
||||||
"scopy", |
|
||||||
"sed", |
|
||||||
"symlink", |
|
||||||
"touch", |
|
||||||
"wait", |
|
||||||
"Function", |
|
||||||
"Prompt", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def archive(from_path, absolute=False, exclude=None, file_name="archive.tgz", strip=None, to_path=".", view=False, |
|
||||||
**kwargs): |
|
||||||
"""Create a file archive. |
|
||||||
|
|
||||||
- from_path (str): The path that should be archived. |
|
||||||
- absolute (bool): Set to ``True`` to preserve the leading slash. |
|
||||||
- exclude (str): A pattern to be excluded from the archive. |
|
||||||
- strip (int): Remove the specified number of leading elements from the path. |
|
||||||
- to_path (str): Where the archive should be created. This should *not* include the file name. |
|
||||||
- view (bool): View the output of the command as it happens. |
|
||||||
|
|
||||||
""" |
|
||||||
tokens = ["tar"] |
|
||||||
switches = ["-cz"] |
|
||||||
|
|
||||||
if absolute: |
|
||||||
switches.append("P") |
|
||||||
|
|
||||||
if view: |
|
||||||
switches.append("v") |
|
||||||
|
|
||||||
tokens.append("".join(switches)) |
|
||||||
|
|
||||||
if exclude: |
|
||||||
tokens.append("--exclude %s" % exclude) |
|
||||||
|
|
||||||
if strip: |
|
||||||
tokens.append("--strip-components %s" % strip) |
|
||||||
|
|
||||||
to_path = os.path.join(to_path, file_name) |
|
||||||
tokens.append('-f %s %s' % (to_path, from_path)) |
|
||||||
|
|
||||||
name = " ".join(tokens) |
|
||||||
|
|
||||||
return Command(name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def certbot(domain_name, email=None, webroot=None, **kwargs): |
|
||||||
"""Get new SSL certificate from Let's Encrypt. |
|
||||||
|
|
||||||
- domain_name (str): The domain name for which the SSL certificate is requested. |
|
||||||
- email (str): The email address of the requester sent to the certificate authority. Required. |
|
||||||
- webroot (str): The directory where the challenge file will be created. |
|
||||||
|
|
||||||
""" |
|
||||||
_email = email or os.environ.get("SCRIPTTEASE_CERTBOT_EMAIL", None) |
|
||||||
_webroot = webroot or os.path.join("/var", "www", "domains", domain_name.replace(".", "_"), "www") |
|
||||||
|
|
||||||
if not _email: |
|
||||||
raise ValueError("Email is required for certbot command.") |
|
||||||
|
|
||||||
template = "certbot certonly --agree-tos --email %(email)s -n --webroot -w %(webroot)s -d %(domain_name)s" |
|
||||||
name = template % { |
|
||||||
'domain_name': domain_name, |
|
||||||
'email': _email, |
|
||||||
'webroot': _webroot, |
|
||||||
} |
|
||||||
|
|
||||||
return Command(name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def dialog(message, height=15, title="Message", width=100, **kwargs): |
|
||||||
"""Display a dialog message. |
|
||||||
|
|
||||||
- message (str): The message to be displayed. |
|
||||||
- height (int): The height of the dialog. |
|
||||||
- title (str): The title of the dialog. |
|
||||||
- width (int): The width of the dialog. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "display a dialog message") |
|
||||||
|
|
||||||
a = list() |
|
||||||
a.append('dialog --clear --backtitle "%s"' % title) |
|
||||||
a.append('--msgbox "%s" %s %s; clear;' % (message, height, width)) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def echo(message, **kwargs): |
|
||||||
"""Echo a message. |
|
||||||
|
|
||||||
- message (str): The message to be printed to screen. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "print message to screen") |
|
||||||
|
|
||||||
return Command('echo "%s"' % message, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def extract(from_path, absolute=False, exclude=None, strip=None, to_path=None, view=False, **kwargs): |
|
||||||
"""Extract a file archive. |
|
||||||
|
|
||||||
- from_path (str): The path that should be archived. |
|
||||||
- absolute (bool): Set to ``True`` to preserve the leading slash. |
|
||||||
- exclude (str): A pattern to be excluded from the archive. |
|
||||||
- strip (int): Remove the specified number of leading elements from the path. |
|
||||||
- to_path (str): Where the archive should be extracted. This should *not* include the file name. |
|
||||||
- view (bool): View the output of the command as it happens. |
|
||||||
|
|
||||||
""" |
|
||||||
_to_path = to_path or "./" |
|
||||||
|
|
||||||
tokens = ["tar"] |
|
||||||
switches = ["-xz"] |
|
||||||
|
|
||||||
if absolute: |
|
||||||
switches.append("P") |
|
||||||
|
|
||||||
if view: |
|
||||||
switches.append("v") |
|
||||||
|
|
||||||
tokens.append("".join(switches)) |
|
||||||
|
|
||||||
if exclude: |
|
||||||
tokens.append("--exclude %s" % exclude) |
|
||||||
|
|
||||||
if strip: |
|
||||||
tokens.append("--strip-components %s" % strip) |
|
||||||
|
|
||||||
tokens.append('-f %s %s' % (from_path, _to_path)) |
|
||||||
|
|
||||||
name = " ".join(tokens) |
|
||||||
|
|
||||||
return Command(name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def file_append(path, content=None, **kwargs): |
|
||||||
"""Append content to a file. |
|
||||||
|
|
||||||
- path (str): The path to the file. |
|
||||||
- content (str): The content to be appended. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "append to %s" % path) |
|
||||||
|
|
||||||
statement = 'echo "%s" >> %s' % (content or "", path) |
|
||||||
|
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def file_copy(from_path, to_path, overwrite=False, recursive=False, **kwargs): |
|
||||||
"""Copy a file or directory. |
|
||||||
|
|
||||||
- from_path (str): The file or directory to be copied. |
|
||||||
- to_path (str): The location to which the file or directory should be copied. |
|
||||||
- overwrite (bool): Indicates files and directories should be overwritten if they exist. |
|
||||||
- recursive (bool): Copy sub-directories. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "copy %s to %s" % (from_path, to_path)) |
|
||||||
|
|
||||||
a = list() |
|
||||||
a.append("cp") |
|
||||||
|
|
||||||
if not overwrite: |
|
||||||
a.append("-n") |
|
||||||
|
|
||||||
if recursive: |
|
||||||
a.append("-R") |
|
||||||
|
|
||||||
a.append(from_path) |
|
||||||
a.append(to_path) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def file_write(path, content=None, **kwargs): |
|
||||||
"""Write to a file. |
|
||||||
|
|
||||||
- path (str): The file to be written. |
|
||||||
- content (str): The content to be written. Note: If omitted, this command is equivalent to ``touch``. |
|
||||||
|
|
||||||
""" |
|
||||||
_content = content or "" |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "write to %s" % path) |
|
||||||
|
|
||||||
a = list() |
|
||||||
|
|
||||||
if len(_content.split("\n")) > 1: |
|
||||||
a.append("cat > %s << EOF" % path) |
|
||||||
a.append(_content) |
|
||||||
a.append("EOF") |
|
||||||
else: |
|
||||||
a.append('echo "%s" > %s' % (_content, path)) |
|
||||||
|
|
||||||
return Command(" ".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def mkdir(path, mode=None, recursive=True, **kwargs): |
|
||||||
"""Create a directory. |
|
||||||
|
|
||||||
- path (str): The path to be created. |
|
||||||
- mode (int | str): The access permissions of the new directory. |
|
||||||
- recursive (bool): Create all directories along the path. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "create directory %s" % path) |
|
||||||
|
|
||||||
statement = ["mkdir"] |
|
||||||
if mode is not None: |
|
||||||
statement.append("-m %s" % mode) |
|
||||||
|
|
||||||
if recursive: |
|
||||||
statement.append("-p") |
|
||||||
|
|
||||||
statement.append(path) |
|
||||||
|
|
||||||
return Command(" ".join(statement), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def move(from_path, to_path, **kwargs): |
|
||||||
"""Move a file or directory. |
|
||||||
|
|
||||||
- from_path (str): The current path. |
|
||||||
- to_path (str): The new path. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "move %s to %s" % (from_path, to_path)) |
|
||||||
statement = "mv %s %s" % (from_path, to_path) |
|
||||||
|
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def perms(path, group=None, mode=None, owner=None, recursive=False, **kwargs): |
|
||||||
"""Set permissions on a file or directory. |
|
||||||
|
|
||||||
- path (str): The path to be changed. |
|
||||||
- group (str): The name of the group to be applied. |
|
||||||
- mode (int | str): The access permissions of the file or directory. |
|
||||||
- owner (str): The name of the user to be applied. |
|
||||||
- recursive: Create all directories along the path. |
|
||||||
|
|
||||||
""" |
|
||||||
commands = list() |
|
||||||
|
|
||||||
kwargs['comment'] = "set permissions on %s" % path |
|
||||||
|
|
||||||
if group is not None: |
|
||||||
statement = ["chgrp"] |
|
||||||
|
|
||||||
if recursive: |
|
||||||
statement.append("-R") |
|
||||||
|
|
||||||
statement.append(group) |
|
||||||
statement.append(path) |
|
||||||
|
|
||||||
commands.append(Command(" ".join(statement), **kwargs)) |
|
||||||
|
|
||||||
if owner is not None: |
|
||||||
statement = ["chown"] |
|
||||||
|
|
||||||
if recursive: |
|
||||||
statement.append("-R") |
|
||||||
|
|
||||||
statement.append(owner) |
|
||||||
statement.append(path) |
|
||||||
|
|
||||||
commands.append(Command(" ".join(statement), **kwargs)) |
|
||||||
|
|
||||||
if mode is not None: |
|
||||||
statement = ["chmod"] |
|
||||||
|
|
||||||
if recursive: |
|
||||||
statement.append("-R") |
|
||||||
|
|
||||||
statement.append(str(mode)) |
|
||||||
statement.append(path) |
|
||||||
|
|
||||||
commands.append(Command(" ".join(statement), **kwargs)) |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "set permissions on %s" % path) |
|
||||||
|
|
||||||
a = list() |
|
||||||
for c in commands: |
|
||||||
a.append(c.get_statement(suppress_comment=True)) |
|
||||||
|
|
||||||
return Command("\n".join(a), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def prompt(name, back_title="Input", choices=None, default=None, fancy=False, help_text=None, label=None, **kwargs): |
|
||||||
"""Prompt the user for input. |
|
||||||
|
|
||||||
- name (str): The programmatic name of the input. |
|
||||||
- back_title (str): The back title used with the dialog command. |
|
||||||
- choices (str | list): A list of valid choices. |
|
||||||
- default: The default value. |
|
||||||
- fancy (bool): Use a dialog command for the prompt. |
|
||||||
- help_text (str): The text to display with the dialog command. |
|
||||||
- label (str): The label for the input. |
|
||||||
|
|
||||||
""" |
|
||||||
return Prompt( |
|
||||||
name, |
|
||||||
back_title=back_title, |
|
||||||
choices=choices, |
|
||||||
default=default, |
|
||||||
fancy=fancy, |
|
||||||
help_text=help_text, |
|
||||||
label=label, |
|
||||||
**kwargs |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
def remove(path, force=False, recursive=False, **kwargs): |
|
||||||
"""Remove a file or directory. |
|
||||||
|
|
||||||
- path (str): The path to be removed. |
|
||||||
- force (bool): Force the removal. |
|
||||||
- recursive (bool): Remove all directories along the path. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "remove %s" % path) |
|
||||||
|
|
||||||
statement = ["rm"] |
|
||||||
|
|
||||||
if force: |
|
||||||
statement.append("-f") |
|
||||||
|
|
||||||
if recursive: |
|
||||||
statement.append("-r") |
|
||||||
|
|
||||||
statement.append(path) |
|
||||||
|
|
||||||
return Command(" ".join(statement), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def rename(from_name, to_name, **kwargs): |
|
||||||
"""Rename a file or directory. |
|
||||||
|
|
||||||
- from_name (str): The name (or path) of the existing file. |
|
||||||
- to_name (str): The name (or path) of the new file. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "rename %s" % from_name) |
|
||||||
return move(from_name, to_name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def rsync(source, target, delete=False, exclude=None, host=None, key_file=None, links=True, port=22, |
|
||||||
recursive=True, user=None, **kwargs): |
|
||||||
"""Synchronize a directory structure. |
|
||||||
|
|
||||||
- source (str): The source directory. |
|
||||||
- target (str): The target directory. |
|
||||||
- delete (bool): Indicates target files that exist in source but not in target should be removed. |
|
||||||
- exclude (str): The path to an exclude file. |
|
||||||
- host (str): The host name or IP address. This causes the command to run over SSH. |
|
||||||
- key_file (str): The privacy SSH key (path) for remote connections. User expansion is automatically applied. |
|
||||||
- links (bool): Include symlinks in the sync. |
|
||||||
- port (int): The SSH port to use for remote connections. |
|
||||||
- recursive (bool): Indicates source contents should be recursively synchronized. |
|
||||||
- user (str): The user name to use for remote connections. |
|
||||||
|
|
||||||
""" |
|
||||||
# - guess: When ``True``, the ``host``, ``key_file``, and ``user`` will be guessed based on the base name of |
|
||||||
# the source path. |
|
||||||
# :type guess: bool |
|
||||||
# if guess: |
|
||||||
# host = host or os.path.basename(source).replace("_", ".") |
|
||||||
# key_file = key_file or os.path.expanduser(os.path.join("~/.ssh", os.path.basename(source))) |
|
||||||
# user = user or os.path.basename(source) |
|
||||||
# else: |
|
||||||
# host = host |
|
||||||
# key_file = key_file |
|
||||||
# user = user |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "sync %s with %s" % (source, target)) |
|
||||||
|
|
||||||
# rsync -e "ssh -i $(SSH_KEY) -p $(SSH_PORT)" -P -rvzc --delete |
|
||||||
# $(OUTPUTH_PATH) $(SSH_USER)@$(SSH_HOST):$(UPLOAD_PATH) --cvs-exclude; |
|
||||||
|
|
||||||
tokens = list() |
|
||||||
tokens.append("rsync") |
|
||||||
tokens.append("--cvs-exclude") |
|
||||||
tokens.append("--checksum") |
|
||||||
tokens.append("--compress") |
|
||||||
|
|
||||||
if links: |
|
||||||
tokens.append("--copy-links") |
|
||||||
|
|
||||||
if delete: |
|
||||||
tokens.append("--delete") |
|
||||||
|
|
||||||
if exclude is not None: |
|
||||||
tokens.append("--exclude-from=%s" % exclude) |
|
||||||
|
|
||||||
# --partial and --progress |
|
||||||
tokens.append("-P") |
|
||||||
|
|
||||||
if recursive: |
|
||||||
tokens.append("--recursive") |
|
||||||
|
|
||||||
tokens.append(source) |
|
||||||
|
|
||||||
conditions = [ |
|
||||||
host is not None, |
|
||||||
key_file is not None, |
|
||||||
user is not None, |
|
||||||
] |
|
||||||
if all(conditions): |
|
||||||
tokens.append('-e "ssh -i %s -p %s"' % (key_file, port)) |
|
||||||
tokens.append("%s@%s:%s" % (user, host, target)) |
|
||||||
else: |
|
||||||
tokens.append(target) |
|
||||||
|
|
||||||
statement = " ".join(tokens) |
|
||||||
|
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def scopy(from_path, to_path, host=None, key_file=None, port=22, user=None, **kwargs): |
|
||||||
"""Copy a file or directory to a remote server. |
|
||||||
|
|
||||||
- from_path (str): The source directory. |
|
||||||
- to_path (str): The target directory. |
|
||||||
- host (str): The host name or IP address. Required. |
|
||||||
- key_file (str): The privacy SSH key (path) for remote connections. User expansion is automatically applied. |
|
||||||
- port (int): The SSH port to use for remote connections. |
|
||||||
- user (str): The user name to use for remote connections. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "copy %s to remote %s" % (from_path, to_path)) |
|
||||||
|
|
||||||
# TODO: What to do to force local versus remote commands? |
|
||||||
# kwargs['local'] = True |
|
||||||
|
|
||||||
kwargs['sudo'] = False |
|
||||||
|
|
||||||
statement = ["scp"] |
|
||||||
|
|
||||||
if key_file is not None: |
|
||||||
statement.append("-i %s" % key_file) |
|
||||||
|
|
||||||
statement.append("-P %s" % port) |
|
||||||
statement.append(from_path) |
|
||||||
|
|
||||||
if host is not None and user is not None: |
|
||||||
statement.append("%s@%s:%s" % (user, host, to_path)) |
|
||||||
elif host is not None: |
|
||||||
statement.append("%s:%s" % (host, to_path)) |
|
||||||
else: |
|
||||||
raise ValueError("Host is a required keyword argument.") |
|
||||||
|
|
||||||
return Command(" ".join(statement), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def sed(path, backup=".b", delimiter="/", find=None, replace=None, **kwargs): |
|
||||||
"""Find and replace text in a file. |
|
||||||
|
|
||||||
- path (str): The path to the file to be edited. |
|
||||||
- backup (str): The backup file extension to use. |
|
||||||
- delimiter (str): The pattern delimiter. |
|
||||||
- find (str): The old text. Required. |
|
||||||
- replace (str): The new text. Required. |
|
||||||
|
|
||||||
""" |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "find and replace in %s" % path) |
|
||||||
|
|
||||||
context = { |
|
||||||
'backup': backup, |
|
||||||
'delimiter': delimiter, |
|
||||||
'path': path, |
|
||||||
'pattern': find, |
|
||||||
'replace': replace, |
|
||||||
} |
|
||||||
|
|
||||||
template = "sed -i %(backup)s 's%(delimiter)s%(pattern)s%(delimiter)s%(replace)s%(delimiter)sg' %(path)s" |
|
||||||
|
|
||||||
statement = template % context |
|
||||||
|
|
||||||
return Command(statement, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def symlink(source, force=False, target=None, **kwargs): |
|
||||||
"""Create a symlink. |
|
||||||
|
|
||||||
- source (str): The source of the link. |
|
||||||
- force (bool): Force the creation of the link. |
|
||||||
- target (str): The name or path of the target. Defaults to the base name of the source path. |
|
||||||
|
|
||||||
""" |
|
||||||
_target = target or os.path.basename(source) |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "link to %s" % source) |
|
||||||
|
|
||||||
statement = ["ln -s"] |
|
||||||
|
|
||||||
if force: |
|
||||||
statement.append("-f") |
|
||||||
|
|
||||||
statement.append(source) |
|
||||||
statement.append(_target) |
|
||||||
|
|
||||||
return Command(" ".join(statement), **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def touch(path, **kwargs): |
|
||||||
"""Touch a file or directory. |
|
||||||
|
|
||||||
- path (str): The file or directory to touch. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "touch %s" % path) |
|
||||||
|
|
||||||
return Command("touch %s" % path, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def wait(seconds, **kwargs): |
|
||||||
"""Pause execution for a number of seconds. |
|
||||||
|
|
||||||
- seconds (int): The number of seconds to wait. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "pause for %s seconds" % seconds) |
|
||||||
|
|
||||||
return Command("sleep %s" % seconds, **kwargs) |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Function(object): |
|
||||||
"""A function that may be used to organize related commands to be called together.""" |
|
||||||
|
|
||||||
def __init__(self, name, commands=None, comment=None): |
|
||||||
"""Initialize a function. |
|
||||||
|
|
||||||
:param name: The name of the function. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param commands: The command instances to be included in the function's output. |
|
||||||
:type commands: list |
|
||||||
|
|
||||||
:param comment: A comment regarding the function. |
|
||||||
:type comment: str |
|
||||||
|
|
||||||
""" |
|
||||||
self.commands = commands or list() |
|
||||||
self.comment = comment |
|
||||||
self.name = name |
|
||||||
|
|
||||||
def to_string(self): |
|
||||||
"""Export the function as a string. |
|
||||||
|
|
||||||
:rtype: str |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
if self.comment is not None: |
|
||||||
a.append("# %s" % self.comment) |
|
||||||
|
|
||||||
a.append("function %s()" % self.name) |
|
||||||
a.append("{") |
|
||||||
for command in self.commands: |
|
||||||
a.append(indent(command.get_statement(cd=True))) |
|
||||||
a.append("") |
|
||||||
|
|
||||||
a.append("}") |
|
||||||
|
|
||||||
return "\n".join(a) |
|
||||||
|
|
||||||
|
|
||||||
class Prompt(Command): |
|
||||||
"""Prompt the user for input.""" |
|
||||||
|
|
||||||
def __init__(self, name, back_title="Input", choices=None, default=None, fancy=False, help_text=None, label=None, |
|
||||||
**kwargs): |
|
||||||
"""Initialize a prompt for user input. |
|
||||||
|
|
||||||
:param name: The variable name. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param back_title: The back title of the input. Used only when ``dialog`` is enabled. |
|
||||||
:type back_title: str |
|
||||||
|
|
||||||
:param choices: Valid choices for the variable. May be given as a list of strings or a comma separated string. |
|
||||||
:type choices: list[str] | str |
|
||||||
|
|
||||||
:param default: The default value of the variable. |
|
||||||
|
|
||||||
:param fancy: Indicates the dialog command should be used. |
|
||||||
:type fancy: bool |
|
||||||
|
|
||||||
:param help_text: Additional text to display. Only use when ``fancy`` is ``True``. |
|
||||||
:type help_text: str |
|
||||||
|
|
||||||
:param label: The label of the prompt. |
|
||||||
|
|
||||||
""" |
|
||||||
self.back_title = back_title |
|
||||||
self.default = default |
|
||||||
self.dialog_enabled = fancy |
|
||||||
self.help_text = help_text |
|
||||||
self.label = label or name.replace("_", " ").title() |
|
||||||
self.variable_name = name |
|
||||||
|
|
||||||
if type(choices) in (list, tuple): |
|
||||||
self.choices = choices |
|
||||||
elif type(choices) is str: |
|
||||||
self.choices = split_csv(choices, smart=False) |
|
||||||
# for i in choices.split(","): |
|
||||||
# self.choices.append(i.strip()) |
|
||||||
else: |
|
||||||
self.choices = None |
|
||||||
|
|
||||||
kwargs.setdefault("comment", "prompt user for %s input" % name) |
|
||||||
|
|
||||||
super().__init__(name, **kwargs) |
|
||||||
|
|
||||||
def get_statement(self, cd=False, suppress_comment=False): |
|
||||||
"""Get the statement using dialog or read.""" |
|
||||||
if self.dialog_enabled: |
|
||||||
return self._get_dialog_statement() |
|
||||||
|
|
||||||
return self._get_read_statement() |
|
||||||
|
|
||||||
def _get_dialog_statement(self): |
|
||||||
"""Get the dialog statement.""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
a.append('dialog --clear --backtitle "%s" --title "%s"' % (self.back_title, self.label)) |
|
||||||
|
|
||||||
if self.choices is not None: |
|
||||||
a.append('--menu "%s" 15 40 %s' % (self.help_text or "Select", len(self.choices))) |
|
||||||
count = 1 |
|
||||||
for choice in self.choices: |
|
||||||
a.append('"%s" %s' % (choice, count)) |
|
||||||
count += 1 |
|
||||||
|
|
||||||
a.append('2>/tmp/input.txt') |
|
||||||
else: |
|
||||||
if self.help_text is not None: |
|
||||||
a.append('--inputbox "%s"' % self.help_text) |
|
||||||
else: |
|
||||||
a.append('--inputbox ""') |
|
||||||
|
|
||||||
a.append('8 60 2>/tmp/input.txt') |
|
||||||
|
|
||||||
b = list() |
|
||||||
|
|
||||||
b.append('touch /tmp/input.txt') |
|
||||||
b.append(" ".join(a)) |
|
||||||
|
|
||||||
b.append('%s=$(</tmp/input.txt)' % self.variable_name) |
|
||||||
b.append('clear') |
|
||||||
b.append('rm /tmp/input.txt') |
|
||||||
|
|
||||||
if self.default is not None: |
|
||||||
b.append('if [[ -z "$%s" ]]; then %s="%s"; fi;' % (self.variable_name, self.variable_name, self.default)) |
|
||||||
|
|
||||||
# b.append('echo "$%s"' % self.name) |
|
||||||
|
|
||||||
return "\n".join(b) |
|
||||||
|
|
||||||
def _get_read_statement(self): |
|
||||||
"""Get the standard read statement.""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
if self.choices is not None: |
|
||||||
a.append('echo "%s "' % self.label) |
|
||||||
|
|
||||||
options = list() |
|
||||||
for choice in self.choices: |
|
||||||
options.append('"%s"' % choice) |
|
||||||
|
|
||||||
a.append('options=(%s)' % " ".join(options)) |
|
||||||
a.append('select opt in "${options[@]}"') |
|
||||||
a.append('do') |
|
||||||
a.append(' case $opt in') |
|
||||||
|
|
||||||
for choice in self.choices: |
|
||||||
a.append(' "%s") %s=$opt; break;;' % (choice, self.variable_name)) |
|
||||||
|
|
||||||
# a.append(' %s) %s=$opt;;' % ("|".join(self.choices), self.name)) |
|
||||||
a.append(' *) echo "invalid choice";;') |
|
||||||
a.append(' esac') |
|
||||||
a.append('done') |
|
||||||
|
|
||||||
# a.append("read %s" % self.name) |
|
||||||
else: |
|
||||||
a.append('echo -n "%s "' % self.label) |
|
||||||
a.append("read %s" % self.variable_name) |
|
||||||
|
|
||||||
if self.default is not None: |
|
||||||
a.append('if [[ -z "$%s" ]]; then %s="%s"; fi;' % (self.variable_name, self.variable_name, self.default)) |
|
||||||
|
|
||||||
# a.append('echo "$%s"' % self.name) |
|
||||||
|
|
||||||
return "\n".join(a) |
|
||||||
|
|
||||||
# Mappings |
|
||||||
|
|
||||||
|
|
||||||
POSIX_MAPPINGS = { |
|
||||||
'append': file_append, |
|
||||||
'archive': archive, |
|
||||||
'certbot': certbot, |
|
||||||
'copy': file_copy, |
|
||||||
'dialog': dialog, |
|
||||||
'echo': echo, |
|
||||||
'extract': extract, |
|
||||||
'func': Function, |
|
||||||
# 'function': Function, |
|
||||||
'mkdir': mkdir, |
|
||||||
'move': move, |
|
||||||
'perms': perms, |
|
||||||
'prompt': prompt, |
|
||||||
'push': rsync, |
|
||||||
'remove': remove, |
|
||||||
'rename': rename, |
|
||||||
'rsync': rsync, |
|
||||||
'scopy': scopy, |
|
||||||
'sed': sed, |
|
||||||
'ssl': certbot, |
|
||||||
'symlink': symlink, |
|
||||||
'touch': touch, |
|
||||||
'wait': wait, |
|
||||||
'write': file_write, |
|
||||||
} |
|
@ -1,338 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import split_csv |
|
||||||
from ..commands import Command, Template |
|
||||||
from .common import COMMON_MAPPINGS |
|
||||||
from .django import DJANGO_MAPPINGS |
|
||||||
from .mysql import MYSQL_MAPPINGS |
|
||||||
from .pgsql import PGSQL_MAPPINGS |
|
||||||
from .posix import POSIX_MAPPINGS, Function |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"MAPPINGS", |
|
||||||
"apache", |
|
||||||
"apache_disable_module", |
|
||||||
"apache_disable_site", |
|
||||||
"apache_enable_module", |
|
||||||
"apache_enable_site", |
|
||||||
"apache_reload", |
|
||||||
"apache_restart", |
|
||||||
"apache_start", |
|
||||||
"apache_stop", |
|
||||||
"apache_test", |
|
||||||
"command_exists", |
|
||||||
"service_reload", |
|
||||||
"service_restart", |
|
||||||
"service_start", |
|
||||||
"service_stop", |
|
||||||
"system", |
|
||||||
"system_install", |
|
||||||
"system_reboot", |
|
||||||
"system_update", |
|
||||||
"system_upgrade", |
|
||||||
"system_uninstall", |
|
||||||
"template", |
|
||||||
"user", |
|
||||||
"Function", |
|
||||||
) |
|
||||||
|
|
||||||
|
|
||||||
def command_exists(name): |
|
||||||
"""Indicates whether a given command exists in this overlay. |
|
||||||
|
|
||||||
:param name: The name of the command. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
return name in MAPPINGS |
|
||||||
|
|
||||||
|
|
||||||
def apache(op, **kwargs): |
|
||||||
"""Execute an Apache-related command. |
|
||||||
|
|
||||||
- op (str): The operation to perform; reload, restart, start, stop, test. |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "reload": |
|
||||||
return apache_reload(**kwargs) |
|
||||||
elif op == "restart": |
|
||||||
return apache_restart(**kwargs) |
|
||||||
elif op == "start": |
|
||||||
return apache_start(**kwargs) |
|
||||||
elif op == "stop": |
|
||||||
return apache_stop(**kwargs) |
|
||||||
elif op == "test": |
|
||||||
return apache_test(**kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported apache operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
def apache_disable_module(name, **kwargs): |
|
||||||
"""Disable an Apache module. |
|
||||||
|
|
||||||
- name (str): The module name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "disable %s apache module" % name) |
|
||||||
|
|
||||||
return Command("a2dismod %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_disable_site(name, **kwargs): |
|
||||||
"""Disable an Apache site. |
|
||||||
|
|
||||||
- name (str): The domain name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "disable %s apache site" % name) |
|
||||||
|
|
||||||
return Command("a2dissite %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_enable_module(name, **kwargs): |
|
||||||
"""Enable an Apache module. |
|
||||||
|
|
||||||
- name (str): The module name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "enable %s apache module" % name) |
|
||||||
|
|
||||||
return Command("a2enmod %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_enable_site(name, **kwargs): |
|
||||||
"""Enable an Apache site. |
|
||||||
|
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "enable %s apache module" % name) |
|
||||||
|
|
||||||
return Command("a2ensite %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_reload(**kwargs): |
|
||||||
kwargs.setdefault("comment", "reload apache") |
|
||||||
kwargs.setdefault("register", "apache_reloaded") |
|
||||||
|
|
||||||
return Command("service apache2 reload", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_restart(**kwargs): |
|
||||||
kwargs.setdefault("comment", "restart apache") |
|
||||||
kwargs.setdefault("register", "apache_restarted") |
|
||||||
|
|
||||||
return Command("service apache2 restart", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_start(**kwargs): |
|
||||||
kwargs.setdefault("comment", "start apache") |
|
||||||
kwargs.setdefault("register", "apache_started") |
|
||||||
|
|
||||||
return Command("service apache2 start", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_stop(**kwargs): |
|
||||||
kwargs.setdefault("comment", "stop apache") |
|
||||||
|
|
||||||
return Command("service apache2 stop", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def apache_test(**kwargs): |
|
||||||
kwargs.setdefault("comment", "check apache configuration") |
|
||||||
kwargs.setdefault("register", "apache_checks_out") |
|
||||||
|
|
||||||
return Command("apachectl configtest", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_reload(name, **kwargs): |
|
||||||
"""Reload a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "reload %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_reloaded" % name) |
|
||||||
|
|
||||||
return Command("service %s reload" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_restart(name, **kwargs): |
|
||||||
"""Restart a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "restart %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_restarted" % name) |
|
||||||
|
|
||||||
return Command("service %s restart" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_start(name, **kwargs): |
|
||||||
"""Start a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "start %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_started" % name) |
|
||||||
|
|
||||||
return Command("service %s start" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def service_stop(name, **kwargs): |
|
||||||
"""Stop a service. |
|
||||||
|
|
||||||
- name (str): The service name. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "stop %s service" % name) |
|
||||||
kwargs.setdefault("register", "%s_stopped" % name) |
|
||||||
|
|
||||||
return Command("service %s stop" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system(op, **kwargs): |
|
||||||
"""Perform a system operation. |
|
||||||
|
|
||||||
- op (str): The operation to perform; reboot, update, upgrade. |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "reboot": |
|
||||||
return system_reboot(**kwargs) |
|
||||||
elif op == "update": |
|
||||||
return system_update(**kwargs) |
|
||||||
elif op == "upgrade": |
|
||||||
return system_upgrade(**kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unrecognized or unsupported system operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
def system_install(name, **kwargs): |
|
||||||
"""Install a system-level package. |
|
||||||
|
|
||||||
- name (str): The name of the package to install. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "install system package %s" % name) |
|
||||||
|
|
||||||
return Command("apt-get install -y %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_reboot(**kwargs): |
|
||||||
kwargs.setdefault("comment", "reboot the system") |
|
||||||
|
|
||||||
return Command("reboot", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_uninstall(name, **kwargs): |
|
||||||
"""Uninstall a system-level package. |
|
||||||
|
|
||||||
- name (str): The name of the package to uninstall. |
|
||||||
|
|
||||||
""" |
|
||||||
kwargs.setdefault("comment", "remove system package %s" % name) |
|
||||||
|
|
||||||
return Command("apt-get uninstall -y %s" % name, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_update(**kwargs): |
|
||||||
kwargs.setdefault("comment", "update system package info") |
|
||||||
|
|
||||||
return Command("apt-get update -y", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def system_upgrade(**kwargs): |
|
||||||
kwargs.setdefault("comment", "upgrade the system") |
|
||||||
|
|
||||||
return Command("apt-get upgrade -y", **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def template(source, target, backup=True, parser=None, **kwargs): |
|
||||||
"""Create a file from a template. |
|
||||||
|
|
||||||
- source (str): The path to the template file. |
|
||||||
- target (str): The path to where the new file should be created. |
|
||||||
- backup (bool): Indicates whether a backup should be made if the target file already exists. |
|
||||||
- parser (str): The parser to use ``jinja`` (the default) or ``simple``. |
|
||||||
|
|
||||||
""" |
|
||||||
return Template(source, target, backup=backup, parser=parser, **kwargs) |
|
||||||
|
|
||||||
|
|
||||||
def user(name, groups=None, home=None, op="add", password=None, **kwargs): |
|
||||||
"""Create or remove a user. |
|
||||||
|
|
||||||
- name (str): The user name. |
|
||||||
- groups (str | list): A list of groups to which the user should belong. |
|
||||||
- home (str): The path to the user's home directory. |
|
||||||
- op (str); The operation to perform; ``add`` or ``remove``. |
|
||||||
- password (str): The user's password. (NOT IMPLEMENTED) |
|
||||||
|
|
||||||
""" |
|
||||||
if op == "add": |
|
||||||
kwargs.setdefault("comment", "create a user named %s" % name) |
|
||||||
|
|
||||||
commands = list() |
|
||||||
|
|
||||||
# The gecos switch eliminates the prompts. |
|
||||||
a = list() |
|
||||||
a.append('adduser %s --disabled-password --gecos ""' % name) |
|
||||||
if home is not None: |
|
||||||
a.append("--home %s" % home) |
|
||||||
|
|
||||||
commands.append(Command(" ".join(a), **kwargs)) |
|
||||||
|
|
||||||
if type(groups) is str: |
|
||||||
groups = split_csv(groups, smart=False) |
|
||||||
|
|
||||||
if type(groups) in [list, tuple]: |
|
||||||
for group in groups: |
|
||||||
commands.append(Command("adduser %s %s" % (name, group), **kwargs)) |
|
||||||
|
|
||||||
a = list() |
|
||||||
for c in commands: |
|
||||||
a.append(c.get_statement(suppress_comment=True)) |
|
||||||
|
|
||||||
return Command("\n".join(a), **kwargs) |
|
||||||
elif op == "remove": |
|
||||||
kwargs.setdefault("comment", "remove a user named %s" % name) |
|
||||||
return Command("deluser %s" % name, **kwargs) |
|
||||||
else: |
|
||||||
raise NameError("Unsupported or unrecognized operation: %s" % op) |
|
||||||
|
|
||||||
|
|
||||||
MAPPINGS = { |
|
||||||
'apache': apache, |
|
||||||
'apache.disable_module': apache_disable_module, |
|
||||||
'apache.disable_site': apache_disable_site, |
|
||||||
'apache.enable_module': apache_enable_module, |
|
||||||
'apache.enable_site': apache_enable_site, |
|
||||||
'apache.reload': apache_reload, |
|
||||||
'apache.restart': apache_restart, |
|
||||||
'apache.start': apache_start, |
|
||||||
'apache.stop': apache_stop, |
|
||||||
'apache.test': apache_test, |
|
||||||
'install': system_install, |
|
||||||
'reboot': system_reboot, |
|
||||||
'reload': service_reload, |
|
||||||
'restart': service_restart, |
|
||||||
'start': service_start, |
|
||||||
'stop': service_stop, |
|
||||||
'system': system, |
|
||||||
'template': template, |
|
||||||
'update': system_update, |
|
||||||
'uninstall': system_uninstall, |
|
||||||
'upgrade': system_upgrade, |
|
||||||
'user': user, |
|
||||||
} |
|
||||||
|
|
||||||
MAPPINGS.update(COMMON_MAPPINGS) |
|
||||||
MAPPINGS.update(DJANGO_MAPPINGS) |
|
||||||
MAPPINGS.update(MYSQL_MAPPINGS) |
|
||||||
MAPPINGS.update(PGSQL_MAPPINGS) |
|
||||||
MAPPINGS.update(POSIX_MAPPINGS) |
|
@ -1,69 +0,0 @@ |
|||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Script(object): |
|
||||||
"""A script is a collection of commands.""" |
|
||||||
|
|
||||||
def __init__(self, name, commands=None, functions=None, shell="bash"): |
|
||||||
"""Initialize a script. |
|
||||||
|
|
||||||
:param name: The name of the script. Note: This becomes the file name. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param commands: The commands to be included. |
|
||||||
:type commands: list[scripttease.library.commands.base.Command] |
|
||||||
|
|
||||||
:param functions: The functions to be included. |
|
||||||
:type functions: list[Function] |
|
||||||
|
|
||||||
:param shell: The shell to use for the script. |
|
||||||
:type shell: str |
|
||||||
|
|
||||||
""" |
|
||||||
self.commands = commands or list() |
|
||||||
self.functions = functions |
|
||||||
self.name = name |
|
||||||
self.shell = shell |
|
||||||
|
|
||||||
def __str__(self): |
|
||||||
return self.to_string() |
|
||||||
|
|
||||||
def append(self, command): |
|
||||||
"""Append a command instance to the script's commands. |
|
||||||
|
|
||||||
:param command: The command instance to be included. |
|
||||||
:type command: BaseType[Command] | ItemizedCommand |
|
||||||
|
|
||||||
""" |
|
||||||
self.commands.append(command) |
|
||||||
|
|
||||||
def to_string(self, shebang="#! /usr/bin/env %(shell)s"): |
|
||||||
"""Export the script as a string. |
|
||||||
|
|
||||||
:param shebang: The shebang to be included. Set to ``None`` to omit the shebang. |
|
||||||
:type shebang: str |
|
||||||
|
|
||||||
:rtype: str |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
|
|
||||||
if shebang is not None: |
|
||||||
a.append(shebang % {'shell': self.shell}) |
|
||||||
a.append("") |
|
||||||
|
|
||||||
if self.functions is not None: |
|
||||||
for function in self.functions: |
|
||||||
a.append(function.to_string()) |
|
||||||
a.append("") |
|
||||||
|
|
||||||
# for function in self.functions: |
|
||||||
# a.append("%s;" % function.name) |
|
||||||
|
|
||||||
a.append("") |
|
||||||
|
|
||||||
for command in self.commands: |
|
||||||
a.append(command.get_statement(cd=True)) |
|
||||||
a.append("") |
|
||||||
|
|
||||||
return "\n".join(a) |
|
@ -1,4 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from .ini import Config |
|
||||||
from .utils import filter_commands, load_commands, load_config |
|
@ -1,80 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import File |
|
||||||
from ..factory import Factory |
|
||||||
from ..library.scripts import Script |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"Parser", |
|
||||||
) |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Parser(File): |
|
||||||
"""Base class for implementing a command parser.""" |
|
||||||
|
|
||||||
def __init__(self, path, context=None, locations=None, options=None, overlay="ubuntu"): |
|
||||||
super().__init__(path) |
|
||||||
|
|
||||||
self.context = context |
|
||||||
self.factory = Factory(overlay) |
|
||||||
self.is_loaded = False |
|
||||||
self.locations = locations or list() |
|
||||||
self.options = options or dict() |
|
||||||
self.overlay = overlay |
|
||||||
self._commands = list() |
|
||||||
self._functions = list() |
|
||||||
|
|
||||||
def as_script(self): |
|
||||||
"""Convert loaded commands to a script. |
|
||||||
|
|
||||||
:rtype: Script |
|
||||||
|
|
||||||
""" |
|
||||||
return Script( |
|
||||||
"%s.sh" % self.name, |
|
||||||
commands=self.get_commands(), |
|
||||||
functions=self.get_functions() |
|
||||||
) |
|
||||||
|
|
||||||
def get_commands(self): |
|
||||||
"""Get the commands that have been loaded from the file. |
|
||||||
|
|
||||||
:rtype: list[BaseType[scripttease.library.commands.base.Command]] |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
for c in self._commands: |
|
||||||
if c.function is not None: |
|
||||||
continue |
|
||||||
|
|
||||||
a.append(c) |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
def get_functions(self): |
|
||||||
"""Get the functions that have been loaded from the file. |
|
||||||
|
|
||||||
:rtype: list[scripttease.library.scripts.Function] |
|
||||||
|
|
||||||
""" |
|
||||||
a = list() |
|
||||||
for f in self._functions: |
|
||||||
for c in self._commands: |
|
||||||
if c.function is not None and f.name == c.function: |
|
||||||
f.commands.append(c) |
|
||||||
|
|
||||||
a.append(f) |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
def load(self): |
|
||||||
"""Load the factory and the configuration file. |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
raise NotImplementedError() |
|
@ -1,179 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import parse_jinja_template, read_file, smart_cast, split_csv |
|
||||||
from configparser import ConfigParser, ParsingError |
|
||||||
import logging |
|
||||||
import os |
|
||||||
from ..constants import LOGGER_NAME |
|
||||||
from ..library.commands import ItemizedCommand |
|
||||||
from ..library.commands.templates import Template |
|
||||||
from .base import Parser |
|
||||||
|
|
||||||
log = logging.getLogger(LOGGER_NAME) |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"Config", |
|
||||||
) |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Config(Parser): |
|
||||||
"""An INI configuration for loading commands.""" |
|
||||||
|
|
||||||
def load(self): |
|
||||||
"""Load commands from a INI file.""" |
|
||||||
if not self.exists: |
|
||||||
return False |
|
||||||
|
|
||||||
if not self.factory.load(): |
|
||||||
return False |
|
||||||
|
|
||||||
ini = self._load_ini() |
|
||||||
if ini is None: |
|
||||||
return False |
|
||||||
|
|
||||||
success = True |
|
||||||
for comment in ini.sections(): |
|
||||||
args = list() |
|
||||||
command_name = None |
|
||||||
count = 0 |
|
||||||
kwargs = self.options.copy() |
|
||||||
kwargs['comment'] = comment |
|
||||||
|
|
||||||
for key, value in ini.items(comment): |
|
||||||
# The first key/value pair is the command name and arguments. |
|
||||||
if count == 0: |
|
||||||
command_name = key |
|
||||||
|
|
||||||
# Arguments surrounded by quotes are considered to be one argument. All others are split into a |
|
||||||
# list to be passed to the callback. It is also possible that this is a call where no arguments are |
|
||||||
# present, so the whole thing is wrapped to protect against an index error. |
|
||||||
try: |
|
||||||
if value[0] == '"': |
|
||||||
args.append(value.replace('"', "")) |
|
||||||
else: |
|
||||||
args = value.split(" ") |
|
||||||
except IndexError: |
|
||||||
pass |
|
||||||
else: |
|
||||||
_key, _value = self._get_key_value(key, value) |
|
||||||
|
|
||||||
kwargs[_key] = _value |
|
||||||
|
|
||||||
count += 1 |
|
||||||
|
|
||||||
command = self.factory.get_command(command_name, *args, **kwargs) |
|
||||||
if command is not None: |
|
||||||
if isinstance(command, self.factory.overlay.Function): |
|
||||||
self._functions.append(command) |
|
||||||
elif isinstance(command, Template): |
|
||||||
self._load_template(command) |
|
||||||
self._commands.append(command) |
|
||||||
elif isinstance(command, ItemizedCommand): |
|
||||||
itemized_template = False |
|
||||||
for c in command.get_commands(): |
|
||||||
if isinstance(c, Template): |
|
||||||
itemized_template = True |
|
||||||
self._load_template(c) |
|
||||||
self._commands.append(c) |
|
||||||
|
|
||||||
if not itemized_template: |
|
||||||
self._commands.append(command) |
|
||||||
else: |
|
||||||
self._commands.append(command) |
|
||||||
|
|
||||||
# if isinstance(command, Function): |
|
||||||
# self._functions.append(command) |
|
||||||
# elif isinstance(command, Include): |
|
||||||
# subcommands = self._load_include(command) |
|
||||||
# if subcommands is not None: |
|
||||||
# self._commands += subcommands |
|
||||||
# elif isinstance(command, Template): |
|
||||||
# self._load_template(command) |
|
||||||
# self._commands.append(command) |
|
||||||
# elif isinstance(command, ItemizedCommand) and issubclass(command.command_class, Template): |
|
||||||
# for c in command.get_commands(): |
|
||||||
# self._load_template(c) |
|
||||||
# self._commands.append(c) |
|
||||||
# else: |
|
||||||
# self._commands.append(command) |
|
||||||
else: |
|
||||||
success = False |
|
||||||
|
|
||||||
self.is_loaded = success |
|
||||||
return self.is_loaded |
|
||||||
|
|
||||||
# noinspection PyMethodMayBeStatic |
|
||||||
def _get_key_value(self, key, value): |
|
||||||
"""Process a key/value pair from an INI section. |
|
||||||
|
|
||||||
:param key: The key to be processed. |
|
||||||
:type key: str |
|
||||||
|
|
||||||
:param value: The value to be processed. |
|
||||||
|
|
||||||
:rtype: tuple |
|
||||||
:returns: The key and value, both of which may be modified from the originals. |
|
||||||
|
|
||||||
""" |
|
||||||
if key in ("environments", "environs", "envs", "env"): |
|
||||||
_key = "environments" |
|
||||||
_value = split_csv(value) |
|
||||||
elif key in ("func", "function"): |
|
||||||
_key = "function" |
|
||||||
_value = value |
|
||||||
elif key == "items": |
|
||||||
_key = "items" |
|
||||||
_value = split_csv(value) |
|
||||||
elif key == "tags": |
|
||||||
_key = "tags" |
|
||||||
_value = split_csv(value) |
|
||||||
else: |
|
||||||
_key = key |
|
||||||
_value = smart_cast(value) |
|
||||||
|
|
||||||
return _key, _value |
|
||||||
|
|
||||||
def _load_ini(self): |
|
||||||
"""Load the configuration file. |
|
||||||
|
|
||||||
:rtype: ConfigParser | None |
|
||||||
|
|
||||||
""" |
|
||||||
ini = ConfigParser() |
|
||||||
if self.context is not None: |
|
||||||
try: |
|
||||||
content = parse_jinja_template(self.path, self.context) |
|
||||||
except Exception as e: |
|
||||||
log.error("Failed to parse %s as template: %s" % (self.path, e)) |
|
||||||
return None |
|
||||||
else: |
|
||||||
content = read_file(self.path) |
|
||||||
|
|
||||||
try: |
|
||||||
ini.read_string(content) |
|
||||||
return ini |
|
||||||
except ParsingError as e: |
|
||||||
log.error("Failed to parse %s: %s" % (self.path, e)) |
|
||||||
return None |
|
||||||
|
|
||||||
def _load_template(self, command): |
|
||||||
"""Load additional resources for a template command. |
|
||||||
|
|
||||||
:param command: The template command. |
|
||||||
:type command: Template |
|
||||||
|
|
||||||
""" |
|
||||||
# This may produce problems if template kwargs are the same as the given context. |
|
||||||
if self.context is not None: |
|
||||||
command.context.update(self.context) |
|
||||||
|
|
||||||
# Custom locations come before default locations. |
|
||||||
command.locations += self.locations |
|
||||||
|
|
||||||
# This allows template files to be specified relative to the configuration file. |
|
||||||
command.locations.append(os.path.join(self.directory, "templates")) |
|
||||||
command.locations.append(self.directory) |
|
@ -1,325 +0,0 @@ |
|||||||
# Imports |
|
||||||
|
|
||||||
from commonkit import any_list_item, smart_cast, split_csv |
|
||||||
from configparser import RawConfigParser |
|
||||||
import logging |
|
||||||
import os |
|
||||||
from ..constants import LOGGER_NAME |
|
||||||
from .ini import Config |
|
||||||
|
|
||||||
log = logging.getLogger(LOGGER_NAME) |
|
||||||
|
|
||||||
# Exports |
|
||||||
|
|
||||||
__all__ = ( |
|
||||||
"filter_commands", |
|
||||||
"load_commands", |
|
||||||
"load_config", |
|
||||||
"load_variables", |
|
||||||
"Context", |
|
||||||
"Variable", |
|
||||||
) |
|
||||||
|
|
||||||
# Functions |
|
||||||
|
|
||||||
|
|
||||||
def filter_commands(commands, environments=None, tags=None): |
|
||||||
"""Filter commands based on the given criteria. |
|
||||||
|
|
||||||
:param commands: The commands to be filtered. |
|
||||||
:type commands: list |
|
||||||
|
|
||||||
:param environments: Environment names to be matched. |
|
||||||
:type environments: list[str] |
|
||||||
|
|
||||||
:param tags: Tag names to be matched. |
|
||||||
:type tags: list[str] |
|
||||||
|
|
||||||
""" |
|
||||||
filtered = list() |
|
||||||
for command in commands: |
|
||||||
if environments is not None and len(command.environments) > 0: |
|
||||||
if not any_list_item(environments, command.environments): |
|
||||||
continue |
|
||||||
|
|
||||||
if tags is not None: |
|
||||||
if not any_list_item(tags, command.tags): |
|
||||||
continue |
|
||||||
|
|
||||||
filtered.append(command) |
|
||||||
|
|
||||||
return filtered |
|
||||||
|
|
||||||
|
|
||||||
def load_commands(path, filters=None, overlay="ubuntu", **kwargs): |
|
||||||
"""Load commands from a configuration file. |
|
||||||
|
|
||||||
:param path: The path to the configuration file. |
|
||||||
:type path: str |
|
||||||
|
|
||||||
:param filters: Used to filter commands. |
|
||||||
:type filters: dict |
|
||||||
|
|
||||||
:param overlay: The name of the command overlay to apply to generated commands. |
|
||||||
:type overlay: str |
|
||||||
|
|
||||||
:rtype: list[scriptetease.library.commands.base.Command] | scriptetease.library.commands.base.ItemizedCommand] | |
|
||||||
None |
|
||||||
|
|
||||||
:returns: A list of command instances or ``None`` if the configuration could not be loaded. |
|
||||||
|
|
||||||
kwargs are passed to the configuration class for instantiation. |
|
||||||
|
|
||||||
""" |
|
||||||
_config = load_config(path, overlay, **kwargs) |
|
||||||
if _config is None: |
|
||||||
return None |
|
||||||
|
|
||||||
commands = _config.get_commands() |
|
||||||
|
|
||||||
if filters is not None: |
|
||||||
criteria = dict() |
|
||||||
for attribute, values in filters.items(): |
|
||||||
criteria[attribute] = values |
|
||||||
|
|
||||||
commands = filter_commands(commands, **criteria) |
|
||||||
|
|
||||||
return commands |
|
||||||
|
|
||||||
|
|
||||||
def load_config(path, overlay="ubuntu", **kwargs): |
|
||||||
"""Load a command configuration. |
|
||||||
|
|
||||||
:param path: The path to the configuration file. |
|
||||||
:type path: str |
|
||||||
|
|
||||||
:param overlay: The name of the command overlay to apply to generated commands. |
|
||||||
:type overlay: str |
|
||||||
|
|
||||||
:rtype: Config | None |
|
||||||
|
|
||||||
kwargs are passed to the configuration class for instantiation. |
|
||||||
|
|
||||||
""" |
|
||||||
if path.endswith(".ini"): |
|
||||||
_config = Config(path, overlay=overlay, **kwargs) |
|
||||||
# elif path.endswith(".yml"): |
|
||||||
# _config = YAML(path, **kwargs) |
|
||||||
else: |
|
||||||
log.warning("Input file format is not currently supported: %s" % path) |
|
||||||
return None |
|
||||||
|
|
||||||
if not _config.load(): |
|
||||||
log.error("Failed to load config file: %s" % path) |
|
||||||
return None |
|
||||||
|
|
||||||
return _config |
|
||||||
|
|
||||||
|
|
||||||
def load_variables(path, environment=None): |
|
||||||
"""Load variables from a file. |
|
||||||
|
|
||||||
:param path: The path to the file. |
|
||||||
:type path: str |
|
||||||
|
|
||||||
:param environment: Filter variables by the given environment name. |
|
||||||
:type environment: str |
|
||||||
|
|
||||||
:rtype: list[scripttease.parsers.utils.Variable] |
|
||||||
|
|
||||||
""" |
|
||||||
if not os.path.exists(path): |
|
||||||
log.warning("Path to variables file does not exist: %s" % path) |
|
||||||
return list() |
|
||||||
|
|
||||||
if path.endswith(".ini"): |
|
||||||
return _load_variables_ini(path, environment=environment) |
|
||||||
else: |
|
||||||
log.warning("Variable file format is not currently supports: %s" % path) |
|
||||||
return list() |
|
||||||
|
|
||||||
|
|
||||||
def _load_variables_ini(path, environment=None): |
|
||||||
"""Load variables from an INI file. See ``load_variables()``.""" |
|
||||||
|
|
||||||
ini = RawConfigParser() |
|
||||||
ini.read(path) |
|
||||||
|
|
||||||
a = list() |
|
||||||
for section in ini.sections(): |
|
||||||
if ":" in section: |
|
||||||
variable_name, _environment = section.split(":") |
|
||||||
else: |
|
||||||
_environment = None |
|
||||||
variable_name = section |
|
||||||
|
|
||||||
_kwargs = { |
|
||||||
'environment': _environment, |
|
||||||
} |
|
||||||
for key, value in ini.items(section): |
|
||||||
if key == "tags": |
|
||||||
value = split_csv(value) |
|
||||||
else: |
|
||||||
value = smart_cast(value) |
|
||||||
|
|
||||||
_kwargs[key] = value |
|
||||||
|
|
||||||
a.append(Variable(variable_name, **_kwargs)) |
|
||||||
|
|
||||||
if environment is not None: |
|
||||||
b = list() |
|
||||||
for var in a: |
|
||||||
if var.environment and var.environment == environment or var.environment is None: |
|
||||||
b.append(var) |
|
||||||
|
|
||||||
return b |
|
||||||
|
|
||||||
return a |
|
||||||
|
|
||||||
# Classes |
|
||||||
|
|
||||||
|
|
||||||
class Context(object): |
|
||||||
"""A collection of variables.""" |
|
||||||
|
|
||||||
def __init__(self, **kwargs): |
|
||||||
"""Initialize the context. |
|
||||||
|
|
||||||
kwargs are added as variable instances. |
|
||||||
|
|
||||||
""" |
|
||||||
self.variables = dict() |
|
||||||
|
|
||||||
for key, value in kwargs.items(): |
|
||||||
self.add(key, value) |
|
||||||
|
|
||||||
def __getattr__(self, item): |
|
||||||
if item in self.variables: |
|
||||||
return self.variables[item].value |
|
||||||
|
|
||||||
return None |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
return "<%s (%s)>" % (self.__class__.__name__, len(self.variables)) |
|
||||||
|
|
||||||
def add(self, name, value, environment=None, tags=None): |
|
||||||
"""Add a variable to the context. |
|
||||||
|
|
||||||
:param name: The name of the variable. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param value: The value of the variable in this context. |
|
||||||
|
|
||||||
:param environment: The environment name to which the variable applies. ``None`` applies to all environments. |
|
||||||
:type environment: str |
|
||||||
|
|
||||||
:param tags: A list of tags that describe the variable. |
|
||||||
:type tags: list[str] |
|
||||||
|
|
||||||
:rtype: scripttease.parsers.utils.Variable |
|
||||||
|
|
||||||
:raise: RuntimeError |
|
||||||
:raises: ``RuntimeError`` if the variable already exists. |
|
||||||
|
|
||||||
""" |
|
||||||
if name in self.variables: |
|
||||||
raise RuntimeError("Variable already exists: %s" % name) |
|
||||||
|
|
||||||
v = Variable(name, value, environment=environment, tags=tags) |
|
||||||
self.variables[name] = v |
|
||||||
|
|
||||||
return v |
|
||||||
|
|
||||||
def get(self, name, default=None): |
|
||||||
"""Get a the value of the variable from the context. |
|
||||||
|
|
||||||
:param name: The name of the variable. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param default: The default value to return. |
|
||||||
|
|
||||||
""" |
|
||||||
if not self.has(name): |
|
||||||
return default |
|
||||||
|
|
||||||
return self.variables[name].value |
|
||||||
|
|
||||||
def has(self, name): |
|
||||||
"""Indicates whether the named variable exists in this context, and whether the value is not ``None``. |
|
||||||
|
|
||||||
:rtype: bool |
|
||||||
|
|
||||||
""" |
|
||||||
if name not in self.variables: |
|
||||||
return False |
|
||||||
|
|
||||||
return self.variables[name].value is not None |
|
||||||
|
|
||||||
def join(self, variables): |
|
||||||
"""Join a list of variables to the context. |
|
||||||
|
|
||||||
:param variables: the list of variables to be added. |
|
||||||
:type variables: list[scripttease.parsers.utils.Variable] |
|
||||||
|
|
||||||
.. note:: |
|
||||||
This *replaces* a variable if it already exists. |
|
||||||
|
|
||||||
""" |
|
||||||
for v in variables: |
|
||||||
self.variables[v.name] = v |
|
||||||
|
|
||||||
def mapping(self): |
|
||||||
"""Export the context as a dictionary. |
|
||||||
|
|
||||||
:rtype: dict |
|
||||||
|
|
||||||
""" |
|
||||||
values = dict() |
|
||||||
for key, var in self.variables.items(): |
|
||||||
values[key] = var.value or var.default |
|
||||||
|
|
||||||
return values |
|
||||||
|
|
||||||
def merge(self, context): |
|
||||||
"""Merge another context with this one. |
|
||||||
|
|
||||||
:param context: The context to be merged. |
|
||||||
:type context: scripttease.parser.utils.Context |
|
||||||
|
|
||||||
.. note:: |
|
||||||
Variables that exist in the current context are *not* replaced with variables from the provided context. |
|
||||||
|
|
||||||
""" |
|
||||||
for name, var in context.variables.items(): |
|
||||||
if not self.has(name): |
|
||||||
self.variables[name] = var |
|
||||||
|
|
||||||
|
|
||||||
class Variable(object): |
|
||||||
"""Represents a variable to be used in the context of pre-processing a config file.""" |
|
||||||
|
|
||||||
def __init__(self, name, value, **kwargs): |
|
||||||
"""Initialize a variable. |
|
||||||
|
|
||||||
:param name: The variable name. |
|
||||||
:type name: str |
|
||||||
|
|
||||||
:param value: The value of the variable. |
|
||||||
|
|
||||||
kwargs are added as attributes of the instance. |
|
||||||
|
|
||||||
""" |
|
||||||
self.name = name |
|
||||||
self.value = value |
|
||||||
|
|
||||||
kwargs.setdefault("tags", list()) |
|
||||||
self._attributes = kwargs |
|
||||||
|
|
||||||
def __eq__(self, other): |
|
||||||
return self.value == other |
|
||||||
|
|
||||||
def __getattr__(self, item): |
|
||||||
return self._attributes.get(item) |
|
||||||
|
|
||||||
def __repr__(self): |
|
||||||
return "<%s %s>" % (self.__class__.__name__, self.name) |
|
@ -1,5 +1,5 @@ |
|||||||
DATE = "2022-09-24" |
DATE = "2023-03-30" |
||||||
VERSION = "6.8.33" |
VERSION = "7.0.0" |
||||||
MAJOR = 6 |
MAJOR = 7 |
||||||
MINOR = 8 |
MINOR = 0 |
||||||
PATCH = 33 |
PATCH = 0 |
||||||
|
@ -0,0 +1,9 @@ |
|||||||
|
[disable the default site] |
||||||
|
apache.disable_site = default |
||||||
|
|
||||||
|
[enable SSL] |
||||||
|
apache.enable_module = ssl |
||||||
|
|
||||||
|
; see test_lib_factories |
||||||
|
[this will raise a type error because bad_custom_command doesn't accept kwargs] |
||||||
|
bad_custom = path/to/display |
@ -0,0 +1,15 @@ |
|||||||
|
[domain_name |
||||||
|
value = example.com |
||||||
|
|
||||||
|
[domain_tld] |
||||||
|
value = example_com |
||||||
|
|
||||||
|
[debug_enabled:testing] |
||||||
|
value = True |
||||||
|
|
||||||
|
[postgres_version] |
||||||
|
value = 11 |
||||||
|
tags = postgres |
||||||
|
|
||||||
|
[mailgun_domain:live] |
||||||
|
value = mg.example.com |
@ -0,0 +1,9 @@ |
|||||||
|
[disable the default site] |
||||||
|
apache.disable_site = default |
||||||
|
|
||||||
|
[enable SSL] |
||||||
|
apache.enable_module = ssl |
||||||
|
|
||||||
|
; see test_lib_factories |
||||||
|
[this requires that a mapping be passed to the command factory] |
||||||
|
custom = path/to/display |
@ -0,0 +1,3 @@ |
|||||||
|
TESTING = %(testing)s |
||||||
|
TOTAL_TIMES = %(times)s |
||||||
|
|
@ -1,45 +0,0 @@ |
|||||||
import pytest |
|
||||||
from scripttease.library.commands import Command, ItemizedCommand |
|
||||||
from scripttease.factory import Factory |
|
||||||
|
|
||||||
|
|
||||||
class TestFactory(object): |
|
||||||
|
|
||||||
def test_get_command(self): |
|
||||||
f = Factory("ubuntu") |
|
||||||
with pytest.raises(RuntimeError): |
|
||||||
f.get_command("testing") |
|
||||||
|
|
||||||
f = Factory("ubuntu") |
|
||||||
f.load() |
|
||||||
|
|
||||||
# Non-existent command. |
|
||||||
c = f.get_command("nonexistent") |
|
||||||
assert c is None |
|
||||||
|
|
||||||
# A good command with itemized parameters. |
|
||||||
c = f.get_command( |
|
||||||
"pip", |
|
||||||
"$item", |
|
||||||
items=["Pillow", "psycopg2-binary", "django"] |
|
||||||
) |
|
||||||
assert isinstance(c, ItemizedCommand) |
|
||||||
|
|
||||||
# A good, normal command. |
|
||||||
c = f.get_command("pip", "django") |
|
||||||
assert isinstance(c, Command) |
|
||||||
|
|
||||||
# Command exists, but given bad arguments. |
|
||||||
c = f.get_command("pip") |
|
||||||
assert c is None |
|
||||||
|
|
||||||
def test_load(self): |
|
||||||
f = Factory("nonexistent") |
|
||||||
assert f.load() is False |
|
||||||
|
|
||||||
f = Factory("ubuntu") |
|
||||||
assert f.load() is True |
|
||||||
|
|
||||||
def test_repr(self): |
|
||||||
f = Factory("centos") |
|
||||||
assert repr(f) == "<Factory centos>" |
|
@ -0,0 +1,416 @@ |
|||||||
|
from scripttease.lib.commands.base import Command, Content, ItemizedCommand, MultipleCommands, Prompt, Sudo, Template |
||||||
|
from scripttease.lib.commands.python import python_pip |
||||||
|
|
||||||
|
|
||||||
|
class TestCommand(object): |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
c = Command("ls -ls", extra=True) |
||||||
|
assert c.extra is True |
||||||
|
|
||||||
|
def test_get_statement(self): |
||||||
|
c = Command( |
||||||
|
"ls -ls", |
||||||
|
comment="kitchen sink", |
||||||
|
condition="$last_command -eq 0", |
||||||
|
cd="/path/to/project", |
||||||
|
prefix="source python/bin/active", |
||||||
|
register="list_success", |
||||||
|
stop=True, |
||||||
|
sudo="deploy" |
||||||
|
) |
||||||
|
statement = c.get_statement(cd=True) |
||||||
|
assert "( cd" in statement |
||||||
|
assert "sudo" in statement |
||||||
|
assert ")" in statement |
||||||
|
assert "# kitchen sink" in statement |
||||||
|
assert "if [[ $last_command" in statement |
||||||
|
assert "list_success=$?" in statement |
||||||
|
assert "if [[ $list_success" in statement |
||||||
|
|
||||||
|
c = Command( |
||||||
|
"ls -ls", |
||||||
|
stop=True |
||||||
|
) |
||||||
|
statement = c.get_statement() |
||||||
|
assert "if [[ $?" in statement |
||||||
|
|
||||||
|
def test_init(self): |
||||||
|
c = Command("ls -ls", sudo=Sudo(user="deploy")) |
||||||
|
assert isinstance(c.sudo, Sudo) |
||||||
|
assert c.sudo.user == "deploy" |
||||||
|
|
||||||
|
c = Command("ls -ls", sudo="deploy") |
||||||
|
assert isinstance(c.sudo, Sudo) |
||||||
|
assert c.sudo.user == "deploy" |
||||||
|
|
||||||
|
c = Command("ls -ls", sudo=True) |
||||||
|
assert isinstance(c.sudo, Sudo) |
||||||
|
assert c.sudo.user == "root" |
||||||
|
|
||||||
|
c = Command("ls -ls") |
||||||
|
assert isinstance(c.sudo, Sudo) |
||||||
|
assert c.sudo.user == "root" |
||||||
|
assert c.sudo.enabled is False |
||||||
|
|
||||||
|
def test_is_itemized(self): |
||||||
|
c = Command("ls -ls") |
||||||
|
assert c.is_itemized is False |
||||||
|
|
||||||
|
def test_repr(self): |
||||||
|
c = Command("ls -ls", comment="listing") |
||||||
|
assert repr(c) == "<Command listing>" |
||||||
|
|
||||||
|
c = Command("ls -ls") |
||||||
|
assert repr(c) == "<Command>" |
||||||
|
|
||||||
|
|
||||||
|
class TestContent(object): |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
c = Content("testing", extra=True) |
||||||
|
assert c.extra is True |
||||||
|
|
||||||
|
def test_get_image_output(self): |
||||||
|
c = Content("screenshot", caption="this is a test", image="static/images/testing.png") |
||||||
|
assert "# this is a test" in c.get_output("bash") |
||||||
|
assert "# static/images/testing.png" in c.get_output("bash") |
||||||
|
|
||||||
|
c.css = "img right" |
||||||
|
c.height = 100 |
||||||
|
c.width = 150 |
||||||
|
assert '<img src="static/images/testing.png"' in c.get_output("html") |
||||||
|
assert 'alt="this is a test"' in c.get_output("html") |
||||||
|
assert 'class="img right"' in c.get_output("html") |
||||||
|
assert 'height="100"' in c.get_output("html") |
||||||
|
assert 'width="150"' in c.get_output("html") |
||||||
|
|
||||||
|
assert c.get_output("md") == "![this is a test](static/images/testing.png)\n" |
||||||
|
|
||||||
|
assert ".. figure:: static/images/testing.png" in c.get_output("rst") |
||||||
|
assert ":alt: this is a test" in c.get_output("rst") |
||||||
|
assert ":height: 100" in c.get_output("rst") |
||||||
|
assert ":width: 150" in c.get_output("rst") |
||||||
|
|
||||||
|
assert "this is a test: static/images/testing.png" in c.get_output("plain") |
||||||
|
c.caption = None |
||||||
|
assert "static/images/testing.png" in c.get_output("plain") |
||||||
|
|
||||||
|
def test_get_message_output(self): |
||||||
|
c = Content("explain", message="this is a test") |
||||||
|
assert "# this is a test" in c.get_output("bash") |
||||||
|
assert "<p>this is a test" in c.get_output("html") |
||||||
|
assert "this is a test" in c.get_output("md") |
||||||
|
assert "this is a test" in c.get_output("rst") |
||||||
|
assert "this is a test" in c.get_output("plain") |
||||||
|
|
||||||
|
c.heading = "Test" |
||||||
|
assert "# Test: this is a test" in c.get_output("bash") |
||||||
|
assert "<h2>Test</h2>" in c.get_output("html") |
||||||
|
assert "## Test" in c.get_output("md") |
||||||
|
assert "Test" in c.get_output("rst") |
||||||
|
assert "====" in c.get_output("rst") |
||||||
|
assert "***** Test *****" in c.get_output("plain") |
||||||
|
|
||||||
|
def test_get_output(self): |
||||||
|
c = Content("testing") |
||||||
|
assert c.get_output("nonexistent") is None |
||||||
|
|
||||||
|
def test_is_itemized(self): |
||||||
|
c = Content("testing") |
||||||
|
assert c.is_itemized is False |
||||||
|
|
||||||
|
def test_repr(self): |
||||||
|
c = Content("testing") |
||||||
|
assert repr(c) == "<Content testing>" |
||||||
|
|
||||||
|
def test_get_statement(self): |
||||||
|
c = Content("screenshot", caption="this is a test", image="static/images/testing.png") |
||||||
|
assert "# this is a test" in c.get_statement() |
||||||
|
assert "# static/images/testing.png" in c.get_statement() |
||||||
|
|
||||||
|
|
||||||
|
class TestItemizedCommand(object): |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item", extra=True) |
||||||
|
assert c.extra is True |
||||||
|
|
||||||
|
def test_get_commands(self): |
||||||
|
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
||||||
|
commands = c.get_commands() |
||||||
|
for i in commands: |
||||||
|
assert isinstance(i, Command) |
||||||
|
|
||||||
|
def test_get_statement(self): |
||||||
|
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
||||||
|
statement = c.get_statement() |
||||||
|
assert "Pillow" in statement |
||||||
|
assert "psycopg2-binary" in statement |
||||||
|
assert "django" in statement |
||||||
|
|
||||||
|
def test_is_itemized(self): |
||||||
|
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
||||||
|
assert c.is_itemized is True |
||||||
|
|
||||||
|
def test_repr(self): |
||||||
|
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
||||||
|
assert repr(c) == "<ItemizedCommand python_pip>" |
||||||
|
|
||||||
|
|
||||||
|
class TestMultipleCommands(object): |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
commands = [ |
||||||
|
Command("ls -ls", name="run"), |
||||||
|
Command("pip install django", name="pip"), |
||||||
|
Command("touch /tmp/testing.txt", name="touch") |
||||||
|
] |
||||||
|
multiple = MultipleCommands(commands, extra=True) |
||||||
|
assert multiple.extra is True |
||||||
|
|
||||||
|
def test_iter(self): |
||||||
|
commands = [ |
||||||
|
Command("ls -ls", name="run"), |
||||||
|
Command("pip install django", name="pip"), |
||||||
|
Command("touch /tmp/testing.txt", name="touch") |
||||||
|
] |
||||||
|
multiple = MultipleCommands(commands) |
||||||
|
|
||||||
|
count = 0 |
||||||
|
for c in multiple: |
||||||
|
count += 1 |
||||||
|
|
||||||
|
assert count == 3 |
||||||
|
|
||||||
|
def test_repr(self): |
||||||
|
commands = [ |
||||||
|
Command("ls -ls", name="run"), |
||||||
|
Command("pip install django", name="pip"), |
||||||
|
Command("touch /tmp/testing.txt", name="touch") |
||||||
|
] |
||||||
|
multiple = MultipleCommands(commands) |
||||||
|
assert repr(multiple) == "<MultipleCommands run multiple commands>" |
||||||
|
|
||||||
|
def test_get_statement(self): |
||||||
|
commands = [ |
||||||
|
Command("ls -ls", name="run"), |
||||||
|
Command("pip install django", name="pip"), |
||||||
|
Command("touch /tmp/testing.txt", name="touch") |
||||||
|
] |
||||||
|
multiple = MultipleCommands(commands) |
||||||
|
statement = multiple.get_statement() |
||||||
|
assert "ls -ls" in statement |
||||||
|
assert "pip install django" in statement |
||||||
|
assert "touch /tmp/testing.txt" in statement |
||||||
|
|
||||||
|
|
||||||
|
class TestPrompt(object): |
||||||
|
|
||||||
|
def test_get_dialog_statement(self): |
||||||
|
prompt = Prompt("testing", choices=["1", "2", "3"], default="1", dialog=True, help_text="This is a test.", label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "--menu" in statement |
||||||
|
assert "This is a test." in statement |
||||||
|
|
||||||
|
prompt = Prompt("testing", choices="1,2,3", default="1", dialog=True, help_text="This is a test.", label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "--menu" in statement |
||||||
|
|
||||||
|
prompt = Prompt("testing", default="1", dialog=True, help_text="This is a test.", label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "--inputbox" in statement |
||||||
|
assert "This is a test." in statement |
||||||
|
|
||||||
|
prompt = Prompt("testing", default="1", dialog=True, label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "--inputbox" in statement |
||||||
|
|
||||||
|
def test_get_read_statement(self): |
||||||
|
prompt = Prompt("testing", choices="1,2,3", default="1", help_text="This is a test.", label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "select opt in" in statement |
||||||
|
|
||||||
|
prompt = Prompt("testing", default="1", help_text="This is a test.", label="Test") |
||||||
|
statement = prompt.get_statement() |
||||||
|
assert "echo -n" in statement |
||||||
|
|
||||||
|
|
||||||
|
class TestSudo(object): |
||||||
|
|
||||||
|
def test_bool(self): |
||||||
|
s = Sudo() |
||||||
|
assert bool(s) is False |
||||||
|
|
||||||
|
s = Sudo(True) |
||||||
|
assert bool(s) is True |
||||||
|
|
||||||
|
def test_str(self): |
||||||
|
s = Sudo() |
||||||
|
assert str(s) == "" |
||||||
|
|
||||||
|
s = Sudo(True) |
||||||
|
assert str(s) == "sudo -u root" |
||||||
|
|
||||||
|
|
||||||
|
class TestTemplate(object): |
||||||
|
|
||||||
|
def test_get_content(self): |
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/simple.txt", |
||||||
|
"tests/tmp/simple.txt", |
||||||
|
backup=False, |
||||||
|
context=context, |
||||||
|
parser=Template.PARSER_SIMPLE |
||||||
|
) |
||||||
|
content = t.get_content() |
||||||
|
assert "I am testing? yes" in content |
||||||
|
assert "How many times? 123" in content |
||||||
|
|
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/simple.sh.txt", |
||||||
|
"tests/tmp/simple.sh", |
||||||
|
backup=False, |
||||||
|
context=context, |
||||||
|
parser=Template.PARSER_SIMPLE |
||||||
|
) |
||||||
|
content = t.get_content() |
||||||
|
assert "I am testing? yes" in content |
||||||
|
assert "How many times? 123" in content |
||||||
|
|
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/good.j2.txt", |
||||||
|
"tests/tmp/good.txt", |
||||||
|
backup=False, |
||||||
|
context=context |
||||||
|
) |
||||||
|
content = t.get_content() |
||||||
|
assert "I am testing? yes" in content |
||||||
|
assert "How many times? 123" in content |
||||||
|
|
||||||
|
t = Template("tests/examples/templates/nonexistent.j2.txt", "test/tmp/nonexistent.txt") |
||||||
|
assert t.get_content() is None |
||||||
|
|
||||||
|
t = Template("tests/examples/templates/bad.j2.txt", "test/tmp/nonexistent.txt") |
||||||
|
assert t.get_content() is None |
||||||
|
|
||||||
|
context = { |
||||||
|
'testing': True, |
||||||
|
'times': 3, |
||||||
|
} |
||||||
|
t = Template("tests/examples/templates/settings.py", "test/tmp/settings.py", context=context, parser=Template.PARSER_PYTHON) |
||||||
|
content = t.get_content() |
||||||
|
assert "TESTING = True" in content |
||||||
|
assert "TOTAL_TIMES = 3" in content |
||||||
|
|
||||||
|
def test_get_statement(self): |
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/simple.txt", |
||||||
|
"tests/tmp/simple.txt", |
||||||
|
context=context, |
||||||
|
comment="A simple parser example.", |
||||||
|
parser=Template.PARSER_SIMPLE, |
||||||
|
register="template_created", |
||||||
|
stop=True, |
||||||
|
sudo=Sudo(user="root") |
||||||
|
) |
||||||
|
s = t.get_statement() |
||||||
|
assert "I am testing? yes" in s |
||||||
|
assert "How many times? 123" in s |
||||||
|
|
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/simple.sh.txt", |
||||||
|
"tests/tmp/simple.txt", |
||||||
|
context=context, |
||||||
|
parser=Template.PARSER_SIMPLE, |
||||||
|
stop=True, |
||||||
|
sudo="root" |
||||||
|
) |
||||||
|
s = t.get_statement() |
||||||
|
assert "I am testing? yes" in s |
||||||
|
assert "How many times? 123" in s |
||||||
|
|
||||||
|
context = { |
||||||
|
'testing': "yes", |
||||||
|
'times': 123, |
||||||
|
} |
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/good.j2.txt", |
||||||
|
"tests/tmp/good.txt", |
||||||
|
context=context, |
||||||
|
sudo=True |
||||||
|
) |
||||||
|
s = t.get_statement() |
||||||
|
assert "I am testing? yes" in s |
||||||
|
assert "How many times? 123" in s |
||||||
|
|
||||||
|
t = Template( |
||||||
|
"tests/examples/templates/simple.txt", |
||||||
|
"tests/tmp/simple.txt", |
||||||
|
parser="nonexistent" |
||||||
|
) |
||||||
|
assert "# NO CONTENT AVAILABLE" in t.get_statement() |
||||||
|
|
||||||
|
def test_repr(self): |
||||||
|
t = Template("/path/to/template.conf", "/path/to/file.conf") |
||||||
|
assert repr(t) == "<Template /path/to/template.conf>" |
||||||
|
|
||||||
|
def test_get_target_language(self): |
||||||
|
t = Template("/path/to/template.conf", "/path/to/file.conf", lang="c++") |
||||||
|
assert t.get_target_language() == "c++" |
||||||
|
|
||||||
|
t = Template("/path/to/template.conf", "/path/to/file.conf") |
||||||
|
assert t.get_target_language() == "conf" |
||||||
|
|
||||||
|
t = Template("/path/to/template.ini", "/path/to/file.ini") |
||||||
|
assert t.get_target_language() == "ini" |
||||||
|
|
||||||
|
t = Template("/path/to/template.php", "/path/to/file.php") |
||||||
|
assert t.get_target_language() == "php" |
||||||
|
|
||||||
|
t = Template("/path/to/template.py", "/path/to/file.py") |
||||||
|
assert t.get_target_language() == "python" |
||||||
|
|
||||||
|
t = Template("/path/to/template.sh", "/path/to/file.sh") |
||||||
|
assert t.get_target_language() == "bash" |
||||||
|
|
||||||
|
t = Template("/path/to/template.sql", "/path/to/file.sql") |
||||||
|
assert t.get_target_language() == "sql" |
||||||
|
|
||||||
|
t = Template("/path/to/template.yml", "/path/to/file.yml") |
||||||
|
assert t.get_target_language() == "yaml" |
||||||
|
|
||||||
|
t = Template("/path/to/template.txt", "/path/to/file.txt") |
||||||
|
assert t.get_target_language() == "text" |
||||||
|
|
||||||
|
def test_get_template(self): |
||||||
|
t = Template( |
||||||
|
"simple.txt", |
||||||
|
"tests/tmp/simple.txt", |
||||||
|
locations=["tests/examples/templates"] |
||||||
|
) |
||||||
|
assert t.get_template() == "tests/examples/templates/simple.txt" |
||||||
|
|
||||||
|
def test_is_itemized(self): |
||||||
|
t = Template("/path/to/template.conf", "/path/to/file.conf") |
||||||
|
assert t.is_itemized is False |
@ -0,0 +1,46 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.exceptions import InvalidInput |
||||||
|
from scripttease.lib.commands.messages import * |
||||||
|
|
||||||
|
|
||||||
|
def test_dialog(): |
||||||
|
c = dialog("This is a test.", title="Testing") |
||||||
|
s = c.get_statement(include_comment=False) |
||||||
|
# dialog --clear --backtitle "Testing" --msgbox "This is a test." 15 100; clear; |
||||||
|
assert 'dialog --clear --backtitle "Testing"' in s |
||||||
|
assert '--msgbox "This is a test." 15 100; clear;' |
||||||
|
|
||||||
|
|
||||||
|
def test_echo(): |
||||||
|
c = echo("This is a test.") |
||||||
|
s = c.get_statement(include_comment=False) |
||||||
|
assert "echo" in s |
||||||
|
assert "This is a test." in s |
||||||
|
|
||||||
|
|
||||||
|
def test_explain(): |
||||||
|
assert explain("this is a test") is not None |
||||||
|
|
||||||
|
|
||||||
|
def test_screenshot(): |
||||||
|
assert screenshot("static/images/testing.png") is not None |
||||||
|
|
||||||
|
|
||||||
|
def test_slack(): |
||||||
|
with pytest.raises(InvalidInput): |
||||||
|
slack("This is a test.") |
||||||
|
|
||||||
|
c = slack("This is a test.", url="https://example.slack.com/asdf/1234") |
||||||
|
s = c.get_statement(include_comment=False) |
||||||
|
assert "curl -X POST -H 'Content-type: application/json' --data" in s |
||||||
|
assert "This is a test." in s |
||||||
|
assert "https://example.slack.com/asdf/1234" in s |
||||||
|
|
||||||
|
|
||||||
|
def test_twist(): |
||||||
|
with pytest.raises(InvalidInput): |
||||||
|
twist("This is a test.") |
||||||
|
|
||||||
|
c = twist("This is a test.", url="https://example.twist.com/asdf/1234") |
||||||
|
s = c.get_statement(include_comment=False) |
||||||
|
print(s) |
@ -0,0 +1,7 @@ |
|||||||
|
from scripttease.lib.commands.php import * |
||||||
|
|
||||||
|
|
||||||
|
def test_php_module(): |
||||||
|
c = php_module("testing") |
||||||
|
s = c.get_statement() |
||||||
|
assert "phpenmod testing" in s |
@ -0,0 +1,25 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.lib.commands.python import * |
||||||
|
|
||||||
|
|
||||||
|
def test_python_pip(): |
||||||
|
c = python_pip("Pillow") |
||||||
|
assert "pip3 install Pillow" in c.get_statement() |
||||||
|
|
||||||
|
c = python_pip("Pillow", upgrade=True) |
||||||
|
assert "--upgrade" in c.get_statement() |
||||||
|
|
||||||
|
c = python_pip("Pillow", venv="python") |
||||||
|
assert "source python/bin/activate" in c.get_statement() |
||||||
|
|
||||||
|
|
||||||
|
def test_python_pip_file(): |
||||||
|
c = python_pip_file("deploy/packages/testing.pip", venv="python") |
||||||
|
s = c.get_statement() |
||||||
|
assert "pip3 install -r deploy/packages/testing.pip" in s |
||||||
|
assert "source python/bin/activate" in s |
||||||
|
|
||||||
|
|
||||||
|
def test_python_virtual_env(): |
||||||
|
c = python_virtualenv("python") |
||||||
|
assert "virtualenv python" in c.get_statement() |
@ -0,0 +1,37 @@ |
|||||||
|
from scripttease.lib.contexts import * |
||||||
|
|
||||||
|
|
||||||
|
class TestContext(object): |
||||||
|
|
||||||
|
def test_add(self): |
||||||
|
c = Context() |
||||||
|
v = c.add("testing", True) |
||||||
|
assert isinstance(v, Variable) |
||||||
|
|
||||||
|
def test_append(self): |
||||||
|
c = Context() |
||||||
|
v = Variable("testing", True) |
||||||
|
c.append(v) |
||||||
|
assert len(c.variables.keys()) == 1 |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
c = Context() |
||||||
|
c.add("testing", True) |
||||||
|
assert c.testing.value is True |
||||||
|
|
||||||
|
def test_mapping(self): |
||||||
|
c = Context() |
||||||
|
c.add("testing", True) |
||||||
|
assert 'testing' in c.mapping() |
||||||
|
|
||||||
|
|
||||||
|
class TestVariable(object): |
||||||
|
|
||||||
|
def test_getattr(self): |
||||||
|
tags = ["this", "is", "a", "test"] |
||||||
|
v = Variable("testing", True, tags=tags) |
||||||
|
assert v.tags == tags |
||||||
|
|
||||||
|
def test_str(self): |
||||||
|
v = Variable("testing", True) |
||||||
|
assert str(v) == "True" |
@ -0,0 +1,50 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.exceptions import InvalidInput |
||||||
|
from scripttease.lib.commands.base import Command |
||||||
|
from scripttease.lib.loaders import INILoader |
||||||
|
from scripttease.lib.factories import * |
||||||
|
|
||||||
|
|
||||||
|
def bad_custom_command(path): |
||||||
|
# this will fail because the function doesn't except kwargs. |
||||||
|
pass |
||||||
|
|
||||||
|
|
||||||
|
def custom_command(path, **kwargs): |
||||||
|
return Command("ls -ls %s" % path, name="custom", **kwargs) |
||||||
|
|
||||||
|
|
||||||
|
def test_command_factory(): |
||||||
|
ini = INILoader("tests/examples/kitchen_sink.ini") |
||||||
|
assert command_factory(ini, profile="nonexistent") is None |
||||||
|
|
||||||
|
ini = INILoader("tests/examples/kitchen_sink.ini") |
||||||
|
ini.load() |
||||||
|
commands = command_factory(ini) |
||||||
|
assert len(commands) == 48 |
||||||
|
|
||||||
|
ini = INILoader("tests/examples/bad_command.ini") |
||||||
|
ini.load() |
||||||
|
commands = command_factory(ini) |
||||||
|
assert len(commands) == 0 |
||||||
|
|
||||||
|
ini = INILoader("tests/examples/apache_examples.ini") |
||||||
|
ini.load() |
||||||
|
commands = command_factory(ini) |
||||||
|
assert len(commands) == 2 |
||||||
|
|
||||||
|
# This should result in no commands because CentOS doesn't have enable/disable site or module. |
||||||
|
ini = INILoader("tests/examples/apache_examples.ini") |
||||||
|
ini.load() |
||||||
|
commands = command_factory(ini, profile="centos") |
||||||
|
assert len(commands) == 0 |
||||||
|
|
||||||
|
ini = INILoader("tests/examples/custom_example.ini") |
||||||
|
ini.load() |
||||||
|
commands = command_factory(ini, mappings={'custom': custom_command}) |
||||||
|
assert len(commands) == 3 |
||||||
|
|
||||||
|
ini = INILoader("tests/examples/bad_custom_example.ini") |
||||||
|
ini.load() |
||||||
|
with pytest.raises(InvalidInput): |
||||||
|
command_factory(ini, mappings={'bad_custom': bad_custom_command}) |
@ -0,0 +1,83 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.exceptions import InvalidInput |
||||||
|
from scripttease.lib.contexts import Context |
||||||
|
from scripttease.lib.loaders.base import * |
||||||
|
|
||||||
|
|
||||||
|
def test_load_variables(): |
||||||
|
vars = load_variables("nonexistent.ini") |
||||||
|
assert len(vars) == 0 |
||||||
|
|
||||||
|
vars = load_variables("tests/examples/bad_variables.ini") |
||||||
|
assert len(vars) == 0 |
||||||
|
|
||||||
|
vars = load_variables("tests/examples/variables.ini") |
||||||
|
assert len(vars) == 5 |
||||||
|
|
||||||
|
vars = load_variables("tests/examples/variables.ini", env="testing") |
||||||
|
assert len(vars) == 4 |
||||||
|
|
||||||
|
vars = load_variables("tests/examples/variables.ini", env="live") |
||||||
|
assert len(vars) == 4 |
||||||
|
|
||||||
|
|
||||||
|
class TestBaseLoader(object): |
||||||
|
|
||||||
|
def test_get_context(self): |
||||||
|
c = Context() |
||||||
|
c.add("testing", True) |
||||||
|
o = BaseLoader("path/does/not/matter.txt", context=c) |
||||||
|
assert 'testing' in o.get_context() |
||||||
|
|
||||||
|
def test_get_key_value(self): |
||||||
|
o = BaseLoader("path/does/not/matter.txt") |
||||||
|
|
||||||
|
key, value = o._get_key_value("env", ["development", "testing"]) |
||||||
|
assert value == ["development", "testing"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("env", "testing") |
||||||
|
assert value == ["testing"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("env", "development, testing") |
||||||
|
assert value == ["development", "testing"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("func", "test_function") |
||||||
|
assert key == "function" |
||||||
|
assert value == "test_function" |
||||||
|
|
||||||
|
key, value = o._get_key_value("groups", ["one", "two", "three"]) |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("groups", "one, two, three") |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("items", ["one", "two", "three"]) |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("items", "one, two, three") |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("tags", ["one", "two", "three"]) |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("tags", "one, two, three") |
||||||
|
assert value == ["one", "two", "three"] |
||||||
|
|
||||||
|
key, value = o._get_key_value("testing", "1") |
||||||
|
assert value == 1 |
||||||
|
|
||||||
|
def test_load(self): |
||||||
|
o = BaseLoader("path/does/not/matter.txt") |
||||||
|
with pytest.raises(NotImplementedError): |
||||||
|
o.load() |
||||||
|
|
||||||
|
def test_read_file(self): |
||||||
|
c = Context() |
||||||
|
c.add("domain_tld", "example_app") |
||||||
|
o = BaseLoader("tests/examples/template_example.ini", context=c) |
||||||
|
assert "example_app" in o.read_file() |
||||||
|
|
||||||
|
c = Context() |
||||||
|
c.add("domain_tld", "example_app") |
||||||
|
o = BaseLoader("tests/examples/bad_template_example.ini", context=c) |
||||||
|
assert o.read_file() is None |
@ -0,0 +1,23 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.exceptions import InvalidInput |
||||||
|
from scripttease.lib.contexts import Context |
||||||
|
from scripttease.lib.loaders import INILoader |
||||||
|
|
||||||
|
|
||||||
|
class TestINILoader(object): |
||||||
|
|
||||||
|
def test_load(self): |
||||||
|
o = INILoader("nonexistent.ini") |
||||||
|
assert o.load() is False |
||||||
|
|
||||||
|
# ConfigParser raises InterpolationSyntaxError if context is not provided. |
||||||
|
c = Context() |
||||||
|
c.add("testing", True) |
||||||
|
o = INILoader("tests/examples/bad_template_example.ini", context=c) |
||||||
|
assert o.load() is False |
||||||
|
|
||||||
|
o = INILoader("tests/examples/python_examples.ini") |
||||||
|
assert o.load() is True |
||||||
|
|
||||||
|
o = INILoader('tests/examples/bad_examples.ini') |
||||||
|
assert o.load() is False |
@ -0,0 +1,4 @@ |
|||||||
|
import pytest |
||||||
|
from scripttease.exceptions import InvalidInput |
||||||
|
from scripttease.lib.loaders import YMLLoader |
||||||
|
|
@ -1,130 +0,0 @@ |
|||||||
from scripttease.library.commands.base import Command, ItemizedCommand, Sudo |
|
||||||
from scripttease.library.overlays.common import python_pip |
|
||||||
|
|
||||||
|
|
||||||
class TestCommand(object): |
|
||||||
|
|
||||||
def test_getattr(self): |
|
||||||
c = Command("ls -ls", extra=True) |
|
||||||
assert c.extra is True |
|
||||||
|
|
||||||
def test_get_statement(self): |
|
||||||
c = Command( |
|
||||||
"ls -ls", |
|
||||||
comment="kitchen sink", |
|
||||||
condition="$last_command -eq 0", |
|
||||||
cd="/path/to/project", |
|
||||||
prefix="source python/bin/active", |
|
||||||
register="list_success", |
|
||||||
stop=True, |
|
||||||
sudo="deploy" |
|
||||||
) |
|
||||||
statement = c.get_statement(cd=True) |
|
||||||
assert "( cd" in statement |
|
||||||
assert "sudo" in statement |
|
||||||
assert ")" in statement |
|
||||||
assert "# kitchen sink" in statement |
|
||||||
assert "if [[ $last_command" in statement |
|
||||||
assert "list_success=$?" in statement |
|
||||||
assert "if [[ $list_success" in statement |
|
||||||
|
|
||||||
c = Command( |
|
||||||
"ls -ls", |
|
||||||
stop=True |
|
||||||
) |
|
||||||
statement = c.get_statement() |
|
||||||
assert "if [[ $?" in statement |
|
||||||
|
|
||||||
def test_has_attribute(self): |
|
||||||
c = Command("ls -ls") |
|
||||||
assert c.has_attribute("testing") is False |
|
||||||
|
|
||||||
def test_init(self): |
|
||||||
c = Command("ls -ls", sudo=Sudo(user="deploy")) |
|
||||||
assert isinstance(c.sudo, Sudo) |
|
||||||
assert c.sudo.user == "deploy" |
|
||||||
|
|
||||||
c = Command("ls -ls", sudo="deploy") |
|
||||||
assert isinstance(c.sudo, Sudo) |
|
||||||
assert c.sudo.user == "deploy" |
|
||||||
|
|
||||||
c = Command("ls -ls", sudo=True) |
|
||||||
assert isinstance(c.sudo, Sudo) |
|
||||||
assert c.sudo.user == "root" |
|
||||||
|
|
||||||
c = Command("ls -ls") |
|
||||||
assert isinstance(c.sudo, Sudo) |
|
||||||
assert c.sudo.user == "root" |
|
||||||
assert c.sudo.enabled is False |
|
||||||
|
|
||||||
def test_is_itemized(self): |
|
||||||
c = Command("ls -ls") |
|
||||||
assert c.is_itemized is False |
|
||||||
|
|
||||||
def test_repr(self): |
|
||||||
c = Command("ls -ls", comment="listing") |
|
||||||
assert repr(c) == "<Command listing>" |
|
||||||
|
|
||||||
c = Command("ls -ls") |
|
||||||
assert repr(c) == "<Command>" |
|
||||||
|
|
||||||
def test_set_attribute(self): |
|
||||||
c = Command("ls -ls") |
|
||||||
assert c.testing is None |
|
||||||
c.set_attribute("testing", True) |
|
||||||
assert c.testing is True |
|
||||||
|
|
||||||
|
|
||||||
class TestItemizedCommand(object): |
|
||||||
|
|
||||||
def test_getattr(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item", extra=True) |
|
||||||
assert c.extra is True |
|
||||||
|
|
||||||
def test_get_commands(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
commands = c.get_commands() |
|
||||||
for i in commands: |
|
||||||
assert isinstance(i, Command) |
|
||||||
|
|
||||||
def test_get_statement(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
statement = c.get_statement() |
|
||||||
assert "Pillow" in statement |
|
||||||
assert "psycopg2-binary" in statement |
|
||||||
assert "django" in statement |
|
||||||
|
|
||||||
def test_has_attribute(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
assert c.has_attribute("testing") is False |
|
||||||
|
|
||||||
def test_is_itemized(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
assert c.is_itemized is True |
|
||||||
|
|
||||||
def test_repr(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
assert repr(c) == "<ItemizedCommand python_pip>" |
|
||||||
|
|
||||||
def test_set_attribute(self): |
|
||||||
c = ItemizedCommand(python_pip, ["Pillow", "psycopg2-binary", "django"], "$item") |
|
||||||
assert c.testing is None |
|
||||||
c.set_attribute("testing", True) |
|
||||||
assert c.testing is True |
|
||||||
|
|
||||||
|
|
||||||
class TestSudo(object): |
|
||||||
|
|
||||||
def test_bool(self): |
|
||||||
s = Sudo() |
|
||||||
assert bool(s) is False |
|
||||||
|
|
||||||
s = Sudo(True) |
|
||||||
assert bool(s) is True |
|
||||||
|
|
||||||
def test_str(self): |
|
||||||
s = Sudo() |
|
||||||
assert str(s) == "" |
|
||||||
|
|
||||||
s = Sudo(True) |
|
||||||
assert str(s) == "sudo -u root" |
|
@ -1,113 +0,0 @@ |
|||||||
from scripttease.library.commands.base import Command, ItemizedCommand, Sudo |
|
||||||
from scripttease.library.commands.templates import Template |
|
||||||
|
|
||||||
|
|
||||||
class TestTemplate(object): |
|
||||||
|
|
||||||
def test_get_content(self): |
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/simple.txt", |
|
||||||
"tests/tmp/simple.txt", |
|
||||||
backup=False, |
|
||||||
context=context, |
|
||||||
parser=Template.PARSER_SIMPLE |
|
||||||
) |
|
||||||
content = t.get_content() |
|
||||||
assert "I am testing? yes" in content |
|
||||||
assert "How many times? 123" in content |
|
||||||
|
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/simple.sh.txt", |
|
||||||
"tests/tmp/simple.sh", |
|
||||||
backup=False, |
|
||||||
context=context, |
|
||||||
parser=Template.PARSER_SIMPLE |
|
||||||
) |
|
||||||
content = t.get_content() |
|
||||||
assert "I am testing? yes" in content |
|
||||||
assert "How many times? 123" in content |
|
||||||
|
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/good.j2.txt", |
|
||||||
"tests/tmp/good.txt", |
|
||||||
backup=False, |
|
||||||
context=context |
|
||||||
) |
|
||||||
content = t.get_content() |
|
||||||
assert "I am testing? yes" in content |
|
||||||
assert "How many times? 123" in content |
|
||||||
|
|
||||||
t = Template("tests/examples/templates/nonexistent.j2.txt", "test/tmp/nonexistent.txt") |
|
||||||
assert t.get_content() is None |
|
||||||
|
|
||||||
t = Template("tests/examples/templates/bad.j2.txt", "test/tmp/nonexistent.txt") |
|
||||||
assert t.get_content() is None |
|
||||||
|
|
||||||
def test_get_statement(self): |
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/simple.txt", |
|
||||||
"tests/tmp/simple.txt", |
|
||||||
context=context, |
|
||||||
parser=Template.PARSER_SIMPLE |
|
||||||
) |
|
||||||
s = t.get_statement() |
|
||||||
assert "I am testing? yes" in s |
|
||||||
assert "How many times? 123" in s |
|
||||||
|
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/simple.sh.txt", |
|
||||||
"tests/tmp/simple.txt", |
|
||||||
context=context, |
|
||||||
parser=Template.PARSER_SIMPLE |
|
||||||
) |
|
||||||
s = t.get_statement() |
|
||||||
assert "I am testing? yes" in s |
|
||||||
assert "How many times? 123" in s |
|
||||||
|
|
||||||
context = { |
|
||||||
'testing': "yes", |
|
||||||
'times': 123, |
|
||||||
} |
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/good.j2.txt", |
|
||||||
"tests/tmp/good.txt", |
|
||||||
context=context |
|
||||||
) |
|
||||||
s = t.get_statement() |
|
||||||
assert "I am testing? yes" in s |
|
||||||
assert "How many times? 123" in s |
|
||||||
|
|
||||||
t = Template( |
|
||||||
"tests/examples/templates/simple.txt", |
|
||||||
"tests/tmp/simple.txt", |
|
||||||
parser="nonexistent" |
|
||||||
) |
|
||||||
assert t.get_statement() is None |
|
||||||
|
|
||||||
def test_get_template(self): |
|
||||||
t = Template( |
|
||||||
"simple.txt", |
|
||||||
"tests/tmp/simple.txt", |
|
||||||
locations=["tests/examples/templates"] |
|
||||||
) |
|
||||||
assert t.get_template() == "tests/examples/templates/simple.txt" |
|
@ -1,57 +0,0 @@ |
|||||||
import pytest |
|
||||||
from scripttease.library.overlays.common import * |
|
||||||
|
|
||||||
|
|
||||||
def test_python_pip(): |
|
||||||
c = python_pip("Pillow") |
|
||||||
assert "pip3 install Pillow" in c.get_statement() |
|
||||||
|
|
||||||
c = python_pip("Pillow", upgrade=True) |
|
||||||
assert "--upgrade" in c.get_statement() |
|
||||||
|
|
||||||
c = python_pip("Pillow", venv="python") |
|
||||||
assert "source python/bin/activate" in c.get_statement() |
|
||||||
|
|
||||||
|
|
||||||
def test_python_virtual_env(): |
|
||||||
c = python_virtualenv("python") |
|
||||||
assert "virtualenv python" in c.get_statement() |
|
||||||
|
|
||||||
|
|
||||||
def test_run(): |
|
||||||
c = run("ls -ls") |
|
||||||
assert "ls -ls" in c.get_statement() |
|
||||||
|
|
||||||
|
|
||||||
def test_slack(): |
|
||||||
with pytest.raises(ValueError): |
|
||||||
slack("This is a test.") |
|
||||||
|
|
||||||
c = slack("This is a test.", url="https://example.slack.com/asdf/1234") |
|
||||||
s = c.get_statement(suppress_comment=True) |
|
||||||
assert "curl -X POST -H 'Content-type: application/json' --data" in s |
|
||||||
assert "This is a test." in s |
|
||||||
assert "https://example.slack.com/asdf/1234" in s |
|
||||||
|
|
||||||
|
|
||||||
def test_twist(): |
|
||||||
with pytest.raises(ValueError): |
|
||||||
twist("This is a test.") |
|
||||||
|
|
||||||
c = twist("This is a test.", url="https://example.twist.com/asdf/1234") |
|
||||||
s = c.get_statement(suppress_comment=True) |
|
||||||
print(s) |
|
||||||
|
|
||||||
|
|
||||||
def test_udf(): |
|
||||||
c = udf("testing") |
|
||||||
s = c.get_statement() |
|
||||||
assert s == '# <UDF name="testing" label="Testing" />' |
|
||||||
|
|
||||||
c = udf("testing", default="yes") |
|
||||||
s = c.get_statement() |
|
||||||
assert s == '# <UDF name="testing" label="Testing" default="yes" />' |
|
||||||
|
|
||||||
c = udf("testing", example="example.com") |
|
||||||
s = c.get_statement() |
|
||||||
assert s == '# <UDF name="testing" label="Testing" example="example.com" />' |
|
@ -1,31 +0,0 @@ |
|||||||
from scripttease.library.commands import Command, ItemizedCommand |
|
||||||
from scripttease.library.overlays.posix import Function |
|
||||||
from scripttease.library.scripts import Script |
|
||||||
|
|
||||||
|
|
||||||
class TestScript(object): |
|
||||||
|
|
||||||
def test_append(self): |
|
||||||
s = Script("testing") |
|
||||||
s.append(Command("ls -ls", comment="list some stuff")) |
|
||||||
s.append(Command("touch /path/to/file.txt", comment="touch a file")) |
|
||||||
s.append(Command("ln -s /path/to/file.txt", comment="link to a file")) |
|
||||||
|
|
||||||
assert len(s.commands) == 3 |
|
||||||
|
|
||||||
def test_to_string(self): |
|
||||||
s = Script("testing") |
|
||||||
s.append(Command("ls -ls", comment="list some stuff")) |
|
||||||
s.append(Command("touch /path/to/file.txt", comment="touch a file")) |
|
||||||
s.append(Command("ln -s /path/to/file.txt", comment="link to a file")) |
|
||||||
|
|
||||||
s.functions = list() |
|
||||||
s.functions.append(Function("testing")) |
|
||||||
|
|
||||||
output = s.to_string() |
|
||||||
assert output == str(s) |
|
||||||
|
|
||||||
assert "ls -ls" in output |
|
||||||
assert "touch /path/to/file.txt" in output |
|
||||||
assert "ln -s /path/to/file.txt" in output |
|
||||||
assert "function testing()" in output |
|
@ -1,30 +0,0 @@ |
|||||||
import pytest |
|
||||||
from scripttease.library.overlays.posix import touch, Function |
|
||||||
from scripttease.library.scripts import Script |
|
||||||
# from scripttease.parsers import filter_commands, load_commands |
|
||||||
from scripttease.parsers.base import Parser |
|
||||||
|
|
||||||
|
|
||||||
class TestParser(object): |
|
||||||
|
|
||||||
def test_as_script(self): |
|
||||||
p = Parser("/path/to/nonexistent.txt") |
|
||||||
assert isinstance(p.as_script(), Script) |
|
||||||
|
|
||||||
# def test_get_commands(self): |
|
||||||
# pass |
|
||||||
# |
|
||||||
def test_get_functions(self): |
|
||||||
parser = Parser("/it/does/not/matter.ini") |
|
||||||
|
|
||||||
command = touch("/path/to/file.txt", function="testing") |
|
||||||
function = Function("testing") |
|
||||||
parser._commands.append(command) |
|
||||||
parser._functions.append(function) |
|
||||||
|
|
||||||
assert len(parser.get_functions()) == 1 |
|
||||||
|
|
||||||
def test_load(self): |
|
||||||
p = Parser("/path/to/nonexistent.txt") |
|
||||||
with pytest.raises(NotImplementedError): |
|
||||||
p.load() |
|
@ -1,48 +0,0 @@ |
|||||||
import pytest |
|
||||||
from scripttease.parsers.ini import Config |
|
||||||
|
|
||||||
|
|
||||||
class TestConfig(object): |
|
||||||
|
|
||||||
def test_get_commands(self): |
|
||||||
c = Config("tests/examples/kitchen_sink.ini") |
|
||||||
assert c.load() is True |
|
||||||
|
|
||||||
assert len(c.get_commands()) > 0 |
|
||||||
|
|
||||||
def test_get_functions(self): |
|
||||||
c = Config("tests/examples/kitchen_sink.ini") |
|
||||||
assert c.load() is True |
|
||||||
|
|
||||||
assert len(c.get_functions()) > 0 |
|
||||||
|
|
||||||
def test_load(self): |
|
||||||
c = Config("nonexistent.ini") |
|
||||||
assert c.load() is False |
|
||||||
|
|
||||||
c = Config("tests/examples/python_examples.ini", overlay="nonexistent") |
|
||||||
assert c.load() is False |
|
||||||
|
|
||||||
c = Config("tests/examples/bad_examples.ini") |
|
||||||
assert c.load() is False |
|
||||||
|
|
||||||
c = Config("tests/examples/kitchen_sink.ini") |
|
||||||
assert c.load() is True |
|
||||||
|
|
||||||
c = Config("tests/examples/kitchen_sink.ini", context={'testing': "yes"}) |
|
||||||
assert c.load() is True |
|
||||||
|
|
||||||
c = Config("tests/examples/bad_command.ini") |
|
||||||
assert c.load() is False |
|
||||||
|
|
||||||
context = { |
|
||||||
'domain_tld': "example_com", |
|
||||||
} |
|
||||||
c = Config("tests/examples/template_example.ini", context=context) |
|
||||||
assert c.load() is True |
|
||||||
|
|
||||||
context = { |
|
||||||
'domain_tld': "example_com", |
|
||||||
} |
|
||||||
c = Config("tests/examples/bad_template_example.ini", context=context) |
|
||||||
assert c.load() is False |
|
@ -1,133 +0,0 @@ |
|||||||
import os |
|
||||||
import pytest |
|
||||||
from scripttease.library.commands import Command, ItemizedCommand |
|
||||||
from scripttease.parsers.utils import * |
|
||||||
|
|
||||||
|
|
||||||
def test_filter_commands(): |
|
||||||
commands = [ |
|
||||||
Command("apt-get install apache2 -y", environments=["base"], tags=["web"]), |
|
||||||
Command("apt-get install apache-top -y", environments=["live"], tags=["web"]), |
|
||||||
Command("pip install django-debug-toolbar", environments=["development"], tags=["django"]), |
|
||||||
Command("pip install django", environments=["base"], tags=["django"]), |
|
||||||
] |
|
||||||
f1 = filter_commands(commands, environments=["base", "live"]) |
|
||||||
assert len(f1) == 3 |
|
||||||
|
|
||||||
f2 = filter_commands(commands, tags=["django"]) |
|
||||||
assert len(f2) == 2 |
|
||||||
|
|
||||||
f3 = filter_commands(commands, environments=["base", "development"]) |
|
||||||
assert len(f3) == 3 |
|
||||||
|
|
||||||
f4 = filter_commands(commands, environments=["base"], tags=["web"]) |
|
||||||
assert len(f4) == 1 |
|
||||||
|
|
||||||
|
|
||||||
def test_load_commands(): |
|
||||||
commands = load_commands("nonexistent.xml") |
|
||||||
assert commands is None |
|
||||||
|
|
||||||
commands = load_commands("nonexistent.ini") |
|
||||||
assert commands is None |
|
||||||
|
|
||||||
commands = load_commands("tests/examples/bad_examples.ini") |
|
||||||
assert commands is None |
|
||||||
|
|
||||||
commands = load_commands( |
|
||||||
"tests/examples/python_examples.ini", |
|
||||||
filters={ |
|
||||||
'tags': ["python-support"], |
|
||||||
} |
|
||||||
) |
|
||||||
assert len(commands) == 2 |
|
||||||
|
|
||||||
|
|
||||||
def test_load_variables(): |
|
||||||
assert len(load_variables("nonexistent.ini")) == 0 |
|
||||||
|
|
||||||
assert len(load_variables(os.path.join("tests", "examples", "templates", "simple.txt"))) == 0 |
|
||||||
|
|
||||||
variables = load_variables(os.path.join("tests", "examples", "variables.ini")) |
|
||||||
assert len(variables) == 5 |
|
||||||
|
|
||||||
variables = load_variables(os.path.join("tests", "examples", "variables.ini"), environment="testing") |
|
||||||
assert len(variables) == 4 |
|
||||||
|
|
||||||
|
|
||||||
class TestContext(object): |
|
||||||
|
|
||||||
def test_add(self): |
|
||||||
c = Context() |
|
||||||
c.add("testing", True) |
|
||||||
assert len(c.variables) == 1 |
|
||||||
|
|
||||||
c.add("also_testing", False) |
|
||||||
assert len(c.variables) == 2 |
|
||||||
|
|
||||||
assert isinstance(c.add("still_testing", True), Variable) |
|
||||||
|
|
||||||
with pytest.raises(RuntimeError): |
|
||||||
c.add("testing", True) |
|
||||||
|
|
||||||
def test_get(self): |
|
||||||
c = Context(testing=True) |
|
||||||
assert c.get("testing") is True |
|
||||||
assert c.get("nonexistent") is None |
|
||||||
assert c.get("nonexistent", default=True) is True |
|
||||||
|
|
||||||
def test_getattr(self): |
|
||||||
c = Context(testing=True) |
|
||||||
assert c.testing is True |
|
||||||
assert c.nonexistent is None |
|
||||||
|
|
||||||
def test_has(self): |
|
||||||
c = Context(testing=True) |
|
||||||
assert c.has("testing") is True |
|
||||||
assert c.has("nonexistent") is False |
|
||||||
|
|
||||||
def test_init(self): |
|
||||||
c = Context(testing=True, also_testing=123) |
|
||||||
assert len(c.variables) == 2 |
|
||||||
|
|
||||||
def test_join(self): |
|
||||||
c = Context(testing=True) |
|
||||||
|
|
||||||
variables = [ |
|
||||||
Variable("testing", True), |
|
||||||
Variable("also_testing", True), |
|
||||||
] |
|
||||||
c.join(variables) |
|
||||||
assert len(c.variables) == 2 |
|
||||||
|
|
||||||
def test_mapping(self): |
|
||||||
c = Context(testing=True, also_testing=False, still_testing=True) |
|
||||||
assert type(c.mapping()) is dict |
|
||||||
assert len(c.mapping()) == 3 |
|
||||||
|
|
||||||
def test_merge(self): |
|
||||||
c1 = Context(testing=True, also_testing=False) |
|
||||||
c2 = Context(still_testing=True) |
|
||||||
c1.merge(c2) |
|
||||||
assert len(c1.variables) == 3 |
|
||||||
|
|
||||||
def test_repr(self): |
|
||||||
c = Context(testing=True, also_testing=False, still_testing=True) |
|
||||||
assert repr(c) == "<Context (3)>" |
|
||||||
|
|
||||||
|
|
||||||
class TestVariable(object): |
|
||||||
|
|
||||||
def test_eq(self): |
|
||||||
var = Variable("testing", True) |
|
||||||
assert var == True |
|
||||||
|
|
||||||
def test_getattr(self): |
|
||||||
var = Variable("testing", True, one="a", two="b") |
|
||||||
assert var.one == "a" |
|
||||||
assert var.two == "b" |
|
||||||
assert var.three is None |
|
||||||
|
|
||||||
def test_repr(self): |
|
||||||
var = Variable("testing", True) |
|
||||||
assert repr(var) == "<Variable testing>" |
|
Loading…
Reference in new issue