← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~twom/launchpad:oci-registry into launchpad:master

 

Tom Wardill has proposed merging ~twom/launchpad:oci-registry into launchpad:master with ~twom/launchpad:oci-buildbehaviour as a prerequisite.

Commit message:
Add OCIRegistry model

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~twom/launchpad/+git/launchpad/+merge/376860

OCI Images need to be pushed somewhere so that they can be retrieved.
This adds a reference to OCIRecipe so that a built recipe can retrieve the details required to push the artifacts.
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~twom/launchpad:oci-registry into launchpad:master.
diff --git a/lib/lp/oci/configure.zcml b/lib/lp/oci/configure.zcml
index 70bb3bc..32150ec 100644
--- a/lib/lp/oci/configure.zcml
+++ b/lib/lp/oci/configure.zcml
@@ -52,6 +52,28 @@
         <allow interface="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource" />
     </securedutility>
 
+    <!-- OCIRegistry -->
+    <class
+        class="lp.oci.model.ociregistry.OCIRegistry">
+        <require
+            permission="launchpad.View"
+            interface="lp.oci.interfaces.ociregistry.IOCIRegistryView
+                       lp.oci.interfaces.ociregistry.IOCIRegistryEditableAttributes"/>
+        <require
+            permission="launchpad.Edit"
+            interface="lp.oci.interfaces.ociregistry.IOCIRegistryEdit"
+            set_schema="lp.oci.interfaces.ociregistry.IOCIRegistryEditableAttributes" />
+    </class>
+
+    <!-- OCIRegistrySet -->
+    <securedutility
+        class="lp.oci.model.ociregistry.OCIRegistrySet"
+        provides="lp.oci.interfaces.ociregistry.IOCIRegistrySet">
+        <allow
+            interface="lp.oci.interfaces.ociregistry.IOCIRegistrySet"/>
+    </securedutility>
+
+
     <adapter
         for="lp.oci.interfaces.ocirecipebuild.IOCIRecipeBuild"
         provides="lp.buildmaster.interfaces.buildfarmjobbehaviour.IBuildFarmJobBehaviour"
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index 9532834..7e11fa0 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -37,6 +37,7 @@ from zope.schema import (
 from zope.security.interfaces import Unauthorized
 
 from lp import _
+from lp.oci.interfaces.ociregistry import IOCIRegistry
 from lp.registry.interfaces.ociproject import IOCIProject
 from lp.registry.interfaces.role import IHasOwner
 from lp.services.fields import (
@@ -153,6 +154,11 @@ class IOCIRecipeEditableAttributes(IHasOwner):
         title=_("Build daily"), required=True, default=False,
         description=_("If True, this recipe should be built daily."))
 
+    registry = Reference(
+        IOCIRegistry,
+        title=_("The OCI registry that builds of "
+                "this recipe should be pushed to."))
+
 
 class IOCIRecipe(IOCIRecipeView, IOCIRecipeEdit, IOCIRecipeEditableAttributes):
     """A recipe for building Open Container Initiative images."""
diff --git a/lib/lp/oci/interfaces/ociregistry.py b/lib/lp/oci/interfaces/ociregistry.py
new file mode 100644
index 0000000..df65ddf
--- /dev/null
+++ b/lib/lp/oci/interfaces/ociregistry.py
@@ -0,0 +1,82 @@
+# Copyright 2019 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Interfaces for handling OCI registry details."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'IOCIRegistry',
+    'IOCIRegistryEdit',
+    'IOCIRegistryEditableAttributes',
+    'IOCIRegistryView',
+    'IOCIRegistrySet',
+]
+
+from zope.interface import Interface
+from zope.schema import (
+    Bool,
+    Datetime,
+    Int,
+    TextLine,
+    )
+
+from lp import _
+from lp.app.validators.name import name_validator
+from lp.app.validators.url import validate_url
+from lp.services.fields import PublicPersonChoice
+
+
+class IOCIRegistryView(Interface):
+    """`IOCIRegistry` attributes that require launchpad.View permission."""
+
+    id = Int(title=_("ID"), required=True, readonly=True)
+    date_created = Datetime(
+        title=_("The date on which this registry was created in Launchpad."),
+        required=True, readonly=True)
+
+    registrant = PublicPersonChoice(
+        title=_("The user who registered this registry."),
+        vocabulary='ValidPersonOrTeam', required=True, readonly=True)
+
+
+class IOCIRegistryEditableAttributes(Interface):
+    """`IOCIRegistry` attributes that can be edited.
+
+    These attributes need launchpad.View to see, and launchpad.Edit to change.
+    """
+
+    name = TextLine(
+        title=_("The name of this registry."),
+        constraint=name_validator,
+        required=True)
+
+    title = TextLine(
+        title=_("A title for this registry."),
+        required=True)
+
+    base_url = TextLine(
+        title=_("The base URL for this registry."),
+        constraint=validate_url,
+        required=True)
+
+    active = Bool(
+        title=_("If True, this registry is active."),
+        required=True, default=True)
+
+
+class IOCIRegistryEdit(Interface):
+    """`IOCIRegistry` methods that require launchpad.Edit permission."""
+
+
+class IOCIRegistry(IOCIRegistryView, IOCIRegistryEditableAttributes,
+                   IOCIRegistryEdit):
+    """A registry for Open Container Initiative images."""
+
+
+class IOCIRegistrySet(Interface):
+    """A utility to create and access OCI Registries."""
+
+    def new(registrant, name, title, base_url, active):
+        """Create an IOCIRegistry."""
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index eaa03c5..d2eab30 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -87,6 +87,9 @@ class OCIRecipe(Storm):
 
     build_daily = Bool(name="build_daily", default=False)
 
