pulpcore 3.83.2__py3-none-any.whl → 3.85.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pulpcore might be problematic. Click here for more details.

Files changed (169) hide show
  1. pulp_certguard/app/__init__.py +1 -1
  2. pulp_certguard/app/models.py +7 -26
  3. pulp_certguard/app/serializers.py +0 -2
  4. pulp_certguard/rhsm/__init__.py +4 -0
  5. pulp_certguard/rhsm/rhsm_check_path.py +198 -0
  6. pulp_certguard/tests/unit/certdata.py +249 -0
  7. pulp_certguard/tests/unit/test_rhsm_check_path.py +213 -0
  8. pulp_file/app/__init__.py +1 -1
  9. pulp_file/app/migrations/0001_initial_squashed_0016_add_domain.py +0 -20
  10. pulp_file/app/migrations/0017_alter_filealternatecontentsource_alternatecontentsource_ptr_and_more.py +1 -1
  11. pulpcore/app/apps.py +2 -12
  12. pulpcore/app/entrypoint.py +22 -22
  13. pulpcore/app/migrations/0001_squashed_0090_char_to_text_field.py +0 -95
  14. pulpcore/app/migrations/0091_systemid.py +1 -1
  15. pulpcore/app/migrations/0134_task_insert_trigger.py +81 -0
  16. pulpcore/app/migrations/0135_task_pulp_task_resources_index.py +25 -0
  17. pulp_file/app/migrations/0006_delete_filefilesystemexporter.py → pulpcore/app/migrations/0136_delete_basedistribution.py +3 -3
  18. pulpcore/app/migrations/0137_appstatus.py +33 -0
  19. pulpcore/app/migrations/0138_vulnerabilityreport.py +33 -0
  20. pulpcore/app/models/__init__.py +4 -1
  21. pulpcore/app/models/publication.py +0 -41
  22. pulpcore/app/models/status.py +145 -0
  23. pulpcore/app/models/task.py +8 -0
  24. pulpcore/app/models/vulnerability_report.py +34 -0
  25. pulpcore/app/serializers/__init__.py +1 -0
  26. pulpcore/app/serializers/content.py +13 -1
  27. pulpcore/app/serializers/repository.py +8 -1
  28. pulpcore/app/serializers/vulnerability_report.py +27 -0
  29. pulpcore/app/settings.py +13 -38
  30. pulpcore/app/tasks/__init__.py +2 -0
  31. pulpcore/app/tasks/purge.py +8 -5
  32. pulpcore/app/tasks/vulnerability_report.py +159 -0
  33. pulpcore/app/viewsets/__init__.py +1 -0
  34. pulpcore/app/viewsets/vulnerability_report.py +20 -0
  35. pulpcore/constants.py +8 -0
  36. pulpcore/content/__init__.py +23 -22
  37. pulpcore/content/handler.py +5 -2
  38. pulpcore/migrations.py +38 -11
  39. pulpcore/openapi/__init__.py +8 -0
  40. pulpcore/plugin/models/__init__.py +2 -0
  41. pulpcore/plugin/serializers/__init__.py +2 -0
  42. pulpcore/plugin/tasking.py +2 -0
  43. pulpcore/plugin/viewsets/__init__.py +2 -0
  44. pulpcore/pytest_plugin.py +21 -21
  45. pulpcore/tasking/entrypoint.py +12 -2
  46. pulpcore/tasking/tasks.py +5 -30
  47. pulpcore/tasking/worker.py +115 -74
  48. pulpcore/tests/functional/api/test_auth.py +18 -3
  49. pulpcore/tests/functional/api/test_login.py +62 -32
  50. pulpcore/tests/functional/api/test_openapi_schema.py +32 -15
  51. pulpcore/tests/functional/api/using_plugin/test_checkpoint.py +23 -1
  52. pulpcore/tests/functional/api/using_plugin/test_proxy.py +1 -1
  53. pulpcore/tests/unit/content/test_heartbeat.py +11 -8
  54. pulpcore/tests/unit/test_vulnerability_report.py +74 -0
  55. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/METADATA +13 -18
  56. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/RECORD +60 -156
  57. pulp_certguard/app/utils.py +0 -28
  58. pulp_certguard/tests/unit/test_models.py +0 -9
  59. pulp_file/app/migrations/0001_initial.py +0 -59
  60. pulp_file/app/migrations/0002_file_related_names.py +0 -55
  61. pulp_file/app/migrations/0003_auto_20191014_1721.py +0 -18
  62. pulp_file/app/migrations/0004_filefilesystemexporter.py +0 -21
  63. pulp_file/app/migrations/0005_filerepository.py +0 -24
  64. pulp_file/app/migrations/0007_filefilesystemexporter.py +0 -25
  65. pulp_file/app/migrations/0008_add_manifest_field.py +0 -19
  66. pulp_file/app/migrations/0009_move_data_to_new_master_distribution_model.py +0 -77
  67. pulp_file/app/migrations/0010_auto_publish.py +0 -23
  68. pulp_file/app/migrations/0011_fix_auto_publish.py +0 -36
  69. pulp_file/app/migrations/0012_delete_filefilesystemexporter.py +0 -28
  70. pulp_file/app/migrations/0013_file_acs.py +0 -24
  71. pulp_file/app/migrations/0014_new_rbac_permissions.py +0 -33
  72. pulp_file/app/migrations/0015_allow_null_manifest.py +0 -23
  73. pulp_file/app/migrations/0016_add_domain.py +0 -25
  74. pulpcore/app/migrations/0001_initial.py +0 -451
  75. pulpcore/app/migrations/0002_increase_artifact_size_field.py +0 -18
  76. pulpcore/app/migrations/0003_remove_upload_completed.py +0 -17
  77. pulpcore/app/migrations/0004_add_duplicated_reserved_resources.py +0 -45
  78. pulpcore/app/migrations/0005_progressreport_code.py +0 -19
  79. pulpcore/app/migrations/0006_repository_plugin_managed.py +0 -18
  80. pulpcore/app/migrations/0007_delete_progress_proxies.py +0 -19
  81. pulpcore/app/migrations/0008_published_metadata_as_content.py +0 -44
  82. pulpcore/app/migrations/0009_remove_task_non_fatal_errors.py +0 -17
  83. pulpcore/app/migrations/0010_pulp_fields.py +0 -570
  84. pulpcore/app/migrations/0011_relative_path.py +0 -28
  85. pulpcore/app/migrations/0012_auto_20191104_2000.py +0 -31
  86. pulpcore/app/migrations/0013_repository_pulp_type.py +0 -18
  87. pulpcore/app/migrations/0014_remove_repository_plugin_managed.py +0 -17
  88. pulpcore/app/migrations/0015_auto_20191112_1426.py +0 -33
  89. pulpcore/app/migrations/0016_charfield_to_textfield.py +0 -68
  90. pulpcore/app/migrations/0017_remove_task_parent.py +0 -17
  91. pulpcore/app/migrations/0018_auto_20191127_2350.py +0 -20
  92. pulpcore/app/migrations/0019_add_signing_service_model.py +0 -27
  93. pulpcore/app/migrations/0020_change_publishedartifact_constraints.py +0 -17
  94. pulpcore/app/migrations/0021_add_signing_service_foreign_key.py +0 -24
  95. pulpcore/app/migrations/0022_rename_last_version.py +0 -27
  96. pulpcore/app/migrations/0023_change_exporter_models.py +0 -82
  97. pulpcore/app/migrations/0024_use_local_storage_for_uploads.py +0 -19
  98. pulpcore/app/migrations/0025_task_parent_task.py +0 -19
  99. pulpcore/app/migrations/0026_task_group.py +0 -32
  100. pulpcore/app/migrations/0027_export_backend.py +0 -31
  101. pulpcore/app/migrations/0028_import_importer_pulpimporter_pulpimporterrepository.py +0 -85
  102. pulpcore/app/migrations/0029_export_delete.py +0 -19
  103. pulpcore/app/migrations/0030_taskgroup_all_tasks_dispatched.py +0 -24
  104. pulpcore/app/migrations/0031_import_export_validate_params.py +0 -19
  105. pulpcore/app/migrations/0032_export_to_chunks.py +0 -27
  106. pulpcore/app/migrations/0033_increase_remote_artifact_size_field.py +0 -18
  107. pulpcore/app/migrations/0034_groupprogressreport.py +0 -32
  108. pulpcore/app/migrations/0035_content_upstream_id.py +0 -18
  109. pulpcore/app/migrations/0036_unprotect_last_export.py +0 -19
  110. pulpcore/app/migrations/0037_pulptemporaryfile.py +0 -28
  111. pulpcore/app/migrations/0038_repository_remote.py +0 -19
  112. pulpcore/app/migrations/0039_change_download_concurrency.py +0 -25
  113. pulpcore/app/migrations/0040_set_admin_is_staff.py +0 -28
  114. pulpcore/app/migrations/0041_accesspolicy.py +0 -29
  115. pulpcore/app/migrations/0042_rbac_for_tasks.py +0 -56
  116. pulpcore/app/migrations/0043_toc_attribute.py +0 -19
  117. pulpcore/app/migrations/0044_temp_file_artifact_field.py +0 -20
  118. pulpcore/app/migrations/0045_accesspolicy_permissions_allow_null.py +0 -19
  119. pulpcore/app/migrations/0046_task__resource_job_id.py +0 -35
  120. pulpcore/app/migrations/0047_improve_orphan_cleanup.py +0 -59
  121. pulpcore/app/migrations/0048_fips_checksums.py +0 -38
  122. pulpcore/app/migrations/0049_add_file_field_to_uploadchunk.py +0 -24
  123. pulpcore/app/migrations/0050_namespace_access_policies.py +0 -28
  124. pulpcore/app/migrations/0051_timeoutfields.py +0 -34
  125. pulpcore/app/migrations/0052_tasking_logging_cid.py +0 -18
  126. pulpcore/app/migrations/0053_remote_headers.py +0 -19
  127. pulpcore/app/migrations/0054_add_public_key.py +0 -104
  128. pulpcore/app/migrations/0055_label.py +0 -31
  129. pulpcore/app/migrations/0056_remote_rate_limit.py +0 -18
  130. pulpcore/app/migrations/0057_add_label_indexes.py +0 -23
  131. pulpcore/app/migrations/0058_accesspolicy_customized.py +0 -18
  132. pulpcore/app/migrations/0059_proxy_creds.py +0 -23
  133. pulpcore/app/migrations/0060_data_migration_proxy_creds.py +0 -45
  134. pulpcore/app/migrations/0061_call_handle_artifact_checksums_command.py +0 -87
  135. pulpcore/app/migrations/0062_add_new_distribution_mastermodel.py +0 -36
  136. pulpcore/app/migrations/0063_repository_retained_versions.py +0 -18
  137. pulpcore/app/migrations/0064_add_new_style_task_columns.py +0 -109
  138. pulpcore/app/migrations/0064_repository_user_hidden.py +0 -18
  139. pulpcore/app/migrations/0065_merge_20210615_1211.py +0 -14
  140. pulpcore/app/migrations/0066_download_concurrency_and_retry_changes.py +0 -24
  141. pulpcore/app/migrations/0067_add_protect_to_task_reservation.py +0 -19
  142. pulpcore/app/migrations/0068_add_timestamp_of_interest.py +0 -23
  143. pulpcore/app/migrations/0069_update_json_fields.py +0 -63
  144. pulpcore/app/migrations/0070_rename_retained_versions.py +0 -18
  145. pulpcore/app/migrations/0071_filesystemexport_filesystemexporter.py +0 -35
  146. pulpcore/app/migrations/0072_add_method_to_filesystem_exporter.py +0 -18
  147. pulpcore/app/migrations/0073_encrypt_remote_fields.py +0 -139
  148. pulpcore/app/migrations/0074_acs.py +0 -47
  149. pulpcore/app/migrations/0075_rbaccontentguard.py +0 -25
  150. pulpcore/app/migrations/0076_remove_reserved_resource.py +0 -39
  151. pulpcore/app/migrations/0077_move_remote_url_credentials.py +0 -41
  152. pulpcore/app/migrations/0078_grouprole_role_userrole.py +0 -70
  153. pulpcore/app/migrations/0079_rename_permissions_assignment_accesspolicy_creation_hooks.py +0 -18
  154. pulpcore/app/migrations/0080_proxy_group_model.py +0 -37
  155. pulpcore/app/migrations/0081_reapplabel_group_permissions.py +0 -59
  156. pulpcore/app/migrations/0082_add_manage_roles_permissions.py +0 -17
  157. pulpcore/app/migrations/0083_alter_group_options.py +0 -17
  158. pulpcore/app/migrations/0084_alter_rbaccontentguard_options.py +0 -17
  159. pulpcore/app/migrations/0085_contentredirectcontentguard.py +0 -26
  160. pulpcore/app/migrations/0086_task_json_fields.py +0 -77
  161. pulpcore/app/migrations/0087_taskschedule.py +0 -34
  162. pulpcore/app/migrations/0088_accesspolicy_queryset_scoping.py +0 -18
  163. pulpcore/app/migrations/0089_alter_contentredirectcontentguard_options.py +0 -17
  164. pulpcore/app/migrations/0090_char_to_text_field.py +0 -79
  165. pulpcore/tests/unit/migration/test_0077_move_remote_url_credentials.py +0 -35
  166. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/WHEEL +0 -0
  167. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/entry_points.txt +0 -0
  168. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/licenses/LICENSE +0 -0
  169. {pulpcore-3.83.2.dist-info → pulpcore-3.85.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,213 @@
1
+ from cryptography import x509
2
+ import pytest
3
+
4
+ from . import certdata
5
+
6
+ from pulp_certguard.rhsm import check_path
7
+ from pulp_certguard.rhsm.rhsm_check_path import (
8
+ bitstream,
9
+ Entitlement,
10
+ HuffmannNode,
11
+ cert_version,
12
+ split_count,
13
+ V1_PATH_OID_REGEX,
14
+ )
15
+
16
+
17
+ @pytest.fixture(scope="session")
18
+ def ent_v1() -> x509.Certificate:
19
+ return x509.load_pem_x509_certificate(certdata.ENTITLEMENT_CERT_V1_0.encode())
20
+
21
+
22
+ @pytest.fixture(scope="session")
23
+ def ent_v3_0() -> x509.Certificate:
24
+ return x509.load_pem_x509_certificate(certdata.ENTITLEMENT_CERT_V3_0.encode())
25
+
26
+
27
+ @pytest.fixture(scope="session")
28
+ def ent_v3_2() -> x509.Certificate:
29
+ return x509.load_pem_x509_certificate(certdata.ENTITLEMENT_CERT_V3_2.encode())
30
+
31
+
32
+ @pytest.fixture(scope="session", params=["3.0", "3.2"])
33
+ def ent_v3_version(request: pytest.FixtureRequest) -> str:
34
+ return request.param
35
+
36
+
37
+ @pytest.fixture(scope="session")
38
+ def ent_v3(ent_v3_version: str) -> x509.Certificate:
39
+ ver = ent_v3_version.replace(".", "_")
40
+ attribute = f"ENTITLEMENT_CERT_V{ver}"
41
+ return x509.load_pem_x509_certificate(getattr(certdata, attribute).encode())
42
+
43
+
44
+ class TestSplitCount:
45
+ """
46
+ If the first byte is < 128, that is the count itself.
47
+ Otherwise the 7 lsb describe the number of bytes to combine to the count.
48
+ """
49
+
50
+ @pytest.mark.parametrize(
51
+ "header, count",
52
+ [
53
+ pytest.param(b"\x00", 0, id="short zero"),
54
+ pytest.param(b"\x0a", 10, id="short ten"),
55
+ pytest.param(b"\x7f", 127, id="short max"),
56
+ pytest.param(b"\x80", 0, id="zero bytes zero"),
57
+ pytest.param(b"\x81\x00", 0, id="one byte zero"),
58
+ pytest.param(b"\x81\x01", 1, id="one byte one"),
59
+ pytest.param(b"\x81\xff", 255, id="one byte max"),
60
+ pytest.param(b"\x84\x00\x00\x00\x00", 0, id="four bytes zero"),
61
+ pytest.param(b"\x84\x00\x00\x00\x01", 1, id="four bytes one"),
62
+ pytest.param(b"\x84\x00\x00\x01\x00", 256, id="four bytes 256"),
63
+ pytest.param(b"\x84\x00\x00\x01\x04", 260, id="four bytes 260"),
64
+ ],
65
+ )
66
+ def test_returns_count_from(self, header: bytes, count: int) -> None:
67
+ assert split_count(header + b"\xde\xad\xbe\xaf") == (count, b"\xde\xad\xbe\xaf")
68
+
69
+
70
+ class TestBitstream:
71
+ def test_0000_produces_16_false(self) -> None:
72
+ assert list(bitstream(b"\x00\x00")) == [False] * 16
73
+
74
+ def test_0001_produces_15_false_true(self) -> None:
75
+ assert list(bitstream(b"\x00\x01")) == [False] * 15 + [True]
76
+
77
+ def test_fff0_produces_12_true_4_false(self) -> None:
78
+ assert list(bitstream(b"\xff\xf0")) == [True] * 12 + [False] * 4
79
+
80
+
81
+ def test_product_certificate_has_subject() -> None:
82
+ cert = x509.load_pem_x509_certificate(certdata.PRODUCT_CERT_V1_0.encode())
83
+ assert cert.subject.rfc4514_string() == "CN=100000000000002"
84
+
85
+
86
+ class TestV1EntitlementCertificate:
87
+ def test_has_subject(self, ent_v1: x509.Certificate) -> None:
88
+ assert ent_v1.subject.rfc4514_string() == "CN=ff80808138574bd20138574d85a50b2f"
89
+
90
+ def test_has_4_paths(self, ent_v1: x509.Certificate) -> None:
91
+ assert (
92
+ len(
93
+ [ext for ext in ent_v1.extensions if V1_PATH_OID_REGEX.match(ext.oid.dotted_string)]
94
+ )
95
+ == 4
96
+ )
97
+
98
+ def test_has_version_1_0(self, ent_v1: x509.Certificate) -> None:
99
+ assert cert_version(ent_v1) == "1.0"
100
+
101
+
102
+ class TestV3EntitlementCertificate:
103
+ def test_cert_version(self, ent_v3_version: str, ent_v3: x509.Certificate) -> None:
104
+ assert cert_version(ent_v3) == ent_v3_version
105
+
106
+
107
+ class TestEntitlement:
108
+ @pytest.fixture(scope="class")
109
+ def entitlement(self, ent_v3: x509.Certificate) -> Entitlement:
110
+ return Entitlement(ent_v3)
111
+
112
+ @pytest.fixture(scope="class")
113
+ def word_tree(self, ent_v3: x509.Certificate) -> "HuffmannNode[str]":
114
+ lex_data, _, _ = Entitlement._split_payload(Entitlement._read_payload(ent_v3))
115
+ return Entitlement._build_word_tree(lex_data)
116
+
117
+ def test_has_node_count_10(self, ent_v3: x509.Certificate) -> None:
118
+ _, node_count, _ = Entitlement._split_payload(Entitlement._read_payload(ent_v3))
119
+ assert node_count == 10
120
+
121
+ def test_00_decodes_to_empty(self, word_tree: "HuffmannNode[str]") -> None:
122
+ assert word_tree.decode(iter([False, False])) == ""
123
+
124
+ def test_100_decodes_to_path(self, word_tree: "HuffmannNode[str]") -> None:
125
+ assert word_tree.decode(iter([True, False, False])) == "path"
126
+
127
+ def test_path_tree_root_contains_foo_and_path(self, entitlement: Entitlement) -> None:
128
+ assert {"foo", "path"} == entitlement._path_tree.keys()
129
+
130
+ def test_path_tree_contains_foo_path_never(self, entitlement: Entitlement) -> None:
131
+ assert entitlement._path_tree["foo"]["path"]["never"] == {}
132
+
133
+ def test_path_tree_contains_foo_path_var_always(self, entitlement: Entitlement) -> None:
134
+ assert entitlement._path_tree["foo"]["path"]["always"]["$releasever"] == {}
135
+
136
+
137
+ class TestCheckPathV1:
138
+ @pytest.mark.parametrize(
139
+ "path",
140
+ (
141
+ "/foo/path/never",
142
+ "/foo/path/never/",
143
+ "//foo/path/never/",
144
+ "/foo/path/never/bar/a/b/c",
145
+ "/foo/path/never/bar//a/b/c",
146
+ "/foo//path/never/bar//a/b/c",
147
+ ),
148
+ )
149
+ def test_matches_foo_path_never(self, ent_v1: x509.Certificate, path: str) -> None:
150
+ assert check_path(ent_v1, path)
151
+
152
+ @pytest.mark.parametrize(
153
+ "path",
154
+ (
155
+ pytest.param("/path/never", id="misses the first part"),
156
+ pytest.param("foo", id="is only the middle part"),
157
+ "/foo",
158
+ "/foo/",
159
+ pytest.param("/foo/path/", id="misses the last part"),
160
+ pytest.param("baz/foo/path/never", id="prepended parts"),
161
+ pytest.param("/path/to//bar/awesomeos", id="substituted an empty string"),
162
+ pytest.param("/path/to/foo/bar/awesomeo", id="misses the last character"),
163
+ pytest.param("/path/to/foo/bar/awesomeos1", id="adds characters at the end"),
164
+ ),
165
+ )
166
+ def test_rejects_a_path_that(self, ent_v1: x509.Certificate, path: str) -> None:
167
+ assert not check_path(ent_v1, path)
168
+
169
+ @pytest.mark.parametrize(
170
+ "path",
171
+ (
172
+ "/path/to/foo/bar/awesomeos",
173
+ "/path/to/foo/bar/awesomeos/",
174
+ "/path/to/foo/bar/awesomeos/a/b/c",
175
+ "/path/to//bar/foo/awesomeos",
176
+ ),
177
+ )
178
+ def test_matches_path_with_substituted_variables(
179
+ self, ent_v1: x509.Certificate, path: str
180
+ ) -> None:
181
+ assert check_path(ent_v1, path)
182
+
183
+
184
+ class TestCheckPathV3:
185
+ @pytest.mark.parametrize(
186
+ "path",
187
+ (
188
+ pytest.param("/foo/path/never", id="exact path without vars"),
189
+ pytest.param("/foo/path/never/a/b/c", id="exact path with extra segments"),
190
+ pytest.param(
191
+ "////foo///path//never///", id="exact path without vars and extra slashes"
192
+ ),
193
+ pytest.param("foo/path/always/c-3po", id="exact path with variable"),
194
+ pytest.param("/path/to/awesomeos/x86_64", id="another exact path without vars"),
195
+ pytest.param("/path/to/c-3po/r2-d2/awesomeos", id="another exact path with vars"),
196
+ ),
197
+ )
198
+ def test_matches(self, ent_v3: x509.Certificate, path: str) -> None:
199
+ assert check_path(ent_v3, path)
200
+
201
+ @pytest.mark.parametrize(
202
+ "path",
203
+ (
204
+ pytest.param("/a/path/to/wherever", id="lunatic path"),
205
+ pytest.param("", id="empty path"),
206
+ pytest.param("////", id="just slashes"),
207
+ pytest.param("foo", id="to short path"),
208
+ pytest.param("path/never", id="path with front segment missing"),
209
+ pytest.param("content/foo/path/never", id="path with extra front segment"),
210
+ ),
211
+ )
212
+ def test_rejects(self, ent_v3: x509.Certificate, path: str) -> None:
213
+ assert not check_path(ent_v3, path)
pulp_file/app/__init__.py CHANGED
@@ -8,6 +8,6 @@ class PulpFilePluginAppConfig(PulpPluginAppConfig):
8
8
 
9
9
  name = "pulp_file.app"
10
10
  label = "file"
11
- version = "3.83.2"
11
+ version = "3.85.0"
12
12
  python_package_name = "pulpcore"
13
13
  domain_compatible = True
@@ -7,28 +7,8 @@ import pulpcore.app.util
7
7
 
8
8
  class Migration(migrations.Migration):
9
9
 
10
- replaces = [
11
- ("file", "0001_initial"),
12
- ("file", "0002_file_related_names"),
13
- ("file", "0003_auto_20191014_1721"),
14
- ("file", "0004_filefilesystemexporter"),
15
- ("file", "0005_filerepository"),
16
- ("file", "0006_delete_filefilesystemexporter"),
17
- ("file", "0007_filefilesystemexporter"),
18
- ("file", "0008_add_manifest_field"),
19
- ("file", "0009_move_data_to_new_master_distribution_model"),
20
- ("file", "0010_auto_publish"),
21
- ("file", "0011_fix_auto_publish"),
22
- ("file", "0012_delete_filefilesystemexporter"),
23
- ("file", "0013_file_acs"),
24
- ("file", "0014_new_rbac_permissions"),
25
- ("file", "0015_allow_null_manifest"),
26
- ("file", "0016_add_domain"),
27
- ]
28
-
29
10
  dependencies = [
30
11
  ("core", "0102_add_domain_relations"),
31
- ("core", "0091_systemid"),
32
12
  ]
33
13
 
34
14
  operations = [
@@ -8,7 +8,7 @@ import pulpcore.app.util
8
8
  class Migration(migrations.Migration):
9
9
 
10
10
  dependencies = [
11
- ('file', '0016_add_domain'),
11
+ ('file', '0001_initial_squashed_0016_add_domain'),
12
12
  ]
13
13
 
14
14
  operations = [
pulpcore/app/apps.py CHANGED
@@ -5,6 +5,7 @@ from gettext import gettext as _
5
5
  from importlib import import_module
6
6
 
7
7
  from django import apps
8
+ from django.conf import settings
8
9
  from django.core.exceptions import ImproperlyConfigured
9
10
  from django.db import connection, transaction
10
11
  from django.db.models.signals import post_migrate
@@ -67,15 +68,6 @@ class PulpPluginAppConfig(apps.AppConfig):
67
68
 
68
69
  def __init__(self, app_name, app_module):
69
70
  super().__init__(app_name, app_module)
70
- # begin Compatilibity layer for DEFAULT_FILE_STORAGE deprecation
71
- # Remove on pulpcore=3.85 or pulpcore=4.0
72
- # * Workaround for getting the up-to-date settings instance, otherwise is doesnt
73
- # get the patch in settings.py
74
- # * Update code in signal handlers to use module-level imports again
75
- from django.conf import settings
76
-
77
- self.settings = settings
78
- # end
79
71
 
80
72
  try:
81
73
  self.version
@@ -247,7 +239,7 @@ class PulpAppConfig(PulpPluginAppConfig):
247
239
  label = "core"
248
240
 
249
241
  # The version of this app
250
- version = "3.83.2"
242
+ version = "3.85.0"
251
243
 
252
244
  # The python package name providing this app
253
245
  python_package_name = "pulpcore"
@@ -322,7 +314,6 @@ def _populate_system_id(sender, apps, verbosity, **kwargs):
322
314
 
323
315
 
324
316
  def _ensure_default_domain(sender, **kwargs):
325
- settings = sender.settings
326
317
  table_names = connection.introspection.table_names()
327
318
  if "core_domain" in table_names:
328
319
  from pulpcore.app.util import get_default_domain
@@ -402,7 +393,6 @@ def adjust_roles(apps, role_prefix, desired_roles, verbosity=1):
402
393
 
403
394
 
404
395
  def _populate_artifact_serving_distribution(sender, apps, verbosity, **kwargs):
405
- settings = sender.settings
406
396
  if (
407
397
  settings.STORAGES["default"]["BACKEND"] == "pulpcore.app.models.storage.FileSystem"
408
398
  or not settings.REDIRECT_TO_OBJECT_STORAGE
@@ -7,7 +7,8 @@ import click
7
7
  import django
8
8
  from django.conf import settings
9
9
  from django.db import connection
10
- from django.db.utils import InterfaceError, DatabaseError
10
+ from django.db.utils import IntegrityError, InterfaceError, DatabaseError
11
+ from gunicorn.arbiter import Arbiter
11
12
  from gunicorn.workers.sync import SyncWorker
12
13
 
13
14
  from pulpcore.app.apps import pulp_plugin_configs
@@ -27,26 +28,21 @@ class PulpApiWorker(SyncWorker):
27
28
 
28
29
  def heartbeat(self):
29
30
  try:
30
- self.api_app_status, created = self.ApiAppStatus.objects.get_or_create(
31
- name=self.name, defaults={"versions": self.versions}
32
- )
33
-
34
- if not created:
35
- self.api_app_status.save_heartbeat()
36
-
37
- if self.api_app_status.versions != self.versions:
38
- self.api_app_status.versions = self.versions
39
- self.api_app_status.save(update_fields=["versions"])
40
-
31
+ self.app_status.save_heartbeat()
41
32
  logger.debug(self.beat_msg)
42
33
  except (InterfaceError, DatabaseError):
43
34
  connection.close_if_unusable_or_obsolete()
44
- logger.info(self.fail_beat_msg)
35
+ try:
36
+ self.app_status.save_heartbeat()
37
+ logger.debug(self.beat_msg)
38
+ except (InterfaceError, DatabaseError):
39
+ logger.error(self.fail_beat_msg)
40
+ exit(Arbiter.WORKER_BOOT_ERROR)
45
41
 
46
42
  def init_process(self):
47
43
  os.environ.setdefault("DJANGO_SETTINGS_MODULE", "pulpcore.app.settings")
48
44
  django.setup()
49
- from pulpcore.app.models import ApiAppStatus
45
+ from pulpcore.app.models import AppStatus
50
46
 
51
47
  if settings.API_APP_TTL < 2 * self.timeout:
52
48
  logger.warning(
@@ -56,9 +52,6 @@ class PulpApiWorker(SyncWorker):
56
52
  self.timeout,
57
53
  )
58
54
 
59
- self.ApiAppStatus = ApiAppStatus
60
- self.api_app_status = None
61
-
62
55
  self.name = "{pid}@{hostname}".format(pid=self.pid, hostname=socket.gethostname())
63
56
  self.versions = {app.label: app.version for app in pulp_plugin_configs()}
64
57
  self.beat_msg = (
@@ -67,9 +60,16 @@ class PulpApiWorker(SyncWorker):
67
60
  )
68
61
  )
69
62
  self.fail_beat_msg = (
70
- "Api App '{name}' failed to write a heartbeat to the database, sleeping for "
71
- "'{interarrival}' seconds."
72
- ).format(name=self.name, interarrival=self.timeout)
63
+ "Api App '{name}' failed to write a heartbeat to the database."
64
+ ).format(name=self.name)
65
+ try:
66
+ self.app_status = AppStatus.objects.create(
67
+ name=self.name, app_type="api", versions=self.versions
68
+ )
69
+ except IntegrityError:
70
+ logger.error(f"An API app with name {self.name} already exists in the database.")
71
+ exit(Arbiter.WORKER_BOOT_ERROR)
72
+
73
73
  super().init_process()
74
74
 
75
75
  def run(self):
@@ -77,8 +77,8 @@ class PulpApiWorker(SyncWorker):
77
77
  super().run()
78
78
  finally:
79
79
  # cleanup
80
- if self.api_app_status:
81
- self.api_app_status.delete()
80
+ if self.app_status:
81
+ self.app_status.delete()
82
82
 
83
83
 
84
84
  class PulpcoreApiApplication(PulpcoreGunicornApplication):
@@ -20,105 +20,10 @@ import uuid
20
20
 
21
21
  class Migration(migrations.Migration):
22
22
 
23
- replaces = [
24
- ("core", "0001_initial"),
25
- ("core", "0002_increase_artifact_size_field"),
26
- ("core", "0003_remove_upload_completed"),
27
- ("core", "0004_add_duplicated_reserved_resources"),
28
- ("core", "0005_progressreport_code"),
29
- ("core", "0006_repository_plugin_managed"),
30
- ("core", "0007_delete_progress_proxies"),
31
- ("core", "0008_published_metadata_as_content"),
32
- ("core", "0009_remove_task_non_fatal_errors"),
33
- ("core", "0010_pulp_fields"),
34
- ("core", "0011_relative_path"),
35
- ("core", "0012_auto_20191104_2000"),
36
- ("core", "0013_repository_pulp_type"),
37
- ("core", "0014_remove_repository_plugin_managed"),
38
- ("core", "0015_auto_20191112_1426"),
39
- ("core", "0016_charfield_to_textfield"),
40
- ("core", "0017_remove_task_parent"),
41
- ("core", "0018_auto_20191127_2350"),
42
- ("core", "0019_add_signing_service_model"),
43
- ("core", "0020_change_publishedartifact_constraints"),
44
- ("core", "0021_add_signing_service_foreign_key"),
45
- ("core", "0022_rename_last_version"),
46
- ("core", "0023_change_exporter_models"),
47
- ("core", "0024_use_local_storage_for_uploads"),
48
- ("core", "0025_task_parent_task"),
49
- ("core", "0026_task_group"),
50
- ("core", "0027_export_backend"),
51
- ("core", "0028_import_importer_pulpimporter_pulpimporterrepository"),
52
- ("core", "0029_export_delete"),
53
- ("core", "0030_taskgroup_all_tasks_dispatched"),
54
- ("core", "0031_import_export_validate_params"),
55
- ("core", "0032_export_to_chunks"),
56
- ("core", "0033_increase_remote_artifact_size_field"),
57
- ("core", "0034_groupprogressreport"),
58
- ("core", "0035_content_upstream_id"),
59
- ("core", "0036_unprotect_last_export"),
60
- ("core", "0037_pulptemporaryfile"),
61
- ("core", "0038_repository_remote"),
62
- ("core", "0039_change_download_concurrency"),
63
- ("core", "0040_set_admin_is_staff"),
64
- ("core", "0041_accesspolicy"),
65
- ("core", "0042_rbac_for_tasks"),
66
- ("core", "0043_toc_attribute"),
67
- ("core", "0044_temp_file_artifact_field"),
68
- ("core", "0045_accesspolicy_permissions_allow_null"),
69
- ("core", "0046_task__resource_job_id"),
70
- ("core", "0047_improve_orphan_cleanup"),
71
- ("core", "0048_fips_checksums"),
72
- ("core", "0049_add_file_field_to_uploadchunk"),
73
- ("core", "0050_namespace_access_policies"),
74
- ("core", "0051_timeoutfields"),
75
- ("core", "0052_tasking_logging_cid"),
76
- ("core", "0053_remote_headers"),
77
- ("core", "0054_add_public_key"),
78
- ("core", "0055_label"),
79
- ("core", "0056_remote_rate_limit"),
80
- ("core", "0057_add_label_indexes"),
81
- ("core", "0058_accesspolicy_customized"),
82
- ("core", "0059_proxy_creds"),
83
- ("core", "0060_data_migration_proxy_creds"),
84
- ("core", "0061_call_handle_artifact_checksums_command"),
85
- ("core", "0062_add_new_distribution_mastermodel"),
86
- ("core", "0063_repository_retained_versions"),
87
- ("core", "0064_repository_user_hidden"),
88
- ("core", "0064_add_new_style_task_columns"),
89
- ("core", "0065_merge_20210615_1211"),
90
- ("core", "0066_download_concurrency_and_retry_changes"),
91
- ("core", "0067_add_protect_to_task_reservation"),
92
- ("core", "0068_add_timestamp_of_interest"),
93
- ("core", "0069_update_json_fields"),
94
- ("core", "0070_rename_retained_versions"),
95
- ("core", "0071_filesystemexport_filesystemexporter"),
96
- ("core", "0072_add_method_to_filesystem_exporter"),
97
- ("core", "0073_encrypt_remote_fields"),
98
- ("core", "0074_acs"),
99
- ("core", "0075_rbaccontentguard"),
100
- ("core", "0076_remove_reserved_resource"),
101
- ("core", "0077_move_remote_url_credentials"),
102
- ("core", "0078_grouprole_role_userrole"),
103
- ("core", "0079_rename_permissions_assignment_accesspolicy_creation_hooks"),
104
- ("core", "0080_proxy_group_model"),
105
- ("core", "0081_reapplabel_group_permissions"),
106
- ("core", "0082_add_manage_roles_permissions"),
107
- ("core", "0083_alter_group_options"),
108
- ("core", "0084_alter_rbaccontentguard_options"),
109
- ("core", "0085_contentredirectcontentguard"),
110
- ("core", "0086_task_json_fields"),
111
- ("core", "0087_taskschedule"),
112
- ("core", "0088_accesspolicy_queryset_scoping"),
113
- ("core", "0089_alter_contentredirectcontentguard_options"),
114
- ("core", "0090_char_to_text_field"),
115
- ]
116
-
117
23
  initial = True
118
24
 
119
25
  dependencies = [
120
26
  ("auth", "0012_alter_user_first_name_max_length"),
121
- ("auth", "__first__"),
122
27
  ("contenttypes", "0002_remove_content_type_name"),
123
28
  ]
124
29
 
@@ -8,7 +8,7 @@ import uuid
8
8
  class Migration(migrations.Migration):
9
9
 
10
10
  dependencies = [
11
- ('core', '0090_char_to_text_field'),
11
+ ('core', '0001_squashed_0090_char_to_text_field'),
12
12
  ]
13
13
 
14
14
  operations = [
@@ -0,0 +1,81 @@
1
+ # Generated by Django 4.2.22 on 2025-06-06 14:32
2
+
3
+ from django.db import migrations
4
+ from pulpcore.migrations import RequireVersion
5
+
6
+ # This migration is supposed to be zero downtime upgrade safe.
7
+
8
+
9
+ TASK_INSERT_TRIGGER_UP = """
10
+ CREATE FUNCTION on_insert_timestamp_task()
11
+ RETURNS TRIGGER
12
+ LANGUAGE plpgsql
13
+ AS $$
14
+ DECLARE
15
+ res_list text[];
16
+ res_list_shared text[];
17
+ BEGIN
18
+ res_list := array_agg(q.res) FROM (SELECT regexp_replace(unnest(NEW.reserved_resources_record), '^shared:', '') res) AS q;
19
+ res_list_shared := array_agg(q.res) FROM (SELECT 'shared:' || unnest(res_list) AS res) AS q;
20
+ PERFORM pg_advisory_xact_lock(4711, q.id) FROM (SELECT hashtext(res) AS id FROM unnest(res_list) AS res ORDER BY id) AS q;
21
+ NEW.pulp_created = clock_timestamp();
22
+ IF NEW.pulp_created <= (
23
+ SELECT max(pulp_created) FROM core_task
24
+ WHERE
25
+ state NOT IN ('completed', 'failed', 'canceled', 'skipped')
26
+ AND
27
+ reserved_resources_record && (res_list || res_list_shared)
28
+ )
29
+ THEN
30
+ RAISE EXCEPTION 'Clock screw detected.';
31
+ END IF;
32
+ RETURN NEW;
33
+ END;
34
+ $$
35
+ ;
36
+
37
+ CREATE FUNCTION on_update_timestamp_task()
38
+ RETURNS TRIGGER
39
+ LANGUAGE plpgsql
40
+ AS $$
41
+ BEGIN
42
+ -- This is mainly a safeguard to prevent old code from messing with the timestamp, now that the database is in charge.
43
+ RAISE EXCEPTION 'Updating pulp_created is not allowed.';
44
+ END;
45
+ $$
46
+ ;
47
+
48
+ CREATE TRIGGER on_insert_timestamp_task_trigger
49
+ BEFORE INSERT
50
+ ON core_task
51
+ FOR EACH ROW
52
+ EXECUTE FUNCTION on_insert_timestamp_task()
53
+ ;
54
+
55
+ CREATE TRIGGER on_update_timestamp_task_trigger
56
+ BEFORE UPDATE
57
+ ON core_task
58
+ FOR EACH ROW
59
+ WHEN (OLD.pulp_created <> NEW.pulp_created)
60
+ EXECUTE FUNCTION on_update_timestamp_task()
61
+ ;
62
+ """
63
+
64
+ TASK_INSERT_TRIGGER_DOWN = """
65
+ DROP TRIGGER on_update_timestamp_task_trigger on core_task;
66
+ DROP TRIGGER on_insert_timestamp_task_trigger on core_task;
67
+ DROP FUNCTION on_update_timestamp_task;
68
+ DROP FUNCTION on_insert_timestamp_task;
69
+ """
70
+
71
+
72
+ class Migration(migrations.Migration):
73
+
74
+ dependencies = [
75
+ ("core", "0133_repositoryversion_content_ids"),
76
+ ]
77
+
78
+ operations = [
79
+ RequireVersion("core", "3.82.1"),
80
+ migrations.RunSQL(sql=TASK_INSERT_TRIGGER_UP, reverse_sql=TASK_INSERT_TRIGGER_DOWN),
81
+ ]
@@ -0,0 +1,25 @@
1
+ # Generated by Django 4.2.22 on 2025-07-07 11:07
2
+
3
+ import django.contrib.postgres.indexes
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ("core", "0134_task_insert_trigger"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddIndex(
15
+ model_name="task",
16
+ index=django.contrib.postgres.indexes.GinIndex(
17
+ condition=models.Q(
18
+ ("state__in", ["completed", "failed", "canceled", "skipped"]), _negated=True
19
+ ),
20
+ fields=["reserved_resources_record"],
21
+ name="pulp_task_resources_index",
22
+ opclasses=["array_ops"],
23
+ ),
24
+ ),
25
+ ]
@@ -1,4 +1,4 @@
1
- # Generated by Django 2.2.6 on 2020-03-13 16:18
1
+ # Generated by Django 4.2.16 on 2024-12-05 12:37
2
2
 
3
3
  from django.db import migrations
4
4
 
@@ -6,11 +6,11 @@ from django.db import migrations
6
6
  class Migration(migrations.Migration):
7
7
 
8
8
  dependencies = [
9
- ('file', '0005_filerepository'),
9
+ ("core", "0135_task_pulp_task_resources_index"),
10
10
  ]
11
11
 
12
12
  operations = [
13
13
  migrations.DeleteModel(
14
- name='FileFileSystemExporter',
14
+ name="BaseDistribution",
15
15
  ),
16
16
  ]
@@ -0,0 +1,33 @@
1
+ # Generated by Django 4.2.23 on 2025-08-07 08:46
2
+
3
+ import django.contrib.postgres.fields.hstore
4
+ from django.db import migrations, models
5
+ import django_lifecycle.mixins
6
+ import pulpcore.app.models.base
7
+
8
+
9
+ class Migration(migrations.Migration):
10
+
11
+ dependencies = [
12
+ ('core', '0136_delete_basedistribution'),
13
+ ]
14
+
15
+ operations = [
16
+ migrations.CreateModel(
17
+ name='AppStatus',
18
+ fields=[
19
+ ('pulp_id', models.UUIDField(default=pulpcore.app.models.base.pulp_uuid, editable=False, primary_key=True, serialize=False)),
20
+ ('pulp_created', models.DateTimeField(auto_now_add=True)),
21
+ ('pulp_last_updated', models.DateTimeField(auto_now=True, null=True)),
22
+ ('app_type', models.CharField(choices=[('api', 'api'), ('content', 'content'), ('worker', 'worker')], max_length=10)),
23
+ ('name', models.TextField(db_index=True, unique=True)),
24
+ ('versions', django.contrib.postgres.fields.hstore.HStoreField(default=dict)),
25
+ ('ttl', models.DurationField()),
26
+ ('last_heartbeat', models.DateTimeField(auto_now=True)),
27
+ ],
28
+ options={
29
+ 'abstract': False,
30
+ },
31
+ bases=(django_lifecycle.mixins.LifecycleModelMixin, models.Model),
32
+ ),
33
+ ]