Commits

Mikhail Korobov committed f3cd9df

Better support for machines with older DFD versions; deleting/erasing apache files is confirmed; tests that ask for user input fails.

Comments (0)

Files changed (5)

 Preparations
 ------------
 
-django-fab-deploy requires latest `fabtest`_ package for running tests and
-(optionally) `coverage.py`_ for test coverage reports::
+django-fab-deploy requires latest `fabtest`_ and `mock`_ packages
+for running tests and (optionally) `coverage.py`_ for test coverage reports::
 
     pip install -U fabtest
+    pip install mock==0.8
     pip install coverage
 
 Please follow `instructions <http://pypi.python.org/pypi/fabtest>`_ for
 .. _VirtualBox: http://www.virtualbox.org/
 .. _fabtest: https://bitbucket.org/kmike/fabtest
 .. _coverage.py: http://pypi.python.org/pypi/coverage
+.. _mock: http://pypi.python.org/pypi/mock
 
 Running tests
 -------------
 
-Pass VM name (e.g. Lenny) to runtests.py script::
+Pass VM name (e.g. Squeeze) to runtests.py script::
 
     cd fab_deploy_tests
     ./runtests.py <VM name or uid> <what to run>

fab_deploy/webserver/apache.py

 from __future__ import with_statement, absolute_import
-import re
 
-from fabric.api import env, run, settings, sudo, hide, puts, warn, task
-from fabric.contrib import files
+from fabric.api import env, run, settings, sudo, hide, task, abort
+from fabric.contrib import files, console
 
 from fab_deploy import utils
 from fab_deploy import system
 APACHE_PORTS_FILE = '/etc/apache2/ports.conf'
 APACHE_FIRST_PORT = 50000 # see http://www.iana.org/assignments/port-numbers
 APACHE_LAST_PORT = 60000
-TAKEOVER_STRING = '# This file is managed by django-fab-deploy 0.8.x. "Listen" directives are in /etc/apache2/sites-available/*'
+TAKEOVER_STRING = '# This file is managed by django-fab-deploy. "Listen" directives are in /etc/apache2/sites-available/*'
+OLD_TAKEOVER_STRING = '# This file is managed by django-fab-deploy. Please do not edit it manually.'
 
 @task
 def touch(wsgi_file=None):
 
 @task
 @utils.run_as_sudo
-def install():
+def install(confirm=True):
     """ Installs apache. """
     system.aptitude_install('apache2 libapache2-mod-wsgi libapache2-mod-rpaf locales-all')
-    sudo('rm -f /etc/apache2/sites-enabled/default')
-    sudo('rm -f /etc/apache2/sites-enabled/000-default')
     setup_locale()
-    _disable_ports_conf()
+
+    default_sites = [
+        '/etc/apache2/sites-enabled/default',
+        '/etc/apache2/sites-enabled/000-default',
+    ]
+
+    for site in default_sites:
+        if files.exists(site, use_sudo=True):
+            msg = "Remote %s will be deleted.\n" \
+                  "This is necessary for django-fab-deploy to work.\n" \
+                  "Choose 'n' and do a backup if you have customized this file.\n"\
+                  "Do you wish to continue?"  % site
+            if not confirm or console.confirm(msg):
+                sudo('rm -f %s' % site)
+            else:
+                abort("Aborting.")
+
+
+    if _ports_conf_needs_disabling():
+        msg = "The contents of remote %s will be erased.\n"\
+              "This is necessary for django-fab-deploy to work.\n" \
+              "Choose 'n' and do a backup if you have customized this file.\n" \
+              "Do you wish to continue?" % APACHE_PORTS_FILE
+        if not confirm or console.confirm(msg):
+            _disable_ports_conf()
+        else:
+            abort("Aborting.")
+
 
 @task
 @utils.run_as_sudo
 
 # === automatic apache ports management ===
 
