← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~allenap/maas/database-run into lp:maas

 

Gavin Panella has proposed merging lp:~allenap/maas/database-run into lp:maas with lp:~allenap/maas/convert-enums-redux as a prerequisite.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~allenap/maas/database-run/+merge/104759

This moves the management of the database cluster into a tested Python module. I attempted to use van.pg, but it wasn't quite suitable; the exposed API was too restrictive.

The database (including cluster) are also now managed as a service, but to support testing and other uses, it also allows direct use of the new bin/database command to launch a shell, get status, stop, or destroy the cluster. Each user of the cluster also takes out a lock on the cluster, so that requests to stop it or shut it down while in use are not followed.

-- 
https://code.launchpad.net/~allenap/maas/database-run/+merge/104759
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/maas/database-run into lp:maas.
=== modified file 'Makefile'
--- Makefile	2012-05-18 13:11:58 +0000
+++ Makefile	2012-05-21 14:04:25 +0000
@@ -7,6 +7,7 @@
 
 build: \
     bin/buildout \
+    bin/database \
     bin/maas bin/test.maas \
     bin/twistd.pserv bin/test.pserv \
     bin/twistd.txlongpoll \
@@ -19,6 +20,10 @@
 	$(python) bootstrap.py --distribute --setup-source distribute_setup.py
 	@touch --no-create $@  # Ensure it's newer than its dependencies.
 
+bin/database: bin/buildout buildout.cfg versions.cfg setup.py
+	bin/buildout install database
+	@touch --no-create $@
+
 bin/maas: bin/buildout buildout.cfg versions.cfg setup.py $(js_enums)
 	bin/buildout install maas
 	@touch --no-create $@
@@ -51,9 +56,6 @@
 	bin/buildout install repl
 	@touch --no-create bin/py bin/ipy
 
-dev-db:
-	utilities/maasdb start ./db/ disposable
-
 test: bin/test.maas bin/test.pserv $(js_enums)
 	bin/test.maas
 	bin/test.pserv
@@ -99,7 +101,6 @@
 	$(RM) celerybeat-schedule
 
 distclean: clean stop
-	utilities/maasdb delete-cluster ./db/
 	$(RM) -r eggs develop-eggs
 	$(RM) -r bin build dist logs/* parts
 	$(RM) tags TAGS .installed.cfg
@@ -109,10 +110,13 @@
 	$(RM) -r run/* services/*/supervise
 	$(RM) twisted/plugins/dropin.cache
 
-harness: bin/maas dev-db
+harness: bin/maas services/database/@start
 	bin/maas shell --settings=maas.demo
 
-syncdb: bin/maas dev-db
+dbharness: bin/database
+	bin/database shell --preserve
+
+syncdb: bin/maas services/database/@start
 	bin/maas syncdb --noinput
 	bin/maas migrate maasserver --noinput
 	bin/maas migrate metadataserver --noinput
@@ -121,7 +125,7 @@
   build
   check
   clean
-  dev-db
+  dbharness
   distclean
   doc
   enums
@@ -138,7 +142,7 @@
 # Development services.
 #
 
-service_names := pserv celeryd reloader txlongpoll web webapp
+service_names := celeryd database pserv reloader txlongpoll web webapp
 services := $(patsubst %,services/%/,$(service_names))
 
 run:
@@ -211,6 +215,10 @@
 
 # Dependencies for individual services.
 
+services/celeryd/@deps:
+
+services/database/@deps: bin/database
+
 services/pserv/@deps: bin/twistd.pserv
 
 services/reloader/@deps:
@@ -219,9 +227,7 @@
 
 services/web/@deps:
 
-services/webapp/@deps: bin/maas dev-db
-
-services/celeryd/@deps:
+services/webapp/@deps: bin/maas
 
 #
 # Phony stuff.

=== modified file 'buildout.cfg'
--- buildout.cfg	2012-05-15 13:32:50 +0000
+++ buildout.cfg	2012-05-21 14:04:25 +0000
@@ -47,7 +47,6 @@
   # Convenient developer dependencies
   Jinja2
   Pygments
-  pyinotify
   Sphinx
   docutils
   lxml
@@ -59,35 +58,44 @@
 extra-paths =
   ${buildout:directory}/src
   ${buildout:directory}
