Skip to content

Commit 7a977c3

Browse files
committed
Add Client.create_repository() [RHELDST-22483]
TODO
1 parent dfe6dd8 commit 7a977c3

File tree

14 files changed

+207
-21
lines changed

14 files changed

+207
-21
lines changed

pubtools/pulplib/_impl/client/client.py

+31
Original file line numberDiff line numberDiff line change
@@ -1054,3 +1054,34 @@ def _do_sync(self, repo_id, sync_options):
10541054
return self._task_executor.submit(
10551055
self._do_request, method="POST", url=url, json=body
10561056
)
1057+
1058+
def create_repository(self, repo):
1059+
"""
1060+
TODO ddocstring
1061+
"""
1062+
url = os.path.join(self._url, "pulp/api/v2/repositories/")
1063+
1064+
body = repo._to_data()
1065+
repo_id = body["id"]
1066+
1067+
importer = body.pop("importers", [])
1068+
body["importer_type_id"] = importer[0]["importer_type_id"] if importer else None
1069+
body["importer_config"] = importer[0]["config"] if importer else None
1070+
1071+
def log_existing_repo(exception):
1072+
if (
1073+
getattr(exception, "response", None) is not None
1074+
and exception.response.status_code == 409
1075+
):
1076+
LOG.warning("Repository %s already exists", repo_id)
1077+
return None
1078+
1079+
raise exception
1080+
1081+
LOG.debug("Creating repository %s", repo_id)
1082+
out = self._request_executor.submit(
1083+
self._do_request, method="POST", url=url, json=body
1084+
)
1085+
1086+
out = f_map(out, error_fn=log_existing_repo)
1087+
return f_map(out, lambda _: None)

pubtools/pulplib/_impl/client/retry.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ def should_retry(self, attempt, future):
2929

3030
exception = future.exception()
3131
if exception and getattr(exception, "response", None) is not None:
32-
# if returned status code is 404, never retry on that
33-
if exception.response.status_code == 404:
32+
# if returned status code is 404 or 409, never retry on that
33+
if exception.response.status_code in (404, 409):
3434
return False
3535

3636
if exception and retry:

pubtools/pulplib/_impl/fake/client.py

+9
Original file line numberDiff line numberDiff line change
@@ -651,3 +651,12 @@ def _do_sync(self, repo_id, sync_config): # pylint:disable = unused-argument
651651
self._state.sync_history.append(Sync(repo_f.result(), [task], sync_config))
652652

653653
return f_return([task])
654+
655+
def create_repository(self, repo):
656+
with self._state.lock:
657+
if repo.id not in [
658+
existing_repo.id for existing_repo in self._state.repositories
659+
]:
660+
self._state.repositories.append(repo)
661+
662+
return f_return(None)

pubtools/pulplib/_impl/model/common.py

+12
Original file line numberDiff line numberDiff line change
@@ -234,3 +234,15 @@ def _delete(self, resource_type, resource_id):
234234
delete_f = client._delete_resource(resource_type, resource_id)
235235
delete_f = f_map(delete_f, self.__detach)
236236
return f_proxy(delete_f)
237+
238+
239+
def schemaless_init(cls, data):
240+
# Construct and return an instance of (attrs-using) cls from
241+
# pulp data, where data in pulp has no schema at all (and hence
242+
# every field could possibly be missing).
243+
kwargs = {}
244+
for key in [fld.name for fld in attr.fields(cls)]:
245+
if key in data:
246+
kwargs[key] = data[key]
247+
248+
return cls(**kwargs)

pubtools/pulplib/_impl/model/repository/base.py

+37
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ...hooks import pm
2020
from ...util import dict_put, lookup, ABSENT
2121

22+
from ..common import schemaless_init
2223

2324
LOG = logging.getLogger("pubtools.pulplib")
2425

@@ -35,6 +36,29 @@ def decorate(klass):
3536
return decorate
3637

3738

39+
@attr.s(kw_only=True, frozen=True)
40+
class Importer(object):
41+
type_id = pulp_attrib(default=None, type=str, pulp_field="importer_type_id")
42+
43+
config = pulp_attrib(default=None, type=dict, pulp_field="importer_config")
44+
45+
@classmethod
46+
def _from_data(cls, data):
47+
# Convert from raw list/dict as provided in Pulp responses into model.
48+
if isinstance(data, list):
49+
return [cls._from_data(item) for item in data]
50+
51+
return schemaless_init(cls, data)
52+
53+
def _to_data(self):
54+
return [
55+
{
56+
"importer_type_id": self.type_id,
57+
"config": self.config,
58+
}
59+
]
60+
61+
3862
@attr.s(kw_only=True, frozen=True)
3963
class PublishOptions(object):
4064
"""Options controlling a repository
@@ -338,6 +362,19 @@ class Repository(PulpObject, Deletable):
338362
339363
.. versionadded:: 2.37.0
340364
"""
365+
"""
366+
367+
"""
368+
importer = pulp_attrib(
369+
default=Importer(),
370+
type=Importer,
371+
pulp_field="importers",
372+
pulp_py_converter=Importer._from_data,
373+
py_pulp_converter=Importer._to_data,
374+
)
375+
"""
376+
todo
377+
"""
341378