-@task
+@utils.run_as_sudo
+def _ports_conf_needs_disabling():
+    lines = _ports_lines()
+    if lines[0] == TAKEOVER_STRING:
+        return False
+    if lines[0] == OLD_TAKEOVER_STRING:
+        # ports.conf from old DFD versions should be harmless
+        # and it is necessary to preserve it if there are sites managed by
+        # DFD < 0.8 on the same server.
+        return False
+    return True
+
+
 @utils.run_as_sudo
 def _disable_ports_conf():
-    lines = _ports_lines()
-    if lines[0] == TAKEOVER_STRING:
-        return
     sudo("echo '%s\n' > %s" % (TAKEOVER_STRING, APACHE_PORTS_FILE))
 
 #@task

fab_deploy_tests/tests/base.py

 from __future__ import absolute_import
-from fabric.api import *
+from fabric.api import env, run
 from fabric.state import _AttributeDict
 from fabtest import FabTest
 from fab_deploy.utils import update_env
 
     def setup_env(self):
         super(FabDeployTest, self).setup_env()
+        env.abort_on_prompts = True
         self.setup_conf()
         update_env()
 

fab_deploy_tests/tests/deploy.py

 from __future__ import absolute_import
+import os
 from urllib2 import HTTPError
-import os
-from fabric.api import *
+import mock
+import contextlib
+from fabric.api import hide, run, env, settings
 from fabtest import fab, vbox_urlopen, FabricAbortException
 from fab_deploy.utils import run_as
 from fab_deploy.deploy import deploy_project, push, remove
 from ..test_project2.fabfile import foo_site as foo_site2
 from ..test_project3.fabfile import postgis_site, postgres_site
 
+@contextlib.contextmanager
+def answer_yes():
+    with mock.patch('fabric.contrib.console.confirm') as confirm:
+        confirm.return_value = True # answer 'YES' for all questions
+        yield
+
+
 def get_file_content(remote_file):
     @run_as('root')
     def cat():
         self.assertFalse(is_local_port_bound(port))
 
     def test_apache_config(self):
-        fab(apache.install)
+        fab(apache.install, confirm=False)
 
         # first site
         fab(foo_site)
 
         # deploy first site
         fab(foo_site)
-        fab(deploy_project)
+
+        with answer_yes():
+            fab(deploy_project)
 
         self.assertCommandAvailable('syncdb')
         self.assertCommandNotAvailable('syncddb')
 
         # deploy second site
         fab(bar_site)
-        fab(deploy_project)
+
+        with answer_yes():
+            fab(deploy_project)
 
         self.assertInstanceWorks('bar')
         self.assertInstanceWorks('foo')
         fab(invalid_site)
 
         # config errors should not be silenced
-        self.assertRaises(FabricAbortException, fab, deploy_project)
+        with answer_yes():
+            self.assertRaises(FabricAbortException, fab, deploy_project)
 
         # old sites still work
         self.assertInstanceWorks('bar')
         setup_ssh()
 
         fab(mysql.create_db)
-        fab(deploy_project)
+
+        with answer_yes():
+            fab(deploy_project)
+
         self.assertResponse(url, 'foo2')
 
         # just check that blank push doesn't break anything
         setup_ssh()
 
         fab(mysql.create_db)
-        fab(deploy_project)
+
+        with answer_yes():
+            fab(deploy_project)
+
         fab(my_push)
         self.assertTrue(before_restart.called)
 
         fab(db_backend.create_user)
         fab(db_backend.create_db)
 
-        fab(deploy_project)
+        with answer_yes():
+            fab(deploy_project)
 
         res = fab(db_backend.execute_sql, "select * from auth_user;")
         assert 'is_active' in res

fab_deploy_tests/tests/system_tests.py

         fab(prepare)
         self.assertPackageInstalled('python')
 
-        fab(apache.install)
+        fab(apache.install, confirm=False)
         fab(nginx.install)
         self.assertPackageInstalled('nginx')
         self.assertPackageInstalled('apache2')