Skip to content

Commit 28ab72d

Browse files
committed
refactor: split tuf.__init__.tasks to tuf.tasks
this refactor split the tasks from the tuf.__init__.py to tasks.py Signed-off-by: Kairo de Araujo <[email protected]>
1 parent 2a4e996 commit 28ab72d

File tree

7 files changed

+139
-101
lines changed

7 files changed

+139
-101
lines changed

tests/unit/forklift/test_legacy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@
5757
Role,
5858
)
5959
from warehouse.packaging.tasks import sync_file_to_cache, update_bigquery_release_files
60-
from warehouse.tuf import update_metadata
60+
from warehouse.tuf.tasks import update_metadata
6161

6262
from ...common.db.accounts import EmailFactory, UserFactory
6363
from ...common.db.classifiers import ClassifierFactory

tests/unit/tuf/test_tasks.py

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
from pretend import call, call_recorder, stub
13+
14+
from warehouse import tuf
15+
16+
17+
class TestTUFTasks:
18+
task_id = "123456"
19+
20+
def test_update_metadata(self, db_request, monkeypatch):
21+
project_id = "id"
22+
project_name = "name"
23+
24+
project = stub(normalized_name=project_name)
25+
26+
one = call_recorder(lambda: project)
27+
db_request.db.query = lambda a: stub(filter=lambda a: stub(one=one))
28+
29+
rstuf_url = "url"
30+
index_digest = "digest"
31+
index_size = 42
32+
33+
db_request.registry.settings = {"rstuf.api_url": rstuf_url}
34+
35+
render = call_recorder(lambda *a, **kw: (index_digest, None, index_size))
36+
tuf.tasks.render_simple_detail = render
37+
38+
post = call_recorder(lambda *a: self.task_id)
39+
monkeypatch.setattr(tuf.tasks, "post_artifacts", post)
40+
41+
wait = call_recorder(lambda *a: None)
42+
monkeypatch.setattr(tuf.tasks, "wait_for_success", wait)
43+
44+
tuf.tasks.update_metadata(db_request, project_id)
45+
assert one.calls == [call()]
46+
assert render.calls == [call(project, db_request, store=True)]
47+
assert post.calls == [
48+
call(
49+
rstuf_url,
50+
{
51+
"targets": [
52+
{
53+
"path": project_name,
54+
"info": {
55+
"length": index_size,
56+
"hashes": {"blake2b-256": index_digest},
57+
},
58+
}
59+
]
60+
},
61+
)
62+
]
63+
assert wait.calls == [call(rstuf_url, self.task_id)]
64+
65+
def test_update_metadata_no_rstuf_api_url(self, db_request):
66+
project_id = "id"
67+
project_name = "name"
68+
69+
project = stub(normalized_name=project_name)
70+
71+
one = call_recorder(lambda: project)
72+
db_request.db.query = lambda a: stub(filter=lambda a: stub(one=one))
73+
74+
# Test early return, if no RSTUF API URL configured
75+
db_request.registry.settings = {"rstuf.api_url": None}
76+
tuf.tasks.update_metadata(db_request, project_id)
77+
78+
assert not one.calls

tests/unit/tuf/test_tuf.py

-52
Original file line numberDiff line numberDiff line change
@@ -119,55 +119,3 @@ def test_wait_for_success_error(self, state, iterations, monkeypatch):
119119
tuf.wait_for_success(self.server, self.task_id)
120120

