qontract-reconcile 0.10.2.dev256__py3-none-any.whl → 0.10.2.dev257__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.2.dev256.dist-info → qontract_reconcile-0.10.2.dev257.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev257.dist-info}/RECORD +94 -94
- reconcile/aus/advanced_upgrade_service.py +1 -1
- reconcile/aus/base.py +2 -2
- reconcile/aus/version_gates/sts_version_gate_handler.py +2 -2
- reconcile/aws_account_manager/reconciler.py +22 -20
- reconcile/aws_iam_keys.py +5 -5
- reconcile/aws_iam_password_reset.py +5 -5
- reconcile/aws_saml_roles/integration.py +5 -5
- reconcile/aws_version_sync/integration.py +4 -3
- reconcile/cli.py +5 -5
- reconcile/closedbox_endpoint_monitoring_base.py +1 -0
- reconcile/database_access_manager.py +4 -4
- reconcile/dynatrace_token_provider/integration.py +2 -2
- reconcile/external_resources/manager.py +2 -2
- reconcile/external_resources/model.py +1 -1
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/gabi_authorized_users.py +3 -3
- reconcile/github_org.py +2 -2
- reconcile/gitlab_housekeeping.py +1 -1
- reconcile/gitlab_mr_sqs_consumer.py +1 -1
- reconcile/glitchtip/integration.py +2 -2
- reconcile/jenkins_worker_fleets.py +5 -5
- reconcile/ldap_groups/integration.py +3 -3
- reconcile/ocm_clusters.py +2 -2
- reconcile/ocm_internal_notifications/integration.py +2 -2
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/openshift_base.py +12 -11
- reconcile/openshift_cluster_bots.py +2 -2
- reconcile/openshift_resources_base.py +3 -3
- reconcile/openshift_rhcs_certs.py +2 -2
- reconcile/openshift_saas_deploy.py +1 -1
- reconcile/quay_membership.py +4 -4
- reconcile/rhidp/common.py +3 -2
- reconcile/run_integration.py +7 -4
- reconcile/skupper_network/integration.py +3 -3
- reconcile/slack_usergroups.py +4 -4
- reconcile/status_board.py +3 -3
- reconcile/terraform_cloudflare_dns.py +5 -5
- reconcile/terraform_cloudflare_users.py +15 -17
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_vpc_peerings.py +9 -9
- reconcile/unleash_feature_toggles/integration.py +1 -1
- reconcile/utils/aggregated_list.py +2 -2
- reconcile/utils/aws_api_typed/iam.py +2 -2
- reconcile/utils/aws_api_typed/organization.py +4 -4
- reconcile/utils/aws_api_typed/service_quotas.py +4 -4
- reconcile/utils/aws_api_typed/support.py +9 -9
- reconcile/utils/aws_helper.py +1 -1
- reconcile/utils/config.py +8 -4
- reconcile/utils/deadmanssnitch_api.py +2 -4
- reconcile/utils/glitchtip/models.py +18 -12
- reconcile/utils/gql.py +4 -4
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/jinja2/utils.py +7 -3
- reconcile/utils/jjb_client.py +2 -2
- reconcile/utils/models.py +2 -1
- reconcile/utils/mr/__init__.py +3 -3
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/aws_access.py +5 -2
- reconcile/utils/mr/base.py +3 -3
- reconcile/utils/mr/user_maintenance.py +1 -1
- reconcile/utils/oc.py +11 -11
- reconcile/utils/oc_connection_parameters.py +4 -4
- reconcile/utils/ocm/base.py +3 -3
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/search_filters.py +2 -2
- reconcile/utils/openshift_resource.py +21 -18
- reconcile/utils/pagerduty_api.py +5 -5
- reconcile/utils/quay_api.py +2 -2
- reconcile/utils/rosa/rosa_cli.py +1 -1
- reconcile/utils/rosa/session.py +2 -2
- reconcile/utils/runtime/desired_state_diff.py +7 -7
- reconcile/utils/saasherder/interfaces.py +1 -0
- reconcile/utils/saasherder/models.py +1 -1
- reconcile/utils/saasherder/saasherder.py +1 -1
- reconcile/utils/secret_reader.py +20 -20
- reconcile/utils/slack_api.py +5 -5
- reconcile/utils/slo_document_manager.py +6 -6
- reconcile/utils/state.py +8 -8
- reconcile/utils/terraform_client.py +3 -3
- reconcile/utils/terrascript/cloudflare_client.py +2 -2
- reconcile/utils/terrascript/cloudflare_resources.py +1 -0
- reconcile/utils/terrascript_aws_client.py +12 -11
- reconcile/utils/vault.py +22 -22
- reconcile/vault_replication.py +15 -15
- tools/cli_commands/erv2.py +3 -2
- tools/cli_commands/gpg_encrypt.py +9 -9
- tools/cli_commands/systems_and_tools.py +1 -1
- tools/qontract_cli.py +13 -14
- tools/saas_promotion_state/saas_promotion_state.py +4 -4
- tools/template_validation.py +5 -5
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev257.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev257.dist-info}/entry_points.txt +0 -0
reconcile/utils/vault.py
CHANGED
@@ -18,27 +18,27 @@ LOG = logging.getLogger(__name__)
|
|
18
18
|
VAULT_AUTO_REFRESH_INTERVAL = int(os.getenv("VAULT_AUTO_REFRESH_INTERVAL") or 600)
|
19
19
|
|
20
20
|
|
21
|
-
class
|
21
|
+
class PathAccessForbiddenError(Exception):
|
22
22
|
pass
|
23
23
|
|
24
24
|
|
25
|
-
class
|
25
|
+
class SecretNotFoundError(Exception):
|
26
26
|
pass
|
27
27
|
|
28
28
|
|
29
|
-
class
|
29
|
+
class SecretAccessForbiddenError(Exception):
|
30
30
|
pass
|
31
31
|
|
32
32
|
|
33
|
-
class
|
33
|
+
class SecretVersionIsNoneError(Exception):
|
34
34
|
pass
|
35
35
|
|
36
36
|
|
37
|
-
class
|
37
|
+
class SecretVersionNotFoundError(Exception):
|
38
38
|
pass
|
39
39
|
|
40
40
|
|
41
|
-
class
|
41
|
+
class SecretFieldNotFoundError(Exception):
|
42
42
|
pass
|
43
43
|
|
44
44
|
|
@@ -189,7 +189,7 @@ class _VaultClient:
|
|
189
189
|
data = secret_data
|
190
190
|
|
191
191
|
if data is None:
|
192
|
-
raise
|
192
|
+
raise SecretNotFoundError
|
193
193
|
|
194
194
|
return data, version
|
195
195
|
|
@@ -223,7 +223,7 @@ class _VaultClient:
|
|
223
223
|
read_path = "/".join(path_split[1:])
|
224
224
|
if version is None:
|
225
225
|
msg = f"version can not be null for secret with path '{path}'."
|
226
|
-
raise
|
226
|
+
raise SecretVersionIsNoneError(msg)
|
227
227
|
if version == SECRET_VERSION_LATEST:
|
228
228
|
# https://github.com/hvac/hvac/blob/
|
229
229
|
# ec048ded30d21c13c21cfa950d148c8bfc1467b0/
|
@@ -237,12 +237,12 @@ class _VaultClient:
|
|
237
237
|
)
|
238
238
|
except InvalidPath:
|
239
239
|
msg = f"version '{version}' not found for secret with path '{path}'."
|
240
|
-
raise
|
240
|
+
raise SecretVersionNotFoundError(msg) from None
|
241
241
|
except hvac.exceptions.Forbidden:
|
242
242
|
msg = f"permission denied accessing secret '{path}'"
|
243
|
-
raise
|
243
|
+
raise SecretAccessForbiddenError(msg) from None
|
244
244
|
if secret is None or "data" not in secret or "data" not in secret["data"]:
|
245
|
-
raise
|
245
|
+
raise SecretNotFoundError(path)
|
246
246
|
|
247
247
|
data = secret["data"]["data"]
|
248
248
|
secret_version = secret["data"]["metadata"]["version"]
|
@@ -253,10 +253,10 @@ class _VaultClient:
|
|
253
253
|
secret = self._client.read(path)
|
254
254
|
except hvac.exceptions.Forbidden:
|
255
255
|
msg = f"permission denied accessing secret '{path}'"
|
256
|
-
raise
|
256
|
+
raise SecretAccessForbiddenError(msg) from None
|
257
257
|
|
258
258
|
if secret is None or "data" not in secret:
|
259
|
-
raise
|
259
|
+
raise SecretNotFoundError(path)
|
260
260
|
|
261
261
|
return secret["data"]
|
262
262
|
|
@@ -285,7 +285,7 @@ class _VaultClient:
|
|
285
285
|
data = self._read_v1(secret_path, secret_field)
|
286
286
|
|
287
287
|
if data is None:
|
288
|
-
raise
|
288
|
+
raise SecretNotFoundError
|
289
289
|
|
290
290
|
return (
|
291
291
|
base64.b64decode(data).decode("utf-8")
|
@@ -298,7 +298,7 @@ class _VaultClient:
|
|
298
298
|
try:
|
299
299
|
secret_field = data[field]
|
300
300
|
except KeyError:
|
301
|
-
raise
|
301
|
+
raise SecretFieldNotFoundError(f"{path}/{field} ({version})") from None
|
302
302
|
return secret_field
|
303
303
|
|
304
304
|
def _read_v1(self, path, field):
|
@@ -306,7 +306,7 @@ class _VaultClient:
|
|
306
306
|
try:
|
307
307
|
secret_field = data[field]
|
308
308
|
except KeyError:
|
309
|
-
raise
|
309
|
+
raise SecretFieldNotFoundError(f"{path}/{field}") from None
|
310
310
|
return secret_field
|
311
311
|
|
312
312
|
@retry()
|
@@ -345,7 +345,7 @@ class _VaultClient:
|
|
345
345
|
if current_data == data and not force:
|
346
346
|
logging.debug(f"current data is up-to-date, skipping {path}")
|
347
347
|
return
|
348
|
-
except
|
348
|
+
except SecretVersionNotFoundError:
|
349
349
|
# if the secret is not found we need to write it
|
350
350
|
logging.debug(f"secret not found in {path}, will create it")
|
351
351
|
|
@@ -358,14 +358,14 @@ class _VaultClient:
|
|
358
358
|
self._read_all_v2.cache_clear()
|
359
359
|
except hvac.exceptions.Forbidden:
|
360
360
|
msg = f"permission denied accessing secret '{path}'"
|
361
|
-
raise
|
361
|
+
raise SecretAccessForbiddenError(msg) from None
|
362
362
|
|
363
363
|
def _write_v1(self, path, data):
|
364
364
|
try:
|
365
365
|
self._client.write(path, **data)
|
366
366
|
except hvac.exceptions.Forbidden:
|
367
367
|
msg = f"permission denied accessing secret '{path}'"
|
368
|
-
raise
|
368
|
+
raise SecretAccessForbiddenError(msg) from None
|
369
369
|
|
370
370
|
def _list_kv2(self, path: str) -> dict:
|
371
371
|
try:
|
@@ -376,14 +376,14 @@ class _VaultClient:
|
|
376
376
|
return response
|
377
377
|
except hvac.exceptions.Forbidden:
|
378
378
|
msg = f"permission denied accessing path '{path}'"
|
379
|
-
raise
|
379
|
+
raise PathAccessForbiddenError(msg) from None
|
380
380
|
|
381
381
|
def _list(self, path: str) -> dict:
|
382
382
|
try:
|
383
383
|
return self._client.list(path)
|
384
384
|
except hvac.exceptions.Forbidden:
|
385
385
|
msg = f"permission denied accessing path '{path}'"
|
386
|
-
raise
|
386
|
+
raise PathAccessForbiddenError(msg) from None
|
387
387
|
|
388
388
|
def list(self, path: str) -> list[str]:
|
389
389
|
"""Returns a list of secrets in a given path."""
|
@@ -420,7 +420,7 @@ class _VaultClient:
|
|
420
420
|
self._client.delete(path)
|
421
421
|
except hvac.exceptions.Forbidden:
|
422
422
|
msg = f"permission denied accessing secret '{path}'"
|
423
|
-
raise
|
423
|
+
raise SecretAccessForbiddenError(msg) from None
|
424
424
|
|
425
425
|
|
426
426
|
class VaultClient:
|
reconcile/vault_replication.py
CHANGED
@@ -26,9 +26,9 @@ from reconcile.gql_definitions.vault_policies.vault_policies import (
|
|
26
26
|
)
|
27
27
|
from reconcile.utils import gql
|
28
28
|
from reconcile.utils.vault import (
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
SecretAccessForbiddenError,
|
30
|
+
SecretNotFoundError,
|
31
|
+
SecretVersionNotFoundError,
|
32
32
|
VaultClient,
|
33
33
|
_VaultClient,
|
34
34
|
)
|
@@ -37,15 +37,15 @@ QONTRACT_INTEGRATION = "vault-replication"
|
|
37
37
|
SECRET_PATH_PATTERN = re.compile(r"^[\w/-]+?(?P<folder>/\*?)?$")
|
38
38
|
|
39
39
|
|
40
|
-
class
|
40
|
+
class VaultInvalidPathsError(Exception):
|
41
41
|
pass
|
42
42
|
|
43
43
|
|
44
|
-
class
|
44
|
+
class VaultInvalidAuthMethodError(Exception):
|
45
45
|
pass
|
46
46
|
|
47
47
|
|
48
|
-
class
|
48
|
+
class VaultInvalidPolicyError(Exception):
|
49
49
|
pass
|
50
50
|
|
51
51
|
|
@@ -64,7 +64,7 @@ def deep_copy_versions(
|
|
64
64
|
|
65
65
|
try:
|
66
66
|
secret, src_version = source_vault.read_all_with_version(secret_dict)
|
67
|
-
except (
|
67
|
+
except (SecretNotFoundError, SecretVersionNotFoundError):
|
68
68
|
# Handle the case where the difference between the source and destination
|
69
69
|
# versions is greater than the number of versions in the source vault.
|
70
70
|
# By default the secret engines store up to 10 versions of a secret.
|
@@ -119,7 +119,7 @@ def copy_vault_secret(
|
|
119
119
|
|
120
120
|
try:
|
121
121
|
source_data, version = source_vault.read_all_with_version(secret_dict)
|
122
|
-
except
|
122
|
+
except SecretAccessForbiddenError:
|
123
123
|
# Raise exception if we can't read the secret from the source vault.
|
124
124
|
# This is likely to be related to the approle permissions.
|
125
125
|
logging.error([
|
@@ -128,7 +128,7 @@ def copy_vault_secret(
|
|
128
128
|
path,
|
129
129
|
])
|
130
130
|
raise
|
131
|
-
except
|
131
|
+
except SecretNotFoundError:
|
132
132
|
# If the secret is present in vault, but there are no versions of it
|
133
133
|
# we want to be aware of it, but not cause a failure of the complete
|
134
134
|
# integration
|
@@ -161,7 +161,7 @@ def copy_vault_secret(
|
|
161
161
|
current_source_version=version,
|
162
162
|
path=path,
|
163
163
|
)
|
164
|
-
except (
|
164
|
+
except (SecretVersionNotFoundError, SecretNotFoundError):
|
165
165
|
logging.info(["replicate_vault_secret", "Secret not found", path])
|
166
166
|
# Handle v1 secrets where version is None and we don't need to deep sync.
|
167
167
|
if version is None:
|
@@ -194,7 +194,7 @@ def check_invalid_paths(
|
|
194
194
|
# Exit if we have paths not present in the policy that needs to be replicated
|
195
195
|
# this is to prevent to replicate secrets that are not allowed.
|
196
196
|
logging.error(["replicate_vault_secret", "Invalid paths", invalid_paths])
|
197
|
-
raise
|
197
|
+
raise VaultInvalidPathsError
|
198
198
|
|
199
199
|
|
200
200
|
def list_invalid_paths(
|
@@ -236,7 +236,7 @@ def get_policy_secret_list(
|
|
236
236
|
match = SECRET_PATH_PATTERN.match(path)
|
237
237
|
if not match:
|
238
238
|
logging.error(["get_policy_secret_list", "Invalid path to replicate", path])
|
239
|
-
raise
|
239
|
+
raise VaultInvalidPathsError
|
240
240
|
|
241
241
|
if match.group("folder"):
|
242
242
|
# Remove the * at the end of the path because list method expects
|
@@ -304,7 +304,7 @@ def get_vault_credentials(
|
|
304
304
|
VaultInstanceV1_VaultReplicationConfigV1_VaultInstanceAuthV1_VaultInstanceAuthApproleV1,
|
305
305
|
):
|
306
306
|
# Exit if the auth method is not approle as is the only one supported
|
307
|
-
raise
|
307
|
+
raise VaultInvalidAuthMethodError
|
308
308
|
|
309
309
|
role_id = {
|
310
310
|
"path": vault_auth.role_id.path,
|
@@ -359,7 +359,7 @@ def replicate_paths(
|
|
359
359
|
if path.policy is None:
|
360
360
|
# Exit if the replication config is empty, this should never happen
|
361
361
|
# as policy is a required field in the schema but makes mypy happy.
|
362
|
-
raise
|
362
|
+
raise VaultInvalidPolicyError(
|
363
363
|
"Policy is required when using policy provider"
|
364
364
|
)
|
365
365
|
policy_paths = get_policy_paths(
|
@@ -402,7 +402,7 @@ def get_secrets_from_templated_path(path: str, vault_list: Iterable[str]) -> lis
|
|
402
402
|
suffix = cap_groups.group(3)
|
403
403
|
else:
|
404
404
|
# Exit if the path is not a valid formatted template on the secret path
|
405
|
-
raise
|
405
|
+
raise VaultInvalidPathsError
|
406
406
|
|
407
407
|
secret_start, secret_end = _get_start_end_secret(path)
|
408
408
|
|
tools/cli_commands/erv2.py
CHANGED
@@ -39,6 +39,9 @@ from reconcile.utils import gql
|
|
39
39
|
from reconcile.utils.exceptions import FetchResourceError
|
40
40
|
from reconcile.utils.secret_reader import SecretReaderBase
|
41
41
|
|
42
|
+
UP = "\x1b[1A"
|
43
|
+
CLEAR = "\x1b[2K"
|
44
|
+
|
42
45
|
|
43
46
|
def progress_spinner() -> Progress:
|
44
47
|
"""Display shiny progress spinner."""
|
@@ -56,8 +59,6 @@ def pause_progress_spinner(progress: Progress | None) -> Iterator:
|
|
56
59
|
"""Pause the progress spinner."""
|
57
60
|
if progress:
|
58
61
|
progress.stop()
|
59
|
-
UP = "\x1b[1A"
|
60
|
-
CLEAR = "\x1b[2K"
|
61
62
|
for task in progress.tasks:
|
62
63
|
if task.finished:
|
63
64
|
continue
|
@@ -23,15 +23,15 @@ class GPGEncryptCommandData:
|
|
23
23
|
target_user: str = ""
|
24
24
|
|
25
25
|
|
26
|
-
class
|
26
|
+
class UserError(Exception):
|
27
27
|
pass
|
28
28
|
|
29
29
|
|
30
|
-
class
|
30
|
+
class ArgumentError(Exception):
|
31
31
|
pass
|
32
32
|
|
33
33
|
|
34
|
-
class
|
34
|
+
class OpenshiftError(Exception):
|
35
35
|
pass
|
36
36
|
|
37
37
|
|
@@ -51,7 +51,7 @@ class GPGEncryptCommand:
|
|
51
51
|
def _fetch_oc_secret(self) -> str:
|
52
52
|
parts = self._command_data.openshift_path.split("/")
|
53
53
|
if len(parts) != 3:
|
54
|
-
raise
|
54
|
+
raise ArgumentError(
|
55
55
|
f"Wrong format! --openshift-path must be of format {{cluster}}/{{namespace}}/{{secret}}. Got {self._command_data.openshift_path}"
|
56
56
|
)
|
57
57
|
cluster_name, namespace, secret = parts
|
@@ -62,7 +62,7 @@ class GPGEncryptCommand:
|
|
62
62
|
)
|
63
63
|
|
64
64
|
if not clusters:
|
65
|
-
raise
|
65
|
+
raise ArgumentError(f"No cluster found with name '{cluster_name}'")
|
66
66
|
|
67
67
|
settings = queries.get_app_interface_settings()
|
68
68
|
data = {}
|
@@ -81,7 +81,7 @@ class GPGEncryptCommand:
|
|
81
81
|
"data"
|
82
82
|
]
|
83
83
|
except Exception as e:
|
84
|
-
raise
|
84
|
+
raise OpenshiftError(
|
85
85
|
f"Could not fetch secret from Openshift cluster {cluster_name}"
|
86
86
|
) from e
|
87
87
|
|
@@ -107,7 +107,7 @@ class GPGEncryptCommand:
|
|
107
107
|
return self._fetch_local_file_secret()
|
108
108
|
if self._command_data.openshift_path:
|
109
109
|
return self._fetch_oc_secret()
|
110
|
-
raise
|
110
|
+
raise ArgumentError(
|
111
111
|
f"No argument given which defines how to fetch the secret {self._command_data}"
|
112
112
|
)
|
113
113
|
|
@@ -120,13 +120,13 @@ class GPGEncryptCommand:
|
|
120
120
|
),
|
121
121
|
)
|
122
122
|
if len(users) != 1:
|
123
|
-
raise
|
123
|
+
raise UserError(
|
124
124
|
f"Expected to find exactly one user for '{target_user}', but found {len(users)}."
|
125
125
|
)
|
126
126
|
user = users[0]
|
127
127
|
|
128
128
|
if "public_gpg_key" not in user:
|
129
|
-
raise
|
129
|
+
raise UserError(
|
130
130
|
f"User '{target_user}' does not have an associated GPG key."
|
131
131
|
)
|
132
132
|
|
tools/qontract_cli.py
CHANGED
@@ -1066,14 +1066,14 @@ def network_reservations(ctx: click.Context) -> None:
|
|
1066
1066
|
return url
|
1067
1067
|
|
1068
1068
|
for network in get_networks():
|
1069
|
-
|
1069
|
+
parent_address = "none"
|
1070
1070
|
if network.parent_network:
|
1071
|
-
|
1071
|
+
parent_address = network.parent_network.network_address
|
1072
1072
|
if network.in_use_by and network.in_use_by.vpc:
|
1073
1073
|
network_table.append({
|
1074
1074
|
"name": network.name,
|
1075
1075
|
"network Address": network.network_address,
|
1076
|
-
"parent Network":
|
1076
|
+
"parent Network": parent_address,
|
1077
1077
|
"Account Name": network.in_use_by.vpc.account.name,
|
1078
1078
|
"Account UID": network.in_use_by.vpc.account.uid,
|
1079
1079
|
"Console Login URL": md_link(network.in_use_by.vpc.account.console_url),
|
@@ -1082,7 +1082,7 @@ def network_reservations(ctx: click.Context) -> None:
|
|
1082
1082
|
network_table.append({
|
1083
1083
|
"name": network.name,
|
1084
1084
|
"network Address": network.network_address,
|
1085
|
-
"parent Network":
|
1085
|
+
"parent Network": parent_address,
|
1086
1086
|
"Account Name": "Unclaimed network",
|
1087
1087
|
"Account UID": "Unclaimed network",
|
1088
1088
|
"Console Login URL": "Unclaimed network",
|
@@ -1857,8 +1857,8 @@ You can view the source of this Markdown to extract the JSON data.
|
|
1857
1857
|
@get.command
|
1858
1858
|
@click.pass_context
|
1859
1859
|
def rds_recommendations(ctx: click.Context) -> None:
|
1860
|
-
|
1861
|
-
|
1860
|
+
ignored_statuses = ("resolved",)
|
1861
|
+
ignored_severities = ("informational",)
|
1862
1862
|
|
1863
1863
|
settings = queries.get_app_interface_settings()
|
1864
1864
|
|
@@ -1919,8 +1919,8 @@ def rds_recommendations(ctx: click.Context) -> None:
|
|
1919
1919
|
"Description": rec["Description"].replace("\n", " "),
|
1920
1920
|
}
|
1921
1921
|
for rec in db_recommendations
|
1922
|
-
if rec.get("Status") not in
|
1923
|
-
and rec.get("Severity") not in
|
1922
|
+
if rec.get("Status") not in ignored_statuses
|
1923
|
+
and rec.get("Severity") not in ignored_severities
|
1924
1924
|
]
|
1925
1925
|
# If we have no recommendations to show, skip
|
1926
1926
|
if not recommendations:
|
@@ -2678,7 +2678,6 @@ def ec2_jenkins_workers(
|
|
2678
2678
|
ec2 = boto3.resource("ec2")
|
2679
2679
|
results = []
|
2680
2680
|
now = datetime.now(UTC)
|
2681
|
-
DATE_FORMAT = "%Y-%m-%d %H:%M:%S"
|
2682
2681
|
columns = [
|
2683
2682
|
"type",
|
2684
2683
|
"id",
|
@@ -2723,7 +2722,7 @@ def ec2_jenkins_workers(
|
|
2723
2722
|
"id": f"[{instance.id}]({url})",
|
2724
2723
|
"IP": instance.private_ip_address,
|
2725
2724
|
"instance type": instance.instance_type,
|
2726
|
-
"launch time (utc)": f"{instance.launch_time.strftime(
|
2725
|
+
"launch time (utc)": f"{instance.launch_time.strftime('%Y-%m-%d %H:%M:%S')} {launch_emoji}",
|
2727
2726
|
"OS": os,
|
2728
2727
|
"AMI": f"[{image.name}]({commit_url})",
|
2729
2728
|
}
|
@@ -2801,12 +2800,12 @@ def slo_document_services(ctx: click.Context, status_board_instance: str) -> Non
|
|
2801
2800
|
@click.argument("file_path")
|
2802
2801
|
@click.pass_context
|
2803
2802
|
def alerts(ctx: click.Context, file_path: str) -> None:
|
2804
|
-
|
2803
|
+
big_number = 10
|
2805
2804
|
|
2806
2805
|
def sort_by_threshold(item: dict[str, str]) -> int:
|
2807
2806
|
threshold = item["threshold"]
|
2808
2807
|
if not threshold:
|
2809
|
-
return
|
2808
|
+
return big_number * 60 * 24
|
2810
2809
|
value = int(threshold[:-1])
|
2811
2810
|
unit = threshold[-1]
|
2812
2811
|
match unit:
|
@@ -2817,7 +2816,7 @@ def alerts(ctx: click.Context, file_path: str) -> None:
|
|
2817
2816
|
case "d":
|
2818
2817
|
return value * 60 * 24
|
2819
2818
|
case _:
|
2820
|
-
return
|
2819
|
+
return big_number * 60 * 24
|
2821
2820
|
|
2822
2821
|
def sort_by_severity(item: dict[str, str]) -> int:
|
2823
2822
|
match item["severity"].lower():
|
@@ -2828,7 +2827,7 @@ def alerts(ctx: click.Context, file_path: str) -> None:
|
|
2828
2827
|
case "info":
|
2829
2828
|
return 2
|
2830
2829
|
case _:
|
2831
|
-
return
|
2830
|
+
return big_number
|
2832
2831
|
|
2833
2832
|
with open(file_path, encoding="locale") as f:
|
2834
2833
|
content = json.loads(f.read())
|
@@ -14,11 +14,11 @@ from reconcile.utils.secret_reader import create_secret_reader
|
|
14
14
|
from reconcile.utils.state import init_state
|
15
15
|
|
16
16
|
|
17
|
-
class
|
17
|
+
class SaasPromotionStateError(Exception):
|
18
18
|
pass
|
19
19
|
|
20
20
|
|
21
|
-
class
|
21
|
+
class SaasPromotionStateMissingError(Exception):
|
22
22
|
pass
|
23
23
|
|
24
24
|
|
@@ -72,12 +72,12 @@ class SaasPromotionState:
|
|
72
72
|
)
|
73
73
|
|
74
74
|
if not current_data:
|
75
|
-
raise
|
75
|
+
raise SaasPromotionStateMissingError(
|
76
76
|
f"No promotion state in S3 for given {publisher_uid=} {sha=} {channel=}"
|
77
77
|
)
|
78
78
|
|
79
79
|
if current_data.success:
|
80
|
-
raise
|
80
|
+
raise SaasPromotionStateError(
|
81
81
|
f"The current promotion state is already marked successful for given {publisher_uid=} {sha=} {channel=}",
|
82
82
|
current_data,
|
83
83
|
)
|
tools/template_validation.py
CHANGED
@@ -57,17 +57,17 @@ def print_test_diffs(diffs: list[TemplateDiff]) -> None:
|
|
57
57
|
def main(templates: tuple[str]) -> None:
|
58
58
|
for template_path in templates:
|
59
59
|
okay = True
|
60
|
-
|
60
|
+
template_raw = load_clean_yaml(template_path)
|
61
61
|
|
62
62
|
tests = []
|
63
|
-
for
|
64
|
-
test_yaml = load_clean_yaml(
|
63
|
+
for test_raw in template_raw["templateTest"]:
|
64
|
+
test_yaml = load_clean_yaml(test_raw["$ref"])
|
65
65
|
variables = json.dumps(test_yaml["variables"])
|
66
66
|
test_yaml["variables"] = variables
|
67
67
|
tests.append(test_yaml)
|
68
68
|
|
69
|
-
|
70
|
-
template: TemplateV1 = TemplateV1(**data_default_none(TemplateV1,
|
69
|
+
template_raw["templateTest"] = tests
|
70
|
+
template: TemplateV1 = TemplateV1(**data_default_none(TemplateV1, template_raw))
|
71
71
|
|
72
72
|
# templates_to_validate = {}
|
73
73
|
for test in template.template_test:
|
{qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev257.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|