← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] lp:~stevenk/launchpad/das-setchroot-api into lp:launchpad

 

Steve Kowalik has proposed merging lp:~stevenk/launchpad/das-setchroot-api into lp:launchpad.

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~stevenk/launchpad/das-setchroot-api/+merge/167464

Add a new API, IDistroArchSeries.setChroot() which allows owners of the relevant main_archive to upload chroots using the API.
-- 
https://code.launchpad.net/~stevenk/launchpad/das-setchroot-api/+merge/167464
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~stevenk/launchpad/das-setchroot-api into lp:launchpad.
=== modified file 'lib/lp/security.py'
--- lib/lp/security.py	2013-05-10 06:28:17 +0000
+++ lib/lp/security.py	2013-06-05 06:47:44 +0000
@@ -1346,6 +1346,16 @@
     usedfor = IDistroArchSeries
 
 
+class EditDistroArchSeries(AuthorizationBase):
+    permission = 'launchpad.Edit'
+    usedfor = IDistroArchSeries
+
+    def checkAuthenticated(self, user):
+        return (
+            user.isOwner(self.obj.distroseries.distribution.main_archive)
+            or user.in_admin)
+
+
 class ViewAnnouncement(AuthorizationBase):
     permission = 'launchpad.View'
     usedfor = IAnnouncement

=== modified file 'lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py'
--- lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2012-01-01 02:58:52 +0000
+++ lib/lp/soyuz/browser/tests/test_distroarchseries_webservice.py	2013-06-05 06:47:44 +0000
@@ -1,8 +1,14 @@
-# Copyright 2010-2011 Canonical Ltd.  This software is licensed under the
+# Copyright 2010-2013 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
 
+import hashlib
+
+from lazr.restfulclient.errors import (
+    BadRequest,
+    Unauthorized,
+    )
 from zope.security.management import endInteraction
 
 from lp.testing import (
@@ -10,13 +16,13 @@
     TestCaseWithFactory,
     ws_object,
     )
-from lp.testing.layers import DatabaseFunctionalLayer
+from lp.testing.layers import LaunchpadFunctionalLayer
 
 
 class TestDistroArchSeriesWebservice(TestCaseWithFactory):
     """Unit Tests for 'DistroArchSeries' Webservice.
     """
-    layer = DatabaseFunctionalLayer
+    layer = LaunchpadFunctionalLayer
 
     def _makeDistroArchSeries(self):
         """Create a `DistroSeries` object, that is prefilled with 1
@@ -38,8 +44,8 @@
         endInteraction()
         launchpad = launchpadlib_for('test', person=None, version='devel')
         ws_distroseries = ws_object(launchpad, distroseries)
-        #Note, we test the length of architectures.entries, not
-        #architectures due to the removal of the entries by lazr
+        # Note, we test the length of architectures.entries, not
+        # architectures due to the removal of the entries by lazr
         self.assertEqual(1, len(ws_distroseries.architectures.entries))
 
     def test_distroseries_architectures_authenticated(self):
@@ -51,3 +57,33 @@
         ws_distroseries = ws_object(launchpad, distroseries)
         #See note above regarding testing of length of .entries
         self.assertEqual(1, len(ws_distroseries.architectures.entries))
+
+    def test_setChroot_random_user(self):
+        # Random users are not allowed to set chroots.
+        das = self.factory.makeDistroArchSeries()
+        user = self.factory.makePerson()
+        webservice = launchpadlib_for("testing", user, version='devel')
+        ws_das = ws_object(webservice, das)
+        self.assertRaises(
+            Unauthorized, ws_das.setChroot, data='xyz',sha1sum='0')
+
+    def test_setChroot_wrong_sha1sum(self):
+        # If the sha1sum calculated is different, the chroot is not set.
+        das = self.factory.makeDistroArchSeries()
+        user = das.distroseries.distribution.main_archive.owner
+        webservice = launchpadlib_for("testing", user)
+        ws_das = ws_object(webservice, das)
+        self.assertRaises(
+            BadRequest, ws_das.setChroot, data='zyx', sha1sum='x')
+
+    def test_setChroot(self):
+        das = self.factory.makeDistroArchSeries()
+        user = das.distroseries.distribution.main_archive.owner
+        expected_file = 'chroot-%s-%s-%s.tar.bz2' % (
+            das.distroseries.distribution.name, das.distroseries.name,
+            das.architecturetag)
+        webservice = launchpadlib_for("testing", user)
+        ws_das = ws_object(webservice, das)
+        sha1 = hashlib.sha1('abcxyz').hexdigest()
+        ws_das.setChroot(data='abcxyz', sha1sum=sha1)
+        self.assertTrue(ws_das.chroot_url.endswith(expected_file))

=== modified file 'lib/lp/soyuz/configure.zcml'
--- lib/lp/soyuz/configure.zcml	2013-05-24 01:28:22 +0000
+++ lib/lp/soyuz/configure.zcml	2013-06-05 06:47:44 +0000
@@ -617,14 +617,17 @@
         <class
             class="lp.soyuz.model.distroarchseries.DistroArchSeries">
             <allow
-                interface="lp.soyuz.interfaces.distroarchseries.IDistroArchSeries"/>
+                interface="lp.soyuz.interfaces.distroarchseries.IDistroArchSeriesPublic"/>
             <allow
                 interface="lp.soyuz.interfaces.buildrecords.IHasBuildRecords"/>
             <allow
                 interface="lp.soyuz.interfaces.publishing.ICanPublishPackages"/>
             <require
                 permission="launchpad.Admin"
-                set_schema="lp.soyuz.interfaces.distroarchseries.IDistroArchSeries"/>
+                set_schema="lp.soyuz.interfaces.distroarchseries.IDistroArchSeriesPublic"/>
+            <require
+                permission="launchpad.Edit"
+                interface="lp.soyuz.interfaces.distroarchseries.IDistroArchSeriesEdit"/>
         </class>
         <adapter
             for="lp.soyuz.interfaces.distroarchseries.IDistroArchSeries"

=== modified file 'lib/lp/soyuz/interfaces/distroarchseries.py'
--- lib/lp/soyuz/interfaces/distroarchseries.py	2013-05-01 18:29:52 +0000
+++ lib/lp/soyuz/interfaces/distroarchseries.py	2013-06-05 06:47:44 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2013 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 """Distribution architecture series interfaces."""
@@ -7,12 +7,19 @@
 
 __all__ = [
     'IDistroArchSeries',
+    'InvalidChrootUploaded',
     'IPocketChroot',
     ]
 
+import httplib
+
 from lazr.restful.declarations import (
+    error_status,
     export_as_webservice_entry,
+    export_write_operation,
     exported,
+    operation_for_version,
+    operation_parameters,
     )
 from lazr.restful.fields import Reference
 from zope.interface import (
@@ -21,8 +28,10 @@
     )
 from zope.schema import (
     Bool,
+    Bytes,
     Choice,
     Int,
+    Text,
     TextLine,
     )
 
@@ -33,9 +42,13 @@
 from lp.registry.interfaces.role import IHasOwner
 
 
-class IDistroArchSeries(IHasOwner):
-    """DistroArchSeries Table Interface"""
-    export_as_webservice_entry()
+@error_status(httplib.BAD_REQUEST)
+class InvalidChrootUploaded(Exception):
+    """Raised when the sha1sum of an uploaded chroot does not match."""
+
+
+class IDistroArchSeriesPublic(IHasOwner):
+    """Public attributes for a DistroArchSeries."""
 
     id = Attribute("Identifier")
     distroseries = exported(
@@ -185,18 +198,30 @@
         this distro arch series.
         """
 
