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
@@ -1,1611 +0,0 @@
|
|
1
|
-
from collections.abc import (
|
2
|
-
Callable,
|
3
|
-
Iterable,
|
4
|
-
MutableMapping,
|
5
|
-
)
|
6
|
-
from datetime import UTC, datetime, timedelta
|
7
|
-
from typing import Any
|
8
|
-
from unittest import TestCase
|
9
|
-
from unittest.mock import (
|
10
|
-
MagicMock,
|
11
|
-
patch,
|
12
|
-
)
|
13
|
-
|
14
|
-
import pytest
|
15
|
-
import yaml
|
16
|
-
from github import (
|
17
|
-
Github,
|
18
|
-
GithubException,
|
19
|
-
)
|
20
|
-
from pydantic import BaseModel
|
21
|
-
|
22
|
-
from reconcile.gql_definitions.common.saas_files import (
|
23
|
-
SaasResourceTemplateTargetImageV1,
|
24
|
-
SaasResourceTemplateTargetPromotionV1,
|
25
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1,
|
26
|
-
)
|
27
|
-
from reconcile.typed_queries.saas_files import (
|
28
|
-
SaasFile,
|
29
|
-
SaasResourceTemplate,
|
30
|
-
)
|
31
|
-
from reconcile.utils.jjb_client import JJB
|
32
|
-
from reconcile.utils.openshift_resource import ResourceInventory
|
33
|
-
from reconcile.utils.promotion_state import PromotionData
|
34
|
-
from reconcile.utils.saasherder import SaasHerder
|
35
|
-
from reconcile.utils.saasherder.interfaces import SaasFile as SaasFileInterface
|
36
|
-
from reconcile.utils.saasherder.models import (
|
37
|
-
Channel,
|
38
|
-
Promotion,
|
39
|
-
TriggerSpecContainerImage,
|
40
|
-
TriggerSpecMovingCommit,
|
41
|
-
TriggerSpecUpstreamJob,
|
42
|
-
)
|
43
|
-
from reconcile.utils.secret_reader import SecretReaderBase
|
44
|
-
|
45
|
-
from .fixtures import Fixtures
|
46
|
-
|
47
|
-
|
48
|
-
class MockJJB:
|
49
|
-
def __init__(self, data: dict[str, list[dict]]) -> None:
|
50
|
-
self.jobs = data
|
51
|
-
|
52
|
-
def get_all_jobs(self, job_types: Iterable[str]) -> dict[str, list[dict]]:
|
53
|
-
return self.jobs
|
54
|
-
|
55
|
-
@staticmethod
|
56
|
-
def get_repo_url(job: dict[str, Any]) -> str:
|
57
|
-
return JJB.get_repo_url(job)
|
58
|
-
|
59
|
-
@staticmethod
|
60
|
-
def get_ref(job: dict[str, Any]) -> str:
|
61
|
-
return JJB.get_ref(job)
|
62
|
-
|
63
|
-
|
64
|
-
class MockSecretReader(SecretReaderBase):
|
65
|
-
"""
|
66
|
-
Read secrets from a config file
|
67
|
-
"""
|
68
|
-
|
69
|
-
def _read(
|
70
|
-
self, path: str, field: str, format: str | None, version: int | None
|
71
|
-
) -> str:
|
72
|
-
return "secret"
|
73
|
-
|
74
|
-
def _read_all(
|
75
|
-
self, path: str, field: str, format: str | None, version: int | None
|
76
|
-
) -> dict[str, str]:
|
77
|
-
return {"param": "secret"}
|
78
|
-
|
79
|
-
|
80
|
-
@pytest.fixture()
|
81
|
-
def inject_gql_class_factory(
|
82
|
-
request: pytest.FixtureRequest,
|
83
|
-
gql_class_factory: Callable[..., SaasFile],
|
84
|
-
) -> None:
|
85
|
-
def _gql_class_factory(
|
86
|
-
self: Any,
|
87
|
-
klass: type[BaseModel],
|
88
|
-
data: MutableMapping[str, Any] | None = None,
|
89
|
-
) -> BaseModel:
|
90
|
-
return gql_class_factory(klass, data)
|
91
|
-
|
92
|
-
request.cls.gql_class_factory = _gql_class_factory
|
93
|
-
|
94
|
-
|
95
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
96
|
-
class TestSaasFileValid(TestCase):
|
97
|
-
def setUp(self) -> None:
|
98
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
99
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas.gql.yml")
|
100
|
-
)
|
101
|
-
jjb_mock_data = {
|
102
|
-
"ci": [
|
103
|
-
{
|
104
|
-
"name": "job",
|
105
|
-
"properties": [
|
106
|
-
{
|
107
|
-
"github": {
|
108
|
-
"url": "https://github.com/app-sre/test-saas-deployments"
|
109
|
-
}
|
110
|
-
}
|
111
|
-
],
|
112
|
-
"scm": [{"git": {"branches": ["main"]}}],
|
113
|
-
},
|
114
|
-
{
|
115
|
-
"name": "job",
|
116
|
-
"properties": [
|
117
|
-
{
|
118
|
-
"github": {
|
119
|
-
"url": "https://github.com/app-sre/test-saas-deployments"
|
120
|
-
}
|
121
|
-
}
|
122
|
-
],
|
123
|
-
"scm": [{"git": {"branches": ["master"]}}],
|
124
|
-
},
|
125
|
-
]
|
126
|
-
}
|
127
|
-
self.jjb = MockJJB(jjb_mock_data)
|
128
|
-
|
129
|
-
def test_check_saas_file_env_combo_unique(self) -> None:
|
130
|
-
saasherder = SaasHerder(
|
131
|
-
[self.saas_file],
|
132
|
-
secret_reader=MockSecretReader(),
|
133
|
-
thread_pool_size=1,
|
134
|
-
integration="",
|
135
|
-
integration_version="",
|
136
|
-
hash_length=7,
|
137
|
-
repo_url="https://repo-url.com",
|
138
|
-
validate=True,
|
139
|
-
)
|
140
|
-
self.assertTrue(saasherder.valid)
|
141
|
-
|
142
|
-
def test_check_saas_file_env_combo_not_unique(self) -> None:
|
143
|
-
self.saas_file.name = "long-name-which-is-too-long-to-produce-unique-combo"
|
144
|
-
saasherder = SaasHerder(
|
145
|
-
[self.saas_file],
|
146
|
-
secret_reader=MockSecretReader(),
|
147
|
-
thread_pool_size=1,
|
148
|
-
integration="",
|
149
|
-
integration_version="",
|
150
|
-
hash_length=7,
|
151
|
-
repo_url="https://repo-url.com",
|
152
|
-
validate=True,
|
153
|
-
)
|
154
|
-
|
155
|
-
self.assertFalse(saasherder.valid)
|
156
|
-
|
157
|
-
def test_saas_file_auto_promotion_used_with_commit_sha(self) -> None:
|
158
|
-
self.saas_file.resource_templates[0].targets[
|
159
|
-
1
|
160
|
-
].ref = "1234567890123456789012345678901234567890"
|
161
|
-
self.saas_file.resource_templates[0].targets[
|
162
|
-
1
|
163
|
-
].promotion = SaasResourceTemplateTargetPromotionV1(
|
164
|
-
auto=True,
|
165
|
-
publish=None,
|
166
|
-
subscribe=None,
|
167
|
-
redeployOnPublisherConfigChange=None,
|
168
|
-
promotion_data=None,
|
169
|
-
soakDays=0,
|
170
|
-
schedule="* * * * *",
|
171
|
-
)
|
172
|
-
saasherder = SaasHerder(
|
173
|
-
[self.saas_file],
|
174
|
-
secret_reader=MockSecretReader(),
|
175
|
-
thread_pool_size=1,
|
176
|
-
integration="",
|
177
|
-
integration_version="",
|
178
|
-
hash_length=7,
|
179
|
-
repo_url="https://repo-url.com",
|
180
|
-
validate=True,
|
181
|
-
)
|
182
|
-
|
183
|
-
self.assertTrue(saasherder.valid)
|
184
|
-
|
185
|
-
def test_saas_file_auto_promotion_not_used_with_commit_sha(self) -> None:
|
186
|
-
self.saas_file.resource_templates[0].targets[1].ref = "main"
|
187
|
-
self.saas_file.resource_templates[0].targets[
|
188
|
-
1
|
189
|
-
].promotion = SaasResourceTemplateTargetPromotionV1(
|
190
|
-
auto=True,
|
191
|
-
publish=None,
|
192
|
-
subscribe=None,
|
193
|
-
promotion_data=None,
|
194
|
-
redeployOnPublisherConfigChange=None,
|
195
|
-
soakDays=0,
|
196
|
-
schedule="* * * * *",
|
197
|
-
)
|
198
|
-
saasherder = SaasHerder(
|
199
|
-
[self.saas_file],
|
200
|
-
secret_reader=MockSecretReader(),
|
201
|
-
thread_pool_size=1,
|
202
|
-
integration="",
|
203
|
-
integration_version="",
|
204
|
-
hash_length=7,
|
205
|
-
repo_url="https://repo-url.com",
|
206
|
-
validate=True,
|
207
|
-
)
|
208
|
-
|
209
|
-
self.assertFalse(saasherder.valid)
|
210
|
-
|
211
|
-
def test_check_saas_file_upstream_not_used_with_commit_sha(self) -> None:
|
212
|
-
saasherder = SaasHerder(
|
213
|
-
[self.saas_file],
|
214
|
-
secret_reader=MockSecretReader(),
|
215
|
-
thread_pool_size=1,
|
216
|
-
integration="",
|
217
|
-
integration_version="",
|
218
|
-
hash_length=7,
|
219
|
-
repo_url="https://repo-url.com",
|
220
|
-
validate=True,
|
221
|
-
)
|
222
|
-
|
223
|
-
self.assertTrue(saasherder.valid)
|
224
|
-
|
225
|
-
def test_check_saas_file_upstream_used_with_commit_sha(self) -> None:
|
226
|
-
self.saas_file.resource_templates[0].targets[
|
227
|
-
0
|
228
|
-
].ref = "2637b6c41bda7731b1bcaaf18b4a50d7c5e63e30"
|
229
|
-
saasherder = SaasHerder(
|
230
|
-
[self.saas_file],
|
231
|
-
secret_reader=MockSecretReader(),
|
232
|
-
thread_pool_size=1,
|
233
|
-
integration="",
|
234
|
-
integration_version="",
|
235
|
-
hash_length=7,
|
236
|
-
repo_url="https://repo-url.com",
|
237
|
-
validate=True,
|
238
|
-
)
|
239
|
-
|
240
|
-
self.assertFalse(saasherder.valid)
|
241
|
-
|
242
|
-
def test_dangling_target_config_hash(self) -> None:
|
243
|
-
self.saas_file.resource_templates[0].targets[1].promotion.promotion_data[
|
244
|
-
0
|
245
|
-
].channel = "does-not-exist"
|
246
|
-
saasherder = SaasHerder(
|
247
|
-
[self.saas_file],
|
248
|
-
secret_reader=MockSecretReader(),
|
249
|
-
thread_pool_size=1,
|
250
|
-
integration="",
|
251
|
-
integration_version="",
|
252
|
-
hash_length=7,
|
253
|
-
repo_url="https://repo-url.com",
|
254
|
-
validate=True,
|
255
|
-
)
|
256
|
-
|
257
|
-
self.assertFalse(saasherder.valid)
|
258
|
-
|
259
|
-
def test_check_saas_file_upstream_used_with_image(self) -> None:
|
260
|
-
self.saas_file.resource_templates[0].targets[
|
261
|
-
0
|
262
|
-
].image = SaasResourceTemplateTargetImageV1(**{
|
263
|
-
"name": "image",
|
264
|
-
"org": {"name": "org", "instance": {"url": "url"}},
|
265
|
-
})
|
266
|
-
saasherder = SaasHerder(
|
267
|
-
[self.saas_file],
|
268
|
-
secret_reader=MockSecretReader(),
|
269
|
-
thread_pool_size=1,
|
270
|
-
integration="",
|
271
|
-
integration_version="",
|
272
|
-
hash_length=7,
|
273
|
-
repo_url="https://repo-url.com",
|
274
|
-
validate=True,
|
275
|
-
)
|
276
|
-
|
277
|
-
self.assertFalse(saasherder.valid)
|
278
|
-
|
279
|
-
def test_check_saas_file_image_used_with_commit_sha(self) -> None:
|
280
|
-
self.saas_file.resource_templates[0].targets[
|
281
|
-
0
|
282
|
-
].ref = "2637b6c41bda7731b1bcaaf18b4a50d7c5e63e30"
|
283
|
-
self.saas_file.resource_templates[0].targets[
|
284
|
-
0
|
285
|
-
].image = SaasResourceTemplateTargetImageV1(**{
|
286
|
-
"name": "image",
|
287
|
-
"org": {"name": "org", "instance": {"url": "url"}},
|
288
|
-
})
|
289
|
-
saasherder = SaasHerder(
|
290
|
-
[self.saas_file],
|
291
|
-
secret_reader=MockSecretReader(),
|
292
|
-
thread_pool_size=1,
|
293
|
-
integration="",
|
294
|
-
integration_version="",
|
295
|
-
hash_length=7,
|
296
|
-
repo_url="https://repo-url.com",
|
297
|
-
validate=True,
|
298
|
-
)
|
299
|
-
|
300
|
-
self.assertFalse(saasherder.valid)
|
301
|
-
|
302
|
-
def test_validate_image_tag_not_equals_ref_valid(self) -> None:
|
303
|
-
self.saas_file.resource_templates[0].targets[0].parameters = {
|
304
|
-
"IMAGE_TAG": "2637b6c"
|
305
|
-
}
|
306
|
-
saasherder = SaasHerder(
|
307
|
-
[self.saas_file],
|
308
|
-
secret_reader=MockSecretReader(),
|
309
|
-
thread_pool_size=1,
|
310
|
-
integration="",
|
311
|
-
integration_version="",
|
312
|
-
hash_length=7,
|
313
|
-
repo_url="https://repo-url.com",
|
314
|
-
validate=True,
|
315
|
-
)
|
316
|
-
|
317
|
-
self.assertTrue(saasherder.valid)
|
318
|
-
|
319
|
-
def test_validate_image_tag_not_equals_ref_invalid(self) -> None:
|
320
|
-
self.saas_file.resource_templates[0].targets[
|
321
|
-
0
|
322
|
-
].ref = "2637b6c41bda7731b1bcaaf18b4a50d7c5e63e30"
|
323
|
-
self.saas_file.resource_templates[0].targets[0].parameters = {
|
324
|
-
"IMAGE_TAG": "2637b6c"
|
325
|
-
}
|
326
|
-
saasherder = SaasHerder(
|
327
|
-
[self.saas_file],
|
328
|
-
secret_reader=MockSecretReader(),
|
329
|
-
thread_pool_size=1,
|
330
|
-
integration="",
|
331
|
-
integration_version="",
|
332
|
-
hash_length=7,
|
333
|
-
repo_url="https://repo-url.com",
|
334
|
-
validate=True,
|
335
|
-
)
|
336
|
-
|
337
|
-
self.assertFalse(saasherder.valid)
|
338
|
-
|
339
|
-
def test_validate_upstream_jobs_valid(self) -> None:
|
340
|
-
saasherder = SaasHerder(
|
341
|
-
[self.saas_file],
|
342
|
-
secret_reader=MockSecretReader(),
|
343
|
-
thread_pool_size=1,
|
344
|
-
integration="",
|
345
|
-
integration_version="",
|
346
|
-
hash_length=7,
|
347
|
-
repo_url="https://repo-url.com",
|
348
|
-
validate=True,
|
349
|
-
)
|
350
|
-
saasherder.validate_upstream_jobs(self.jjb) # type: ignore
|
351
|
-
self.assertTrue(saasherder.valid)
|
352
|
-
|
353
|
-
def test_validate_upstream_jobs_invalid(self) -> None:
|
354
|
-
saasherder = SaasHerder(
|
355
|
-
[self.saas_file],
|
356
|
-
secret_reader=MockSecretReader(),
|
357
|
-
thread_pool_size=1,
|
358
|
-
integration="",
|
359
|
-
integration_version="",
|
360
|
-
hash_length=7,
|
361
|
-
repo_url="https://repo-url.com",
|
362
|
-
validate=True,
|
363
|
-
)
|
364
|
-
jjb = MockJJB({"ci": []})
|
365
|
-
saasherder.validate_upstream_jobs(jjb) # type: ignore
|
366
|
-
self.assertFalse(saasherder.valid)
|
367
|
-
|
368
|
-
def test_check_saas_file_promotion_same_source(self) -> None:
|
369
|
-
raw_rts = [
|
370
|
-
{
|
371
|
-
"name": "rt_publisher",
|
372
|
-
"url": "repo_publisher",
|
373
|
-
"path": "path",
|
374
|
-
"targets": [
|
375
|
-
{
|
376
|
-
"namespace": {
|
377
|
-
"name": "ns",
|
378
|
-
"app": {"name": "app"},
|
379
|
-
"environment": {
|
380
|
-
"name": "env1",
|
381
|
-
},
|
382
|
-
"cluster": {
|
383
|
-
"name": "appsres03ue1",
|
384
|
-
"serverUrl": "https://url",
|
385
|
-
"internal": True,
|
386
|
-
},
|
387
|
-
},
|
388
|
-
"parameters": "{}",
|
389
|
-
"ref": "0000000000000",
|
390
|
-
"promotion": {
|
391
|
-
"publish": ["channel-1"],
|
392
|
-
},
|
393
|
-
}
|
394
|
-
],
|
395
|
-
},
|
396
|
-
{
|
397
|
-
"name": "rt_subscriber",
|
398
|
-
"url": "this-repo-will-not-match-the-publisher",
|
399
|
-
"path": "path",
|
400
|
-
"targets": [
|
401
|
-
{
|
402
|
-
"namespace": {
|
403
|
-
"name": "ns2",
|
404
|
-
"app": {"name": "app"},
|
405
|
-
"environment": {
|
406
|
-
"name": "env1",
|
407
|
-
},
|
408
|
-
"cluster": {
|
409
|
-
"name": "appsres03ue1",
|
410
|
-
"serverUrl": "https://url",
|
411
|
-
"internal": True,
|
412
|
-
},
|
413
|
-
},
|
414
|
-
"parameters": "{}",
|
415
|
-
"ref": "0000000000000",
|
416
|
-
"promotion": {
|
417
|
-
"auto": "True",
|
418
|
-
"subscribe": ["channel-1"],
|
419
|
-
},
|
420
|
-
}
|
421
|
-
],
|
422
|
-
},
|
423
|
-
]
|
424
|
-
rts = [
|
425
|
-
self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
426
|
-
SaasResourceTemplate, rt
|
427
|
-
)
|
428
|
-
for rt in raw_rts
|
429
|
-
]
|
430
|
-
self.saas_file.resource_templates = rts
|
431
|
-
saasherder = SaasHerder(
|
432
|
-
[self.saas_file],
|
433
|
-
secret_reader=MockSecretReader(),
|
434
|
-
thread_pool_size=1,
|
435
|
-
integration="",
|
436
|
-
integration_version="",
|
437
|
-
hash_length=7,
|
438
|
-
repo_url="https://repo-url.com",
|
439
|
-
validate=True,
|
440
|
-
)
|
441
|
-
self.assertFalse(saasherder.valid)
|
442
|
-
|
443
|
-
|
444
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
445
|
-
class TestGetMovingCommitsDiffSaasFile(TestCase):
|
446
|
-
def setUp(self) -> None:
|
447
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
448
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-trigger.gql.yml")
|
449
|
-
)
|
450
|
-
|
451
|
-
self.initiate_gh_patcher = patch.object(
|
452
|
-
SaasHerder, "_initiate_github", autospec=True
|
453
|
-
)
|
454
|
-
self.get_commit_sha_patcher = patch.object(
|
455
|
-
SaasHerder, "_get_commit_sha", autospec=True
|
456
|
-
)
|
457
|
-
self.initiate_gh = self.initiate_gh_patcher.start()
|
458
|
-
self.get_commit_sha = self.get_commit_sha_patcher.start()
|
459
|
-
self.maxDiff = None
|
460
|
-
|
461
|
-
def tearDown(self) -> None:
|
462
|
-
for p in (
|
463
|
-
self.initiate_gh_patcher,
|
464
|
-
self.get_commit_sha_patcher,
|
465
|
-
):
|
466
|
-
p.stop()
|
467
|
-
|
468
|
-
def test_get_moving_commits_diff_saas_file_all_fine(self) -> None:
|
469
|
-
saasherder = SaasHerder(
|
470
|
-
[self.saas_file],
|
471
|
-
secret_reader=MockSecretReader(),
|
472
|
-
thread_pool_size=1,
|
473
|
-
integration="",
|
474
|
-
integration_version="",
|
475
|
-
hash_length=7,
|
476
|
-
repo_url="https://repo-url.com",
|
477
|
-
)
|
478
|
-
saasherder.state = MagicMock()
|
479
|
-
saasherder.state.get.return_value = "asha"
|
480
|
-
self.get_commit_sha.return_value = "abcd4242"
|
481
|
-
# 2nd target is the one that will be promoted
|
482
|
-
expected = [
|
483
|
-
TriggerSpecMovingCommit(
|
484
|
-
saas_file_name=self.saas_file.name,
|
485
|
-
env_name="App-SRE",
|
486
|
-
timeout=None,
|
487
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
488
|
-
resource_template_name="test-saas-deployments",
|
489
|
-
cluster_name="appsres03ue1",
|
490
|
-
namespace_name="test-moving-commit-trigger",
|
491
|
-
state_content="abcd4242",
|
492
|
-
ref="main",
|
493
|
-
reason=None,
|
494
|
-
)
|
495
|
-
]
|
496
|
-
|
497
|
-
self.assertEqual(
|
498
|
-
saasherder.get_moving_commits_diff_saas_file(self.saas_file, True),
|
499
|
-
expected,
|
500
|
-
)
|
501
|
-
|
502
|
-
def test_get_moving_commits_diff_saas_file_all_fine_include_trigger_trace(
|
503
|
-
self,
|
504
|
-
) -> None:
|
505
|
-
saasherder = SaasHerder(
|
506
|
-
[self.saas_file],
|
507
|
-
secret_reader=MockSecretReader(),
|
508
|
-
thread_pool_size=1,
|
509
|
-
integration="",
|
510
|
-
integration_version="",
|
511
|
-
hash_length=7,
|
512
|
-
repo_url="https://repo-url.com",
|
513
|
-
include_trigger_trace=True,
|
514
|
-
)
|
515
|
-
|
516
|
-
saasherder.state = MagicMock()
|
517
|
-
saasherder.state.get.return_value = "asha"
|
518
|
-
self.get_commit_sha.return_value = "abcd4242"
|
519
|
-
expected = [
|
520
|
-
TriggerSpecMovingCommit(
|
521
|
-
saas_file_name=self.saas_file.name,
|
522
|
-
env_name="App-SRE",
|
523
|
-
timeout=None,
|
524
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
525
|
-
resource_template_name="test-saas-deployments",
|
526
|
-
cluster_name="appsres03ue1",
|
527
|
-
namespace_name="test-moving-commit-trigger",
|
528
|
-
state_content="abcd4242",
|
529
|
-
ref="main",
|
530
|
-
reason="https://github.com/app-sre/test-saas-deployments/commit/abcd4242",
|
531
|
-
),
|
532
|
-
]
|
533
|
-
|
534
|
-
self.assertEqual(
|
535
|
-
saasherder.get_moving_commits_diff_saas_file(self.saas_file, True),
|
536
|
-
expected,
|
537
|
-
)
|
538
|
-
|
539
|
-
def test_get_moving_commits_diff_saas_file_bad_sha1(self) -> None:
|
540
|
-
saasherder = SaasHerder(
|
541
|
-
[self.saas_file],
|
542
|
-
secret_reader=MockSecretReader(),
|
543
|
-
thread_pool_size=1,
|
544
|
-
integration="",
|
545
|
-
integration_version="",
|
546
|
-
hash_length=7,
|
547
|
-
repo_url="https://repo-url.com",
|
548
|
-
)
|
549
|
-
saasherder.state = MagicMock()
|
550
|
-
saasherder.state.get.return_value = "asha"
|
551
|
-
self.get_commit_sha.side_effect = GithubException(
|
552
|
-
401, "somedata", {"aheader": "avalue"}
|
553
|
-
)
|
554
|
-
# At least we don't crash!
|
555
|
-
self.assertEqual(
|
556
|
-
saasherder.get_moving_commits_diff_saas_file(self.saas_file, True), []
|
557
|
-
)
|
558
|
-
|
559
|
-
|
560
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
561
|
-
class TestGetUpstreamJobsDiffSaasFile(TestCase):
|
562
|
-
def setUp(self) -> None:
|
563
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
564
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-trigger.gql.yml")
|
565
|
-
)
|
566
|
-
self.maxDiff = None
|
567
|
-
|
568
|
-
def test_get_upstream_jobs_diff_saas_file_all_fine(self) -> None:
|
569
|
-
state_content = {"number": 2, "result": "SUCCESS", "commit_sha": "abcd4242"}
|
570
|
-
current_state = {"ci": {"job": [state_content]}}
|
571
|
-
saasherder = SaasHerder(
|
572
|
-
[self.saas_file],
|
573
|
-
secret_reader=MockSecretReader(),
|
574
|
-
thread_pool_size=1,
|
575
|
-
integration="",
|
576
|
-
integration_version="",
|
577
|
-
hash_length=7,
|
578
|
-
repo_url="https://repo-url.com",
|
579
|
-
)
|
580
|
-
saasherder.state = MagicMock()
|
581
|
-
saasherder.state.get.return_value = {
|
582
|
-
"number": 1,
|
583
|
-
"result": "SUCCESS",
|
584
|
-
"commit_sha": "4242efg",
|
585
|
-
}
|
586
|
-
expected = [
|
587
|
-
TriggerSpecUpstreamJob(
|
588
|
-
saas_file_name=self.saas_file.name,
|
589
|
-
env_name="App-SRE-stage",
|
590
|
-
timeout=None,
|
591
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
592
|
-
resource_template_name="test-saas-deployments",
|
593
|
-
cluster_name="appsres03ue1",
|
594
|
-
namespace_name="test-upstream-job-trigger",
|
595
|
-
instance_name="ci",
|
596
|
-
job_name="job",
|
597
|
-
state_content=state_content,
|
598
|
-
reason=None,
|
599
|
-
)
|
600
|
-
]
|
601
|
-
|
602
|
-
self.assertEqual(
|
603
|
-
saasherder.get_upstream_jobs_diff_saas_file(
|
604
|
-
self.saas_file, True, current_state
|
605
|
-
),
|
606
|
-
expected,
|
607
|
-
)
|
608
|
-
|
609
|
-
def test_get_upstream_jobs_diff_saas_file_all_fine_include_trigger_trace(
|
610
|
-
self,
|
611
|
-
) -> None:
|
612
|
-
state_content = {"number": 2, "result": "SUCCESS", "commit_sha": "abcd4242"}
|
613
|
-
current_state = {"ci": {"job": [state_content]}}
|
614
|
-
saasherder = SaasHerder(
|
615
|
-
[self.saas_file],
|
616
|
-
secret_reader=MockSecretReader(),
|
617
|
-
thread_pool_size=1,
|
618
|
-
integration="",
|
619
|
-
integration_version="",
|
620
|
-
hash_length=7,
|
621
|
-
repo_url="https://repo-url.com",
|
622
|
-
include_trigger_trace=True,
|
623
|
-
)
|
624
|
-
saasherder.state = MagicMock()
|
625
|
-
saasherder.state.get.return_value = {
|
626
|
-
"number": 1,
|
627
|
-
"result": "SUCCESS",
|
628
|
-
"commit_sha": "4242efg",
|
629
|
-
}
|
630
|
-
expected = [
|
631
|
-
TriggerSpecUpstreamJob(
|
632
|
-
saas_file_name=self.saas_file.name,
|
633
|
-
env_name="App-SRE-stage",
|
634
|
-
timeout=None,
|
635
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
636
|
-
resource_template_name="test-saas-deployments",
|
637
|
-
cluster_name="appsres03ue1",
|
638
|
-
namespace_name="test-upstream-job-trigger",
|
639
|
-
instance_name="ci",
|
640
|
-
job_name="job",
|
641
|
-
state_content=state_content,
|
642
|
-
reason="https://github.com/app-sre/test-saas-deployments/commit/abcd4242 via https://jenkins.com/job/job/2",
|
643
|
-
)
|
644
|
-
]
|
645
|
-
|
646
|
-
self.assertEqual(
|
647
|
-
saasherder.get_upstream_jobs_diff_saas_file(
|
648
|
-
self.saas_file, True, current_state
|
649
|
-
),
|
650
|
-
expected,
|
651
|
-
)
|
652
|
-
|
653
|
-
|
654
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
655
|
-
class TestGetContainerImagesDiffSaasFile(TestCase):
|
656
|
-
def setUp(self) -> None:
|
657
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
658
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-trigger.gql.yml")
|
659
|
-
)
|
660
|
-
|
661
|
-
self.initiate_gh_patcher = patch.object(
|
662
|
-
SaasHerder, "_initiate_github", autospec=True
|
663
|
-
)
|
664
|
-
self.get_commit_sha_patcher = patch.object(
|
665
|
-
SaasHerder, "_get_commit_sha", autospec=True
|
666
|
-
)
|
667
|
-
self.get_image_patcher = patch.object(SaasHerder, "_get_image", autospec=True)
|
668
|
-
self.initiate_gh = self.initiate_gh_patcher.start()
|
669
|
-
self.get_commit_sha = self.get_commit_sha_patcher.start()
|
670
|
-
self.get_image = self.get_image_patcher.start()
|
671
|
-
self.maxDiff = None
|
672
|
-
|
673
|
-
def tearDown(self) -> None:
|
674
|
-
for p in (
|
675
|
-
self.initiate_gh_patcher,
|
676
|
-
self.get_commit_sha_patcher,
|
677
|
-
self.get_image_patcher,
|
678
|
-
):
|
679
|
-
p.stop()
|
680
|
-
|
681
|
-
def test_get_container_images_diff_saas_file_all_fine(self) -> None:
|
682
|
-
saasherder = SaasHerder(
|
683
|
-
[self.saas_file],
|
684
|
-
secret_reader=MockSecretReader(),
|
685
|
-
thread_pool_size=1,
|
686
|
-
integration="",
|
687
|
-
integration_version="",
|
688
|
-
hash_length=7,
|
689
|
-
repo_url="https://repo-url.com",
|
690
|
-
)
|
691
|
-
saasherder.state = MagicMock()
|
692
|
-
saasherder.state.get.return_value = "asha"
|
693
|
-
self.get_commit_sha.return_value = "abcd4242"
|
694
|
-
self.get_image.return_value = MagicMock()
|
695
|
-
expected = [
|
696
|
-
TriggerSpecContainerImage(
|
697
|
-
saas_file_name=self.saas_file.name,
|
698
|
-
env_name="App-SRE-stage",
|
699
|
-
timeout=None,
|
700
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
701
|
-
resource_template_name="test-saas-deployments",
|
702
|
-
cluster_name="appsres03ue1",
|
703
|
-
namespace_name="test-image-trigger",
|
704
|
-
image="quay.io/centos/centos",
|
705
|
-
state_content="abcd424",
|
706
|
-
reason=None,
|
707
|
-
)
|
708
|
-
]
|
709
|
-
|
710
|
-
self.assertEqual(
|
711
|
-
saasherder.get_container_images_diff_saas_file(self.saas_file, True),
|
712
|
-
expected,
|
713
|
-
)
|
714
|
-
|
715
|
-
def test_get_container_images_diff_saas_file_all_fine_include_trigger_trace(
|
716
|
-
self,
|
717
|
-
) -> None:
|
718
|
-
saasherder = SaasHerder(
|
719
|
-
[self.saas_file],
|
720
|
-
secret_reader=MockSecretReader(),
|
721
|
-
thread_pool_size=1,
|
722
|
-
integration="",
|
723
|
-
integration_version="",
|
724
|
-
hash_length=7,
|
725
|
-
repo_url="https://repo-url.com",
|
726
|
-
include_trigger_trace=True,
|
727
|
-
)
|
728
|
-
saasherder.state = MagicMock()
|
729
|
-
saasherder.state.get.return_value = "asha"
|
730
|
-
self.get_commit_sha.return_value = "abcd4242"
|
731
|
-
self.get_image.return_value = MagicMock()
|
732
|
-
expected = [
|
733
|
-
TriggerSpecContainerImage(
|
734
|
-
saas_file_name=self.saas_file.name,
|
735
|
-
env_name="App-SRE-stage",
|
736
|
-
timeout=None,
|
737
|
-
pipelines_provider=self.saas_file.pipelines_provider,
|
738
|
-
resource_template_name="test-saas-deployments",
|
739
|
-
cluster_name="appsres03ue1",
|
740
|
-
namespace_name="test-image-trigger",
|
741
|
-
image="quay.io/centos/centos",
|
742
|
-
state_content="abcd424",
|
743
|
-
reason="https://github.com/app-sre/test-saas-deployments/commit/abcd4242 build quay.io/centos/centos:abcd424",
|
744
|
-
)
|
745
|
-
]
|
746
|
-
|
747
|
-
self.assertEqual(
|
748
|
-
saasherder.get_container_images_diff_saas_file(self.saas_file, True),
|
749
|
-
expected,
|
750
|
-
)
|
751
|
-
|
752
|
-
|
753
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
754
|
-
class TestGetArchiveInfo(TestCase):
|
755
|
-
def setUp(self) -> None:
|
756
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
757
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-trigger.gql.yml")
|
758
|
-
)
|
759
|
-
self.initiate_gh_patcher = patch.object(
|
760
|
-
SaasHerder, "_initiate_github", autospec=True
|
761
|
-
)
|
762
|
-
self.initiate_gh = self.initiate_gh_patcher.start()
|
763
|
-
self.maxDiff = None
|
764
|
-
|
765
|
-
def tearDown(self) -> None:
|
766
|
-
for p in (self.initiate_gh_patcher,):
|
767
|
-
p.stop()
|
768
|
-
|
769
|
-
def test_get_gitlab_archive_info(self) -> None:
|
770
|
-
trigger_reason = "https://gitlab.com/app-sre/test-saas-deployments/commit/abcd4242 via https://jenkins.com/job/job/2"
|
771
|
-
saasherder = SaasHerder(
|
772
|
-
[self.saas_file],
|
773
|
-
secret_reader=MockSecretReader(),
|
774
|
-
thread_pool_size=1,
|
775
|
-
integration="",
|
776
|
-
integration_version="",
|
777
|
-
hash_length=7,
|
778
|
-
repo_url="https://repo-url.com",
|
779
|
-
include_trigger_trace=True,
|
780
|
-
)
|
781
|
-
file_name = "app-sre-test-saas-deployments-abcd4242.tar.gz"
|
782
|
-
archive_url = f"https://gitlab.com/app-sre/test-saas-deployments/-/archive/abcd4242/{file_name}"
|
783
|
-
self.assertEqual(
|
784
|
-
saasherder.get_archive_info(self.saas_file, trigger_reason),
|
785
|
-
(file_name, archive_url),
|
786
|
-
)
|
787
|
-
|
788
|
-
def test_get_github_archive_info(self) -> None:
|
789
|
-
trigger_reason = "https://github.com/app-sre/test-saas-deployments/commit/abcd4242 build quay.io/centos/centos:abcd424"
|
790
|
-
saasherder = SaasHerder(
|
791
|
-
[self.saas_file],
|
792
|
-
secret_reader=MockSecretReader(),
|
793
|
-
thread_pool_size=1,
|
794
|
-
integration="",
|
795
|
-
integration_version="",
|
796
|
-
hash_length=7,
|
797
|
-
repo_url="https://repo-url.com",
|
798
|
-
include_trigger_trace=True,
|
799
|
-
)
|
800
|
-
file_name = "app-sre-test-saas-deployments-abcd4242.tar.gz"
|
801
|
-
archive_url = "https://api.github.com/repos/app-sre/test-saas-deployments/tarball/abcd4242"
|
802
|
-
self.initiate_gh.return_value.get_repo.return_value.get_archive_link.return_value = archive_url
|
803
|
-
self.assertEqual(
|
804
|
-
saasherder.get_archive_info(self.saas_file, trigger_reason),
|
805
|
-
(file_name, archive_url),
|
806
|
-
)
|
807
|
-
|
808
|
-
|
809
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
810
|
-
class TestPopulateDesiredState(TestCase):
|
811
|
-
def setUp(self) -> None:
|
812
|
-
self.fxts = Fixtures("saasherder_populate_desired")
|
813
|
-
raw_saas_file = self.fxts.get_anymarkup("saas_remote_openshift_template.yaml")
|
814
|
-
del raw_saas_file["_placeholders"]
|
815
|
-
saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
816
|
-
SaasFile, raw_saas_file
|
817
|
-
)
|
818
|
-
self.saasherder = SaasHerder(
|
819
|
-
[saas_file],
|
820
|
-
secret_reader=MockSecretReader(),
|
821
|
-
thread_pool_size=1,
|
822
|
-
integration="",
|
823
|
-
integration_version="",
|
824
|
-
hash_length=7,
|
825
|
-
repo_url="https://repo-url.com",
|
826
|
-
)
|
827
|
-
|
828
|
-
# Mock GitHub interactions.
|
829
|
-
self.initiate_gh_patcher = patch.object(
|
830
|
-
SaasHerder,
|
831
|
-
"_initiate_github",
|
832
|
-
autospec=True,
|
833
|
-
return_value=None,
|
834
|
-
)
|
835
|
-
self.get_file_contents_patcher = patch.object(
|
836
|
-
SaasHerder,
|
837
|
-
"_get_file_contents",
|
838
|
-
wraps=self.fake_get_file_contents,
|
839
|
-
)
|
840
|
-
self.initiate_gh_patcher.start()
|
841
|
-
self.get_file_contents_patcher.start()
|
842
|
-
|
843
|
-
# Mock image checking.
|
844
|
-
self.get_check_images_patcher = patch.object(
|
845
|
-
SaasHerder,
|
846
|
-
"_check_images",
|
847
|
-
autospec=True,
|
848
|
-
return_value=None,
|
849
|
-
)
|
850
|
-
self.get_check_images_patcher.start()
|
851
|
-
|
852
|
-
def fake_get_file_contents(
|
853
|
-
self, url: str, path: str, ref: str, github: Github
|
854
|
-
) -> tuple[Any, str]:
|
855
|
-
self.assertEqual("https://github.com/rhobs/configuration", url)
|
856
|
-
|
857
|
-
content = self.fxts.get(ref + (path.replace("/", "_")))
|
858
|
-
return yaml.safe_load(content), ref
|
859
|
-
|
860
|
-
def tearDown(self) -> None:
|
861
|
-
for p in (
|
862
|
-
self.initiate_gh_patcher,
|
863
|
-
self.get_file_contents_patcher,
|
864
|
-
self.get_check_images_patcher,
|
865
|
-
):
|
866
|
-
p.stop()
|
867
|
-
|
868
|
-
def test_populate_desired_state_cases(self) -> None:
|
869
|
-
ri = ResourceInventory()
|
870
|
-
for resource_type in (
|
871
|
-
"Deployment",
|
872
|
-
"Service",
|
873
|
-
"ConfigMap",
|
874
|
-
):
|
875
|
-
ri.initialize_resource_type("stage-1", "yolo-stage", resource_type)
|
876
|
-
ri.initialize_resource_type("prod-1", "yolo", resource_type)
|
877
|
-
self.saasherder.populate_desired_state(ri)
|
878
|
-
|
879
|
-
cnt = 0
|
880
|
-
for cluster, namespace, resource_type, data in ri:
|
881
|
-
for _, d_item in data["desired"].items():
|
882
|
-
expected = yaml.safe_load(
|
883
|
-
self.fxts.get(
|
884
|
-
f"expected_{cluster}_{namespace}_{resource_type}.json",
|
885
|
-
)
|
886
|
-
)
|
887
|
-
self.assertEqual(expected, d_item.body)
|
888
|
-
cnt += 1
|
889
|
-
|
890
|
-
self.assertEqual(5, cnt, "expected 5 resources, found less")
|
891
|
-
self.assertEqual(self.saasherder.promotions, [None, None, None, None])
|
892
|
-
|
893
|
-
|
894
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
895
|
-
class TestCollectRepoUrls(TestCase):
|
896
|
-
def setUp(self) -> None:
|
897
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
898
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas.gql.yml")
|
899
|
-
)
|
900
|
-
|
901
|
-
def test_collect_repo_urls(self) -> None:
|
902
|
-
repo_url = "https://github.com/app-sre/test-saas-deployments"
|
903
|
-
saasherder = SaasHerder(
|
904
|
-
[self.saas_file],
|
905
|
-
secret_reader=MockSecretReader(),
|
906
|
-
thread_pool_size=1,
|
907
|
-
integration="",
|
908
|
-
integration_version="",
|
909
|
-
hash_length=7,
|
910
|
-
repo_url="https://repo-url.com",
|
911
|
-
)
|
912
|
-
self.assertEqual({repo_url}, saasherder.repo_urls)
|
913
|
-
|
914
|
-
|
915
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
916
|
-
class TestCollectImagePatterns(TestCase):
|
917
|
-
def setUp(self) -> None:
|
918
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
919
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas.gql.yml")
|
920
|
-
)
|
921
|
-
|
922
|
-
def test_collect_image_patterns(self) -> None:
|
923
|
-
image_pattern = "quay.io/centos/centos:centos8"
|
924
|
-
saasherder = SaasHerder(
|
925
|
-
[self.saas_file],
|
926
|
-
secret_reader=MockSecretReader(),
|
927
|
-
thread_pool_size=1,
|
928
|
-
integration="",
|
929
|
-
integration_version="",
|
930
|
-
hash_length=7,
|
931
|
-
repo_url="https://repo-url.com",
|
932
|
-
)
|
933
|
-
self.assertEqual({image_pattern}, saasherder.image_patterns)
|
934
|
-
|
935
|
-
|
936
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
937
|
-
class TestGetSaasFileAttribute(TestCase):
|
938
|
-
def setUp(self) -> None:
|
939
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
940
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas.gql.yml")
|
941
|
-
)
|
942
|
-
|
943
|
-
def test_no_such_attribute(self) -> None:
|
944
|
-
saasherder = SaasHerder(
|
945
|
-
[self.saas_file],
|
946
|
-
secret_reader=MockSecretReader(),
|
947
|
-
thread_pool_size=1,
|
948
|
-
integration="",
|
949
|
-
integration_version="",
|
950
|
-
hash_length=7,
|
951
|
-
repo_url="https://repo-url.com",
|
952
|
-
)
|
953
|
-
att = saasherder._get_saas_file_feature_enabled("no_such_attribute")
|
954
|
-
self.assertEqual(att, None)
|
955
|
-
|
956
|
-
def test_attribute_none(self) -> None:
|
957
|
-
saasherder = SaasHerder(
|
958
|
-
[self.saas_file],
|
959
|
-
secret_reader=MockSecretReader(),
|
960
|
-
thread_pool_size=1,
|
961
|
-
integration="",
|
962
|
-
integration_version="",
|
963
|
-
hash_length=7,
|
964
|
-
repo_url="https://repo-url.com",
|
965
|
-
)
|
966
|
-
att = saasherder._get_saas_file_feature_enabled("takeover")
|
967
|
-
self.assertEqual(att, None)
|
968
|
-
|
969
|
-
def test_attribute_not_none(self) -> None:
|
970
|
-
saasherder = SaasHerder(
|
971
|
-
[self.saas_file],
|
972
|
-
secret_reader=MockSecretReader(),
|
973
|
-
thread_pool_size=1,
|
974
|
-
integration="",
|
975
|
-
integration_version="",
|
976
|
-
hash_length=7,
|
977
|
-
repo_url="https://repo-url.com",
|
978
|
-
)
|
979
|
-
att = saasherder._get_saas_file_feature_enabled("publish_job_logs")
|
980
|
-
self.assertEqual(att, True)
|
981
|
-
|
982
|
-
def test_attribute_none_with_default(self) -> None:
|
983
|
-
saasherder = SaasHerder(
|
984
|
-
[self.saas_file],
|
985
|
-
secret_reader=MockSecretReader(),
|
986
|
-
thread_pool_size=1,
|
987
|
-
integration="",
|
988
|
-
integration_version="",
|
989
|
-
hash_length=7,
|
990
|
-
repo_url="https://repo-url.com",
|
991
|
-
)
|
992
|
-
att = saasherder._get_saas_file_feature_enabled("no_such_att", default=True)
|
993
|
-
self.assertEqual(att, True)
|
994
|
-
|
995
|
-
def test_attribute_not_none_with_default(self) -> None:
|
996
|
-
saasherder = SaasHerder(
|
997
|
-
[self.saas_file],
|
998
|
-
secret_reader=MockSecretReader(),
|
999
|
-
thread_pool_size=1,
|
1000
|
-
integration="",
|
1001
|
-
integration_version="",
|
1002
|
-
hash_length=7,
|
1003
|
-
repo_url="https://repo-url.com",
|
1004
|
-
)
|
1005
|
-
att = saasherder._get_saas_file_feature_enabled(
|
1006
|
-
"publish_job_logs", default=False
|
1007
|
-
)
|
1008
|
-
self.assertEqual(att, True)
|
1009
|
-
|
1010
|
-
def test_attribute_multiple_saas_files_return_false(self) -> None:
|
1011
|
-
saasherder = SaasHerder(
|
1012
|
-
[self.saas_file, self.saas_file],
|
1013
|
-
secret_reader=MockSecretReader(),
|
1014
|
-
thread_pool_size=1,
|
1015
|
-
integration="",
|
1016
|
-
integration_version="",
|
1017
|
-
hash_length=7,
|
1018
|
-
repo_url="https://repo-url.com",
|
1019
|
-
)
|
1020
|
-
self.assertFalse(saasherder._get_saas_file_feature_enabled("publish_job_logs"))
|
1021
|
-
|
1022
|
-
def test_attribute_multiple_saas_files_with_default_return_false(self) -> None:
|
1023
|
-
saasherder = SaasHerder(
|
1024
|
-
[self.saas_file, self.saas_file],
|
1025
|
-
secret_reader=MockSecretReader(),
|
1026
|
-
thread_pool_size=1,
|
1027
|
-
integration="",
|
1028
|
-
integration_version="",
|
1029
|
-
hash_length=7,
|
1030
|
-
repo_url="https://repo-url.com",
|
1031
|
-
)
|
1032
|
-
att = saasherder._get_saas_file_feature_enabled("attrib", default=True)
|
1033
|
-
self.assertFalse(att)
|
1034
|
-
|
1035
|
-
|
1036
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
1037
|
-
class TestConfigHashPromotionsValidation(TestCase):
|
1038
|
-
"""TestCase to test SaasHerder promotions validation. SaasHerder is
|
1039
|
-
initialized with ResourceInventory population. Like is done in
|
1040
|
-
openshift-saas-deploy"""
|
1041
|
-
|
1042
|
-
cluster: str
|
1043
|
-
namespace: str
|
1044
|
-
fxt: Any
|
1045
|
-
template: Any
|
1046
|
-
|
1047
|
-
@classmethod
|
1048
|
-
def setUpClass(cls) -> None:
|
1049
|
-
cls.fxt = Fixtures("saasherder")
|
1050
|
-
cls.cluster = "test-cluster"
|
1051
|
-
cls.template = cls.fxt.get_anymarkup("template_1.yml")
|
1052
|
-
|
1053
|
-
def setUp(self) -> None:
|
1054
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
1055
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-multi-channel.gql.yml")
|
1056
|
-
)
|
1057
|
-
self.state_patcher = patch("reconcile.utils.state.State", autospec=True)
|
1058
|
-
self.state_mock = self.state_patcher.start().return_value
|
1059
|
-
|
1060
|
-
self.ig_patcher = patch.object(SaasHerder, "_initiate_github", autospec=True)
|
1061
|
-
self.ig_patcher.start()
|
1062
|
-
|
1063
|
-
self.image_auth_patcher = patch.object(SaasHerder, "_initiate_image_auth")
|
1064
|
-
self.image_auth_patcher.start()
|
1065
|
-
|
1066
|
-
self.gfc_patcher = patch.object(SaasHerder, "_get_file_contents", autospec=True)
|
1067
|
-
gfc_mock = self.gfc_patcher.start()
|
1068
|
-
gfc_mock.return_value = (self.template, "ahash")
|
1069
|
-
|
1070
|
-
self.deploy_current_state_fxt = self.fxt.get_anymarkup("saas_deploy.state.json")
|
1071
|
-
|
1072
|
-
self.post_deploy_current_state_fxt = self.fxt.get_anymarkup(
|
1073
|
-
"saas_post_deploy.state.json"
|
1074
|
-
)
|
1075
|
-
|
1076
|
-
self.saasherder = SaasHerder(
|
1077
|
-
[self.saas_file],
|
1078
|
-
secret_reader=MockSecretReader(),
|
1079
|
-
thread_pool_size=1,
|
1080
|
-
state=self.state_mock,
|
1081
|
-
integration="",
|
1082
|
-
integration_version="",
|
1083
|
-
hash_length=24,
|
1084
|
-
repo_url="https://repo-url.com",
|
1085
|
-
all_saas_files=[self.saas_file],
|
1086
|
-
)
|
1087
|
-
|
1088
|
-
# IMPORTANT: Populating desired state modify self.saas_files within
|
1089
|
-
# saasherder object.
|
1090
|
-
self.ri = ResourceInventory()
|
1091
|
-
for ns in ["test-ns-publisher", "test-ns-subscriber"]:
|
1092
|
-
for kind in ["Service", "Deployment"]:
|
1093
|
-
self.ri.initialize_resource_type(self.cluster, ns, kind)
|
1094
|
-
|
1095
|
-
self.saasherder.populate_desired_state(self.ri)
|
1096
|
-
if self.ri.has_error_registered():
|
1097
|
-
raise Exception("Errors registered in Resourceinventory")
|
1098
|
-
|
1099
|
-
def tearDown(self) -> None:
|
1100
|
-
self.state_patcher.stop()
|
1101
|
-
self.ig_patcher.stop()
|
1102
|
-
self.gfc_patcher.stop()
|
1103
|
-
|
1104
|
-
def test_promotion_state_config_hash_match_validates(self) -> None:
|
1105
|
-
"""A promotion is valid if the parent target config_hash set in
|
1106
|
-
the state is equal to the one set in the subscriber target
|
1107
|
-
promotion data. This is the happy path.
|
1108
|
-
"""
|
1109
|
-
publisher_state = {
|
1110
|
-
"success": True,
|
1111
|
-
"saas_file": self.saas_file.name,
|
1112
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1113
|
-
"has_succeeded_once": True,
|
1114
|
-
}
|
1115
|
-
self.state_mock.get.return_value = publisher_state
|
1116
|
-
result = self.saasherder.validate_promotions()
|
1117
|
-
self.assertTrue(result)
|
1118
|
-
|
1119
|
-
def test_promotion_state_config_hash_not_match_no_validates(self) -> None:
|
1120
|
-
"""Promotion is not valid if the parent target config hash set in
|
1121
|
-
the state does not match with the one set in the subscriber target
|
1122
|
-
promotion_data. This could happen if the parent target has run again
|
1123
|
-
with the same ref before the subscriber target promotion MR is merged.
|
1124
|
-
"""
|
1125
|
-
publisher_states = [
|
1126
|
-
{
|
1127
|
-
"success": True,
|
1128
|
-
"saas_file": self.saas_file.name,
|
1129
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1130
|
-
"has_succeeded_once": True,
|
1131
|
-
},
|
1132
|
-
{
|
1133
|
-
"success": True,
|
1134
|
-
"saas_file": self.saas_file.name,
|
1135
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1136
|
-
"has_succeeded_once": True,
|
1137
|
-
},
|
1138
|
-
{
|
1139
|
-
"success": True,
|
1140
|
-
"saas_file": self.saas_file.name,
|
1141
|
-
"target_config_hash": "will_not_match",
|
1142
|
-
"has_succeeded_once": True,
|
1143
|
-
},
|
1144
|
-
]
|
1145
|
-
self.state_mock.get.side_effect = publisher_states
|
1146
|
-
result = self.saasherder.validate_promotions()
|
1147
|
-
self.assertFalse(result)
|
1148
|
-
|
1149
|
-
def test_promotion_without_state_config_hash_validates(self) -> None:
|
1150
|
-
"""Existent states won't have promotion data. If there is an ongoing
|
1151
|
-
promotion, this ensures it will happen.
|
1152
|
-
"""
|
1153
|
-
publisher_state = {
|
1154
|
-
"success": True,
|
1155
|
-
}
|
1156
|
-
self.state_mock.get.return_value = publisher_state
|
1157
|
-
result = self.saasherder.validate_promotions()
|
1158
|
-
self.assertTrue(result)
|
1159
|
-
|
1160
|
-
def test_promotion_without_promotion_data_validates(self) -> None:
|
1161
|
-
"""A manual promotion might be required, subsribed targets without
|
1162
|
-
promotion_data should validate if the parent target job has succed
|
1163
|
-
with the same ref.
|
1164
|
-
"""
|
1165
|
-
publisher_state = {
|
1166
|
-
"success": True,
|
1167
|
-
"saas_file": self.saas_file.name,
|
1168
|
-
"target_config_hash": "whatever",
|
1169
|
-
"has_succeeded_once": True,
|
1170
|
-
}
|
1171
|
-
|
1172
|
-
self.assertEqual(len(self.saasherder.promotions), 4)
|
1173
|
-
self.assertIsNotNone(self.saasherder.promotions[3])
|
1174
|
-
# Remove promotion_data on the promoted target
|
1175
|
-
self.saasherder.promotions[3].promotion_data = None # type: ignore
|
1176
|
-
|
1177
|
-
self.state_mock.get.return_value = publisher_state
|
1178
|
-
result = self.saasherder.validate_promotions()
|
1179
|
-
self.assertTrue(result)
|
1180
|
-
|
1181
|
-
def test_promotion_state_re_deployment_failed(self) -> None:
|
1182
|
-
"""A promotion is valid if it has ever succeeded for that ref.
|
1183
|
-
Re-deployment results should be neglected for validation.
|
1184
|
-
"""
|
1185
|
-
publisher_state = {
|
1186
|
-
# Latest state is failed ...
|
1187
|
-
"success": False,
|
1188
|
-
"saas_file": self.saas_file.name,
|
1189
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1190
|
-
# ... however, the deployment succeeded sometime before once.
|
1191
|
-
"has_succeeded_once": True,
|
1192
|
-
}
|
1193
|
-
self.state_mock.get.return_value = publisher_state
|
1194
|
-
result = self.saasherder.validate_promotions()
|
1195
|
-
self.assertTrue(result)
|
1196
|
-
|
1197
|
-
def test_promotion_state_never_successfully_deployed(self) -> None:
|
1198
|
-
"""A promotion is invalid, if it never succeeded before."""
|
1199
|
-
publisher_state = {
|
1200
|
-
# Latest state is failed ...
|
1201
|
-
"success": False,
|
1202
|
-
"saas_file": self.saas_file.name,
|
1203
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1204
|
-
# ... and it never succeeded once before.
|
1205
|
-
"has_succeeded_once": False,
|
1206
|
-
}
|
1207
|
-
self.state_mock.get.return_value = publisher_state
|
1208
|
-
result = self.saasherder.validate_promotions()
|
1209
|
-
self.assertFalse(result)
|
1210
|
-
|
1211
|
-
def test_promotion_state_success_backwards_compatibility_success(self) -> None:
|
1212
|
-
"""Not all states have the has_succeeded_once attribute yet.
|
1213
|
-
If it doesnt exist, we should always fall back to latest success state.
|
1214
|
-
"""
|
1215
|
-
publisher_state = {
|
1216
|
-
"success": True,
|
1217
|
-
"saas_file": self.saas_file.name,
|
1218
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1219
|
-
}
|
1220
|
-
self.state_mock.get.return_value = publisher_state
|
1221
|
-
result = self.saasherder.validate_promotions()
|
1222
|
-
self.assertTrue(result)
|
1223
|
-
|
1224
|
-
def test_promotion_state_success_backwards_compatibility_fail(self) -> None:
|
1225
|
-
"""Not all states have the has_succeeded_once attribute yet.
|
1226
|
-
If it doesnt exist, we should always fall back to latest success state.
|
1227
|
-
"""
|
1228
|
-
publisher_state = {
|
1229
|
-
"success": False,
|
1230
|
-
"saas_file": self.saas_file.name,
|
1231
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1232
|
-
}
|
1233
|
-
self.state_mock.get.return_value = publisher_state
|
1234
|
-
result = self.saasherder.validate_promotions()
|
1235
|
-
self.assertFalse(result)
|
1236
|
-
|
1237
|
-
|
1238
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
1239
|
-
class TestSoakDays(TestCase):
|
1240
|
-
"""TestCase to test SaasHerder soakDays gate. SaasHerder is
|
1241
|
-
initialized with ResourceInventory population. Like is done in
|
1242
|
-
openshift-saas-deploy"""
|
1243
|
-
|
1244
|
-
cluster: str
|
1245
|
-
namespace: str
|
1246
|
-
fxt: Any
|
1247
|
-
template: Any
|
1248
|
-
|
1249
|
-
@classmethod
|
1250
|
-
def setUpClass(cls) -> None:
|
1251
|
-
cls.fxt = Fixtures("saasherder")
|
1252
|
-
cls.cluster = "test-cluster"
|
1253
|
-
cls.template = cls.fxt.get_anymarkup("template_1.yml")
|
1254
|
-
|
1255
|
-
def setUp(self) -> None:
|
1256
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
1257
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas-soak-days.gql.yml")
|
1258
|
-
)
|
1259
|
-
self.state_patcher = patch("reconcile.utils.state.State", autospec=True)
|
1260
|
-
self.state_mock = self.state_patcher.start().return_value
|
1261
|
-
|
1262
|
-
self.ig_patcher = patch.object(SaasHerder, "_initiate_github", autospec=True)
|
1263
|
-
self.ig_patcher.start()
|
1264
|
-
|
1265
|
-
self.image_auth_patcher = patch.object(SaasHerder, "_initiate_image_auth")
|
1266
|
-
self.image_auth_patcher.start()
|
1267
|
-
|
1268
|
-
self.gfc_patcher = patch.object(SaasHerder, "_get_file_contents", autospec=True)
|
1269
|
-
gfc_mock = self.gfc_patcher.start()
|
1270
|
-
gfc_mock.return_value = (self.template, "ahash")
|
1271
|
-
|
1272
|
-
self.deploy_current_state_fxt = self.fxt.get_anymarkup("saas_deploy.state.json")
|
1273
|
-
|
1274
|
-
self.post_deploy_current_state_fxt = self.fxt.get_anymarkup(
|
1275
|
-
"saas_post_deploy.state.json"
|
1276
|
-
)
|
1277
|
-
|
1278
|
-
self.saasherder = SaasHerder(
|
1279
|
-
[self.saas_file],
|
1280
|
-
secret_reader=MockSecretReader(),
|
1281
|
-
thread_pool_size=1,
|
1282
|
-
state=self.state_mock,
|
1283
|
-
integration="",
|
1284
|
-
integration_version="",
|
1285
|
-
hash_length=24,
|
1286
|
-
repo_url="https://repo-url.com",
|
1287
|
-
all_saas_files=[self.saas_file],
|
1288
|
-
)
|
1289
|
-
|
1290
|
-
# IMPORTANT: Populating desired state modify self.saas_files within
|
1291
|
-
# saasherder object.
|
1292
|
-
self.ri = ResourceInventory()
|
1293
|
-
for ns in ["test-ns-publisher", "test-ns-subscriber"]:
|
1294
|
-
for kind in ["Service", "Deployment"]:
|
1295
|
-
self.ri.initialize_resource_type(self.cluster, ns, kind)
|
1296
|
-
|
1297
|
-
self.saasherder.populate_desired_state(self.ri)
|
1298
|
-
if self.ri.has_error_registered():
|
1299
|
-
raise Exception("Errors registered in Resourceinventory")
|
1300
|
-
|
1301
|
-
def tearDown(self) -> None:
|
1302
|
-
self.state_patcher.stop()
|
1303
|
-
self.ig_patcher.stop()
|
1304
|
-
self.gfc_patcher.stop()
|
1305
|
-
|
1306
|
-
def test_soak_days_passed(self) -> None:
|
1307
|
-
"""A promotion is valid if the parent targets accumulated soak_days
|
1308
|
-
passed. We have a soakDays setting of 2 days.
|
1309
|
-
"""
|
1310
|
-
publisher_states = [
|
1311
|
-
{
|
1312
|
-
"success": True,
|
1313
|
-
"saas_file": self.saas_file.name,
|
1314
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1315
|
-
# the deployment happened 1 hour ago
|
1316
|
-
"check_in": str(datetime.now(UTC) - timedelta(hours=1)),
|
1317
|
-
},
|
1318
|
-
{
|
1319
|
-
"success": True,
|
1320
|
-
"saas_file": self.saas_file.name,
|
1321
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1322
|
-
# the deployment happened 47 hours ago
|
1323
|
-
"check_in": str(datetime.now(UTC) - timedelta(hours=47)),
|
1324
|
-
},
|
1325
|
-
]
|
1326
|
-
self.state_mock.get.side_effect = publisher_states
|
1327
|
-
result = self.saasherder.validate_promotions()
|
1328
|
-
self.assertTrue(result)
|
1329
|
-
|
1330
|
-
def test_soak_days_not_passed(self) -> None:
|
1331
|
-
"""A promotion is valid if the parent target accumulated soak_days
|
1332
|
-
passed. We have a soakDays setting of 2 days.
|
1333
|
-
"""
|
1334
|
-
publisher_states = [
|
1335
|
-
{
|
1336
|
-
"success": True,
|
1337
|
-
"saas_file": self.saas_file.name,
|
1338
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1339
|
-
# the deployment happened 12 hours ago
|
1340
|
-
"check_in": str(datetime.now(UTC) - timedelta(hours=12)),
|
1341
|
-
},
|
1342
|
-
{
|
1343
|
-
"success": True,
|
1344
|
-
"saas_file": self.saas_file.name,
|
1345
|
-
"target_config_hash": "ed2af38cf21f268c",
|
1346
|
-
# the deployment happened 1 hour ago
|
1347
|
-
"check_in": str(datetime.now(UTC) - timedelta(hours=1)),
|
1348
|
-
},
|
1349
|
-
]
|
1350
|
-
self.state_mock.get.side_effect = publisher_states
|
1351
|
-
result = self.saasherder.validate_promotions()
|
1352
|
-
self.assertFalse(result)
|
1353
|
-
|
1354
|
-
|
1355
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
1356
|
-
class TestConfigHashTrigger(TestCase):
|
1357
|
-
"""TestCase to test Openshift SAAS deploy configs trigger. SaasHerder is
|
1358
|
-
initialized WITHOUT ResourceInventory population. Like is done in the
|
1359
|
-
config changes trigger"""
|
1360
|
-
|
1361
|
-
cluster: str
|
1362
|
-
namespace: str
|
1363
|
-
fxt: Any
|
1364
|
-
template: Any
|
1365
|
-
|
1366
|
-
@classmethod
|
1367
|
-
def setUpClass(cls) -> None:
|
1368
|
-
cls.fxt = Fixtures("saasherder")
|
1369
|
-
cls.cluster = "test-cluster"
|
1370
|
-
|
1371
|
-
def setUp(self) -> None:
|
1372
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
1373
|
-
SaasFile, Fixtures("saasherder").get_anymarkup("saas.gql.yml")
|
1374
|
-
)
|
1375
|
-
self.state_patcher = patch("reconcile.utils.state.State", autospec=True)
|
1376
|
-
self.state_mock = self.state_patcher.start().return_value
|
1377
|
-
|
1378
|
-
self.deploy_current_state_fxt = self.fxt.get_anymarkup("saas_deploy.state.json")
|
1379
|
-
|
1380
|
-
self.post_deploy_current_state_fxt = self.fxt.get_anymarkup(
|
1381
|
-
"saas_post_deploy.state.json"
|
1382
|
-
)
|
1383
|
-
|
1384
|
-
self.state_mock.get.side_effect = [
|
1385
|
-
self.deploy_current_state_fxt,
|
1386
|
-
self.post_deploy_current_state_fxt,
|
1387
|
-
]
|
1388
|
-
|
1389
|
-
self.saasherder = SaasHerder(
|
1390
|
-
[self.saas_file],
|
1391
|
-
secret_reader=MockSecretReader(),
|
1392
|
-
thread_pool_size=1,
|
1393
|
-
state=self.state_mock,
|
1394
|
-
integration="",
|
1395
|
-
integration_version="",
|
1396
|
-
hash_length=24,
|
1397
|
-
repo_url="https://repo-url.com",
|
1398
|
-
all_saas_files=[self.saas_file],
|
1399
|
-
)
|
1400
|
-
|
1401
|
-
def tearDown(self) -> None:
|
1402
|
-
self.state_patcher.stop()
|
1403
|
-
|
1404
|
-
def test_same_configs_do_not_trigger(self) -> None:
|
1405
|
-
"""Ensures that if the same config is found, no job is triggered
|
1406
|
-
current Config is fetched from the state
|
1407
|
-
"""
|
1408
|
-
trigger_specs = self.saasherder.get_configs_diff_saas_file(self.saas_file)
|
1409
|
-
self.assertListEqual(trigger_specs, [])
|
1410
|
-
|
1411
|
-
def test_config_hash_change_do_trigger(self) -> None:
|
1412
|
-
"""Ensures a new job is triggered if the parent config hash changes"""
|
1413
|
-
self.saasherder.saas_files[0].resource_templates[0].targets[ # type: ignore
|
1414
|
-
1
|
1415
|
-
].promotion.promotion_data[0].data[0].target_config_hash = "Changed"
|
1416
|
-
trigger_specs = self.saasherder.get_configs_diff_saas_file(self.saas_file)
|
1417
|
-
self.assertEqual(len(trigger_specs), 1)
|
1418
|
-
|
1419
|
-
def test_non_existent_config_triggers(self) -> None:
|
1420
|
-
self.state_mock.get.side_effect = [self.deploy_current_state_fxt, None]
|
1421
|
-
trigger_specs = self.saasherder.get_configs_diff_saas_file(self.saas_file)
|
1422
|
-
self.assertEqual(len(trigger_specs), 1)
|
1423
|
-
|
1424
|
-
|
1425
|
-
class TestRemoveNoneAttributes(TestCase):
|
1426
|
-
def testSimpleDict(self) -> None:
|
1427
|
-
input = {"a": 1, "b": {}, "d": None, "e": {"aa": "aa", "bb": None}}
|
1428
|
-
expected = {"a": 1, "b": {}, "e": {"aa": "aa"}}
|
1429
|
-
res = SaasHerder.remove_none_values(input)
|
1430
|
-
self.assertEqual(res, expected)
|
1431
|
-
|
1432
|
-
def testNoneValue(self) -> None:
|
1433
|
-
input = None
|
1434
|
-
expected: dict[Any, Any] = {}
|
1435
|
-
res = SaasHerder.remove_none_values(input)
|
1436
|
-
self.assertEqual(res, expected)
|
1437
|
-
|
1438
|
-
|
1439
|
-
@pytest.mark.usefixtures("inject_gql_class_factory")
|
1440
|
-
class TestPromotionBlockedHoxfixVersions(TestCase):
|
1441
|
-
def setUp(self) -> None:
|
1442
|
-
self.saas_file = self.gql_class_factory( # type: ignore[attr-defined] # it's set in the fixture
|
1443
|
-
SaasFile,
|
1444
|
-
Fixtures("saasherder").get_anymarkup("saas.gql.yml"),
|
1445
|
-
)
|
1446
|
-
self.state_patcher = patch("reconcile.utils.state.State", autospec=True)
|
1447
|
-
self.state_mock = self.state_patcher.start().return_value
|
1448
|
-
self.saasherder = SaasHerder(
|
1449
|
-
[self.saas_file],
|
1450
|
-
secret_reader=MockSecretReader(),
|
1451
|
-
thread_pool_size=1,
|
1452
|
-
state=self.state_mock,
|
1453
|
-
integration="",
|
1454
|
-
integration_version="",
|
1455
|
-
hash_length=7,
|
1456
|
-
repo_url="https://repo-url.com",
|
1457
|
-
)
|
1458
|
-
self.promotion_state_patcher = patch(
|
1459
|
-
"reconcile.utils.promotion_state.PromotionState", autospec=True
|
1460
|
-
)
|
1461
|
-
self.promotion_state_mock = self.promotion_state_patcher.start().return_value
|
1462
|
-
self.saasherder._promotion_state = self.promotion_state_mock
|
1463
|
-
|
1464
|
-
def tearDown(self) -> None:
|
1465
|
-
self.state_patcher.stop()
|
1466
|
-
self.promotion_state_patcher.stop()
|
1467
|
-
|
1468
|
-
def test_blocked_hotfix_version_promotion_validity(self) -> None:
|
1469
|
-
code_component_url = "https://github.com/app-sre/test-saas-deployments"
|
1470
|
-
hotfix_version = "1234567890123456789012345678901234567890"
|
1471
|
-
# code_component = self.saas_file.app.code_components[0]
|
1472
|
-
channel = Channel(
|
1473
|
-
name="",
|
1474
|
-
publisher_uids=[""],
|
1475
|
-
)
|
1476
|
-
promotion = Promotion(
|
1477
|
-
url=code_component_url,
|
1478
|
-
commit_sha=hotfix_version,
|
1479
|
-
saas_file=self.saas_file.name,
|
1480
|
-
target_config_hash="",
|
1481
|
-
saas_target_uid="",
|
1482
|
-
soak_days=0,
|
1483
|
-
subscribe=[channel],
|
1484
|
-
)
|
1485
|
-
self.saasherder.promotions = [promotion]
|
1486
|
-
self.promotion_state_mock.get_promotion_data.return_value = PromotionData(
|
1487
|
-
success=False
|
1488
|
-
)
|
1489
|
-
self.assertFalse(self.saasherder.validate_promotions())
|
1490
|
-
|
1491
|
-
self.saasherder.hotfix_versions[code_component_url] = {hotfix_version}
|
1492
|
-
self.assertTrue(self.saasherder.validate_promotions())
|
1493
|
-
|
1494
|
-
self.saasherder.blocked_versions[code_component_url] = {hotfix_version}
|
1495
|
-
self.assertFalse(self.saasherder.validate_promotions())
|
1496
|
-
|
1497
|
-
|
1498
|
-
def test_render_templated_parameters(
|
1499
|
-
gql_class_factory: Callable[..., SaasFileInterface],
|
1500
|
-
) -> None:
|
1501
|
-
saas_file = gql_class_factory(
|
1502
|
-
SaasFile,
|
1503
|
-
Fixtures("saasherder").get_anymarkup("saas-templated-params.gql.yml"),
|
1504
|
-
)
|
1505
|
-
SaasHerder.resolve_templated_parameters([saas_file])
|
1506
|
-
assert saas_file.resource_templates[0].targets[0].parameters == {
|
1507
|
-
"no-template": "v1",
|
1508
|
-
"ignore-go-template": "{{ .GO_PARAM }}-go",
|
1509
|
-
"template-param-1": "test-namespace-ns",
|
1510
|
-
"template-param-2": "appsres03ue1-cluster",
|
1511
|
-
}
|
1512
|
-
assert saas_file.resource_templates[0].targets[0].secret_parameters == [
|
1513
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1514
|
-
name="no-template",
|
1515
|
-
secret={
|
1516
|
-
"path": "path/to/secret",
|
1517
|
-
"field": "secret_key",
|
1518
|
-
"version": 1,
|
1519
|
-
"format": None,
|
1520
|
-
},
|
1521
|
-
),
|
1522
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1523
|
-
name="ignore-go-template",
|
1524
|
-
secret={
|
1525
|
-
"path": "path/{{ .GO_PARAM }}/secret",
|
1526
|
-
"field": "{{ .GO_PARAM }}-secret_key",
|
1527
|
-
"version": 1,
|
1528
|
-
"format": None,
|
1529
|
-
},
|
1530
|
-
),
|
1531
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1532
|
-
name="template-param-1",
|
1533
|
-
secret={
|
1534
|
-
"path": "path/appsres03ue1/test-namespace/secret",
|
1535
|
-
"field": "secret_key",
|
1536
|
-
"version": 1,
|
1537
|
-
"format": None,
|
1538
|
-
},
|
1539
|
-
),
|
1540
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1541
|
-
name="template-param-2",
|
1542
|
-
secret={
|
1543
|
-
"path": "path/appsres03ue1/test-namespace/secret",
|
1544
|
-
"field": "App-SRE-stage-secret_key",
|
1545
|
-
"version": 1,
|
1546
|
-
"format": None,
|
1547
|
-
},
|
1548
|
-
),
|
1549
|
-
]
|
1550
|
-
|
1551
|
-
|
1552
|
-
def test_render_templated_parameters_in_init(
|
1553
|
-
gql_class_factory: Callable[..., SaasFile],
|
1554
|
-
) -> None:
|
1555
|
-
saas_file = gql_class_factory(
|
1556
|
-
SaasFile,
|
1557
|
-
Fixtures("saasherder").get_anymarkup("saas-templated-params.gql.yml"),
|
1558
|
-
)
|
1559
|
-
SaasHerder(
|
1560
|
-
[saas_file],
|
1561
|
-
secret_reader=MockSecretReader(),
|
1562
|
-
thread_pool_size=1,
|
1563
|
-
integration="",
|
1564
|
-
integration_version="",
|
1565
|
-
hash_length=24,
|
1566
|
-
repo_url="https://repo-url.com",
|
1567
|
-
)
|
1568
|
-
assert saas_file.resource_templates[0].targets[0].parameters == {
|
1569
|
-
"no-template": "v1",
|
1570
|
-
"ignore-go-template": "{{ .GO_PARAM }}-go",
|
1571
|
-
"template-param-1": "test-namespace-ns",
|
1572
|
-
"template-param-2": "appsres03ue1-cluster",
|
1573
|
-
}
|
1574
|
-
assert saas_file.resource_templates[0].targets[0].secret_parameters == [
|
1575
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1576
|
-
name="no-template",
|
1577
|
-
secret={
|
1578
|
-
"path": "path/to/secret",
|
1579
|
-
"field": "secret_key",
|
1580
|
-
"version": 1,
|
1581
|
-
"format": None,
|
1582
|
-
},
|
1583
|
-
),
|
1584
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1585
|
-
name="ignore-go-template",
|
1586
|
-
secret={
|
1587
|
-
"path": "path/{{ .GO_PARAM }}/secret",
|
1588
|
-
"field": "{{ .GO_PARAM }}-secret_key",
|
1589
|
-
"version": 1,
|
1590
|
-
"format": None,
|
1591
|
-
},
|
1592
|
-
),
|
1593
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1594
|
-
name="template-param-1",
|
1595
|
-
secret={
|
1596
|
-
"path": "path/appsres03ue1/test-namespace/secret",
|
1597
|
-
"field": "secret_key",
|
1598
|
-
"version": 1,
|
1599
|
-
"format": None,
|
1600
|
-
},
|
1601
|
-
),
|
1602
|
-
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1603
|
-
name="template-param-2",
|
1604
|
-
secret={
|
1605
|
-
"path": "path/appsres03ue1/test-namespace/secret",
|
1606
|
-
"field": "App-SRE-stage-secret_key",
|
1607
|
-
"version": 1,
|
1608
|
-
"format": None,
|
1609
|
-
},
|
1610
|
-
),
|
1611
|
-
]
|