342379
@distributors.validator
343380
def _check_repo_id(self, _, value):

pubtools/pulplib/_impl/model/repository/file.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from attr import validators
55
from frozenlist2 import frozenlist
66

7-
from .base import Repository, SyncOptions, repo_type
7+
from .base import Repository, SyncOptions, repo_type, Importer
88
from ...model.unit import FileUnit
99
from ..attr import pulp_attrib
1010
from ... import compat_attr as attr
@@ -13,6 +13,13 @@
1313
LOG = logging.getLogger("pubtools.pulplib")
1414

1515

16+
@attr.s(kw_only=True, frozen=True)
17+
class IsoImporter(Importer):
18+
type_id = pulp_attrib(
19+
default="iso_importer", type=str, pulp_field="importer_type_id"
20+
)
21+
22+
1623
@attr.s(kw_only=True, frozen=True)
1724
class FileSyncOptions(SyncOptions):
1825
"""Options controlling a file repository
@@ -49,6 +56,14 @@ class FileRepository(Repository):
4956
converter=frozenlist,
5057
)
5158

59+
importer = pulp_attrib(
60+
default=IsoImporter(),
61+
type=IsoImporter,
62+
pulp_field="importers",
63+
pulp_py_converter=IsoImporter._from_data,
64+
py_pulp_converter=IsoImporter._to_data,
65+
)
66+
5267
def upload_file(self, file_obj, relative_url=None, **kwargs):
5368
"""Upload a file to this repository.
5469

pubtools/pulplib/_impl/model/repository/yum.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,21 @@
33
from frozenlist2 import frozenlist
44

55
from more_executors.futures import f_map, f_proxy, f_return, f_zip, f_flat_map
6-
from .base import Repository, SyncOptions, repo_type
6+
from .base import Repository, SyncOptions, repo_type, Importer
77
from ..attr import pulp_attrib
88
from ..common import DetachedException
99
from ...model.unit import RpmUnit
1010
from ... import compat_attr as attr, comps
1111
from ...criteria import Criteria, Matcher
1212

1313

14+
@attr.s(kw_only=True, frozen=True)
15+
class YumImporter(Importer):
16+
type_id = pulp_attrib(
17+
default="yum_importer", type=str, pulp_field="importer_type_id"
18+
)
19+
20+
1421
@attr.s(kw_only=True, frozen=True)
1522
class YumSyncOptions(SyncOptions):
1623
"""Options controlling a yum repository
@@ -103,6 +110,14 @@ class YumRepository(Repository):
103110
)
104111
"""Version of UBI config that should be used for population of this repository."""
105112

113+
importer = pulp_attrib(
114+
default=YumImporter(),
115+
type=YumImporter,
116+
pulp_field="importers",
117+
pulp_py_converter=YumImporter._from_data,
118+
py_pulp_converter=YumImporter._to_data,
119+
)
120+
106121
def get_binary_repository(self):
107122
"""Find and return the binary repository relating to this repository.
108123

pubtools/pulplib/_impl/model/unit/base.py

-12
Original file line numberDiff line numberDiff line change
@@ -139,15 +139,3 @@ def _usermeta_from_kwargs(cls, **kwargs):
139139
)
140140

141141
return out
142-
143-
144-
def schemaless_init(cls, data):
145-
# Construct and return an instance of (attrs-using) cls from
146-
# pulp data, where data in pulp has no schema at all (and hence
147-
# every field could possibly be missing).
148-
kwargs = {}
149-
for key in [fld.name for fld in attr.fields(cls)]:
150-
if key in data:
151-
kwargs[key] = data[key]
152-
153-
return cls(**kwargs)

pubtools/pulplib/_impl/model/unit/erratum.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
from frozenlist2 import frozenlist
22

3-
from .base import Unit, PulpObject, unit_type, schemaless_init
3+
from .base import Unit, PulpObject, unit_type
44

