qontract-reconcile 0.10.1rc879__py3-none-any.whl → 0.10.1rc894__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.1rc879.dist-info → qontract_reconcile-0.10.1rc894.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc879.dist-info → qontract_reconcile-0.10.1rc894.dist-info}/RECORD +291 -284
- 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 +9 -14
- reconcile/aws_account_manager/reconciler.py +51 -1
- reconcile/aws_account_manager/utils.py +3 -0
- 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 +26 -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 +3 -6
- reconcile/dynatrace_token_provider/integration_v2.py +20 -0
- reconcile/dynatrace_token_provider/meta.py +1 -0
- 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 +5 -8
- reconcile/glitchtip_project_alerts/integration.py +57 -33
- reconcile/glitchtip_project_dsn/integration.py +8 -11
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +6 -0
- reconcile/gql_definitions/fragments/aws_account_managed.py +8 -0
- reconcile/gql_definitions/glitchtip/glitchtip_project.py +4 -4
- reconcile/gql_definitions/glitchtip_project_alerts/glitchtip_project.py +27 -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 +29 -30
- 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 +36 -15
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +8 -0
- 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 +6 -6
- reconcile/test/conftest.py +7 -10
- reconcile/test/fixtures.py +1 -1
- reconcile/test/saas_auto_promotions_manager/conftest.py +3 -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/dynatrace_environments.py +14 -0
- 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/aws_api_typed/account.py +23 -0
- reconcile/utils/aws_api_typed/api.py +20 -9
- 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 +38 -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 +39 -54
- 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 +12 -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 +50 -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 +105 -17
- tools/saas_promotion_state/__init__.py +0 -0
- tools/saas_promotion_state/saas_promotion_state.py +105 -0
- tools/template_validation.py +1 -1
- tools/test/conftest.py +45 -6
- tools/test/test_saas_promotion_state.py +187 -0
- {qontract_reconcile-0.10.1rc879.dist-info → qontract_reconcile-0.10.1rc894.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc879.dist-info → qontract_reconcile-0.10.1rc894.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc879.dist-info → qontract_reconcile-0.10.1rc894.dist-info}/top_level.txt +0 -0
reconcile/acs_rbac.py
CHANGED
@@ -2,7 +2,6 @@ import logging
|
|
2
2
|
from collections import defaultdict
|
3
3
|
from collections.abc import Callable
|
4
4
|
from typing import (
|
5
|
-
Optional,
|
6
5
|
Self,
|
7
6
|
)
|
8
7
|
|
@@ -64,7 +63,7 @@ class AcsRole(BaseModel):
|
|
64
63
|
assignments: list[AssignmentPair]
|
65
64
|
permission_set_name: str
|
66
65
|
access_scope: AcsAccessScope
|
67
|
-
system_default:
|
66
|
+
system_default: bool | None
|
68
67
|
|
69
68
|
@classmethod
|
70
69
|
def build(cls, permission: Permission, usernames: list[str]) -> Self:
|
@@ -197,7 +197,7 @@ class AdvancedUpgradeServiceIntegration(OCMClusterUpgradeSchedulerOrgIntegration
|
|
197
197
|
|
198
198
|
def discover_clusters(
|
199
199
|
ocm_api: OCMBaseClient,
|
200
|
-
org_ids:
|
200
|
+
org_ids: set[str] | None = None,
|
201
201
|
ignore_sts_clusters: bool = False,
|
202
202
|
) -> dict[str, list[ClusterDetails]]:
|
203
203
|
"""
|
@@ -222,7 +222,7 @@ def discover_clusters(
|
|
222
222
|
|
223
223
|
|
224
224
|
def _get_org_labels(
|
225
|
-
ocm_api: OCMBaseClient, org_ids:
|
225
|
+
ocm_api: OCMBaseClient, org_ids: set[str] | None
|
226
226
|
) -> dict[str, LabelContainer]:
|
227
227
|
"""
|
228
228
|
Fetch all AUS OCM org labels from organizations. They hold config
|
@@ -263,7 +263,7 @@ def _build_org_upgrade_specs_for_ocm_env(
|
|
263
263
|
}
|
264
264
|
|
265
265
|
|
266
|
-
def aus_label_key(config_atom:
|
266
|
+
def aus_label_key(config_atom: str | None = None) -> str:
|
267
267
|
"""
|
268
268
|
Generates label keys for aus, compliant with the naming schema defined in
|
269
269
|
https://service.pages.redhat.com/dev-guidelines/docs/sre-capabilities/framework/ocm-labels/
|
@@ -276,7 +276,7 @@ class OrganizationLabelSet(BaseModel):
|
|
276
276
|
Parses, represents and validates a set of organization labels for AUS.
|
277
277
|
"""
|
278
278
|
|
279
|
-
blocked_versions:
|
279
|
+
blocked_versions: CSV | None = Field(alias=aus_label_key("blocked-versions"))
|
280
280
|
|
281
281
|
sector_deps: dict[str, CSV] = labelset_groupfield(
|
282
282
|
group_prefix=aus_label_key("sector-deps.")
|
@@ -395,10 +395,10 @@ class ClusterUpgradePolicyLabelSet(BaseModel):
|
|
395
395
|
soak_days: int = Field(alias=aus_label_key("soak-days"), ge=0)
|
396
396
|
workloads: CSV = Field(alias=aus_label_key("workloads"), csv_min_items=1)
|
397
397
|
schedule: str = Field(alias=aus_label_key("schedule"))
|
398
|
-
mutexes:
|
399
|
-
sector:
|
400
|
-
blocked_versions:
|
401
|
-
version_gate_approvals:
|
398
|
+
mutexes: CSV | None = Field(alias=aus_label_key("mutexes"))
|
399
|
+
sector: str | None = Field(alias=aus_label_key("sector"))
|
400
|
+
blocked_versions: CSV | None = Field(alias=aus_label_key("blocked-versions"))
|
401
|
+
version_gate_approvals: CSV | None = Field(
|
402
402
|
alias=aus_label_key("version-gate-approvals")
|
403
403
|
)
|
404
404
|
_schedule_validator = validator("schedule", allow_reuse=True)(cron_validator)
|
@@ -422,10 +422,10 @@ def build_cluster_upgrade_policy_label_set(
|
|
422
422
|
workloads: list[str],
|
423
423
|
schedule: str,
|
424
424
|
soak_days: int,
|
425
|
-
mutexes:
|
426
|
-
sector:
|
427
|
-
blocked_versions:
|
428
|
-
version_gate_approvals:
|
425
|
+
mutexes: list[str] | None = None,
|
426
|
+
sector: str | None = None,
|
427
|
+
blocked_versions: list[str] | None = None,
|
428
|
+
version_gate_approvals: list[str] | None = None,
|
429
429
|
) -> ClusterUpgradePolicyLabelSet:
|
430
430
|
return ClusterUpgradePolicyLabelSet(**{
|
431
431
|
aus_label_key("workloads"): ",".join(workloads),
|
@@ -463,7 +463,7 @@ def _build_policy_from_labels(labels: LabelContainer) -> ClusterUpgradePolicyV1:
|
|
463
463
|
|
464
464
|
|
465
465
|
class VersionDataInheritanceLabelSet(BaseModel):
|
466
|
-
inherit_version_data:
|
466
|
+
inherit_version_data: CSV | None = Field(
|
467
467
|
alias=aus_label_key("version-data.inherit")
|
468
468
|
)
|
469
469
|
"""
|
@@ -472,7 +472,7 @@ class VersionDataInheritanceLabelSet(BaseModel):
|
|
472
472
|
Version data publishing/inheritance can also be defined between OCM environments.
|
473
473
|
"""
|
474
474
|
|
475
|
-
publish_version_data:
|
475
|
+
publish_version_data: CSV | None = Field(
|
476
476
|
alias=aus_label_key("version-data.publish")
|
477
477
|
)
|
478
478
|
"""
|
reconcile/aus/base.py
CHANGED
@@ -5,16 +5,13 @@ from abc import (
|
|
5
5
|
ABC,
|
6
6
|
abstractmethod,
|
7
7
|
)
|
8
|
+
from collections.abc import Callable, Sequence
|
8
9
|
from datetime import (
|
9
10
|
datetime,
|
10
11
|
timedelta,
|
11
|
-
timezone,
|
12
12
|
)
|
13
13
|
from typing import (
|
14
|
-
Callable,
|
15
|
-
Optional,
|
16
14
|
Protocol,
|
17
|
-
Sequence,
|
18
15
|
cast,
|
19
16
|
)
|
20
17
|
|
@@ -112,9 +109,9 @@ MIN_DELTA_MINUTES = 6
|
|
112
109
|
|
113
110
|
|
114
111
|
class AdvancedUpgradeSchedulerBaseIntegrationParams(PydanticRunParams):
|
115
|
-
ocm_environment:
|
116
|
-
ocm_organization_ids:
|
117
|
-
excluded_ocm_organization_ids:
|
112
|
+
ocm_environment: str | None = None
|
113
|
+
ocm_organization_ids: set[str] | None = None
|
114
|
+
excluded_ocm_organization_ids: set[str] | None = None
|
118
115
|
ignore_sts_clusters: bool = False
|
119
116
|
|
120
117
|
|
@@ -342,7 +339,7 @@ class AdvancedUpgradeSchedulerBaseIntegration(
|
|
342
339
|
def _build_telemeter_health_check_provider_for_env(
|
343
340
|
self,
|
344
341
|
ocm_env_name: str,
|
345
|
-
) ->
|
342
|
+
) -> TelemeterClusterHealthProvider | None:
|
346
343
|
ocm_env = next(
|
347
344
|
iter(
|
348
345
|
ocm_env_telemeter_query(
|
@@ -403,12 +400,12 @@ class AbstractUpgradePolicy(ABC, BaseModel):
|
|
403
400
|
|
404
401
|
cluster: OCMCluster
|
405
402
|
|
406
|
-
id:
|
407
|
-
next_run:
|
408
|
-
schedule:
|
403
|
+
id: str | None
|
404
|
+
next_run: str | None
|
405
|
+
schedule: str | None
|
409
406
|
schedule_type: str
|
410
407
|
version: str
|
411
|
-
state:
|
408
|
+
state: str | None
|
412
409
|
|
413
410
|
@abstractmethod
|
414
411
|
def create(self, ocm_api: OCMBaseClient) -> None:
|
@@ -424,7 +421,7 @@ class AbstractUpgradePolicy(ABC, BaseModel):
|
|
424
421
|
|
425
422
|
|
426
423
|
def addon_upgrade_policy_soonest_next_run() -> str:
|
427
|
-
now = datetime.now(tz=
|
424
|
+
now = datetime.now(tz=dt.UTC)
|
428
425
|
next_run = now + timedelta(minutes=MIN_DELTA_MINUTES)
|
429
426
|
return next_run.strftime("%Y-%m-%dT%H:%M:%SZ")
|
430
427
|
|
@@ -683,7 +680,7 @@ def update_history(
|
|
683
680
|
version_data.check_in = now
|
684
681
|
|
685
682
|
|
686
|
-
def version_data_state_key(ocm_env: str, org_id: str, addon_id:
|
683
|
+
def version_data_state_key(ocm_env: str, org_id: str, addon_id: str | None) -> str:
|
687
684
|
return f"{ocm_env}/{org_id}/{addon_id}" if addon_id else f"{ocm_env}/{org_id}"
|
688
685
|
|
689
686
|
|
@@ -694,7 +691,7 @@ def get_version_data_map(
|
|
694
691
|
integration: str,
|
695
692
|
addon_id: str = "",
|
696
693
|
inherit_version_data: bool = True,
|
697
|
-
defer:
|
694
|
+
defer: Callable | None = None,
|
698
695
|
) -> VersionDataMap:
|
699
696
|
"""Get a summary of versions history per OCM instance
|
700
697
|
|
@@ -788,7 +785,7 @@ def version_conditions_met(
|
|
788
785
|
version: str,
|
789
786
|
version_data: VersionData,
|
790
787
|
upgrade_policy: ClusterUpgradePolicyV1,
|
791
|
-
sector:
|
788
|
+
sector: Sector | None,
|
792
789
|
) -> bool:
|
793
790
|
"""Check that upgrade conditions are met for a version
|
794
791
|
|
@@ -887,8 +884,8 @@ def gates_to_agree(
|
|
887
884
|
def upgradeable_version(
|
888
885
|
spec: ClusterUpgradeSpec,
|
889
886
|
version_data: VersionData,
|
890
|
-
sector:
|
891
|
-
) ->
|
887
|
+
sector: Sector | None,
|
888
|
+
) -> str | None:
|
892
889
|
"""Get the highest next version we can upgrade to, fulfilling all conditions"""
|
893
890
|
for version in reversed(sort_versions(spec.get_available_upgrades())):
|
894
891
|
if spec.version_blocked(version):
|
@@ -908,7 +905,7 @@ def verify_current_should_skip(
|
|
908
905
|
desired: ClusterUpgradeSpec,
|
909
906
|
now: datetime,
|
910
907
|
addon_id: str = "",
|
911
|
-
) -> tuple[bool,
|
908
|
+
) -> tuple[bool, UpgradePolicyHandler | None]:
|
912
909
|
current_policies = [c for c in current_state if c.cluster.id == desired.cluster.id]
|
913
910
|
if not current_policies:
|
914
911
|
return False, None
|
@@ -944,7 +941,7 @@ def verify_schedule_should_skip(
|
|
944
941
|
desired: ClusterUpgradeSpec,
|
945
942
|
now: datetime,
|
946
943
|
addon_id: str = "",
|
947
|
-
) ->
|
944
|
+
) -> str | None:
|
948
945
|
schedule = desired.upgrade_policy.schedule
|
949
946
|
iter = croniter(schedule)
|
950
947
|
# ClusterService refuses scheduling upgrades less than 5m in advance
|
@@ -1006,7 +1003,7 @@ def _create_upgrade_policy(
|
|
1006
1003
|
|
1007
1004
|
def _calculate_node_pool_diffs(
|
1008
1005
|
ocm_api: OCMBaseClient, spec: ClusterUpgradeSpec, now: datetime
|
1009
|
-
) ->
|
1006
|
+
) -> UpgradePolicyHandler | None:
|
1010
1007
|
node_pools = get_node_pools(ocm_api, spec.cluster.id)
|
1011
1008
|
if node_pools:
|
1012
1009
|
for pool in node_pools:
|
@@ -1051,7 +1048,7 @@ def calculate_diff(
|
|
1051
1048
|
"""
|
1052
1049
|
|
1053
1050
|
def set_mutex(
|
1054
|
-
locked: dict[str, str], cluster_id: str, mutexes:
|
1051
|
+
locked: dict[str, str], cluster_id: str, mutexes: set[str] | None = None
|
1055
1052
|
) -> None:
|
1056
1053
|
for mutex in mutexes or set():
|
1057
1054
|
locked[mutex] = cluster_id
|
@@ -1174,7 +1171,7 @@ def act(
|
|
1174
1171
|
dry_run: bool,
|
1175
1172
|
diffs: list[UpgradePolicyHandler],
|
1176
1173
|
ocm_api: OCMBaseClient,
|
1177
|
-
addon_id:
|
1174
|
+
addon_id: str | None = None,
|
1178
1175
|
) -> None:
|
1179
1176
|
diffs.sort(key=sort_diffs)
|
1180
1177
|
for diff in diffs:
|
@@ -1207,8 +1204,8 @@ def get_orgs_for_environment(
|
|
1207
1204
|
integration: str,
|
1208
1205
|
ocm_env_name: str,
|
1209
1206
|
query_func: Callable,
|
1210
|
-
ocm_organization_ids:
|
1211
|
-
excluded_ocm_organization_ids:
|
1207
|
+
ocm_organization_ids: set[str] | None = None,
|
1208
|
+
excluded_ocm_organization_ids: set[str] | None = None,
|
1212
1209
|
only_addon_managed_upgrades: bool = False,
|
1213
1210
|
) -> list[AUSOCMOrganization]:
|
1214
1211
|
"""
|
@@ -1243,7 +1240,7 @@ def get_orgs_for_environment(
|
|
1243
1240
|
def remaining_soak_day_metric_values_for_cluster(
|
1244
1241
|
spec: ClusterUpgradeSpec,
|
1245
1242
|
soaked_versions: dict[str, float],
|
1246
|
-
current_upgrade:
|
1243
|
+
current_upgrade: AbstractUpgradePolicy | None,
|
1247
1244
|
) -> dict[str, float]:
|
1248
1245
|
"""
|
1249
1246
|
Calculate what versions and metric values to report for `AUS*VersionRemainingSoakDaysGauge` metrics.
|
@@ -1,8 +1,8 @@
|
|
1
1
|
import json
|
2
|
+
from collections.abc import Iterable
|
2
3
|
from datetime import datetime
|
3
4
|
from typing import (
|
4
5
|
Any,
|
5
|
-
Iterable,
|
6
6
|
Optional,
|
7
7
|
)
|
8
8
|
|
@@ -93,9 +93,9 @@ class VersionData(BaseModel):
|
|
93
93
|
upgrade policies.
|
94
94
|
"""
|
95
95
|
|
96
|
-
check_in:
|
96
|
+
check_in: datetime | None
|
97
97
|
versions: dict[str, VersionHistory] = Field(default_factory=dict)
|
98
|
-
stats:
|
98
|
+
stats: Stats | None
|
99
99
|
|
100
100
|
def jsondict(self) -> dict[str, Any]:
|
101
101
|
return json.loads(self.json(exclude_none=True))
|
@@ -104,7 +104,7 @@ class VersionData(BaseModel):
|
|
104
104
|
state.add(ocm_name, self.jsondict(), force=True)
|
105
105
|
|
106
106
|
def workload_history(
|
107
|
-
self, version: str, workload: str, default:
|
107
|
+
self, version: str, workload: str, default: WorkloadHistory | None = None
|
108
108
|
) -> WorkloadHistory:
|
109
109
|
if not default:
|
110
110
|
vh = self.versions.get(version, VersionHistory())
|
reconcile/aus/models.py
CHANGED
@@ -7,7 +7,6 @@ from collections.abc import (
|
|
7
7
|
Mapping,
|
8
8
|
Sequence,
|
9
9
|
)
|
10
|
-
from typing import Optional
|
11
10
|
|
12
11
|
from pydantic import (
|
13
12
|
BaseModel,
|
@@ -108,7 +107,7 @@ class OrganizationUpgradeSpec(BaseModel):
|
|
108
107
|
def __init__(
|
109
108
|
self,
|
110
109
|
org: AUSOCMOrganization,
|
111
|
-
specs:
|
110
|
+
specs: Iterable[ClusterUpgradeSpec] | None = None,
|
112
111
|
) -> None:
|
113
112
|
super().__init__(org=org)
|
114
113
|
|
@@ -200,7 +199,7 @@ class SectorConfigError(Exception):
|
|
200
199
|
|
201
200
|
class Sector(BaseModel):
|
202
201
|
name: str
|
203
|
-
dependencies: list[
|
202
|
+
dependencies: list[Sector] = Field(default_factory=list)
|
204
203
|
_specs: dict[str, ClusterUpgradeSpec] = PrivateAttr(default_factory=dict)
|
205
204
|
|
206
205
|
def __key(self) -> str:
|
@@ -1,9 +1,5 @@
|
|
1
1
|
import logging
|
2
|
-
from
|
3
|
-
Callable,
|
4
|
-
Iterable,
|
5
|
-
Optional,
|
6
|
-
)
|
2
|
+
from collections.abc import Callable, Iterable
|
7
3
|
|
8
4
|
from reconcile.aus.advanced_upgrade_service import aus_label_key
|
9
5
|
from reconcile.aus.base import gates_to_agree, get_orgs_for_environment
|
@@ -54,7 +50,7 @@ class VersionGateApproverParams(PydanticRunParams):
|
|
54
50
|
job_controller_namespace: str
|
55
51
|
rosa_job_service_account: str
|
56
52
|
rosa_role: str
|
57
|
-
rosa_job_image:
|
53
|
+
rosa_job_image: str | None = None
|
58
54
|
|
59
55
|
|
60
56
|
class VersionGateApprover(QontractReconcileIntegration[VersionGateApproverParams]):
|
@@ -1,5 +1,3 @@
|
|
1
|
-
from typing import Type
|
2
|
-
|
3
1
|
from reconcile.aus.version_gates import (
|
4
2
|
ingress_gate_handler,
|
5
3
|
ocp_gate_handler,
|
@@ -7,7 +5,7 @@ from reconcile.aus.version_gates import (
|
|
7
5
|
)
|
8
6
|
from reconcile.aus.version_gates.handler import GateHandler
|
9
7
|
|
10
|
-
HANDLERS: dict[str,
|
8
|
+
HANDLERS: dict[str, type[GateHandler]] = {
|
11
9
|
ocp_gate_handler.GATE_LABEL: ocp_gate_handler.OCPGateHandler,
|
12
10
|
sts_version_gate_handler.GATE_LABEL: sts_version_gate_handler.STSGateHandler,
|
13
11
|
ingress_gate_handler.GATE_LABEL: ingress_gate_handler.IngressGateHandler,
|
@@ -1,5 +1,4 @@
|
|
1
1
|
import logging
|
2
|
-
from typing import Optional
|
3
2
|
|
4
3
|
from reconcile.aus.version_gates.handler import GateHandler
|
5
4
|
from reconcile.utils.jobcontroller.controller import K8sJobController
|
@@ -16,8 +15,8 @@ class STSGateHandler(GateHandler):
|
|
16
15
|
self,
|
17
16
|
job_controller: K8sJobController,
|
18
17
|
aws_iam_role: str,
|
19
|
-
rosa_job_service_account:
|
20
|
-
rosa_job_image:
|
18
|
+
rosa_job_service_account: str | None = None,
|
19
|
+
rosa_job_image: str | None = None,
|
21
20
|
) -> None:
|
22
21
|
self.job_controller = job_controller
|
23
22
|
self.aws_iam_role = aws_iam_role
|
@@ -1,5 +1,5 @@
|
|
1
1
|
from collections.abc import Callable, Iterable
|
2
|
-
from datetime import
|
2
|
+
from datetime import UTC, datetime
|
3
3
|
from typing import Any
|
4
4
|
|
5
5
|
import jinja2
|
@@ -99,7 +99,7 @@ class AwsAccountMgmtIntegration(
|
|
99
99
|
"accountRequest": account_request.dict(by_alias=True),
|
100
100
|
"uid": uid,
|
101
101
|
"settings": settings,
|
102
|
-
"timestamp": int(datetime.now(tz=
|
102
|
+
"timestamp": int(datetime.now(tz=UTC).timestamp()),
|
103
103
|
})
|
104
104
|
return tmpl
|
105
105
|
|
@@ -114,10 +114,8 @@ class AwsAccountMgmtIntegration(
|
|
114
114
|
for account in data.accounts or []
|
115
115
|
if integration_is_enabled(self.name, account)
|
116
116
|
and (not account_name or account.name == account_name)
|
117
|
+
and validate(account)
|
117
118
|
]
|
118
|
-
for account in all_aws_accounts:
|
119
|
-
validate(account)
|
120
|
-
|
121
119
|
payer_accounts = [
|
122
120
|
account
|
123
121
|
for account in all_aws_accounts
|
@@ -219,18 +217,16 @@ class AwsAccountMgmtIntegration(
|
|
219
217
|
self.reconcile_account(account_role_api, reconciler, account)
|
220
218
|
|
221
219
|
def reconcile_account(
|
222
|
-
self,
|
223
|
-
aws_api: AWSApi,
|
224
|
-
reconciler: AWSReconciler,
|
225
|
-
account: AWSAccountManaged,
|
226
|
-
create_initial_user: bool = True,
|
220
|
+
self, aws_api: AWSApi, reconciler: AWSReconciler, account: AWSAccountManaged
|
227
221
|
) -> None:
|
228
222
|
"""Reconcile an AWS account."""
|
223
|
+
assert account.security_contact # mypy
|
229
224
|
reconciler.reconcile_account(
|
230
225
|
aws_api=aws_api,
|
231
226
|
name=account.name,
|
232
227
|
alias=account.alias,
|
233
228
|
quotas=[q for ql in account.quota_limits or [] for q in ql.quotas],
|
229
|
+
security_contact=account.security_contact,
|
234
230
|
)
|
235
231
|
|
236
232
|
def reconcile_payer_accounts(
|
@@ -245,8 +241,7 @@ class AwsAccountMgmtIntegration(
|
|
245
241
|
# reconcile accounts within payer accounts, aka organization accounts
|
246
242
|
for payer_account in payer_accounts:
|
247
243
|
# having a state per flavor and payer account makes it easier in a shared environment
|
248
|
-
reconciler.state.state_path = default_state_path
|
249
|
-
reconciler.state.state_path += f"/{payer_account.name}"
|
244
|
+
reconciler.state.state_path = f"{default_state_path}/{payer_account.name}"
|
250
245
|
aws_account_manager_role = (
|
251
246
|
payer_account.automation_role.aws_account_manager
|
252
247
|
if payer_account.automation_role
|
@@ -290,8 +285,8 @@ class AwsAccountMgmtIntegration(
|
|
290
285
|
) -> None:
|
291
286
|
"""Reconcile accounts not part of an organization via a payer account (e.g. payer accounts themselves)"""
|
292
287
|
for account in non_organization_accounts:
|
293
|
-
|
294
|
-
reconciler.state.state_path
|
288
|
+
# the state must be account specific
|
289
|
+
reconciler.state.state_path = f"{default_state_path}/{account.name}"
|
295
290
|
secret = self.secret_reader.read_all_secret(account.automation_token)
|
296
291
|
with AWSApi(
|
297
292
|
AWSStaticCredentials(
|
@@ -25,6 +25,7 @@ TASK_REQUEST_SERVICE_QUOTA = "request-service-quota"
|
|
25
25
|
TASK_CHECK_SERVICE_QUOTA_STATUS = "check-service-quota-status"
|
26
26
|
TASK_ENABLE_ENTERPRISE_SUPPORT = "enable-enterprise-support"
|
27
27
|
TASK_CHECK_ENTERPRISE_SUPPORT_STATUS = "check-enterprise-support-status"
|
28
|
+
TASK_SET_SECURITY_CONTACT = "set-security-contact"
|
28
29
|
|
29
30
|
|
30
31
|
class Quota(Protocol):
|
@@ -35,6 +36,15 @@ class Quota(Protocol):
|
|
35
36
|
def dict(self) -> dict[str, Any]: ...
|
36
37
|
|
37
38
|
|
39
|
+
class Contact(Protocol):
|
40
|
+
name: str
|
41
|
+
title: str | None
|
42
|
+
email: str
|
43
|
+
phone_number: str
|
44
|
+
|
45
|
+
def dict(self) -> dict[str, Any]: ...
|
46
|
+
|
47
|
+
|
38
48
|
class AWSReconciler:
|
39
49
|
def __init__(self, state: State, dry_run: bool) -> None:
|
40
50
|
self.state = state
|
@@ -288,6 +298,33 @@ class AWSReconciler:
|
|
288
298
|
)
|
289
299
|
raise AbortStateTransaction("Enterprise support case still open")
|
290
300
|
|
301
|
+
def _set_security_contact(
|
302
|
+
self,
|
303
|
+
aws_api: AWSApi,
|
304
|
+
account: str,
|
305
|
+
name: str,
|
306
|
+
title: str | None,
|
307
|
+
email: str,
|
308
|
+
phone_number: str,
|
309
|
+
) -> None:
|
310
|
+
"""Set the security contact for the account."""
|
311
|
+
title = title or name
|
312
|
+
security_contact = f"{name} {title} {email} {phone_number}"
|
313
|
+
with self.state.transaction(
|
314
|
+
state_key(account, TASK_SET_SECURITY_CONTACT)
|
315
|
+
) as _state:
|
316
|
+
if _state.exists and _state.value == security_contact:
|
317
|
+
return
|
318
|
+
|
319
|
+
logging.info(f"Setting security contact for {account}")
|
320
|
+
if self.dry_run:
|
321
|
+
raise AbortStateTransaction("Dry run")
|
322
|
+
|
323
|
+
aws_api.account.set_security_contact(
|
324
|
+
name=name, title=title, email=email, phone_number=phone_number
|
325
|
+
)
|
326
|
+
_state.value = security_contact
|
327
|
+
|
291
328
|
#
|
292
329
|
# Public methods
|
293
330
|
#
|
@@ -345,9 +382,22 @@ class AWSReconciler:
|
|
345
382
|
self._check_enterprise_support_status(aws_api, case_id)
|
346
383
|
|
347
384
|
def reconcile_account(
|
348
|
-
self,
|
385
|
+
self,
|
386
|
+
aws_api: AWSApi,
|
387
|
+
name: str,
|
388
|
+
alias: str | None,
|
389
|
+
quotas: Iterable[Quota],
|
390
|
+
security_contact: Contact,
|
349
391
|
) -> None:
|
350
392
|
"""Reconcile/update the AWS account. Return the initial user access key if a new user was created."""
|
351
393
|
self._set_account_alias(aws_api, name, alias)
|
352
394
|
if request_ids := self._request_quotas(aws_api, name, quotas):
|
353
395
|
self._check_quota_change_requests(aws_api, name, request_ids)
|
396
|
+
self._set_security_contact(
|
397
|
+
aws_api,
|
398
|
+
account=name,
|
399
|
+
name=security_contact.name,
|
400
|
+
title=security_contact.title,
|
401
|
+
email=security_contact.email,
|
402
|
+
phone_number=security_contact.phone_number,
|
403
|
+
)
|
@@ -30,6 +30,9 @@ def validate(account: AWSAccountV1) -> bool:
|
|
30
30
|
f"Premium support is required for payer account {account.name}"
|
31
31
|
)
|
32
32
|
|
33
|
+
# security contact is mandatory for all accounts since June 2024
|
34
|
+
if not account.security_contact:
|
35
|
+
raise ValueError(f"Security contact is required for account {account.name}")
|
33
36
|
return True
|
34
37
|
|
35
38
|
|
@@ -12,7 +12,6 @@ from datetime import (
|
|
12
12
|
from typing import (
|
13
13
|
TYPE_CHECKING,
|
14
14
|
Any,
|
15
|
-
Optional,
|
16
15
|
)
|
17
16
|
|
18
17
|
from botocore.exceptions import ClientError
|
@@ -153,7 +152,7 @@ def get_region(
|
|
153
152
|
|
154
153
|
|
155
154
|
def get_app_interface_amis(
|
156
|
-
namespaces:
|
155
|
+
namespaces: list[NamespaceV1] | None, ts: Terrascript
|
157
156
|
) -> list[AIAmi]:
|
158
157
|
"""Returns all the ami referenced in ASGs in app-interface."""
|
159
158
|
app_interface_amis = []
|
@@ -185,7 +184,7 @@ def get_app_interface_amis(
|
|
185
184
|
|
186
185
|
def check_aws_ami_in_use(
|
187
186
|
aws_ami: AWSAmi, app_interface_amis: list[AIAmi]
|
188
|
-
) ->
|
187
|
+
) -> str | None:
|
189
188
|
"""Verifies if the given AWS ami is in use in a defined app-interface ASG."""
|
190
189
|
for ai_ami in app_interface_amis:
|
191
190
|
# This can happen if the ASG init template has changed over the time. We don't have a way
|
@@ -203,7 +202,7 @@ def check_aws_ami_in_use(
|
|
203
202
|
|
204
203
|
|
205
204
|
@defer
|
206
|
-
def run(dry_run: bool, thread_pool_size: int, defer:
|
205
|
+
def run(dry_run: bool, thread_pool_size: int, defer: Callable | None = None) -> None:
|
207
206
|
exit_code = ExitCodes.SUCCESS
|
208
207
|
|
209
208
|
# We still use here a non-typed query; accounts is passed to AWSApi and Terrascript classes
|
@@ -4,10 +4,7 @@ from collections.abc import (
|
|
4
4
|
Iterable,
|
5
5
|
Mapping,
|
6
6
|
)
|
7
|
-
from typing import
|
8
|
-
Any,
|
9
|
-
Optional,
|
10
|
-
)
|
7
|
+
from typing import Any
|
11
8
|
|
12
9
|
from pydantic import BaseModel
|
13
10
|
|
@@ -21,7 +18,7 @@ QONTRACT_INTEGRATION = "aws-iam-password-reset"
|
|
21
18
|
|
22
19
|
def get_roles(
|
23
20
|
roles: Iterable[Mapping[str, Any]], org_username: str
|
24
|
-
) ->
|
21
|
+
) -> Mapping[str, Any] | None:
|
25
22
|
for d in roles:
|
26
23
|
if d["org_username"] == org_username:
|
27
24
|
return d
|
@@ -3,7 +3,7 @@ from collections.abc import (
|
|
3
3
|
Callable,
|
4
4
|
Iterable,
|
5
5
|
)
|
6
|
-
from enum import
|
6
|
+
from enum import StrEnum
|
7
7
|
from typing import Any
|
8
8
|
|
9
9
|
import semver
|
@@ -69,7 +69,7 @@ class ExternalResourceProvisioner(BaseModel):
|
|
69
69
|
path: str | None = None
|
70
70
|
|
71
71
|
|
72
|
-
class VersionFormat(
|
72
|
+
class VersionFormat(StrEnum):
|
73
73
|
MAJOR = "major"
|
74
74
|
MAJOR_MINOR = "major_minor"
|
75
75
|
MAJOR_MINOR_PATCH = "major_minor_patch"
|
@@ -1,9 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
|
-
from typing import
|
4
|
-
Any,
|
5
|
-
Optional,
|
6
|
-
)
|
3
|
+
from typing import Any
|
7
4
|
|
8
5
|
from reconcile import queries
|
9
6
|
from reconcile.closedbox_endpoint_monitoring_base import (
|
@@ -69,7 +66,7 @@ def get_blackbox_providers() -> list[EndpointMonitoringProvider]:
|
|
69
66
|
|
70
67
|
def build_probe(
|
71
68
|
provider: EndpointMonitoringProvider, endpoints: list[Endpoint]
|
72
|
-
) ->
|
69
|
+
) -> OpenshiftResource | None:
|
73
70
|
blackbox_exporter = provider.blackboxExporter
|
74
71
|
if blackbox_exporter:
|
75
72
|
prober_url = parse_prober_url(blackbox_exporter.exporterUrl)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from typing import (
|
3
|
-
Optional,
|
4
3
|
Protocol,
|
5
4
|
)
|
6
5
|
|
@@ -17,25 +16,25 @@ class Approver:
|
|
17
16
|
"""
|
18
17
|
|
19
18
|
org_username: str
|
20
|
-
tag_on_merge_requests:
|
19
|
+
tag_on_merge_requests: bool | None = False
|
21
20
|
|
22
21
|
|
23
22
|
class ApproverResolver(Protocol):
|
24
|
-
def lookup_approver_by_path(self, path: str) ->
|
23
|
+
def lookup_approver_by_path(self, path: str) -> Approver | None: ...
|
25
24
|
|
26
25
|
|
27
26
|
class GqlApproverResolver:
|
28
27
|
def __init__(self, gqlapis: list[GqlApi]):
|
29
28
|
self.gqlapis = gqlapis
|
30
29
|
|
31
|
-
def lookup_approver_by_path(self, path: str) ->
|
30
|
+
def lookup_approver_by_path(self, path: str) -> Approver | None:
|
32
31
|
for gqlapi in self.gqlapis:
|
33
32
|
approver = self._lookup_approver_by_path(gqlapi, path)
|
34
33
|
if approver:
|
35
34
|
return approver
|
36
35
|
return None
|
37
36
|
|
38
|
-
def _lookup_approver_by_path(self, gqlapi: GqlApi, path: str) ->
|
37
|
+
def _lookup_approver_by_path(self, gqlapi: GqlApi, path: str) -> Approver | None:
|
39
38
|
approvers = gqlapi.query(
|
40
39
|
"""
|
41
40
|
query Approvers($path: String) {
|