121121
assert get_task_state.calls == [call(self.server, self.task_id)] * iterations
122-
123-
def test_update_metadata(self, db_request, monkeypatch):
124-
project_id = "id"
125-
project_name = "name"
126-
127-
project = stub(normalized_name=project_name)
128-
129-
one = call_recorder(lambda: project)
130-
db_request.db.query = lambda a: stub(filter=lambda a: stub(one=one))
131-
132-
# Test early return, if no RSTUF API URL configured
133-
db_request.registry.settings = {"rstuf.api_url": None}
134-
tuf.update_metadata(db_request, project_id)
135-
136-
assert not one.calls
137-
138-
# Test regular run
139-
rstuf_url = "url"
140-
index_digest = "digest"
141-
index_size = 42
142-
143-
db_request.registry.settings = {"rstuf.api_url": rstuf_url}
144-
145-
render = call_recorder(lambda *a, **kw: (index_digest, None, index_size))
146-
monkeypatch.setattr(tuf, "render_simple_detail", render)
147-
148-
post = call_recorder(lambda *a: self.task_id)
149-
monkeypatch.setattr(tuf, "post_artifacts", post)
150-
151-
wait = call_recorder(lambda *a: None)
152-
monkeypatch.setattr(tuf, "wait_for_success", wait)
153-
154-
tuf.update_metadata(db_request, project_id)
155-
assert one.calls == [call()]
156-
assert render.calls == [call(project, db_request, store=True)]
157-
assert post.calls == [
158-
call(
159-
rstuf_url,
160-
{
161-
"targets": [
162-
{
163-
"path": project_name,
164-
"info": {
165-
"length": index_size,
166-
"hashes": {"blake2b-256": index_digest},
167-
},
168-
}
169-
]
170-
},
171-
)
172-
]
173-
assert wait.calls == [call(rstuf_url, self.task_id)]

warehouse/forklift/legacy.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1307,7 +1307,7 @@ def file_upload(request):
13071307

13081308
request.db.flush() # flush db now so server default values are populated for celery
13091309

1310-
request.task(tuf.update_metadata).delay(release.project.id)
1310+
request.task(tuf.tasks.update_metadata).delay(release.project.id)
13111311

13121312
# Push updates to BigQuery
13131313
dist_metadata = {

warehouse/manage/views/__init__.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -2052,7 +2052,7 @@ def delete_project(project, request):
20522052

20532053
remove_project(project, request)
20542054

2055-
request.task(tuf.update_metadata).delay(project.id)
2055+
request.task(tuf.tasks.update_metadata).delay(project.id)
20562056

20572057
return HTTPSeeOther(request.route_path("manage.projects"))
20582058

@@ -2224,7 +2224,7 @@ def yank_project_release(self):
22242224
recipient_role=contributor_role,
22252225
)
22262226

