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

Proposed by Tom Wardill
Status: Work in progress
Proposed branch: ~twom/launchpad:oci-registry
Merge into: launchpad:master
Prerequisite: ~twom/launchpad:oci-buildbehaviour
Diff against target: 346 lines (+255/-0)
8 files modified
lib/lp/oci/configure.zcml (+22/-0)
lib/lp/oci/interfaces/ocirecipe.py (+6/-0)
lib/lp/oci/interfaces/ociregistry.py (+82/-0)
lib/lp/oci/model/ocirecipe.py (+3/-0)
lib/lp/oci/model/ociregistry.py (+67/-0)
lib/lp/oci/tests/test_ociregistry.py (+53/-0)
lib/lp/security.py (+6/-0)
lib/lp/testing/factory.py (+16/-0)
Reviewer Review Type Date Requested Status
Launchpad code reviewers Pending
Review via email: mp+376860@code.launchpad.net

Commit message

Add OCIRegistry model

Description of the change

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.

To post a comment you must log in.
~twom/launchpad:oci-registry updated
a5b7d43... by Tom Wardill

Add git_repository column to OCIRecipe

Unmerged commits

c70b76c... by Tom Wardill

Add OCIRegistry

fe09183... by Tom Wardill

Remove WaitingSlaveWithFiles in favour of changes to WaitingSlave

5aa12c1... by Tom Wardill

Test file cleanups and config handling

9b08cb3... by Tom Wardill

Refactor and tidy up in ocirecipebuildbehaviour

f43d08a... by Tom Wardill

Add tests for ocirecipebuildbehaviour

954b8ed... by Tom Wardill

Add mock slave with files

9721a10... by Tom Wardill

Allow downloadfiles to be overriden

59f133a... by Tom Wardill

Import format

70b5f86... by Tom Wardill

Test getOopsVars

7016d62... by Tom Wardill

