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,785 +0,0 @@
|
|
1
|
-
import copy
|
2
|
-
from collections.abc import (
|
3
|
-
Iterable,
|
4
|
-
Sequence,
|
5
|
-
)
|
6
|
-
from datetime import (
|
7
|
-
datetime,
|
8
|
-
timedelta,
|
9
|
-
)
|
10
|
-
from typing import Any
|
11
|
-
from unittest.mock import (
|
12
|
-
Mock,
|
13
|
-
call,
|
14
|
-
create_autospec,
|
15
|
-
)
|
16
|
-
|
17
|
-
import pytest
|
18
|
-
from pytest_mock import MockerFixture
|
19
|
-
|
20
|
-
import reconcile.slack_base as slackbase
|
21
|
-
import reconcile.slack_usergroups as integ
|
22
|
-
from reconcile.gql_definitions.slack_usergroups.clusters import ClusterAuthV1, ClusterV1
|
23
|
-
from reconcile.gql_definitions.slack_usergroups.permissions import (
|
24
|
-
PagerDutyInstanceV1,
|
25
|
-
PagerDutyTargetV1,
|
26
|
-
PermissionSlackUsergroupV1,
|
27
|
-
ScheduleEntryV1,
|
28
|
-
)
|
29
|
-
from reconcile.gql_definitions.slack_usergroups.users import (
|
30
|
-
AccessV1,
|
31
|
-
NamespaceV1,
|
32
|
-
NamespaceV1_ClusterV1,
|
33
|
-
RoleV1,
|
34
|
-
UserV1,
|
35
|
-
)
|
36
|
-
from reconcile.gql_definitions.slack_usergroups.users import ClusterV1 as AccessCluster
|
37
|
-
from reconcile.slack_usergroups import (
|
38
|
-
SlackMap,
|
39
|
-
SlackState,
|
40
|
-
State,
|
41
|
-
WorkspaceSpec,
|
42
|
-
act,
|
43
|
-
get_clusters,
|
44
|
-
get_permissions,
|
45
|
-
get_users,
|
46
|
-
)
|
47
|
-
from reconcile.utils import repo_owners
|
48
|
-
from reconcile.utils.github_api import GithubRepositoryApi
|
49
|
-
from reconcile.utils.pagerduty_api import PagerDutyMap
|
50
|
-
from reconcile.utils.slack_api import SlackApi
|
51
|
-
|
52
|
-
from .fixtures import Fixtures
|
53
|
-
|
54
|
-
|
55
|
-
@pytest.fixture
|
56
|
-
def base_state():
|
57
|
-
state = SlackState({
|
58
|
-
"slack-workspace": {
|
59
|
-
"usergroup-1": State(
|
60
|
-
workspace="slack-workspace",
|
61
|
-
usergroup="usergroup-1",
|
62
|
-
usergroup_id="USERGA",
|
63
|
-
user_names={"username"},
|
64
|
-
channel_names={"channelname"},
|
65
|
-
description="Some description",
|
66
|
-
)
|
67
|
-
}
|
68
|
-
})
|
69
|
-
|
70
|
-
return state
|
71
|
-
|
72
|
-
|
73
|
-
@pytest.fixture
|
74
|
-
def user() -> UserV1:
|
75
|
-
return UserV1(
|
76
|
-
org_username="org",
|
77
|
-
slack_username="slack",
|
78
|
-
github_username="github",
|
79
|
-
name="name",
|
80
|
-
pagerduty_username="pagerduty",
|
81
|
-
tag_on_cluster_updates=None,
|
82
|
-
tag_on_merge_requests=None,
|
83
|
-
roles=None,
|
84
|
-
)
|
85
|
-
|
86
|
-
|
87
|
-
@pytest.fixture
|
88
|
-
def fxt() -> Fixtures:
|
89
|
-
return Fixtures("slack_usergroups")
|
90
|
-
|
91
|
-
|
92
|
-
@pytest.fixture
|
93
|
-
def permissions(fxt: Fixtures) -> list[PermissionSlackUsergroupV1]:
|
94
|
-
def q(*args: Any, **kwargs: Any) -> dict[Any, Any]:
|
95
|
-
return fxt.get_anymarkup("permissions.yml")
|
96
|
-
|
97
|
-
return get_permissions(q)
|
98
|
-
|
99
|
-
|
100
|
-
@pytest.fixture
|
101
|
-
def slack_client_mock() -> SlackApi:
|
102
|
-
api = create_autospec(SlackApi)
|
103
|
-
api.channel = "channel"
|
104
|
-
return api
|
105
|
-
|
106
|
-
|
107
|
-
@pytest.fixture
|
108
|
-
def slack_map(slack_client_mock: Mock) -> SlackMap:
|
109
|
-
return {
|
110
|
-
"slack-workspace": WorkspaceSpec(
|
111
|
-
slack=slack_client_mock, managed_usergroups=[]
|
112
|
-
),
|
113
|
-
"coreos": WorkspaceSpec(slack=slack_client_mock, managed_usergroups=[]),
|
114
|
-
}
|
115
|
-
|
116
|
-
|
117
|
-
def test_get_permissions(permissions: Sequence[PermissionSlackUsergroupV1]) -> None:
|
118
|
-
assert len(permissions) == 2
|
119
|
-
p = permissions[0]
|
120
|
-
assert p.channels and len(p.channels) == 2
|
121
|
-
assert p.roles and p.roles[0].users == []
|
122
|
-
|
123
|
-
p = permissions[1]
|
124
|
-
assert p.roles and p.roles[0].users and p.roles[0].users[0].name == "Rafael"
|
125
|
-
|
126
|
-
|
127
|
-
def test_get_users(fxt: Fixtures) -> None:
|
128
|
-
def q(*args: Any, **kwargs: Any) -> dict[Any, Any]:
|
129
|
-
return fxt.get_anymarkup("users.yml")
|
130
|
-
|
131
|
-
users = get_users(q)
|
132
|
-
assert len(users) == 2
|
133
|
-
assert users[0].org_username == "user1-org-username"
|
134
|
-
assert users[0].roles
|
135
|
-
assert len(users[0].roles) == 1
|
136
|
-
assert (
|
137
|
-
users[1].roles
|
138
|
-
and users[1].roles[0].access
|
139
|
-
and users[1].roles[0].access[0].namespace
|
140
|
-
and users[1].roles[0].access[0].namespace.cluster.name == "cluster-1"
|
141
|
-
)
|
142
|
-
|
143
|
-
|
144
|
-
def test_get_clusters(fxt: Fixtures) -> None:
|
145
|
-
def q(*args: Any, **kwargs: Any) -> dict[Any, Any]:
|
146
|
-
return fxt.get_anymarkup("clusters.yml")
|
147
|
-
|
148
|
-
clusters = get_clusters(q)
|
149
|
-
assert len(clusters) == 2
|
150
|
-
assert clusters[0].name == "cluster-1"
|
151
|
-
assert clusters[0].disable
|
152
|
-
|
153
|
-
|
154
|
-
def test_get_slack_usernames_from_schedule_none() -> None:
|
155
|
-
result = integ.get_slack_usernames_from_schedule([])
|
156
|
-
assert not result
|
157
|
-
|
158
|
-
|
159
|
-
def test_get_slack_usernames_from_schedule(user: UserV1) -> None:
|
160
|
-
now = datetime.utcnow()
|
161
|
-
schedule = ScheduleEntryV1(
|
162
|
-
start=(now - timedelta(hours=1)).strftime(integ.DATE_FORMAT),
|
163
|
-
end=(now + timedelta(hours=1)).strftime(integ.DATE_FORMAT),
|
164
|
-
users=[user],
|
165
|
-
)
|
166
|
-
result = integ.get_slack_usernames_from_schedule([schedule])
|
167
|
-
assert result == [user.slack_username]
|
168
|
-
|
169
|
-
|
170
|
-
def test_get_slack_username_org_username(user: UserV1) -> None:
|
171
|
-
user.slack_username = None
|
172
|
-
result = integ.get_slack_username(user)
|
173
|
-
assert result == user.org_username
|
174
|
-
|
175
|
-
|
176
|
-
def test_get_slack_username_slack_username(user: UserV1) -> None:
|
177
|
-
result = integ.get_slack_username(user)
|
178
|
-
assert result == user.slack_username
|
179
|
-
|
180
|
-
|
181
|
-
def test_get_pagerduty_username_org_username(user: UserV1) -> None:
|
182
|
-
user.pagerduty_username = None
|
183
|
-
result = integ.get_pagerduty_name(user)
|
184
|
-
assert result == user.org_username
|
185
|
-
|
186
|
-
|
187
|
-
def test_get_pagerduty_username_slack_username(user: UserV1) -> None:
|
188
|
-
result = integ.get_pagerduty_name(user)
|
189
|
-
assert result == user.pagerduty_username
|
190
|
-
|
191
|
-
|
192
|
-
def test_get_usernames_from_pagerduty(user: UserV1) -> None:
|
193
|
-
pagerduties = [
|
194
|
-
PagerDutyTargetV1(
|
195
|
-
name="app-sre-pagerduty-primary-oncall",
|
196
|
-
instance=PagerDutyInstanceV1(name="redhat"),
|
197
|
-
scheduleID="PHS3079",
|
198
|
-
escalationPolicyID=None,
|
199
|
-
)
|
200
|
-
]
|
201
|
-
mock_pagerduty_map = create_autospec(PagerDutyMap)
|
202
|
-
mock_pagerduty_map.get.return_value.get_pagerduty_users.return_value = [
|
203
|
-
"pagerduty+foobar",
|
204
|
-
"nobody",
|
205
|
-
"nobody+foobar",
|
206
|
-
]
|
207
|
-
result = integ.get_usernames_from_pagerduty(
|
208
|
-
pagerduties=pagerduties,
|
209
|
-
users=[user],
|
210
|
-
usergroup="usergroup",
|
211
|
-
pagerduty_map=mock_pagerduty_map,
|
212
|
-
)
|
213
|
-
assert result == [user.slack_username]
|
214
|
-
|
215
|
-
|
216
|
-
def test_get_slack_usernames_from_owners(mocker: MockerFixture, user: UserV1) -> None:
|
217
|
-
mocker.patch(
|
218
|
-
"reconcile.slack_usergroups.get_git_api"
|
219
|
-
).return_value.__enter__.return_value = create_autospec(GithubRepositoryApi)
|
220
|
-
mock_repo_owner = create_autospec(repo_owners.RepoOwners)
|
221
|
-
mock_repo_owner.return_value.get_root_owners.return_value = {
|
222
|
-
"approvers": ["approver1"],
|
223
|
-
"reviewers": ["github"], # <- the test user
|
224
|
-
}
|
225
|
-
result = integ.get_slack_usernames_from_owners(
|
226
|
-
owners_from_repo=["https://github.com/owner/repo"],
|
227
|
-
users=[user],
|
228
|
-
usergroup="usergroup",
|
229
|
-
repo_owner_class=mock_repo_owner,
|
230
|
-
)
|
231
|
-
assert result == [user.slack_username]
|
232
|
-
|
233
|
-
|
234
|
-
def test_include_user_to_cluster_usergroup_user_has_cluster_access(
|
235
|
-
mocker: MockerFixture, user: UserV1
|
236
|
-
) -> None:
|
237
|
-
mocker.patch(
|
238
|
-
"reconcile.openshift_base.user_has_cluster_access",
|
239
|
-
autospec=True,
|
240
|
-
return_value=False,
|
241
|
-
)
|
242
|
-
cluster = ClusterV1(name="cluster", auth=[], disable={"integrations": []})
|
243
|
-
# user_has_cluster_access -> False
|
244
|
-
assert not integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
245
|
-
|
246
|
-
|
247
|
-
def test_user_has_cluster_access(mocker: MockerFixture, user: UserV1) -> None:
|
248
|
-
cluster = ClusterV1(
|
249
|
-
name="cluster",
|
250
|
-
auth=[ClusterAuthV1(service="oidc")],
|
251
|
-
disable={"integrations": []},
|
252
|
-
)
|
253
|
-
assert integ.user_has_cluster_access(user, cluster, [user.org_username])
|
254
|
-
assert not integ.user_has_cluster_access(user, cluster, ["just-another-user"])
|
255
|
-
|
256
|
-
|
257
|
-
def test_include_user_to_cluster_usergroup(mocker: MockerFixture, user: UserV1) -> None:
|
258
|
-
mocker.patch.object(
|
259
|
-
integ,
|
260
|
-
"user_has_cluster_access",
|
261
|
-
autospec=True,
|
262
|
-
return_value=True,
|
263
|
-
)
|
264
|
-
cluster = ClusterV1(name="cluster", auth=[], disable={"integrations": []})
|
265
|
-
|
266
|
-
# user.tag_on_cluster_updates
|
267
|
-
user.tag_on_cluster_updates = False
|
268
|
-
assert not integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
269
|
-
user.tag_on_cluster_updates = True
|
270
|
-
assert integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
271
|
-
|
272
|
-
# no roles
|
273
|
-
user.tag_on_cluster_updates = None
|
274
|
-
user.roles = []
|
275
|
-
assert not integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
276
|
-
|
277
|
-
# role: cluster and tag_on_cluster_updates = True & False
|
278
|
-
user.roles = [
|
279
|
-
RoleV1(
|
280
|
-
tag_on_cluster_updates=True,
|
281
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
282
|
-
),
|
283
|
-
RoleV1(
|
284
|
-
tag_on_cluster_updates=False,
|
285
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
286
|
-
),
|
287
|
-
]
|
288
|
-
assert integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
289
|
-
# role: cluster and tag_on_cluster_updates = None & False
|
290
|
-
user.roles = [
|
291
|
-
RoleV1(
|
292
|
-
tag_on_cluster_updates=None,
|
293
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
294
|
-
),
|
295
|
-
RoleV1(
|
296
|
-
tag_on_cluster_updates=False,
|
297
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
298
|
-
),
|
299
|
-
]
|
300
|
-
assert integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
301
|
-
|
302
|
-
# role: cluster and tag_on_cluster_updates = False & False
|
303
|
-
user.roles = [
|
304
|
-
RoleV1(
|
305
|
-
tag_on_cluster_updates=False,
|
306
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
307
|
-
),
|
308
|
-
RoleV1(
|
309
|
-
tag_on_cluster_updates=False,
|
310
|
-
access=[AccessV1(cluster=AccessCluster(name=cluster.name), namespace=None)],
|
311
|
-
),
|
312
|
-
]
|
313
|
-
assert not integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
314
|
-
|
315
|
-
# role: namespace and tag_on_cluster_updates = True & False
|
316
|
-
user.roles = [
|
317
|
-
RoleV1(
|
318
|
-
tag_on_cluster_updates=True,
|
319
|
-
access=[
|
320
|
-
AccessV1(
|
321
|
-
cluster=None,
|
322
|
-
namespace=NamespaceV1(
|
323
|
-
name="namespace",
|
324
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
325
|
-
),
|
326
|
-
)
|
327
|
-
],
|
328
|
-
),
|
329
|
-
RoleV1(
|
330
|
-
tag_on_cluster_updates=False,
|
331
|
-
access=[
|
332
|
-
AccessV1(
|
333
|
-
cluster=None,
|
334
|
-
namespace=NamespaceV1(
|
335
|
-
name="namespace",
|
336
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
337
|
-
),
|
338
|
-
)
|
339
|
-
],
|
340
|
-
),
|
341
|
-
]
|
342
|
-
assert integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
343
|
-
# role: namespace and tag_on_cluster_updates = None & False
|
344
|
-
user.roles = [
|
345
|
-
RoleV1(
|
346
|
-
tag_on_cluster_updates=None,
|
347
|
-
access=[
|
348
|
-
AccessV1(
|
349
|
-
cluster=None,
|
350
|
-
namespace=NamespaceV1(
|
351
|
-
name="namespace",
|
352
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
353
|
-
),
|
354
|
-
)
|
355
|
-
],
|
356
|
-
),
|
357
|
-
RoleV1(
|
358
|
-
tag_on_cluster_updates=False,
|
359
|
-
access=[
|
360
|
-
AccessV1(
|
361
|
-
cluster=None,
|
362
|
-
namespace=NamespaceV1(
|
363
|
-
name="namespace",
|
364
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
365
|
-
),
|
366
|
-
)
|
367
|
-
],
|
368
|
-
),
|
369
|
-
]
|
370
|
-
assert integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
371
|
-
|
372
|
-
# role: namespace and tag_on_cluster_updates = False & False
|
373
|
-
user.roles = [
|
374
|
-
RoleV1(
|
375
|
-
tag_on_cluster_updates=False,
|
376
|
-
access=[
|
377
|
-
AccessV1(
|
378
|
-
cluster=None,
|
379
|
-
namespace=NamespaceV1(
|
380
|
-
name="namespace",
|
381
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
382
|
-
),
|
383
|
-
)
|
384
|
-
],
|
385
|
-
),
|
386
|
-
RoleV1(
|
387
|
-
tag_on_cluster_updates=False,
|
388
|
-
access=[
|
389
|
-
AccessV1(
|
390
|
-
cluster=None,
|
391
|
-
namespace=NamespaceV1(
|
392
|
-
name="namespace",
|
393
|
-
cluster=NamespaceV1_ClusterV1(name=cluster.name),
|
394
|
-
),
|
395
|
-
)
|
396
|
-
],
|
397
|
-
),
|
398
|
-
]
|
399
|
-
assert not integ.include_user_to_cluster_usergroup(user, cluster, ["user1"])
|
400
|
-
|
401
|
-
|
402
|
-
def test_get_desired_state(
|
403
|
-
mocker: MockerFixture,
|
404
|
-
permissions: Sequence[PermissionSlackUsergroupV1],
|
405
|
-
user: UserV1,
|
406
|
-
) -> None:
|
407
|
-
mocker.patch(
|
408
|
-
"reconcile.slack_usergroups.get_usernames_from_pagerduty"
|
409
|
-
).return_value = ["user1"]
|
410
|
-
mocker.patch(
|
411
|
-
"reconcile.slack_usergroups.get_slack_usernames_from_owners"
|
412
|
-
).return_value = ["repo-user"]
|
413
|
-
mock_pagerduty_map = create_autospec(PagerDutyMap)
|
414
|
-
result = integ.get_desired_state(
|
415
|
-
mock_pagerduty_map,
|
416
|
-
permissions[1:],
|
417
|
-
[user],
|
418
|
-
desired_workspace_name=None,
|
419
|
-
desired_usergroup_name=None,
|
420
|
-
)
|
421
|
-
|
422
|
-
assert result == {
|
423
|
-
"coreos": {
|
424
|
-
"saas-osd-operators": State(
|
425
|
-
workspace="coreos",
|
426
|
-
usergroup="saas-osd-operators",
|
427
|
-
description="SREP managed-cluster-config owners (managed via app-interface)",
|
428
|
-
users=set(),
|
429
|
-
user_names={"repo-user", "user1", "slack_username"},
|
430
|
-
channels=set(),
|
431
|
-
channel_names={"sre-operators", "sd-sre-platform"},
|
432
|
-
usergroup_id="ugid",
|
433
|
-
)
|
434
|
-
}
|
435
|
-
}
|
436
|
-
|
437
|
-
|
438
|
-
def test_get_desired_state_with_error(
|
439
|
-
mocker: MockerFixture,
|
440
|
-
permissions: Sequence[PermissionSlackUsergroupV1],
|
441
|
-
user: UserV1,
|
442
|
-
) -> None:
|
443
|
-
mocker.patch(
|
444
|
-
"reconcile.slack_usergroups.get_usernames_from_pagerduty"
|
445
|
-
).return_value = ["user1"]
|
446
|
-
mocker.patch(
|
447
|
-
"reconcile.slack_usergroups.get_slack_usernames_from_owners"
|
448
|
-
).side_effect = Exception("some error")
|
449
|
-
mock_pagerduty_map = create_autospec(PagerDutyMap)
|
450
|
-
result = integ.get_desired_state(
|
451
|
-
mock_pagerduty_map,
|
452
|
-
permissions[1:],
|
453
|
-
[user],
|
454
|
-
desired_workspace_name=None,
|
455
|
-
desired_usergroup_name=None,
|
456
|
-
)
|
457
|
-
|
458
|
-
assert result == {}
|
459
|
-
|
460
|
-
|
461
|
-
def test_get_desired_state_cluster_usergroups(
|
462
|
-
mocker: MockerFixture, slack_map: SlackMap, user: UserV1
|
463
|
-
) -> None:
|
464
|
-
mocker.patch("reconcile.openshift_users.fetch_desired_state", autospec=True)
|
465
|
-
mocker.patch(
|
466
|
-
"reconcile.slack_usergroups.include_user_to_cluster_usergroup"
|
467
|
-
).return_value = True
|
468
|
-
|
469
|
-
cluster = ClusterV1(name="cluster1", auth=[], disable={"integrations": []})
|
470
|
-
result = integ.get_desired_state_cluster_usergroups(
|
471
|
-
slack_map, [cluster], [user], None, None
|
472
|
-
)
|
473
|
-
|
474
|
-
assert result == {
|
475
|
-
"coreos": {
|
476
|
-
"cluster1-cluster": State(
|
477
|
-
workspace="coreos",
|
478
|
-
usergroup="cluster1-cluster",
|
479
|
-
description="Users with access to the cluster1 cluster",
|
480
|
-
users=set(),
|
481
|
-
user_names={"slack"},
|
482
|
-
channels=set(),
|
483
|
-
channel_names={"channel"},
|
484
|
-
usergroup_id="ugid",
|
485
|
-
)
|
486
|
-
},
|
487
|
-
"slack-workspace": {
|
488
|
-
"cluster1-cluster": State(
|
489
|
-
workspace="slack-workspace",
|
490
|
-
usergroup="cluster1-cluster",
|
491
|
-
description="Users with access to the cluster1 cluster",
|
492
|
-
users=set(),
|
493
|
-
user_names={"slack"},
|
494
|
-
channels=set(),
|
495
|
-
channel_names={"channel"},
|
496
|
-
usergroup_id="ugid",
|
497
|
-
)
|
498
|
-
},
|
499
|
-
}
|
500
|
-
|
501
|
-
|
502
|
-
def test_get_desired_state_non_existing_usergroup(
|
503
|
-
mocker: MockerFixture, slack_map: SlackMap, user: UserV1
|
504
|
-
) -> None:
|
505
|
-
mocker.patch("reconcile.openshift_users.fetch_desired_state", autospec=True)
|
506
|
-
mocker.patch(
|
507
|
-
"reconcile.slack_usergroups.include_user_to_cluster_usergroup"
|
508
|
-
).return_value = True
|
509
|
-
|
510
|
-
cluster = ClusterV1(name="cluster1", auth=[], disable={"integrations": []})
|
511
|
-
result = integ.get_desired_state_cluster_usergroups(
|
512
|
-
slack_map, [cluster], [user], None, None
|
513
|
-
)
|
514
|
-
|
515
|
-
assert result == {
|
516
|
-
"coreos": {
|
517
|
-
"cluster1-cluster": State(
|
518
|
-
workspace="coreos",
|
519
|
-
usergroup="cluster1-cluster",
|
520
|
-
description="Users with access to the cluster1 cluster",
|
521
|
-
users=set(),
|
522
|
-
user_names={"slack"},
|
523
|
-
channels=set(),
|
524
|
-
channel_names={"channel"},
|
525
|
-
usergroup_id=None,
|
526
|
-
)
|
527
|
-
},
|
528
|
-
"slack-workspace": {
|
529
|
-
"cluster1-cluster": State(
|
530
|
-
workspace="slack-workspace",
|
531
|
-
usergroup="cluster1-cluster",
|
532
|
-
description="Users with access to the cluster1 cluster",
|
533
|
-
users=set(),
|
534
|
-
user_names={"slack"},
|
535
|
-
channels=set(),
|
536
|
-
channel_names={"channel"},
|
537
|
-
usergroup_id=None,
|
538
|
-
)
|
539
|
-
},
|
540
|
-
}
|
541
|
-
|
542
|
-
|
543
|
-
def test_get_slack_map_return_expected(
|
544
|
-
mocker: MockerFixture, permissions: Iterable[PermissionSlackUsergroupV1]
|
545
|
-
) -> None:
|
546
|
-
mock_slack_api = mocker.patch.object(slackbase, "SlackApi", autospec=True)
|
547
|
-
mock_secretreader = mocker.patch(
|
548
|
-
"reconcile.utils.secret_reader.SecretReader", autospec=True
|
549
|
-
)
|
550
|
-
mock_secretreader.return_value.read.return_value = "secret"
|
551
|
-
|
552
|
-
result = integ.get_slack_map(mock_secretreader, permissions)
|
553
|
-
mock_slack_api.assert_called_once()
|
554
|
-
# just one workspace in the permissions
|
555
|
-
assert len(result) == 1
|
556
|
-
assert isinstance(result["coreos"].slack, SlackApi)
|
557
|
-
assert result["coreos"].managed_usergroups == [
|
558
|
-
"app-sre-team",
|
559
|
-
"app-sre-ic",
|
560
|
-
"backplane-team",
|
561
|
-
]
|
562
|
-
|
563
|
-
|
564
|
-
def test_act_no_changes_detected(
|
565
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
566
|
-
) -> None:
|
567
|
-
"""No changes should be made when the states are identical."""
|
568
|
-
current_state = base_state
|
569
|
-
desired_state = base_state
|
570
|
-
|
571
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
572
|
-
|
573
|
-
slack_client_mock.update_usergroup.assert_not_called()
|
574
|
-
slack_client_mock.update_usergroup_users.assert_not_called()
|
575
|
-
|
576
|
-
|
577
|
-
def test_act_dryrun_no_changes_made(
|
578
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
579
|
-
) -> None:
|
580
|
-
"""No changes should be made when dryrun mode is enabled."""
|
581
|
-
|
582
|
-
current_state = base_state
|
583
|
-
desired_state = copy.deepcopy(base_state)
|
584
|
-
|
585
|
-
desired_state["slack-workspace"]["usergroup-1"].user_names = {"foo"}
|
586
|
-
|
587
|
-
act(current_state, desired_state, slack_map, dry_run=True)
|
588
|
-
|
589
|
-
slack_client_mock.update_usergroup.assert_not_called()
|
590
|
-
slack_client_mock.update_usergroup_users.assert_not_called()
|
591
|
-
|
592
|
-
|
593
|
-
def test_act_empty_current_state(
|
594
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
595
|
-
) -> None:
|
596
|
-
"""
|
597
|
-
An empty current state should be able to be handled properly (watching for
|
598
|
-
TypeErrors, etc).
|
599
|
-
"""
|
600
|
-
|
601
|
-
current_state: SlackState = {}
|
602
|
-
desired_state = base_state
|
603
|
-
|
604
|
-
slack_client_mock.create_usergroup.return_value = "USERGA"
|
605
|
-
slack_client_mock.get_usergroup_id.return_value = "USERGA"
|
606
|
-
slack_client_mock.get_active_users_by_names.return_value = {"USERA": "username"}
|
607
|
-
slack_client_mock.get_channels_by_names.return_value = {"CHANA": "someotherchannel"}
|
608
|
-
|
609
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
610
|
-
|
611
|
-
assert slack_client_mock.create_usergroup.call_args_list == [call("usergroup-1")]
|
612
|
-
assert slack_client_mock.update_usergroup.call_args_list == [
|
613
|
-
call(id="USERGA", channels_list=["CHANA"], description="Some description")
|
614
|
-
]
|
615
|
-
assert slack_client_mock.update_usergroup_users.call_args_list == [
|
616
|
-
call(id="USERGA", users_list=["USERA"])
|
617
|
-
]
|
618
|
-
|
619
|
-
|
620
|
-
def test_act_update_usergroup_users(
|
621
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
622
|
-
) -> None:
|
623
|
-
current_state = base_state
|
624
|
-
desired_state = copy.deepcopy(base_state)
|
625
|
-
|
626
|
-
desired_state["slack-workspace"]["usergroup-1"].user_names = {
|
627
|
-
"someotherusername",
|
628
|
-
"anotheruser",
|
629
|
-
}
|
630
|
-
|
631
|
-
slack_client_mock.get_usergroup_id.return_value = "USERGA"
|
632
|
-
slack_client_mock.get_active_users_by_names.return_value = {
|
633
|
-
"USERB": "someotherusername",
|
634
|
-
"USERC": "anotheruser",
|
635
|
-
}
|
636
|
-
slack_client_mock.get_channels_by_names.return_value = {"CHANA": "channelname"}
|
637
|
-
|
638
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
639
|
-
|
640
|
-
slack_client_mock.update_usergroup.assert_not_called()
|
641
|
-
slack_client_mock.update_usergroup_users.assert_called_once()
|
642
|
-
assert slack_client_mock.update_usergroup_users.call_args_list == [
|
643
|
-
call(id="USERGA", users_list=["USERB", "USERC"])
|
644
|
-
]
|
645
|
-
|
646
|
-
|
647
|
-
def test_act_update_usergroup_channels(
|
648
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
649
|
-
) -> None:
|
650
|
-
current_state = base_state
|
651
|
-
desired_state = copy.deepcopy(base_state)
|
652
|
-
|
653
|
-
desired_state["slack-workspace"]["usergroup-1"].channel_names = {"CHANB"}
|
654
|
-
|
655
|
-
slack_client_mock.get_usergroup_id.return_value = "USERGA"
|
656
|
-
slack_client_mock.get_active_users_by_names.return_value = {"USERA": "username"}
|
657
|
-
slack_client_mock.get_channels_by_names.return_value = {"CHANB": "channel"}
|
658
|
-
|
659
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
660
|
-
|
661
|
-
assert slack_client_mock.update_usergroup.call_args_list == [
|
662
|
-
call(id="USERGA", channels_list=["CHANB"], description="Some description")
|
663
|
-
]
|
664
|
-
slack_client_mock.update_usergroup_users.assert_not_called()
|
665
|
-
|
666
|
-
|
667
|
-
def test_act_update_usergroup_description(
|
668
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
669
|
-
) -> None:
|
670
|
-
current_state = base_state
|
671
|
-
desired_state = copy.deepcopy(base_state)
|
672
|
-
|
673
|
-
desired_state["slack-workspace"][
|
674
|
-
"usergroup-1"
|
675
|
-
].description = "A different description"
|
676
|
-
|
677
|
-
slack_client_mock.get_usergroup_id.return_value = "USERGA"
|
678
|
-
slack_client_mock.get_active_users_by_names.return_value = {"USERA": "username"}
|
679
|
-
slack_client_mock.get_channels_by_names.return_value = {"CHANA": "channel"}
|
680
|
-
|
681
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
682
|
-
|
683
|
-
assert slack_client_mock.update_usergroup.call_args_list == [
|
684
|
-
call(
|
685
|
-
id="USERGA", channels_list=["CHANA"], description="A different description"
|
686
|
-
)
|
687
|
-
]
|
688
|
-
slack_client_mock.update_usergroup_users.assert_not_called()
|
689
|
-
|
690
|
-
|
691
|
-
def test_act_update_usergroup_desc_and_channels(
|
692
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
693
|
-
) -> None:
|
694
|
-
current_state = base_state
|
695
|
-
desired_state = copy.deepcopy(base_state)
|
696
|
-
|
697
|
-
desired_state["slack-workspace"][
|
698
|
-
"usergroup-1"
|
699
|
-
].description = "A different description"
|
700
|
-
|
701
|
-
slack_client_mock.get_usergroup_id.return_value = "USERGA"
|
702
|
-
slack_client_mock.get_active_users_by_names.return_value = {"USERA": "username"}
|
703
|
-
slack_client_mock.get_channels_by_names.return_value = {"CHANB": "someotherchannel"}
|
704
|
-
|
705
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
706
|
-
|
707
|
-
assert slack_client_mock.update_usergroup.call_args_list == [
|
708
|
-
call(
|
709
|
-
id="USERGA", channels_list=["CHANB"], description="A different description"
|
710
|
-
)
|
711
|
-
]
|
712
|
-
slack_client_mock.update_usergroup_users.assert_not_called()
|
713
|
-
|
714
|
-
|
715
|
-
def test_act_add_new_usergroups(
|
716
|
-
base_state: SlackState, slack_map: SlackMap, slack_client_mock: Mock
|
717
|
-
) -> None:
|
718
|
-
def get_users(users: set[str]) -> dict[str, str]:
|
719
|
-
if "username" in users:
|
720
|
-
return {"USERA": "username"}
|
721
|
-
if "userb" in users:
|
722
|
-
return {"USERB": "userb", "USERC": "userc"}
|
723
|
-
return {"USERF": "userf", "USERG": "userg"}
|
724
|
-
|
725
|
-
def get_channels(channels: set[str]) -> dict[str, str]:
|
726
|
-
if "channelname" in channels:
|
727
|
-
return {"CHANA": "channelname"}
|
728
|
-
if "channelb" in channels:
|
729
|
-
return {"CHANB": "channelb", "CHANC": "channelc"}
|
730
|
-
return {"CHANF": "channelf", "CHANG": "channelg"}
|
731
|
-
|
732
|
-
def get_ugid(usergroup: str) -> str:
|
733
|
-
if usergroup == "usergroup-1":
|
734
|
-
return "USERGA"
|
735
|
-
if usergroup == "usergroup-2":
|
736
|
-
return "USERGB"
|
737
|
-
return "USERGC"
|
738
|
-
|
739
|
-
current_state = base_state
|
740
|
-
desired_state = copy.deepcopy(base_state)
|
741
|
-
|
742
|
-
slack_client_mock.get_usergroup_id.side_effect = get_ugid
|
743
|
-
slack_client_mock.get_active_users_by_names.side_effect = get_users
|
744
|
-
slack_client_mock.get_channels_by_names.side_effect = get_channels
|
745
|
-
|
746
|
-
desired_state["slack-workspace"]["usergroup-2"] = State(
|
747
|
-
workspace="slack-workspace",
|
748
|
-
usergroup="usergroup-2",
|
749
|
-
usergroup_id="USERGB",
|
750
|
-
user_names={"userb", "userc"},
|
751
|
-
channel_names={"channelb", "channelc"},
|
752
|
-
description="A new usergroup",
|
753
|
-
)
|
754
|
-
|
755
|
-
desired_state["slack-workspace"]["usergroup-3"] = State(
|
756
|
-
workspace="slack-workspace",
|
757
|
-
usergroup="usergroup-3",
|
758
|
-
usergroup_id="USERGC",
|
759
|
-
user_names={"userf", "userg"},
|
760
|
-
channel_names={"channelf", "channelg"},
|
761
|
-
description="Another new usergroup",
|
762
|
-
)
|
763
|
-
slack_client_mock.create_usergroup.side_effect = ["USERGB", "USERGC"]
|
764
|
-
|
765
|
-
act(current_state, desired_state, slack_map, dry_run=False)
|
766
|
-
|
767
|
-
assert slack_client_mock.create_usergroup.call_args_list == [
|
768
|
-
call("usergroup-2"),
|
769
|
-
call("usergroup-3"),
|
770
|
-
]
|
771
|
-
|
772
|
-
assert slack_client_mock.update_usergroup.call_args_list == [
|
773
|
-
call(
|
774
|
-
id="USERGB", channels_list=["CHANB", "CHANC"], description="A new usergroup"
|
775
|
-
),
|
776
|
-
call(
|
777
|
-
id="USERGC",
|
778
|
-
channels_list=["CHANF", "CHANG"],
|
779
|
-
description="Another new usergroup",
|
780
|
-
),
|
781
|
-
]
|
782
|
-
assert slack_client_mock.update_usergroup_users.call_args_list == [
|
783
|
-
call(id="USERGB", users_list=["USERB", "USERC"]),
|
784
|
-
call(id="USERGC", users_list=["USERF", "USERG"]),
|
785
|
-
]
|