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.
Files changed (133) hide show
  1. {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/METADATA +1 -1
  2. {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/RECORD +133 -133
  3. reconcile/acs_rbac.py +2 -4
  4. reconcile/aus/base.py +13 -13
  5. reconcile/aws_ami_share.py +1 -2
  6. reconcile/aws_cloudwatch_log_retention/integration.py +1 -1
  7. reconcile/aws_saml_idp/integration.py +1 -1
  8. reconcile/aws_saml_roles/integration.py +1 -1
  9. reconcile/aws_version_sync/integration.py +3 -3
  10. reconcile/change_owners/change_owners.py +8 -5
  11. reconcile/change_owners/change_types.py +18 -18
  12. reconcile/change_owners/changes.py +8 -9
  13. reconcile/change_owners/decision.py +12 -15
  14. reconcile/change_owners/self_service_roles.py +6 -4
  15. reconcile/change_owners/tester.py +8 -10
  16. reconcile/cli.py +9 -11
  17. reconcile/closedbox_endpoint_monitoring_base.py +1 -1
  18. reconcile/cna/integration.py +2 -2
  19. reconcile/dashdotdb_base.py +2 -2
  20. reconcile/dashdotdb_cso.py +1 -1
  21. reconcile/dashdotdb_dora.py +6 -4
  22. reconcile/dashdotdb_slo.py +1 -1
  23. reconcile/database_access_manager.py +15 -19
  24. reconcile/email_sender.py +4 -8
  25. reconcile/external_resources/secrets_sync.py +2 -2
  26. reconcile/external_resources/state.py +17 -17
  27. reconcile/gabi_authorized_users.py +3 -3
  28. reconcile/gcr_mirror.py +2 -2
  29. reconcile/github_org.py +9 -13
  30. reconcile/gitlab_housekeeping.py +1 -1
  31. reconcile/gitlab_owners.py +10 -12
  32. reconcile/gitlab_permissions.py +5 -4
  33. reconcile/glitchtip/integration.py +14 -14
  34. reconcile/glitchtip_project_alerts/integration.py +3 -4
  35. reconcile/integrations_manager.py +1 -2
  36. reconcile/jenkins_job_builds_cleaner.py +7 -5
  37. reconcile/jenkins_roles.py +10 -6
  38. reconcile/jenkins_worker_fleets.py +5 -4
  39. reconcile/jira_permissions_validator.py +2 -6
  40. reconcile/ldap_groups/integration.py +3 -2
  41. reconcile/ocm_groups.py +5 -5
  42. reconcile/ocm_update_recommended_version.py +2 -2
  43. reconcile/openshift_base.py +15 -20
  44. reconcile/openshift_groups.py +9 -8
  45. reconcile/openshift_namespace_labels.py +3 -4
  46. reconcile/openshift_namespaces.py +1 -1
  47. reconcile/openshift_network_policies.py +1 -1
  48. reconcile/openshift_resources_base.py +4 -4
  49. reconcile/openshift_serviceaccount_tokens.py +1 -1
  50. reconcile/openshift_tekton_resources.py +1 -2
  51. reconcile/openshift_users.py +5 -4
  52. reconcile/prometheus_rules_tester/integration.py +8 -8
  53. reconcile/quay_mirror.py +3 -4
  54. reconcile/quay_mirror_org.py +1 -1
  55. reconcile/rhidp/ocm_oidc_idp/base.py +10 -15
  56. reconcile/run_integration.py +7 -7
  57. reconcile/saas_auto_promotions_manager/publisher.py +1 -1
  58. reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +3 -9
  59. reconcile/service_dependencies.py +2 -7
  60. reconcile/skupper_network/reconciler.py +5 -5
  61. reconcile/skupper_network/site_controller.py +3 -3
  62. reconcile/sql_query.py +5 -5
  63. reconcile/status_board.py +24 -24
  64. reconcile/terraform_cloudflare_users.py +2 -2
  65. reconcile/terraform_repo.py +6 -6
  66. reconcile/terraform_users.py +8 -5
  67. reconcile/terraform_vpc_peerings.py +1 -1
  68. reconcile/terraform_vpc_resources/integration.py +1 -1
  69. reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +1 -1
  70. reconcile/typed_queries/app_quay_repos_escalation_policies.py +1 -1
  71. reconcile/typed_queries/aws_vpc_requests.py +1 -1
  72. reconcile/typed_queries/aws_vpcs.py +1 -1
  73. reconcile/typed_queries/clusters.py +1 -1
  74. reconcile/typed_queries/clusters_minimal.py +1 -1
  75. reconcile/typed_queries/clusters_with_dms.py +1 -1
  76. reconcile/typed_queries/dynatrace_environments.py +1 -1
  77. reconcile/typed_queries/dynatrace_token_provider_token_specs.py +1 -1
  78. reconcile/typed_queries/reserved_networks.py +1 -1
  79. reconcile/typed_queries/saas_files.py +1 -1
  80. reconcile/typed_queries/slo_documents.py +1 -1
  81. reconcile/typed_queries/status_board.py +1 -2
  82. reconcile/utils/amtool.py +2 -2
  83. reconcile/utils/aws_api.py +10 -10
  84. reconcile/utils/aws_helper.py +1 -1
  85. reconcile/utils/binary.py +1 -2
  86. reconcile/utils/differ.py +4 -7
  87. reconcile/utils/dnsutils.py +4 -12
  88. reconcile/utils/external_resources.py +1 -2
  89. reconcile/utils/gitlab_api.py +2 -4
  90. reconcile/utils/glitchtip/models.py +1 -1
  91. reconcile/utils/helm.py +1 -1
  92. reconcile/utils/instrumented_wrappers.py +2 -2
  93. reconcile/utils/jjb_client.py +1 -1
  94. reconcile/utils/jump_host.py +1 -1
  95. reconcile/utils/metrics.py +6 -11
  96. reconcile/utils/mr/aws_access.py +1 -1
  97. reconcile/utils/mr/base.py +2 -4
  98. reconcile/utils/mr/notificator.py +1 -1
  99. reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py +1 -1
  100. reconcile/utils/oc.py +17 -31
  101. reconcile/utils/oc_map.py +1 -1
  102. reconcile/utils/ocm/base.py +4 -2
  103. reconcile/utils/ocm/search_filters.py +4 -3
  104. reconcile/utils/ocm/status_board.py +2 -2
  105. reconcile/utils/ocm/upgrades.py +4 -7
  106. reconcile/utils/ocm_base_client.py +1 -1
  107. reconcile/utils/openshift_resource.py +1 -1
  108. reconcile/utils/promtool.py +1 -1
  109. reconcile/utils/quay_api.py +1 -3
  110. reconcile/utils/raw_github_api.py +3 -10
  111. reconcile/utils/repo_owners.py +5 -5
  112. reconcile/utils/rest_api_base.py +1 -2
  113. reconcile/utils/rosa/rosa_cli.py +3 -3
  114. reconcile/utils/saasherder/saasherder.py +9 -15
  115. reconcile/utils/secret_reader.py +2 -2
  116. reconcile/utils/sharding.py +2 -2
  117. reconcile/utils/state.py +5 -5
  118. reconcile/utils/terraform_client.py +2 -2
  119. reconcile/utils/terrascript/cloudflare_resources.py +4 -6
  120. reconcile/utils/terrascript_aws_client.py +16 -28
  121. reconcile/utils/vault.py +2 -2
  122. reconcile/utils/vcs.py +8 -16
  123. reconcile/vault_replication.py +1 -8
  124. tools/app_interface_reporter.py +1 -1
  125. tools/cli_commands/container_images_report.py +1 -1
  126. tools/cli_commands/cost_report/view.py +4 -2
  127. tools/cli_commands/gpg_encrypt.py +1 -5
  128. tools/qontract_cli.py +14 -13
  129. tools/saas_metrics_exporter/commit_distance/channel.py +1 -1
  130. tools/saas_promotion_state/saas_promotion_state.py +1 -1
  131. tools/sd_app_sre_alert_report.py +3 -3
  132. {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/WHEEL +0 -0
  133. {qontract_reconcile-0.10.2.dev14.dist-info → qontract_reconcile-0.10.2.dev15.dist-info}/entry_points.txt +0 -0
@@ -7,6 +7,6 @@ from reconcile.utils.gql import GqlApi
7
7
 
8
8
 
9
9
  def get_networks(gql_api: GqlApi | None = None) -> list[NetworkV1]:
10
- api = gql_api if gql_api else gql.get_api()
10
+ api = gql_api or gql.get_api()
11
11
  data = query(query_func=api.query)
12
12
  return list(data.networks or [])
@@ -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 if self.name else 'default'}:{self.namespace.cluster.name}:{self.namespace.name}".encode(),
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 if gql_api else gql.get_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'Error running amtool command [{" ".join(cmd)}]'
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:
@@ -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 if region_name else session.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 if region_name else session.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
- for address in nat["NatGatewayAddresses"]:
1008
- egress_ips.add(address["PublicIp"])
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=lambda obj: obj["LastModified"], reverse=True
1700
+ objects, key=operator.itemgetter("LastModified"), reverse=True
1701
1701
  )
1702
1702
  ]