2227-
self.request.task(tuf.update_metadata).delay(self.release.project.id)
2227+
self.request.task(tuf.tasks.update_metadata).delay(self.release.project.id)
22282228
return HTTPSeeOther(
22292229
self.request.route_path(
22302230
"manage.project.releases", project_name=self.release.project.name
@@ -2309,7 +2309,7 @@ def unyank_project_release(self):
23092309
submitter_role=submitter_role,
23102310
recipient_role=contributor_role,
23112311
)
2312-
self.request.task(tuf.update_metadata).delay(self.release.project.id)
2312+
self.request.task(tuf.tasks.update_metadata).delay(self.release.project.id)
23132313
return HTTPSeeOther(
23142314
self.request.route_path(
23152315
"manage.project.releases", project_name=self.release.project.name
@@ -2409,7 +2409,7 @@ def delete_project_release(self):
24092409
recipient_role=contributor_role,
24102410
)
24112411

2412-
self.request.task(tuf.update_metadata).delay(self.release.project.id)
2412+
self.request.task(tuf.tasks.update_metadata).delay(self.release.project.id)
24132413
return HTTPSeeOther(
24142414
self.request.route_path(
24152415
"manage.project.releases", project_name=self.release.project.name
@@ -2508,7 +2508,7 @@ def _error(message):
25082508
self.request.session.flash(
25092509
f"Deleted file {release_file.filename!r}", queue="success"
25102510
)
2511-
self.request.task(tuf.update_metadata).delay(self.release.project.id)
2511+
self.request.task(tuf.tasks.update_metadata).delay(self.release.project.id)
25122512
return HTTPSeeOther(
25132513
self.request.route_path(
25142514
"manage.project.release",

warehouse/tuf/__init__.py

-42
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,9 @@
1717
import time
1818

1919
from typing import Any
20-
from uuid import UUID
2120

2221
import requests
2322

24-
from pyramid.request import Request
25-
26-
from warehouse import tasks
27-
from warehouse.packaging.models import Project
28-
from warehouse.packaging.utils import render_simple_detail
29-
3023

3124
class RSTUFError(Exception):
3225
pass
@@ -101,38 +94,3 @@ def wait_for_success(server: str, task_id: str):
10194

10295
else:
10396
raise RSTUFError("RSTUF job failed, please check payload and retry")
104-
105-
106-
@tasks.task(ignore_result=True, acks_late=True)
107-
def update_metadata(request: Request, project_id: UUID):
108-
"""Update TUF metadata to capture project changes (PEP 458).
109-
110-
NOTE: PEP 458 says, TUF targets metadata must include path, hash and size of
111-
distributions files and simple detail files. In reality, simple detail files
112-
are enough, as they already include all relevant distribution file infos.
113-
"""
114-
server = request.registry.settings["rstuf.api_url"]
115-
if not server:
116-
return
117-
118-
project = request.db.query(Project).filter(Project.id == project_id).one()
119-
120-
# NOTE: We ignore the returned simple detail path with the content hash as
121-
# infix. In TUF metadata the project name and hash are listed separately, so
122-
# that there is only one entry per target file, even if the content changes.
123-
digest, _, size = render_simple_detail(project, request, store=True)
124-
payload = {
125-
"targets": [
126-
{
127-
"path": project.normalized_name,
128-
"info": {
129-
"length": size,
130-
"hashes": {"blake2b-256": digest},
131-
},
132-
}
133-
]
134-
}
135-
136-
# TODO: Handle errors: pass, retry or notify
137-
task_id = post_artifacts(server, payload)
138-
wait_for_success(server, task_id)

warehouse/tuf/tasks.py

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Licensed under the Apache License, Version 2.0 (the "License");
2+
# you may not use this file except in compliance with the License.
3+
# You may obtain a copy of the License at
4+
#
5+
# http://www.apache.org/licenses/LICENSE-2.0
6+
#
7+
# Unless required by applicable law or agreed to in writing, software
8+
# distributed under the License is distributed on an "AS IS" BASIS,
9+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
# See the License for the specific language governing permissions and
11+
# limitations under the License.
12+
from uuid import UUID
13+
14+
from pyramid.request import Request
15+
16+
from warehouse import tasks
17+
from warehouse.packaging.models import Project
18+
from warehouse.packaging.utils import render_simple_detail
19+
from warehouse.tuf import post_artifacts, wait_for_success
20+
21+
22+
@tasks.task(ignore_result=True, acks_late=True)
23+
def update_metadata(request: Request, project_id: UUID):
24+
"""Update TUF metadata to capture project changes (PEP 458).
25+
26+
NOTE: PEP 458 says, TUF targets metadata must include path, hash and size of
27+
distributions files and simple detail files. In reality, simple detail files
28+
are enough, as they already include all relevant distribution file infos.
29+
"""
30+
server = request.registry.settings["rstuf.api_url"]
31+
if not server:
32+
return
33+
34+
project = request.db.query(Project).filter(Project.id == project_id).one()
35+
36+
# NOTE: We ignore the returned simple detail path with the content hash as
37+
# infix. In TUF metadata the project name and hash are listed separately, so
38+
# that there is only one entry per target file, even if the content changes.
39+
digest, _, size = render_simple_detail(project, request, store=True)
40+
payload = {
41+
"targets": [
42+
{
43+
"path": project.normalized_name,
44+
"info": {
45+
"length": size,
46+
"hashes": {"blake2b-256": digest},
47+
},
48+
}
49+
]
50+
}
51+
52+
# TODO: Handle errors: pass, retry or notify
53+
task_id = post_artifacts(server, payload)
54+
wait_for_success(server, task_id)

0 commit comments

Comments
 (0)