55
from ..attr import pulp_attrib
6+
from ..common import schemaless_init
67
from ... import compat_attr as attr
78
from ..validate import (
89
optional_bool,

pubtools/pulplib/_impl/model/unit/modulemd.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import re
22

3-
from .base import Unit, unit_type, schemaless_init
3+
from .base import Unit, unit_type
44

55
from ..attr import pulp_attrib
6+
from ..common import schemaless_init
67
from ... import compat_attr as attr
78
from ..convert import (
89
frozenlist_or_none_converter,

pubtools/pulplib/_impl/model/unit/rpm.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import datetime
22

33
from pubtools.pulplib._impl.model.validate import optional_list_of
4-
from .base import Unit, unit_type, schemaless_init
4+
from .base import Unit, unit_type
55

66
from ..attr import pulp_attrib
7+
from ..common import schemaless_init
78
from ... import compat_attr as attr
89
from ..convert import (
910
frozenlist_or_none_converter,

pubtools/pulplib/_impl/schema/repository.yaml

+6-2
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ properties:
6969
# Note that this field is not set by Pulp itself. Only certain tools
7070
# are expected to initialize this field when creating a repo.
7171
created:
72-
type: string
72+
anyOf:
73+
- type: "null"
74+
- type: string
7375
# example: 2019-06-05T11:56:50Z
7476
pattern: "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z"
7577

@@ -100,7 +102,9 @@ properties:
100102

101103
# Version of ubi config that should be used for population of this repository
102104
ubi_config_version:
103-
type: string
105+
anyOf:
106+
- type: "null"
107+
- type: string
104108

105109
# Flag indicating whether the repository is visible in production instance
106110
# of download service. Stored as string.

tests/fake/test_fake_create_repo.py

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
from pubtools.pulplib import FakeController, Repository
2+
3+
4+
def test_create_repository():
5+
"""Client.create_repository() with fake client adds new repositories to controller."""
6+
controller = FakeController()
7+
8+
client = controller.client
9+
client.create_repository(Repository(id="repo1"))
10+
client.create_repository(Repository(id="repo2"))
11+
12+
# adding already existing repository has no effect
13+
client.create_repository(Repository(id="repo1"))
14+
# The change should be reflected in the controller,
15+
# with two repositories present
16+
assert controller.repositories == [Repository(id="repo1"), Repository(id="repo2")]

tests/repository/test_yum_repository.py

+56
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pytest
2+
import logging
23

34
from pubtools.pulplib import Repository, YumRepository, DetachedException
45

@@ -193,3 +194,58 @@ def test_related_repositories_detached_client():
193194

194195
with pytest.raises(DetachedException):
195196
repo_binary_test.get_binary_repository()
197+
198+
199+
def test_create_repository(client, requests_mocker):
200+
repo = YumRepository(id="yum_repo")
201+
repo.__dict__["_client"] = client
202+
203+
requests_mocker.post(
204+
"https://pulp.example.com/pulp/api/v2/repositories/",
205+
json={},
206+
)
207+
208+
out = client.create_repository(repo)
209+
# check return value of create_repository() call
210+
assert out.result() is None
211+
212+
hist = requests_mocker.request_history
213+
# there should be exactly one request sent
214+
assert len(hist) == 1
215+
216+
query = hist[0].json()
217+
# check id of repository sent in request body
218+
assert query["id"] == "yum_repo"
219+
# check importer data sent in request body that
220+
# are automatically added for yum repository
221+
assert query["importer_type_id"] == "yum_importer"
222+
assert query["importer_config"] is None
223+
224+
225+
def test_create_repository_already_exists(client, requests_mocker, caplog):
226+
repo = YumRepository(id="yum_repo")
227+
repo.__dict__["_client"] = client
228+
229+
requests_mocker.post(
230+
"https://pulp.example.com/pulp/api/v2/repositories/",
231+
status_code=409,
232+
text="Conflict 409 status",
233+
)
234+
with caplog.at_level(logging.WARNING):
235+
out = client.create_repository(repo)
236+
# check return value of create_repository() call
237+
assert out.result() is None
238+
239+
hist = requests_mocker.request_history
240+
# there should are exactly one request sent, 409 status is never retried
241+
assert len(hist) == 1
242+
243+
query = hist[0].json()
244+
# check id of repository sent in request body
245+
assert query["id"] == "yum_repo"
246+
# check importer data sent in request body that
247+
# are automatically added for yum repository
248+
assert query["importer_type_id"] == "yum_importer"
249+
assert query["importer_config"] is None
250+
# check logged information about existing repository
251+
assert "Repository yum_repo already exists" in caplog.text

0 commit comments

Comments
 (0)