launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #28528
[Merge] ~andrey-fedoseev/launchpad:snap-target-architectures into launchpad:master
Andrey Fedoseev has proposed merging ~andrey-fedoseev/launchpad:snap-target-architectures into launchpad:master.
Commit message:
Add `target_architectures` to snap builds
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~andrey-fedoseev/launchpad/+git/launchpad/+merge/423921
Allow snap builds to have an associated list of target architectures to build the snap for.
The target architectures are either specified using the `build-to` property in snapcraft.yml, or
may can be passed as an argument to the `Snap.requestBuild` method.
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~andrey-fedoseev/launchpad:snap-target-architectures into launchpad:master.
diff --git a/lib/lp/snappy/adapters/buildarch.py b/lib/lp/snappy/adapters/buildarch.py
index 76e7a4f..9697ff8 100644
--- a/lib/lp/snappy/adapters/buildarch.py
+++ b/lib/lp/snappy/adapters/buildarch.py
@@ -6,6 +6,13 @@ __all__ = [
]
from collections import Counter
+from typing import (
+ Any,
+ Dict,
+ List,
+ Optional,
+ Union,
+ )
from lp.services.helpers import english_list
@@ -56,7 +63,12 @@ class UnsupportedBuildOnError(SnapArchitecturesParserError):
class SnapArchitecture:
"""A single entry in the snapcraft.yaml 'architectures' list."""
- def __init__(self, build_on, build_to=None, build_error=None):
+ def __init__(
+ self,
+ build_on: Union[str, List[str]],
+ build_to: Optional[Union[str, List[str]]] = None,
+ build_error: Optional[str] = None
+ ):
"""Create a new architecture entry.
:param build_on: string or list; build-on property from
@@ -67,10 +79,12 @@ class SnapArchitecture:
snapcraft.yaml.
"""
self.build_on = (
- [build_on] if isinstance(build_on, str) else build_on)
+ [build_on] if isinstance(build_on, str) else build_on
+ ) # type: List[str]
if build_to:
self.build_to = (
- [build_to] if isinstance(build_to, str) else build_to)
+ [build_to] if isinstance(build_to, str) else build_to
+ ) # type: List[str]
else:
self.build_to = self.build_on
self.build_error = build_error
@@ -83,9 +97,11 @@ class SnapArchitecture:
except KeyError:
raise MissingPropertyError("build-on")
+ build_to = properties.get("build-to", properties.get("run-on"))
+
return cls(
build_on=build_on,
- build_to=properties.get("build-to", properties.get("run-on")),
+ build_to=build_to,
build_error=properties.get("build-error"),
)
@@ -97,11 +113,17 @@ class SnapBuildInstance:
- architecture: The architecture tag that should be used to build the
snap.
+ - target_architectures: The architecture tags that should be used to
+ build the snap for.
- required: Whether or not failure to build should cause the entire
set to fail.
"""
- def __init__(self, architecture, supported_architectures):
+ def __init__(
+ self,
+ architecture: SnapArchitecture,
+ supported_architectures: List[str],
+ ):
"""Construct a new `SnapBuildInstance`.
:param architecture: `SnapArchitecture` instance.
@@ -115,10 +137,14 @@ class SnapBuildInstance:
except StopIteration:
raise UnsupportedBuildOnError(architecture.build_on)
+ self.target_architectures = architecture.build_to
self.required = architecture.build_error != "ignore"
-def determine_architectures_to_build(snapcraft_data, supported_arches):
+def determine_architectures_to_build(
+ snapcraft_data: Dict[str, Any],
+ supported_arches: List[str],
+) -> List[SnapBuildInstance]:
"""Return a list of architectures to build based on snapcraft.yaml.
:param snapcraft_data: A parsed snapcraft.yaml.
@@ -126,7 +152,9 @@ def determine_architectures_to_build(snapcraft_data, supported_arches):
we can create builds for.
:return: a list of `SnapBuildInstance`s.
"""
- architectures_list = snapcraft_data.get("architectures")
+ architectures_list = (
+ snapcraft_data.get("architectures")
+ ) # type: Optional[List]
if architectures_list:
# First, determine what style we're parsing. Is it a list of
diff --git a/lib/lp/snappy/adapters/tests/test_buildarch.py b/lib/lp/snappy/adapters/tests/test_buildarch.py
index 53d3937..b3018ed 100644
--- a/lib/lp/snappy/adapters/tests/test_buildarch.py
+++ b/lib/lp/snappy/adapters/tests/test_buildarch.py
@@ -77,12 +77,14 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
build_on="i386", build_to=["amd64", "i386"]),
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_architecture": "i386",
+ "expected_target_architectures": ["amd64", "i386"],
"expected_required": True,
}),
("amd64", {
"architecture": SnapArchitecture(build_on="amd64", build_to="all"),
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_architecture": "amd64",
+ "expected_target_architectures": ["all"],
"expected_required": True,
}),
("amd64 priority", {
@@ -90,6 +92,7 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
build_on=["amd64", "i386"], build_to="all"),
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_architecture": "amd64",
+ "expected_target_architectures": ["all"],
"expected_required": True,
}),
("i386 priority", {
@@ -97,6 +100,7 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
build_on=["amd64", "i386"], build_to="all"),
"supported_architectures": ["i386", "amd64", "armhf"],
"expected_architecture": "i386",
+ "expected_target_architectures": ["all"],
"expected_required": True,
}),
("optional", {
@@ -104,6 +108,7 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
build_on="amd64", build_error="ignore"),
"supported_architectures": ["amd64", "i386", "armhf"],
"expected_architecture": "amd64",
+ "expected_target_architectures": ["amd64"],
"expected_required": False,
}),
]
@@ -112,6 +117,8 @@ class TestSnapBuildInstance(WithScenarios, TestCase):
instance = SnapBuildInstance(
self.architecture, self.supported_architectures)
self.assertEqual(self.expected_architecture, instance.architecture)
+ self.assertEqual(self.expected_target_architectures,
+ instance.target_architectures)
self.assertEqual(self.expected_required, instance.required)
@@ -128,50 +135,91 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCase):
# Scenarios taken from the architectures document:
# https://forum.snapcraft.io/t/architectures/4972
+
scenarios = [
("none", {
"architectures": None,
"supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
- {"architecture": "amd64", "required": True},
- {"architecture": "i386", "required": True},
- {"architecture": "armhf", "required": True},
- ],
- }),
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True
+ },
+ {
+ "architecture": "armhf",
+ "target_architectures": ["armhf"],
+ "required": True
+ },
+ ],
+ }),
("i386", {
"architectures": [
{"build-on": "i386", "build-to": ["amd64", "i386"]},
- ],
+ ],
"supported_architectures": ["amd64", "i386", "armhf"],
- "expected": [{"architecture": "i386", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "i386",
+ "target_architectures": ["amd64", "i386"],
+ "required": True,
+ }
+ ],
+ }),
("amd64", {
"architectures": [{"build-on": "amd64", "build-to": "all"}],
"supported_architectures": ["amd64", "i386", "armhf"],
- "expected": [{"architecture": "amd64", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "amd64",
+ "target_architectures": ["all"],
+ "required": True
+ }
+ ],
+ }),
("amd64 and i386", {
"architectures": [
{"build-on": "amd64", "build-to": "amd64"},
{"build-on": "i386", "build-to": "i386"},
- ],
+ ],
"supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
- {"architecture": "amd64", "required": True},
- {"architecture": "i386", "required": True},
- ],
- }),
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True,
+ },
+ ],
+ }),
("amd64 and i386 shorthand", {
"architectures": [
{"build-on": "amd64"},
{"build-on": "i386"},
- ],
+ ],
"supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
- {"architecture": "amd64", "required": True},
- {"architecture": "i386", "required": True},
- ],
- }),
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True,
+ },
+ ],
+ }),
("amd64, i386, and armhf", {
"architectures": [
{"build-on": "amd64", "build-to": "amd64"},
@@ -180,51 +228,95 @@ class TestDetermineArchitecturesToBuild(WithScenarios, TestCase):
"build-on": "armhf",
"build-to": "armhf",
"build-error": "ignore",
- },
- ],
+ },
+ ],
"supported_architectures": ["amd64", "i386", "armhf"],
"expected": [
- {"architecture": "amd64", "required": True},
- {"architecture": "i386", "required": True},
- {"architecture": "armhf", "required": False},
- ],
- }),
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True,
+ },
+ {
+ "architecture": "armhf",
+ "target_architectures": ["armhf"],
+ "required": False,
+ },
+ ],
+ }),
("amd64 priority", {
"architectures": [
{"build-on": ["amd64", "i386"], "build-to": "all"},
- ],
+ ],
"supported_architectures": ["amd64", "i386", "armhf"],
- "expected": [{"architecture": "amd64", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "amd64",
+ "target_architectures": ["all"],
+ "required": True,
+ }
+ ],
+ }),
("i386 priority", {
"architectures": [
{"build-on": ["amd64", "i386"], "build-to": "all"},
- ],
+ ],
"supported_architectures": ["i386", "amd64", "armhf"],
- "expected": [{"architecture": "i386", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "i386",
+ "target_architectures": ["all"],
+ "required": True,
+ }
+ ],
+ }),
("old style i386 priority", {
"architectures": ["amd64", "i386"],
"supported_architectures": ["i386", "amd64", "armhf"],
- "expected": [{"architecture": "i386", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "i386",
+ "target_architectures": ["amd64", "i386"],
+ "required": True
+ }
+ ],
+ }),
("old style amd64 priority", {
"architectures": ["amd64", "i386"],
"supported_architectures": ["amd64", "i386", "armhf"],
- "expected": [{"architecture": "amd64", "required": True}],
- }),
+ "expected": [
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64", "i386"],
+ "required": True,
+ }
+ ],
+ }),
("more architectures listed than are supported", {
"architectures": [
{"build-on": "amd64"},
{"build-on": "i386"},
{"build-on": "armhf"},
- ],
+ ],
"supported_architectures": ["amd64", "i386"],
"expected": [
- {"architecture": "amd64", "required": True},
- {"architecture": "i386", "required": True},
- ],
- })
+ {
+ "architecture": "amd64",
+ "target_architectures": ["amd64"],
+ "required": True,
+ },
+ {
+ "architecture": "i386",
+ "target_architectures": ["i386"],
+ "required": True,
+ },
+ ],
+ })
]
def test_parser(self):
diff --git a/lib/lp/snappy/interfaces/snap.py b/lib/lp/snappy/interfaces/snap.py
index a809b08..8cb4d37 100644
--- a/lib/lp/snappy/interfaces/snap.py
+++ b/lib/lp/snappy/interfaces/snap.py
@@ -418,12 +418,22 @@ class ISnapView(Interface):
"A dictionary mapping snap names to channels to use for this "
"build. Currently only 'core', 'core18', 'core20', 'core22', "
"and 'snapcraft' keys are supported."),
- key_type=TextLine(), required=False))
+ key_type=TextLine(), required=False),
+ target_architectures=List(
+ required=False,
+ title=_("The list of target architectures."),
+ description=_(
+ "The optional list of target architectures to build snap for. "
+ "If omitted then no target architecture is assumed."
+ ),
+ ),
+ )
# Really ISnapBuild, patched in lp.snappy.interfaces.webservice.
@export_factory_operation(Interface, [])
@operation_for_version("devel")
def requestBuild(requester, archive, distro_arch_series, pocket,
- snap_base=None, channels=None, build_request=None):
+ snap_base=None, channels=None, build_request=None,
+ target_architectures=None):
"""Request that the snap package be built.
:param requester: The person requesting the build.
@@ -435,6 +445,8 @@ class ISnapView(Interface):
for this build.
:param build_request: The `ISnapBuildRequest` job being processed,
if any.
+ :param target_architectures: The optional list of target architectures
+ to build snap for.
:return: `ISnapBuild`.
"""
diff --git a/lib/lp/snappy/interfaces/snapbuild.py b/lib/lp/snappy/interfaces/snapbuild.py
index 4012391..60dd0f4 100644
--- a/lib/lp/snappy/interfaces/snapbuild.py
+++ b/lib/lp/snappy/interfaces/snapbuild.py
@@ -159,12 +159,20 @@ class ISnapBuildView(IPackageBuildView, IPrivacy):
distro_arch_series = exported(Reference(
IDistroArchSeries,
- title=_("The series and architecture for which to build."),
+ title=_("The series and architecture to build on."),
required=True, readonly=True))
arch_tag = exported(
TextLine(title=_("Architecture tag"), required=True, readonly=True))
+ target_architectures = exported(
+ List(
+ TextLine(),
+ title=_("The target architectures to build for."),
+ required=False, readonly=True,
+ )
+ )
+
pocket = exported(Choice(
title=_("The pocket for which to build."),
description=(
@@ -327,7 +335,8 @@ class ISnapBuildSet(ISpecificBuildFarmJobSource):
def new(requester, snap, archive, distro_arch_series, pocket,
snap_base=None, channels=None, date_created=DEFAULT,
- store_upload_metadata=None, build_request=None):
+ store_upload_metadata=None, build_request=None,
+ target_architectures=None):
"""Create an `ISnapBuild`."""
def preloadBuildsData(builds):
diff --git a/lib/lp/snappy/model/snap.py b/lib/lp/snappy/model/snap.py
index 16bbfed..bfc5795 100644
--- a/lib/lp/snappy/model/snap.py
+++ b/lib/lp/snappy/model/snap.py
@@ -13,6 +13,7 @@ from datetime import (
timedelta,
)
from operator import attrgetter
+import typing as t
from urllib.parse import urlsplit
from breezy import urlutils
@@ -116,6 +117,7 @@ from lp.registry.interfaces.accesspolicy import (
IAccessArtifactGrantSource,
IAccessArtifactSource,
)
+from lp.registry.interfaces.distroseries import IDistroSeries
from lp.registry.interfaces.person import (
IPerson,
IPersonSet,
@@ -205,7 +207,10 @@ from lp.snappy.interfaces.snapbase import (
ISnapBaseSet,
NoSuchSnapBase,
)
-from lp.snappy.interfaces.snapbuild import ISnapBuildSet
+from lp.snappy.interfaces.snapbuild import (
+ ISnapBuild,
+ ISnapBuildSet,
+ )
from lp.snappy.interfaces.snapjob import ISnapRequestBuildsJobSource
from lp.snappy.interfaces.snappyseries import ISnappyDistroSeriesSet
from lp.snappy.interfaces.snapstoreclient import ISnapStoreClient
@@ -213,6 +218,7 @@ from lp.snappy.model.snapbuild import SnapBuild
from lp.snappy.model.snapjob import SnapJob
from lp.snappy.model.snapsubscription import SnapSubscription
from lp.soyuz.interfaces.archive import ArchiveDisabled
+from lp.soyuz.interfaces.distroarchseries import IDistroArchSeries
from lp.soyuz.model.archive import (
Archive,
get_enabled_archive_filter,
@@ -639,7 +645,11 @@ class Snap(Storm, WebhookTargetMixin):
das.getChroot(pocket=pocket) is not None
and self._isBuildableArchitectureAllowed(das, snap_base=snap_base))
- def getAllowedArchitectures(self, distro_series=None, snap_base=None):
+ def getAllowedArchitectures(
+ self,
+ distro_series: t.Optional[IDistroSeries] = None,
+ snap_base = None
+ ) -> t.List[IDistroArchSeries]:
"""See `ISnap`."""
if distro_series is None:
distro_series = self.distro_series
@@ -783,14 +793,26 @@ class Snap(Storm, WebhookTargetMixin):
# See rationale in `SnapBuildArchiveOwnerMismatch` docstring.
raise SnapBuildArchiveOwnerMismatch()
- def requestBuild(self, requester, archive, distro_arch_series, pocket,
- snap_base=None, channels=None, build_request=None):
+ def requestBuild(
+ self,
+ requester,
+ archive,
+ distro_arch_series,
+ pocket,
+ snap_base=None,
+ channels=None,
+ build_request=None,
+ target_architectures: t.Optional[t.List[str]] = None
+ ) -> ISnapBuild:
"""See `ISnap`."""
self._checkRequestBuild(requester, archive)
if not self._isArchitectureAllowed(
distro_arch_series, pocket, snap_base=snap_base):
raise SnapBuildDisallowedArchitecture(distro_arch_series, pocket)
+ if target_architectures:
+ target_architectures = sorted(target_architectures)
+
if not channels:
channels_clause = Or(
SnapBuild.channels == None, SnapBuild.channels == {})
@@ -802,6 +824,7 @@ class Snap(Storm, WebhookTargetMixin):
SnapBuild.archive_id == archive.id,
SnapBuild.distro_arch_series_id == distro_arch_series.id,
SnapBuild.pocket == pocket,
+ SnapBuild.target_architectures == target_architectures,
channels_clause,
SnapBuild.status == BuildStatus.NEEDSBUILD)
if pending.any() is not None:
@@ -810,7 +833,8 @@ class Snap(Storm, WebhookTargetMixin):
build = getUtility(ISnapBuildSet).new(
requester, self, archive, distro_arch_series, pocket,
snap_base=snap_base, channels=channels,
- build_request=build_request)
+ build_request=build_request,
+ target_architectures=target_architectures)
build.queueBuild()
notify(ObjectCreatedEvent(build, user=requester))
return build
@@ -917,7 +941,7 @@ class Snap(Storm, WebhookTargetMixin):
if (architectures is None or
das.architecturetag in architectures))
architectures_to_build = determine_architectures_to_build(
- snapcraft_data, supported_arches.keys())
+ snapcraft_data, list(supported_arches.keys()))
except Exception as e:
if not allow_failures:
raise
@@ -938,7 +962,9 @@ class Snap(Storm, WebhookTargetMixin):
build = self.requestBuild(
requester, archive, supported_arches[arch], pocket,
snap_base=snap_base, channels=arch_channels,
- build_request=build_request)
+ build_request=build_request,
+ target_architectures=build_instance.target_architectures
+ )
if logger is not None:
logger.debug(
" - %s/%s/%s: Build requested.",
diff --git a/lib/lp/snappy/model/snapbuild.py b/lib/lp/snappy/model/snapbuild.py
index 4ec5275..dece4fe 100644
--- a/lib/lp/snappy/model/snapbuild.py
+++ b/lib/lp/snappy/model/snapbuild.py
@@ -11,6 +11,7 @@ from operator import attrgetter
import pytz
import six
+from storm.databases import postgres
from storm.expr import (
Column,
Table,
@@ -156,6 +157,10 @@ class SnapBuild(PackageBuildMixin, Storm):
distro_arch_series = Reference(
distro_arch_series_id, 'DistroArchSeries.id')
+ target_architectures = postgres.JSON(
+ 'target_architectures', allow_none=True
+ )
+
pocket = DBEnum(enum=PackagePublishingPocket, allow_none=False)
snap_base_id = Int(name='snap_base', allow_none=True)
@@ -197,7 +202,8 @@ class SnapBuild(PackageBuildMixin, Storm):
def __init__(self, build_farm_job, requester, snap, archive,
distro_arch_series, pocket, snap_base, channels,
processor, virtualized, date_created,
- store_upload_metadata=None, build_request=None):
+ store_upload_metadata=None, build_request=None,
+ target_architectures=None):
"""Construct a `SnapBuild`."""
super().__init__()
self.build_farm_job = build_farm_job
@@ -212,6 +218,7 @@ class SnapBuild(PackageBuildMixin, Storm):
self.virtualized = virtualized
self.date_created = date_created
self.store_upload_metadata = store_upload_metadata
+ self.target_architectures = target_architectures
if build_request is not None:
self.build_request_id = build_request.id
self.status = BuildStatus.NEEDSBUILD
@@ -522,7 +529,8 @@ class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
def new(self, requester, snap, archive, distro_arch_series, pocket,
snap_base=None, channels=None, date_created=DEFAULT,
- store_upload_metadata=None, build_request=None):
+ store_upload_metadata=None, build_request=None,
+ target_architectures=None):
"""See `ISnapBuildSet`."""
store = IMasterStore(SnapBuild)
build_farm_job = getUtility(IBuildFarmJobSource).new(
@@ -534,7 +542,8 @@ class SnapBuildSet(SpecificBuildFarmJobSourceMixin):
not distro_arch_series.processor.supports_nonvirtualized
or snap.require_virtualized or archive.require_virtualized,
date_created, store_upload_metadata=store_upload_metadata,
- build_request=build_request)
+ build_request=build_request,
+ target_architectures=target_architectures)
store.add(snapbuild)
store.flush()
return snapbuild
diff --git a/lib/lp/snappy/model/snapbuildbehaviour.py b/lib/lp/snappy/model/snapbuildbehaviour.py
index 62cb2f9..612e963 100644
--- a/lib/lp/snappy/model/snapbuildbehaviour.py
+++ b/lib/lp/snappy/model/snapbuildbehaviour.py
@@ -10,6 +10,8 @@ __all__ = [
'SnapBuildBehaviour',
]
+import typing
+
from twisted.internet import defer
from zope.component import adapter
from zope.interface import implementer
@@ -101,11 +103,11 @@ class SnapBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
config.builddmaster.authentication_timeout)
@defer.inlineCallbacks
- def extraBuildArgs(self, logger=None):
+ def extraBuildArgs(self, logger=None) -> typing.Dict[str, typing.Any]:
"""
Return the extra arguments required by the worker for the given build.
"""
- build = self.build
+ build = self.build # type: ISnapBuild
args = yield super().extraBuildArgs(logger=logger)
yield self.addProxyArgs(args, build.snap.allow_internet)
args["name"] = build.snap.store_name or build.snap.name
@@ -165,6 +167,9 @@ class SnapBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
# (matching snapd, SAS and snapcraft representation)
timestamp = format_as_rfc3339(build_request.date_requested)
args["build_request_timestamp"] = timestamp
+
+ args["target_architectures"] = build.target_architectures
+
return args
def verifySuccessfulBuild(self):
diff --git a/lib/lp/snappy/tests/test_snap.py b/lib/lp/snappy/tests/test_snap.py
index abd99d3..41f8c0d 100644
--- a/lib/lp/snappy/tests/test_snap.py
+++ b/lib/lp/snappy/tests/test_snap.py
@@ -238,8 +238,12 @@ class TestSnap(TestCaseWithFactory):
distroseries=distroarchseries.distroseries,
processors=[distroarchseries.processor])
build = snap.requestBuild(
- snap.owner, snap.distro_series.main_archive, distroarchseries,
- PackagePublishingPocket.UPDATES)
+ snap.owner,
+ snap.distro_series.main_archive,
+ distroarchseries,
+ PackagePublishingPocket.UPDATES,
+ target_architectures=["amd64", "i386"],
+ )
self.assertTrue(ISnapBuild.providedBy(build))
self.assertThat(build, MatchesStructure(
requester=Equals(snap.owner),
@@ -249,7 +253,8 @@ class TestSnap(TestCaseWithFactory):
snap_base=Is(None),
channels=Is(None),
status=Equals(BuildStatus.NEEDSBUILD),
- ))
+ target_architectures=Equals(["amd64", "i386"]),
+ ))
store = Store.of(build)
store.flush()
build_queue = store.find(
@@ -359,6 +364,19 @@ class TestSnap(TestCaseWithFactory):
SnapBuildAlreadyPending, snap.requestBuild,
snap.owner, snap.distro_series.main_archive, arches[0],
PackagePublishingPocket.UPDATES, channels={"core": "edge"})
+ # target_architectures are taken into account when looking for pending
+ # builds. the order of the list should not matter.
+ snap.requestBuild(
+ snap.owner, snap.distro_series.main_archive, arches[0],
+ PackagePublishingPocket.UPDATES,
+ target_architectures=["i386", "amd64"]
+ )
+ self.assertRaises(
+ SnapBuildAlreadyPending, snap.requestBuild,
+ snap.owner, snap.distro_series.main_archive, arches[0],
+ PackagePublishingPocket.UPDATES,
+ target_architectures=["amd64", "i386"]
+ )
# Changing the status of the old build allows a new build.
old_build.updateStatus(BuildStatus.BUILDING)
old_build.updateStatus(BuildStatus.FULLYBUILT)
@@ -3866,11 +3884,13 @@ class TestSnapWebservice(TestCaseWithFactory):
snap = self.makeSnap(distroseries=distroseries, processors=[processor])
response = self.webservice.named_post(
snap["self_link"], "requestBuild", archive=archive_url,
- distro_arch_series=distroarchseries_url, pocket="Updates")
+ distro_arch_series=distroarchseries_url, pocket="Updates",
+ target_architectures=[distroarchseries.architecturetag])
self.assertEqual(201, response.status)
response = self.webservice.named_post(
snap["self_link"], "requestBuild", archive=archive_url,
- distro_arch_series=distroarchseries_url, pocket="Updates")
+ distro_arch_series=distroarchseries_url, pocket="Updates",
+ target_architectures=[distroarchseries.architecturetag])
self.assertEqual(400, response.status)
self.assertEqual(
b"An identical build of this snap package is already pending.",
@@ -4201,7 +4221,8 @@ class TestSnapWebservice(TestCaseWithFactory):
auto_build_pocket=PackagePublishingPocket.PROPOSED)
response = self.webservice.named_post(
snap["self_link"], "requestBuild", archive=archive_url,
- distro_arch_series=das_urls[0], pocket="Proposed")
+ distro_arch_series=das_urls[0], pocket="Proposed",
+ target_architectures=[dases[0].architecturetag])
self.assertEqual(201, response.status)
response = self.webservice.named_post(
snap["self_link"], "requestAutoBuilds")
diff --git a/lib/lp/snappy/tests/test_snapbuild.py b/lib/lp/snappy/tests/test_snapbuild.py
index f11a097..fe16fc5 100644
--- a/lib/lp/snappy/tests/test_snapbuild.py
+++ b/lib/lp/snappy/tests/test_snapbuild.py
@@ -694,6 +694,10 @@ class TestSnapBuild(TestCaseWithFactory):
logger.output, LogsScheduledWebhooks([
(hook, "snap:build:0.1", MatchesDict(expected_payload))]))
+ def test_can_have_target_architectures(self):
+ build = self.factory.makeSnapBuild(target_architectures=["amd64"])
+ self.assertEqual(build.target_architectures, ["amd64"])
+
class TestSnapBuildSet(TestCaseWithFactory):
diff --git a/lib/lp/snappy/tests/test_snapbuildbehaviour.py b/lib/lp/snappy/tests/test_snapbuildbehaviour.py
index db202cb..618c741 100644
--- a/lib/lp/snappy/tests/test_snapbuildbehaviour.py
+++ b/lib/lp/snappy/tests/test_snapbuildbehaviour.py
@@ -148,7 +148,7 @@ class TestSnapBuildBehaviourBase(TestCaseWithFactory):
build = self.factory.makeSnapBuild(
archive=archive, distroarchseries=distroarchseries, pocket=pocket,
- name="test-snap", **kwargs)
+ name="test-snap", target_architectures=["i386"], **kwargs)
return IBuildFarmJobBehaviour(build)
@@ -353,6 +353,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
@@ -394,6 +395,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
@@ -424,6 +426,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
@@ -471,6 +474,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
@@ -503,6 +507,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
@@ -533,6 +538,7 @@ class TestAsyncSnapBuildBehaviour(StatsMixin, TestSnapBuildBehaviourBase):
"revocation_endpoint": RevocationEndpointMatcher(job, self.now),
"series": Equals("unstable"),
"trusted_keys": Equals(expected_trusted_keys),
+ "target_architectures": Equals(["i386"]),
}))
@defer.inlineCallbacks
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 6a5e459..db3c389 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -5017,7 +5017,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
archive=None, distroarchseries=None, pocket=None,
snap_base=None, channels=None, date_created=DEFAULT,
build_request=None, status=BuildStatus.NEEDSBUILD,
- builder=None, duration=None, **kwargs):
+ builder=None, duration=None, target_architectures=None,
+ **kwargs):
"""Make a new SnapBuild."""
if requester is None:
requester = self.makePerson()
@@ -5046,7 +5047,8 @@ class BareLaunchpadObjectFactory(ObjectFactory):
snapbuild = getUtility(ISnapBuildSet).new(
requester, snap, archive, distroarchseries, pocket,
snap_base=snap_base, channels=channels, date_created=date_created,
- build_request=build_request)
+ build_request=build_request,
+ target_architectures=target_architectures)
if duration is not None:
removeSecurityProxy(snapbuild).updateStatus(
BuildStatus.BUILDING, builder=builder,
diff --git a/requirements/launchpad.txt b/requirements/launchpad.txt
index c2081c1..abde93b 100644
--- a/requirements/launchpad.txt
+++ b/requirements/launchpad.txt
@@ -73,7 +73,7 @@ lazr.delegates==2.0.4
lazr.enum==1.2.1
lazr.jobrunner==0.17
lazr.lifecycle==1.2.1
-lazr.restful==1.1.0
+lazr.restful==2.0.0
lazr.restfulclient==0.14.4
lazr.sshserver==0.1.13
lazr.uri==1.0.6