-dev-eggs =
-  pyinotify
 test-eggs =
   coverage
   fixtures
   lxml
   nose
   nose-subunit
+  postgresfixture
   python-subunit
   sst
   testresources
   testscenarios
   testtools
 
+[database]
+recipe = z3c.recipe.scripts
+eggs = postgresfixture
+extra-paths = ${common:extra-paths}
+interpreter =
+entry-points = database=postgresfixture.main:main
+
 [maas]
 recipe = zc.recipe.egg
 # avahi and dbus should be listed as eggs
 # but they don't have links on PyPI and that makes buildout really
 # unhappy. It refuses to see them, even if they are in site-packages :-(
 # We rely on them being installed through system packages instead.
-eggs =
-  ${common:dev-eggs}
-  ${common:test-eggs}
-  celery
-  convoy
-  django
-  django-celery
+dev-eggs =
   django-debug-toolbar
+test-eggs =
+  ${common:test-eggs}
   django-nose
+eggs =
+  ${maas:dev-eggs}
+  ${maas:test-eggs}
+  celery
+  convoy
+  django
+  django-celery
   django-piston
   docutils
   oauth
@@ -124,7 +132,6 @@
 [pserv]
 recipe = zc.recipe.egg
 eggs =
-  ${common:dev-eggs}
   formencode
   oops-datedir-repo
   oops-twisted

=== modified file 'qa/scripts/prepare-for-juju-with-vdenv'
--- qa/scripts/prepare-for-juju-with-vdenv	2012-03-22 12:31:16 +0000
+++ qa/scripts/prepare-for-juju-with-vdenv	2012-05-21 14:04:25 +0000
@@ -37,7 +37,7 @@
     --username "${LOGNAME}" --password test \
     --email "${LOGNAME}@example.com"
 bin/maas reconcile
-utilities/maasdb shell db <<'EOF'
+make dbharness <<'EOF'
 UPDATE maasserver_node
    SET owner_id = NULL, status = 4
  WHERE hostname LIKE 'odev-node%';

=== added directory 'services/database'
=== added file 'services/database/down'
=== added file 'services/database/run'
--- services/database/run	1970-01-01 00:00:00 +0000
+++ services/database/run	2012-05-21 14:04:25 +0000
@@ -0,0 +1,19 @@
+#!/usr/bin/env bash
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# Exit immediately if a command exits with a non-zero status.
+set -o errexit
+# Treat unset variables as an error when substituting.
+set -o nounset
+
+# Move to the project root.
+cd "$(dirname "$0")/../.."
+
+# Start logging, if requested. Not using multilog here right now
+# because there are race issues when restarting.
+[ -z "${logdir:-}" ] || exec &>> "${logdir}/current"
+
+# Start the database.
+script="$(readlink -f bin/database)"
+exec "${script}" run --preserve

=== modified file 'src/maas/demo.py'
--- src/maas/demo.py	2012-04-18 10:51:03 +0000
+++ src/maas/demo.py	2012-05-21 14:04:25 +0000
@@ -11,7 +11,7 @@
 
 __metaclass__ = type
 