+    registry_id = Int(name="registry")
+    registry = Reference(registry_id, 'OCIRegistry.id')
+
     def __init__(self, registrant, owner, ociproject, ociproject_default=False,
                  require_virtualized=True):
         super(OCIRecipe, self).__init__()
diff --git a/lib/lp/oci/model/ociregistry.py b/lib/lp/oci/model/ociregistry.py
new file mode 100644
index 0000000..9d69baa
--- /dev/null
+++ b/lib/lp/oci/model/ociregistry.py
@@ -0,0 +1,67 @@
+# Copyright 2019 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""An instance of an OCI Registry."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+    'OCIRegistry',
+    'OCIRegistrySet'
+    ]
+
+import pytz
+from storm.locals import (
+    Bool,
+    DateTime,
+    Int,
+    Reference,
+    Storm,
+    Unicode,
+    )
+from zope.interface import implementer
+
+from lp.oci.interfaces.ociregistry import (
+    IOCIRegistry,
+    IOCIRegistrySet,
+    )
+from lp.services.database.interfaces import IMasterStore
+
+
+@implementer(IOCIRegistry)
+class OCIRegistry(Storm):
+
+    __storm_table__ = "OCIRegistry"
+
+    id = Int(primary=True)
+    date_created = DateTime(
+        name="date_created", tzinfo=pytz.UTC, allow_none=False)
+
+    registrant_id = Int(name='registrant', allow_none=False)
+    registrant = Reference(registrant_id, "Person.id")
+
+    name = Unicode(name="name", allow_none=False)
+
+    title = Unicode(name="title", allow_none=False)
+
+    base_url = Unicode(name="base_url", allow_none=False)
+
+    active = Bool(name="active", default=False)
+
+    def __init__(self, registrant, name, title, base_url, active):
+        self.registrant = registrant
+        self.name = name
+        self.title = title
+        self.base_url = base_url
+        self.active = active
+
+
+@implementer(IOCIRegistrySet)
+class OCIRegistrySet:
+
+    def new(self, registrant, name, title, base_url, active):
+        store = IMasterStore(OCIRegistry)
+        oci_registry = OCIRegistry(registrant, name, title, base_url, active)
+        store.add(oci_registry)
+        return oci_registry
diff --git a/lib/lp/oci/tests/test_ociregistry.py b/lib/lp/oci/tests/test_ociregistry.py
new file mode 100644
index 0000000..4f9fbda
--- /dev/null
+++ b/lib/lp/oci/tests/test_ociregistry.py
@@ -0,0 +1,53 @@
+# Copyright 2019 Canonical Ltd.  This software is licensed under the
+# GNU Affero General Public License version 3 (see the file LICENSE).
+
+"""Tests for OCI image registry handling functionality."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from zope.component import getUtility
+
+from lp.oci.interfaces.ociregistry import (
+    IOCIRegistry,
+    IOCIRegistrySet,
+    )
+from lp.testing import (
+    admin_logged_in,
+    TestCaseWithFactory,
+    )
+from lp.testing.layers import DatabaseFunctionalLayer
+
+
+class TestOCIRegistry(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def test_implements_interface(self):
+        target = self.factory.makeOCIRegistry()
+        with admin_logged_in():
+            self.assertProvides(target, IOCIRegistry)
+
+
+class TestOCIRegistrySet(TestCaseWithFactory):
+
+    layer = DatabaseFunctionalLayer
+
+    def test_implements_interface(self):
+        target_set = getUtility(IOCIRegistrySet)
+        with admin_logged_in():
+            self.assertProvides(target_set, IOCIRegistrySet)
+
+    def test_new(self):
+        registrant = self.factory.makePerson()
+        name = self.factory.getUniqueString(u"oci-registry-name")
+        title = self.factory.getUniqueString(u"oci-registry-title")
+        base_url = self.factory.getUniqueURL().decode('utf-8')
+        active = True
+        target = getUtility(IOCIRegistrySet).new(
+            registrant=registrant, name=name, title=title, base_url=base_url,
+            active=active)
+        self.assertEqual(target.registrant, registrant)
+        self.assertEqual(target.name, name)
+        self.assertEqual(target.title, title)
+        self.assertEqual(target.base_url, base_url)
+        self.assertEqual(target.active, active)
diff --git a/lib/lp/security.py b/lib/lp/security.py
index 4e586d1..dee5521 100644
--- a/lib/lp/security.py
+++ b/lib/lp/security.py
@@ -114,6 +114,7 @@ from lp.hardwaredb.interfaces.hwdb import (
     )
 from lp.oci.interfaces.ocirecipe import IOCIRecipe
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
+from lp.oci.interfaces.ociregistry import IOCIRegistry
 from lp.registry.enums import PersonVisibility
 from lp.registry.interfaces.announcement import IAnnouncement
 from lp.registry.interfaces.distribution import IDistribution
@@ -3472,3 +3473,8 @@ class ViewOCIRecipe(AnonymousAuthorization):
 class ViewOCIRecipeBuild(AnonymousAuthorization):
     """Anyone can view an `IOCIRecipe`."""
     usedfor = IOCIRecipeBuild
+
+
+class ViewOCIRegistry(AnonymousAuthorization):
+    """Anyone can view an `IOCIRegistry`."""
+    usedfor = IOCIRegistry
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 2cd8adb..f2bc2ed 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -158,6 +158,7 @@ from lp.hardwaredb.interfaces.hwdb import (
     IHWSubmissionSet,
     )
 from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
+from lp.oci.interfaces.ociregistry import IOCIRegistrySet
 from lp.oci.model.ocirecipe import (
     OCIRecipe,
     OCIRecipeArch,
@@ -5023,6 +5024,21 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         return OCIFile(build=build, library_file=library_file,
                        layer_file_digest=layer_file_digest)
 
+    def makeOCIRegistry(self, registrant=None, name=None, title=None,
+                        base_url=None, active=True):
+        """Make a new OCIRegistry."""
+        if registrant is None:
+            registrant = self.makePerson()
+        if name is None:
+            name = self.getUniqueString(u"oci-registry-name")
+        if title is None:
+            title = self.getUniqueString(u"oci-registry-title")
+        if base_url is None:
+            base_url = self.getUniqueURL().decode('utf-8')
+        return getUtility(IOCIRegistrySet).new(
+            registrant=registrant, name=name, title=title, base_url=base_url,
+            active=active)
+
 
 # Some factory methods return simple Python types. We don't add
 # security wrappers for them, as well as for objects created by

Follow ups