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
@@ -74,47 +74,47 @@ class DynamoDBStateAdapter:
|
|
74
74
|
MODCONF_DRIFT_MINS = "drift_detection_minutes"
|
75
75
|
MODCONF_TIMEOUT_MINS = "timeout_minutes"
|
76
76
|
|
77
|
-
def _get_value(self, item: Mapping[str, Any], key: str,
|
78
|
-
return item[key][
|
77
|
+
def _get_value(self, item: Mapping[str, Any], key: str, type: str = "S") -> Any:
|
78
|
+
return item[key][type]
|
79
79
|
|
80
80
|
def deserialize(
|
81
81
|
self,
|
82
82
|
item: Mapping[str, Any],
|
83
83
|
partial_data: bool = False,
|
84
84
|
) -> ExternalResourceState:
|
85
|
-
|
85
|
+
key_ = self._get_value(item, self.ER_KEY, type="M")
|
86
86
|
key = ExternalResourceKey(
|
87
|
-
provision_provider=self._get_value(
|
88
|
-
provisioner_name=self._get_value(
|
89
|
-
provider=self._get_value(
|
90
|
-
identifier=self._get_value(
|
87
|
+
provision_provider=self._get_value(key_, self.ER_KEY_PROVISION_PROVIDER),
|
88
|
+
provisioner_name=self._get_value(key_, self.ER_KEY_PROVISIONER_NAME),
|
89
|
+
provider=self._get_value(key_, self.ER_KEY_PROVIDER),
|
90
|
+
identifier=self._get_value(key_, self.ER_KEY_IDENTIFIER),
|
91
91
|
)
|
92
|
-
|
92
|
+
reconciliation = self._get_value(item, self.RECONC, type="M")
|
93
93
|
|
94
94
|
if partial_data:
|
95
95
|
r = Reconciliation(
|
96
96
|
key=key,
|
97
97
|
resource_hash=self._get_value(
|
98
|
-
|
98
|
+
reconciliation, self.RECONC_RESOURCE_HASH
|
99
99
|
),
|
100
100
|
)
|
101
101
|
else:
|
102
|
-
|
102
|
+
modconf = self._get_value(reconciliation, self.MODCONF, type="M")
|
103
103
|
r = Reconciliation(
|
104
104
|
key=key,
|
105
105
|
resource_hash=self._get_value(
|
106
|
-
|
106
|
+
reconciliation, self.RECONC_RESOURCE_HASH
|
107
107
|
),
|
108
|
-
input=self._get_value(
|
109
|
-
action=self._get_value(
|
108
|
+
input=self._get_value(reconciliation, self.RECONC_INPUT),
|
109
|
+
action=self._get_value(reconciliation, self.RECONC_ACTION),
|
110
110
|
module_configuration=ExternalResourceModuleConfiguration(
|
111
|
-
image=self._get_value(
|
112
|
-
version=self._get_value(
|
111
|
+
image=self._get_value(modconf, self.MODCONF_IMAGE),
|
112
|
+
version=self._get_value(modconf, self.MODCONF_VERSION),
|
113
113
|
reconcile_drift_interval_minutes=self._get_value(
|
114
|
-
|
114
|
+
modconf, self.MODCONF_DRIFT_MINS, type="N"
|
115
115
|
),
|
116
116
|
reconcile_timeout_minutes=self._get_value(
|
117
|
-
|
117
|
+
modconf, self.MODCONF_TIMEOUT_MINS, type="N"
|
118
118
|
),
|
119
119
|
),
|
120
120
|
)
|
@@ -67,7 +67,7 @@ def fetch_desired_state(
|
|
67
67
|
expiration_date = datetime.strptime(g["expirationDate"], "%Y-%m-%d").date()
|
68
68
|
if (expiration_date - date.today()).days > EXPIRATION_DAYS_MAX:
|
69
69
|
raise RunnerException(
|
70
|
-
f
|
70
|
+
f"The maximum expiration date of {g['name']} shall not "
|
71
71
|
f"exceed {EXPIRATION_DAYS_MAX} days from today"
|
72
72
|
)
|
73
73
|
for i in g["instances"]:
|
@@ -92,8 +92,8 @@ def fetch_desired_state(
|
|
92
92
|
break
|
93
93
|
if not found:
|
94
94
|
raise RunnerException(
|
95
|
-
f
|
96
|
-
f
|
95
|
+
f"[gabi:{g['name']} (path: {g['path']})] Could not find RDS identifier {identifier} "
|
96
|
+
f"for account {account} in namespace {namespace['name']}. "
|
97
97
|
"If this is a removed read only instance, consider updating the identifier to the source replica."
|
98
98
|
)
|
99
99
|
users = get_usernames(g["users"], cluster)
|
reconcile/gcr_mirror.py
CHANGED
@@ -147,7 +147,7 @@ class QuayMirror:
|
|
147
147
|
for org, data in summary.items():
|
148
148
|
for item in data:
|
149
149
|
image = Image(
|
150
|
-
f
|
150
|
+
f"{item['server_url']}/{org}/{item['name']}",
|
151
151
|
session=self.session,
|
152
152
|
timeout=REQUEST_TIMEOUT,
|
153
153
|
)
|
@@ -267,7 +267,7 @@ class QuayMirror:
|
|
267
267
|
raw_data = self.secret_reader.read_all(push_secret)
|
268
268
|
project = project_data["name"]
|
269
269
|
token = base64.b64decode(raw_data["token"]).decode()
|
270
|
-
creds[project] = f
|
270
|
+
creds[project] = f"{raw_data['user']}:{token}"
|
271
271
|
return creds
|
272
272
|
|
273
273
|
|
reconcile/github_org.py
CHANGED
@@ -120,7 +120,7 @@ def get_config(default=False):
|
|
120
120
|
raise KeyError("default github org config not found")
|
121
121
|
if len(found_defaults) > 1:
|
122
122
|
raise KeyError(
|
123
|
-
"multiple default github org configs found:
|
123
|
+
f"multiple default github org configs found: {found_defaults}"
|
124
124
|
)
|
125
125
|
|
126
126
|
return config
|
@@ -206,15 +206,11 @@ def fetch_desired_state(infer_clusters=True):
|
|
206
206
|
if not permissions:
|
207
207
|
continue
|
208
208
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
for bot in role["bots"]:
|
215
|
-
if "github_username" in bot:
|
216
|
-
members.append(bot["github_username"])
|
217
|
-
members = [m.lower() for m in members]
|
209
|
+
user_members = [user["github_username"] for user in role["users"]]
|
210
|
+
bot_members = [
|
211
|
+
bot["github_username"] for bot in role["bots"] if "github_username" in bot
|
212
|
+
]
|
213
|
+
members = [m.lower() for m in user_members + bot_members]
|
218
214
|
|
219
215
|
for permission in permissions:
|
220
216
|
if permission["service"] == "github-org":
|
@@ -436,9 +432,9 @@ def run(dry_run):
|
|
436
432
|
current_orgs = {item["params"]["org"] for item in current_state.dump()}
|
437
433
|
desired_orgs = {item["params"]["org"] for item in desired_state.dump()}
|
438
434
|
|
439
|
-
assert (
|
440
|
-
current_orgs
|
441
|
-
)
|
435
|
+
assert current_orgs == desired_orgs, (
|
436
|
+
f"Current orgs ({current_orgs}) don't match desired orgs ({desired_orgs})"
|
437
|
+
)
|
442
438
|
|
443
439
|
# Calculate diff
|
444
440
|
diff = current_state.diff(desired_state)
|
reconcile/gitlab_housekeeping.py
CHANGED
@@ -174,7 +174,7 @@ def clean_pipelines(
|
|
174
174
|
gl_piplelines.get(p["id"]).cancel()
|
175
175
|
except gitlab.exceptions.GitlabPipelineCancelError as err:
|
176
176
|
logging.error(
|
177
|
-
f
|
177
|
+
f"unable to cancel {p['web_url']} - "
|
178
178
|
f"error message {err.error_message}"
|
179
179
|
)
|
180
180
|
|
reconcile/gitlab_owners.py
CHANGED
@@ -198,7 +198,7 @@ class MRApproval:
|
|
198
198
|
markdown_report = ""
|
199
199
|
|
200
200
|
closest_approvers = []
|
201
|
-
for
|
201
|
+
for owners in report.values():
|
202
202
|
new_group = []
|
203
203
|
|
204
204
|
if "closest_approvers" not in owners:
|
@@ -232,10 +232,10 @@ class MRApproval:
|
|
232
232
|
)
|
233
233
|
|
234
234
|
for group in sorted(closest_approvers):
|
235
|
-
markdown_report += f
|
235
|
+
markdown_report += f"* {', '.join(group)}\n"
|
236
236
|
|
237
237
|
approvers = set()
|
238
|
-
for
|
238
|
+
for owners in report.values():
|
239
239
|
if "approvers" not in owners:
|
240
240
|
continue
|
241
241
|
|
@@ -254,10 +254,10 @@ class MRApproval:
|
|
254
254
|
"\nIn case of emergency, the override approvers "
|
255
255
|
"(from parent directories) are:\n\n"
|
256
256
|
)
|
257
|
-
markdown_report += f
|
257
|
+
markdown_report += f"* {', '.join(sorted(approvers))}\n"
|
258
258
|
|
259
259
|
closest_reviewers = set()
|
260
|
-
for
|
260
|
+
for owners in report.values():
|
261
261
|
if "closest_reviewers" not in owners:
|
262
262
|
continue
|
263
263
|
|
@@ -274,11 +274,11 @@ class MRApproval:
|
|
274
274
|
closest_reviewers.add(closest_reviewer)
|
275
275
|
|
276
276
|
if closest_reviewers:
|
277
|
-
markdown_report += "\nRelevant reviewers (with no
|
278
|
-
markdown_report += f
|
277
|
+
markdown_report += "\nRelevant reviewers (with no merge rights) are:\n\n"
|
278
|
+
markdown_report += f"* {', '.join(sorted(closest_reviewers))}\n"
|
279
279
|
|
280
280
|
reviewers = set()
|
281
|
-
for
|
281
|
+
for owners in report.values():
|
282
282
|
if "reviewers" not in owners:
|
283
283
|
continue
|
284
284
|
|
@@ -303,7 +303,7 @@ class MRApproval:
|
|
303
303
|
"merge rights) from parent "
|
304
304
|
"directories are:\n\n"
|
305
305
|
)
|
306
|
-
markdown_report += f
|
306
|
+
markdown_report += f"* {', '.join(sorted(reviewers))}\n"
|
307
307
|
|
308
308
|
return markdown_report.rstrip()
|
309
309
|
|
@@ -328,9 +328,7 @@ def act(repo, dry_run, instance, settings, defer=None):
|
|
328
328
|
|
329
329
|
if mr_approval.top_commit_created_at is None:
|
330
330
|
_LOG.info([
|
331
|
-
f"Project:{gitlab_cli.project.id} "
|
332
|
-
f"Merge Request:{mr.iid} "
|
333
|
-
f"- skipping"
|
331
|
+
f"Project:{gitlab_cli.project.id} Merge Request:{mr.iid} - skipping"
|
334
332
|
])
|
335
333
|
continue
|
336
334
|
|
reconcile/gitlab_permissions.py
CHANGED
@@ -91,11 +91,12 @@ class GroupPermissionHandler:
|
|
91
91
|
current_state: dict[str, GroupSpec],
|
92
92
|
) -> None:
|
93
93
|
# gather list of app-interface managed repos
|
94
|
-
managed_repos: set[str] = set()
|
95
94
|
instance = queries.get_gitlab_instance()
|
96
|
-
|
97
|
-
|
98
|
-
|
95
|
+
managed_repos = {
|
96
|
+
f"{instance['url']}/{project_request['group']}/{r}"
|
97
|
+
for project_request in instance.get("projectRequests", [])
|
98
|
+
for r in project_request.get("projects", [])
|
99
|
+
}
|
99
100
|
|
100
101
|
# get the diff data
|
101
102
|
diff_data = diff_mappings(
|
@@ -97,7 +97,7 @@ def fetch_desired_state(
|
|
97
97
|
project = Project(
|
98
98
|
name=glitchtip_project.name,
|
99
99
|
platform=glitchtip_project.platform,
|
100
|
-
slug=glitchtip_project.project_id
|
100
|
+
slug=glitchtip_project.project_id or "",
|
101
101
|
event_throttle_rate=glitchtip_project.event_throttle_rate or 0,
|
102
102
|
)
|
103
103
|
# Check project is unique within an organization
|
@@ -108,24 +108,24 @@ def fetch_desired_state(
|
|
108
108
|
|
109
109
|
# Get users via roles
|
110
110
|
for role in glitchtip_team.roles:
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
role=get_user_role(organization, role),
|
116
|
-
)
|
111
|
+
users.extend(
|
112
|
+
User(
|
113
|
+
email=f"{role_user.org_username}@{mail_domain}",
|
114
|
+
role=get_user_role(organization, role),
|
117
115
|
)
|
116
|
+
for role_user in role.users
|
117
|
+
)
|
118
118
|
|
119
119
|
# Get users via ldap
|
120
120
|
for ldap_group in glitchtip_team.ldap_groups or []:
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
or DEFAULT_MEMBER_ROLE,
|
127
|
-
)
|
121
|
+
users.extend(
|
122
|
+
User(
|
123
|
+
email=f"{member.id}@{mail_domain}",
|
124
|
+
role=glitchtip_team.members_organization_role
|
125
|
+
or DEFAULT_MEMBER_ROLE,
|
128
126
|
)
|
127
|
+
for member in internal_groups_client.group(ldap_group).members
|
128
|
+
)
|
129
129
|
|
130
130
|
# set(users) will take the first occurrence of a user, so the users from roles will be preferred
|
131
131
|
team = Team(name=glitchtip_team.name, users=set(users))
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
import operator
|
2
3
|
from collections import defaultdict
|
3
4
|
from collections.abc import (
|
4
5
|
Callable,
|
@@ -192,9 +193,7 @@ class GlitchtipProjectAlertsIntegration(
|
|
192
193
|
project = Project(
|
193
194
|
name=glitchtip_project.name,
|
194
195
|
platform=None,
|
195
|
-
slug=glitchtip_project.project_id
|
196
|
-
if glitchtip_project.project_id
|
197
|
-
else "",
|
196
|
+
slug=glitchtip_project.project_id or "",
|
198
197
|
alerts=alerts,
|
199
198
|
)
|
200
199
|
|
@@ -238,7 +237,7 @@ class GlitchtipProjectAlertsIntegration(
|
|
238
237
|
current_project.alerts,
|
239
238
|
desired_project.alerts,
|
240
239
|
key=lambda g: g.name,
|
241
|
-
equal=
|
240
|
+
equal=operator.eq,
|
242
241
|
)
|
243
242
|
|
244
243
|
for alert_to_add in diff_result.add.values():
|
@@ -59,8 +59,7 @@ INTEGRATION_UPSTREAM_REPOS_PARAM = "INTEGRATION_UPSTREAM_REPOS"
|
|
59
59
|
|
60
60
|
def get_image_tag_from_ref(ref: str, upstream: str) -> str:
|
61
61
|
gh_prefix = "https://github.com/"
|
62
|
-
|
63
|
-
upstream = upstream[len(gh_prefix) :]
|
62
|
+
upstream = upstream.removeprefix(gh_prefix)
|
64
63
|
settings = queries.get_app_interface_settings()
|
65
64
|
gh_token = get_default_config()["token"]
|
66
65
|
github = Github(gh_token, base_url=GH_BASE_URL)
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
import operator
|
2
3
|
import re
|
3
4
|
import time
|
4
5
|
|
@@ -36,7 +37,7 @@ def delete_builds(jenkins, builds_todel, dry_run=True):
|
|
36
37
|
|
37
38
|
def get_last_build_ids(builds):
|
38
39
|
builds_to_keep = []
|
39
|
-
sorted_builds = sorted(builds, key=
|
40
|
+
sorted_builds = sorted(builds, key=operator.itemgetter("timestamp"), reverse=True)
|
40
41
|
if sorted_builds:
|
41
42
|
last_build = sorted_builds[0]
|
42
43
|
builds_to_keep.append(last_build["id"])
|
@@ -88,14 +89,15 @@ def run(dry_run):
|
|
88
89
|
continue
|
89
90
|
|
90
91
|
# Process cleanup rules, pre-compile as regexes
|
91
|
-
cleanup_rules = [
|
92
|
-
|
93
|
-
cleanup_rules.append({
|
92
|
+
cleanup_rules = [
|
93
|
+
{
|
94
94
|
"name": rule["name"],
|
95
95
|
"name_re": re.compile(rule["name"]),
|
96
96
|
"keep_hours": rule["keep_hours"],
|
97
97
|
"keep_ms": hours_to_ms(rule["keep_hours"]),
|
98
|
-
}
|
98
|
+
}
|
99
|
+
for rule in instance_cleanup_rules
|
100
|
+
]
|
99
101
|
|
100
102
|
token = instance["token"]
|
101
103
|
instance_name = instance["name"]
|
reconcile/jenkins_roles.py
CHANGED
@@ -85,12 +85,14 @@ def get_current_state(jenkins_map):
|
|
85
85
|
if role_name == "anonymous":
|
86
86
|
continue
|
87
87
|
|
88
|
-
|
89
|
-
|
88
|
+
current_state.extend(
|
89
|
+
{
|
90
90
|
"instance": instance,
|
91
91
|
"role": role_name,
|
92
92
|
"user": user,
|
93
|
-
}
|
93
|
+
}
|
94
|
+
for user in users
|
95
|
+
)
|
94
96
|
|
95
97
|
return current_state
|
96
98
|
|
@@ -105,12 +107,14 @@ def get_desired_state():
|
|
105
107
|
if p["service"] != "jenkins-role":
|
106
108
|
continue
|
107
109
|
|
108
|
-
|
109
|
-
|
110
|
+
desired_state.extend(
|
111
|
+
{
|
110
112
|
"instance": p["instance"]["name"],
|
111
113
|
"role": p["role"],
|
112
114
|
"user": u["org_username"],
|
113
|
-
}
|
115
|
+
}
|
116
|
+
for u in r["users"]
|
117
|
+
)
|
114
118
|
for u in r["bots"]:
|
115
119
|
if u["org_username"] is None:
|
116
120
|
continue
|
@@ -71,7 +71,7 @@ def get_desired_state(
|
|
71
71
|
if not found:
|
72
72
|
raise ValueError(
|
73
73
|
f"Could not find asg identifier {identifier} "
|
74
|
-
f
|
74
|
+
f"for account {account} in namespace {namespace['name']}"
|
75
75
|
)
|
76
76
|
return desired_state
|
77
77
|
|
@@ -105,9 +105,10 @@ def act(
|
|
105
105
|
logging.info(["update_jenkins_worker_fleet", instance_name, fleet.name])
|
106
106
|
|
107
107
|
if not dry_run:
|
108
|
-
d_clouds = [
|
109
|
-
|
110
|
-
|
108
|
+
d_clouds = [
|
109
|
+
{"eC2Fleet": d.dict(by_alias=True, exclude_none=True)}
|
110
|
+
for d in desired_state
|
111
|
+
]
|
111
112
|
config = {"jenkins": {"clouds": d_clouds}}
|
112
113
|
jenkins.apply_jcasc_config(config)
|
113
114
|
|
@@ -107,7 +107,7 @@ def board_is_valid(
|
|
107
107
|
)
|
108
108
|
error |= ValidationError.INVALID_COMPONENT
|
109
109
|
|
110
|
-
issue_type = board.issue_type
|
110
|
+
issue_type = board.issue_type or default_issue_type
|
111
111
|
project_issue_types = jira.project_issue_types()
|
112
112
|
project_issue_types_str = [i.name for i in project_issue_types]
|
113
113
|
if issue_type not in project_issue_types_str:
|
@@ -128,11 +128,7 @@ def board_is_valid(
|
|
128
128
|
)
|
129
129
|
error |= ValidationError.INVALID_ISSUE_TYPE
|
130
130
|
|
131
|
-
reopen_state =
|
132
|
-
board.issue_reopen_state
|
133
|
-
if board.issue_reopen_state
|
134
|
-
else default_reopen_state
|
135
|
-
)
|
131
|
+
reopen_state = board.issue_reopen_state or default_reopen_state
|
136
132
|
if reopen_state.lower() not in [t.lower() for t in available_states]:
|
137
133
|
logging.error(
|
138
134
|
f"[{board.name}] '{reopen_state}' is not a valid state in project. Valid states: {available_states}"
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import contextlib
|
2
2
|
import logging
|
3
|
+
import operator
|
3
4
|
from collections.abc import (
|
4
5
|
Callable,
|
5
6
|
Iterable,
|
@@ -86,7 +87,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
|
|
86
87
|
owner = Entity(
|
87
88
|
type=EntityType.SERVICE_ACCOUNT,
|
88
89
|
# OIDC service accounts are named service-account-<client_id>
|
89
|
-
id=f
|
90
|
+
id=f"service-account-{secret['client_id']}",
|
90
91
|
)
|
91
92
|
desired_groups_for_roles = self.get_desired_groups_for_roles(
|
92
93
|
roles,
|
@@ -241,7 +242,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
|
|
241
242
|
current_groups,
|
242
243
|
desired_groups,
|
243
244
|
key=lambda g: g.name,
|
244
|
-
equal=
|
245
|
+
equal=operator.eq,
|
245
246
|
)
|
246
247
|
# Internal Groups API does not support listing all managed groups, therefore
|
247
248
|
# we need to keep track of them ourselves.
|
reconcile/ocm_groups.py
CHANGED
@@ -19,16 +19,16 @@ QONTRACT_INTEGRATION = "ocm-groups"
|
|
19
19
|
|
20
20
|
|
21
21
|
def get_cluster_state(group_items, ocm_map):
|
22
|
-
results = []
|
23
22
|
cluster = group_items["cluster"]
|
24
23
|
ocm = ocm_map.get(cluster)
|
25
24
|
group_name = group_items["group_name"]
|
26
25
|
group = ocm.get_group_if_exists(cluster, group_name)
|
27
26
|
if group is None:
|
28
|
-
return
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
return []
|
28
|
+
return [
|
29
|
+
{"cluster": cluster, "group": group_name, "user": user}
|
30
|
+
for user in group["users"] or []
|
31
|
+
]
|
32
32
|
|
33
33
|
|
34
34
|
def fetch_current_state(clusters, thread_pool_size):
|
@@ -19,8 +19,8 @@ QONTRACT_INTEGRATION = "ocm-update-recommended-version"
|
|
19
19
|
|
20
20
|
def get_highest(version_set: set[str]) -> str:
|
21
21
|
def _compare(v1: str, v2: str) -> int:
|
22
|
-
|
23
|
-
return
|
22
|
+
v1_ = semver.VersionInfo.parse(v1)
|
23
|
+
return v1_.compare(v2)
|
24
24
|
|
25
25
|
sorted_version_set = sorted(
|
26
26
|
version_set, key=functools.cmp_to_key(_compare), reverse=True
|
reconcile/openshift_base.py
CHANGED
@@ -220,17 +220,17 @@ def init_specs_to_fetch(
|
|
220
220
|
|
221
221
|
# Initialize desired state specs
|
222
222
|
openshift_resources = namespace_info.get("openshiftResources")
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
privileged=privileged,
|
232
|
-
)
|
223
|
+
state_specs.extend(
|
224
|
+
DesiredStateSpec(
|
225
|
+
oc=oc,
|
226
|
+
cluster=cluster,
|
227
|
+
namespace=namespace,
|
228
|
+
resource=openshift_resource,
|
229
|
+
parent=namespace_info,
|
230
|
+
privileged=privileged,
|
233
231
|
)
|
232
|
+
for openshift_resource in openshift_resources or []
|
233
|
+
)
|
234
234
|
|
235
235
|
elif clusters:
|
236
236
|
# set namespace to something indicative
|
@@ -488,7 +488,7 @@ def apply(
|
|
488
488
|
obsolete_rs["metadata"]["ownerReferences"] = owner_references
|
489
489
|
oc.apply(namespace=namespace, resource=OR(obsolete_rs, "", ""))
|
490
490
|
except (MayNotChangeOnceSetError, PrimaryClusterIPCanNotBeUnsetError):
|
491
|
-
if resource_type
|
491
|
+
if resource_type != "Service":
|
492
492
|
raise
|
493
493
|
|
494
494
|
oc.delete(namespace=namespace, kind=resource_type, name=resource.name)
|
@@ -882,10 +882,7 @@ def apply_action(
|
|
882
882
|
if resource_type != "Secret"
|
883
883
|
else f"error applying Secret {resource.name}: REDACTED"
|
884
884
|
)
|
885
|
-
msg = (
|
886
|
-
f"[{cluster}/{namespace}] {err} "
|
887
|
-
f"(error details: {resource.error_details})"
|
888
|
-
)
|
885
|
+
msg = f"[{cluster}/{namespace}] {err} (error details: {resource.error_details})"
|
889
886
|
logging.error(msg)
|
890
887
|
|
891
888
|
|
@@ -956,7 +953,7 @@ def _realize_resource_data_3way_diff(
|
|
956
953
|
actions: list[dict] = []
|
957
954
|
|
958
955
|
if ri.has_error_registered(cluster=cluster):
|
959
|
-
msg = f"[{cluster}] skipping realize_data for
|
956
|
+
msg = f"[{cluster}] skipping realize_data for cluster with errors"
|
960
957
|
logging.error(msg)
|
961
958
|
return actions
|
962
959
|
|
@@ -1212,8 +1209,7 @@ def validate_realized_data(actions: Iterable[dict[str, str]], oc_map: ClusterMap
|
|
1212
1209
|
state = status.get("state")
|
1213
1210
|
if state != "AtLatestKnown":
|
1214
1211
|
logging.info(
|
1215
|
-
f"Subscription {name} state is invalid. "
|
1216
|
-
f"Current state: {state}"
|
1212
|
+
f"Subscription {name} state is invalid. Current state: {state}"
|
1217
1213
|
)
|
1218
1214
|
raise ValidationError(name)
|
1219
1215
|
elif kind == "Job":
|
@@ -1311,8 +1307,7 @@ def aggregate_shared_resources(namespace_info, shared_resources_type):
|
|
1311
1307
|
]
|
1312
1308
|
if shared_resources_type not in supported_shared_resources_types:
|
1313
1309
|
raise KeyError(
|
1314
|
-
f"shared_resource_type must be one of "
|
1315
|
-
f"{supported_shared_resources_types}."
|
1310
|
+
f"shared_resource_type must be one of {supported_shared_resources_types}."
|
1316
1311
|
)
|
1317
1312
|
shared_resources = namespace_info.get("sharedResources")
|
1318
1313
|
namespace_type_resources = namespace_info.get(shared_resources_type)
|
reconcile/openshift_groups.py
CHANGED
@@ -41,12 +41,11 @@ QONTRACT_INTEGRATION = "openshift-groups"
|
|
41
41
|
def get_cluster_state(
|
42
42
|
group_items: Mapping[str, str], oc_map: ClusterMap
|
43
43
|
) -> list[dict[str, str]]:
|
44
|
-
results: list[dict[str, str]] = []
|
45
44
|
cluster = group_items["cluster"]
|
46
45
|
oc = oc_map.get(cluster)
|
47
46
|
if isinstance(oc, OCLogMsg):
|
48
47
|
logging.log(level=oc.log_level, msg=oc.message)
|
49
|
-
return
|
48
|
+
return []
|
50
49
|
group_name = group_items["group_name"]
|
51
50
|
try:
|
52
51
|
group = oc.get_group_if_exists(group_name)
|
@@ -55,10 +54,11 @@ def get_cluster_state(
|
|
55
54
|
logging.error(msg)
|
56
55
|
raise e
|
57
56
|
if group is None:
|
58
|
-
return
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
return []
|
58
|
+
return [
|
59
|
+
{"cluster": cluster, "group": group_name, "user": user}
|
60
|
+
for user in group["users"] or []
|
61
|
+
]
|
62
62
|
|
63
63
|
|
64
64
|
def create_groups_list(
|
@@ -74,8 +74,9 @@ def create_groups_list(
|
|
74
74
|
if isinstance(oc, OCLogMsg):
|
75
75
|
logging.log(level=oc.log_level, msg=oc.message)
|
76
76
|
groups = cluster_info["managedGroups"] or []
|
77
|
-
|
78
|
-
|
77
|
+
groups_list.extend(
|
78
|
+
{"cluster": cluster, "group_name": group_name} for group_name in groups
|
79
|
+
)
|
79
80
|
return groups_list
|
80
81
|
|
81
82
|
|