launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #32551
[Merge] ~ruinedyourlife/launchpad:native-publishing-missing-db-grants into launchpad:db-devel
Quentin Debhi has proposed merging ~ruinedyourlife/launchpad:native-publishing-missing-db-grants into launchpad:db-devel.
Commit message:
Add sourcepackagename db grant for native publisher jobs
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ruinedyourlife/launchpad/+git/launchpad/+merge/486209
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ruinedyourlife/launchpad:native-publishing-missing-db-grants into launchpad:db-devel.
diff --git a/database/schema/security.cfg b/database/schema/security.cfg
index e812fc4..b56dacd 100644
--- a/database/schema/security.cfg
+++ b/database/schema/security.cfg
@@ -3016,6 +3016,7 @@ public.personsettings = SELECT
public.pocketchroot = SELECT
public.processor = SELECT
public.product = SELECT
+public.sourcepackagename = SELECT
public.teammembership = SELECT
public.teamparticipation = SELECT
public.webhook = SELECT
diff --git a/lib/lp/charms/interfaces/charmrecipe.py b/lib/lp/charms/interfaces/charmrecipe.py
index 05a1add..77dcf02 100644
--- a/lib/lp/charms/interfaces/charmrecipe.py
+++ b/lib/lp/charms/interfaces/charmrecipe.py
@@ -74,6 +74,7 @@ from lp.app.errors import NameLookupFailed
from lp.app.interfaces.informationtype import IInformationType
from lp.app.validators.name import name_validator
from lp.app.validators.path import path_does_not_escape
+from lp.buildmaster.builderproxy import FetchServicePolicy
from lp.code.interfaces.gitref import IGitRef
from lp.code.interfaces.gitrepository import IGitRepository
from lp.registry.interfaces.person import IPerson
@@ -783,6 +784,20 @@ class ICharmRecipeAdminAttributes(Interface):
)
)
+ fetch_service_policy = exported(
+ Choice(
+ title=_("Fetch service policy"),
+ vocabulary=FetchServicePolicy,
+ required=False,
+ readonly=False,
+ default=FetchServicePolicy.STRICT,
+ description=_(
+ "Which policy to use when using the fetch service. Ignored if "
+ "`use_fetch_service` flag is False."
+ ),
+ )
+ )
+
# XXX cjwatson 2021-09-15 bug=760849: "beta" is a lie to get WADL
# generation working. Individual attributes must set their version to
@@ -843,6 +858,7 @@ class ICharmRecipeSet(Interface):
store_channels=None,
date_created=None,
use_fetch_service=False,
+ fetch_service_policy=FetchServicePolicy.STRICT,
):
"""Create an `ICharmRecipe`."""
diff --git a/lib/lp/charms/model/charmrecipe.py b/lib/lp/charms/model/charmrecipe.py
index 04bc448..82ea9c3 100644
--- a/lib/lp/charms/model/charmrecipe.py
+++ b/lib/lp/charms/model/charmrecipe.py
@@ -44,6 +44,7 @@ from lp.app.enums import (
InformationType,
)
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.buildmaster.builderproxy import FetchServicePolicy
from lp.buildmaster.enums import BuildStatus
from lp.buildmaster.interfaces.buildqueue import IBuildQueueSet
from lp.buildmaster.model.builder import Builder
@@ -320,6 +321,13 @@ class CharmRecipe(StormBase, WebhookTargetMixin):
use_fetch_service = Bool(name="use_fetch_service", allow_none=False)
+ fetch_service_policy = DBEnum(
+ enum=FetchServicePolicy,
+ default=FetchServicePolicy.STRICT,
+ name="fetch_service_policy",
+ allow_none=True,
+ )
+
def __init__(
self,
registrant,
@@ -339,6 +347,7 @@ class CharmRecipe(StormBase, WebhookTargetMixin):
store_channels=None,
date_created=DEFAULT,
use_fetch_service=False,
+ fetch_service_policy=FetchServicePolicy.STRICT,
):
"""Construct a `CharmRecipe`."""
if not getFeatureFlag(CHARM_RECIPE_ALLOW_CREATE):
@@ -365,6 +374,7 @@ class CharmRecipe(StormBase, WebhookTargetMixin):
self.store_secrets = store_secrets
self.store_channels = store_channels
self.use_fetch_service = use_fetch_service
+ self.fetch_service_policy = fetch_service_policy
def __repr__(self):
return "<CharmRecipe ~%s/%s/+charm/%s>" % (
@@ -962,6 +972,7 @@ class CharmRecipeSet:
store_channels=None,
date_created=DEFAULT,
use_fetch_service=False,
+ fetch_service_policy=FetchServicePolicy.STRICT,
):
"""See `ICharmRecipeSet`."""
if not registrant.inTeam(owner):
@@ -1008,6 +1019,7 @@ class CharmRecipeSet:
store_channels=store_channels,
date_created=date_created,
use_fetch_service=use_fetch_service,
+ fetch_service_policy=fetch_service_policy,
)
store.add(recipe)
diff --git a/lib/lp/charms/model/charmrecipebuildbehaviour.py b/lib/lp/charms/model/charmrecipebuildbehaviour.py
index 6e1b0bd..1386da3 100644
--- a/lib/lp/charms/model/charmrecipebuildbehaviour.py
+++ b/lib/lp/charms/model/charmrecipebuildbehaviour.py
@@ -82,6 +82,7 @@ class CharmRecipeBuildBehaviour(BuilderProxyMixin, BuildFarmJobBehaviourBase):
yield self.startProxySession(
args,
use_fetch_service=build.recipe.use_fetch_service,
+ fetch_service_policy=build.recipe.fetch_service_policy,
)
args["name"] = build.recipe.store_name or build.recipe.name
channels = build.channels or {}
diff --git a/lib/lp/charms/tests/test_charmrecipe.py b/lib/lp/charms/tests/test_charmrecipe.py
index 0150803..e837e4b 100644
--- a/lib/lp/charms/tests/test_charmrecipe.py
+++ b/lib/lp/charms/tests/test_charmrecipe.py
@@ -38,6 +38,7 @@ from zope.security.proxy import removeSecurityProxy
from lp.app.enums import InformationType
from lp.app.interfaces.launchpad import ILaunchpadCelebrities
+from lp.buildmaster.builderproxy import FetchServicePolicy
from lp.buildmaster.enums import (
BuildBaseImageType,
BuildQueueStatus,
@@ -478,6 +479,9 @@ class TestCharmRecipe(TestCaseWithFactory):
build_request = self.factory.makeCharmRecipeBuildRequest(recipe=recipe)
build = recipe.requestBuild(build_request, das)
self.assertEqual(True, build.recipe.use_fetch_service)
+ self.assertEqual(
+ FetchServicePolicy.STRICT, recipe.fetch_service_policy
+ )
def test_requestBuild_nonvirtualized(self):
# A non-virtualized processor can build a charm recipe iff the
@@ -1698,6 +1702,9 @@ class TestCharmRecipeSet(TestCaseWithFactory):
self.assertIsNone(recipe.store_secrets)
self.assertEqual([], recipe.store_channels)
self.assertFalse(recipe.use_fetch_service)
+ self.assertEqual(
+ FetchServicePolicy.STRICT, recipe.fetch_service_policy
+ )
def test_creation_no_source(self):
# Attempting to create a charm recipe without a Git repository
@@ -2149,15 +2156,16 @@ class TestCharmRecipeSet(TestCaseWithFactory):
git_ref=ref, use_fetch_service=True
)
- admin_fields = [
- "require_virtualized",
- "use_fetch_service",
- ]
+ admin_fields = {
+ "require_virtualized": True,
+ "use_fetch_service": True,
+ "fetch_service_policy": FetchServicePolicy.PERMISSIVE,
+ }
- for field_name in admin_fields:
+ for field_name, field_value in admin_fields.items():
# exception is not raised when an admin does the same
with admin_logged_in():
- setattr(charm, field_name, True)
+ setattr(charm, field_name, field_value)
def test_non_admins_cannot_update_admin_only_fields(self):
# The admin fields cannot be updated by a non admin
@@ -2167,12 +2175,13 @@ class TestCharmRecipeSet(TestCaseWithFactory):
git_ref=ref, use_fetch_service=True
)
person = self.factory.makePerson()
- admin_fields = [
- "require_virtualized",
- "use_fetch_service",
- ]
+ admin_fields = {
+ "require_virtualized": True,
+ "use_fetch_service": True,
+ "fetch_service_policy": FetchServicePolicy.PERMISSIVE,
+ }
- for field_name in admin_fields:
+ for field_name, field_value in admin_fields.items():
# exception is raised when a non admin updates the fields
with person_logged_in(person):
self.assertRaises(
@@ -2180,7 +2189,7 @@ class TestCharmRecipeSet(TestCaseWithFactory):
setattr,
charm,
field_name,
- True,
+ field_value,
)
diff --git a/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py b/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
index a2f186a..1d0b134 100644
--- a/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
+++ b/lib/lp/charms/tests/test_charmrecipebuildbehaviour.py
@@ -36,6 +36,10 @@ from lp.app.enums import InformationType
from lp.archivepublisher.interfaces.archivegpgsigningkey import (
IArchiveGPGSigningKey,
)
+from lp.buildmaster.builderproxy import (
+ FetchServicePolicy,
+ ProxyServiceException,
+)
from lp.buildmaster.enums import BuildBaseImageType, BuildStatus
from lp.buildmaster.interactor import shut_down_default_process_pool
from lp.buildmaster.interfaces.builder import CannotBuild
@@ -712,6 +716,61 @@ class TestAsyncCharmRecipeBuildBehaviourFetchService(
)
@defer.inlineCallbacks
+ def test_requestFetchServiceSession_permissive(self):
+ """Create a charm recipe build request with a successful fetch service
+ configuration.
+
+ `proxy_url` and `revocation_endpoint` are correctly populated.
+ """
+ job = self.makeJob(
+ use_fetch_service=True,
+ fetch_service_policy=FetchServicePolicy.PERMISSIVE,
+ )
+ args = yield job.extraBuildArgs()
+ request_matcher = MatchesDict(
+ {
+ "method": Equals(b"POST"),
+ "uri": Equals(b"/session"),
+ "headers": ContainsDict(
+ {
+ b"Authorization": MatchesListwise(
+ [
+ Equals(
+ b"Basic "
+ + base64.b64encode(
+ b"admin-launchpad.test:admin-secret"
+ )
+ )
+ ]
+ ),
+ b"Content-Type": MatchesListwise(
+ [
+ Equals(b"application/json"),
+ ]
+ ),
+ }
+ ),
+ "json": MatchesDict(
+ {
+ "policy": Equals("permissive"),
+ }
+ ),
+ }
+ )
+ self.assertThat(
+ self.fetch_service_api.sessions.requests,
+ MatchesListwise([request_matcher]),
+ )
+ self.assertIn("proxy_url", args)
+ self.assertIn("revocation_endpoint", args)
+ self.assertTrue(args["use_fetch_service"])
+ self.assertIn("secrets", args)
+ self.assertIn("fetch_service_mitm_certificate", args["secrets"])
+ self.assertIn(
+ "fake-cert", args["secrets"]["fetch_service_mitm_certificate"]
+ )
+
+ @defer.inlineCallbacks
def test_requestFetchServiceSession_mitm_certficate_redacted(self):
"""The `fetch_service_mitm_certificate` field in the build arguments
is redacted in the build logs."""
@@ -806,6 +865,7 @@ class TestAsyncCharmRecipeBuildBehaviourFetchService(
# The expected file is created in the `tem_upload_path`
expected_filename = f"{job.build.build_cookie}_metadata.json"
expected_file_path = os.path.join(tem_upload_path, expected_filename)
+ self.assertTrue(os.path.exists(expected_file_path))
with open(expected_file_path) as f:
self.assertEqual(
json.dumps(self.fetch_service_api.sessions.responses[1]),
@@ -832,6 +892,22 @@ class TestAsyncCharmRecipeBuildBehaviourFetchService(
# No calls go through to the fetch service
self.assertEqual(0, len(self.fetch_service_api.sessions.requests))
+ @defer.inlineCallbacks
+ def test_endProxySession_bad_revocation_endpoint(self):
+ """When `revocation_endpoint` is not properly set, it should raise
+ an exception."""
+
+ job = self.makeJob(use_fetch_service=True)
+ job._worker.proxy_info = MagicMock(
+ return_value={
+ "use_fetch_service": True,
+ "revocation_endpoint": "http://bad/endpoint",
+ }
+ )
+ yield job.extraBuildArgs()
+ with ExpectedException(ProxyServiceException):
+ yield job.endProxySession(upload_path="ignored")
+
class MakeCharmRecipeBuildMixin:
"""Provide the common makeBuild method returning a queued build."""
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 31bd3ed..1574c4b 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -6752,6 +6752,7 @@ class LaunchpadObjectFactory(ObjectFactory):
store_channels=None,
date_created=DEFAULT,
use_fetch_service=False,
+ fetch_service_policy=FetchServicePolicy.STRICT,
):
"""Make a new charm recipe."""
if registrant is None:
@@ -6801,6 +6802,7 @@ class LaunchpadObjectFactory(ObjectFactory):
store_channels=store_channels,
date_created=date_created,
use_fetch_service=use_fetch_service,
+ fetch_service_policy=fetch_service_policy,
)
if is_stale is not None:
removeSecurityProxy(recipe).is_stale = is_stale