Use DBEnum

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
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 @@
52 <allow interface="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource" />52 <allow interface="lp.buildmaster.interfaces.buildfarmjob.ISpecificBuildFarmJobSource" />
53 </securedutility>53 </securedutility>
5454
55 <!-- OCIRegistry -->
56 <class
57 class="lp.oci.model.ociregistry.OCIRegistry">
58 <require
59 permission="launchpad.View"
60 interface="lp.oci.interfaces.ociregistry.IOCIRegistryView
61 lp.oci.interfaces.ociregistry.IOCIRegistryEditableAttributes"/>
62 <require
63 permission="launchpad.Edit"
64 interface="lp.oci.interfaces.ociregistry.IOCIRegistryEdit"
65 set_schema="lp.oci.interfaces.ociregistry.IOCIRegistryEditableAttributes" />
66 </class>
67
68 <!-- OCIRegistrySet -->
69 <securedutility
70 class="lp.oci.model.ociregistry.OCIRegistrySet"
71 provides="lp.oci.interfaces.ociregistry.IOCIRegistrySet">
72 <allow
73 interface="lp.oci.interfaces.ociregistry.IOCIRegistrySet"/>
74 </securedutility>
75
76
55 <adapter77 <adapter
56 for="lp.oci.interfaces.ocirecipebuild.IOCIRecipeBuild"78 for="lp.oci.interfaces.ocirecipebuild.IOCIRecipeBuild"
57 provides="lp.buildmaster.interfaces.buildfarmjobbehaviour.IBuildFarmJobBehaviour"79 provides="lp.buildmaster.interfaces.buildfarmjobbehaviour.IBuildFarmJobBehaviour"
diff --git a/lib/lp/oci/interfaces/ocirecipe.py b/lib/lp/oci/interfaces/ocirecipe.py
index fab516f..9b75a45 100644
--- a/lib/lp/oci/interfaces/ocirecipe.py
+++ b/lib/lp/oci/interfaces/ocirecipe.py
@@ -38,6 +38,7 @@ from zope.security.interfaces import Unauthorized
3838
39from lp import _39from lp import _
40from lp.code.interfaces.gitrepository import IGitRepository40from lp.code.interfaces.gitrepository import IGitRepository
41from lp.oci.interfaces.ociregistry import IOCIRegistry
41from lp.registry.interfaces.ociproject import IOCIProject42from lp.registry.interfaces.ociproject import IOCIProject
42from lp.registry.interfaces.role import IHasOwner43from lp.registry.interfaces.role import IHasOwner
43from lp.services.fields import (44from lp.services.fields import (
@@ -158,6 +159,11 @@ class IOCIRecipeEditableAttributes(IHasOwner):
158 title=_("Build daily"), required=True, default=False,159 title=_("Build daily"), required=True, default=False,
159 description=_("If True, this recipe should be built daily."))160 description=_("If True, this recipe should be built daily."))
160161
162 registry = Reference(
163 IOCIRegistry,
164 title=_("The OCI registry that builds of "
165 "this recipe should be pushed to."))
166
161167
162class IOCIRecipe(IOCIRecipeView, IOCIRecipeEdit, IOCIRecipeEditableAttributes):168class IOCIRecipe(IOCIRecipeView, IOCIRecipeEdit, IOCIRecipeEditableAttributes):
163 """A recipe for building Open Container Initiative images."""169 """A recipe for building Open Container Initiative images."""
diff --git a/lib/lp/oci/interfaces/ociregistry.py b/lib/lp/oci/interfaces/ociregistry.py
164new file mode 100644170new file mode 100644
index 0000000..df65ddf
--- /dev/null
+++ b/lib/lp/oci/interfaces/ociregistry.py
@@ -0,0 +1,82 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Interfaces for handling OCI registry details."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9__all__ = [
10 'IOCIRegistry',
11 'IOCIRegistryEdit',
12 'IOCIRegistryEditableAttributes',
13 'IOCIRegistryView',
14 'IOCIRegistrySet',
15]
16
17from zope.interface import Interface
18from zope.schema import (
19 Bool,
20 Datetime,
21 Int,
22 TextLine,
23 )
24
25from lp import _
26from lp.app.validators.name import name_validator
27from lp.app.validators.url import validate_url
28from lp.services.fields import PublicPersonChoice
29
30
31class IOCIRegistryView(Interface):
32 """`IOCIRegistry` attributes that require launchpad.View permission."""
33
34 id = Int(title=_("ID"), required=True, readonly=True)
35 date_created = Datetime(
36 title=_("The date on which this registry was created in Launchpad."),
37 required=True, readonly=True)
38
39 registrant = PublicPersonChoice(
40 title=_("The user who registered this registry."),
41 vocabulary='ValidPersonOrTeam', required=True, readonly=True)
42
43
44class IOCIRegistryEditableAttributes(Interface):
45 """`IOCIRegistry` attributes that can be edited.
46
47 These attributes need launchpad.View to see, and launchpad.Edit to change.
48 """
49
50 name = TextLine(
51 title=_("The name of this registry."),
52 constraint=name_validator,
53 required=True)
54
55 title = TextLine(
56 title=_("A title for this registry."),
57 required=True)
58
59 base_url = TextLine(
60 title=_("The base URL for this registry."),
61 constraint=validate_url,
62 required=True)
63
64 active = Bool(
65 title=_("If True, this registry is active."),
66 required=True, default=True)
67
68
69class IOCIRegistryEdit(Interface):
70 """`IOCIRegistry` methods that require launchpad.Edit permission."""
71
72
73class IOCIRegistry(IOCIRegistryView, IOCIRegistryEditableAttributes,
74 IOCIRegistryEdit):
75 """A registry for Open Container Initiative images."""
76
77
78class IOCIRegistrySet(Interface):
79 """A utility to create and access OCI Registries."""
80
81 def new(registrant, name, title, base_url, active):
82 """Create an IOCIRegistry."""
diff --git a/lib/lp/oci/model/ocirecipe.py b/lib/lp/oci/model/ocirecipe.py
index 27ad543..88b4700 100644
--- a/lib/lp/oci/model/ocirecipe.py
+++ b/lib/lp/oci/model/ocirecipe.py
@@ -90,6 +90,9 @@ class OCIRecipe(Storm):
9090
91 build_daily = Bool(name="build_daily", default=False)91 build_daily = Bool(name="build_daily", default=False)
9292
93 registry_id = Int(name="registry")
94 registry = Reference(registry_id, 'OCIRegistry.id')
95
93 def __init__(self, registrant, owner, ociproject, ociproject_default=False,96 def __init__(self, registrant, owner, ociproject, ociproject_default=False,
94 require_virtualized=True, git_repository=None):97 require_virtualized=True, git_repository=None):
95 super(OCIRecipe, self).__init__()98 super(OCIRecipe, self).__init__()
diff --git a/lib/lp/oci/model/ociregistry.py b/lib/lp/oci/model/ociregistry.py
96new file mode 10064499new file mode 100644
index 0000000..9d69baa
--- /dev/null
+++ b/lib/lp/oci/model/ociregistry.py
@@ -0,0 +1,67 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""An instance of an OCI Registry."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8__metaclass__ = type
9__all__ = [
10 'OCIRegistry',
11 'OCIRegistrySet'
12 ]
13
14import pytz
15from storm.locals import (
16 Bool,
17 DateTime,
18 Int,
19 Reference,
20 Storm,
21 Unicode,
22 )
23from zope.interface import implementer
24
25from lp.oci.interfaces.ociregistry import (
26 IOCIRegistry,
27 IOCIRegistrySet,
28 )
29from lp.services.database.interfaces import IMasterStore
30
31
32@implementer(IOCIRegistry)
33class OCIRegistry(Storm):
34
35 __storm_table__ = "OCIRegistry"
36
37 id = Int(primary=True)
38 date_created = DateTime(
39 name="date_created", tzinfo=pytz.UTC, allow_none=False)
40
41 registrant_id = Int(name='registrant', allow_none=False)
42 registrant = Reference(registrant_id, "Person.id")
43
44 name = Unicode(name="name", allow_none=False)
45
46 title = Unicode(name="title", allow_none=False)
47
48 base_url = Unicode(name="base_url", allow_none=False)
49
50 active = Bool(name="active", default=False)
51
52 def __init__(self, registrant, name, title, base_url, active):
53 self.registrant = registrant
54 self.name = name
55 self.title = title
56 self.base_url = base_url
57 self.active = active
58
59
60@implementer(IOCIRegistrySet)
61class OCIRegistrySet:
62
63 def new(self, registrant, name, title, base_url, active):
64 store = IMasterStore(OCIRegistry)
65 oci_registry = OCIRegistry(registrant, name, title, base_url, active)
66 store.add(oci_registry)
67 return oci_registry
diff --git a/lib/lp/oci/tests/test_ociregistry.py b/lib/lp/oci/tests/test_ociregistry.py
0new file mode 10064468new file mode 100644
index 0000000..4f9fbda
--- /dev/null
+++ b/lib/lp/oci/tests/test_ociregistry.py
@@ -0,0 +1,53 @@
1# Copyright 2019 Canonical Ltd. This software is licensed under the
2# GNU Affero General Public License version 3 (see the file LICENSE).
3
4"""Tests for OCI image registry handling functionality."""
5
6from __future__ import absolute_import, print_function, unicode_literals
7
8from zope.component import getUtility
9
10from lp.oci.interfaces.ociregistry import (
11 IOCIRegistry,
12 IOCIRegistrySet,
13 )
14from lp.testing import (
15 admin_logged_in,
16 TestCaseWithFactory,
17 )
18from lp.testing.layers import DatabaseFunctionalLayer
19
20
21class TestOCIRegistry(TestCaseWithFactory):
22
23 layer = DatabaseFunctionalLayer
24
25 def test_implements_interface(self):
26 target = self.factory.makeOCIRegistry()
27 with admin_logged_in():
28 self.assertProvides(target, IOCIRegistry)
29
30
31class TestOCIRegistrySet(TestCaseWithFactory):
32
33 layer = DatabaseFunctionalLayer
34
35 def test_implements_interface(self):
36 target_set = getUtility(IOCIRegistrySet)
37 with admin_logged_in():
38 self.assertProvides(target_set, IOCIRegistrySet)
39
40 def test_new(self):
41 registrant = self.factory.makePerson()
42 name = self.factory.getUniqueString(u"oci-registry-name")
43 title = self.factory.getUniqueString(u"oci-registry-title")
44 base_url = self.factory.getUniqueURL().decode('utf-8')
45 active = True
46 target = getUtility(IOCIRegistrySet).new(
47 registrant=registrant, name=name, title=title, base_url=base_url,
48 active=active)
49 self.assertEqual(target.registrant, registrant)
50 self.assertEqual(target.name, name)
51 self.assertEqual(target.title, title)
52 self.assertEqual(target.base_url, base_url)
53 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 (
114 )114 )
115from lp.oci.interfaces.ocirecipe import IOCIRecipe115from lp.oci.interfaces.ocirecipe import IOCIRecipe
116from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild116from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuild
117from lp.oci.interfaces.ociregistry import IOCIRegistry
117from lp.registry.enums import PersonVisibility118from lp.registry.enums import PersonVisibility
118from lp.registry.interfaces.announcement import IAnnouncement119from lp.registry.interfaces.announcement import IAnnouncement
119from lp.registry.interfaces.distribution import IDistribution120from lp.registry.interfaces.distribution import IDistribution
@@ -3472,3 +3473,8 @@ class ViewOCIRecipe(AnonymousAuthorization):
3472class ViewOCIRecipeBuild(AnonymousAuthorization):3473class ViewOCIRecipeBuild(AnonymousAuthorization):
3473 """Anyone can view an `IOCIRecipe`."""3474 """Anyone can view an `IOCIRecipe`."""
3474 usedfor = IOCIRecipeBuild3475 usedfor = IOCIRecipeBuild
3476
3477
3478class ViewOCIRegistry(AnonymousAuthorization):
3479 """Anyone can view an `IOCIRegistry`."""
3480 usedfor = IOCIRegistry
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 05d3e19..fca50bb 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -158,6 +158,7 @@ from lp.hardwaredb.interfaces.hwdb import (
158 IHWSubmissionSet,158 IHWSubmissionSet,
159 )159 )
160from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet160from lp.oci.interfaces.ocirecipebuild import IOCIRecipeBuildSet
161from lp.oci.interfaces.ociregistry import IOCIRegistrySet
161from lp.oci.model.ocirecipe import (162from lp.oci.model.ocirecipe import (
162 OCIRecipe,163 OCIRecipe,
163 OCIRecipeArch,164 OCIRecipeArch,
@@ -5025,6 +5026,21 @@ class BareLaunchpadObjectFactory(ObjectFactory):
5025 return OCIFile(build=build, library_file=library_file,5026 return OCIFile(build=build, library_file=library_file,
5026 layer_file_digest=layer_file_digest)5027 layer_file_digest=layer_file_digest)
50275028
5029 def makeOCIRegistry(self, registrant=None, name=None, title=None,
5030 base_url=None, active=True):
5031 """Make a new OCIRegistry."""
5032 if registrant is None:
5033 registrant = self.makePerson()
5034 if name is None:
5035 name = self.getUniqueString(u"oci-registry-name")
5036 if title is None:
5037 title = self.getUniqueString(u"oci-registry-title")
5038 if base_url is None:
5039 base_url = self.getUniqueURL().decode('utf-8')
5040 return getUtility(IOCIRegistrySet).new(
5041 registrant=registrant, name=name, title=title, base_url=base_url,
5042 active=active)
5043
50285044
5029# Some factory methods return simple Python types. We don't add5045# Some factory methods return simple Python types. We don't add
5030# security wrappers for them, as well as for objects created by5046# security wrappers for them, as well as for objects created by

Subscribers

People subscribed via source and target branches

to status/vote changes: