]> scripts.mit.edu Git - wizard.git/commitdiff
Refactor URL parsing code to be clean, support for .scripts/url
authorEdward Z. Yang <ezyang@mit.edu>
Tue, 22 Dec 2009 18:10:13 +0000 (13:10 -0500)
committerEdward Z. Yang <ezyang@mit.edu>
Tue, 22 Dec 2009 18:10:13 +0000 (13:10 -0500)
Signed-off-by: Edward Z. Yang <ezyang@mit.edu>
TODO
wizard/app/__init__.py
wizard/command/install.py
wizard/command/mass_upgrade.py
wizard/deploy.py
wizard/install/__init__.py
wizard/scripts.py

diff --git a/TODO b/TODO
index 254a2aaab1633d3442263958dd9e57f6a1a1ef44..839d96b0c6ae5851df028e25792e4eacbb21aa92 100644 (file)
--- a/TODO
+++ b/TODO
@@ -2,15 +2,6 @@ The Git Autoinstaller
 
 TODO NOW:
 
-- The calling web code invocations are a mess, with stubs living
-  in the install, deploy modules and the real deal living in util.  Furthermore,
-  we use the scripts-specific heuristic to determine where the app
-  lives, and the only reason my test scripts work is because they
-  get manually fed the domain and path by my environment variables.
-
-  Use system similar to database, with option for explicit override,
-  but otherwise attempting to determine from ambient code
-
 - If you try to do an install on scripts w/o sql, it will sign you up but fail to write
   the sql.cnf file. This sucks.
 
@@ -36,7 +27,7 @@ TODO NOW:
   operations.
 
 - Pay back code debt
-    - Genericize callAsUser and drop_priviledges in shell
+    - Tidy up common code in callAsUser and drop_priviledges in shell
     - Summary script should be more machine friendly, and should not
       output summary charts when I increase specificity
     - Summary script should do something intelligent when distinguishing
index 769d6e910bae6b31050b05c99f1e2b5413f99b61..a40ff04debd06810cf2f0c7b73522f247a036784 100644 (file)
@@ -30,6 +30,7 @@ import shutil
 import sqlalchemy
 import random
 import string
+import urlparse
 
 import wizard
 from wizard import resolve, scripts, shell, util
@@ -179,6 +180,34 @@ class Application(object):
         # XXX: You'd have to put support for an explicit different database
         # type here
         return sqlalchemy.engine.url.URL(self.database, username=user, password=password, host=host, database=database)
+    def url(self, deployment):
+        """
+        Returns the deployment specific web URL.  Uses the override file
+        in :file:`.scripts` if it exists, and otherwise attempt to extract
+        the variables from the source files.
+
+        This function might return ``None``, which indicates we couldn't figure
+        it out.
+        """
+        url = self.urlFromOverride(deployment)
+        if url:
+            return url
+        return self.urlFromExtract(deployment)
+    def urlFromOverride(self, deployment):
+        """
+        Extracts URL from explicit url override file.
+        """
+        try:
+            return urlparse.urlparse(open(deployment.url_file).read().strip())
+        except IOError:
+            return None
+    def urlFromExtract(self, deployment):
+        """
+        Extracts URL from a deployment, and returns ``None`` if we can't
+        figure it out.  Default implementation is to fail; we might
+        do something clever with extractable variables in the future.
+        """
+        return None
     def parametrize(self, deployment, ref_deployment):
         """
         Takes a generic source checkout and parametrizes it according to the
index 5999c27ffa5661b200562ceb011548b3662275c2..a4faf83db6963e1280d8f820b21483ebf65808da 100644 (file)
@@ -51,6 +51,8 @@ Autoinstalls the application %s in the directory DIR.""" % (appname, appname))
         application.install(distutils.version.LooseVersion(version), options)
         if not old_options.no_commit:
             git.commit_configure()
+    if not hasattr(options, "web_inferred"):
+        open(os.path.join(dir, ".scripts/url"), "w").write("http://%s%s" % (options.web_host, options.web_path)) # XXX: no support for https yet!
     input.infobox("Congratulations, your new install is now accessible at:\n\nhttp://%s%s" % (options.web_host, options.web_path), width=80)
 
 def configure_parser(parser, baton):
index 11d5237dfd301f89a2c85ac1b7ce34d1f479a7f0..5119eef689f3f892966d74cf5c66812bbe2b3ca3 100644 (file)
@@ -77,11 +77,7 @@ def main(argv, baton):
                     else:
                         name = e.name
                         if name == "WebVerificationError":
-                            try:
-                                host, path = scripts.get_web_host_and_path(d.location)
-                                url = "http://%s%s" % (host, path)
-                            except ValueError:
-                                url = d.location
+                            url = d.url.geturl()
                             # This should actually be a warning, but
                             # it's a really common error
                             logging.info("[%04d] Could not verify application at %s" % (i, url))
