qontract-reconcile 0.10.2.dev14__py3-none-any.whl → 0.10.2.dev15__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.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/RECORD +133 -133
- reconcile/acs_rbac.py +2 -4
- reconcile/aus/base.py +13 -13
- reconcile/aws_ami_share.py +1 -2
- reconcile/aws_cloudwatch_log_retention/integration.py +1 -1
- reconcile/aws_saml_idp/integration.py +1 -1
- reconcile/aws_saml_roles/integration.py +1 -1
- reconcile/aws_version_sync/integration.py +3 -3
- reconcile/change_owners/change_owners.py +8 -5
- reconcile/change_owners/change_types.py +18 -18
- reconcile/change_owners/changes.py +8 -9
- reconcile/change_owners/decision.py +12 -15
- reconcile/change_owners/self_service_roles.py +6 -4
- reconcile/change_owners/tester.py +8 -10
- reconcile/cli.py +9 -11
- reconcile/closedbox_endpoint_monitoring_base.py +1 -1
- reconcile/cna/integration.py +2 -2
- reconcile/dashdotdb_base.py +2 -2
- reconcile/dashdotdb_cso.py +1 -1
- reconcile/dashdotdb_dora.py +6 -4
- reconcile/dashdotdb_slo.py +1 -1
- reconcile/database_access_manager.py +15 -19
- reconcile/email_sender.py +4 -8
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/external_resources/state.py +17 -17
- reconcile/gabi_authorized_users.py +3 -3
- reconcile/gcr_mirror.py +2 -2
- reconcile/github_org.py +9 -13
- reconcile/gitlab_housekeeping.py +1 -1
- reconcile/gitlab_owners.py +10 -12
- reconcile/gitlab_permissions.py +5 -4
- reconcile/glitchtip/integration.py +14 -14
- reconcile/glitchtip_project_alerts/integration.py +3 -4
- reconcile/integrations_manager.py +1 -2
- reconcile/jenkins_job_builds_cleaner.py +7 -5
- reconcile/jenkins_roles.py +10 -6
- reconcile/jenkins_worker_fleets.py +5 -4
- reconcile/jira_permissions_validator.py +2 -6
- reconcile/ldap_groups/integration.py +3 -2
- reconcile/ocm_groups.py +5 -5
- reconcile/ocm_update_recommended_version.py +2 -2
- reconcile/openshift_base.py +15 -20
- reconcile/openshift_groups.py +9 -8
- reconcile/openshift_namespace_labels.py +3 -4
- reconcile/openshift_namespaces.py +1 -1
- reconcile/openshift_network_policies.py +1 -1
- reconcile/openshift_resources_base.py +4 -4
- reconcile/openshift_serviceaccount_tokens.py +1 -1
- reconcile/openshift_tekton_resources.py +1 -2
- reconcile/openshift_users.py +5 -4
- reconcile/prometheus_rules_tester/integration.py +8 -8
- reconcile/quay_mirror.py +3 -4
- reconcile/quay_mirror_org.py +1 -1
- reconcile/rhidp/ocm_oidc_idp/base.py +10 -15
- reconcile/run_integration.py +7 -7
- reconcile/saas_auto_promotions_manager/publisher.py +1 -1
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +3 -9
- reconcile/service_dependencies.py +2 -7
- reconcile/skupper_network/reconciler.py +5 -5
- reconcile/skupper_network/site_controller.py +3 -3
- reconcile/sql_query.py +5 -5
- reconcile/status_board.py +24 -24
- reconcile/terraform_cloudflare_users.py +2 -2
- reconcile/terraform_repo.py +6 -6
- reconcile/terraform_users.py +8 -5
- reconcile/terraform_vpc_peerings.py +1 -1
- reconcile/terraform_vpc_resources/integration.py +1 -1
- reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +1 -1
- reconcile/typed_queries/app_quay_repos_escalation_policies.py +1 -1
- reconcile/typed_queries/aws_vpc_requests.py +1 -1
- reconcile/typed_queries/aws_vpcs.py +1 -1
- reconcile/typed_queries/clusters.py +1 -1
- reconcile/typed_queries/clusters_minimal.py +1 -1
- reconcile/typed_queries/clusters_with_dms.py +1 -1
- reconcile/typed_queries/dynatrace_environments.py +1 -1
- reconcile/typed_queries/dynatrace_token_provider_token_specs.py +1 -1
- reconcile/typed_queries/reserved_networks.py +1 -1
- reconcile/typed_queries/saas_files.py +1 -1
- reconcile/typed_queries/slo_documents.py +1 -1
- reconcile/typed_queries/status_board.py +1 -2
- reconcile/utils/amtool.py +2 -2
- reconcile/utils/aws_api.py +10 -10
- reconcile/utils/aws_helper.py +1 -1
- reconcile/utils/binary.py +1 -2
- reconcile/utils/differ.py +4 -7
- reconcile/utils/dnsutils.py +4 -12
- reconcile/utils/external_resources.py +1 -2
- reconcile/utils/gitlab_api.py +2 -4
- reconcile/utils/glitchtip/models.py +1 -1
- reconcile/utils/helm.py +1 -1
- reconcile/utils/instrumented_wrappers.py +2 -2
- reconcile/utils/jjb_client.py +1 -1
- reconcile/utils/jump_host.py +1 -1
- reconcile/utils/metrics.py +6 -11
- reconcile/utils/mr/aws_access.py +1 -1
- reconcile/utils/mr/base.py +2 -4
- reconcile/utils/mr/notificator.py +1 -1
- reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +1 -1
- reconcile/utils/oc.py +17 -31
- reconcile/utils/oc_map.py +1 -1
- reconcile/utils/ocm/base.py +4 -2
- reconcile/utils/ocm/search_filters.py +4 -3
- reconcile/utils/ocm/status_board.py +2 -2
- reconcile/utils/ocm/upgrades.py +4 -7
- reconcile/utils/ocm_base_client.py +1 -1
- reconcile/utils/openshift_resource.py +1 -1
- reconcile/utils/promtool.py +1 -1
- reconcile/utils/quay_api.py +1 -3
- reconcile/utils/raw_github_api.py +3 -10
- reconcile/utils/repo_owners.py +5 -5
- reconcile/utils/rest_api_base.py +1 -2
- reconcile/utils/rosa/rosa_cli.py +3 -3
- reconcile/utils/saasherder/saasherder.py +9 -15
- reconcile/utils/secret_reader.py +2 -2
- reconcile/utils/sharding.py +2 -2
- reconcile/utils/state.py +5 -5
- reconcile/utils/terraform_client.py +2 -2
- reconcile/utils/terrascript/cloudflare_resources.py +4 -6
- reconcile/utils/terrascript_aws_client.py +16 -28
- reconcile/utils/vault.py +2 -2
- reconcile/utils/vcs.py +8 -16
- reconcile/vault_replication.py +1 -8
- tools/app_interface_reporter.py +1 -1
- tools/cli_commands/container_images_report.py +1 -1
- tools/cli_commands/cost_report/view.py +4 -2
- tools/cli_commands/gpg_encrypt.py +1 -5
- tools/qontract_cli.py +14 -13
- tools/saas_metrics_exporter/commit_distance/channel.py +1 -1
- tools/saas_promotion_state/saas_promotion_state.py +1 -1
- tools/sd_app_sre_alert_report.py +3 -3
- {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/entry_points.txt +0 -0
@@ -73,7 +73,7 @@ class SaasResourceTemplateTarget(ConfiguredBaseModel):
|
|
73
73
|
) -> str:
|
74
74
|
"""Returns a unique identifier for a target."""
|
75
75
|
return hashlib.blake2s(
|
76
|
-
f"{parent_saas_file_name}:{parent_resource_template_name}:{self.name
|
76
|
+
f"{parent_saas_file_name}:{parent_resource_template_name}:{self.name or 'default'}:{self.namespace.cluster.name}:{self.namespace.name}".encode(),
|
77
77
|
digest_size=20,
|
78
78
|
).hexdigest()
|
79
79
|
|
@@ -7,6 +7,6 @@ from reconcile.utils.gql import GqlApi
|
|
7
7
|
|
8
8
|
|
9
9
|
def get_slo_documents(gql_api: GqlApi | None = None) -> list[SLODocumentV1]:
|
10
|
-
api = gql_api
|
10
|
+
api = gql_api or gql.get_api()
|
11
11
|
data = query(query_func=api.query)
|
12
12
|
return data.slo_document_v1 or []
|
@@ -51,8 +51,7 @@ def get_selected_app_names(
|
|
51
51
|
for selector in selectors:
|
52
52
|
apps_to_remove: set[str] = set()
|
53
53
|
results = parser.parse(selector).find(apps)
|
54
|
-
for match in results
|
55
|
-
apps_to_remove.add(match.value["name"])
|
54
|
+
apps_to_remove.update(match.value["name"] for match in results)
|
56
55
|
selected_app_names -= apps_to_remove
|
57
56
|
|
58
57
|
return selected_app_names
|
reconcile/utils/amtool.py
CHANGED
@@ -52,7 +52,7 @@ def version() -> AmtoolResult:
|
|
52
52
|
"""Returns the version parsed from amtool --version"""
|
53
53
|
result = _run_cmd(["amtool", "--version"])
|
54
54
|
|
55
|
-
pattern = re.compile("^amtool, version (?P<version>[^ ]+) .+")
|
55
|
+
pattern = re.compile(r"^amtool, version (?P<version>[^ ]+) .+")
|
56
56
|
if m := pattern.match(result.message):
|
57
57
|
return AmtoolResult(True, m.group("version"))
|
58
58
|
|
@@ -63,7 +63,7 @@ def _run_cmd(cmd: list[str]) -> AmtoolResult:
|
|
63
63
|
try:
|
64
64
|
result = run(cmd, capture_output=True, check=True)
|
65
65
|
except CalledProcessError as e:
|
66
|
-
msg = f
|
66
|
+
msg = f"Error running amtool command [{' '.join(cmd)}]"
|
67
67
|
if e.stdout:
|
68
68
|
msg += f" {e.stdout.decode()}"
|
69
69
|
if e.stderr:
|
reconcile/utils/aws_api.py
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
import operator
|
2
3
|
import os
|
3
4
|
import re
|
4
5
|
import time
|
@@ -191,7 +192,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
191
192
|
service_name,
|
192
193
|
region_name: str | None = None,
|
193
194
|
):
|
194
|
-
region = region_name
|
195
|
+
region = region_name or session.region_name
|
195
196
|
client = session.client(
|
196
197
|
service_name,
|
197
198
|
region_name=region,
|
@@ -205,7 +206,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
205
206
|
def _get_session_resource(
|
206
207
|
session: Session, service_name, region_name: str | None = None
|
207
208
|
):
|
208
|
-
region = region_name
|
209
|
+
region = region_name or session.region_name
|
209
210
|
return session.resource(service_name, region_name=region)
|
210
211
|
|
211
212
|
def _account_ec2_client(
|
@@ -1000,12 +1001,13 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
1000
1001
|
assumed_role_data = self._get_account_assume_data(account)
|
1001
1002
|
assumed_ec2 = self._get_assumed_role_client(*assumed_role_data)
|
1002
1003
|
nat_gateways = assumed_ec2.describe_nat_gateways()
|
1003
|
-
egress_ips = set()
|
1004
|
+
egress_ips: set[str] = set()
|
1004
1005
|
for nat in nat_gateways.get("NatGateways") or []:
|
1005
1006
|
if nat["VpcId"] != vpc_id:
|
1006
1007
|
continue
|
1007
|
-
|
1008
|
-
|
1008
|
+
egress_ips.update(
|
1009
|
+
address["PublicIp"] for address in nat["NatGatewayAddresses"]
|
1010
|
+
)
|
1009
1011
|
|
1010
1012
|
return egress_ips
|
1011
1013
|
|
@@ -1481,13 +1483,11 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
1481
1483
|
client.delete_hosted_zone(Id=zone_id)
|
1482
1484
|
except client.exceptions.NoSuchHostedZone:
|
1483
1485
|
logging.error(
|
1484
|
-
f"[{account_name}] Error trying to delete "
|
1485
|
-
f"unknown DNS zone {zone_id}"
|
1486
|
+
f"[{account_name}] Error trying to delete unknown DNS zone {zone_id}"
|
1486
1487
|
)
|
1487
1488
|
except client.exceptions.HostedZoneNotEmpty:
|
1488
1489
|
logging.error(
|
1489
|
-
f"[{account_name}] Cannot delete DNS zone that "
|
1490
|
-
f"is not empty {zone_id}"
|
1490
|
+
f"[{account_name}] Cannot delete DNS zone that is not empty {zone_id}"
|
1491
1491
|
)
|
1492
1492
|
except Exception as e:
|
1493
1493
|
logging.error(f"[{account_name}] unhandled exception: {e}")
|
@@ -1697,7 +1697,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
1697
1697
|
return [
|
1698
1698
|
obj["Key"]
|
1699
1699
|
for obj in sorted(
|
1700
|
-
objects, key=
|
1700
|
+
objects, key=operator.itemgetter("LastModified"), reverse=True
|
1701
1701
|
)
|
1702
1702
|
]
|
1703
1703
|
|
reconcile/utils/aws_helper.py
CHANGED
@@ -74,7 +74,7 @@ def get_account(accounts: Iterable[Account], account_name: str) -> Account:
|
|
74
74
|
raise AccountNotFoundError(account_name)
|
75
75
|
|
76
76
|
|
77
|
-
def get_region_from_availability_zone(availability_zone: str) -> str:
|
77
|
+
def get_region_from_availability_zone(availability_zone: str) -> str: # noqa: FURB118
|
78
78
|
return availability_zone[:-1]
|
79
79
|
|
80
80
|
|
reconcile/utils/binary.py
CHANGED
@@ -39,8 +39,7 @@ def binary_version(binary, version_args, search_regex, expected_versions):
|
|
39
39
|
result = subprocess.run(cmd, capture_output=True, check=True)
|
40
40
|
except subprocess.CalledProcessError as e:
|
41
41
|
msg = (
|
42
|
-
f"Could not execute binary '{binary}' "
|
43
|
-
f"for binary version check: {e}"
|
42
|
+
f"Could not execute binary '{binary}' for binary version check: {e}"
|
44
43
|
)
|
45
44
|
raise Exception(msg) from e
|
46
45
|
|
reconcile/utils/differ.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import operator
|
1
2
|
from collections.abc import (
|
2
3
|
Callable,
|
3
4
|
Iterable,
|
@@ -30,10 +31,6 @@ class DiffResult(Generic[Current, Desired, Key]):
|
|
30
31
|
identical: dict[Key, DiffPair[Current, Desired]]
|
31
32
|
|
32
33
|
|
33
|
-
def _default_equal(current: Current, desired: Desired) -> bool:
|
34
|
-
return current == desired
|
35
|
-
|
36
|
-
|
37
34
|
def _default_key(item: Any) -> Any:
|
38
35
|
return item
|
39
36
|
|
@@ -41,7 +38,7 @@ def _default_key(item: Any) -> Any:
|
|
41
38
|
def diff_mappings(
|
42
39
|
current: Mapping[Key, Current],
|
43
40
|
desired: Mapping[Key, Desired],
|
44
|
-
equal: Callable[[Current, Desired], bool] =
|
41
|
+
equal: Callable[[Current, Desired], bool] = operator.eq,
|
45
42
|
) -> DiffResult[Current, Desired, Key]:
|
46
43
|
"""
|
47
44
|
Compare two mappings and return a `DiffResult` instance containing the differences between them.
|
@@ -91,7 +88,7 @@ def diff_any_iterables(
|
|
91
88
|
desired: Iterable[Desired],
|
92
89
|
current_key: Callable[[Current], Key] = _default_key,
|
93
90
|
desired_key: Callable[[Desired], Key] = _default_key,
|
94
|
-
equal: Callable[[Current, Desired], bool] =
|
91
|
+
equal: Callable[[Current, Desired], bool] = operator.eq,
|
95
92
|
) -> DiffResult[Current, Desired, Key]:
|
96
93
|
"""
|
97
94
|
Compare two iterables and return a `DiffResult` instance containing the differences between them.
|
@@ -152,7 +149,7 @@ def diff_iterables(
|
|
152
149
|
current: Iterable[T],
|
153
150
|
desired: Iterable[T],
|
154
151
|
key: Callable[[T], Key] = _default_key,
|
155
|
-
equal: Callable[[T, T], bool] =
|
152
|
+
equal: Callable[[T, T], bool] = operator.eq,
|
156
153
|
) -> DiffResult[T, T, Key]:
|
157
154
|
"""
|
158
155
|
Compare two iterables with same type and return a `DiffResult` instance containing the differences between them.
|
reconcile/utils/dnsutils.py
CHANGED
@@ -1,17 +1,9 @@
|
|
1
1
|
from dns import resolver
|
2
2
|
|
3
3
|
|
4
|
-
def get_nameservers(domain):
|
5
|
-
|
6
|
-
answers = resolver.query(domain, "NS")
|
7
|
-
for rdata in answers:
|
8
|
-
records.append(rdata.to_text())
|
9
|
-
return records
|
4
|
+
def get_nameservers(domain: str) -> list[str]:
|
5
|
+
return [rdata.to_text() for rdata in resolver.query(domain, "NS")]
|
10
6
|
|
11
7
|
|
12
|
-
def get_a_records(host):
|
13
|
-
|
14
|
-
answers = resolver.query(host, "A")
|
15
|
-
for rdata in answers:
|
16
|
-
records.append(rdata.to_text())
|
17
|
-
return records
|
8
|
+
def get_a_records(host: str) -> list[str]:
|
9
|
+
return [rdata.to_text() for rdata in resolver.query(host, "A")]
|
@@ -52,8 +52,7 @@ def get_provision_providers(namespace_info: Mapping[str, Any]) -> set[str]:
|
|
52
52
|
return providers
|
53
53
|
|
54
54
|
external_resources = namespace_info.get("externalResources") or []
|
55
|
-
for e in external_resources
|
56
|
-
providers.add(e["provider"])
|
55
|
+
providers.update(e["provider"] for e in external_resources)
|
57
56
|
|
58
57
|
return providers
|
59
58
|
|
reconcile/utils/gitlab_api.py
CHANGED
@@ -413,7 +413,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
413
413
|
items = self.get_items(mr.pipelines.list)
|
414
414
|
return sorted(
|
415
415
|
[i.asdict() for i in items],
|
416
|
-
key=
|
416
|
+
key=itemgetter("created_at"),
|
417
417
|
reverse=True,
|
418
418
|
)
|
419
419
|
|
@@ -599,9 +599,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
599
599
|
if label in labels:
|
600
600
|
return
|
601
601
|
labels.append(label)
|
602
|
-
note_body =
|
603
|
-
f"item has been marked as {label}. " f"to remove say `/{label} cancel`"
|
604
|
-
)
|
602
|
+
note_body = f"item has been marked as {label}. to remove say `/{label} cancel`"
|
605
603
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
606
604
|
item.notes.create({"body": note_body})
|
607
605
|
gitlab_request.labels(integration=INTEGRATION_NAME).inc()
|
@@ -130,7 +130,7 @@ class ProjectAlert(BaseModel):
|
|
130
130
|
) -> MutableMapping[str, Any]:
|
131
131
|
# name is an empty string if the alert was created manually because it can't be set via UI
|
132
132
|
# use the pk instead.
|
133
|
-
values["name"] = values.get("name") or f
|
133
|
+
values["name"] = values.get("name") or f"alert-{values.get('pk')}"
|
134
134
|
return values
|
135
135
|
|
136
136
|
def __eq__(self, other: object) -> bool:
|
reconcile/utils/helm.py
CHANGED
@@ -87,7 +87,7 @@ def do_template(
|
|
87
87
|
]
|
88
88
|
result = run(cmd, capture_output=True, check=True)
|
89
89
|
except CalledProcessError as e:
|
90
|
-
msg = f
|
90
|
+
msg = f"Error running helm template [{' '.join(cmd)}]"
|
91
91
|
if e.stdout:
|
92
92
|
msg += f" {e.stdout.decode()}"
|
93
93
|
if e.stderr:
|
@@ -10,8 +10,8 @@ from reconcile.utils import metrics
|
|
10
10
|
# TODO: move these to a shared, constants module
|
11
11
|
|
12
12
|
INTEGRATION_NAME = os.environ.get("INTEGRATION_NAME", "")
|
13
|
-
SHARDS = os.environ.get("SHARDS", 1)
|
14
|
-
SHARD_ID = int(os.environ.get("SHARD_ID", 0))
|
13
|
+
SHARDS = int(os.environ.get("SHARDS", "1"))
|
14
|
+
SHARD_ID = int(os.environ.get("SHARD_ID", "0"))
|
15
15
|
|
16
16
|
|
17
17
|
class InstrumentedImage(Image):
|
reconcile/utils/jjb_client.py
CHANGED
@@ -236,7 +236,7 @@ class JJB: # pylint: disable=too-many-public-methods
|
|
236
236
|
result = subprocess.run(
|
237
237
|
cmd, check=True, stdout=PIPE, stderr=STDOUT, encoding="utf-8"
|
238
238
|
)
|
239
|
-
if re.search("updated: [1-9]", result.stdout):
|
239
|
+
if re.search(r"updated: [1-9]", result.stdout):
|
240
240
|
logging.info(result.stdout)
|
241
241
|
except CalledProcessError as ex:
|
242
242
|
logging.error(ex.stdout)
|
reconcile/utils/jump_host.py
CHANGED
@@ -36,7 +36,7 @@ class JumpHostBase:
|
|
36
36
|
def __init__(self, parameters: JumphostParameters):
|
37
37
|
self._hostname = parameters.hostname
|
38
38
|
self._user = parameters.user
|
39
|
-
self._port = parameters.port
|
39
|
+
self._port = parameters.port or 22
|
40
40
|
self._identity = parameters.key
|
41
41
|
self._init_identity_file()
|
42
42
|
|
reconcile/utils/metrics.py
CHANGED
@@ -65,8 +65,8 @@ execution_counter = Counter(
|
|
65
65
|
)
|
66
66
|
|
67
67
|
reconcile_time = Histogram(
|
68
|
-
name="
|
69
|
-
documentation="Run time seconds for tracked
|
68
|
+
name="qontract_reconcile_function_elapsed_seconds_since_bundle_commit",
|
69
|
+
documentation="Run time seconds for tracked functions",
|
70
70
|
labelnames=["name", "integration"],
|
71
71
|
buckets=(60.0, 150.0, 300.0, 600.0, 1200.0, 1800.0, 2400.0, 3000.0, float("inf")),
|
72
72
|
)
|
@@ -133,8 +133,7 @@ class BaseMetric(ABC, BaseModel):
|
|
133
133
|
class name. Removes the suffix `_metric` is present. Subclasses can override this.
|
134
134
|
"""
|
135
135
|
metric_name = re.sub(r"(?<!^)(?=[A-Z])", "_", cls.__name__).lower()
|
136
|
-
|
137
|
-
metric_name = metric_name[:-7]
|
136
|
+
metric_name = metric_name.removesuffix("_metric")
|
138
137
|
return metric_name
|
139
138
|
|
140
139
|
|
@@ -151,8 +150,7 @@ class GaugeMetric(BaseMetric):
|
|
151
150
|
@classmethod
|
152
151
|
def name(cls) -> str:
|
153
152
|
metric_name = super().name()
|
154
|
-
|
155
|
-
metric_name = metric_name[:-6]
|
153
|
+
metric_name = metric_name.removesuffix("_gauge")
|
156
154
|
return metric_name
|
157
155
|
|
158
156
|
|
@@ -175,8 +173,7 @@ class CounterMetric(BaseMetric):
|
|
175
173
|
@classmethod
|
176
174
|
def name(cls) -> str:
|
177
175
|
metric_name = super().name()
|
178
|
-
|
179
|
-
metric_name = metric_name[:-8]
|
176
|
+
metric_name = metric_name.removesuffix("_counter")
|
180
177
|
return metric_name
|
181
178
|
|
182
179
|
|
@@ -221,9 +218,7 @@ class MetricsContainer:
|
|
221
218
|
self._counters[counter.__class__][label_values] = current_value + by
|
222
219
|
|
223
220
|
def _aggregate_scopes(self) -> "MetricsContainer":
|
224
|
-
containers = [self]
|
225
|
-
for sub in self._scopes.values():
|
226
|
-
containers.append(sub._aggregate_scopes())
|
221
|
+
containers = [self] + [sub._aggregate_scopes() for sub in self._scopes.values()]
|
227
222
|
return join_metric_containers(containers)
|
228
223
|
|
229
224
|
def collect(self) -> Generator[Metric, None, None]:
|
reconcile/utils/mr/aws_access.py
CHANGED
@@ -64,7 +64,7 @@ class CreateDeleteAwsAccessKey(MergeRequestBase):
|
|
64
64
|
|
65
65
|
body = body_template.render(ACCOUNT=self.account, ACCESS_KEY=self.key)
|
66
66
|
email_name = f"{self.account}-{self.key}"
|
67
|
-
ref = self.path
|
67
|
+
ref = self.path.removeprefix("data")
|
68
68
|
content = app_interface_email(
|
69
69
|
name=email_name, subject=self.title, aws_accounts=[ref], body=pss(body)
|
70
70
|
)
|
reconcile/utils/mr/base.py
CHANGED
@@ -65,9 +65,7 @@ class MergeRequestBase(ABC):
|
|
65
65
|
def cancel(self, message: str) -> None:
|
66
66
|
self.cancelled = True
|
67
67
|
raise CancelMergeRequest(
|
68
|
-
f"{self.name} MR canceled for "
|
69
|
-
f"branch {self.branch}. "
|
70
|
-
f"Reason: {message}"
|
68
|
+
f"{self.name} MR canceled for branch {self.branch}. Reason: {message}"
|
71
69
|
)
|
72
70
|
|
73
71
|
@property
|
@@ -222,7 +220,7 @@ class MergeRequestBase(ABC):
|
|
222
220
|
# we are not going to let an otherwise fine MR
|
223
221
|
# processing fail just because of this
|
224
222
|
LOG.error(
|
225
|
-
f"Failed to delete branch {self.branch}.
|
223
|
+
f"Failed to delete branch {self.branch}. Reason: {gitlab_error}"
|
226
224
|
)
|
227
225
|
|
228
226
|
def diffs(self, gitlab_cli: GitLabApi) -> Any:
|
@@ -40,7 +40,7 @@ class CreateAppInterfaceNotificator(MergeRequestBase):
|
|
40
40
|
self._notification = notification
|
41
41
|
self._email_base_path = email_base_path
|
42
42
|
self._dry_run = dry_run
|
43
|
-
self.labels = labels
|
43
|
+
self.labels = labels or [DO_NOT_MERGE_HOLD]
|
44
44
|
|
45
45
|
@property
|
46
46
|
def title(self) -> str:
|
@@ -21,7 +21,7 @@ class CreateOCMUpgradeSchedulerOrgUpdates(MergeRequestBase):
|
|
21
21
|
|
22
22
|
@property
|
23
23
|
def description(self) -> str:
|
24
|
-
return f
|
24
|
+
return f"ocm upgrade scheduler org updates for {self.updates_info['name']}"
|
25
25
|
|
26
26
|
def process(self, gitlab_cli: GitLabApi) -> None:
|
27
27
|
changes = False
|
reconcile/utils/oc.py
CHANGED
@@ -213,20 +213,13 @@ class OCDecorators:
|
|
213
213
|
return wrapper
|
214
214
|
|
215
215
|
|
216
|
+
@dataclass
|
216
217
|
class OCProcessReconcileTimeDecoratorMsg:
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
slow_oc_reconcile_threshold: float,
|
223
|
-
is_log_slow_oc_reconcile: bool,
|
224
|
-
):
|
225
|
-
self.namespace = namespace
|
226
|
-
self.resource = resource
|
227
|
-
self.server = server
|
228
|
-
self.slow_oc_reconcile_threshold = slow_oc_reconcile_threshold
|
229
|
-
self.is_log_slow_oc_reconcile = is_log_slow_oc_reconcile
|
218
|
+
namespace: str
|
219
|
+
resource: OR
|
220
|
+
server: str | None
|
221
|
+
slow_oc_reconcile_threshold: float
|
222
|
+
is_log_slow_oc_reconcile: bool
|
230
223
|
|
231
224
|
|
232
225
|
def oc_process(template, parameters=None):
|
@@ -383,7 +376,7 @@ class OCCli: # pylint: disable=too-many-public-methods
|
|
383
376
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
384
377
|
|
385
378
|
self.slow_oc_reconcile_threshold = float(
|
386
|
-
os.environ.get("SLOW_OC_RECONCILE_THRESHOLD", 600)
|
379
|
+
os.environ.get("SLOW_OC_RECONCILE_THRESHOLD", "600")
|
387
380
|
)
|
388
381
|
|
389
382
|
self.is_log_slow_oc_reconcile = os.environ.get(
|
@@ -456,7 +449,7 @@ class OCCli: # pylint: disable=too-many-public-methods
|
|
456
449
|
self.projects = {p["metadata"]["name"] for p in self.get_all(kind)["items"]}
|
457
450
|
|
458
451
|
self.slow_oc_reconcile_threshold = float(
|
459
|
-
os.environ.get("SLOW_OC_RECONCILE_THRESHOLD", 600)
|
452
|
+
os.environ.get("SLOW_OC_RECONCILE_THRESHOLD", "600")
|
460
453
|
)
|
461
454
|
|
462
455
|
self.is_log_slow_oc_reconcile = os.environ.get(
|
@@ -484,9 +477,7 @@ class OCCli: # pylint: disable=too-many-public-methods
|
|
484
477
|
|
485
478
|
if "labels" in kwargs:
|
486
479
|
labels_list = [f"{k}={v}" for k, v in kwargs.get("labels").items()]
|
487
|
-
|
488
|
-
cmd.append("-l")
|
489
|
-
cmd.append(",".join(labels_list))
|
480
|
+
cmd += ["-l", ",".join(labels_list)]
|
490
481
|
|
491
482
|
resource_names = kwargs.get("resource_names")
|
492
483
|
if resource_names:
|
@@ -766,9 +757,9 @@ class OCCli: # pylint: disable=too-many-public-methods
|
|
766
757
|
if not finished_pods:
|
767
758
|
raise JobNotRunningError(name)
|
768
759
|
|
769
|
-
latest_pod =
|
760
|
+
latest_pod = max(
|
770
761
|
finished_pods, key=lambda pod: pod["metadata"]["creationTimestamp"]
|
771
|
-
)
|
762
|
+
)
|
772
763
|
cmd = [
|
773
764
|
"logs",
|
774
765
|
"--all-containers=true",
|
@@ -1180,7 +1171,7 @@ class OCCli: # pylint: disable=too-many-public-methods
|
|
1180
1171
|
|
1181
1172
|
if not find:
|
1182
1173
|
raise StatusCodeError(
|
1183
|
-
f"{self.server}: {apigroup_override}
|
1174
|
+
f"{self.server}: {apigroup_override} does not have kind {kind}"
|
1184
1175
|
)
|
1185
1176
|
return (kind, group_version)
|
1186
1177
|
|
@@ -1420,7 +1411,7 @@ class OCLocal(OCCli):
|
|
1420
1411
|
class OC:
|
1421
1412
|
client_status = Counter(
|
1422
1413
|
name="qontract_reconcile_native_client",
|
1423
|
-
documentation="Cluster is using openshift
|
1414
|
+
documentation="Cluster is using openshift native client",
|
1424
1415
|
labelnames=["cluster_name", "native_client"],
|
1425
1416
|
)
|
1426
1417
|
|
@@ -1667,7 +1658,7 @@ class OC_Map:
|
|
1667
1658
|
cluster,
|
1668
1659
|
OCLogMsg(
|
1669
1660
|
log_level=logging.ERROR,
|
1670
|
-
message=f"[{cluster}]
|
1661
|
+
message=f"[{cluster}] is unreachable: {e}",
|
1671
1662
|
),
|
1672
1663
|
privileged,
|
1673
1664
|
)
|
@@ -1770,13 +1761,9 @@ def validate_labels(labels: dict[str, str]) -> Iterable[str]:
|
|
1770
1761
|
|
1771
1762
|
for k, v in labels.items():
|
1772
1763
|
if len(v) > LABEL_MAX_VALUE_LENGTH:
|
1773
|
-
err.append(
|
1774
|
-
f"Label value longer than " f"{LABEL_MAX_VALUE_LENGTH} chars: {v}"
|
1775
|
-
)
|
1764
|
+
err.append(f"Label value longer than {LABEL_MAX_VALUE_LENGTH} chars: {v}")
|
1776
1765
|
if not v_pattern.match(v):
|
1777
|
-
err.append(
|
1778
|
-
f"Label value is invalid, it needs to match " f"'{v_pattern}': {v}"
|
1779
|
-
)
|
1766
|
+
err.append(f"Label value is invalid, it needs to match '{v_pattern}': {v}")
|
1780
1767
|
|
1781
1768
|
prefix, name = "", k
|
1782
1769
|
if "/" in k:
|
@@ -1792,8 +1779,7 @@ def validate_labels(labels: dict[str, str]) -> Iterable[str]:
|
|
1792
1779
|
)
|
1793
1780
|
if not k_name_pattern.match(name):
|
1794
1781
|
err.append(
|
1795
|
-
f"Label key name is invalid, it needs to mach "
|
1796
|
-
f"'{v_pattern}'': {name}"
|
1782
|
+
f"Label key name is invalid, it needs to mach '{v_pattern}'': {name}"
|
1797
1783
|
)
|
1798
1784
|
|
1799
1785
|
if prefix:
|
reconcile/utils/oc_map.py
CHANGED
reconcile/utils/ocm/base.py
CHANGED
@@ -166,8 +166,10 @@ class OCMClusterAWSSettings(BaseModel):
|
|
166
166
|
roles.append(self.sts.role_arn)
|
167
167
|
if self.sts.support_role_arn:
|
168
168
|
roles.append(self.sts.support_role_arn)
|
169
|
-
|
170
|
-
|
169
|
+
roles.extend(
|
170
|
+
instance_iam_role
|
171
|
+
for instance_iam_role in (self.sts.instance_iam_roles or {}).values()
|
172
|
+
)
|
171
173
|
return roles
|
172
174
|
|
173
175
|
@property
|
@@ -355,9 +355,10 @@ class Filter:
|
|
355
355
|
"""
|
356
356
|
if not self.conditions:
|
357
357
|
raise InvalidFilterError("no conditions within filter object")
|
358
|
-
rendered_conditions = [
|
359
|
-
|
360
|
-
|
358
|
+
rendered_conditions = [
|
359
|
+
condition.render()
|
360
|
+
for condition in sorted(self.conditions, key=lambda c: c.key)
|
361
|
+
]
|
361
362
|
if self.mode == FilterMode.OR:
|
362
363
|
concat = " or ".join(rendered_conditions)
|
363
364
|
if len(rendered_conditions) > 1:
|
@@ -22,7 +22,7 @@ def get_product_applications(
|
|
22
22
|
application.get("metadata", {}).get(METADATA_MANAGED_BY_KEY, "")
|
23
23
|
== METADATA_MANAGED_BY_VALUE
|
24
24
|
):
|
25
|
-
results.append({
|
25
|
+
results.append({ # noqa: PERF401
|
26
26
|
k: v for k, v in application.items() if k in APPLICATION_DESIRED_KEYS
|
27
27
|
})
|
28
28
|
|
@@ -36,7 +36,7 @@ def get_managed_products(ocm_api: OCMBaseClient) -> list[dict[str, Any]]:
|
|
36
36
|
product.get("metadata", {}).get(METADATA_MANAGED_BY_KEY, "")
|
37
37
|
== METADATA_MANAGED_BY_VALUE
|
38
38
|
):
|
39
|
-
results.append({
|
39
|
+
results.append({ # noqa: PERF401
|
40
40
|
k: v for k, v in product.items() if k in PRODUCTS_DESIRED_KEYS
|
41
41
|
})
|
42
42
|
return results
|
reconcile/utils/ocm/upgrades.py
CHANGED
@@ -132,7 +132,7 @@ def get_node_pool_upgrade_policies(
|
|
132
132
|
for policy in ocm_api.get_paginated(
|
133
133
|
f"{build_cluster_url(cluster_id)}/node_pools/{node_pool}/upgrade_policies"
|
134
134
|
):
|
135
|
-
results.append({
|
135
|
+
results.append({ # noqa: PERF401
|
136
136
|
k: v for k, v in policy.items() if k in UPGRADE_POLICY_DESIRED_KEYS
|
137
137
|
})
|
138
138
|
return results
|
@@ -166,12 +166,9 @@ def create_version_agreement(
|
|
166
166
|
def get_version_agreement(
|
167
167
|
ocm_api: OCMBaseClient, cluster_id: str
|
168
168
|
) -> list[dict[str, Any]]:
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
):
|
173
|
-
agreements.append(item)
|
174
|
-
return agreements
|
169
|
+
return list(
|
170
|
+
ocm_api.get_paginated(f"{build_cluster_url(cluster_id)}/gate_agreements")
|
171
|
+
)
|
175
172
|
|
176
173
|
|
177
174
|
def get_version_gates(ocm_api: OCMBaseClient) -> list[OCMVersionGate]:
|
@@ -44,7 +44,7 @@ class OCMBaseClient:
|
|
44
44
|
self._access_token_client_id = access_token_client_id
|
45
45
|
self._access_token_url = access_token_url
|
46
46
|
self._url = url
|
47
|
-
self._session = session
|
47
|
+
self._session = session or Session()
|
48
48
|
self._init_access_token()
|
49
49
|
self._init_request_headers()
|
50
50
|
|
reconcile/utils/promtool.py
CHANGED
@@ -68,7 +68,7 @@ def _run_yaml_spec_cmd(cmd: list[str], yaml_spec: Mapping) -> CommandExecutionRe
|
|
68
68
|
try:
|
69
69
|
result = subprocess.run(cmd, capture_output=True, check=True)
|
70
70
|
except subprocess.CalledProcessError as e:
|
71
|
-
msg = f
|
71
|
+
msg = f"Error running promtool command [{' '.join(cmd)}]"
|
72
72
|
if e.stdout:
|
73
73
|
msg += f" {e.stdout.decode()}"
|
74
74
|
if e.stderr:
|
reconcile/utils/quay_api.py
CHANGED
@@ -45,9 +45,7 @@ class QuayApi:
|
|
45
45
|
body = r.json()
|
46
46
|
|
47
47
|
# Using a set because members may be repeated
|
48
|
-
members =
|
49
|
-
for member in body["members"]:
|
50
|
-
members.add(member["name"])
|
48
|
+
members = {member["name"] for member in body["members"]}
|
51
49
|
|
52
50
|
members_list = list(members)
|
53
51
|
self.team_members[team] = members_list
|