qontract-reconcile 0.10.1rc884__py3-none-any.whl → 0.10.1rc886__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.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/RECORD +279 -276
- reconcile/acs_rbac.py +1 -2
- reconcile/aus/advanced_upgrade_service.py +14 -14
- reconcile/aus/aus_label_source.py +1 -2
- reconcile/aus/base.py +23 -26
- reconcile/aus/cluster_version_data.py +4 -4
- reconcile/aus/models.py +2 -3
- reconcile/aus/version_gate_approver.py +2 -6
- reconcile/aus/version_gates/__init__.py +1 -3
- reconcile/aus/version_gates/sts_version_gate_handler.py +2 -3
- reconcile/aws_account_manager/integration.py +2 -2
- reconcile/aws_ami_cleanup/integration.py +3 -4
- reconcile/aws_iam_password_reset.py +2 -5
- reconcile/aws_version_sync/integration.py +2 -2
- reconcile/blackbox_exporter_endpoint_monitoring.py +2 -5
- reconcile/change_owners/approver.py +4 -5
- reconcile/change_owners/bundle.py +20 -22
- reconcile/change_owners/change_types.py +23 -24
- reconcile/change_owners/changes.py +13 -16
- reconcile/change_owners/decision.py +2 -5
- reconcile/change_owners/diff.py +11 -15
- reconcile/change_owners/self_service_roles.py +1 -2
- reconcile/change_owners/tester.py +7 -10
- reconcile/checkpoint.py +2 -5
- reconcile/cli.py +9 -12
- reconcile/closedbox_endpoint_monitoring_base.py +8 -11
- reconcile/cluster_deployment_mapper.py +2 -5
- reconcile/cna/assets/asset.py +4 -7
- reconcile/cna/assets/null.py +2 -5
- reconcile/cna/integration.py +2 -3
- reconcile/cna/state.py +2 -5
- reconcile/dashdotdb_base.py +8 -11
- reconcile/dashdotdb_cso.py +3 -6
- reconcile/dashdotdb_dora.py +10 -14
- reconcile/dashdotdb_dvo.py +10 -13
- reconcile/dashdotdb_slo.py +5 -8
- reconcile/database_access_manager.py +5 -6
- reconcile/dynatrace_token_provider/integration.py +2 -5
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/manager.py +4 -4
- reconcile/external_resources/model.py +3 -3
- reconcile/external_resources/secrets_sync.py +5 -5
- reconcile/external_resources/state.py +5 -5
- reconcile/gabi_authorized_users.py +3 -6
- reconcile/gcr_mirror.py +1 -1
- reconcile/github_org.py +1 -3
- reconcile/github_repo_invites.py +2 -5
- reconcile/gitlab_housekeeping.py +7 -11
- reconcile/gitlab_labeler.py +1 -2
- reconcile/gitlab_members.py +2 -5
- reconcile/gitlab_permissions.py +1 -3
- reconcile/glitchtip/integration.py +2 -5
- reconcile/glitchtip_project_alerts/integration.py +3 -6
- reconcile/glitchtip_project_dsn/integration.py +4 -7
- reconcile/integrations_manager.py +5 -8
- reconcile/jenkins/types.py +5 -6
- reconcile/jenkins_job_builder.py +9 -12
- reconcile/jenkins_roles.py +1 -1
- reconcile/jira_watcher.py +2 -2
- reconcile/ldap_groups/integration.py +2 -5
- reconcile/ocm/types.py +21 -26
- reconcile/ocm_addons_upgrade_tests_trigger.py +3 -6
- reconcile/ocm_clusters.py +8 -8
- reconcile/ocm_internal_notifications/integration.py +1 -2
- reconcile/ocm_labels/integration.py +2 -5
- reconcile/ocm_machine_pools.py +11 -15
- reconcile/ocm_upgrade_scheduler_org_updater.py +2 -5
- reconcile/openshift_base.py +27 -29
- reconcile/openshift_groups.py +15 -20
- reconcile/openshift_namespace_labels.py +8 -14
- reconcile/openshift_namespaces.py +5 -8
- reconcile/openshift_network_policies.py +2 -4
- reconcile/openshift_resources_base.py +19 -29
- reconcile/openshift_saas_deploy.py +9 -10
- reconcile/openshift_saas_deploy_change_tester.py +7 -10
- reconcile/openshift_saas_deploy_trigger_base.py +4 -7
- reconcile/openshift_saas_deploy_trigger_cleaner.py +5 -8
- reconcile/openshift_saas_deploy_trigger_configs.py +1 -2
- reconcile/openshift_saas_deploy_trigger_images.py +1 -2
- reconcile/openshift_saas_deploy_trigger_moving_commits.py +1 -2
- reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +1 -2
- reconcile/openshift_tekton_resources.py +7 -11
- reconcile/openshift_upgrade_watcher.py +10 -13
- reconcile/openshift_users.py +8 -11
- reconcile/oum/base.py +3 -4
- reconcile/oum/labelset.py +1 -2
- reconcile/oum/metrics.py +2 -2
- reconcile/oum/models.py +1 -2
- reconcile/oum/standalone.py +2 -3
- reconcile/prometheus_rules_tester/integration.py +6 -9
- reconcile/quay_membership.py +1 -2
- reconcile/quay_mirror.py +12 -13
- reconcile/quay_mirror_org.py +10 -10
- reconcile/queries.py +4 -7
- reconcile/resource_scraper.py +3 -4
- reconcile/rhidp/common.py +2 -2
- reconcile/saas_auto_promotions_manager/integration.py +5 -6
- reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py +1 -2
- reconcile/saas_auto_promotions_manager/publisher.py +5 -6
- reconcile/saas_auto_promotions_manager/subscriber.py +3 -4
- reconcile/saas_file_validator.py +2 -5
- reconcile/signalfx_endpoint_monitoring.py +2 -5
- reconcile/skupper_network/integration.py +3 -6
- reconcile/skupper_network/models.py +3 -5
- reconcile/slack_base.py +4 -7
- reconcile/slack_usergroups.py +15 -17
- reconcile/sql_query.py +5 -9
- reconcile/status_board.py +4 -5
- reconcile/statuspage/atlassian.py +14 -15
- reconcile/statuspage/integrations/maintenances.py +3 -3
- reconcile/statuspage/page.py +8 -8
- reconcile/statuspage/state.py +4 -5
- reconcile/statuspage/status.py +7 -8
- reconcile/templating/lib/rendering.py +8 -8
- reconcile/templating/renderer.py +10 -11
- reconcile/templating/validator.py +4 -4
- reconcile/terraform_aws_route53.py +3 -6
- reconcile/terraform_cloudflare_dns.py +9 -12
- reconcile/terraform_cloudflare_resources.py +9 -11
- reconcile/terraform_cloudflare_users.py +8 -11
- reconcile/terraform_init/integration.py +2 -2
- reconcile/terraform_repo.py +11 -14
- reconcile/terraform_resources.py +20 -21
- reconcile/terraform_tgw_attachments.py +32 -36
- reconcile/terraform_users.py +6 -7
- reconcile/terraform_vpc_resources/integration.py +5 -5
- reconcile/test/conftest.py +7 -10
- reconcile/test/fixtures.py +1 -1
- reconcile/test/saas_auto_promotions_manager/conftest.py +2 -2
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +2 -2
- reconcile/test/test_database_access_manager.py +3 -6
- reconcile/test/test_gitlab_labeler.py +2 -5
- reconcile/test/test_jump_host.py +5 -8
- reconcile/test/test_ocm_machine_pools.py +1 -4
- reconcile/test/test_openshift_base.py +3 -6
- reconcile/test/test_openshift_cluster_bots.py +5 -5
- reconcile/test/test_openshift_namespace_labels.py +2 -3
- reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +2 -2
- reconcile/test/test_saasherder.py +9 -12
- reconcile/test/test_slack_base.py +4 -6
- reconcile/test/test_status_board.py +4 -7
- reconcile/test/test_terraform_tgw_attachments.py +14 -20
- reconcile/typed_queries/alerting_services_settings.py +1 -2
- reconcile/typed_queries/app_interface_custom_messages.py +2 -3
- reconcile/typed_queries/app_interface_deadmanssnitch_settings.py +1 -3
- reconcile/typed_queries/app_interface_repo_url.py +1 -2
- reconcile/typed_queries/app_interface_state_settings.py +1 -3
- reconcile/typed_queries/app_interface_vault_settings.py +1 -2
- reconcile/typed_queries/aws_vpc_requests.py +1 -3
- reconcile/typed_queries/aws_vpcs.py +1 -3
- reconcile/typed_queries/clusters.py +2 -4
- reconcile/typed_queries/clusters_minimal.py +1 -3
- reconcile/typed_queries/clusters_with_dms.py +1 -3
- reconcile/typed_queries/external_resources.py +3 -4
- reconcile/typed_queries/pagerduty_instances.py +1 -2
- reconcile/typed_queries/repos.py +2 -3
- reconcile/typed_queries/reserved_networks.py +1 -3
- reconcile/typed_queries/saas_files.py +49 -59
- reconcile/typed_queries/slo_documents.py +1 -3
- reconcile/typed_queries/status_board.py +3 -7
- reconcile/typed_queries/tekton_pipeline_providers.py +1 -2
- reconcile/typed_queries/terraform_namespaces.py +1 -2
- reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py +1 -3
- reconcile/utils/acs/base.py +2 -3
- reconcile/utils/acs/notifiers.py +3 -3
- reconcile/utils/acs/policies.py +3 -3
- reconcile/utils/aggregated_list.py +1 -1
- reconcile/utils/amtool.py +1 -2
- reconcile/utils/aws_api.py +28 -31
- reconcile/utils/binary.py +1 -3
- reconcile/utils/clusterhealth/providerbase.py +1 -2
- reconcile/utils/clusterhealth/telemeter.py +2 -2
- reconcile/utils/deadmanssnitch_api.py +1 -2
- reconcile/utils/disabled_integrations.py +4 -6
- reconcile/utils/environ.py +1 -1
- reconcile/utils/expiration.py +3 -7
- reconcile/utils/external_resource_spec.py +3 -4
- reconcile/utils/external_resources.py +4 -7
- reconcile/utils/filtering.py +1 -2
- reconcile/utils/git.py +3 -9
- reconcile/utils/git_secrets.py +5 -5
- reconcile/utils/github_api.py +5 -9
- reconcile/utils/gitlab_api.py +2 -3
- reconcile/utils/glitchtip/client.py +2 -4
- reconcile/utils/glitchtip/models.py +8 -11
- reconcile/utils/gql.py +26 -35
- reconcile/utils/grouping.py +1 -3
- reconcile/utils/imap_client.py +2 -5
- reconcile/utils/internal_groups/client.py +1 -2
- reconcile/utils/internal_groups/models.py +8 -9
- reconcile/utils/jenkins_api.py +4 -4
- reconcile/utils/jinja2/extensions.py +1 -1
- reconcile/utils/jinja2/filters.py +4 -4
- reconcile/utils/jinja2/utils.py +16 -16
- reconcile/utils/jira_client.py +10 -11
- reconcile/utils/jjb_client.py +14 -17
- reconcile/utils/jobcontroller/controller.py +5 -5
- reconcile/utils/jobcontroller/models.py +2 -2
- reconcile/utils/jsonpath.py +4 -5
- reconcile/utils/jump_host.py +7 -8
- reconcile/utils/keycloak.py +3 -7
- reconcile/utils/ldap_client.py +2 -3
- reconcile/utils/lean_terraform_client.py +13 -17
- reconcile/utils/membershipsources/app_interface_resolver.py +1 -1
- reconcile/utils/membershipsources/models.py +19 -22
- reconcile/utils/metrics.py +13 -15
- reconcile/utils/mr/base.py +7 -11
- reconcile/utils/mr/glitchtip_access_reporter.py +2 -2
- reconcile/utils/mr/notificator.py +1 -2
- reconcile/utils/oc.py +32 -38
- reconcile/utils/oc_connection_parameters.py +24 -25
- reconcile/utils/oc_filters.py +2 -3
- reconcile/utils/oc_map.py +9 -15
- reconcile/utils/ocm/addons.py +7 -10
- reconcile/utils/ocm/base.py +38 -39
- reconcile/utils/ocm/clusters.py +6 -9
- reconcile/utils/ocm/label_sources.py +1 -2
- reconcile/utils/ocm/labels.py +3 -6
- reconcile/utils/ocm/ocm.py +11 -14
- reconcile/utils/ocm/products.py +1 -3
- reconcile/utils/ocm/search_filters.py +16 -17
- reconcile/utils/ocm/service_log.py +2 -3
- reconcile/utils/ocm/sre_capability_labels.py +4 -8
- reconcile/utils/ocm/subscriptions.py +1 -3
- reconcile/utils/ocm/syncsets.py +2 -4
- reconcile/utils/ocm/upgrades.py +5 -9
- reconcile/utils/ocm_base_client.py +13 -16
- reconcile/utils/openshift_resource.py +5 -11
- reconcile/utils/output.py +2 -3
- reconcile/utils/pagerduty_api.py +4 -5
- reconcile/utils/prometheus.py +2 -2
- reconcile/utils/promotion_state.py +4 -5
- reconcile/utils/promtool.py +2 -8
- reconcile/utils/quay_api.py +12 -22
- reconcile/utils/raw_github_api.py +3 -5
- reconcile/utils/rosa/rosa_cli.py +6 -6
- reconcile/utils/rosa/session.py +6 -7
- reconcile/utils/runtime/desired_state_diff.py +3 -8
- reconcile/utils/runtime/environment.py +4 -7
- reconcile/utils/runtime/integration.py +4 -4
- reconcile/utils/runtime/meta.py +1 -2
- reconcile/utils/runtime/runner.py +7 -10
- reconcile/utils/runtime/sharding.py +22 -27
- reconcile/utils/saasherder/interfaces.py +63 -69
- reconcile/utils/saasherder/models.py +30 -35
- reconcile/utils/saasherder/saasherder.py +37 -53
- reconcile/utils/secret_reader.py +17 -19
- reconcile/utils/slack_api.py +15 -17
- reconcile/utils/smtp_client.py +1 -2
- reconcile/utils/sqs_gateway.py +1 -3
- reconcile/utils/state.py +1 -2
- reconcile/utils/terraform/config_client.py +4 -5
- reconcile/utils/terraform_client.py +3 -8
- reconcile/utils/terrascript/cloudflare_client.py +4 -10
- reconcile/utils/terrascript/cloudflare_resources.py +10 -13
- reconcile/utils/terrascript/models.py +2 -3
- reconcile/utils/terrascript/resources.py +1 -2
- reconcile/utils/terrascript_aws_client.py +30 -38
- reconcile/utils/unleash/client.py +4 -7
- reconcile/utils/unleash/server.py +2 -2
- reconcile/utils/vault.py +8 -11
- reconcile/utils/vaultsecretref.py +2 -3
- reconcile/utils/vcs.py +7 -8
- reconcile/vault_replication.py +4 -8
- reconcile/vpc_peerings_validator.py +4 -9
- release/version.py +6 -7
- tools/app_interface_reporter.py +2 -2
- tools/cli_commands/gpg_encrypt.py +3 -6
- tools/cli_commands/systems_and_tools.py +4 -7
- tools/qontract_cli.py +31 -17
- tools/saas_promotion_state/__init__.py +0 -0
- tools/saas_promotion_state/saas_promotion_state.py +72 -0
- tools/template_validation.py +1 -1
- tools/test/conftest.py +45 -6
- tools/test/test_saas_promotion_state.py +86 -0
- {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc884.dist-info → qontract_reconcile-0.10.1rc886.dist-info}/top_level.txt +0 -0
reconcile/utils/prometheus.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import NamedTuple,
|
1
|
+
from typing import NamedTuple, Protocol
|
2
2
|
|
3
3
|
import requests
|
4
4
|
from pydantic import BaseModel, Field
|
@@ -41,7 +41,7 @@ class PrometheusVector(BaseModel):
|
|
41
41
|
def mandatory_label(self, label_name: str) -> str:
|
42
42
|
return self.metric[label_name]
|
43
43
|
|
44
|
-
def label(self, label_name: str, default:
|
44
|
+
def label(self, label_name: str, default: str | None = None) -> str | None:
|
45
45
|
return self.metric.get(label_name, default)
|
46
46
|
|
47
47
|
|
@@ -1,6 +1,5 @@
|
|
1
1
|
import logging
|
2
2
|
from collections import defaultdict
|
3
|
-
from typing import Optional
|
4
3
|
|
5
4
|
from pydantic import (
|
6
5
|
BaseModel,
|
@@ -20,9 +19,9 @@ class PromotionData(BaseModel):
|
|
20
19
|
"""
|
21
20
|
|
22
21
|
success: bool
|
23
|
-
target_config_hash:
|
24
|
-
saas_file:
|
25
|
-
check_in:
|
22
|
+
target_config_hash: str | None
|
23
|
+
saas_file: str | None
|
24
|
+
check_in: str | None
|
26
25
|
|
27
26
|
class Config:
|
28
27
|
smart_union = True
|
@@ -71,7 +70,7 @@ class PromotionState:
|
|
71
70
|
target_uid: str = "",
|
72
71
|
pre_check_sha_exists: bool = True,
|
73
72
|
use_cache: bool = False,
|
74
|
-
) ->
|
73
|
+
) -> PromotionData | None:
|
75
74
|
"""
|
76
75
|
Fetch promotion data from S3.
|
77
76
|
|
reconcile/utils/promtool.py
CHANGED
@@ -2,11 +2,7 @@ import copy
|
|
2
2
|
import os
|
3
3
|
import subprocess
|
4
4
|
import tempfile
|
5
|
-
from
|
6
|
-
Iterable,
|
7
|
-
Mapping,
|
8
|
-
MutableMapping,
|
9
|
-
)
|
5
|
+
from collections.abc import Iterable, Mapping, MutableMapping
|
10
6
|
|
11
7
|
import yaml
|
12
8
|
|
@@ -70,9 +66,7 @@ def _run_yaml_spec_cmd(cmd: list[str], yaml_spec: Mapping) -> CommandExecutionRe
|
|
70
66
|
return CommandExecutionResult(False, f"Error creating temporary file: {e}")
|
71
67
|
|
72
68
|
try:
|
73
|
-
result = subprocess.run(
|
74
|
-
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True
|
75
|
-
)
|
69
|
+
result = subprocess.run(cmd, capture_output=True, check=True)
|
76
70
|
except subprocess.CalledProcessError as e:
|
77
71
|
msg = f'Error running promtool command [{" ".join(cmd)}]'
|
78
72
|
if e.stdout:
|
reconcile/utils/quay_api.py
CHANGED
@@ -30,9 +30,7 @@ class QuayApi:
|
|
30
30
|
if cache_members:
|
31
31
|
return cache_members
|
32
32
|
|
33
|
-
url = "{}/organization/{}/team/{}/members?includePending=true"
|
34
|
-
self.api_url, self.organization, team
|
35
|
-
)
|
33
|
+
url = f"{self.api_url}/organization/{self.organization}/team/{team}/members?includePending=true"
|
36
34
|
|
37
35
|
r = requests.get(url, headers=self.auth_header, timeout=self._timeout)
|
38
36
|
if r.status_code == 404:
|
@@ -57,7 +55,7 @@ class QuayApi:
|
|
57
55
|
return members_list
|
58
56
|
|
59
57
|
def user_exists(self, user):
|
60
|
-
url = "{}/users/{}"
|
58
|
+
url = f"{self.api_url}/users/{user}"
|
61
59
|
r = requests.get(url, headers=self.auth_header, timeout=self._timeout)
|
62
60
|
if not r.ok:
|
63
61
|
return False
|
@@ -68,22 +66,18 @@ class QuayApi:
|
|
68
66
|
|
69
67
|
:raises HTTPError if there are any problems with the request
|
70
68
|
"""
|
71
|
-
url_team = "{}/organization/{}/team/{}/members/{}"
|
72
|
-
self.api_url, self.organization, team, user
|
73
|
-
)
|
69
|
+
url_team = f"{self.api_url}/organization/{self.organization}/team/{team}/members/{user}"
|
74
70
|
|
75
71
|
r = requests.delete(url_team, headers=self.auth_header, timeout=self._timeout)
|
76
72
|
if not r.ok:
|
77
73
|
message = r.json().get("message", "")
|
78
74
|
|
79
|
-
expected_message = "User {} does not belong to team {}"
|
75
|
+
expected_message = f"User {user} does not belong to team {team}"
|
80
76
|
|
81
77
|
if message != expected_message:
|
82
78
|
r.raise_for_status()
|
83
79
|
|
84
|
-
url_org = "{}/organization/{}/members/{}"
|
85
|
-
self.api_url, self.organization, user
|
86
|
-
)
|
80
|
+
url_org = f"{self.api_url}/organization/{self.organization}/members/{user}"
|
87
81
|
|
88
82
|
r = requests.delete(url_org, headers=self.auth_header, timeout=self._timeout)
|
89
83
|
r.raise_for_status()
|
@@ -98,9 +92,7 @@ class QuayApi:
|
|
98
92
|
if user in self.list_team_members(team, cache=True):
|
99
93
|
return True
|
100
94
|
|
101
|
-
url = "{}/organization/{}/team/{}/members/{}"
|
102
|
-
self.api_url, self.organization, team, user
|
103
|
-
)
|
95
|
+
url = f"{self.api_url}/organization/{self.organization}/team/{team}/members/{user}"
|
104
96
|
r = requests.put(url, headers=self.auth_header, timeout=self._timeout)
|
105
97
|
r.raise_for_status()
|
106
98
|
return True
|
@@ -117,7 +109,7 @@ class QuayApi:
|
|
117
109
|
:raises HTTPError: unsuccessful attempt to create the team
|
118
110
|
"""
|
119
111
|
|
120
|
-
url = "{}/organization/{}/team/{}"
|
112
|
+
url = f"{self.api_url}/organization/{self.organization}/team/{team}"
|
121
113
|
|
122
114
|
payload = {"role": role}
|
123
115
|
|
@@ -140,7 +132,7 @@ class QuayApi:
|
|
140
132
|
if count > self.LIMIT_FOLLOWS:
|
141
133
|
raise ValueError("Too many page follows")
|
142
134
|
|
143
|
-
url = "{}/repository"
|
135
|
+
url = f"{self.api_url}/repository"
|
144
136
|
|
145
137
|
# params
|
146
138
|
params = {"namespace": self.organization}
|
@@ -176,7 +168,7 @@ class QuayApi:
|
|
176
168
|
"""
|
177
169
|
visibility = "public" if public else "private"
|
178
170
|
|
179
|
-
url = "{}/repository"
|
171
|
+
url = f"{self.api_url}/repository"
|
180
172
|
|
181
173
|
params = {
|
182
174
|
"repo_kind": "image",
|
@@ -193,14 +185,14 @@ class QuayApi:
|
|
193
185
|
r.raise_for_status()
|
194
186
|
|
195
187
|
def repo_delete(self, repo_name):
|
196
|
-
url = "{}/repository/{}/{}"
|
188
|
+
url = f"{self.api_url}/repository/{self.organization}/{repo_name}"
|
197
189
|
|
198
190
|
# perform request
|
199
191
|
r = requests.delete(url, headers=self.auth_header, timeout=self._timeout)
|
200
192
|
r.raise_for_status()
|
201
193
|
|
202
194
|
def repo_update_description(self, repo_name, description):
|
203
|
-
url = "{}/repository/{}/{}"
|
195
|
+
url = f"{self.api_url}/repository/{self.organization}/{repo_name}"
|
204
196
|
|
205
197
|
params = {"description": description}
|
206
198
|
|
@@ -217,9 +209,7 @@ class QuayApi:
|
|
217
209
|
self._repo_change_visibility(repo_name, "private")
|
218
210
|
|
219
211
|
def _repo_change_visibility(self, repo_name, visibility):
|
220
|
-
url = "{}/repository/{}/{}/changevisibility"
|
221
|
-
self.api_url, self.organization, repo_name
|
222
|
-
)
|
212
|
+
url = f"{self.api_url}/repository/{self.organization}/{repo_name}/changevisibility"
|
223
213
|
|
224
214
|
params = {"visibility": visibility}
|
225
215
|
|
@@ -72,7 +72,7 @@ class RawGithubApi:
|
|
72
72
|
return result
|
73
73
|
|
74
74
|
def org_invitations(self, org):
|
75
|
-
invitations = self.query("/orgs/{}/invitations"
|
75
|
+
invitations = self.query(f"/orgs/{org}/invitations")
|
76
76
|
|
77
77
|
return [
|
78
78
|
login
|
@@ -81,9 +81,7 @@ class RawGithubApi:
|
|
81
81
|
]
|
82
82
|
|
83
83
|
def team_invitations(self, org_id, team_id):
|
84
|
-
invitations = self.query(
|
85
|
-
"/organizations/{}/team/{}/invitations".format(org_id, team_id)
|
86
|
-
)
|
84
|
+
invitations = self.query(f"/organizations/{org_id}/team/{team_id}/invitations")
|
87
85
|
|
88
86
|
return [
|
89
87
|
login
|
@@ -95,6 +93,6 @@ class RawGithubApi:
|
|
95
93
|
return self.query("/user/repository_invitations")
|
96
94
|
|
97
95
|
def accept_repo_invitation(self, invitation_id):
|
98
|
-
url = self.BASE_URL + "/user/repository_invitations/{}"
|
96
|
+
url = self.BASE_URL + f"/user/repository_invitations/{invitation_id}"
|
99
97
|
res = self.patch(url)
|
100
98
|
res.raise_for_status()
|
reconcile/utils/rosa/rosa_cli.py
CHANGED
@@ -2,8 +2,8 @@ import collections
|
|
2
2
|
import itertools
|
3
3
|
import os
|
4
4
|
import textwrap
|
5
|
-
from collections.abc import Iterable
|
6
|
-
from typing import Any
|
5
|
+
from collections.abc import Callable, Iterable
|
6
|
+
from typing import Any
|
7
7
|
|
8
8
|
from kubernetes.client import (
|
9
9
|
V1Container,
|
@@ -42,13 +42,13 @@ class LogHandle:
|
|
42
42
|
) -> Iterable[str]:
|
43
43
|
if max_lines <= 0:
|
44
44
|
return []
|
45
|
-
with open(self.log_file,
|
45
|
+
with open(self.log_file, encoding="utf-8") as f:
|
46
46
|
if from_file_end:
|
47
47
|
return collections.deque(f, maxlen=max_lines)
|
48
48
|
return [line.rstrip() for line in itertools.islice(f, max_lines)]
|
49
49
|
|
50
50
|
def write_logs_to_logger(self, logger: Callable[..., None]) -> None:
|
51
|
-
with open(self.log_file,
|
51
|
+
with open(self.log_file, encoding="utf-8") as f:
|
52
52
|
logger(f.read())
|
53
53
|
|
54
54
|
def exists(self) -> bool:
|
@@ -67,7 +67,7 @@ class RosaCliResult:
|
|
67
67
|
self,
|
68
68
|
status: JobStatus,
|
69
69
|
command: str,
|
70
|
-
log_handle:
|
70
|
+
log_handle: LogHandle | None = None,
|
71
71
|
) -> None:
|
72
72
|
self.status = status
|
73
73
|
self.command = command
|
@@ -100,7 +100,7 @@ class RosaCliException(Exception, RosaCliResult):
|
|
100
100
|
self,
|
101
101
|
status: JobStatus,
|
102
102
|
command: str,
|
103
|
-
log_handle:
|
103
|
+
log_handle: LogHandle | None = None,
|
104
104
|
) -> None:
|
105
105
|
Exception.__init__(
|
106
106
|
self, f"ROSA CLI execution failed with status: {status}, cmd: {command}"
|
reconcile/utils/rosa/session.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import logging
|
2
2
|
import tempfile
|
3
|
-
from typing import Optional
|
4
3
|
|
5
4
|
from jinja2 import Environment, FileSystemLoader
|
6
5
|
from pydantic import BaseModel
|
@@ -53,8 +52,8 @@ class RosaSession:
|
|
53
52
|
ocm_org_id: str,
|
54
53
|
ocm_api: OCMBaseClient,
|
55
54
|
job_controller: K8sJobController,
|
56
|
-
image:
|
57
|
-
service_account:
|
55
|
+
image: str | None = None,
|
56
|
+
service_account: str | None = None,
|
58
57
|
):
|
59
58
|
self.aws_account_id = aws_account_id
|
60
59
|
self.aws_region = aws_region
|
@@ -68,8 +67,8 @@ class RosaSession:
|
|
68
67
|
def assemble_job(
|
69
68
|
self,
|
70
69
|
cmd: str,
|
71
|
-
annotations:
|
72
|
-
image:
|
70
|
+
annotations: dict[str, str] | None = None,
|
71
|
+
image: str | None = None,
|
73
72
|
) -> RosaJob:
|
74
73
|
return RosaJob(
|
75
74
|
aws_account_id=self.aws_account_id,
|
@@ -86,8 +85,8 @@ class RosaSession:
|
|
86
85
|
def cli_execute(
|
87
86
|
self,
|
88
87
|
cmd: str,
|
89
|
-
annotations:
|
90
|
-
image:
|
88
|
+
annotations: dict[str, str] | None = None,
|
89
|
+
image: str | None = None,
|
91
90
|
check_interval_seconds: int = 5,
|
92
91
|
timeout_seconds: int = 60,
|
93
92
|
) -> RosaCliResult:
|
@@ -1,13 +1,8 @@
|
|
1
1
|
import logging
|
2
2
|
import multiprocessing
|
3
|
+
from collections.abc import Callable, Iterable, Mapping
|
3
4
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
5
|
-
Any,
|
6
|
-
Callable,
|
7
|
-
Iterable,
|
8
|
-
Mapping,
|
9
|
-
Optional,
|
10
|
-
)
|
5
|
+
from typing import Any
|
11
6
|
|
12
7
|
from deepdiff import DeepHash
|
13
8
|
from jsonpath_ng.ext.parser import parse
|
@@ -177,7 +172,7 @@ def extract_diffs_with_timeout(
|
|
177
172
|
|
178
173
|
|
179
174
|
def build_desired_state_diff(
|
180
|
-
sharding_config:
|
175
|
+
sharding_config: DesiredStateShardConfig | None,
|
181
176
|
previous_desired_state: Mapping[str, Any],
|
182
177
|
current_desired_state: Mapping[str, Any],
|
183
178
|
) -> DesiredStateDiff:
|
@@ -1,7 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import os
|
3
3
|
import sys
|
4
|
-
from typing import Optional
|
5
4
|
|
6
5
|
from reconcile.utils import (
|
7
6
|
config,
|
@@ -19,9 +18,7 @@ DRY_RUN_MAP = {
|
|
19
18
|
}
|
20
19
|
|
21
20
|
|
22
|
-
def log_fmt(
|
23
|
-
dry_run: Optional[bool] = None, dry_run_option: Optional[str] = None
|
24
|
-
) -> str:
|
21
|
+
def log_fmt(dry_run: bool | None = None, dry_run_option: str | None = None) -> str:
|
25
22
|
if dry_run and dry_run_option:
|
26
23
|
raise ValueError("Please set either dry_run or dry_run_option.")
|
27
24
|
|
@@ -46,9 +43,9 @@ def log_fmt(
|
|
46
43
|
|
47
44
|
|
48
45
|
def init_env(
|
49
|
-
log_level:
|
50
|
-
config_file:
|
51
|
-
dry_run:
|
46
|
+
log_level: str | None = None,
|
47
|
+
config_file: str | None = None,
|
48
|
+
dry_run: bool | None = None,
|
52
49
|
print_gql_url: bool = True,
|
53
50
|
) -> None:
|
54
51
|
# store env configs in environment variables. this way child processes
|
@@ -2,11 +2,11 @@ from abc import (
|
|
2
2
|
ABC,
|
3
3
|
abstractmethod,
|
4
4
|
)
|
5
|
+
from collections.abc import Callable
|
5
6
|
from dataclasses import dataclass
|
6
7
|
from types import ModuleType
|
7
8
|
from typing import (
|
8
9
|
Any,
|
9
|
-
Callable,
|
10
10
|
Generic,
|
11
11
|
Optional,
|
12
12
|
TypeVar,
|
@@ -153,7 +153,7 @@ class QontractReconcileIntegration(ABC, Generic[RunParamsTypeVar]):
|
|
153
153
|
|
154
154
|
def __init__(self, params: RunParamsTypeVar) -> None:
|
155
155
|
self.params: RunParamsTypeVar = params
|
156
|
-
self._secret_reader:
|
156
|
+
self._secret_reader: SecretReaderBase | None = None
|
157
157
|
|
158
158
|
@property
|
159
159
|
@abstractmethod
|
@@ -169,7 +169,7 @@ class QontractReconcileIntegration(ABC, Generic[RunParamsTypeVar]):
|
|
169
169
|
self._secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
170
170
|
return self._secret_reader
|
171
171
|
|
172
|
-
def get_early_exit_desired_state(self) ->
|
172
|
+
def get_early_exit_desired_state(self) -> dict[str, Any] | None:
|
173
173
|
"""
|
174
174
|
An integration that wants to support early exit on its desired state
|
175
175
|
must implement this method and return the desired state as a dictionary.
|
@@ -299,7 +299,7 @@ class ModuleBasedQontractReconcileIntegration(
|
|
299
299
|
return self.params.module.QONTRACT_INTEGRATION.replace("_", "-")
|
300
300
|
raise NotImplementedError("Integration missing QONTRACT_INTEGRATION.")
|
301
301
|
|
302
|
-
def get_early_exit_desired_state(self) ->
|
302
|
+
def get_early_exit_desired_state(self) -> dict[str, Any] | None:
|
303
303
|
if self._integration_supports(EARLY_EXIT_DESIRED_STATE_FUNCTION):
|
304
304
|
return self.params.module.early_exit_desired_state(
|
305
305
|
*self.params.args, **self.params.kwargs
|
reconcile/utils/runtime/meta.py
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
import dataclasses
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
4
3
|
|
5
4
|
|
6
5
|
@dataclass
|
7
6
|
class IntegrationMeta:
|
8
7
|
name: str
|
9
8
|
args: list[str]
|
10
|
-
short_help:
|
9
|
+
short_help: str | None
|
11
10
|
|
12
11
|
def to_dict(self):
|
13
12
|
return dataclasses.asdict(self)
|
@@ -3,7 +3,6 @@ import sys
|
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import (
|
5
5
|
Any,
|
6
|
-
Optional,
|
7
6
|
TypeVar,
|
8
7
|
)
|
9
8
|
|
@@ -44,7 +43,7 @@ class IntegrationRunConfiguration:
|
|
44
43
|
potential problems with the integration or the configuration data from app-interface.
|
45
44
|
"""
|
46
45
|
|
47
|
-
early_exit_compare_sha:
|
46
|
+
early_exit_compare_sha: str | None
|
48
47
|
"""
|
49
48
|
The SHA of the bundle to compare the current desired state against.
|
50
49
|
"""
|
@@ -66,11 +65,11 @@ class IntegrationRunConfiguration:
|
|
66
65
|
A debug flag to control whether the URL of the GraphQL endpoint in use is printed.
|
67
66
|
"""
|
68
67
|
|
69
|
-
def main_bundle_desired_state(self) ->
|
68
|
+
def main_bundle_desired_state(self) -> dict[str, Any] | None:
|
70
69
|
self.switch_to_main_bundle()
|
71
70
|
return self.integration.get_early_exit_desired_state()
|
72
71
|
|
73
|
-
def comparison_bundle_desired_state(self) ->
|
72
|
+
def comparison_bundle_desired_state(self) -> dict[str, Any] | None:
|
74
73
|
self.switch_to_comparison_bundle()
|
75
74
|
data = ( # pylint: disable=assignment-from-none
|
76
75
|
self.integration.get_early_exit_desired_state()
|
@@ -78,7 +77,7 @@ class IntegrationRunConfiguration:
|
|
78
77
|
self.switch_to_main_bundle()
|
79
78
|
return data
|
80
79
|
|
81
|
-
def switch_to_main_bundle(self, validate_schemas:
|
80
|
+
def switch_to_main_bundle(self, validate_schemas: bool | None = None) -> None:
|
82
81
|
final_validate_schemas = (
|
83
82
|
validate_schemas if validate_schemas is not None else self.valdiate_schemas
|
84
83
|
)
|
@@ -89,9 +88,7 @@ class IntegrationRunConfiguration:
|
|
89
88
|
print_url=self.print_url,
|
90
89
|
)
|
91
90
|
|
92
|
-
def switch_to_comparison_bundle(
|
93
|
-
self, validate_schemas: Optional[bool] = None
|
94
|
-
) -> None:
|
91
|
+
def switch_to_comparison_bundle(self, validate_schemas: bool | None = None) -> None:
|
95
92
|
final_validate_schemas = (
|
96
93
|
validate_schemas if validate_schemas is not None else self.valdiate_schemas
|
97
94
|
)
|
@@ -105,7 +102,7 @@ class IntegrationRunConfiguration:
|
|
105
102
|
|
106
103
|
def get_desired_state_diff(
|
107
104
|
run_cfg: IntegrationRunConfiguration,
|
108
|
-
) ->
|
105
|
+
) -> DesiredStateDiff | None:
|
109
106
|
"""
|
110
107
|
Calculates the desired state diff between the current bundle and the
|
111
108
|
comparison bundle for an integration. If the integration does not support
|
@@ -172,7 +169,7 @@ def _integration_wet_run(
|
|
172
169
|
|
173
170
|
def _integration_dry_run(
|
174
171
|
integration: QontractReconcileIntegration[RunParamsTypeVar],
|
175
|
-
desired_state_diff:
|
172
|
+
desired_state_diff: DesiredStateDiff | None,
|
176
173
|
) -> None:
|
177
174
|
"""
|
178
175
|
Runs an integration in dry-run mode, i.e. not actually making any changes
|
@@ -2,9 +2,7 @@ import copy
|
|
2
2
|
from collections.abc import Iterable
|
3
3
|
from dataclasses import dataclass
|
4
4
|
from typing import (
|
5
|
-
Optional,
|
6
5
|
Protocol,
|
7
|
-
Union,
|
8
6
|
)
|
9
7
|
|
10
8
|
from pydantic import BaseModel
|
@@ -46,16 +44,15 @@ from reconcile.utils.runtime.meta import IntegrationMeta
|
|
46
44
|
|
47
45
|
class ShardSpec(BaseModel):
|
48
46
|
# Base Sharding
|
49
|
-
shards:
|
50
|
-
shard_id:
|
51
|
-
shard_spec_overrides:
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
] = None
|
47
|
+
shards: str | None = ""
|
48
|
+
shard_id: str | None = ""
|
49
|
+
shard_spec_overrides: (
|
50
|
+
AWSAccountShardSpecOverrideV1
|
51
|
+
| OpenshiftClusterShardSpecOverrideV1
|
52
|
+
| CloudflareDNSZoneShardSpecOverrideV1
|
53
|
+
| OCMOrganizationShardSpecOverrideV1
|
54
|
+
| None
|
55
|
+
) = None
|
59
56
|
|
60
57
|
# Key sharding
|
61
58
|
shard_key: str = ""
|
@@ -136,7 +133,7 @@ class AWSAccountShardingStrategy:
|
|
136
133
|
|
137
134
|
def __init__(
|
138
135
|
self,
|
139
|
-
aws_accounts:
|
136
|
+
aws_accounts: Iterable[sharding_aws_accounts.AWSAccountV1] | None = None,
|
140
137
|
):
|
141
138
|
if not aws_accounts:
|
142
139
|
self.aws_accounts = (
|
@@ -162,7 +159,7 @@ class AWSAccountShardingStrategy:
|
|
162
159
|
]
|
163
160
|
|
164
161
|
def get_shard_spec_overrides(
|
165
|
-
self, sharding:
|
162
|
+
self, sharding: IntegrationShardingV1 | None
|
166
163
|
) -> dict[str, AWSAccountShardSpecOverrideV1]:
|
167
164
|
spos: dict[str, AWSAccountShardSpecOverrideV1] = {}
|
168
165
|
|
@@ -183,7 +180,7 @@ class AWSAccountShardingStrategy:
|
|
183
180
|
self,
|
184
181
|
aws_account: sharding_aws_accounts.AWSAccountV1,
|
185
182
|
integration_spec: IntegrationSpecV1,
|
186
|
-
spo:
|
183
|
+
spo: AWSAccountShardSpecOverrideV1 | None,
|
187
184
|
) -> ShardSpec:
|
188
185
|
return ShardSpec(
|
189
186
|
shard_key=aws_account.name,
|
@@ -213,7 +210,7 @@ class OCMOrganizationShardingStrategy:
|
|
213
210
|
|
214
211
|
def __init__(
|
215
212
|
self,
|
216
|
-
ocm_organizations:
|
213
|
+
ocm_organizations: Iterable[MinimalOCMOrganization] | None = None,
|
217
214
|
):
|
218
215
|
if not ocm_organizations:
|
219
216
|
self.ocm_organizations = (
|
@@ -226,7 +223,7 @@ class OCMOrganizationShardingStrategy:
|
|
226
223
|
self.ocm_organizations = list(ocm_organizations)
|
227
224
|
|
228
225
|
def get_shard_spec_overrides(
|
229
|
-
self, sharding:
|
226
|
+
self, sharding: IntegrationShardingV1 | None
|
230
227
|
) -> dict[str, OCMOrganizationShardSpecOverrideV1]:
|
231
228
|
spos: dict[str, OCMOrganizationShardSpecOverrideV1] = {}
|
232
229
|
|
@@ -249,7 +246,7 @@ class OCMOrganizationShardingStrategy:
|
|
249
246
|
self,
|
250
247
|
org: MinimalOCMOrganization,
|
251
248
|
integration_spec: IntegrationSpecV1,
|
252
|
-
spo:
|
249
|
+
spo: OCMOrganizationShardSpecOverrideV1 | None,
|
253
250
|
) -> ShardSpec:
|
254
251
|
return ShardSpec(
|
255
252
|
shard_key=org.org_id,
|
@@ -276,7 +273,7 @@ class OCMOrganizationShardingStrategy:
|
|
276
273
|
class OpenshiftClusterShardingStrategy:
|
277
274
|
IDENTIFIER = "per-openshift-cluster"
|
278
275
|
|
279
|
-
def __init__(self, clusters:
|
276
|
+
def __init__(self, clusters: Iterable[ClusterV1] | None = None):
|
280
277
|
if not clusters:
|
281
278
|
self.clusters = get_clusters_minimal()
|
282
279
|
else:
|
@@ -300,7 +297,7 @@ class OpenshiftClusterShardingStrategy:
|
|
300
297
|
]
|
301
298
|
|
302
299
|
def get_shard_spec_overrides(
|
303
|
-
self, sharding:
|
300
|
+
self, sharding: IntegrationShardingV1 | None
|
304
301
|
) -> dict[str, OpenshiftClusterShardSpecOverrideV1]:
|
305
302
|
spos: dict[str, OpenshiftClusterShardSpecOverrideV1] = {}
|
306
303
|
|
@@ -324,7 +321,7 @@ class OpenshiftClusterShardingStrategy:
|
|
324
321
|
self,
|
325
322
|
cluster: ClusterV1,
|
326
323
|
integration_spec: IntegrationSpecV1,
|
327
|
-
spo:
|
324
|
+
spo: OpenshiftClusterShardSpecOverrideV1 | None,
|
328
325
|
) -> ShardSpec:
|
329
326
|
return ShardSpec(
|
330
327
|
shard_key=cluster.name,
|
@@ -335,7 +332,7 @@ class OpenshiftClusterShardingStrategy:
|
|
335
332
|
)
|
336
333
|
|
337
334
|
def build_sub_shards(
|
338
|
-
self, base_shard: ShardSpec, spo:
|
335
|
+
self, base_shard: ShardSpec, spo: OpenshiftClusterShardSpecOverrideV1 | None
|
339
336
|
) -> list[ShardSpec]:
|
340
337
|
sub_shards = []
|
341
338
|
if spo and spo.sub_sharding and spo.sub_sharding.strategy:
|
@@ -374,9 +371,7 @@ class CloudflareDnsZoneShardingStrategy:
|
|
374
371
|
|
375
372
|
IDENTIFIER = "per-cloudflare-dns-zone"
|
376
373
|
|
377
|
-
def __init__(
|
378
|
-
self, cloudflare_zones: Optional[Iterable[CloudflareDnsZoneV1]] = None
|
379
|
-
):
|
374
|
+
def __init__(self, cloudflare_zones: Iterable[CloudflareDnsZoneV1] | None = None):
|
380
375
|
if not cloudflare_zones:
|
381
376
|
self.cloudflare_zones = (
|
382
377
|
terraform_cloudflare_zones.query(query_func=gql.get_api().query).zones
|
@@ -389,7 +384,7 @@ class CloudflareDnsZoneShardingStrategy:
|
|
389
384
|
return f"{dns_zone.account.name}-{dns_zone.identifier}"
|
390
385
|
|
391
386
|
def get_shard_spec_overrides(
|
392
|
-
self, sharding:
|
387
|
+
self, sharding: IntegrationShardingV1 | None
|
393
388
|
) -> dict[str, CloudflareDNSZoneShardSpecOverrideV1]:
|
394
389
|
spos: dict[str, CloudflareDNSZoneShardSpecOverrideV1] = {}
|
395
390
|
|
@@ -414,7 +409,7 @@ class CloudflareDnsZoneShardingStrategy:
|
|
414
409
|
self,
|
415
410
|
dns_zone: CloudflareDnsZoneV1,
|
416
411
|
integration_spec: IntegrationSpecV1,
|
417
|
-
spo:
|
412
|
+
spo: CloudflareDNSZoneShardSpecOverrideV1 | None,
|
418
413
|
) -> ShardSpec:
|
419
414
|
return ShardSpec(
|
420
415
|
shard_key=self._get_shard_key(dns_zone),
|