index cadf63341990604a1524dca4dd190db74d543e4f..7bc2c7671f82a75ff90b1304918227ad6eeed305 100644 (file)
@@ -95,6 +95,7 @@ class Deployment(object):
         self._read_cache = {}
         self._old_log = None
         self._dsn = None
+        self._url = None
     def invalidateCache(self):
         """
         Invalidates all cached variables.  This currently applies to
@@ -229,6 +230,10 @@ class Deployment(object):
         """The absolute path of the :file:`.scripts/dsn` override file."""
         return os.path.join(self.scripts_dir, 'dsn')
     @property
+    def url_file(self):
+        """The absolute path of the :file:`.scripts/url` override file."""
+        return os.path.join(self.scripts_dir, 'url')
+    @property
     def application(self):
         """The :class:`app.Application` of this deployment."""
         return self.app_version.application
@@ -270,9 +275,18 @@ class Deployment(object):
         return self._app_version
     @property
     def dsn(self):
+        """The :class:`sqlalchemy.engine.url.URL` for this deployment."""
         if not self._dsn:
             self._dsn = sql.fill_url(self.application.dsn(self))
         return self._dsn
+    @property
+    def url(self):
+        """The :class:`urlparse.ParseResult` for this deployment."""
+        if not self._url:
+            self._url = scripts.fill_url(self.location, self.application.url(self))
+        if not self._url:
+            raise UnknownWebPath
+        return self._url
     @staticmethod
     def parse(line):
         """
@@ -349,11 +363,7 @@ class ProductionCopy(Deployment):
         """
         Performs a HTTP request on the website.
         """
-        try:
-            host, basepath = scripts.get_web_host_and_path(self.location)
-        except (ValueError, TypeError):
-            raise UnknownWebPath
-        return util.fetch(host, basepath, path, post)
+        return util.fetch(self.url.netloc, self.url.path, path, post)
 
 class WorkingCopy(Deployment):
     """
index f5388415e5f52f2ccfff7704cd1e77f739d1ac72..0af8ac439dde4e5e595756a188672ddd489f4f50 100644 (file)
@@ -129,12 +129,14 @@ class ScriptsWebStrategy(Strategy):
         self.dir = dir
     def prepare(self):
         """Uses :func:`wizard.scripts.get_web_host_and_path`."""
-        self._tuple = scripts.get_web_host_and_path(self.dir)
-        if not self._tuple:
+        self._url = scripts.fill_url(self.dir, None)
+        if not self._url:
             raise StrategyFailed
     def execute(self, options):
         """No-op."""
-        options.web_host, options.web_path = self._tuple
+        options.web_host = self._url.netloc
+        options.web_path = self._url.path
+        options.web_inferred = True # hacky: needed to see if we need a .scripts/url file
 
 class ScriptsMysqlStrategy(Strategy):
     """
index a98fda6c14196b414d3474cf4bf1e1f218904f8b..196da97bf5ceb70ad8d99094322d5c05a817d353 100644 (file)
@@ -2,28 +2,37 @@ import os
 import shlex
 import errno
 import logging
+import urlparse
 
 import wizard
 from wizard import shell, util
 
-def get_web_host_and_path(dir=None):
-    """
-    Attempts to determine webhost and path for the current directory
-    as it would be accessible from the web.  Works only for scripts
-    servers.  Returns a tuple web_host, web_path, or None if it failed.
-    """
-    # XXX: THIS CODE SUCKS
+def fill_url(dir, url=None):
+    if url:
+        return url
+
+    # hook hook
+
+    # try the directory
+    homedir, _, web_path = dir.partition("/web_scripts")
+    if web_path:
+        return urlparse.ParseResult(
+                "http",
+                util.get_dir_owner(homedir) + ".scripts.mit.edu",
+                web_path.rstrip('/'),
+                "", "", "")
+
+    # try the environment
     host = os.getenv("WIZARD_WEB_HOST")
     path = os.getenv("WIZARD_WEB_PATH")
     if host is not None and path is not None:
-        return (host, path.rstrip('/'))
-    if not dir:
-        dir = os.getcwd()
-    # XXX: possible security risk?
-    homedir, _, web_path = dir.partition("/web_scripts")
-    if not web_path:
-        return None
-    return (util.get_dir_owner(homedir) + ".scripts.mit.edu", web_path.rstrip('/'))
+        return urlparse.ParseResult(
+                "http",
+                host,
+                path.rstrip('/'),
+                "", "", "")
+
+    return None
 
 def get_quota_usage_and_limit(dir=None):
     """