← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~andrey-fedoseev/launchpad:snap-base-features into launchpad:master

 

Andrey Fedoseev has proposed merging ~andrey-fedoseev/launchpad:snap-base-features into launchpad:master.

Commit message:
Add `ISnapBase.features` field


Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/429878

The field is used to designate the features available for a snap base
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~andrey-fedoseev/launchpad:snap-base-features into launchpad:master.
diff --git a/lib/lp/snappy/interfaces/snapbase.py b/lib/lp/snappy/interfaces/snapbase.py
index 08cfd79..8fdc1f4 100644
--- a/lib/lp/snappy/interfaces/snapbase.py
+++ b/lib/lp/snappy/interfaces/snapbase.py
@@ -8,6 +8,8 @@ __all__ = [
     "ISnapBase",
     "ISnapBaseSet",
     "NoSuchSnapBase",
+    "ALLOW_DUPLICATE_BUILD_ON",
+    "SNAP_BASE_KNOWN_FEATURES",
 ]
 
 import http.client
@@ -74,6 +76,10 @@ class SnapBaseNameField(ContentNameField):
             return None
 
 
+ALLOW_DUPLICATE_BUILD_ON = "allow_duplicate_build_on"
+SNAP_BASE_KNOWN_FEATURES = {ALLOW_DUPLICATE_BUILD_ON}
+
+
 class ISnapBaseView(Interface):
     """`ISnapBase` attributes that anyone can view."""
 
@@ -181,6 +187,20 @@ class ISnapBaseEditableAttributes(Interface):
         )
     )
 
+    features = exported(
+        Dict(
+            title=_("Features supported by this base"),
+            key_type=TextLine(),
+            required=False,
+            readonly=False,
+            description=_(
+                "A dictionary designating the features supported by the base. "
+                "Key is the name of a feature, value is a boolean indicating "
+                "whether the feature is supported or not."
+            ),
+        )
+    )
+
 
 class ISnapBaseEdit(Interface):
     """`ISnapBase` methods that require launchpad.Edit permission."""
@@ -263,7 +283,14 @@ class ISnapBaseSetEdit(Interface):
         )
     )
     @export_factory_operation(
-        ISnapBase, ["name", "display_name", "distro_series", "build_channels"]
+        ISnapBase,
+        [
+            "name",
+            "display_name",
+            "distro_series",
+            "build_channels",
+            "features",
+        ],
     )
     @operation_for_version("devel")
     def new(
@@ -272,6 +299,7 @@ class ISnapBaseSetEdit(Interface):
         display_name,
         distro_series,
         build_channels,
+        features,
         processors=None,
         date_created=None,
     ):
diff --git a/lib/lp/snappy/model/snapbase.py b/lib/lp/snappy/model/snapbase.py
index f87a3e6..e92f91a 100644
--- a/lib/lp/snappy/model/snapbase.py
+++ b/lib/lp/snappy/model/snapbase.py
@@ -7,7 +7,10 @@ __all__ = [
     "SnapBase",
 ]
 
+from typing import Dict, Optional
+
 import pytz
