and use some of the functions in this module in order to specify
new applications.
+There are some submodules for programming languages that define common
+functions and data that may be used by applications in that language. See:
+
+* :mod:`wizard.app.php`
+
.. testsetup:: *
import re
#: a conflict marker string and a result list. See :mod:`wizard.resolve`
#: for more information.
resolutions = {}
+ #: Instance of :class:`wizard.install.ArgSchema` that defines the arguments
+ #: this application requires.
+ install_schema = None
def __init__(self, name):
self.name = name
self.versions = {}
take a :class:`wizard.deploy.Deployment` as a parameter.) Subclasses should
provide an implementation.
"""
- raise NotImplemented
+ raise NotImplementedError
def upgrade(self, deployment, version, options):
"""
Run for 'wizard upgrade' to upgrade database schemas and other
upgraded. This assumes that the current working directory is the
deployment. Subclasses should provide an implementation.
"""
- raise NotImplemented
+ raise NotImplementedError
def backup(self, deployment, outdir, options):
"""
Run for 'wizard backup' and upgrades to backup database schemas
Static user files may not need to be backed up, since in
many applications upgrades do not modify static files.
"""
- raise NotImplemented
+ raise NotImplementedError
def restore(self, deployment, backup_dir, options):
"""
Run for 'wizard restore' and failed upgrades to restore database
that the current working directory is the deployment. Subclasses
should provide an implementation.
"""
- raise NotImplemented
+ raise NotImplementedError
def detectVersion(self, deployment):
"""
Checks source files to determine the version manually. This assumes
that the current working directory is the deployment. Subclasses
should provide an implementation.
"""
- raise NotImplemented
- def checkWeb(self, deployment, output=None):
+ raise NotImplementedError
+ def detectVersionFromFile(self, filename, regex):
+ """
+ Helper method that detects a version by using a regular expression
+ from a file. The regexed value is passed through :mod:`shlex`.
+ This assumes that the current working directory is the deployment.
+ """
+ contents = open(filename).read()
+ match = regex.search(contents)
+ if not match: return None
+ return distutils.version.LooseVersion(shlex.split(match.group(2))[0])
+ def download(self, version):
+ """
+ Returns a URL that can be used to download a tarball of ``version`` of
+ this application.
"""
- Checks if the autoinstall is viewable from the web. To get
- the HTML source that was retrieved, pass a variable containing
- an empty list to ``output``; it will be mutated to have its
- first element be the output. Subclasses should provide an
- implementation.
+ raise NotImplementedError
+ def checkWeb(self, deployment):
+ """
+ Checks if the autoinstall is viewable from the web. Subclasses should
+ provide an implementation.
.. note::
Finding a reasonable heuristic that works across skinning
page does not contain the features you search for. Try
not to depend on pages that are not the main page.
"""
- raise NotImplemented
+ raise NotImplementedError
+ def checkWebPage(self, deployment, page, output):
+ """
+ Checks if a given page of an autoinstall contains a particular string.
+ """
+ page = deployment.fetch(page)
+ result = page.find(output) != -1
+ if result:
+ logging.debug("checkWebPage (passed):\n\n" + page)
+ else:
+ logging.info("checkWebPage (failed):\n\n" + page)
+ return result
def checkConfig(self, deployment):
"""
Checks whether or not an autoinstall has been configured/installed
for use. Assumes that the current working directory is the deployment.
Subclasses should provide an implementation.
"""
- raise NotImplemented
+ # XXX: Unfortunately, this doesn't quite work because we package
+ # bogus config files in the -scripts versions of installs. Maybe
+ # we should check a hash or something?
+ raise NotImplementedError
@staticmethod
def make(name):
"""Makes an application, but uses the correct subtype if available."""
def make_extractors(seed):
"""
- Take a dictionary of ``key``s to ``(file, regex)`` tuples and convert them into
+ Take a dictionary of ``key`` to ``(file, regex)`` tuples and convert them into
extractor functions (which take a :class:`wizard.deploy.Deployment`
and return the value of the second subpattern of ``regex`` when matched
with the contents of ``file``).
def make_substitutions(seed):
"""
- Take a dictionary of ``key``s to ``(file, regex)`` tuples and convert them into substitution
+ Take a dictionary of ``key`` to ``(file, regex)`` tuples and convert them into substitution
functions (which take a :class:`wizard.deploy.Deployment`, replace the second subpattern
of ``regex`` with ``key`` in ``file``, and returns the number of substitutions made.)
"""
# XXX: add support for getting these out of options
vars = d.extract()
if 'WIZARD_DBNAME' not in vars:
- raise app.BackupFailure("Could not determine database name")
+ raise BackupFailure("Could not determine database name")
triplet = scripts.get_sql_credentials(vars)
args = []
if triplet is not None:
def __init__(self, app):
self.app = app
-class UpgradeFailure(Error):
+class Failure(Error):
+ """
+ Represents a failure when performing some double-dispatched operation
+ such as an installation or an upgrade. Failure classes are postfixed
+ with Failure, not Error.
+ """
+ pass
+
+class InstallFailure(Error):
+ """Installation failed for unknown reason."""
+ def __str__(self):
+ return """Installation failed for unknown reason."""
+
+class RecoverableInstallFailure(InstallFailure):
+ """
+ Installation failed, but we were able to determine what the
+ error was, and should give the user a second chance if we were
+ running interactively.
+ """
+ #: List of the errors that were found.
+ errors = None
+ def __init__(self, errors):
+ self.errors = errors
+ def __str__(self):
+ return """Installation failed due to the following errors: %s""" % ", ".join(self.errors)
+
+class UpgradeFailure(Failure):
"""Upgrade script failed."""
#: String details of failure (possibly stdout or stderr output)
details = None
%s""" % self.details
-class UpgradeVerificationFailure(Error):
+class UpgradeVerificationFailure(Failure):
"""Upgrade script passed, but website wasn't accessible afterwards"""
- #: String details of failure (possibly stdout or stderr output)
- details = None
- def __init__(self, details):
- self.details = details
def __str__(self):
return """
-ERROR: Upgrade script passed, but website wasn't accessible afterwards. Details:
-
-%s""" % self.details
+ERROR: Upgrade script passed, but website wasn't accessible afterwards. Check
+the debug logs for the contents of the page."""
-class BackupFailure(Error):
+class BackupFailure(Failure):
"""Backup script failed."""
#: String details of failure
details = None
%s""" % self.details
-class RestoreFailure(Error):
+class RestoreFailure(Failure):
"""Restore script failed."""
#: String details of failure
details = None