qontract-reconcile 0.10.1rc1185__py3-none-any.whl → 0.10.1rc1187__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.
- {qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/RECORD +9 -9
- tools/cli_commands/container_images_report.py +35 -12
- tools/cli_commands/erv2.py +42 -4
- tools/qontract_cli.py +35 -1
- tools/test/test_get_container_images.py +57 -24
- {qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc1187
|
4
4
|
Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
|
5
5
|
Home-page: https://github.com/app-sre/qontract-reconcile
|
6
6
|
Author: Red Hat App-SRE Team
|
{qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/RECORD
RENAMED
@@ -838,12 +838,12 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
|
|
838
838
|
tools/app_interface_reporter.py,sha256=oZPib4HPq0aZ2Zui1QGJGk6qQdfpeihujGDBnSdKyGE,17627
|
839
839
|
tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
|
840
840
|
tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
|
841
|
-
tools/qontract_cli.py,sha256=
|
841
|
+
tools/qontract_cli.py,sha256=EsrEPKyeVBeViz0x21hfA25vkaHVEKBxHRyhozhdXVY,143933
|
842
842
|
tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
|
843
843
|
tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
|
844
844
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
845
|
-
tools/cli_commands/container_images_report.py,sha256=
|
846
|
-
tools/cli_commands/erv2.py,sha256=
|
845
|
+
tools/cli_commands/container_images_report.py,sha256=64LXkv3eoWtMFZwBgkYE9jSnReDD7A9M0GEVXsR9aGk,5183
|
846
|
+
tools/cli_commands/erv2.py,sha256=f_Zc5ZCPenXbFj8zv_kcKJgmGkErYHGNIhbzOfOaD4Q,22041
|
847
847
|
tools/cli_commands/gpg_encrypt.py,sha256=x02JOMn834z89YSNvr5B-oJky7rR1C0begCkPh45eHk,4958
|
848
848
|
tools/cli_commands/systems_and_tools.py,sha256=EMHOF1AtUDaoSk0bbjl6oUKYAz4rTZjIBaF-6E6GspM,16816
|
849
849
|
tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -877,13 +877,13 @@ tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
877
877
|
tools/test/conftest.py,sha256=CsDbu4otrxb7X7kXKKGyV3ZEzu3pCkgjCoCGiHNx6zc,2401
|
878
878
|
tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvftCWEEf-g1mfXOtgCog-g,1271
|
879
879
|
tools/test/test_erv2.py,sha256=EAS7QuJkHisRVO9bMGxm662L5B6i66wF_mT9PAjVzrU,3128
|
880
|
-
tools/test/test_get_container_images.py,sha256=
|
880
|
+
tools/test/test_get_container_images.py,sha256=QUSb0PjZnL35_dhniMux1_D2G_2vvpcasr8tpwPLwIQ,7125
|
881
881
|
tools/test/test_qontract_cli.py,sha256=iuzKbQ6ahinvjoQmQLBrG4shey0z-1rB6qCgS8T6dgU,5789
|
882
882
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
883
883
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
884
884
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
885
|
-
qontract_reconcile-0.10.
|
886
|
-
qontract_reconcile-0.10.
|
887
|
-
qontract_reconcile-0.10.
|
888
|
-
qontract_reconcile-0.10.
|
889
|
-
qontract_reconcile-0.10.
|
885
|
+
qontract_reconcile-0.10.1rc1187.dist-info/METADATA,sha256=ELcUNPGIHkuAtwY7r9iKGDrX38eFrABpKVzusb48UB8,2213
|
886
|
+
qontract_reconcile-0.10.1rc1187.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
887
|
+
qontract_reconcile-0.10.1rc1187.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
888
|
+
qontract_reconcile-0.10.1rc1187.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
889
|
+
qontract_reconcile-0.10.1rc1187.dist-info/RECORD,,
|
@@ -20,7 +20,8 @@ IMAGE_NAME_REGEX = re.compile(r"^(?P<name>[a-zA-Z0-9][a-zA-Z0-9/_.-]+)(?:@sha256
|
|
20
20
|
|
21
21
|
class NamespaceImages(BaseModel):
|
22
22
|
namespace_name: str
|
23
|
-
image_names: list[str]
|
23
|
+
image_names: list[str] | None = None
|
24
|
+
error_message: str | None = None
|
24
25
|
|
25
26
|
|
26
27
|
def get_all_pods_images(
|
@@ -77,9 +78,14 @@ def fetch_pods_images_from_namespaces(
|
|
77
78
|
oc_map=oc_map,
|
78
79
|
)
|
79
80
|
|
81
|
+
errors: defaultdict = defaultdict(int)
|
80
82
|
result: defaultdict = defaultdict(_get_all_images_default)
|
81
83
|
for ni in all_namespace_images:
|
82
|
-
|
84
|
+
if ni.error_message:
|
85
|
+
errors[f"{ni.namespace_name}/{ni.error_message}"] += 1
|
86
|
+
continue
|
87
|
+
|
88
|
+
for name in ni.image_names or []:
|
83
89
|
result[name]["namespaces"].add(ni.namespace_name)
|
84
90
|
result[name]["count"] += 1
|
85
91
|
|
@@ -92,7 +98,7 @@ def fetch_pods_images_from_namespaces(
|
|
92
98
|
include_pattern_compiled = re.compile(include_pattern)
|
93
99
|
|
94
100
|
result_filtered_flattened: list[dict[str, Any]] = []
|
95
|
-
for name, value in result.items():
|
101
|
+
for name, value in sorted(result.items()):
|
96
102
|
if include_pattern_compiled and not include_pattern_compiled.match(name):
|
97
103
|
continue
|
98
104
|
if exclude_pattern_compiled and exclude_pattern_compiled.match(name):
|
@@ -104,6 +110,16 @@ def fetch_pods_images_from_namespaces(
|
|
104
110
|
"count": value["count"],
|
105
111
|
})
|
106
112
|
|
113
|
+
# append errors if they exist in the filtered result
|
114
|
+
# not very canonical, but it is better than ignoring them
|
115
|
+
if errors:
|
116
|
+
for message, count in sorted(errors.items()):
|
117
|
+
result_filtered_flattened.append({
|
118
|
+
"name": "error",
|
119
|
+
"namespaces": message,
|
120
|
+
"count": count,
|
121
|
+
})
|
122
|
+
|
107
123
|
return result_filtered_flattened
|
108
124
|
|
109
125
|
|
@@ -113,15 +129,22 @@ def _get_all_images_default() -> dict[str, Any]:
|
|
113
129
|
|
114
130
|
def _get_namespace_images(ns: NamespaceV1, oc_map: OCMap) -> NamespaceImages:
|
115
131
|
image_names = []
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
132
|
+
|
133
|
+
try:
|
134
|
+
oc = oc_map.get_cluster(ns.cluster.name)
|
135
|
+
pod_items = oc.get_items("Pod", namespace=ns.name)
|
136
|
+
for pod in pod_items:
|
137
|
+
containers = pod.get("spec", {}).get("containers", [])
|
138
|
+
containers.extend(pod.get("spec", {}).get("initContainers", []))
|
139
|
+
|
140
|
+
for c in containers:
|
141
|
+
if m := IMAGE_NAME_REGEX.match(c["image"]):
|
142
|
+
image_names.append(m.group("name"))
|
143
|
+
except Exception as exc:
|
144
|
+
return NamespaceImages(
|
145
|
+
namespace_name=ns.name,
|
146
|
+
error_message=str(exc),
|
147
|
+
)
|
125
148
|
|
126
149
|
return NamespaceImages(
|
127
150
|
namespace_name=ns.name,
|
tools/cli_commands/erv2.py
CHANGED
@@ -215,8 +215,44 @@ class Erv2Cli:
|
|
215
215
|
capture_output=True,
|
216
216
|
)
|
217
217
|
except CalledProcessError as e:
|
218
|
-
|
219
|
-
|
218
|
+
if e.stderr:
|
219
|
+
print(e.stderr.decode("utf-8"))
|
220
|
+
if e.stdout:
|
221
|
+
print(e.stdout.decode("utf-8"))
|
222
|
+
raise
|
223
|
+
|
224
|
+
def enter_shell(self, credentials: Path) -> None:
|
225
|
+
"""Run the CDKTF container and enter the shell."""
|
226
|
+
input_file = self.temp / "input.json"
|
227
|
+
input_file.write_text(self.input_data)
|
228
|
+
|
229
|
+
try:
|
230
|
+
run(["docker", "pull", self.image], check=True, capture_output=True)
|
231
|
+
run(
|
232
|
+
[
|
233
|
+
"docker",
|
234
|
+
"run",
|
235
|
+
"--name",
|
236
|
+
"erv2-debug-shell",
|
237
|
+
"--rm",
|
238
|
+
"-it",
|
239
|
+
"--mount",
|
240
|
+
f"type=bind,source={input_file!s},target=/inputs/input.json",
|
241
|
+
"--mount",
|
242
|
+
f"type=bind,source={credentials!s},target=/credentials",
|
243
|
+
"-e",
|
244
|
+
"AWS_SHARED_CREDENTIALS_FILE=/credentials",
|
245
|
+
"--entrypoint",
|
246
|
+
"/bin/bash",
|
247
|
+
self.image,
|
248
|
+
],
|
249
|
+
check=True,
|
250
|
+
)
|
251
|
+
except CalledProcessError as e:
|
252
|
+
if e.stderr:
|
253
|
+
print(e.stderr.decode("utf-8"))
|
254
|
+
if e.stdout:
|
255
|
+
print(e.stdout.decode("utf-8"))
|
220
256
|
raise
|
221
257
|
|
222
258
|
|
@@ -236,8 +272,10 @@ def tf_run(path: Path, cmd: list[str]) -> str:
|
|
236
272
|
env=env,
|
237
273
|
).stdout.decode("utf-8")
|
238
274
|
except CalledProcessError as e:
|
239
|
-
|
240
|
-
|
275
|
+
if e.stderr:
|
276
|
+
print(e.stderr.decode("utf-8"))
|
277
|
+
if e.stdout:
|
278
|
+
print(e.stdout.decode("utf-8"))
|
241
279
|
raise
|
242
280
|
|
243
281
|
|
tools/qontract_cli.py
CHANGED
@@ -7,6 +7,7 @@ import logging
|
|
7
7
|
import os
|
8
8
|
import re
|
9
9
|
import sys
|
10
|
+
import tempfile
|
10
11
|
import textwrap
|
11
12
|
from collections import defaultdict
|
12
13
|
from datetime import (
|
@@ -4203,7 +4204,7 @@ def request_reconciliation(ctx):
|
|
4203
4204
|
|
4204
4205
|
|
4205
4206
|
@external_resources.command()
|
4206
|
-
@binary(["terraform"])
|
4207
|
+
@binary(["terraform", "docker"])
|
4207
4208
|
@binary_version("terraform", ["version"], TERRAFORM_VERSION_REGEX, TERRAFORM_VERSION)
|
4208
4209
|
@click.option(
|
4209
4210
|
"--dry-run/--no-dry-run",
|
@@ -4320,6 +4321,39 @@ def migrate(ctx, dry_run: bool, skip_build: bool) -> None:
|
|
4320
4321
|
rich_print(f"[b red]Please remove the temporary directory ({tempdir}) manually!")
|
4321
4322
|
|
4322
4323
|
|
4324
|
+
@external_resources.command()
|
4325
|
+
@binary(["docker"])
|
4326
|
+
@click.pass_context
|
4327
|
+
def debug_shell(ctx) -> None:
|
4328
|
+
"""Enter an ERv2 debug shell to manually migrate resources."""
|
4329
|
+
# use a temporary directory in $HOME. The MacOS colima default configuration allows docker mounts from $HOME.
|
4330
|
+
with tempfile.TemporaryDirectory(dir=Path.home(), prefix="erv2-debug.") as _tempdir:
|
4331
|
+
tempdir = Path(_tempdir)
|
4332
|
+
with progress_spinner() as progress:
|
4333
|
+
with task(progress, "Preparing environment ..."):
|
4334
|
+
credentials_file = tempdir / "credentials"
|
4335
|
+
credentials_file.write_text(
|
4336
|
+
ctx.obj["secret_reader"].read_with_parameters(
|
4337
|
+
path=f"app-sre/external-resources/{ctx.obj['provisioner']}",
|
4338
|
+
field="credentials",
|
4339
|
+
format=None,
|
4340
|
+
version=None,
|
4341
|
+
)
|
4342
|
+
)
|
4343
|
+
os.environ["AWS_SHARED_CREDENTIALS_FILE"] = str(credentials_file)
|
4344
|
+
|
4345
|
+
erv2cli = Erv2Cli(
|
4346
|
+
provision_provider=ctx.obj["provision_provider"],
|
4347
|
+
provisioner=ctx.obj["provisioner"],
|
4348
|
+
provider=ctx.obj["provider"],
|
4349
|
+
identifier=ctx.obj["identifier"],
|
4350
|
+
secret_reader=ctx.obj["secret_reader"],
|
4351
|
+
temp_dir=tempdir,
|
4352
|
+
progress_spinner=progress,
|
4353
|
+
)
|
4354
|
+
erv2cli.enter_shell(credentials_file)
|
4355
|
+
|
4356
|
+
|
4323
4357
|
@get.command(help="Get all container images in app-interface defined namespaces")
|
4324
4358
|
@cluster_name
|
4325
4359
|
@namespace_name
|
@@ -99,27 +99,17 @@ def oc_map(mocker: MockerFixture, oc: OCNative) -> OCMap:
|
|
99
99
|
return oc_map
|
100
100
|
|
101
101
|
|
102
|
-
|
103
|
-
def _to_set(list_of_dicts: list[dict]) -> set[tuple]:
|
104
|
-
return {tuple(d.items()) for d in list_of_dicts}
|
105
|
-
|
106
|
-
|
107
|
-
def testfetch_no_filter(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
102
|
+
def test_fetch_no_filter(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
108
103
|
images = fetch_pods_images_from_namespaces(
|
109
104
|
namespaces=namespaces,
|
110
105
|
oc_map=oc_map,
|
111
106
|
thread_pool_size=2,
|
112
107
|
)
|
113
108
|
|
114
|
-
assert
|
115
|
-
{
|
116
|
-
"name": "quay.io/prometheus/blackbox-exporter",
|
117
|
-
"namespaces": "app-sre-observability-stage",
|
118
|
-
"count": 1,
|
119
|
-
},
|
109
|
+
assert images == [
|
120
110
|
{
|
121
|
-
"name": "quay.io/
|
122
|
-
"namespaces": "app-sre-
|
111
|
+
"name": "quay.io/app-sre/clamav",
|
112
|
+
"namespaces": "app-sre-pipelines",
|
123
113
|
"count": 1,
|
124
114
|
},
|
125
115
|
{
|
@@ -128,8 +118,8 @@ def testfetch_no_filter(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
|
128
118
|
"count": 3,
|
129
119
|
},
|
130
120
|
{
|
131
|
-
"name": "quay.io/
|
132
|
-
"namespaces": "app-sre-
|
121
|
+
"name": "quay.io/prometheus/blackbox-exporter",
|
122
|
+
"namespaces": "app-sre-observability-stage",
|
133
123
|
"count": 1,
|
134
124
|
},
|
135
125
|
{
|
@@ -138,26 +128,31 @@ def testfetch_no_filter(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
|
138
128
|
"count": 1,
|
139
129
|
},
|
140
130
|
{
|
141
|
-
"name": "
|
142
|
-
"namespaces": "app-sre-
|
143
|
-
"count":
|
131
|
+
"name": "quay.io/redhat-services-prod/app-sre-tenant/gitlab-project-exporter-main/gitlab-project-exporter-main",
|
132
|
+
"namespaces": "app-sre-observability-stage",
|
133
|
+
"count": 1,
|
144
134
|
},
|
145
135
|
{
|
146
136
|
"name": "quay.io/redhatproductsecurity/rapidast",
|
147
137
|
"namespaces": "app-sre-pipelines",
|
148
138
|
"count": 1,
|
149
139
|
},
|
150
|
-
|
140
|
+
{
|
141
|
+
"name": "registry.redhat.io/openshift-pipelines/pipelines-entrypoint-rhel8",
|
142
|
+
"namespaces": "app-sre-pipelines",
|
143
|
+
"count": 3,
|
144
|
+
},
|
145
|
+
]
|
151
146
|
|
152
147
|
|
153
|
-
def
|
148
|
+
def test_fetch_exclude_pattern(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
154
149
|
images = fetch_pods_images_from_namespaces(
|
155
150
|
namespaces=namespaces,
|
156
151
|
oc_map=oc_map,
|
157
152
|
thread_pool_size=2,
|
158
153
|
exclude_pattern="quay.io/redhat|quay.io/app-sre",
|
159
154
|
)
|
160
|
-
assert
|
155
|
+
assert images == [
|
161
156
|
{
|
162
157
|
"name": "quay.io/prometheus/blackbox-exporter",
|
163
158
|
"namespaces": "app-sre-observability-stage",
|
@@ -168,10 +163,10 @@ def testfetch_exclude_pattern(namespaces: list[NamespaceV1], oc_map: OCMap) -> N
|
|
168
163
|
"namespaces": "app-sre-pipelines",
|
169
164
|
"count": 3,
|
170
165
|
},
|
171
|
-
]
|
166
|
+
]
|
172
167
|
|
173
168
|
|
174
|
-
def
|
169
|
+
def test_fetch_include_pattern(namespaces: list[NamespaceV1], oc_map: OCMap) -> None:
|
175
170
|
images = fetch_pods_images_from_namespaces(
|
176
171
|
namespaces=namespaces,
|
177
172
|
oc_map=oc_map,
|
@@ -185,3 +180,41 @@ def testfetch_include_pattern(namespaces: list[NamespaceV1], oc_map: OCMap) -> N
|
|
185
180
|
"count": 3,
|
186
181
|
},
|
187
182
|
]
|
183
|
+
|
184
|
+
|
185
|
+
def test_fetch_exception(
|
186
|
+
namespaces: list[NamespaceV1], mocker: MockerFixture, observability_pods: list[dict]
|
187
|
+
) -> None:
|
188
|
+
oc = mocker.patch("reconcile.utils.oc.OCNative", autospec=True)
|
189
|
+
oc.get_items.side_effect = [observability_pods, Exception("generic error")]
|
190
|
+
oc_map = mocker.patch("reconcile.utils.oc_map.OCMap", autospec=True)
|
191
|
+
oc_map.get_cluster.return_value = oc
|
192
|
+
|
193
|
+
images = fetch_pods_images_from_namespaces(
|
194
|
+
namespaces=namespaces,
|
195
|
+
oc_map=oc_map,
|
196
|
+
thread_pool_size=2,
|
197
|
+
)
|
198
|
+
|
199
|
+
assert images == [
|
200
|
+
{
|
201
|
+
"name": "quay.io/app-sre/internal-redhat-ca",
|
202
|
+
"namespaces": "app-sre-observability-stage",
|
203
|
+
"count": 2,
|
204
|
+
},
|
205
|
+
{
|
206
|
+
"name": "quay.io/prometheus/blackbox-exporter",
|
207
|
+
"namespaces": "app-sre-observability-stage",
|
208
|
+
"count": 1,
|
209
|
+
},
|
210
|
+
{
|
211
|
+
"name": "quay.io/redhat-services-prod/app-sre-tenant/gitlab-project-exporter-main/gitlab-project-exporter-main",
|
212
|
+
"namespaces": "app-sre-observability-stage",
|
213
|
+
"count": 1,
|
214
|
+
},
|
215
|
+
{
|
216
|
+
"name": "error",
|
217
|
+
"namespaces": "app-sre-pipelines/generic error",
|
218
|
+
"count": 1,
|
219
|
+
},
|
220
|
+
]
|
{qontract_reconcile-0.10.1rc1185.dist-info → qontract_reconcile-0.10.1rc1187.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|
File without changes
|