1703
1703
 
@@ -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] = _default_equal,
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] = _default_equal,
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] = _default_equal,
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.
@@ -1,17 +1,9 @@
1
1
  from dns import resolver
2
2
 
3
3
 
4
- def get_nameservers(domain):
5
- records = []
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
- records = []
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
 
@@ -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=lambda x: x["created_at"],
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'alert-{values.get("pk")}'
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'Error running helm template [{" ".join(cmd)}]'
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):
@@ -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)
@@ -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 if parameters.port else 22
39
+ self._port = parameters.port or 22
40
40
  self._identity = parameters.key
41
41
  self._init_identity_file()
42
42
 
@@ -65,8 +65,8 @@ execution_counter = Counter(
65
65
  )
66
66
 
67
67
  reconcile_time = Histogram(
68
- name="qontract_reconcile_function_" "elapsed_seconds_since_bundle_commit",
69
- documentation="Run time seconds for tracked " "functions",
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
- if metric_name.endswith("_metric"):
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
- if metric_name.endswith("_gauge"):
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
- if metric_name.endswith("_counter"):
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]:
@@ -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[4:] if self.path.startswith("data") else 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
  )
@@ -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}. " f"Reason: {gitlab_error}"
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 if labels else [DO_NOT_MERGE_HOLD]
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'ocm upgrade scheduler org updates for {self.updates_info["name"]}'
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
- def __init__(
218
- self,
219
- namespace: str,
220
- resource: OR,
221
- server: str | None,
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 = sorted(
760
+ latest_pod = max(
770
761
  finished_pods, key=lambda pod: pod["metadata"]["creationTimestamp"]
771
- )[-1]
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}" f" does not have kind {kind}"
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 " "native client",
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}]" f" is unreachable: {e}",
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
@@ -54,7 +54,7 @@ class OCMap:
54
54
  self._init_api_resources = init_api_resources