-import os
+from os.path import abspath
 
 from maas import (
     development,
@@ -27,7 +27,7 @@
 import_settings(settings)
 import_settings(development)
 
-MEDIA_ROOT = os.path.join(os.getcwd(), "media/demo")
+MEDIA_ROOT = abspath("media/demo")
 
 MIDDLEWARE_CLASSES += (
     'debug_toolbar.middleware.DebugToolbarMiddleware',
@@ -39,7 +39,7 @@
 # For demo purposes, use a real provisioning server.
 USE_REAL_PSERV = True
 
-MAAS_CLI = os.path.join(os.getcwd(), 'bin', 'maas')
+MAAS_CLI = abspath("bin/maas")
 
 RABBITMQ_PUBLISH = True
 

=== modified file 'src/maas/development.py'
--- src/maas/development.py	2012-04-16 10:00:51 +0000
+++ src/maas/development.py	2012-05-21 14:04:25 +0000
@@ -12,7 +12,7 @@
 __metaclass__ = type
 
 import logging
-import os
+from os.path import abspath
 
 from maas import (
     import_local_settings,
@@ -58,13 +58,13 @@
         'NAME': 'maas',
         # For PostgreSQL, a "hostname" starting with a slash indicates a
         # Unix socket directory.
-        'HOST': '%s/db' % os.getcwd(),
+        'HOST': abspath('db'),
     }
 }
 
 # Absolute filesystem path to the directory that will hold user-uploaded files.
 # Example: "/home/media/media.lawrence.com/media/"
-MEDIA_ROOT = os.path.join(os.getcwd(), "media/development")
+MEDIA_ROOT = abspath("media/development")
 
 INSTALLED_APPS += (
     'django.contrib.admin',

=== added file 'src/maasserver/management/commands/dbshell.py'
--- src/maasserver/management/commands/dbshell.py	1970-01-01 00:00:00 +0000
+++ src/maasserver/management/commands/dbshell.py	2012-05-21 14:04:25 +0000
@@ -0,0 +1,37 @@
+# Copyright 2012 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Django command: start a database shell.
+
+Overrides the default implementation.
+"""
+
+from __future__ import (
+    absolute_import,
+    print_function,
+    unicode_literals,
+    )
+
+__metaclass__ = type
+__all__ = ['Command']
+
+from django.core.management.commands import dbshell
+from django.db import (
+    connections,
+    DEFAULT_DB_ALIAS,
+    )
+from postgresfixture import ClusterFixture
+
+
+class Command(dbshell.Command):
+    """Customized "dbshell" command."""
+
+    def handle(self, **options):
+        # Don't call up to Django's dbshell, because that ends up exec'ing the
+        # shell, preventing this from clearing down the fixture.
+        connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
+        datadir = connection.settings_dict["HOST"]
+        with ClusterFixture(datadir, preserve=True) as cluster:
+            dbname = connection.settings_dict["NAME"]
+            cluster.createdb(dbname)
+            cluster.shell(dbname)

=== removed file 'src/maasserver/management/commands/deletedb.py'
--- src/maasserver/management/commands/deletedb.py	2012-04-16 10:00:51 +0000
+++ src/maasserver/management/commands/deletedb.py	1970-01-01 00:00:00 +0000
@@ -1,31 +0,0 @@
-# Copyright 2012 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Django command: stop and delete the local database cluster."""
-
-from __future__ import (
-    absolute_import,
-    print_function,
-    unicode_literals,
-    )
-
-__metaclass__ = type
-__all__ = ['Command']
-
-from subprocess import check_call
-
-from django.core.management.base import (
-    BaseCommand,
-    CommandError,
-    )
-
-
-class Command(BaseCommand):
-    """Stop and delete the local development database cluster."""
-
-    help = "Delete the development database cluster."
-
-    def handle(self, *args, **kwargs):
-        if len(args) != 0:
-            raise CommandError("Too many arguments.")
-        check_call(['utilities/maasdb', 'delete-cluster', 'db'])

=== removed file 'src/maasserver/management/commands/query.py'
--- src/maasserver/management/commands/query.py	2012-04-16 10:00:51 +0000
+++ src/maasserver/management/commands/query.py	1970-01-01 00:00:00 +0000
@@ -1,41 +0,0 @@
-# Copyright 2012 Canonical Ltd.  This software is licensed under the
-# GNU Affero General Public License version 3 (see the file LICENSE).
-
-"""Django command: access the development database directly in SQL."""
-
-from __future__ import (
-    absolute_import,
-    print_function,
-    unicode_literals,
-    )
-
-__metaclass__ = type
-__all__ = ['Command']
-
-from subprocess import check_call
-
-from django.core.management.base import (
-    BaseCommand,
-    CommandError,
-    )
-
-
-class Command(BaseCommand):
-    """Custom django command: access the local development database directly.
-
-    Executes an SQL statement given on the command line, or opens an SQL
-    shell if no statement was given.
-    """
-
-    args = "[SQL statement]"
-    help = "Access the database directly in SQL."
-
-    def handle(self, *args, **kwargs):
-        if len(args) > 1:
-            raise CommandError("Too many arguments.")
-        elif len(args) == 1:
-            subcommand = 'query'
-        else:
-            subcommand = 'shell'
-        check_call(
-            ['utilities/maasdb', subcommand, 'db'] + list(args))

=== modified file 'src/maastesting/runner.py'
--- src/maastesting/runner.py	2012-04-16 10:00:51 +0000
+++ src/maastesting/runner.py	2012-05-21 14:04:25 +0000
@@ -14,9 +14,9 @@
     "TestRunner",
     ]
 
-from subprocess import check_call
-
+from django.conf import settings
 from django_nose import NoseTestSuiteRunner
+from postgresfixture import ClusterFixture
 
 
 class TestRunner(NoseTestSuiteRunner):
@@ -24,5 +24,13 @@
 
     def setup_databases(self, *args, **kwargs):
         """Fire up the db cluster, then punt to original implementation."""
-        check_call(['utilities/maasdb', 'start', './db/', 'disposable'])
+        self.cluster = ClusterFixture("db", preserve=True)
+        self.cluster.setUp()
+        for database in settings.DATABASES.values():
+            if database["HOST"] == self.cluster.datadir:
+                self.cluster.createdb(database["NAME"])
         return super(TestRunner, self).setup_databases(*args, **kwargs)
+
+    def teardown_databases(self, *args, **kwargs):
+        super(TestRunner, self).teardown_databases(*args, **kwargs)
+        self.cluster.cleanUp()

=== removed file 'utilities/maasdb'
--- utilities/maasdb	2012-03-15 13:58:32 +0000
+++ utilities/maasdb	1970-01-01 00:00:00 +0000
@@ -1,207 +0,0 @@
-#! /bin/bash -e
-#
-# MAAS database control script.  See main() at the bottom for usage.
-#
-# Most functions take as their first argument a database cluster's data
-# directory.  This is where the database's socket, pidfile, log, and data will
-# reside.
-#
-# Some design choices for this module:
-#
-#  * Everything is PostgreSQL on Ubuntu.
-#  * Each branch gets its own cluster.  Kill & delete when done.
-#  * Databases run under the system user that creates them.  No root required.
-#  * No global configuration apart from a basic PostgreSQL install.
-#  * Connections use Unix sockets.  No TCP port hogging.
-
-POSTGRES_VERSION=9.1
-PGCTL="/usr/lib/postgresql/${POSTGRES_VERSION}/bin/pg_ctl"
-
-
-# Figure out the full absolute data directory path for a given cluster, even
-# if a relative path is given.
-maasdb_locate() {
-    local DATADIR
-    DATADIR="$1"
-
-    if test -z "$1"
-    then
-        echo "Specify a data directory for the MAAS database cluster." >&2
-        return 1
-    fi
-    if ! echo "$DATADIR" | grep '^/'
-    then
-        echo "`pwd`/$DATADIR"
-    fi
-}
-
-
-# Create a database cluster.
-maasdb_create_cluster() {
-    local DATADIR
-    DATADIR="`maasdb_locate "$1"`"
-
-    if ! test -d "$DATADIR/base"
-    then
-        mkdir -p -- "$DATADIR"
-        $PGCTL init -s -D "$DATADIR" -o '-E utf8 -A trust'
-    fi
-}
-
-
-# Start a database cluster.
-maasdb_start_cluster() {
-    local DATADIR DISPOSABLE EXTRA_POSTGRES_OPTS
-    DATADIR="`maasdb_locate "$1"`"
-    # Pass "disposable" as the second argument if the data in this database
-    # is not important at all and you're willing to cut corners for speed.
-    DISPOSABLE="$2"
-
-    if test "$DISPOSABLE" = "disposable"
-    then
-        #  -F -- don't bother fsync'ing.
-        EXTRA_POSTGRES_OPTS="-F"
-    else
-        EXTRA_POSTGRES_OPTS=""
-    fi
-
-    maasdb_create_cluster "$DATADIR"
-
-    if ! test -f "$DATADIR/postmaster.pid"
-    then
-        # pg_ctl options:
-        #  -D <dir> -- data directory.
-        #  -l <file> -- log file.
-        #  -s -- no informational messages.
-        #  -w -- wait until startup is complete.
-        # postgres options:
-        #  -h <arg> -- host name; empty arg means Unix socket only.
-        #  -k -- socket directory.
-        $PGCTL start \
-            -D "$DATADIR" -l "$DATADIR/backend.log" -s -w \
-            -o "-h '' -k '$DATADIR' $EXTRA_POSTGRES_OPTS"
-    fi
-}
-
-
-# Stop a database cluster.
-maasdb_stop_cluster() {
-    local DATADIR
-    DATADIR="`maasdb_locate "$1"`"
-
-    if test -f "$DATADIR/postmaster.pid"
-    then
-        $PGCTL stop -W -m fast -D "$DATADIR"
-    fi
-}
-
-
-# Initialize a MAAS database.
-maasdb_init_db() {
-    local DATADIR DISPOSABLE MARKER
-    DATADIR="`maasdb_locate "$1"`"
-    # Pass "disposable" as the second argument if the data in this database
-    # is not important at all and you're willing to cut corners for speed.
-    DISPOSABLE="$2"
-
-    maasdb_start_cluster "$DATADIR" "$DISPOSABLE"
-
-    MARKER="$DATADIR/maas-created"
-    if ! test -f "$MARKER"
-    then
-        createdb -h "$DATADIR" maas && touch "$MARKER"
-    fi
-}
-
-
-# Open a psql shell on a MAAS database.
-maasdb_shell() {
-    local DATADIR
-    DATADIR="`maasdb_locate "$1"`"
-
-    maasdb_init_db "$DATADIR"
-    psql -h "$DATADIR" maas
-}
-
-
-# Execute a query on a MAAS database.
-maasdb_query() {
-    local DATADIR QUERY
-    DATADIR="`maasdb_locate "$1"`"
-    QUERY="$2"
-
-    maasdb_init_db "$DATADIR"
-    psql -h "$DATADIR" maas -c "$QUERY"
-}
-
-
-# Delete an entire MAAS database and cluster.  Use only with extreme care!
-maasdb_delete_cluster() {
-    local DATADIR
-    DATADIR="`maasdb_locate "$1"`"
-
-    # Before deleting anything, does this at least look like a MAAS database
-    # cluster?
-    if test -d "$DATADIR/base"
-    then
-        maasdb_stop_cluster "$DATADIR"
-        # Removing the data directory may fail because of a race condition
-        # where db/global is nonempty because of a statistics write.  Rather
-        # than block on shutdown, be optimistic and spin on retries.
-        while ! rm -rf -- "$DATADIR"
-        do
-            # If this fails for a more persistent reason, too bad.  Ctrl-C.
-            echo "Retrying deletion of $DATADIR." >&2
-            sleep 0.01
-        done
-    fi
-}
-
-
-usage() {
-    cat <<EOF >&2
-Usage: maasdb <command> <cluster-path>
-
-Where <command> is one of:
-  start
-  stop
-  query
-  shell
-  delete-cluster
-
-And <cluster-path> is the path to the MAAS development database cluster,
-typically "./db"
-EOF
-}
-
-
-unknown_command() {
-    echo >&2 "** Unknown command: $1 **"
-    echo
-    usage
-    exit 1
-}
-
-
-main() {
-    local COMMAND DATADIR
-    COMMAND="$1"
-    DATADIR="$2"
-    if ! shift 2
-    then
-        usage
-        exit 1
-    fi
-
-    case "$COMMAND" in
-        start) maasdb_init_db "$DATADIR" "$@" ;;
-        stop) maasdb_stop_cluster "$DATADIR" "$@" ;;
-        query) maasdb_query "$DATADIR" "$@" ;;
-        shell) maasdb_shell "$DATADIR" "$@" ;;
-        delete-cluster) maasdb_delete_cluster "$DATADIR" "$@" ;;
-        *) unknown_command "$COMMAND" ;;
-    esac
-}
-
-
-main "$@"

=== modified file 'versions.cfg'
--- versions.cfg	2012-05-15 13:32:50 +0000
+++ versions.cfg	2012-05-21 14:04:25 +0000
@@ -31,8 +31,8 @@
 oops-datedir-repo = 0.0.17
 oops-twisted = 0.0.6
 oops-wsgi = 0.0.9
+postgresfixture = 0.1.1
 pyasn1 = 0.0.11a
-pyinotify = 0.9.2
 pymongo = 2.1.1
 python-dateutil = 1.5
 PyYAML = 3.10


Follow ups