parent
6fec406d04
commit
b6a7b9dbbc
50 changed files with 2442 additions and 987 deletions
@ -1,8 +1,9 @@ |
||||
[run] |
||||
omit = |
||||
docs/* |
||||
scripttease/cli/__init__.py |
||||
scripttease/cli/* |
||||
sandbox |
||||
setup.py |
||||
tests/* |
||||
tmp/* |
||||
tmp.* |
||||
|
@ -1 +1 @@ |
||||
5.8.18-d |
||||
6.0.0-d |
@ -0,0 +1,204 @@ |
||||
# Imports |
||||
|
||||
from argparse import ArgumentParser, RawDescriptionHelpFormatter |
||||
from superpython.logging import LoggingHelper |
||||
from ..constants import LOGGER_NAME |
||||
from . import initialize |
||||
from . import subcommands |
||||
|
||||
DEBUG = 10 |
||||
|
||||
logging = LoggingHelper(colorize=True, name=LOGGER_NAME) |
||||
log = logging.setup() |
||||
|
||||
# Commands |
||||
|
||||
|
||||
def main_command(): |
||||
"""Process script configurations.""" |
||||
|
||||
__author__ = "Shawn Davis <shawn@develmaycare.com>" |
||||
__date__ = "2020-07-21" |
||||
__help__ = """NOTES |
||||
|
||||
This command is used to parse configuration files and output the commands. |
||||
|
||||
""" |
||||
__version__ = "0.10.0-d" |
||||
|
||||
# Main argument parser from which sub-commands are created. |
||||
parser = ArgumentParser(description=__doc__, epilog=__help__, formatter_class=RawDescriptionHelpFormatter) |
||||
|
||||
parser.add_argument( |
||||
"path", |
||||
default="commands.ini", |
||||
nargs="?", |
||||
help="The path to the configuration file." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-c", |
||||
"--color", |
||||
action="store_true", |
||||
dest="color_enabled", |
||||
help="Enable code highlighting for terminal output." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-C=", |
||||
"--context=", |
||||
action="append", |
||||
dest="variables", |
||||
help="Context variables for use in pre-parsing the config and templates. In the form of: name:value" |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-d", |
||||
"--docs", |
||||
action="store_true", |
||||
dest="docs_enabled", |
||||
help="Output documentation instead of code." |
||||
) |
||||
|
||||
# parser.add_argument( |
||||
# "-d=", |
||||
# "--docs=", |
||||
# choices=["html", "markdown", "plain", "rst"], |
||||
# dest="docs_enabled", |
||||
# help="Output documentation instead of code." |
||||
# ) |
||||
|
||||
parser.add_argument( |
||||
"-D", |
||||
"--debug", |
||||
action="store_true", |
||||
dest="debug_enabled", |
||||
help="Enable debug output." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-f=", |
||||
"--filter=", |
||||
action="append", |
||||
dest="filters", |
||||
help="Filter the commands in the form of: attribute:value" |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-O=", |
||||
"--option=", |
||||
action="append", |
||||
dest="options", |
||||
help="Common command options in the form of: name:value" |
||||
) |
||||
|
||||
# parser.add_argument( |
||||
# "-O=", |
||||
# "--output=", |
||||
# # default=os.path.join("prototype", "output"), |
||||
# dest="output_path", |
||||
# help="Output to the given directory. Defaults to ./prototype/output/" |
||||
# ) |
||||
|
||||
parser.add_argument( |
||||
"-s", |
||||
"--script", |
||||
action="store_true", |
||||
dest="script_enabled", |
||||
help="Output commands as a script." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-T=", |
||||
"--template-path=", |
||||
action="append", |
||||
dest="template_locations", |
||||
help="The location of template files that may be used with the template command." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-w=", |
||||
"--write=", |
||||
dest="output_file", |
||||
help="Write the output to disk." |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"-V=", |
||||
"--variables-file=", |
||||
dest="variables_file", |
||||
help="Load variables from a file." |
||||
) |
||||
|
||||
# Access to the version number requires special consideration, especially |
||||
# when using sub parsers. The Python 3.3 behavior is different. See this |
||||
# answer: http://stackoverflow.com/questions/8521612/argparse-optional-subparser-for-version |
||||
parser.add_argument( |
||||
"-v", |
||||
action="version", |
||||
help="Show version number and exit.", |
||||
version=__version__ |
||||
) |
||||
|
||||
parser.add_argument( |
||||
"--version", |
||||
action="version", |
||||
help="Show verbose version information and exit.", |
||||
version="%(prog)s" + " %s %s by %s" % (__version__, __date__, __author__) |
||||
) |
||||
|
||||
# Parse arguments. |
||||
args = parser.parse_args() |
||||
|
||||
if args.debug_enabled: |
||||
log.setLevel(DEBUG) |
||||
|
||||
log.debug("Namespace: %s" % args) |
||||
|
||||
# Load context. |
||||
context = dict() |
||||
if args.variables: |
||||
context = initialize.context_from_cli(args.variables) |
||||
|
||||
# Handle filters. |
||||
filters = None |
||||
if args.filters: |
||||
filters = initialize.filters_from_cli(args.filters) |
||||
|
||||
# Handle options. |
||||
options = None |
||||
if args.options: |
||||
options = initialize.options_from_cli(args.options) |
||||
|
||||
if args.variables_file: |
||||
variables = initialize.variable_from_file(args.variables_file) |
||||
if variables: |
||||
context.update(variables) |
||||
|
||||
if args.docs_enabled: |
||||
exit_code = subcommands.output_docs( |
||||
args.path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
elif args.script_enabled: |
||||
exit_code = subcommands.output_script( |
||||
args.path, |
||||
color_enabled=args.color_enabled, |
||||
context=context, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
else: |
||||
exit_code = subcommands.output_commands( |
||||
args.path, |
||||
color_enabled=args.color_enabled, |
||||
context=context, |
||||
filters=filters, |
||||
locations=args.template_locations, |
||||
options=options |
||||
) |
||||
|
||||
exit(exit_code) |
@ -0,0 +1,58 @@ |
||||
# Imports |
||||
|
||||
from configparser import ConfigParser |
||||
import logging |
||||
import os |
||||
from superpython.utils import smart_cast |
||||
from ..constants import LOGGER_NAME |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def context_from_cli(variables): |
||||
context = dict() |
||||
for i in variables: |
||||
key, value = i.split(":") |
||||
context[key] = smart_cast(value) |
||||
|
||||
return context |
||||
|
||||
|
||||
def filters_from_cli(filters): |
||||
_filters = dict() |
||||
for i in filters: |
||||
key, value = i.split(":") |
||||
if key not in filters: |
||||
_filters[key] = list() |
||||
|
||||
_filters[key].append(value) |
||||
|
||||
return _filters |
||||
|
||||
|
||||
def options_from_cli(options): |
||||
_options = dict() |
||||
for i in options: |
||||
key, value = i.split(":") |
||||
_options[key] = smart_cast(value) |
||||
|
||||
return _options |
||||
|
||||
|
||||
def variable_from_file(path): |
||||
if not os.path.exists(path): |
||||
log.warning("Variables file does not exist: %s" % path) |
||||
return None |
||||
|
||||
ini = ConfigParser() |
||||
ini.read(path) |
||||
|
||||
variables = dict() |
||||
for section in ini.sections(): |
||||
for key, value in ini.items(section): |
||||
key = "%s_%s" % (section, key) |
||||
variables[key] = smart_cast(vaue) |
||||
|
||||
return variables |
@ -0,0 +1,76 @@ |
||||
# Imports |
||||
|
||||
from superpython.shell import EXIT |
||||
from superpython.utils import highlight_code |
||||
from ..parsers import load_commands, load_config |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def output_commands(path, color_enabled=False, context=None, filters=None, locations=None, options=None): |
||||
commands = load_commands( |
||||
path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if commands is None: |
||||
return EXIT.ERROR |
||||
|
||||
output = list() |
||||
for command in commands: |
||||
statement = command.get_statement(cd=True) |
||||
if statement is None: |
||||
continue |
||||
|
||||
output.append(statement) |
||||
output.append("") |
||||
|
||||
if color_enabled: |
||||
print(highlight_code("\n".join(output), language="bash")) |
||||
else: |
||||
print("\n".join(output)) |
||||
|
||||
return EXIT.OK |
||||
|
||||
|
||||
def output_docs(path, context=None, filters=None, locations=None, options=None): |
||||
commands = load_commands( |
||||
path, |
||||
context=context, |
||||
filters=filters, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if commands is None: |
||||
return EXIT.ERROR |
||||
|
||||
count = 1 |
||||
output = list() |
||||
for command in commands: |
||||
output.append("%s. %s" % (count, command.comment)) |
||||
count += 1 |
||||
|
||||
print("\n".join(output)) |
||||
|
||||
return EXIT.OK |
||||
|
||||
|
||||
def output_script(path, color_enabled=False, context=None, filters=None, locations=None, options=None): |
||||
config = load_config( |
||||
path, |
||||
context=context, |
||||
locations=locations, |
||||
options=options |
||||
) |
||||
if config is None: |
||||
return EXIT.ERROR |
||||
|
||||
script = config.as_script() |
||||
if color_enabled: |
||||
print(highlight_code(script.to_string(), language="bash")) |
||||
else: |
||||
print(script) |
||||
|
||||
return EXIT.OK |
@ -1,47 +0,0 @@ |
||||
[apache] |
||||
disable_module = a2dismod {{ module_name }} |
||||
disable_site = a2dissite {{ domain_name }}.conf |
||||
enable_module = a2enmod {{ module_name }} |
||||
enable_site = a2ensite {{ domain_name }}.conf |
||||
reload = service apache2 reload |
||||
restart = service apache2 restart |
||||
start = service apache2 start |
||||
stop = service apache2 stop |
||||
test = apachectl configtest |
||||
|
||||
[package_install] |
||||
system = apt-get install -y {{ package_name }} |
||||
pip = pip3 install{% if upgrade %} --upgrade{% endif %} --quiet {{ package_name }} |
||||
|
||||
[package_remove] |
||||
system = apt-get uninstall -y {{ package_name }} |
||||
pip = pip3 uninstall --quiet {{ package_name }} |
||||
|
||||
[system] |
||||
install = apt-get install -y {{ package_name }} |
||||
reboot = reboot |
||||
remove = apt-get uninstall -y {{ package_name }} |
||||
update = apt-get update -y |
||||
upgrade = apt-get upgrade -y |
||||
|
||||
[python] |
||||
virtualenv = virtualenv {{ name }} |
||||
install = pip3 install{% if upgrade %} --upgrade{% endif %} --quiet {{ package_name }} |
||||
remove = pip3 uninstall --quiet {{ package_name }} |
||||
|
||||
[files] |
||||
append = echo "{{ content }}" >> {{ path }} |
||||
chgrp = chgrp{% if recursive %} -R{% endif %} {{ group }} {{ path }} |
||||
chmod = chmod{% if recursive %} -R{% endif %} {{ owner }} {{ path }} |
||||
chown = chown{% if recursive %} -R{% endif %} {{ mode }} {{ path }} |
||||
copy = cp{% if recursive %} -R{% endif %}{% if overwrite %} -n{% endif %} {{ from_path }} {{ to_path }} |
||||
mkdir = mkdir{% if mode %} -m {{ mode }}{% endif %}{% if recursive %} -p{% endif %} {{ path }} |
||||
move = move {{ from_path }} {{ to_path }} |
||||
rename = move {{ from_path }} {{ to_path }} |
||||
remove = rm{% if force %} -f{% endif %}{% if recursive %} -r{% endif %} {{ path }} |
||||
;rsync = ? |
||||
;scopy = ? |
||||
;sed = ? |
||||
symlink = ln -s{% if force %} -f{% endif %} {{ source }} {{ target }} |
||||
touch = touch {{ path }} |
||||
;write = ? |
@ -1,224 +0,0 @@ |
||||
# Imports |
||||
|
||||
from configparser import RawConfigParser |
||||
import os |
||||
from superpython.utils import parse_jinja_string |
||||
from ..constants import PATH_TO_SCRIPT_TEASE |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"Overlay", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class Overlay(object): |
||||
"""An overlay applies commands specific to a given operating system or platform.""" |
||||
|
||||
def __init__(self, name): |
||||
self.is_loaded = False |
||||
self._name = name |
||||
self._path = os.path.join(PATH_TO_SCRIPT_TEASE, "data", "overlays", "%s.ini" % name) |
||||
self._sections = dict() |
||||
|
||||
def __repr__(self): |
||||
return "<%s %s>" % (self.__class__.__name__, self._name) |
||||
|
||||
@property |
||||
def exists(self): |
||||
"""Indicates whether the overlay file exists. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
return os.path.exists(self._path) |
||||
|
||||
def get(self, section, key, **kwargs): |
||||
"""Get the command statement for the given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
kwargs are used to parse the value of the key within the section. |
||||
|
||||
:rtype: str | None |
||||
|
||||
""" |
||||
if not self.has(section, key): |
||||
return None |
||||
|
||||
template = self._sections[section][key] |
||||
|
||||
return parse_jinja_string(template, kwargs) |
||||
|
||||
def has(self, section, key): |
||||
"""Determine whether the overlay contains a given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if section not in self._sections: |
||||
return False |
||||
|
||||
if key not in self._sections[section]: |
||||
return False |
||||
|
||||
return True |
||||
|
||||
def load(self): |
||||
"""Load the overlay. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if not self.exists: |
||||
return False |
||||
|
||||
ini = RawConfigParser() |
||||
ini.read(self._path) |
||||
|
||||
for section in ini.sections(): |
||||
self._sections[section] = dict() |
||||
for key, value in ini.items(section): |
||||
self._sections[section][key] = value |
||||
|
||||
self.is_loaded = True |
||||
return True |
||||
|
||||
def to_mapping(self): |
||||
"""Export the overlay as a dictionary with command names as values. |
||||
|
||||
:rtype: dict |
||||
|
||||
""" |
||||
d = dict() |
||||
for section in self._sections: |
||||
d[section] = list() |
||||
for command_name, statement in self._sections[section].items(): |
||||
d[section].append(command_name) |
||||
|
||||
|
||||
class Overlay2(object): |
||||
"""An overlay applies commands specific to a given operating system or platform.""" |
||||
|
||||
def __init__(self, name): |
||||
self.is_loaded = False |
||||
self._name = name |
||||
self._path = os.path.join(PATH_TO_SCRIPT_TEASE, "data", "overlays", "%s.ini" % name) |
||||
self._sections = dict() |
||||
|
||||
self.exists = os.path.exists(self._path) |
||||
|
||||
def __repr__(self): |
||||
return "<%s %s>" % (self.__class__.__name__, self._name) |
||||
|
||||
def command_exists(self, name): |
||||
"""Determine whether a given command exists. |
||||
|
||||
:param name: The name of the command to check. |
||||
:type name: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
section = None |
||||
if "." in name: |
||||
section, name = name.split(".") |
||||
|
||||
if section is not None: |
||||
if section in self._sections: |
||||
return name in self._sections[section] |
||||
|
||||
for section in self._sections.keys(): |
||||
if name in self._sections[section]: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
def get_statement(self, name, *args, **kwargs): |
||||
pass |
||||
|
||||
def get(self, section, key, **kwargs): |
||||
"""Get the command statement for the given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
kwargs are used to parse the value of the key within the section. |
||||
|
||||
:rtype: str | None |
||||
|
||||
""" |
||||
if not self.has(section, key): |
||||
return None |
||||
|
||||
template = self._sections[section][key] |
||||
|
||||
return parse_jinja_string(template, kwargs) |
||||
|
||||
def has(self, section, key): |
||||
"""Determine whether the overlay contains a given section and key. |
||||
|
||||
:param section: The section name. |
||||
:type section: str |
||||
|
||||
:param key: The key within the section. |
||||
:type key: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if section not in self._sections: |
||||
return False |
||||
|
||||
if key not in self._sections[section]: |
||||
return False |
||||
|
||||
return True |
||||
|
||||
def load(self): |
||||
"""Load the overlay. |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
if not self.exists: |
||||
return False |
||||
|
||||
ini = RawConfigParser() |
||||
ini.read(self._path) |
||||
|
||||
for section in ini.sections(): |
||||
self._sections[section] = dict() |
||||
for command_name, statement_template in ini.items(section): |
||||
self._sections[section][command_name] = statement_template |
||||
|
||||
self.is_loaded = True |
||||
|
||||
return True |
||||
|
||||
def to_mapping(self): |
||||
"""Export the overlay as a dictionary with command names as values. |
||||
|
||||
:rtype: dict |
||||
|
||||
""" |
||||
d = dict() |
||||
for section in self._sections: |
||||
d[section] = list() |
||||
for command_name, statement in self._sections[section].items(): |
||||
d[section].append(command_name) |
@ -1,2 +1,2 @@ |
||||
from .base import Command, ItemizedCommand |
||||
from .factory import command_factory |
||||
# from .factory import command_factory |
||||
|
@ -1,134 +0,0 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from ...constants import LOGGER_NAME |
||||
from .base import Command |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"MAPPING", |
||||
"ConfigTest", |
||||
"DisableModule", |
||||
"DisableSite", |
||||
"EnableModule", |
||||
"EnableSite", |
||||
"Reload", |
||||
"Restart", |
||||
"Start", |
||||
"Stop", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class ConfigTest(Command): |
||||
"""Run an apache config test.""" |
||||
|
||||
def __init__(self, overlay=None, **kwargs): |
||||
"""There is no argument.""" |
||||
if overlay is not None: |
||||
statement = overlay.get("apache", "test") |
||||
else: |
||||
statement = "apachectl configtest" |
||||
|
||||
kwargs.setdefault('register', "apache_checks_out") |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class DisableModule(Command): |
||||
"""Disable an Apache module.""" |
||||
|
||||
def __init__(self, module_name, overlay=None, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param module_name: The module name. |
||||
:type module_name: str |
||||
|
||||
""" |
||||
if overlay is not None: |
||||
statement = overlay.get("apache", "disable_module", module_name=module_name) |
||||
|
||||
statement = "a2dismod %s" % module_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class DisableSite(Command): |
||||
"""Disable a virtual host.""" |
||||
|
||||
def __init__(self, domain_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param domain_name: The domain name. |
||||
:type domain_name: str |
||||
|
||||
""" |
||||
statement = "a2dissite %s.conf" % domain_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class Enable(Command): |
||||
|
||||
def __init__(self, what, name, **kwargs): |
||||
if what in ("mod", "module"): |
||||
statement = EnableModule(name, **kwargs).statement |
||||
elif what == "site": |
||||
statement = EnableSite(name, **kwargs).statement |
||||
else: |
||||
raise ValueError("Invalid Apache item to be enabled: %s" % what) |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class EnableModule(Command): |
||||
"""Enable an Apache module.""" |
||||
|
||||
def __init__(self, module_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param module_name: The module name. |
||||
:type module_name: str |
||||
|
||||
""" |
||||
statement = "a2enmod %s" % module_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class EnableSite(Command): |
||||
"""Enable a virtual host.""" |
||||
|
||||
def __init__(self, domain_name, **kwargs): |
||||
"""Initialize the command. |
||||
|
||||
:param domain_name: The domain name. |
||||
:type domain_name: str |
||||
|
||||
""" |
||||
statement = "a2ensite %s.conf" % domain_name |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
MAPPING = { |
||||
# 'apache': Apache, |
||||
'apache.check': ConfigTest, |
||||
'apache.config': ConfigTest, |
||||
'apache.configtest': ConfigTest, |
||||
'apache.disable': Disable, |
||||
'apache.disable_mod': DisableModule, |
||||
'apache.disable_module': DisableModule, |
||||
'apache.disable_site': DisableSite, |
||||
'apache.enable': Enable, |
||||
'apache.enable_mod': EnableModule, |
||||
'apache.enable_module': EnableModule, |
||||
'apache.enable_site': EnableSite, |
||||
'apache.mod': EnableModule, |
||||
'apache.module': EnableModule, |
||||
'apache.test': ConfigTest, |
||||
} |
@ -1,119 +0,0 @@ |
||||
# Imports |
||||
|
||||
from importlib import import_module |
||||
import logging |
||||
# from ..scripts import Function |
||||
from ...constants import LOGGER_NAME |
||||
# from .base import ItemizedCommand |
||||
# from .mappings import MAPPING |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def command_factory(name, comment, overlay, *args, **kwargs): |
||||
# try: |
||||
# _overlay = import_module("scripttease.library.overlays.%s" % overlay) |
||||
# except ImportError as e: |
||||
# log.error("The %s overlay could not be imported: %s" % (overlay, str(e))) |
||||
# return None |
||||
|
||||
if not overlay.command_exists(name): |
||||
log.warning("Command does not exist in %s overlay: %s" % (overlay.name, name)) |
||||
return None |
||||
|
||||
kwargs['comment'] = comment |
||||
|
||||
callback = overlay.MAPPINGS[name] |
||||
return callback(*args, **kwargs) |
||||
|
||||
''' |
||||
def command_exists(name): |
||||
"""Indicates whether the named command exists. |
||||
|
||||
:param name: The name of the command to be checked. |
||||
:type name: str |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
return name in MAPPING |
||||
|
||||
|
||||
def command_factory(name, comment, overlay, *args, **kwargs): |
||||
# if name in ("func", "function"): |
||||
# kwargs['comment'] = comment |
||||
# return Function(*args, **kwargs) |
||||
|
||||
if not command_exists(name): |
||||
log.warning("No mapping for command: %s" % name) |
||||
return None |
||||
|
||||
_args = list(args) |
||||
kwargs['comment'] = comment |
||||
kwargs['overlay'] = overlay |
||||
|
||||
log.debug("%s: %s" % (comment, kwargs)) |
||||
|
||||
command_class = MAPPING[name] |
||||
try: |
||||
items = kwargs.pop("items", None) |
||||
if items is not None: |
||||
return ItemizedCommand(command_class, items, *_args, **kwargs) |
||||
|
||||
return command_class(*_args, **kwargs) |
||||
except (KeyError, TypeError, ValueError) as e: |
||||
log.critical("Failed to load %s command: %s" % (name, e)) |
||||
return None |
||||
''' |
||||
|
||||
# |
||||
# |
||||
# |
||||
# MAPPINGS = { |
||||
# '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, |
||||
# 'copy': file_copy, |
||||
# 'pip': python_pip, |
||||
# 'virtualenv': python_virtualenv, |
||||
# # 'python': ("pip", "virtualenv"), |
||||
# # 'apache': ("disable_module", "disable_site", "enable_module", "enable_site", "test"), |
||||
# } |
||||
|
||||
|
||||
def nother_command_exists(name): |
||||
return name in MAPPINGS |
||||
|
||||
|
||||
def other_command_exists(name, section=None): |
||||
if section is not None: |
||||
if section not in MAPPINGS: |
||||
return False |
||||
|
||||
return name in MAPPINGS[section] |
||||
|
||||
for _section, commands in MAPPINGS.items(): |
||||
if name in commands: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
|
||||
|
||||
def other_command_factory(name, comment, overlay, *args, **kwargs): |
||||
if not overlay.command_exists(name): |
||||
log.warning("The %s overlay does not have a mapping for command: %s" % (overlay, name)) |
||||
return None |
||||
|
||||
items = kwargs.pop("items", None) |
||||
if items is not None: |
||||
return ItemizedCommand |
||||
|
@ -1,4 +0,0 @@ |
||||
from .python import MAPPING as PYTHON_MAPPING |
||||
|
||||
MAPPING = dict() |
||||
MAPPING.update(PYTHON_MAPPING) |
@ -1,23 +0,0 @@ |
||||
# Classes |
||||
|
||||
|
||||
class Install(object): |
||||
|
||||
def __init__(self, name, manager="pip", overlay=None, upgrade=False, **kwargs): |
||||
if overlay is not None: |
||||
statement = overlay.get("package_install", manager, package_name=name, upgrade=upgrade) |
||||
else: |
||||
statement = "%s install %s" % (manager, name) |
||||
|
||||
self.statement = statement |
||||
|
||||
|
||||
class Remove(object): |
||||
|
||||
def __init__(self, name, manager="pip", overlay=None): |
||||
if overlay is not None: |
||||
statement = overlay.get("package_remove", manager, package_name=name) |
||||
else: |
||||
statement = "%s uninstall %s" % (manager, name) |
||||
|
||||
|
@ -1,43 +0,0 @@ |
||||
# Imports |
||||
|
||||
from .base import Command |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"Pip", |
||||
"VirtualEnv", |
||||
) |
||||
|
||||
# Classes |
||||
|
||||
|
||||
class Pip(Command): |
||||
|
||||
def __init__(self, name, op="install", overlay=None, upgrade=False, venv=None, **kwargs): |
||||
if overlay is not None: |
||||
statement = overlay.get("python", op, package_name=name, upgrade=upgrade) |
||||
else: |
||||
statement = "pip %s -y %s" % (op, name) |
||||
|
||||
if venv is not None: |
||||
kwargs['prefix'] = "source %s/bin/activate" % venv |
||||
|
||||
kwargs.setdefault("comment", "%s %s" % (op, name)) |
||||
|
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
class VirtualEnv(Command): |
||||
|
||||
def __init__(self, name="python", overlay=None, **kwargs): |
||||
kwargs.setdefault("comment", "create %s virtual environment" % name) |
||||
|
||||
statement = "virtualenv %s" % name |
||||
super().__init__(statement, **kwargs) |
||||
|
||||
|
||||
MAPPING = { |
||||
'pip': Pip, |
||||
'virtualenv': VirtualEnv, |
||||
} |
@ -0,0 +1,164 @@ |
||||
# 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): |
||||
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): |
||||
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): |
||||
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): |
||||
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): |
||||
"""Initialize the command. |
||||
|
||||
:param app_name: The name (app label) of the app. ``app_label.ModelName`` may also be given. |
||||
:type app_name: str |
||||
|
||||
:param file_name: The file name to which the data will be dumped. |
||||
:type file_name: str |
||||
|
||||
:param indent: Indentation of the exported fixtures. |
||||
:type indent: int |
||||
|
||||
:param natural_foreign: Use the natural foreign parameter. |
||||
:type natural_foreign: bool |
||||
|
||||
:param natural_primary: Use the natural primary parameter. |
||||
:type natural_primary: bool |
||||
|
||||
:param path: The path to the data file. |
||||
:type path: str |
||||
|
||||
""" |
||||
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): |
||||
"""Initialize the command. |
||||
|
||||
:param app_name: The name (app label) of the app. |
||||
:type app_name: str |
||||
|
||||
:param file_name: The file name to which the data will be dumped. |
||||
:type file_name: str |
||||
|
||||
:param path: The path to the data file. |
||||
:type path: str |
||||
|
||||
""" |
||||
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): |
||||
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, |
||||
} |
@ -0,0 +1,292 @@ |
||||
# Imports |
||||
|
||||
from ..commands import Command |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"PGSQL_MAPPINGS", |
||||
"pg_create_database", |
||||
"pg_create_user", |
||||
"pg_database_exists", |
||||
"pg_drop_database", |
||||
"pg_drop_user", |
||||
"pg_dump_database", |
||||
"psql", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def _get_pgsql_command(name, host="localhost", password=None, port=5432, user="postgres"): |
||||
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 pg_create_database(name, admin_pass=None, admin_user="postgres", host="localhost", owner=None, port=5432, |
||||
template=None, **kwargs): |
||||
"""Create a PostgreSQL database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param owner: The owner (user/role name) of the new database. |
||||
:type owner: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
:param template: The database template name to use, if any. |
||||
:type template: str |
||||
|
||||
""" |
||||
_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(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_create_user(name, admin_pass=None, admin_user="postgres", host="localhost", password=None, port=5432, **kwargs): |
||||
"""Create a PostgreSQL user. |
||||
|
||||
:param name: The user name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param password: The password for the new user. |
||||
:type password: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# 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) |
||||
|
||||
|
||||
def pg_database_exists(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Determine if a Postgres database exists. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. However, sudo may be required for |
||||
# file writing. |
||||
# kwargs['sudo'] = False |
||||
|
||||
kwargs.setdefault("register", "%s_db_exists" % name) |
||||
|
||||
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" % name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_drop_database(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Remove a PostgreSQL database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# 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(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_drop_user(name, admin_pass=None, admin_user="postgres", host="localhost", port=5432, **kwargs): |
||||
"""Remove a Postgres user. |
||||
|
||||
:param name: The user name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
# Postgres commands always run without sudo because the -U may be provided. |
||||
kwargs['sudo'] = False |
||||
|
||||
# Assemble the command. |
||||
base = _get_pgsql_command("dropuser", host=host, password=admin_pass, port=port, user=admin_user) |
||||
base.append(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def pg_dump_database(name, admin_pass=None, admin_user="postgres", file_name=None, host="localhost", port=5432, |
||||
**kwargs): |
||||
"""Export a Postgres database. |
||||
|
||||
:param name: The database name. |
||||
:type name: str |
||||
|
||||
:param admin_pass: The password for the user with sufficient access privileges to execute the command. |
||||
:type admin_pass: str |
||||
|
||||
:param admin_user: The name of the user with sufficient access privileges to execute the command. |
||||
:type admin_user: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param file_name: The name (including the path, if desired) of the export file. Defaults to the |
||||
``database_name`` plus ".sql" |
||||
:type file_name: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
""" |
||||
_file_name = file_name or "%s.sql" % name |
||||
|
||||
# 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(name) |
||||
|
||||
return Command(" ".join(base), **kwargs) |
||||
|
||||
|
||||
def psql(sql, database="template1", host="localhost", password=None, port=5432, user="postgres", **kwargs): |
||||
"""Execute a psql command. |
||||
|
||||
:param sql: The SQL to be executed. |
||||
:type sql: str |
||||
|
||||
:param database: The database name. |
||||
:type database: str |
||||
|
||||
:param password: The password for the user with sufficient access privileges to execute the command. |
||||
:type password: str |
||||
|
||||
:param host: The database host name or IP address. |
||||
:type host: str |
||||
|
||||
:param port: The port number of the Postgres service running on the host. |
||||
:type port: int |
||||
|
||||
:param user: The name of the user with sufficient access privileges to execute the command. |
||||
:type user: str |
||||
|
||||
""" |
||||
# 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) |
||||
|
||||
|
||||
PGSQL_MAPPINGS = { |
||||
'pg.client': psql, |
||||
'pg.createdatabase': pg_create_database, |
||||
'pg.createdb': pg_create_database, |
||||
'pg.createuser': pg_create_user, |
||||
'pg.database': pg_create_database, |
||||
'pg.database_exists': pg_database_exists, |
||||
'pg.db': pg_create_database, |
||||
'pg.dropdatabase': pg_drop_database, |
||||
'pg.dropdb': pg_drop_database, |
||||
'pg.dropuser': pg_drop_user, |
||||
'pg.dump': pg_dump_database, |
||||
'pg.dumpdb': pg_dump_database, |
||||
'pg.exists': pg_database_exists, |
||||
'pg.user': pg_create_user, |
||||
'psql': psql, |
||||
} |
@ -1,85 +1,4 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from superpython.utils import any_list_item |
||||
from ..constants import LOGGER_NAME |
||||
from .ini import Config |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"filter_commands", |
||||
"load_commands", |
||||
) |
||||
|
||||
# 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 |
||||
:return: |
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
if environments is not None: |
||||
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=None, **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 |
||||
|
||||
:rtype: list[BaseType[Command] | 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. |
||||
|
||||
""" |
||||
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 _config.load(): |
||||
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 |
||||
|
||||
log.error("Failed to load config file: %s" % path) |
||||
return None |
||||
from .utils import filter_commands, load_commands, load_config |
||||
|
@ -0,0 +1,110 @@ |
||||
# Imports |
||||
|
||||
import logging |
||||
from superpython.utils import any_list_item |
||||
from ..constants import LOGGER_NAME |
||||
from .ini import Config |
||||
|
||||
log = logging.getLogger(LOGGER_NAME) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"filter_commands", |
||||
"load_commands", |
||||
) |
||||
|
||||
# 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 |
||||
:return: |
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
if environments is not None: |
||||
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 |
@ -1,159 +0,0 @@ |
||||
# Imports |
||||
|
||||
from pygments import highlight |
||||
from pygments.lexers import get_lexer_by_name |
||||
from pygments.formatters import get_formatter_by_name |
||||
|
||||
BashLexer = get_lexer_by_name("bash") |
||||
JSONLexer = get_lexer_by_name("json") |
||||
PythonLexer = get_lexer_by_name("python") |
||||
TerminalFormatter = get_formatter_by_name("terminal", linenos=True) |
||||
|
||||
# Exports |
||||
|
||||
__all__ = ( |
||||
"any_list_item", |
||||
"filter_commands", |
||||
"filter_objects", |
||||
"highlight_code", |
||||
"split_csv", |
||||
) |
||||
|
||||
# Functions |
||||
|
||||
|
||||
def any_list_item(a, b): |
||||
"""Determine whether any item in ``a`` also exists in ``b``. |
||||
|
||||
:param a: The first list to be compared. |
||||
:type a: list |
||||
|
||||
:param b: The second list to be compared. |
||||
:type b: list |
||||
|
||||
:rtype: bool |
||||
|
||||
""" |
||||
for i in a: |
||||
for j in b: |
||||
if i == j: |
||||
return True |
||||
|
||||
return False |
||||
|
||||
|
||||
def filter_commands(commands, values, attribute="tags"): |
||||
"""Filter commands for a given set of values. |
||||
|
||||
:param commands: The commands to be filtered. |
||||
:type commands: list[BaseType[Command]] |
||||
|
||||
:param values: The values to be compared. |
||||
:type values: list |
||||
|
||||
:param attribute: The name of the command attribute to check. This attribute must be a list or tuple of values of |
||||
the same type given in ``values``. |
||||
:type attribute: str |
||||
|
||||
:rtype: bool |
||||
|
||||
.. code-block:: python |
||||
|
||||
commands = [ |
||||
AddUser("bob"), |
||||
Apt("apache2", tags=["apache", "www"]), |
||||
Reload("postgresql", tags=["database", "pgsql"]), |
||||
Touch("/var/www/index.html", tags=["www"]), |
||||
] |
||||
|
||||
values = ["apache", "www"] |
||||
|
||||
# Outputs the Apt and Touch commands above. |
||||
filtered_commands = filter_commands(command, values) |
||||
print(filtered_commands) |
||||
|
||||
""" |
||||
filtered = list() |
||||
for command in commands: |
||||
try: |
||||
list_b = getattr(command, attribute) |
||||
except AttributeError: |
||||
continue |
||||
|
||||
if not any_list_item(values, list_b): |
||||
continue |
||||
|
||||
filtered.append(command) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def filter_objects(objects, environments=None, scope=None, tags=None): |
||||
"""Filter the given objects by the given keys. |
||||
|
||||
:param objects: The objects to be filtered. |
||||
:type objects: list |
||||
|
||||
:param environments: The environments to be included. |
||||
:type environments: list[str] |
||||
|
||||
:param scope: The scope by which to filter; deploy, provision, tenant. |
||||
:type scope: str |
||||
|
||||
:param tags: The tags to be included. |
||||
:type tags: list[str] |
||||
|
||||
:rtype: list |
||||
:returns: Returns the objects that match the given keys. |
||||
|
||||
""" |
||||
filtered = list() |
||||
|
||||
# print("object, object environments, environments, any_list_item") |
||||
|
||||
for o in objects: |
||||
|
||||
# print(o, o.environments, environments, any_list_item(environments, o.environments)) |
||||
|
||||
# Apply environment filter. |
||||
if environments is not None: |
||||
if hasattr(o, "environment"): |
||||
if o.environment is not None and o.environment not in environments: |
||||
continue |
||||
elif hasattr(o, "environments"): |
||||
if type(o.environments) in (list, tuple) and not any_list_item(environments, o.environments): |
||||
continue |
||||
else: |
||||
pass |
||||
|
||||
# # Apply scope filter. |
||||
# if scope is not None: |
||||
# if o.scope not in [None, SCOPE_ALL, scope]: |
||||
# continue |
||||
|
||||
# Apply tag filter. |
||||
if tags is not None: |
||||
if not any_list_item(tags, o.tags): |
||||
continue |
||||
|
||||
# The object has passed the tests above. |
||||
filtered.append(o) |
||||
|
||||
return filtered |
||||
|
||||
|
||||
def highlight_code(string, lexer=None): |
||||
"""Highlight (colorize) the given string as Python code. |
||||
|
||||
:param string: The string to be highlighted. |
||||
:type string: str |
||||
|
||||
:rtype: str |
||||
|
||||
""" |
||||
if lexer is None: |
||||
lexer = BashLexer |
||||
|
||||
return highlight(string, lexer, TerminalFormatter) |
||||
|
||||
|
@ -0,0 +1,2 @@ |
||||
[this command will fail to load] |
||||
nonexistent = testing |
@ -0,0 +1,2 @@ |
||||
[this command will fail to load] |
||||
run - testing |
@ -0,0 +1,6 @@ |
||||
[create the site directory] |
||||
mkdir: /var/www/domains/{{ domain_tld }} |
||||
|
||||
[this will cause template parsing to fail] |
||||
touch: /path/to/{% if this.will.break %}testing |
||||
|
@ -0,0 +1,25 @@ |
||||
[function for setting up apache] |
||||
func = apache_setup |
||||
|
||||
[install apache] |
||||
install: apache2 |
||||
func: apache_setup |
||||
|
||||
[install wsgi] |
||||
install: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[enable wsgi] |
||||
apache.enable_module: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[disable the default site] |
||||
apache.disable_site = default |
||||
func: apache_setup |
||||
|
||||
[restart apache] |
||||
apache: restart |
||||
func: apache_setup |
||||
|
||||
[call apache setup] |
||||
run: apache_setup |
@ -0,0 +1,192 @@ |
||||
[update system repos] |
||||
system: update |
||||
|
||||
[upgrade the system] |
||||
system: upgrade |
||||
|
||||
[reboot the system] |
||||
system: reboot |
||||
|
||||
[function for setting up apache] |
||||
func = apache_setup |
||||
|
||||
[install apache] |
||||
install: apache2 |
||||
func: apache_setup |
||||
|
||||
[install wsgi] |
||||
install: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[enable wsgi] |
||||
apache.enable_module: mod_wsgi |
||||
func: apache_setup |
||||
|
||||
[disable the default site in function] |
||||
apache.disable_site = default |
||||
func: apache_setup |
||||
|
||||
[restart apache] |
||||
apache: restart |
||||
func: apache_setup |
||||
|
||||
[call apache setup] |
||||
run: apache_setup |
||||
|
||||
[disable the default site] |
||||
apache.disable_site = default |
||||
|
||||
[enable mod SSL] |
||||
apache.enable_module = mod_ssl |
||||
|
||||
[enable more than one apache module at once] |
||||
apache.enable_module = $item |
||||
items = mod_wsgi, mod_rewrite |
||||
|
||||
[make sure apache can be reloaded] |
||||
apache: test |
||||
|
||||
[restart apache outside of function] |
||||
apache: restart |
||||
condition: $apache_checks_out -eq 0 |
||||
|
||||
[install the virtualenv package] |
||||
pip = virtualenv |
||||
|
||||
[install django debug toolbar] |
||||
pip: django-debug-toolbar |
||||
env: development |
||||
tags: python, project |
||||
|
||||
[create a virtual environment] |
||||
virtualenv = python |
||||
cd = /path/to/project |
||||
tags = python, project |
||||
|
||||
[install pillow] |
||||
pip = Pillow |
||||
cd = /path/to/project |
||||
upgrade = yes |
||||
venv = python |
||||
tags = python, project |
||||
|
||||
[apply database migrations] |
||||
django: migrate |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[run a custom django command] |
||||
django = custom_command arg1 arg2 |
||||
cd = /path/to/project |
||||
venv = python |
||||
settings = tenants.example_app.settings |
||||
quiet = yes |
||||
|
||||
[collect the project's static files] |
||||
django: collectstatic |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[load data fixtures] |
||||
django.loaddata: categories |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[dump data fixtures] |
||||
django.dumpdata: projects |
||||
cd: /path/to/project |
||||
venv: python |
||||
|
||||
[reload a service] |
||||
reload: postfix |
||||
|
||||
[restart a service] |
||||
restart: postfix |
||||
|
||||
[stop a service] |
||||
stop: postfix |
||||
|
||||
[start a service] |
||||
start: postfix |
||||
|
||||
[install a package] |
||||
install: python3 |
||||
|
||||
[remove a package] |
||||
uninstall: apache-top |
||||
|
||||
[add to a file] |
||||
append: /path/to/file.txt |
||||
content: this is a test |
||||
|
||||
[copy a file] |
||||
copy: /path/to/file.txt /new/path/to/file.txt |
||||
|
||||
[write (overwrite) a file] |
||||
write: /path/to/file.txt |
||||
content: this replaces all text in the file |
||||
|
||||
[create a directory] |
||||
mkdir: /path/to/dir |
||||
mode: 755 |
||||
|
||||
[move a file] |
||||
move: /path/to/file.txt /path/to/file.txt.b |
||||
|
||||
[set permissions on a file] |
||||
perms: /path/to/file.txt |
||||
group: www-data |
||||
mode: 755 |
||||
owner: deploy |
||||
recursive: yes |
||||
|
||||
[remove a file] |
||||
remove: /path/to/file.txt |
||||
|
||||
[sync a directory] |
||||
rsync: /path/to/source /path/to/target |
||||
|
||||
[copy a file to remote server] |
||||
scopy: /path/to/file.txt /path/to/server/file.txt |
||||
host: example.com |
||||
|
||||
[replace text in a file] |
||||
sed: /path/to/file.txt |
||||
find: logging = no |
||||
replace: logging = yes |
||||
|
||||
[create a symlink] |
||||
symlink: /var/www/domains |
||||
|
||||
[touch a file] |
||||
touch: /path/to/file.txt |
||||
|
||||
[create a postgres user/role] |
||||
pg.user: example_app |
||||
|
||||
[create a postgres database] |
||||
pg.db: example_app |
||||
owner: example_app |
||||
|
||||
[determine whether a postgres database exists] |
||||
pg.database_exists: example_app |
||||
|
||||
[export a postgres database] |
||||
pg.dump: testing |
||||
|
||||
[drop a postgres user/role] |
||||
pg.dropuser: testing |
||||
|
||||
[drop a postgres database] |
||||
pg.dropdb: testing |
||||
|
||||
[run an SQL command on a postgres database] |
||||
psql: "SELECT * FROM projects WHERE category = 'testing'" |
||||
database: example_app |
||||
owner: example_app |
||||
|
||||
[create a file archive] |
||||
archive: /var/www/domains/example_com |
||||
|
||||
[extract a file archive] |
||||
extract: /var/www/domains/example_com.tgz |
@ -1,12 +1,15 @@ |
||||
[install the virtualenv package] |
||||
pip = virtualenv |
||||
tags = python-support |
||||
|
||||
[create a virtual environment] |
||||
virtualenv = python |
||||
cd = /path/to/project |
||||
tags = python-support |
||||
|
||||
[install pillow] |
||||
pip = Pillow |
||||
cd = /path/to/project |
||||
upgrade = yes |
||||
venv = python |
||||
tags = depends |
@ -0,0 +1,2 @@ |
||||
[create the site directory] |
||||
mkdir: /var/www/domains/{{ domain_tld }} |
@ -0,0 +1,40 @@ |
||||
from scripttease.library.commands import Command, ItemizedCommand |
||||
from scripttease.factory import Factory |
||||
|
||||
|
||||
class TestFactory(object): |
||||
|
||||
def test_get_command(self): |
||||
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>" |
@ -1,20 +0,0 @@ |
||||
from scripttease.library.commands import command_factory, ItemizedCommand |
||||
from scripttease.library.commands.python import Pip |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
def test_command_factory(): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
|
||||
command = command_factory("nonexistent", "non existent command", overlay) |
||||
assert command is None |
||||
|
||||
command = command_factory("pip", "install pillow", overlay) |
||||
assert command is None |
||||
|
||||
command = command_factory("pip", "install pillow", overlay, "Pillow") |
||||
assert isinstance(command, Pip) |
||||
|
||||
command = command_factory("pip", "install various", overlay, "$item", items=["Pillow", "pyscopg2-binary", "django"]) |
||||
assert isinstance(command, ItemizedCommand) |
@ -1,18 +0,0 @@ |
||||
from scripttease.library.commands.python import * |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
def test_pip(): |
||||
pip = Pip("Pillow") |
||||
assert "pip install -y Pillow" in pip.get_statement() |
||||
|
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
|
||||
pip = Pip("Pillow", op="remove", overlay=overlay, venv="python") |
||||
assert "source python/bin/activate && pip3 uninstall --quiet Pillow" in pip.get_statement() |
||||
|
||||
|
||||
def test_virtualenv(): |
||||
virt = VirtualEnv() |
||||
assert "virtualenv python" in virt.get_statement() |
@ -0,0 +1,17 @@ |
||||
from scripttease.library.overlays.common import * |
||||
|
||||
|
||||
def test_python_pip(): |
||||
c = python_pip("Pillow") |
||||
assert "pip install -y 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() |
||||
assert "virtualenv python" in c.get_statement() |
@ -0,0 +1,61 @@ |
||||
from scripttease.library.overlays.django import * |
||||
|
||||
|
||||
def test_django(): |
||||
c = django("check") |
||||
assert "./manage.py check" in c.get_statement() |
||||
|
||||
c = django("collectstatic") |
||||
assert "./manage.py collectstatic" in c.get_statement() |
||||
|
||||
c = django("migrate") |
||||
assert "./manage.py migrate" in c.get_statement() |
||||
|
||||
c = django("custom", "arg1", "arg2", venv="python", settings="tenants.example.settings", quiet=True) |
||||
s = c.get_statement() |
||||
assert "./manage.py custom" in s |
||||
assert "arg1" in s |
||||
assert "arg2" in s |
||||
assert "--settings=" in s |
||||
assert "source python/bin/activate" in s |
||||
assert "--quiet" in s |
||||
|
||||
|
||||
def test_django_check(): |
||||
c = django_check(venv="python") |
||||
s = c.get_statement() |
||||
assert "./manage.py check" in s |
||||
assert "source python/bin/activate" in s |
||||
|
||||
|
||||
def test_django_collect_static(): |
||||
c = django_collect_static(venv="python") |
||||
s = c.get_statement() |
||||
assert "./manage.py collectstatic" in s |
||||
assert "source python/bin/activate" in s |
||||
|
||||
|
||||
def test_django_dumpdata(): |
||||
c = django_dumpdata("projects") |
||||
s = c.get_statement() |
||||
assert "./manage.py dumpdata" in s |
||||
assert "projects >" in s |
||||
assert "--format=json" in s |
||||
assert "--indent=4" in s |
||||
assert "local/projects/fixtures/initial.json" in s |
||||
|
||||
|
||||
def test_django_loaddata(): |
||||
c = django_loaddata("projects") |
||||
s = c.get_statement() |
||||
print(s) |
||||
assert "./manage.py loaddata" in s |
||||
assert "local/projects/fixtures/initial.json" in s |
||||
|
||||
|
||||
def test_django_migrate(): |
||||
c = django_migrate(cd="/path/to/project/", venv="python") |
||||
s = c.get_statement(cd=True) |
||||
assert "./manage.py migrate" in s |
||||
assert "source python/bin/activate" in s |
||||
assert "cd /path/to/project/" in s |
@ -0,0 +1,60 @@ |
||||
from scripttease.library.overlays.pgsql import * |
||||
|
||||
|
||||
def test_pg_create_database(): |
||||
c = pg_create_database("testing", admin_pass="secret", template="mytemplate") |
||||
s = c.get_statement() |
||||
assert "createdb" in s |
||||
assert "export PGPASSWORD=" in s |
||||
assert "--host=" in s |
||||
assert "--port=" in s |
||||
assert "--username=" in s |
||||
assert "--owner=" in s |
||||
assert "--template=mytemplate" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_create_user(): |
||||
c = pg_create_user("testing", password="secret") |
||||
s = c.get_statement() |
||||
assert "createuser" in s |
||||
assert "-DRS" in s |
||||
assert "testing" in s |
||||
assert "ALTER USER testing" in s |
||||
|
||||
|
||||
def test_pg_database_exists(): |
||||
c = pg_database_exists("testing") |
||||
s = c.get_statement() |
||||
assert "psql" in s |
||||
assert "testing_db_exists" in s |
||||
|
||||
|
||||
def test_pg_drop_database(): |
||||
c = pg_drop_database("testing") |
||||
s = c.get_statement() |
||||
assert "dropdb" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_drop_user(): |
||||
c = pg_drop_user("testing") |
||||
s = c.get_statement() |
||||
assert "dropuser" in s |
||||
assert "testing" in s |
||||
|
||||
|
||||
def test_pg_dump_database(): |
||||
c = pg_dump_database("testing") |
||||
s = c.get_statement() |
||||
assert "pg_dump" in s |
||||
assert "--column-inserts" in s |
||||
assert "--file=testing.sql" in s |
||||
|
||||
|
||||
def test_psql(): |
||||
c = psql("SELECT * FROM projects", database="testing") |
||||
s = c.get_statement() |
||||
assert "psql" in s |
||||
assert "--dbname=testing" in s |
||||
assert '-c "SELECT * FROM projects"' in s |
@ -0,0 +1,209 @@ |
||||
import pytest |
||||
from scripttease.library.overlays.posix import * |
||||
|
||||
|
||||
def test_archive(): |
||||
c = archive( |
||||
"/path/to/target", |
||||
absolute=True, |
||||
exclude="*.log", |
||||
strip=1, |
||||
view=True |
||||
) |
||||
s = c.get_statement() |
||||
print(s) |
||||
# tar -czPv --exclude *.log --strip-components 1 -f ./archive.tgz /path/to/target |
||||
assert "tar -czPv --exclude *.log --strip-components 1" in s |
||||
assert "-f ./archive.tgz /path/to/target" in s |
||||
|
||||
|
||||
def test_certbot(): |
||||
with pytest.raises(ValueError): |
||||
c = certbot("example.com") |
||||
|
||||
c = certbot("example.com", email="webmaster@example.com") |
||||
s = c.get_statement() |
||||
assert "certbot certonly --agree-tos --email webmaster@example.com -n" in s |
||||
assert "--webroot -w /var/www/domains/example_com/www -d example.com" in s |
||||
|
||||
|
||||
def test_extract(): |
||||
c = extract( |
||||
"/path/to/archive.tgz", |
||||
absolute=True, |
||||
exclude="*.log", |
||||
strip=1, |
||||
view=True |
||||
) |
||||
s = c.get_statement() |
||||
assert "tar -xzPv --exclude *.log --strip-components 1" in s |
||||
assert "-f /path/to/archive.tgz ./" in s |
||||
|
||||
|
||||
def test_file_append(): |
||||
c = file_append("/path/to/file.txt", content="testing = yes") |
||||
assert 'echo "testing = yes" >> /path/to/file.txt' in c.get_statement() |
||||
|
||||
|
||||
def test_file_copy(): |
||||
c = file_copy("/path/to/file.txt", "/path/to/new-file.txt") |
||||
s = c.get_statement() |
||||
assert "cp" in s |
||||
assert "-n" in s |
||||
assert "/path/to/file.txt /path/to/new-file.txt" in s |
||||
|
||||
c = file_copy("/path/to/dir", "/path/to/newdir", recursive=True) |
||||
s = c.get_statement() |
||||
assert "cp" in s |
||||
assert "-R" in s |
||||
assert "/path/to/dir /path/to/newdir" in s |
||||
|
||||
|
||||
def test_file_write(): |
||||
c = file_write("/path/to/file.txt", content="testing 123") |
||||
assert 'echo "testing 123" > /path/to/file.txt' in c.get_statement() |
||||
|
||||
content = [ |
||||
"I am testing", |
||||
"I am testing", |
||||
"I am testing", |
||||
"testing 123", |
||||
] |
||||
c = file_write("/path/to/file.txt", content="\n".join(content)) |
||||
s = c.get_statement() |
||||
assert "cat > /path/to/file.txt << EOF" in s |
||||
assert "I am testing" in s |
||||
assert "testing 123" in s |
||||
|
||||
|
||||
def test_mkdir(): |
||||
c = mkdir("/path/to/dir", mode=755, recursive=True) |
||||
s = c.get_statement() |
||||
assert "mkdir" in s |
||||
assert "-m 755" in s |
||||
assert "-p" in s |
||||
assert "/path/to/dir" in s |
||||
|
||||
|
||||
def test_move(): |
||||
c = move("/path/to/file.txt", "/path/to/file.txt.b") |
||||
assert "mv /path/to/file.txt /path/to/file.txt.b" in c.get_statement() |
||||
|
||||
|
||||
def test_perms(): |
||||
c = perms("/path/to/dir", group="www-data", mode=755, owner="deploy", recursive=True) |
||||
s = c.get_statement() |
||||
assert "chgrp -R www-data /path/to/dir" in s |
||||
assert "chown -R deploy /path/to/dir" in s |
||||
assert "chmod -R 755 /path/to/dir" in s |
||||
|
||||
|
||||
def test_remove(): |
||||
c = remove("/path/to/dir", force=True, recursive=True) |
||||
s = c.get_statement() |
||||
assert "rm" in s |
||||
assert "-f" in s |
||||
assert "-r" in s |
||||
assert "/path/to/dir" in s |
||||
|
||||
|
||||
def test_rsync(): |
||||
c = rsync( |
||||
"/path/to/local/", |
||||
"/path/to/remote", |
||||
links=True, |
||||
delete=True, |
||||
exclude="deploy/exclude.txt", |
||||
recursive=True, |
||||
host="example.com", |
||||
key_file="~/.ssh/deploy", |
||||
user="deploy" |
||||
) |
||||
s = c.get_statement() |
||||
assert "rsync --cvs-exclude --checksum --compress --copy-links --delete" in s |
||||
assert "--exclude-from=deploy/exclude.txt" in s |
||||
assert "-P" in s |
||||
assert "--recursive /path/to/local/" in s |
||||
assert '-e "ssh -i ~/.ssh/deploy -p 22"' in s |
||||
assert "deploy@example.com:/path/to/remote" in s |
||||
|
||||
c = rsync( |
||||
"/path/to/local/", |
||||
"/path/to/remote", |
||||
links=True, |
||||
delete=True, |
||||
exclude="deploy/exclude.txt", |
||||
recursive=True, |
||||
) |
||||
s = c.get_statement() |
||||
assert "rsync --cvs-exclude --checksum --compress --copy-links --delete" in s |
||||
assert "--exclude-from=deploy/exclude.txt" in s |
||||
assert "-P" in s |
||||
assert "--recursive" in s |
||||
assert "/path/to/local/" in s |
||||
assert "/path/to/remote" in s |
||||
|
||||
|
||||
def test_run(): |
||||
c = run("ls -ls") |
||||
assert "ls -ls" in c.get_statement() |
||||
|
||||
|
||||
def test_scopy(): |
||||
with pytest.raises(ValueError): |
||||
c = scopy("/path/to/local/file.txt", "/path/to/remote/file.txt") |
||||
|
||||
c = scopy( |
||||
"/path/to/local/file.txt", |
||||
"/path/to/remote/file.txt", |
||||
key_file="~/.ssh/deploy", |
||||
host="example.com", |
||||
user="deploy" |
||||
) |
||||
s = c.get_statement() |
||||
assert "scp -i ~/.ssh/deploy" in s |
||||
assert "-P 22" in s |
||||
assert "/path/to/local/file.txt" in s |
||||
assert "deploy@example.com:/path/to/remote/file.txt" in s |
||||
|
||||
c = scopy( |
||||
"/path/to/local/file.txt", |
||||
"/path/to/remote/file.txt", |
||||
host="example.com", |
||||
) |
||||
s = c.get_statement() |
||||
assert "scp -P 22" in s |
||||
assert "/path/to/local/file.txt" in s |
||||
assert "example.com:/path/to/remote/file.txt" in s |
||||
|
||||
|
||||
def test_sed(): |
||||
c = sed("/path/to/file.txt", find="testing", replace="123") |
||||
s = c.get_statement() |
||||
assert "sed -i .b" in s |
||||
assert "s/testing/123/g" in s |
||||
assert "/path/to/file.txt" in s |
||||
|
||||
|
||||
def test_symlink(): |
||||
c = symlink("/var/www/domains", force=True) |
||||
s = c.get_statement() |
||||
assert "ln -s" in s |
||||
assert "-f" in s |
||||
assert "/var/www/domains" in s |
||||
|
||||
|
||||
def test_touch(): |
||||
c = touch("/path/to/file.txt") |
||||
assert "touch /path/to/file.txt" in c.get_statement() |
||||
|
||||
|
||||
class TestFunction(object): |
||||
|
||||
def test_to_string(self): |
||||
f = Function("testing", comment="A test function.") |
||||
f.commands.append(touch("/path/to/file.txt")) |
||||
s = f.to_string() |
||||
assert "# A test function." in s |
||||
assert "function testing()" in s |
||||
assert "touch /path/to/file.txt" in s |
@ -0,0 +1,86 @@ |
||||
import pytest |
||||
from scripttease.library.overlays.ubuntu import * |
||||
|
||||
|
||||
def test_apache(): |
||||
c = apache("reload") |
||||
assert "service apache2 reload" in c.get_statement() |
||||
|
||||
c = apache("restart") |
||||
assert "service apache2 restart" in c.get_statement() |
||||
|
||||
c = apache("start") |
||||
assert "service apache2 start" in c.get_statement() |
||||
|
||||
c = apache("stop") |
||||
assert "service apache2 stop" in c.get_statement() |
||||
|
||||
c = apache("test") |
||||
assert "apachectl configtest" in c.get_statement() |
||||
|
||||
with pytest.raises(NameError): |
||||
apache("nonexistent") |
||||
|
||||
|
||||
def test_apache_disable_module(): |
||||
c = apache_disable_module("mod_ssl") |
||||
assert "a2dismod mod_ssl" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_disable_site(): |
||||
c = apache_disable_site("default") |
||||
assert "a2dissite default" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_enable_module(): |
||||
c = apache_enable_module("mod_wsgi") |
||||
assert "a2enmod mod_wsgi" in c.get_statement() |
||||
|
||||
|
||||
def test_apache_enable_site(): |
||||
c = apache_enable_site("example.com") |
||||
assert "a2ensite example.com" in c.get_statement() |
||||
|
||||
|
||||
def test_service_reload(): |
||||
c = service_reload("postfix") |
||||
assert "service postfix reload" in c.get_statement() |
||||
|
||||
|
||||
def test_service_restart(): |
||||
c = service_restart("postfix") |
||||
assert "service postfix restart" in c.get_statement() |
||||
|
||||
|
||||
def test_service_start(): |
||||
c = service_start("postfix") |
||||
assert "service postfix start" in c.get_statement() |
||||
|
||||
|
||||
def test_service_stop(): |
||||
c = service_stop("postfix") |
||||
assert "service postfix stop" in c.get_statement() |
||||
|
||||
|
||||
def test_system(): |
||||
c = system("reboot") |
||||
assert "reboot" in c.get_statement() |
||||
|
||||
c = system("update") |
||||
assert "apt-get update -y" in c.get_statement() |
||||
|
||||
c = system("upgrade") |
||||
assert "apt-get upgrade -y" in c.get_statement() |
||||
|
||||
with pytest.raises(NameError): |
||||
system("nonexistent") |
||||
|
||||
|
||||
def test_system_install(): |
||||
c = system_install("vim") |
||||
assert "apt-get install -y vim" in c.get_statement() |
||||
|
||||
|
||||
def test_system_uninstall(): |
||||
c = system_uninstall("lftp") |
||||
assert "apt-get uninstall -y lftp" in c.get_statement() |
@ -0,0 +1,31 @@ |
||||
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,19 +0,0 @@ |
||||
from scripttease.library.overlays import Overlay |
||||
|
||||
|
||||
class TestOverlay(object): |
||||
|
||||
def test_get(self): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
assert overlay.get("nonexistent", "nonexistent") is None |
||||
|
||||
def test_has(self): |
||||
overlay = Overlay("ubuntu") |
||||
overlay.load() |
||||
assert overlay.has("nonexistent", "nonexistent") is False |
||||
assert overlay.has("python", "nonexistent") is False |
||||
|
||||
def test_load(self): |
||||
overlay = Overlay("nonexistent") |
||||
assert overlay.load() is False |
@ -0,0 +1,22 @@ |
||||
import pytest |
||||
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): |
||||
# pass |
||||
|
||||
def test_load(self): |
||||
p = Parser("/path/to/nonexistent.txt") |
||||
with pytest.raises(NotImplementedError): |
||||
p.load() |
@ -0,0 +1,45 @@ |
||||
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/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 |
@ -0,0 +1,42 @@ |
||||
import pytest |
||||
from scripttease.library.commands import Command, ItemizedCommand |
||||
from scripttease.parsers import filter_commands, load_commands |
||||
|
||||
|
||||
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 |
Loading…
Reference in new issue