Merge lp:~cjwatson/launchpad/snap-release-fix-macaroon-auth into lp:launchpad

Proposed by Colin Watson
Status: Merged
Merged at revision: 18329
Proposed branch: lp:~cjwatson/launchpad/snap-release-fix-macaroon-auth
Merge into: lp:launchpad
Diff against target: 153 lines (+67/-20)
2 files modified
lib/lp/snappy/model/snapstoreclient.py (+1/-1)
lib/lp/snappy/tests/test_snapstoreclient.py (+66/-19)
To merge this branch: bzr merge lp:~cjwatson/launchpad/snap-release-fix-macaroon-auth
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+318352@code.launchpad.net

Commit message

Allow releasing a snap build to channels without a discharge macaroon.

Description of the change

I handled pushing but apparently forgot about releasing. Beefed up tests a bit for both.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) :
review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/snappy/model/snapstoreclient.py'
2--- lib/lp/snappy/model/snapstoreclient.py 2017-01-27 12:44:41 +0000
3+++ lib/lp/snappy/model/snapstoreclient.py 2017-02-27 12:24:14 +0000
4@@ -340,7 +340,7 @@
5 release_url, method="POST", json=data,
6 auth=MacaroonAuth(
7 snap.store_secrets["root"],
8- snap.store_secrets["discharge"]))
9+ snap.store_secrets.get("discharge")))
10 except requests.HTTPError as e:
11 if e.response is not None:
12 error = None
13
14=== modified file 'lib/lp/snappy/tests/test_snapstoreclient.py'
15--- lib/lp/snappy/tests/test_snapstoreclient.py 2017-01-27 12:44:41 +0000
16+++ lib/lp/snappy/tests/test_snapstoreclient.py 2017-02-27 12:24:14 +0000
17@@ -323,11 +323,13 @@
18 self.client.requestPackageUploadPermission,
19 snappy_series, "test-snap")
20
21- def test_upload(self):
22+ def makeUploadableSnapBuild(self, store_secrets=None):
23+ if store_secrets is None:
24+ store_secrets = self._make_store_secrets()
25 snap = self.factory.makeSnap(
26 store_upload=True,
27 store_series=self.factory.makeSnappySeries(name="rolling"),
28- store_name="test-snap", store_secrets=self._make_store_secrets())
29+ store_name="test-snap", store_secrets=store_secrets)
30 snapbuild = self.factory.makeSnapBuild(snap=snap)
31 snap_lfa = self.factory.makeLibraryFileAlias(
32 filename="test-snap.snap", content="dummy snap content")
33@@ -336,6 +338,10 @@
34 filename="test-snap.manifest", content="dummy manifest content")
35 self.factory.makeSnapFile(
36 snapbuild=snapbuild, libraryfile=manifest_lfa)
37+ return snapbuild
38+
39+ def test_upload(self):
40+ snapbuild = self.makeUploadableSnapBuild()
41 transaction.commit()
42 with dbuser(config.ISnapStoreUploadJobSource.dbuser):
43 with HTTMock(self._unscanned_upload_handler,
44@@ -362,6 +368,37 @@
45 "name": "test-snap", "updown_id": 1, "series": "rolling",
46 }))
47
48+ def test_upload_no_discharge(self):
49+ root_key = hashlib.sha256(self.factory.getUniqueString()).hexdigest()
50+ root_macaroon = Macaroon(key=root_key)
51+ snapbuild = self.makeUploadableSnapBuild(
52+ store_secrets={"root": root_macaroon.serialize()})
53+ transaction.commit()
54+ with dbuser(config.ISnapStoreUploadJobSource.dbuser):
55+ with HTTMock(self._unscanned_upload_handler,
56+ self._snap_push_handler):
57+ self.assertEqual(
58+ "http://sca.example/dev/api/snaps/1/builds/1/status",
59+ self.client.upload(snapbuild))
60+ self.assertThat(self.unscanned_upload_requests, MatchesListwise([
61+ RequestMatches(
62+ url=Equals("http://updown.example/unscanned-upload/"),
63+ method=Equals("POST"),
64+ form_data={
65+ "binary": MatchesStructure.byEquality(
66+ name="binary", filename="test-snap.snap",
67+ value="dummy snap content",
68+ type="application/octet-stream",
69+ )})]))
70+ self.assertThat(self.snap_push_request, RequestMatches(
71+ url=Equals("http://sca.example/dev/api/snap-push/"),
72+ method=Equals("POST"),
73+ headers=ContainsDict({"Content-Type": Equals("application/json")}),
74+ auth=("Macaroon", MacaroonsVerify(root_key)),
75+ json_data={
76+ "name": "test-snap", "updown_id": 1, "series": "rolling",
77+ }))
78+
79 def test_upload_unauthorized(self):
80 @urlmatch(path=r".*/snap-push/$")
81 def snap_push_handler(url, request):
82@@ -372,14 +409,7 @@
83 }
84
85 store_secrets = self._make_store_secrets()
86- snap = self.factory.makeSnap(
87- store_upload=True,
88- store_series=self.factory.makeSnappySeries(name="rolling"),
89- store_name="test-snap", store_secrets=store_secrets)
90- snapbuild = self.factory.makeSnapBuild(snap=snap)
91- lfa = self.factory.makeLibraryFileAlias(
92- filename="test-snap.snap", content="dummy snap content")
93- self.factory.makeSnapFile(snapbuild=snapbuild, libraryfile=lfa)
94+ snapbuild = self.makeUploadableSnapBuild(store_secrets=store_secrets)
95 transaction.commit()
96 with dbuser(config.ISnapStoreUploadJobSource.dbuser):
97 with HTTMock(self._unscanned_upload_handler, snap_push_handler,
98@@ -402,14 +432,7 @@
99 snap_push_handler.call_count = 0
100
101 store_secrets = self._make_store_secrets()
102- snap = self.factory.makeSnap(
103- store_upload=True,
104- store_series=self.factory.makeSnappySeries(name="rolling"),
105- store_name="test-snap", store_secrets=store_secrets)
106- snapbuild = self.factory.makeSnapBuild(snap=snap)
107- lfa = self.factory.makeLibraryFileAlias(
108- filename="test-snap.snap", content="dummy snap content")
109- self.factory.makeSnapFile(snapbuild=snapbuild, libraryfile=lfa)
110+ snapbuild = self.makeUploadableSnapBuild(store_secrets=store_secrets)
111 transaction.commit()
112 with dbuser(config.ISnapStoreUploadJobSource.dbuser):
113 with HTTMock(self._unscanned_upload_handler, snap_push_handler,
114@@ -419,7 +442,8 @@
115 self.client.upload(snapbuild))
116 self.assertEqual(2, snap_push_handler.call_count)
117 self.assertNotEqual(
118- store_secrets["discharge"], snap.store_secrets["discharge"])
119+ store_secrets["discharge"],
120+ snapbuild.snap.store_secrets["discharge"])
121
122 def test_refresh_discharge_macaroon(self):
123 store_secrets = self._make_store_secrets()
124@@ -595,6 +619,29 @@
125 "channels": ["stable", "edge"], "series": "rolling",
126 }))
127
128+ def test_release_no_discharge(self):
129+ root_key = hashlib.sha256(self.factory.getUniqueString()).hexdigest()
130+ root_macaroon = Macaroon(key=root_key)
131+ with HTTMock(self._channels_handler):
132+ snap = self.factory.makeSnap(
133+ store_upload=True,
134+ store_series=self.factory.makeSnappySeries(name="rolling"),
135+ store_name="test-snap",
136+ store_secrets={"root": root_macaroon.serialize()},
137+ store_channels=["stable", "edge"])
138+ snapbuild = self.factory.makeSnapBuild(snap=snap)
139+ with HTTMock(self._snap_release_handler):
140+ self.client.release(snapbuild, 1)
141+ self.assertThat(self.snap_release_request, RequestMatches(
142+ url=Equals("http://sca.example/dev/api/snap-release/"),
143+ method=Equals("POST"),
144+ headers=ContainsDict({"Content-Type": Equals("application/json")}),
145+ auth=("Macaroon", MacaroonsVerify(root_key)),
146+ json_data={
147+ "name": "test-snap", "revision": 1,
148+ "channels": ["stable", "edge"], "series": "rolling",
149+ }))
150+
151 def test_release_error(self):
152 @urlmatch(path=r".*/snap-release/$")
153 def handler(url, request):