+from storm.databases.postgres import JSON as PgJSON
 from storm.locals import (
     JSON,
     Bool,
@@ -29,6 +32,7 @@ from lp.registry.model.person import Person
 from lp.services.database.constants import DEFAULT
 from lp.services.database.interfaces import IMasterStore, IStore
 from lp.snappy.interfaces.snapbase import (
+    SNAP_BASE_KNOWN_FEATURES,
     CannotDeleteSnapBase,
     ISnapBase,
     ISnapBaseSet,
@@ -69,6 +73,8 @@ class SnapBase(Storm):
 
     is_default = Bool(name="is_default", allow_none=False)
 
+    _features = PgJSON(name="features", allow_none=False)
+
     def __init__(
         self,
         registrant,
@@ -76,6 +82,7 @@ class SnapBase(Storm):
         display_name,
         distro_series,
         build_channels,
+        features,
         date_created=DEFAULT,
     ):
         super().__init__()
@@ -85,8 +92,25 @@ class SnapBase(Storm):
         self.distro_series = distro_series
         self.build_channels = build_channels
         self.date_created = date_created
+        self.features = features
         self.is_default = False
 
+    @property
+    def features(self) -> Dict[str, bool]:
+        return {
+            k: v
+            for k, v in (self._features or {}).items()
+            if k in SNAP_BASE_KNOWN_FEATURES
+        }
+
+    @features.setter
+    def features(self, value: Optional[Dict[str, bool]]) -> None:
+        self._features = {
+            k: v
+            for k, v in (value or {}).items()
+            if k in SNAP_BASE_KNOWN_FEATURES
+        }
+
     def _getProcessors(self):
         return list(
             Store.of(self).find(
@@ -217,6 +241,7 @@ class SnapBaseSet:
         display_name,
         distro_series,
         build_channels,
+        features,
         processors=None,
         date_created=DEFAULT,
     ):
@@ -228,6 +253,7 @@ class SnapBaseSet:
             display_name,
             distro_series,
             build_channels,
+            features,
             date_created=date_created,
         )
         store.add(snap_base)
diff --git a/lib/lp/snappy/tests/test_snapbase.py b/lib/lp/snappy/tests/test_snapbase.py
index afd71c1..36d046e 100644
--- a/lib/lp/snappy/tests/test_snapbase.py
+++ b/lib/lp/snappy/tests/test_snapbase.py
@@ -17,6 +17,7 @@ from lp.app.interfaces.security import IAuthorization
 from lp.registry.interfaces.pocket import PackagePublishingPocket
 from lp.services.webapp.interfaces import OAuthPermission
 from lp.snappy.interfaces.snapbase import (
+    ALLOW_DUPLICATE_BUILD_ON,
     CannotDeleteSnapBase,
     ISnapBase,
     ISnapBaseSet,
@@ -69,6 +70,17 @@ class TestSnapBase(TestCaseWithFactory):
         getUtility(ISnapBaseSet).setDefault(snap_base)
         self.assertRaises(CannotDeleteSnapBase, snap_base.destroySelf)
 
+    def test_features(self):
+        snap_base = self.factory.makeSnapBase(
+            features={ALLOW_DUPLICATE_BUILD_ON: True, "unknown_feature": True}
+        )
+        self.assertEqual(
+            snap_base.features,
+            {
+                ALLOW_DUPLICATE_BUILD_ON: True,
+            },
+        )
+
 
 class TestSnapBaseProcessors(TestCaseWithFactory):
 
@@ -102,6 +114,7 @@ class TestSnapBaseProcessors(TestCaseWithFactory):
             display_name=self.factory.getUniqueUnicode(),
             distro_series=self.distroseries,
             build_channels={},
+            features={},
         )
         self.assertContentEqual(self.procs, snap_base.processors)
 
@@ -113,6 +126,7 @@ class TestSnapBaseProcessors(TestCaseWithFactory):
             display_name=self.factory.getUniqueUnicode(),
             distro_series=self.distroseries,
             build_channels={},
+            features={},
             processors=self.procs[:2],
         )
         self.assertContentEqual(self.procs[:2], snap_base.processors)
@@ -211,6 +225,7 @@ class TestSnapBaseWebservice(TestCaseWithFactory):
             display_name="Dummy",
             distro_series=distroseries_url,
             build_channels={"snapcraft": "stable"},
+            features={"allow_duplicate_build_on": True},
         )
         self.assertEqual(201, response.status)
         snap_base = webservice.get(response.getHeader("Location")).jsonBody()
@@ -228,6 +243,7 @@ class TestSnapBaseWebservice(TestCaseWithFactory):
                             webservice.getAbsoluteUrl(distroseries_url)
                         ),
                         "build_channels": Equals({"snapcraft": "stable"}),
+                        "features": Equals({"allow_duplicate_build_on": True}),
                         "is_default": Is(False),
                     }
                 ),
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 3fd560c..7cbd238 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -6372,6 +6372,7 @@ class LaunchpadObjectFactory(ObjectFactory):
         display_name=None,
         distro_series=None,
         build_channels=None,
+        features=None,
         processors=None,
         date_created=DEFAULT,
     ):
@@ -6394,6 +6395,7 @@ class LaunchpadObjectFactory(ObjectFactory):
             display_name,
             distro_series,
             build_channels,
+            features=features,
             processors=processors,
             date_created=date_created,
         )