qontract-reconcile 0.10.1rc1202__py3-none-any.whl → 0.10.2.dev2__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.dev2.dist-info/METADATA +500 -0
- {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev2.dist-info}/RECORD +12 -130
- {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev2.dist-info}/WHEEL +1 -2
- {qontract_reconcile-0.10.1rc1202.dist-info → qontract_reconcile-0.10.2.dev2.dist-info}/entry_points.txt +1 -0
- reconcile/aws_account_manager/README.md +5 -0
- reconcile/change_owners/README.md +34 -0
- reconcile/glitchtip/README.md +150 -0
- reconcile/gql_definitions/introspection.json +51176 -0
- reconcile/run_integration.py +293 -0
- reconcile/utils/binary.py +2 -2
- reconcile/utils/mr/README.md +198 -0
- reconcile/utils/oc_map.py +2 -2
- tools/qontract_cli.py +0 -0
- qontract_reconcile-0.10.1rc1202.dist-info/METADATA +0 -64
- qontract_reconcile-0.10.1rc1202.dist-info/top_level.txt +0 -3
- reconcile/test/__init__.py +0 -0
- reconcile/test/conftest.py +0 -157
- reconcile/test/fixtures.py +0 -24
- reconcile/test/saas_auto_promotions_manager/__init__.py +0 -0
- reconcile/test/saas_auto_promotions_manager/conftest.py +0 -170
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py +0 -0
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py +0 -0
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -115
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -19
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py +0 -66
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -86
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parser.py +0 -352
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_reconciler.py +0 -494
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/__init__.py +0 -0
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -25
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -37
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -81
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -61
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_json_path_selector.py +0 -74
- reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -52
- reconcile/test/saas_auto_promotions_manager/utils/__init__.py +0 -0
- reconcile/test/test_acs_notifiers.py +0 -393
- reconcile/test/test_acs_policies.py +0 -497
- reconcile/test/test_acs_rbac.py +0 -865
- reconcile/test/test_aggregated_list.py +0 -237
- reconcile/test/test_amtool.py +0 -37
- reconcile/test/test_aws_ami_cleanup.py +0 -230
- reconcile/test/test_aws_ami_share.py +0 -68
- reconcile/test/test_aws_cloudwatch_log_retention.py +0 -434
- reconcile/test/test_aws_iam_keys.py +0 -70
- reconcile/test/test_aws_iam_password_reset.py +0 -35
- reconcile/test/test_aws_support_cases_sos.py +0 -23
- reconcile/test/test_checkpoint.py +0 -178
- reconcile/test/test_cli.py +0 -41
- reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
- reconcile/test/test_dashdotdb_dora.py +0 -245
- reconcile/test/test_database_access_manager.py +0 -660
- reconcile/test/test_deadmanssnitch.py +0 -290
- reconcile/test/test_gabi_authorized_users.py +0 -72
- reconcile/test/test_gcr_mirror.py +0 -14
- reconcile/test/test_github_org.py +0 -156
- reconcile/test/test_github_repo_invites.py +0 -119
- reconcile/test/test_gitlab_housekeeping.py +0 -333
- reconcile/test/test_gitlab_labeler.py +0 -126
- reconcile/test/test_gitlab_members.py +0 -219
- reconcile/test/test_gitlab_permissions.py +0 -164
- reconcile/test/test_instrumented_wrappers.py +0 -18
- reconcile/test/test_integrations_manager.py +0 -1252
- reconcile/test/test_jenkins_worker_fleets.py +0 -57
- reconcile/test/test_jira_permissions_validator.py +0 -519
- reconcile/test/test_jump_host.py +0 -114
- reconcile/test/test_ldap_users.py +0 -125
- reconcile/test/test_make.py +0 -28
- reconcile/test/test_ocm_additional_routers.py +0 -133
- reconcile/test/test_ocm_clusters.py +0 -798
- reconcile/test/test_ocm_clusters_manifest_updates.py +0 -87
- reconcile/test/test_ocm_machine_pools.py +0 -1103
- reconcile/test/test_ocm_update_recommended_version.py +0 -145
- reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -125
- reconcile/test/test_openshift_base.py +0 -1269
- reconcile/test/test_openshift_cluster_bots.py +0 -240
- reconcile/test/test_openshift_namespace_labels.py +0 -344
- reconcile/test/test_openshift_namespaces.py +0 -256
- reconcile/test/test_openshift_resource.py +0 -443
- reconcile/test/test_openshift_resources_base.py +0 -478
- reconcile/test/test_openshift_saas_deploy.py +0 -188
- reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -308
- reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +0 -65
- reconcile/test/test_openshift_serviceaccount_tokens.py +0 -282
- reconcile/test/test_openshift_tekton_resources.py +0 -265
- reconcile/test/test_openshift_upgrade_watcher.py +0 -223
- reconcile/test/test_prometheus_rules_tester.py +0 -151
- reconcile/test/test_quay_membership.py +0 -86
- reconcile/test/test_quay_mirror.py +0 -172
- reconcile/test/test_quay_mirror_org.py +0 -82
- reconcile/test/test_quay_repos.py +0 -59
- reconcile/test/test_queries.py +0 -53
- reconcile/test/test_repo_owners.py +0 -47
- reconcile/test/test_requests_sender.py +0 -139
- reconcile/test/test_saasherder.py +0 -1611
- reconcile/test/test_saasherder_allowed_secret_paths.py +0 -125
- reconcile/test/test_secret_reader.py +0 -153
- reconcile/test/test_slack_base.py +0 -183
- reconcile/test/test_slack_usergroups.py +0 -785
- reconcile/test/test_sql_query.py +0 -316
- reconcile/test/test_status_board.py +0 -258
- reconcile/test/test_terraform_aws_route53.py +0 -29
- reconcile/test/test_terraform_cloudflare_dns.py +0 -117
- reconcile/test/test_terraform_cloudflare_resources.py +0 -408
- reconcile/test/test_terraform_cloudflare_users.py +0 -747
- reconcile/test/test_terraform_repo.py +0 -440
- reconcile/test/test_terraform_resources.py +0 -519
- reconcile/test/test_terraform_tgw_attachments.py +0 -1295
- reconcile/test/test_terraform_users.py +0 -152
- reconcile/test/test_terraform_vpc_peerings.py +0 -576
- reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1434
- reconcile/test/test_three_way_diff_strategy.py +0 -131
- reconcile/test/test_utils_jinja2.py +0 -130
- reconcile/test/test_vault_replication.py +0 -534
- reconcile/test/test_vault_utils.py +0 -47
- reconcile/test/test_version_bump.py +0 -18
- reconcile/test/test_vpc_peerings_validator.py +0 -194
- reconcile/test/test_wrong_region.py +0 -78
- release/__init__.py +0 -0
- release/test_version.py +0 -50
- release/version.py +0 -104
- tools/cli_commands/test/__init__.py +0 -0
- tools/cli_commands/test/conftest.py +0 -332
- tools/cli_commands/test/test_aws_cost_report.py +0 -258
- tools/cli_commands/test/test_cost_management_api.py +0 -326
- tools/cli_commands/test/test_gpg_encrypt.py +0 -235
- tools/cli_commands/test/test_openshift_cost_optimization_report.py +0 -255
- tools/cli_commands/test/test_openshift_cost_report.py +0 -295
- tools/cli_commands/test/test_util.py +0 -70
- tools/test/__init__.py +0 -0
- tools/test/conftest.py +0 -77
- tools/test/test_app_interface_metrics_exporter.py +0 -48
- tools/test/test_erv2.py +0 -80
- tools/test/test_get_container_images.py +0 -230
- tools/test/test_qontract_cli.py +0 -197
- tools/test/test_saas_promotion_state.py +0 -187
- tools/test/test_sd_app_sre_alert_report.py +0 -74
- tools/test/test_sre_checkpoints.py +0 -79
reconcile/test/conftest.py
DELETED
@@ -1,157 +0,0 @@
|
|
1
|
-
import time
|
2
|
-
from collections.abc import (
|
3
|
-
Callable,
|
4
|
-
Iterable,
|
5
|
-
Mapping,
|
6
|
-
MutableMapping,
|
7
|
-
)
|
8
|
-
from pathlib import Path
|
9
|
-
from typing import Any
|
10
|
-
from unittest.mock import MagicMock, create_autospec
|
11
|
-
|
12
|
-
import pytest
|
13
|
-
from pydantic import BaseModel
|
14
|
-
from pydantic.error_wrappers import ValidationError
|
15
|
-
from pytest_httpserver import HTTPServer
|
16
|
-
|
17
|
-
from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
|
18
|
-
from reconcile.test.fixtures import Fixtures
|
19
|
-
from reconcile.utils.gql import GqlApi
|
20
|
-
from reconcile.utils.models import data_default_none
|
21
|
-
from reconcile.utils.state import State
|
22
|
-
|
23
|
-
|
24
|
-
@pytest.fixture
|
25
|
-
def patch_sleep(mocker):
|
26
|
-
yield mocker.patch.object(time, "sleep")
|
27
|
-
|
28
|
-
|
29
|
-
@pytest.fixture
|
30
|
-
def secret_reader(mocker) -> None:
|
31
|
-
mock_secretreader = mocker.patch(
|
32
|
-
"reconcile.utils.secret_reader.SecretReader", autospec=True
|
33
|
-
)
|
34
|
-
mock_secretreader.read.return_value = "secret"
|
35
|
-
mock_secretreader.read_secret.return_value = "secret"
|
36
|
-
return mock_secretreader
|
37
|
-
|
38
|
-
|
39
|
-
@pytest.fixture
|
40
|
-
def s3_state_builder() -> Callable[[Mapping], State]:
|
41
|
-
"""
|
42
|
-
Example input data:
|
43
|
-
{
|
44
|
-
"get": {
|
45
|
-
# This maps data being returned by get
|
46
|
-
"path/to/item": {
|
47
|
-
"some_key": "content",
|
48
|
-
"other_content": "content",
|
49
|
-
},
|
50
|
-
"other/path": {
|
51
|
-
"other": "data",
|
52
|
-
},
|
53
|
-
},
|
54
|
-
"ls": [
|
55
|
-
"/path/item1",
|
56
|
-
"/path/item2",
|
57
|
-
]
|
58
|
-
}
|
59
|
-
"""
|
60
|
-
|
61
|
-
def builder(data: Mapping) -> State:
|
62
|
-
def get(key: str, *args) -> dict:
|
63
|
-
try:
|
64
|
-
return data["get"][key]
|
65
|
-
except KeyError:
|
66
|
-
if args:
|
67
|
-
return args[0]
|
68
|
-
raise
|
69
|
-
|
70
|
-
def __getitem__(self, key: str) -> dict:
|
71
|
-
return get(key)
|
72
|
-
|
73
|
-
state = create_autospec(spec=State)
|
74
|
-
mock_get = MagicMock(side_effect=get)
|
75
|
-
state.get = mock_get
|
76
|
-
state.__getitem__ = __getitem__
|
77
|
-
state.ls.side_effect = [data.get("ls", [])]
|
78
|
-
return state
|
79
|
-
|
80
|
-
return builder
|
81
|
-
|
82
|
-
|
83
|
-
@pytest.fixture
|
84
|
-
def vault_secret():
|
85
|
-
return VaultSecret(
|
86
|
-
path="path/test",
|
87
|
-
field="key",
|
88
|
-
format=None,
|
89
|
-
version=None,
|
90
|
-
)
|
91
|
-
|
92
|
-
|
93
|
-
@pytest.fixture
|
94
|
-
def data_factory() -> Callable[
|
95
|
-
[type[BaseModel], MutableMapping[str, Any] | None],
|
96
|
-
MutableMapping[str, Any],
|
97
|
-
]:
|
98
|
-
"""Set default values to None."""
|
99
|
-
|
100
|
-
def _data_factory(
|
101
|
-
klass: type[BaseModel], data: MutableMapping[str, Any] | None = None
|
102
|
-
) -> MutableMapping[str, Any]:
|
103
|
-
return data_default_none(klass, data or {})
|
104
|
-
|
105
|
-
return _data_factory
|
106
|
-
|
107
|
-
|
108
|
-
class GQLClassFactoryError(Exception):
|
109
|
-
pass
|
110
|
-
|
111
|
-
|
112
|
-
@pytest.fixture
|
113
|
-
def gql_class_factory() -> Callable[
|
114
|
-
[type[BaseModel], MutableMapping[str, Any] | None],
|
115
|
-
BaseModel,
|
116
|
-
]:
|
117
|
-
"""Create a GQL class from a fixture and set default values to None."""
|
118
|
-
|
119
|
-
def _gql_class_factory(
|
120
|
-
klass: type[BaseModel], data: MutableMapping[str, Any] | None = None
|
121
|
-
) -> BaseModel:
|
122
|
-
try:
|
123
|
-
return klass(**data_default_none(klass, data or {}))
|
124
|
-
except ValidationError as e:
|
125
|
-
msg = "[gql_class_factory] Your given data does not match the class ...\n"
|
126
|
-
msg += "\n".join([str(m) for m in list(e.raw_errors)])
|
127
|
-
raise GQLClassFactoryError(msg) from e
|
128
|
-
|
129
|
-
return _gql_class_factory
|
130
|
-
|
131
|
-
|
132
|
-
@pytest.fixture
|
133
|
-
def gql_api_builder() -> Callable[[Mapping | None], GqlApi]:
|
134
|
-
def builder(data: Mapping | None = None) -> GqlApi:
|
135
|
-
gql_api = create_autospec(GqlApi)
|
136
|
-
gql_api.query.return_value = data
|
137
|
-
return gql_api
|
138
|
-
|
139
|
-
return builder
|
140
|
-
|
141
|
-
|
142
|
-
@pytest.fixture
|
143
|
-
def set_httpserver_responses_based_on_fixture(httpserver: HTTPServer) -> Callable:
|
144
|
-
"""Create httpserver responses based fixture files."""
|
145
|
-
|
146
|
-
def _(fx: Fixtures, paths: Iterable[str]) -> None:
|
147
|
-
for path in paths:
|
148
|
-
for method in ["get", "post", "put", "patch", "delete"]:
|
149
|
-
method_file = Path(fx.path(path.lstrip("/"))) / f"{method}.json"
|
150
|
-
if method_file.exists():
|
151
|
-
httpserver.expect_oneshot_request(
|
152
|
-
path, method=method
|
153
|
-
).respond_with_data(
|
154
|
-
method_file.read_text(), content_type="text/json"
|
155
|
-
)
|
156
|
-
|
157
|
-
return _
|
reconcile/test/fixtures.py
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
import json
|
2
|
-
import os
|
3
|
-
|
4
|
-
import anymarkup
|
5
|
-
|
6
|
-
|
7
|
-
class Fixtures:
|
8
|
-
def __init__(self, base_path):
|
9
|
-
self.base_path = base_path
|
10
|
-
|
11
|
-
def path(self, fixture):
|
12
|
-
return os.path.join(
|
13
|
-
os.path.dirname(__file__), "fixtures", self.base_path, fixture
|
14
|
-
)
|
15
|
-
|
16
|
-
def get(self, fixture):
|
17
|
-
with open(self.path(fixture), encoding="locale") as f:
|
18
|
-
return f.read().strip()
|
19
|
-
|
20
|
-
def get_anymarkup(self, fixture):
|
21
|
-
return anymarkup.parse(self.get(fixture), force_types=None)
|
22
|
-
|
23
|
-
def get_json(self, fixture):
|
24
|
-
return json.loads(self.get(fixture))
|
File without changes
|
@@ -1,170 +0,0 @@
|
|
1
|
-
from collections import defaultdict
|
2
|
-
from collections.abc import (
|
3
|
-
Callable,
|
4
|
-
Iterable,
|
5
|
-
Mapping,
|
6
|
-
MutableMapping,
|
7
|
-
)
|
8
|
-
from datetime import UTC, datetime
|
9
|
-
from typing import Any
|
10
|
-
from unittest.mock import (
|
11
|
-
MagicMock,
|
12
|
-
create_autospec,
|
13
|
-
)
|
14
|
-
|
15
|
-
import pytest
|
16
|
-
|
17
|
-
from reconcile.gql_definitions.fragments.saas_target_namespace import (
|
18
|
-
SaasTargetNamespace,
|
19
|
-
)
|
20
|
-
from reconcile.saas_auto_promotions_manager.publisher import DeploymentInfo, Publisher
|
21
|
-
from reconcile.saas_auto_promotions_manager.subscriber import (
|
22
|
-
Channel,
|
23
|
-
ConfigHash,
|
24
|
-
Subscriber,
|
25
|
-
)
|
26
|
-
from reconcile.typed_queries.saas_files import SaasFile
|
27
|
-
from reconcile.utils.gitlab_api import GitLabApi
|
28
|
-
from reconcile.utils.promotion_state import (
|
29
|
-
PromotionData,
|
30
|
-
PromotionState,
|
31
|
-
)
|
32
|
-
from reconcile.utils.vcs import VCS
|
33
|
-
|
34
|
-
|
35
|
-
@pytest.fixture
|
36
|
-
def saas_files_builder(
|
37
|
-
gql_class_factory: Callable[[type[SaasFile], Mapping], SaasFile],
|
38
|
-
) -> Callable[[Iterable[MutableMapping]], list[SaasFile]]:
|
39
|
-
def builder(data: Iterable[MutableMapping]) -> list[SaasFile]:
|
40
|
-
for d in data:
|
41
|
-
if "app" not in d:
|
42
|
-
d["app"] = {}
|
43
|
-
if "pipelinesProvider" not in d:
|
44
|
-
d["pipelinesProvider"] = {}
|
45
|
-
if "managedResourceTypes" not in d:
|
46
|
-
d["managedResourceTypes"] = []
|
47
|
-
if "imagePatterns" not in d:
|
48
|
-
d["imagePatterns"] = []
|
49
|
-
for rt in d.get("resourceTemplates", []):
|
50
|
-
for t in rt.get("targets", []):
|
51
|
-
ns = t["namespace"]
|
52
|
-
if "name" not in ns:
|
53
|
-
ns["name"] = "some_name"
|
54
|
-
if "environment" not in ns:
|
55
|
-
ns["environment"] = {}
|
56
|
-
if "app" not in ns:
|
57
|
-
ns["app"] = {}
|
58
|
-
if "cluster" not in ns:
|
59
|
-
ns["cluster"] = {}
|
60
|
-
return [gql_class_factory(SaasFile, d) for d in data]
|
61
|
-
|
62
|
-
return builder
|
63
|
-
|
64
|
-
|
65
|
-
@pytest.fixture
|
66
|
-
def vcs_builder() -> Callable[..., VCS]:
|
67
|
-
def builder() -> VCS:
|
68
|
-
vcs = create_autospec(spec=VCS)
|
69
|
-
vcs.get_commit_sha.side_effect = ["new_sha"] * 100
|
70
|
-
vcs._app_interface_api = MagicMock()
|
71
|
-
return vcs
|
72
|
-
|
73
|
-
return builder
|
74
|
-
|
75
|
-
|
76
|
-
@pytest.fixture
|
77
|
-
def gql_client_builder() -> Callable[..., GitLabApi]:
|
78
|
-
def builder() -> GitLabApi:
|
79
|
-
api = create_autospec(spec=GitLabApi)
|
80
|
-
api.project = MagicMock()
|
81
|
-
api.project.mergerequests = MagicMock()
|
82
|
-
api.project.mergerequests.create.side_effect = []
|
83
|
-
return api
|
84
|
-
|
85
|
-
return builder
|
86
|
-
|
87
|
-
|
88
|
-
@pytest.fixture
|
89
|
-
def saas_target_namespace_builder(
|
90
|
-
gql_class_factory: Callable[..., SaasTargetNamespace],
|
91
|
-
) -> Callable[..., SaasTargetNamespace]:
|
92
|
-
def builder(data: MutableMapping) -> SaasTargetNamespace:
|
93
|
-
if "environment" not in data:
|
94
|
-
data["environment"] = {}
|
95
|
-
if "app" not in data:
|
96
|
-
data["app"] = {}
|
97
|
-
if "cluster" not in data:
|
98
|
-
data["cluster"] = {}
|
99
|
-
return gql_class_factory(SaasTargetNamespace, data)
|
100
|
-
|
101
|
-
return builder
|
102
|
-
|
103
|
-
|
104
|
-
@pytest.fixture
|
105
|
-
def promotion_state_builder() -> Callable[..., PromotionState]:
|
106
|
-
def builder(data: Iterable[PromotionData]) -> PromotionState:
|
107
|
-
promotion_state = create_autospec(spec=PromotionState)
|
108
|
-
promotion_state.get_promotion_data.side_effect = data
|
109
|
-
return promotion_state
|
110
|
-
|
111
|
-
return builder
|
112
|
-
|
113
|
-
|
114
|
-
@pytest.fixture
|
115
|
-
def subscriber_builder(
|
116
|
-
saas_target_namespace_builder: Callable[..., SaasTargetNamespace],
|
117
|
-
) -> Callable[[Mapping[str, Any]], Subscriber]:
|
118
|
-
def builder(data: Mapping[str, Any]) -> Subscriber:
|
119
|
-
channels: list[Channel] = []
|
120
|
-
for channel_name, channel_data in data.get("CHANNELS", {}).items():
|
121
|
-
channel = Channel(name=channel_name, publishers=[])
|
122
|
-
for publisher_name, publisher_data in channel_data.items():
|
123
|
-
publisher = Publisher(
|
124
|
-
ref="",
|
125
|
-
uid="",
|
126
|
-
repo_url="",
|
127
|
-
cluster_name="",
|
128
|
-
namespace_name="",
|
129
|
-
saas_name="",
|
130
|
-
saas_file_path="",
|
131
|
-
app_name="",
|
132
|
-
resource_template_name="",
|
133
|
-
target_name=None,
|
134
|
-
redeploy_on_config_change=True,
|
135
|
-
has_subscriber=True,
|
136
|
-
auth_code=None,
|
137
|
-
)
|
138
|
-
publisher.commit_sha = publisher_data["REAL_WORLD_SHA"]
|
139
|
-
publisher.deployment_info_by_channel[channel_name] = DeploymentInfo(
|
140
|
-
success=publisher_data.get("SUCCESSFUL_DEPLOYMENT", True),
|
141
|
-
target_config_hash=publisher_data.get("CONFIG_HASH", ""),
|
142
|
-
saas_file=publisher_name,
|
143
|
-
check_in=publisher_data.get("CHECK_IN", datetime.now(UTC)),
|
144
|
-
)
|
145
|
-
channel.publishers.append(publisher)
|
146
|
-
channels.append(channel)
|
147
|
-
cur_config_hashes_by_channel: dict[str, list[ConfigHash]] = defaultdict(list)
|
148
|
-
for cur_config_hash in data.get("CUR_CONFIG_HASHES", []):
|
149
|
-
cur_config_hashes_by_channel[cur_config_hash.channel].append(
|
150
|
-
cur_config_hash
|
151
|
-
)
|
152
|
-
subscriber = Subscriber(
|
153
|
-
uid=data.get("SUB_UID", "default"),
|
154
|
-
target_namespace=saas_target_namespace_builder(data.get("NAMESPACE", {})),
|
155
|
-
ref=data.get("CUR_SUBSCRIBER_REF", ""),
|
156
|
-
saas_name="",
|
157
|
-
target_file_path=data.get("TARGET_FILE_PATH", ""),
|
158
|
-
template_name="",
|
159
|
-
use_target_config_hash=data.get("USE_TARGET_CONFIG_HASH", True),
|
160
|
-
soak_days=data.get("SOAK_DAYS", 0),
|
161
|
-
blocked_versions=data.get("BLOCKED_VERSIONS", {}),
|
162
|
-
schedule=data.get("SCHEDULE", "* * * * *"),
|
163
|
-
)
|
164
|
-
subscriber.channels = channels
|
165
|
-
subscriber.config_hashes_by_channel_name = cur_config_hashes_by_channel
|
166
|
-
subscriber.desired_ref = data.get("DESIRED_REF", "")
|
167
|
-
subscriber.desired_hashes = data.get("DESIRED_TARGET_HASHES", [])
|
168
|
-
return subscriber
|
169
|
-
|
170
|
-
return builder
|
File without changes
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py
DELETED
File without changes
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py
DELETED
@@ -1,115 +0,0 @@
|
|
1
|
-
from collections.abc import (
|
2
|
-
Callable,
|
3
|
-
Iterable,
|
4
|
-
Mapping,
|
5
|
-
)
|
6
|
-
from unittest.mock import create_autospec
|
7
|
-
|
8
|
-
import pytest
|
9
|
-
from gitlab.v4.objects import ProjectMergeRequest
|
10
|
-
|
11
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.batcher import (
|
12
|
-
Batcher,
|
13
|
-
Diff,
|
14
|
-
)
|
15
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.merge_request_manager_v2 import (
|
16
|
-
SAPM_LABEL,
|
17
|
-
)
|
18
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.mr_parser import (
|
19
|
-
MRParser,
|
20
|
-
OpenBatcherMergeRequest,
|
21
|
-
)
|
22
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
23
|
-
CHANNELS_REF,
|
24
|
-
CONTENT_HASHES,
|
25
|
-
IS_BATCHABLE,
|
26
|
-
PROMOTION_DATA_SEPARATOR,
|
27
|
-
SAPM_VERSION,
|
28
|
-
VERSION_REF,
|
29
|
-
Renderer,
|
30
|
-
)
|
31
|
-
from reconcile.utils.vcs import VCS, MRCheckStatus
|
32
|
-
|
33
|
-
from .data_keys import (
|
34
|
-
DESCRIPTION,
|
35
|
-
HAS_CONFLICTS,
|
36
|
-
LABELS,
|
37
|
-
OPEN_MERGE_REQUESTS,
|
38
|
-
PIPELINE_RESULTS,
|
39
|
-
SUBSCRIBER_BATCHABLE,
|
40
|
-
SUBSCRIBER_CHANNELS,
|
41
|
-
SUBSCRIBER_CONTENT_HASH,
|
42
|
-
)
|
43
|
-
|
44
|
-
|
45
|
-
@pytest.fixture
|
46
|
-
def mr_builder() -> Callable[[Mapping], ProjectMergeRequest]:
|
47
|
-
def builder(data: Mapping) -> ProjectMergeRequest:
|
48
|
-
mr = create_autospec(spec=ProjectMergeRequest)
|
49
|
-
if CONTENT_HASHES in data:
|
50
|
-
# Generate with valid defaults
|
51
|
-
mr.attributes = {
|
52
|
-
"labels": [SAPM_LABEL],
|
53
|
-
"description": f"""
|
54
|
-
{PROMOTION_DATA_SEPARATOR}
|
55
|
-
{VERSION_REF}: {data.get(VERSION_REF, SAPM_VERSION)}
|
56
|
-
{CHANNELS_REF}: {data.get(SUBSCRIBER_CHANNELS, "some_channel")}
|
57
|
-
{CONTENT_HASHES}: {data.get(SUBSCRIBER_CONTENT_HASH, "content_hash")}
|
58
|
-
{IS_BATCHABLE}: {data.get(SUBSCRIBER_BATCHABLE, "True")}
|
59
|
-
""",
|
60
|
-
"web_url": "http://localhost",
|
61
|
-
"has_conflicts": False,
|
62
|
-
}
|
63
|
-
else:
|
64
|
-
mr.attributes = {
|
65
|
-
"labels": data.get(LABELS, []),
|
66
|
-
"description": data.get(DESCRIPTION, ""),
|
67
|
-
"web_url": "http://localhost",
|
68
|
-
"has_conflicts": data.get(HAS_CONFLICTS, False),
|
69
|
-
}
|
70
|
-
return mr
|
71
|
-
|
72
|
-
return builder
|
73
|
-
|
74
|
-
|
75
|
-
@pytest.fixture
|
76
|
-
def vcs_builder(
|
77
|
-
mr_builder: Callable[[Mapping], ProjectMergeRequest],
|
78
|
-
) -> Callable[[Mapping], tuple[VCS, list[ProjectMergeRequest]]]:
|
79
|
-
def builder(data: Mapping) -> tuple[VCS, list[ProjectMergeRequest]]:
|
80
|
-
vcs = create_autospec(spec=VCS)
|
81
|
-
open_mrs: list[ProjectMergeRequest] = []
|
82
|
-
for d in data.get(OPEN_MERGE_REQUESTS, []):
|
83
|
-
open_mrs.append(mr_builder(d))
|
84
|
-
vcs.get_open_app_interface_merge_requests.side_effect = [open_mrs]
|
85
|
-
vcs.get_gitlab_mr_check_status.side_effect = data.get(
|
86
|
-
PIPELINE_RESULTS, [MRCheckStatus.SUCCESS] * 100
|
87
|
-
)
|
88
|
-
return (vcs, open_mrs)
|
89
|
-
|
90
|
-
return builder
|
91
|
-
|
92
|
-
|
93
|
-
@pytest.fixture
|
94
|
-
def mr_parser_builder() -> Callable[[Iterable[OpenBatcherMergeRequest]], MRParser]:
|
95
|
-
def builder(data: Iterable[OpenBatcherMergeRequest]) -> MRParser:
|
96
|
-
mr_parser = create_autospec(spec=MRParser)
|
97
|
-
mr_parser.retrieve_open_mrs.side_effect = [data]
|
98
|
-
return mr_parser
|
99
|
-
|
100
|
-
return builder
|
101
|
-
|
102
|
-
|
103
|
-
@pytest.fixture
|
104
|
-
def reconciler_builder() -> Callable[[Diff], Batcher]:
|
105
|
-
def builder(data: Diff) -> Batcher:
|
106
|
-
reconciler = create_autospec(spec=Batcher)
|
107
|
-
reconciler.reconcile.side_effect = [data]
|
108
|
-
return reconciler
|
109
|
-
|
110
|
-
return builder
|
111
|
-
|
112
|
-
|
113
|
-
@pytest.fixture
|
114
|
-
def renderer() -> Renderer:
|
115
|
-
return create_autospec(spec=Renderer)
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
2
|
-
CONTENT_HASHES,
|
3
|
-
)
|
4
|
-
|
5
|
-
OPEN_MERGE_REQUESTS = "open_merge_requests"
|
6
|
-
LABELS = "labels"
|
7
|
-
DESCRIPTION = "description"
|
8
|
-
SUBSCRIBER_CONTENT_HASH = CONTENT_HASHES
|
9
|
-
SUBSCRIBER_TARGET_PATH = "target_path"
|
10
|
-
SUBSCRIBER_DESIRED_CONFIG_HASHES = "config_hashes"
|
11
|
-
SUBSCRIBER_DESIRED_REF = "desired_ref"
|
12
|
-
SUBSCRIBER_CHANNELS = "sub_channels"
|
13
|
-
SUBSCRIBER_BATCHABLE = "sub_batchable"
|
14
|
-
HAS_CONFLICTS = "has_conflicts"
|
15
|
-
SUBSCRIBER_TARGET_NAMESPACE = "subscriber_target_namespace"
|
16
|
-
PIPELINE_RESULTS = "pipeline_results"
|
17
|
-
|
18
|
-
CHANNEL = "channel"
|
19
|
-
REF = "ref"
|
@@ -1,66 +0,0 @@
|
|
1
|
-
from collections.abc import Callable
|
2
|
-
|
3
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.desired_state import (
|
4
|
-
DesiredState,
|
5
|
-
)
|
6
|
-
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
7
|
-
|
8
|
-
|
9
|
-
def test_desired_state_empty() -> None:
|
10
|
-
desired_state = DesiredState(subscribers=[])
|
11
|
-
assert desired_state.promotions == []
|
12
|
-
|
13
|
-
|
14
|
-
def test_desired_state_single_subscriber(
|
15
|
-
subscriber_builder: Callable[..., Subscriber],
|
16
|
-
) -> None:
|
17
|
-
subscriber = subscriber_builder({})
|
18
|
-
desired_state = DesiredState(subscribers=[subscriber])
|
19
|
-
assert len(desired_state.promotions) == 1
|
20
|
-
assert desired_state.promotions[0].content_hashes == {
|
21
|
-
Subscriber.combined_content_hash([subscriber])
|
22
|
-
}
|
23
|
-
|
24
|
-
|
25
|
-
def test_desired_state_multiple_subscribers_same_channel_combo(
|
26
|
-
subscriber_builder: Callable[..., Subscriber],
|
27
|
-
) -> None:
|
28
|
-
subscriber_a = subscriber_builder({
|
29
|
-
"CHANNELS": {"channel-a": {}, "channel-b": {}},
|
30
|
-
"DESIRED_REF": "ref-a",
|
31
|
-
})
|
32
|
-
subscriber_b = subscriber_builder({
|
33
|
-
"CHANNELS": {"channel-a": {}, "channel-b": {}},
|
34
|
-
"DESIRED_REF": "ref-b",
|
35
|
-
})
|
36
|
-
desired_state = DesiredState(subscribers=[subscriber_a, subscriber_b])
|
37
|
-
assert len(desired_state.promotions) == 1
|
38
|
-
assert desired_state.promotions[0].content_hashes == {
|
39
|
-
Subscriber.combined_content_hash([subscriber_a, subscriber_b]),
|
40
|
-
}
|
41
|
-
|
42
|
-
|
43
|
-
def test_desired_state_multiple_subscribers_different_channel_combo(
|
44
|
-
subscriber_builder: Callable[..., Subscriber],
|
45
|
-
) -> None:
|
46
|
-
subscriber_a = subscriber_builder({
|
47
|
-
"CHANNELS": {"channel-a": {}, "channel-b": {}},
|
48
|
-
"DESIRED_REF": "ref-a",
|
49
|
-
})
|
50
|
-
subscriber_b = subscriber_builder({
|
51
|
-
"CHANNELS": {"channel-a": {}, "channel-b": {}},
|
52
|
-
"DESIRED_REF": "ref-b",
|
53
|
-
})
|
54
|
-
subscriber_c = subscriber_builder({
|
55
|
-
"CHANNELS": {"channel-b": {}, "channel-c": {}},
|
56
|
-
"DESIRED_REF": "ref-c",
|
57
|
-
})
|
58
|
-
desired_state = DesiredState(subscribers=[subscriber_a, subscriber_b, subscriber_c])
|
59
|
-
sorted_promotions = sorted(desired_state.promotions)
|
60
|
-
assert len(desired_state.promotions) == 2
|
61
|
-
assert sorted_promotions[0].content_hashes == {
|
62
|
-
Subscriber.combined_content_hash([subscriber_a, subscriber_b]),
|
63
|
-
}
|
64
|
-
assert sorted_promotions[1].content_hashes == {
|
65
|
-
Subscriber.combined_content_hash([subscriber_c]),
|
66
|
-
}
|
@@ -1,86 +0,0 @@
|
|
1
|
-
from collections.abc import Callable
|
2
|
-
from unittest.mock import call, create_autospec
|
3
|
-
|
4
|
-
from gitlab.v4.objects import ProjectMergeRequest
|
5
|
-
|
6
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.batcher import (
|
7
|
-
Addition,
|
8
|
-
Batcher,
|
9
|
-
Deletion,
|
10
|
-
Diff,
|
11
|
-
Reason,
|
12
|
-
)
|
13
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.merge_request_manager_v2 import (
|
14
|
-
MergeRequestManagerV2,
|
15
|
-
)
|
16
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.mr_parser import (
|
17
|
-
MRParser,
|
18
|
-
)
|
19
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.open_merge_requests import (
|
20
|
-
OpenBatcherMergeRequest,
|
21
|
-
)
|
22
|
-
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
23
|
-
Renderer,
|
24
|
-
)
|
25
|
-
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
26
|
-
from reconcile.test.saas_auto_promotions_manager.merge_request_manager.merge_request_manager.data_keys import (
|
27
|
-
CHANNEL,
|
28
|
-
REF,
|
29
|
-
)
|
30
|
-
from reconcile.utils.vcs import VCS
|
31
|
-
|
32
|
-
|
33
|
-
def test_reconcile(
|
34
|
-
reconciler_builder: Callable[[Diff], Batcher],
|
35
|
-
subscriber_builder: Callable[..., Subscriber],
|
36
|
-
) -> None:
|
37
|
-
vcs = create_autospec(spec=VCS)
|
38
|
-
mr_parser = create_autospec(spec=MRParser)
|
39
|
-
renderer = create_autospec(spec=Renderer)
|
40
|
-
subscribers = [
|
41
|
-
subscriber_builder({
|
42
|
-
CHANNEL: ["chan1,chan2"],
|
43
|
-
REF: "hash1",
|
44
|
-
})
|
45
|
-
]
|
46
|
-
deletion = Deletion(
|
47
|
-
mr=OpenBatcherMergeRequest(
|
48
|
-
raw=create_autospec(spec=ProjectMergeRequest),
|
49
|
-
channels=set(),
|
50
|
-
content_hashes=set(),
|
51
|
-
failed_mr_check=False,
|
52
|
-
is_batchable=True,
|
53
|
-
),
|
54
|
-
reason=Reason.NEW_BATCH,
|
55
|
-
)
|
56
|
-
|
57
|
-
additions = [
|
58
|
-
Addition(
|
59
|
-
content_hashes={Subscriber.combined_content_hash(subscribers=subscribers)},
|
60
|
-
channels={"chan1,chan2"},
|
61
|
-
batchable=True,
|
62
|
-
)
|
63
|
-
]
|
64
|
-
|
65
|
-
reconciler = reconciler_builder(
|
66
|
-
Diff(
|
67
|
-
deletions=[deletion],
|
68
|
-
additions=additions,
|
69
|
-
)
|
70
|
-
)
|
71
|
-
manager = MergeRequestManagerV2(
|
72
|
-
vcs=vcs,
|
73
|
-
mr_parser=mr_parser,
|
74
|
-
reconciler=reconciler,
|
75
|
-
renderer=renderer,
|
76
|
-
)
|
77
|
-
|
78
|
-
manager.reconcile(subscribers=subscribers)
|
79
|
-
|
80
|
-
assert len(manager._sapm_mrs) == len(additions)
|
81
|
-
vcs.close_app_interface_mr.assert_has_calls([
|
82
|
-
call(deletion.mr.raw, deletion.reason.value),
|
83
|
-
])
|
84
|
-
vcs.open_app_interface_merge_request.assert_has_calls([
|
85
|
-
call(mr) for mr in manager._sapm_mrs
|
86
|
-
])
|