55
55
  self._lock = Lock()
56
56
  self._jh_ports: dict[str, int] = {}
57
- self._oc_cls = oc_cls if oc_cls else OC
57
+ self._oc_cls = oc_cls or OC
58
58
 
59
59
  threaded.run(
60
60
  self._init_oc_client,
@@ -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
- for instance_iam_role in (self.sts.instance_iam_roles or {}).values():
170
- roles.append(instance_iam_role)
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
- for condition in sorted(self.conditions, key=lambda c: c.key):
360
- rendered_conditions.append(condition.render())
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
@@ -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
- agreements = []
170
- for item in ocm_api.get_paginated(
171
- f"{build_cluster_url(cluster_id)}/gate_agreements"
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 if session else Session()
47
+ self._session = session or Session()
48
48
  self._init_access_token()
49
49
  self._init_request_headers()
50
50
 
@@ -51,7 +51,7 @@ CONTROLLER_MANAGED_LABELS: dict[str, set[str | re.Pattern]] = {
51
51
  "clusterID",
52
52
  "managed-by",
53
53
  "openshiftVersion",
54
- re.compile("feature.open-cluster-management.io/.*"),
54
+ re.compile(r"feature.open-cluster-management.io/.*"),
55
55
  }
56
56
  }
57
57
 
@@ -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'Error running promtool command [{" ".join(cmd)}]'
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:
@@ -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 = set()
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