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/openshift_users.py
CHANGED
@@ -5,10 +5,7 @@ from collections.abc import (
|
|
5
5
|
Iterable,
|
6
6
|
Mapping,
|
7
7
|
)
|
8
|
-
from typing import
|
9
|
-
Any,
|
10
|
-
Optional,
|
11
|
-
)
|
8
|
+
from typing import Any
|
12
9
|
|
13
10
|
from sretoolbox.utils import threaded
|
14
11
|
|
@@ -46,13 +43,13 @@ def get_cluster_users(
|
|
46
43
|
users: list[str] = []
|
47
44
|
|
48
45
|
# get cluster info for current cluster name from clusters list
|
49
|
-
cluster_info = next(
|
46
|
+
cluster_info = next(cl for cl in clusters if cl.name == cluster)
|
50
47
|
|
51
48
|
# backwarts compatibiltiy for clusters w/o auth
|
52
49
|
identity_prefixes = ["github"]
|
53
50
|
|
54
51
|
for auth in cluster_info.auth:
|
55
|
-
if isinstance(auth,
|
52
|
+
if isinstance(auth, ClusterAuthOIDCV1 | ClusterAuthRHIDPV1):
|
56
53
|
identity_prefixes.append(auth.name)
|
57
54
|
|
58
55
|
for u in oc.get_users():
|
@@ -72,7 +69,7 @@ def get_cluster_users(
|
|
72
69
|
|
73
70
|
def fetch_current_state(
|
74
71
|
thread_pool_size: int,
|
75
|
-
internal:
|
72
|
+
internal: bool | None,
|
76
73
|
use_jump_host: bool,
|
77
74
|
) -> tuple[OCMap, list[Any]]:
|
78
75
|
vault_settings = get_app_interface_vault_settings()
|
@@ -99,7 +96,7 @@ def fetch_current_state(
|
|
99
96
|
|
100
97
|
|
101
98
|
def fetch_desired_state(
|
102
|
-
oc_map:
|
99
|
+
oc_map: OCMap | None, enforced_user_keys: Any = None
|
103
100
|
) -> list[Any]:
|
104
101
|
desired_state = []
|
105
102
|
|
@@ -163,16 +160,16 @@ def act(diff: Mapping[str, Any], oc_map: OCMap) -> None:
|
|
163
160
|
raise Exception("No proper Openshift Client for del_user operation")
|
164
161
|
oc.delete_user(user)
|
165
162
|
else:
|
166
|
-
raise Exception("invalid action: {}"
|
163
|
+
raise Exception(f"invalid action: {action}")
|
167
164
|
|
168
165
|
|
169
166
|
@defer
|
170
167
|
def run(
|
171
168
|
dry_run: bool,
|
172
169
|
thread_pool_size: int = 10,
|
173
|
-
internal:
|
170
|
+
internal: bool | None = None,
|
174
171
|
use_jump_host: bool = True,
|
175
|
-
defer:
|
172
|
+
defer: Callable | None = None,
|
176
173
|
) -> None:
|
177
174
|
oc_map, current_state = fetch_current_state(
|
178
175
|
thread_pool_size, internal, use_jump_host
|
reconcile/oum/base.py
CHANGED
@@ -4,7 +4,6 @@ from abc import (
|
|
4
4
|
abstractmethod,
|
5
5
|
)
|
6
6
|
from collections import defaultdict
|
7
|
-
from typing import Optional
|
8
7
|
|
9
8
|
from reconcile.gql_definitions.common.ocm_environments import (
|
10
9
|
query as ocm_environment_query,
|
@@ -56,8 +55,8 @@ from reconcile.utils.runtime.integration import (
|
|
56
55
|
|
57
56
|
|
58
57
|
class OCMUserManagementIntegrationParams(PydanticRunParams):
|
59
|
-
ocm_environment:
|
60
|
-
ocm_organization_ids:
|
58
|
+
ocm_environment: str | None = None
|
59
|
+
ocm_organization_ids: set[str] | None = None
|
61
60
|
group_provider_specs: list[str]
|
62
61
|
|
63
62
|
|
@@ -88,7 +87,7 @@ class OCMUserManagementIntegration(
|
|
88
87
|
|
89
88
|
@abstractmethod
|
90
89
|
def get_user_mgmt_config_for_ocm_env(
|
91
|
-
self, ocm_env: OCMEnvironment, org_ids:
|
90
|
+
self, ocm_env: OCMEnvironment, org_ids: set[str] | None
|
92
91
|
) -> dict[str, OrganizationUserManagementConfiguration]:
|
93
92
|
"""
|
94
93
|
Discover cluster user mgmt configurations in the given OCM environment.
|
reconcile/oum/labelset.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
from collections import defaultdict
|
2
|
-
from typing import Optional
|
3
2
|
|
4
3
|
from pydantic import BaseModel
|
5
4
|
|
@@ -23,7 +22,7 @@ class _GroupMappingLabelset(BaseModel):
|
|
23
22
|
the sre-capabilities.user-mgmt.$provider prefix.
|
24
23
|
"""
|
25
24
|
|
26
|
-
authz_roles:
|
25
|
+
authz_roles: dict[str, CSV] | None = sre_capability_labels.labelset_groupfield(
|
27
26
|
group_prefix="authz."
|
28
27
|
)
|
29
28
|
|
reconcile/oum/metrics.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from enum import
|
1
|
+
from enum import StrEnum
|
2
2
|
|
3
3
|
from pydantic import BaseModel
|
4
4
|
|
@@ -56,7 +56,7 @@ class OCMUserManagementOrganizationActionCounter(
|
|
56
56
|
):
|
57
57
|
"Counter for the number of actions taken for an OCM organization"
|
58
58
|
|
59
|
-
class Action(
|
59
|
+
class Action(StrEnum):
|
60
60
|
AddUser = "add-user"
|
61
61
|
RemoveUser = "remove-user"
|
62
62
|
|
reconcile/oum/models.py
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
from collections import defaultdict
|
2
|
-
from typing import Optional
|
3
2
|
|
4
3
|
from pydantic import (
|
5
4
|
BaseModel,
|
@@ -64,7 +63,7 @@ class ClusterRoleReconcileResult(BaseModel):
|
|
64
63
|
|
65
64
|
users_added: int = 0
|
66
65
|
users_removed: int = 0
|
67
|
-
error:
|
66
|
+
error: Exception | None = None
|
68
67
|
|
69
68
|
class Config:
|
70
69
|
arbitrary_types_allowed = True
|
reconcile/oum/standalone.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
from collections import defaultdict
|
3
3
|
from datetime import timedelta
|
4
|
-
from typing import Optional
|
5
4
|
|
6
5
|
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
|
7
6
|
from reconcile.oum.base import OCMUserManagementIntegration
|
@@ -36,7 +35,7 @@ class OCMStandaloneUserManagementIntegration(OCMUserManagementIntegration):
|
|
36
35
|
return "ocm-standalone-user-management"
|
37
36
|
|
38
37
|
def get_user_mgmt_config_for_ocm_env(
|
39
|
-
self, ocm_env: OCMEnvironment, org_ids:
|
38
|
+
self, ocm_env: OCMEnvironment, org_ids: set[str] | None
|
40
39
|
) -> dict[str, OrganizationUserManagementConfiguration]:
|
41
40
|
ocm_api = init_ocm_base_client(ocm_env, self.secret_reader)
|
42
41
|
clusters_by_org = discover_clusters(
|
@@ -139,7 +138,7 @@ def user_mgmt_label_key(config_atom: str) -> str:
|
|
139
138
|
|
140
139
|
def discover_clusters(
|
141
140
|
ocm_api: OCMBaseClient,
|
142
|
-
org_ids:
|
141
|
+
org_ids: set[str] | None = None,
|
143
142
|
) -> dict[str, list[ClusterDetails]]:
|
144
143
|
"""
|
145
144
|
Discover all clusters with user management enabled on their subscription
|
@@ -6,10 +6,7 @@ from collections.abc import (
|
|
6
6
|
Iterable,
|
7
7
|
Mapping,
|
8
8
|
)
|
9
|
-
from typing import
|
10
|
-
Any,
|
11
|
-
Optional,
|
12
|
-
)
|
9
|
+
from typing import Any
|
13
10
|
|
14
11
|
import yaml
|
15
12
|
from deepdiff import DeepHash
|
@@ -58,8 +55,8 @@ class Test(BaseModel):
|
|
58
55
|
rule_path: str
|
59
56
|
rule: dict
|
60
57
|
rule_length: int
|
61
|
-
tests:
|
62
|
-
result:
|
58
|
+
tests: list[TestContent] | None
|
59
|
+
result: CommandExecutionResult | None = None
|
63
60
|
|
64
61
|
|
65
62
|
class RuleToFetch(BaseModel):
|
@@ -122,7 +119,7 @@ def fetch_rule_and_tests(
|
|
122
119
|
def get_rules_and_tests(
|
123
120
|
vault_settings: AppInterfaceSettingsV1,
|
124
121
|
thread_pool_size: int,
|
125
|
-
cluster_names:
|
122
|
+
cluster_names: Iterable[str] | None = None,
|
126
123
|
) -> list[Test]:
|
127
124
|
"""Iterates through all namespaces and returns a list of tests to run"""
|
128
125
|
namespace_with_prom_rules, _ = orb.get_namespaces(
|
@@ -225,7 +222,7 @@ def check_rules_and_tests(
|
|
225
222
|
vault_settings: AppInterfaceSettingsV1,
|
226
223
|
alerting_services: Iterable[str],
|
227
224
|
thread_pool_size: int,
|
228
|
-
cluster_names:
|
225
|
+
cluster_names: Iterable[str] | None = None,
|
229
226
|
) -> list[Test]:
|
230
227
|
"""Fetch rules and associated tests, run checks on rules and tests if they exist
|
231
228
|
and return a list of failed checks/tests"""
|
@@ -247,7 +244,7 @@ def check_rules_and_tests(
|
|
247
244
|
|
248
245
|
|
249
246
|
def run(
|
250
|
-
dry_run: bool, thread_pool_size: int, cluster_names:
|
247
|
+
dry_run: bool, thread_pool_size: int, cluster_names: Iterable[str] | None = None
|
251
248
|
) -> None:
|
252
249
|
"""Check prometheus rules syntax and run the tests associated to them"""
|
253
250
|
orb.QONTRACT_INTEGRATION = QONTRACT_INTEGRATION
|
reconcile/quay_membership.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
3
|
from collections.abc import Sequence
|
4
|
-
from typing import Union
|
5
4
|
|
6
5
|
from reconcile.gql_definitions.quay_membership import quay_membership
|
7
6
|
from reconcile.gql_definitions.quay_membership.quay_membership import (
|
@@ -81,7 +80,7 @@ def fetch_current_state(quay_api_store):
|
|
81
80
|
return state
|
82
81
|
|
83
82
|
|
84
|
-
def get_usernames(users: Sequence[
|
83
|
+
def get_usernames(users: Sequence[UserV1 | BotV1]) -> list[str]:
|
85
84
|
return [u.quay_username for u in users if u.quay_username]
|
86
85
|
|
87
86
|
|
reconcile/quay_mirror.py
CHANGED
@@ -11,7 +11,6 @@ from collections import (
|
|
11
11
|
from collections.abc import Iterable
|
12
12
|
from typing import (
|
13
13
|
Any,
|
14
|
-
Optional,
|
15
14
|
Self,
|
16
15
|
)
|
17
16
|
|
@@ -67,11 +66,11 @@ class QuayMirror:
|
|
67
66
|
def __init__(
|
68
67
|
self,
|
69
68
|
dry_run: bool = False,
|
70
|
-
control_file_dir:
|
71
|
-
compare_tags:
|
69
|
+
control_file_dir: str | None = None,
|
70
|
+
compare_tags: bool | None = None,
|
72
71
|
compare_tags_interval: int = 86400,
|
73
|
-
repository_urls:
|
74
|
-
exclude_repository_urls:
|
72
|
+
repository_urls: Iterable[str] | None = None,
|
73
|
+
exclude_repository_urls: Iterable[str] | None = None,
|
75
74
|
) -> None:
|
76
75
|
self.dry_run = dry_run
|
77
76
|
self.gqlapi = gql.get_api()
|
@@ -113,7 +112,7 @@ class QuayMirror:
|
|
113
112
|
tempfile.gettempdir(), CONTROL_FILE_NAME
|
114
113
|
)
|
115
114
|
|
116
|
-
self._has_enough_time_passed_since_last_compare_tags:
|
115
|
+
self._has_enough_time_passed_since_last_compare_tags: bool | None = None
|
117
116
|
self.session = requests.Session()
|
118
117
|
|
119
118
|
def __enter__(self) -> Self:
|
@@ -142,8 +141,8 @@ class QuayMirror:
|
|
142
141
|
@classmethod
|
143
142
|
def process_repos_query(
|
144
143
|
cls,
|
145
|
-
repository_urls:
|
146
|
-
exclude_repository_urls:
|
144
|
+
repository_urls: Iterable[str] | None = None,
|
145
|
+
exclude_repository_urls: Iterable[str] | None = None,
|
147
146
|
session: requests.Session | None = None,
|
148
147
|
timeout: int = REQUEST_TIMEOUT,
|
149
148
|
) -> defaultdict[OrgKey, list[dict[str, Any]]]:
|
@@ -370,7 +369,7 @@ class QuayMirror:
|
|
370
369
|
@staticmethod
|
371
370
|
def check_compare_tags_elapsed_time(path, interval) -> bool:
|
372
371
|
try:
|
373
|
-
with open(path,
|
372
|
+
with open(path, encoding="locale") as file_obj:
|
374
373
|
last_compare_tags = float(file_obj.read())
|
375
374
|
except FileNotFoundError:
|
376
375
|
return True
|
@@ -406,11 +405,11 @@ class QuayMirror:
|
|
406
405
|
|
407
406
|
def run(
|
408
407
|
dry_run,
|
409
|
-
control_file_dir:
|
410
|
-
compare_tags:
|
408
|
+
control_file_dir: str | None,
|
409
|
+
compare_tags: bool | None,
|
411
410
|
compare_tags_interval: int,
|
412
|
-
repository_urls:
|
413
|
-
exclude_repository_urls:
|
411
|
+
repository_urls: Iterable[str] | None,
|
412
|
+
exclude_repository_urls: Iterable[str] | None,
|
414
413
|
):
|
415
414
|
with QuayMirror(
|
416
415
|
dry_run,
|
reconcile/quay_mirror_org.py
CHANGED
@@ -3,7 +3,7 @@ import os
|
|
3
3
|
import tempfile
|
4
4
|
from collections import defaultdict
|
5
5
|
from collections.abc import Iterable
|
6
|
-
from typing import Any,
|
6
|
+
from typing import Any, Self
|
7
7
|
|
8
8
|
import requests
|
9
9
|
from sretoolbox.container import (
|
@@ -30,11 +30,11 @@ class QuayMirrorOrg:
|
|
30
30
|
def __init__(
|
31
31
|
self,
|
32
32
|
dry_run: bool = False,
|
33
|
-
control_file_dir:
|
34
|
-
compare_tags:
|
33
|
+
control_file_dir: str | None = None,
|
34
|
+
compare_tags: bool | None = None,
|
35
35
|
compare_tags_interval: int = 28800, # 8 hours
|
36
|
-
orgs:
|
37
|
-
repositories:
|
36
|
+
orgs: Iterable[str] | None = None,
|
37
|
+
repositories: Iterable[str] | None = None,
|
38
38
|
) -> None:
|
39
39
|
self.dry_run = dry_run
|
40
40
|
self.skopeo_cli = Skopeo(dry_run)
|
@@ -56,7 +56,7 @@ class QuayMirrorOrg:
|
|
56
56
|
tempfile.gettempdir(), CONTROL_FILE_NAME
|
57
57
|
)
|
58
58
|
|
59
|
-
self._has_enough_time_passed_since_last_compare_tags:
|
59
|
+
self._has_enough_time_passed_since_last_compare_tags: bool | None = None
|
60
60
|
self.session = requests.Session()
|
61
61
|
|
62
62
|
def __enter__(self) -> Self:
|
@@ -290,11 +290,11 @@ class QuayMirrorOrg:
|
|
290
290
|
|
291
291
|
def run(
|
292
292
|
dry_run,
|
293
|
-
control_file_dir:
|
294
|
-
compare_tags:
|
293
|
+
control_file_dir: str | None,
|
294
|
+
compare_tags: bool | None,
|
295
295
|
compare_tags_interval: int,
|
296
|
-
orgs:
|
297
|
-
repositories:
|
296
|
+
orgs: Iterable[str] | None,
|
297
|
+
repositories: Iterable[str] | None,
|
298
298
|
):
|
299
299
|
with QuayMirrorOrg(
|
300
300
|
dry_run,
|
reconcile/queries.py
CHANGED
@@ -5,10 +5,7 @@ import shlex
|
|
5
5
|
from collections.abc import Mapping
|
6
6
|
from dataclasses import dataclass
|
7
7
|
from textwrap import indent
|
8
|
-
from typing import
|
9
|
-
Any,
|
10
|
-
Optional,
|
11
|
-
)
|
8
|
+
from typing import Any
|
12
9
|
|
13
10
|
from jinja2 import Template
|
14
11
|
|
@@ -25,7 +22,7 @@ SECRET_READER_SETTINGS = """
|
|
25
22
|
"""
|
26
23
|
|
27
24
|
|
28
|
-
def get_secret_reader_settings() ->
|
25
|
+
def get_secret_reader_settings() -> Mapping[str, Any] | None:
|
29
26
|
"""Returns SecretReader settings"""
|
30
27
|
gqlapi = gql.get_api()
|
31
28
|
settings = gqlapi.query(SECRET_READER_SETTINGS)["settings"]
|
@@ -598,7 +595,7 @@ def get_queue_aws_accounts():
|
|
598
595
|
return get_aws_accounts(uid=uid)
|
599
596
|
|
600
597
|
|
601
|
-
def get_jumphosts(hostname:
|
598
|
+
def get_jumphosts(hostname: str | None = None) -> JumphostsQueryData:
|
602
599
|
"""Returns all jumphosts"""
|
603
600
|
variables = {}
|
604
601
|
# The dictionary must be empty if no hostname is set.
|
@@ -2289,7 +2286,7 @@ JIRA_BOARDS_QUERY = """
|
|
2289
2286
|
"""
|
2290
2287
|
|
2291
2288
|
|
2292
|
-
def get_jira_boards(with_slack:
|
2289
|
+
def get_jira_boards(with_slack: bool | None = True):
|
2293
2290
|
"""Returns Jira boards resources defined in app-interface"""
|
2294
2291
|
gqlapi = gql.get_api()
|
2295
2292
|
query = Template(JIRA_BOARDS_QUERY).render(with_slack=with_slack)
|
reconcile/resource_scraper.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
3
|
from typing import (
|
4
|
-
Optional,
|
5
4
|
cast,
|
6
5
|
)
|
7
6
|
|
@@ -23,9 +22,9 @@ QONTRACT_INTEGRATION = "resource-scraper"
|
|
23
22
|
|
24
23
|
def run(
|
25
24
|
dry_run: bool,
|
26
|
-
namespace_name:
|
27
|
-
resource_kind:
|
28
|
-
vault_output_path:
|
25
|
+
namespace_name: str | None,
|
26
|
+
resource_kind: str | None,
|
27
|
+
vault_output_path: str | None,
|
29
28
|
) -> None:
|
30
29
|
"""Get resources from clusters and store in Vault."""
|
31
30
|
if not namespace_name:
|
reconcile/rhidp/common.py
CHANGED
@@ -3,7 +3,7 @@ from collections.abc import (
|
|
3
3
|
Iterable,
|
4
4
|
MutableMapping,
|
5
5
|
)
|
6
|
-
from enum import
|
6
|
+
from enum import StrEnum
|
7
7
|
from typing import Any
|
8
8
|
from urllib.parse import urlparse
|
9
9
|
|
@@ -44,7 +44,7 @@ ISSUER_LABEL_KEY = sre_capability_label_key("rhidp", "issuer")
|
|
44
44
|
AUTH_NAME_LABEL_KEY = sre_capability_label_key("rhidp", "name")
|
45
45
|
|
46
46
|
|
47
|
-
class StatusValue(
|
47
|
+
class StatusValue(StrEnum):
|
48
48
|
# rhidp and oidc are enabled
|
49
49
|
ENABLED = "enabled"
|
50
50
|
# rhidp and oidc are disabled
|
@@ -1,5 +1,4 @@
|
|
1
1
|
from collections.abc import Callable
|
2
|
-
from typing import Optional
|
3
2
|
|
4
3
|
from sretoolbox.utils import threaded
|
5
4
|
|
@@ -95,8 +94,8 @@ class SaasAutoPromotionsManager:
|
|
95
94
|
|
96
95
|
def init_external_dependencies(
|
97
96
|
dry_run: bool,
|
98
|
-
env_name:
|
99
|
-
app_name:
|
97
|
+
env_name: str | None = None,
|
98
|
+
app_name: str | None = None,
|
100
99
|
) -> tuple[
|
101
100
|
PromotionState,
|
102
101
|
VCS,
|
@@ -169,9 +168,9 @@ def init_external_dependencies(
|
|
169
168
|
def run(
|
170
169
|
dry_run: bool,
|
171
170
|
thread_pool_size: int,
|
172
|
-
env_name:
|
173
|
-
app_name:
|
174
|
-
defer:
|
171
|
+
env_name: str | None = None,
|
172
|
+
app_name: str | None = None,
|
173
|
+
defer: Callable | None = None,
|
175
174
|
) -> None:
|
176
175
|
(
|
177
176
|
deployment_state,
|
@@ -1,7 +1,6 @@
|
|
1
1
|
from collections.abc import Iterable
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from typing import Optional
|
5
4
|
|
6
5
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.open_merge_requests import (
|
7
6
|
OpenBatcherMergeRequest,
|
@@ -145,7 +144,7 @@ class Batcher:
|
|
145
144
|
if not unsubmitted_promotions:
|
146
145
|
return
|
147
146
|
|
148
|
-
batch_with_capacity:
|
147
|
+
batch_with_capacity: OpenBatcherMergeRequest | None = None
|
149
148
|
for mr in self._open_mrs:
|
150
149
|
if mr.is_batchable and len(mr.content_hashes) < batch_limit:
|
151
150
|
batch_with_capacity = mr
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
2
|
from datetime import datetime
|
3
|
-
from typing import Optional
|
4
3
|
|
5
4
|
from reconcile.utils.promotion_state import PromotionState
|
6
5
|
from reconcile.utils.secret_reader import HasSecret
|
@@ -18,7 +17,7 @@ class DeploymentInfo:
|
|
18
17
|
success: bool
|
19
18
|
saas_file: str
|
20
19
|
target_config_hash: str
|
21
|
-
check_in:
|
20
|
+
check_in: datetime | None
|
22
21
|
|
23
22
|
|
24
23
|
class Publisher:
|
@@ -37,10 +36,10 @@ class Publisher:
|
|
37
36
|
app_name: str,
|
38
37
|
namespace_name: str,
|
39
38
|
resource_template_name: str,
|
40
|
-
target_name:
|
39
|
+
target_name: str | None,
|
41
40
|
cluster_name: str,
|
42
|
-
auth_code:
|
43
|
-
publish_job_logs:
|
41
|
+
auth_code: HasSecret | None,
|
42
|
+
publish_job_logs: bool | None,
|
44
43
|
has_subscriber: bool = True,
|
45
44
|
):
|
46
45
|
self._ref = ref
|
@@ -48,7 +47,7 @@ class Publisher:
|
|
48
47
|
self._auth_code = auth_code
|
49
48
|
self.channels: set[str] = set()
|
50
49
|
self.commit_sha: str = ""
|
51
|
-
self.deployment_info_by_channel: dict[str,
|
50
|
+
self.deployment_info_by_channel: dict[str, DeploymentInfo | None] = {}
|
52
51
|
self.uid = uid
|
53
52
|
self.saas_name = saas_name
|
54
53
|
self.saas_file_path = saas_file_path
|
@@ -2,8 +2,7 @@ import hashlib
|
|
2
2
|
import logging
|
3
3
|
from collections.abc import Iterable
|
4
4
|
from dataclasses import dataclass
|
5
|
-
from datetime import datetime, timedelta
|
6
|
-
from typing import Optional
|
5
|
+
from datetime import UTC, datetime, timedelta
|
7
6
|
|
8
7
|
from reconcile.gql_definitions.fragments.saas_target_namespace import (
|
9
8
|
SaasTargetNamespace,
|
@@ -45,6 +44,7 @@ class Subscriber:
|
|
45
44
|
use_target_config_hash: bool,
|
46
45
|
uid: str,
|
47
46
|
soak_days: int,
|
47
|
+
blocked_versions: set[str],
|
48
48
|
):
|
49
49
|
self.saas_name = saas_name
|
50
50
|
self.template_name = template_name
|
@@ -59,6 +59,7 @@ class Subscriber:
|
|
59
59
|
self.soak_days = soak_days
|
60
60
|
self._content_hash = ""
|
61
61
|
self._use_target_config_hash = use_target_config_hash
|
62
|
+
self._blocked_versions = blocked_versions
|
62
63
|
|
63
64
|
def has_diff(self) -> bool:
|
64
65
|
current_hashes = {
|
@@ -80,7 +81,7 @@ class Subscriber:
|
|
80
81
|
|
81
82
|
def _validate_deployment(
|
82
83
|
self, publisher: Publisher, channel: Channel
|
83
|
-
) ->
|
84
|
+
) -> DeploymentInfo | None:
|
84
85
|
deployment_info = publisher.deployment_info_by_channel.get(channel.name)
|
85
86
|
if not deployment_info:
|
86
87
|
logging.info(
|
@@ -103,7 +104,7 @@ class Subscriber:
|
|
103
104
|
We accumulate the time a ref is running on all publishers for this subscriber.
|
104
105
|
We compare that accumulated time with the soak_days setting of the subscriber.
|
105
106
|
"""
|
106
|
-
now = datetime.now(
|
107
|
+
now = datetime.now(UTC)
|
107
108
|
delta = timedelta(days=0)
|
108
109
|
for channel in self.channels:
|
109
110
|
for publisher in channel.publishers:
|
@@ -142,22 +143,42 @@ class Subscriber:
|
|
142
143
|
break
|
143
144
|
publisher_refs.add(publisher.commit_sha)
|
144
145
|
|
145
|
-
|
146
|
+
# By default we keep current state
|
147
|
+
self.desired_ref = self.ref
|
148
|
+
|
149
|
+
if any_bad_deployment:
|
150
|
+
logging.info(
|
151
|
+
"Subscriber at path %s promotion stopped because of bad publisher deployment",
|
152
|
+
self.target_file_path,
|
153
|
+
)
|
154
|
+
return
|
155
|
+
|
156
|
+
if len(publisher_refs) != 1:
|
146
157
|
logging.info(
|
147
158
|
"Publishers for subscriber at path %s have mismatching refs: %s",
|
148
159
|
self.target_file_path,
|
149
160
|
publisher_refs,
|
150
161
|
)
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
162
|
+
return
|
163
|
+
|
164
|
+
if not self._passed_accumulated_soak_days():
|
165
|
+
logging.debug(
|
166
|
+
"Subscriber at path %s promotion stopped because of soak days",
|
167
|
+
self.target_file_path,
|
168
|
+
)
|
169
|
+
return
|
170
|
+
|
171
|
+
desired_ref = next(iter(publisher_refs))
|
172
|
+
if desired_ref in self._blocked_versions:
|
173
|
+
logging.info(
|
174
|
+
"Subscriber at path %s promotion stopped because of blocked ref: %s",
|
175
|
+
self.target_file_path,
|
176
|
+
desired_ref,
|
177
|
+
)
|
178
|
+
return
|
179
|
+
|
180
|
+
# Passed all gates -> lets promote desired ref
|
181
|
+
self.desired_ref = desired_ref
|
161
182
|
|
162
183
|
def _compute_desired_config_hashes(self) -> None:
|
163
184
|
"""
|
@@ -86,6 +86,10 @@ class SaasFilesInventory:
|
|
86
86
|
|
87
87
|
def _assemble_subscribers_with_auto_promotions(self) -> None:
|
88
88
|
for saas_file in self._saas_files:
|
89
|
+
blocked_versions: dict[str, set[str]] = {}
|
90
|
+
for code_component in saas_file.app.code_components or []:
|
91
|
+
for version in code_component.blocked_versions or []:
|
92
|
+
blocked_versions.setdefault(code_component.url, set()).add(version)
|
89
93
|
for resource_template in saas_file.resource_templates:
|
90
94
|
for target in resource_template.targets:
|
91
95
|
file_path = target.path if target.path else saas_file.path
|
@@ -98,6 +102,7 @@ class SaasFilesInventory:
|
|
98
102
|
soak_days = (
|
99
103
|
target.promotion.soak_days if target.promotion.soak_days else 0
|
100
104
|
)
|
105
|
+
resource_template.url
|
101
106
|
subscriber = Subscriber(
|
102
107
|
uid=target.uid(
|
103
108
|
parent_saas_file_name=saas_file.name,
|
@@ -109,6 +114,9 @@ class SaasFilesInventory:
|
|
109
114
|
ref=target.ref,
|
110
115
|
target_namespace=target.namespace,
|
111
116
|
soak_days=soak_days,
|
117
|
+
blocked_versions=blocked_versions.get(
|
118
|
+
resource_template.url, set()
|
119
|
+
),
|
112
120
|
# Note: this will be refactored at a later point.
|
113
121
|
# https://issues.redhat.com/browse/APPSRE-7516
|
114
122
|
use_target_config_hash=bool(saas_file.publish_job_logs),
|
reconcile/saas_file_validator.py
CHANGED
@@ -1,9 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
import sys
|
3
|
-
from
|
4
|
-
Callable,
|
5
|
-
Optional,
|
6
|
-
)
|
3
|
+
from collections.abc import Callable
|
7
4
|
|
8
5
|
from reconcile.jenkins_job_builder import init_jjb
|
9
6
|
from reconcile.status import ExitCodes
|
@@ -26,7 +23,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
|
26
23
|
|
27
24
|
|
28
25
|
@defer
|
29
|
-
def run(dry_run: bool, defer:
|
26
|
+
def run(dry_run: bool, defer: Callable | None = None) -> None:
|
30
27
|
vault_settings = get_app_interface_vault_settings()
|
31
28
|
saasherder_settings = get_saasherder_settings()
|
32
29
|
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|