launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #02874
[Merge] lp:~julian-edwards/launchpad/publisher-config-db-schema into lp:launchpad
Julian Edwards has proposed merging lp:~julian-edwards/launchpad/publisher-config-db-schema into lp:launchpad.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~julian-edwards/launchpad/publisher-config-db-schema/+merge/52411
= Summary =
New PublisherConfig table
== Proposed fix ==
This branch adds a new PublisherConfig table which will eventually deprecate
the archivepublisher config section.
The schema allows for separate configurations for each hosted distribution
which is required as part of the Derived Distributions feature. When we start
hosting multiple distributions, the single configuration that currently exists
for the purposes of publishing Ubuntu will not suffice. The configured
options are:
* The base path for the archive
* The URL to the archive
It's entirely possible that custom distros will be hosted in an entirely
different disk area to Ubuntu, so we need to retain this configurability for
each distro.
The intention is to add a trivial LaunchpadEditForm page after this lands so
that we can set up the data, fix the rest of the code to use it, and delete
the original config.
== Implementation details ==
Fairly trivial schema change plus new model code.
== Tests ==
bin/test -cvv test_publisherconfig
== Demo and Q/A ==
n/a yet
--
https://code.launchpad.net/~julian-edwards/launchpad/publisher-config-db-schema/+merge/52411
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~julian-edwards/launchpad/publisher-config-db-schema into lp:launchpad.
=== added file 'database/schema/patch-2208-99-0.sql'
--- database/schema/patch-2208-99-0.sql 1970-01-01 00:00:00 +0000
+++ database/schema/patch-2208-99-0.sql 2011-03-07 13:21:41 +0000
@@ -0,0 +1,14 @@
+SET client_min_messages=ERROR;
+
+CREATE TABLE PublisherConfig (
+ id serial PRIMARY KEY,
+ distribution integer NOT NULL CONSTRAINT publisherconfig__distribution__fk REFERENCES distribution,
+ root_dir text NOT NULL,
+ base_url text NOT NULL,
+ copy_base_url text NOT NULL
+);
+
+CREATE UNIQUE INDEX publisherconfig__distribution__idx
+ ON PublisherConfig(distribution);
+
+INSERT INTO LaunchpadDatabaseRevision VALUES (2208, 99, 0);
=== modified file 'database/schema/security.cfg'
--- database/schema/security.cfg 2011-03-07 07:27:56 +0000
+++ database/schema/security.cfg 2011-03-07 13:21:41 +0000
@@ -260,6 +260,7 @@
public.productrelease = SELECT, INSERT, UPDATE, DELETE
public.productreleasefile = SELECT, INSERT, DELETE
public.productseriescodeimport = SELECT, INSERT, UPDATE
+public.publisherconfig = SELECT, INSERT, UPDATE, DELETE
public.project = SELECT
public.projectbounty = SELECT, INSERT, UPDATE
public.questionbug = SELECT, INSERT, DELETE
=== modified file 'lib/lp/archivepublisher/config.py'
--- lib/lp/archivepublisher/config.py 2010-10-17 13:35:20 +0000
+++ lib/lp/archivepublisher/config.py 2011-03-07 13:21:41 +0000
@@ -83,10 +83,6 @@
return pubconf
-class LucilleConfigError(Exception):
- """Lucille configuration was not present."""
-
-
class Config(object):
"""Manage a publisher configuration from the database. (Read Only)
This class provides a useful abstraction so that if we change
=== modified file 'lib/lp/archivepublisher/deathrow.py'
--- lib/lp/archivepublisher/deathrow.py 2010-10-17 13:35:20 +0000
+++ lib/lp/archivepublisher/deathrow.py 2011-03-07 13:21:41 +0000
@@ -17,7 +17,6 @@
from lp.archivepublisher import ELIGIBLE_DOMINATION_STATES
from lp.archivepublisher.config import (
getPubConfig,
- LucilleConfigError,
)
from lp.archivepublisher.diskpool import DiskPool
from lp.archivepublisher.utils import process_in_batches
@@ -40,12 +39,8 @@
the one provided by the publishing-configuration, it will be only
used for PRIMARY archives.
"""
- log.debug("Grab Lucille config.")
- try:
- pubconf = getPubConfig(archive)
- except LucilleConfigError, info:
- log.error(info)
- raise
+ log.debug("Grab publisher config.")
+ pubconf = getPubConfig(archive)
if (pool_root_override is not None and
archive.purpose == ArchivePurpose.PRIMARY):
=== added file 'lib/lp/archivepublisher/interfaces/publisherconfig.py'
--- lib/lp/archivepublisher/interfaces/publisherconfig.py 1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/interfaces/publisherconfig.py 2011-03-07 13:21:41 +0000
@@ -0,0 +1,58 @@
+# Copyright 2011 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+# pylint: disable-msg=E0211,E0213
+
+"""PublisherConfig interface."""
+
+__metaclass__ = type
+
+__all__ = [
+ 'IPublisherConfig',
+ 'IPublisherConfigSet',
+ ]
+
+from lazr.restful.fields import Reference
+from zope.interface import Interface
+from zope.schema import (
+ Int,
+ TextLine,
+ )
+
+from canonical.launchpad import _
+from lp.registry.interfaces.distribution import IDistribution
+
+
+class IPublisherConfig(Interface):
+ """`PublisherConfig` interface."""
+
+ id = Int(title=_('ID'), required=True, readonly=True)
+
+ distribution = Reference(
+ IDistribution, title=_("Distribution"), required=True,
+ description=_("The Distribution for this configuration."))
+
+ root_dir = TextLine(
+ title=_("Root Directory"), required=True,
+ description=_("The root directory for published archives."))
+
+ base_url = TextLine(
+ title=_("Base URL"), required=True,
+ description=_("The base URL for published archives"))
+
+ copy_base_url = TextLine(
+ title=_("Copy Base URL"), required=True,
+ description=_("The base URL for published copy archives"))
+
+
+class IPublisherConfigSet(Interface):
+ """`PublisherConfigSet` interface."""
+
+ def new(distribution, root_dir, base_url, copy_base_url):
+ """Create a new `PublisherConfig`."""
+
+ def getByDistribution(distribution):
+ """Get the config for a a distribution.
+
+ :param distribution: An `IDistribution`
+ """
=== added file 'lib/lp/archivepublisher/model/publisherconfig.py'
--- lib/lp/archivepublisher/model/publisherconfig.py 1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/model/publisherconfig.py 2011-03-07 13:21:41 +0000
@@ -0,0 +1,68 @@
+# Copyright 2011 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Database class for table PublisherConfig."""
+
+__metaclass__ = type
+
+__all__ = [
+ 'PublisherConfig',
+ 'PublisherConfigSet',
+ ]
+
+from storm.locals import (
+ Int,
+ Reference,
+ Storm,
+ RawStr,
+ )
+from zope.interface import implements
+
+from canonical.launchpad.interfaces.lpstorm import (
+ IMasterStore,
+ )
+from lp.archivepublisher.interfaces.publisherconfig import (
+ IPublisherConfig,
+ IPublisherConfigSet,
+ )
+
+
+class PublisherConfig(Storm):
+ """See `IArchiveAuthToken`."""
+ implements(IPublisherConfig)
+ __storm_table__ = 'PublisherConfig'
+
+ id = Int(primary=True)
+
+ distribution_id = Int(name='distribution', allow_none=False)
+ distribution = Reference(distribution_id, 'Distribution.id')
+
+ root_dir = RawStr(name='root_dir', allow_none=False)
+
+ base_url = RawStr(name='base_url', allow_none=False)
+
+ copy_base_url = RawStr(name='copy_base_url', allow_none=False)
+
+
+class PublisherConfigSet:
+ """See `IPublisherConfigSet`."""
+ implements(IPublisherConfigSet)
+ title = "Soyuz Publisher Configurations"
+
+ def new(self, distribution, root_dir, base_url, copy_base_url):
+ """Make and return a new `PublisherConfig`."""
+ store = IMasterStore(PublisherConfig)
+ pubconf = PublisherConfig()
+ pubconf.distribution = distribution
+ pubconf.root_dir = root_dir
+ pubconf.base_url = base_url
+ pubconf.copy_base_url = copy_base_url
+ store.add(pubconf)
+ return pubconf
+
+ def getByDistribution(self, distribution):
+ """See `IArchiveAuthTokenSet`."""
+ store = IMasterStore(PublisherConfig)
+ return store.find(
+ PublisherConfig,
+ PublisherConfig.distribution_id == distribution.id).one()
=== modified file 'lib/lp/archivepublisher/publishing.py'
--- lib/lp/archivepublisher/publishing.py 2011-02-04 09:07:36 +0000
+++ lib/lp/archivepublisher/publishing.py 2011-03-07 13:21:41 +0000
@@ -21,7 +21,6 @@
from lp.archivepublisher import HARDCODED_COMPONENT_ORDER
from lp.archivepublisher.config import (
getPubConfig,
- LucilleConfigError,
)
from lp.archivepublisher.diskpool import DiskPool
from lp.archivepublisher.domination import Dominator
@@ -120,11 +119,7 @@
else:
log.debug("Finding configuration for '%s' PPA."
% archive.owner.name)
- try:
- pubconf = getPubConfig(archive)
- except LucilleConfigError, info:
- log.error(info)
- raise
+ pubconf = getPubConfig(archive)
disk_pool = _getDiskPool(pubconf, log)
=== added file 'lib/lp/archivepublisher/tests/test_publisherconfig.py'
--- lib/lp/archivepublisher/tests/test_publisherconfig.py 1970-01-01 00:00:00 +0000
+++ lib/lp/archivepublisher/tests/test_publisherconfig.py 2011-03-07 13:21:41 +0000
@@ -0,0 +1,66 @@
+# Copyright 2011 Canonical Ltd. This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for publisherConfig model class."""
+
+__metaclass__ = type
+
+
+from storm.store import Store
+from storm.exceptions import IntegrityError
+from zope.component import getUtility
+from zope.interface.verify import verifyObject
+
+from canonical.testing.layers import ZopelessDatabaseLayer
+from lp.archivepublisher.interfaces.publisherconfig import (
+ IPublisherConfig,
+ IPublisherConfigSet,
+ )
+from lp.testing import TestCaseWithFactory
+
+
+class TestPublisherConfig(TestCaseWithFactory):
+ """Test the `PublisherConfig` model."""
+ layer = ZopelessDatabaseLayer
+
+ def setUp(self):
+ TestCaseWithFactory.setUp(self)
+ self.distribution = self.factory.makeDistribution(name='conftest')
+
+ def test_verify_interface(self):
+ # Test the interface for the model.
+ pubconf = self.factory.makePublisherConfig()
+ verified = verifyObject(IPublisherConfig, pubconf)
+ self.assertTrue(verified)
+
+ def test_properties(self):
+ # Test the model properties.
+ ROOT_DIR = "rootdir/test"
+ BASE_URL = "http://base.url"
+ COPY_BASE_URL = "http://base.url"
+ pubconf = self.factory.makePublisherConfig(
+ distribution=self.distribution,
+ root_dir=ROOT_DIR,
+ base_url=BASE_URL,
+ copy_base_url=COPY_BASE_URL,
+ )
+
+ self.assertEqual(self.distribution.name, pubconf.distribution.name)
+ self.assertEqual(ROOT_DIR, pubconf.root_dir)
+ self.assertEqual(BASE_URL, pubconf.base_url)
+ self.assertEqual(COPY_BASE_URL, pubconf.copy_base_url)
+
+ def test_one_config_per_distro(self):
+ # Only one config for each distro is allowed.
+ pubconf = self.factory.makePublisherConfig(self.distribution)
+ pubconf2 = self.factory.makePublisherConfig(self.distribution)
+ store = Store.of(pubconf)
+ self.assertRaises(IntegrityError, store.flush)
+
+ def test_getByDistribution(self):
+ # Test that IPublisherConfigSet.getByDistribution works.
+ pubconf = self.factory.makePublisherConfig(
+ distribution=self.distribution)
+ pubconf = getUtility(IPublisherConfigSet).getByDistribution(
+ self.distribution)
+ self.assertEqual(self.distribution.name, pubconf.distribution.name)
=== modified file 'lib/lp/archivepublisher/zcml/configure.zcml'
--- lib/lp/archivepublisher/zcml/configure.zcml 2009-07-13 18:15:02 +0000
+++ lib/lp/archivepublisher/zcml/configure.zcml 2011-03-07 13:21:41 +0000
@@ -2,8 +2,30 @@
GNU Affero General Public License version 3 (see the file LICENSE).
-->
-<configure xmlns="http://namespaces.zope.org/zope">
+<configure
+ xmlns="http://namespaces.zope.org/zope"
+ xmlns:browser="http://namespaces.zope.org/browser"
+ xmlns:i18n="http://namespaces.zope.org/i18n"
+ xmlns:webservice="http://namespaces.canonical.com/webservice"
+ xmlns:xmlrpc="http://namespaces.zope.org/xmlrpc"
+ i18n_domain="launchpad">
+
<include package="lp.archivepublisher.zcml"
file="archivesigningkey.zcml" />
+
+ <securedutility
+ class="lp.archivepublisher.model.publisherconfig.PublisherConfigSet"
+ provides="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfigSet">
+ <allow
+ interface="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfigSet"/>
+ </securedutility>
+
+ <class
+ class="lp.archivepublisher.model.publisherconfig.PublisherConfig">
+ <allow
+ interface="lp.archivepublisher.interfaces.publisherconfig.IPublisherConfig" />
+ </class>
+
+
</configure>
=== modified file 'lib/lp/testing/factory.py'
--- lib/lp/testing/factory.py 2011-03-06 23:15:05 +0000
+++ lib/lp/testing/factory.py 2011-03-07 13:21:41 +0000
@@ -101,6 +101,7 @@
)
from canonical.launchpad.webapp.sorting import sorted_version_numbers
from lp.app.enums import ServiceUsage
+from lp.archivepublisher.interfaces.publisherconfig import IPublisherConfigSet
from lp.archiveuploader.dscfile import DSCFile
from lp.archiveuploader.uploadpolicy import BuildDaemonUploadPolicy
from lp.blueprints.enums import (
@@ -3850,6 +3851,20 @@
description = self.getUniqueString()
return getUtility(ICveSet).new(sequence, description, cvestate)
+ def makePublisherConfig(self, distribution=None, root_dir=None,
+ base_url=None, copy_base_url=None):
+ """Create a new `PublisherConfig` record."""
+ if distribution is None:
+ distribution = self.makeDistribution()
+ if root_dir is None:
+ root_dir = self.getUniqueString()
+ if base_url is None:
+ base_url = self.getUniqueString()
+ if copy_base_url is None:
+ copy_base_url = self.getUniqueString()
+ return getUtility(IPublisherConfigSet).new(
+ distribution, root_dir, base_url, copy_base_url)
+
# Some factory methods return simple Python types. We don't add
# security wrappers for them, as well as for objects created by