+class IDistroArchSeriesEdit(Interface):
+
+    @operation_parameters(data=Bytes(), sha1sum=Text())
+    @export_write_operation()
+    @operation_for_version("devel")
+    def setChroot(data, sha1sum):
+        """Upload a new chroot, compare the computed checksum against the
+        provided value, and if they match, set the `IPocketChroot` to the new
+        value.
+        """
+
+
+class IDistroArchSeries(IDistroArchSeriesPublic, IDistroArchSeriesEdit):
+    """An architecture for a distroseries."""
+    export_as_webservice_entry()
+
 
 class IPocketChroot(Interface):
     """PocketChroot Table Interface"""
     id = Attribute("Identifier")
-    distroarchseries = Attribute("The DistroArchSeries this chroot "
-                                  "belongs to.")
+    distroarchseries = Attribute(
+        "The DistroArchSeries this chroot belongs to.")
     pocket = Attribute("The Pocket this chroot is for.")
     chroot = Attribute("The file alias of the chroot.")
 
     def syncUpdate():
         """Commit changes to DB."""
-
-
-# Monkey patching circular import fixes is done in
-# _schema_circular_imports.py

=== modified file 'lib/lp/soyuz/model/distroarchseries.py'
--- lib/lp/soyuz/model/distroarchseries.py	2013-02-06 09:22:35 +0000
+++ lib/lp/soyuz/model/distroarchseries.py	2013-06-05 06:47:44 +0000
@@ -1,4 +1,4 @@
-# Copyright 2009-2012 Canonical Ltd.  This software is licensed under the
+# Copyright 2009-2013 Canonical Ltd.  This software is licensed under the
 # GNU Affero General Public License version 3 (see the file LICENSE).
 
 __metaclass__ = type
@@ -7,6 +7,8 @@
     'PocketChroot'
     ]
 
+from cStringIO import StringIO
+
 from sqlobject import (
     BoolCol,
     ForeignKey,
@@ -39,12 +41,17 @@
     sqlvalues,
     )
 from lp.services.helpers import shortlist
+from lp.services.librarian.interfaces import ILibraryFileAliasSet
+from lp.services.webapp.publisher import (
+    get_raw_form_value_from_current_request,
+    )
 from lp.soyuz.enums import PackagePublishingStatus
 from lp.soyuz.interfaces.binarypackagebuild import IBinaryPackageBuildSet
 from lp.soyuz.interfaces.binarypackagename import IBinaryPackageName
 from lp.soyuz.interfaces.buildrecords import IHasBuildRecords
 from lp.soyuz.interfaces.distroarchseries import (
     IDistroArchSeries,
+    InvalidChrootUploaded,
     IPocketChroot,
     )
 from lp.soyuz.interfaces.publishing import ICanPublishPackages
@@ -166,6 +173,23 @@
 
         return pocket_chroot
 
+    def setChroot(self, data, sha1sum):
+        """See `IDistroArchSeries`."""
+        data = get_raw_form_value_from_current_request('data')
+        if isinstance(data, str):
+            filecontent = data
+        else:
+            filecontent = data.read()
+        filename = 'chroot-%s-%s-%s.tar.bz2' % (
+            self.distroseries.distribution.name, self.distroseries.name,
+            self.architecturetag)
+        lfa = getUtility(ILibraryFileAliasSet).create(
+            name=filename, size=len(filecontent), file=StringIO(filecontent),
+            contentType='application/octet-stream')
+        if lfa.content.sha1 != sha1sum:
+            raise InvalidChrootUploaded("Chroot upload checksums do not match")
+        self.addOrUpdateChroot(lfa)
+
     def searchBinaryPackages(self, text):
         """See `IDistroArchSeries`."""
         from lp.soyuz.model.publishing import BinaryPackagePublishingHistory