qontract-reconcile 0.10.1rc1201__py3-none-any.whl → 0.10.2.dev1__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.
Files changed (140) hide show
  1. qontract_reconcile-0.10.2.dev1.dist-info/METADATA +500 -0
  2. {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/RECORD +14 -132
  3. {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/WHEEL +1 -2
  4. {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/entry_points.txt +1 -0
  5. reconcile/aws_account_manager/README.md +5 -0
  6. reconcile/change_owners/README.md +34 -0
  7. reconcile/external_resources/manager.py +12 -1
  8. reconcile/external_resources/model.py +11 -0
  9. reconcile/glitchtip/README.md +150 -0
  10. reconcile/gql_definitions/introspection.json +51176 -0
  11. reconcile/run_integration.py +293 -0
  12. reconcile/utils/binary.py +2 -2
  13. reconcile/utils/mr/README.md +198 -0
  14. reconcile/utils/oc_map.py +2 -2
  15. tools/qontract_cli.py +0 -0
  16. qontract_reconcile-0.10.1rc1201.dist-info/METADATA +0 -64
  17. qontract_reconcile-0.10.1rc1201.dist-info/top_level.txt +0 -3
  18. reconcile/test/__init__.py +0 -0
  19. reconcile/test/conftest.py +0 -157
  20. reconcile/test/fixtures.py +0 -24
  21. reconcile/test/saas_auto_promotions_manager/__init__.py +0 -0
  22. reconcile/test/saas_auto_promotions_manager/conftest.py +0 -170
  23. reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py +0 -0
  24. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py +0 -0
  25. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py +0 -115
  26. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py +0 -19
  27. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py +0 -66
  28. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py +0 -86
  29. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parser.py +0 -352
  30. reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_reconciler.py +0 -494
  31. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/__init__.py +0 -0
  32. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/conftest.py +0 -25
  33. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_multiple_namespaces.py +0 -37
  34. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_namespace.py +0 -81
  35. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_content_single_target.py +0 -61
  36. reconcile/test/saas_auto_promotions_manager/merge_request_manager/renderer/test_json_path_selector.py +0 -74
  37. reconcile/test/saas_auto_promotions_manager/test_integration_test.py +0 -52
  38. reconcile/test/saas_auto_promotions_manager/utils/__init__.py +0 -0
  39. reconcile/test/test_acs_notifiers.py +0 -393
  40. reconcile/test/test_acs_policies.py +0 -497
  41. reconcile/test/test_acs_rbac.py +0 -865
  42. reconcile/test/test_aggregated_list.py +0 -237
  43. reconcile/test/test_amtool.py +0 -37
  44. reconcile/test/test_aws_ami_cleanup.py +0 -230
  45. reconcile/test/test_aws_ami_share.py +0 -68
  46. reconcile/test/test_aws_cloudwatch_log_retention.py +0 -434
  47. reconcile/test/test_aws_iam_keys.py +0 -70
  48. reconcile/test/test_aws_iam_password_reset.py +0 -35
  49. reconcile/test/test_aws_support_cases_sos.py +0 -23
  50. reconcile/test/test_checkpoint.py +0 -178
  51. reconcile/test/test_cli.py +0 -41
  52. reconcile/test/test_closedbox_endpoint_monitoring.py +0 -207
  53. reconcile/test/test_dashdotdb_dora.py +0 -245
  54. reconcile/test/test_database_access_manager.py +0 -660
  55. reconcile/test/test_deadmanssnitch.py +0 -290
  56. reconcile/test/test_gabi_authorized_users.py +0 -72
  57. reconcile/test/test_gcr_mirror.py +0 -14
  58. reconcile/test/test_github_org.py +0 -156
  59. reconcile/test/test_github_repo_invites.py +0 -119
  60. reconcile/test/test_gitlab_housekeeping.py +0 -333
  61. reconcile/test/test_gitlab_labeler.py +0 -126
  62. reconcile/test/test_gitlab_members.py +0 -219
  63. reconcile/test/test_gitlab_permissions.py +0 -164
  64. reconcile/test/test_instrumented_wrappers.py +0 -18
  65. reconcile/test/test_integrations_manager.py +0 -1252
  66. reconcile/test/test_jenkins_worker_fleets.py +0 -57
  67. reconcile/test/test_jira_permissions_validator.py +0 -519
  68. reconcile/test/test_jump_host.py +0 -114
  69. reconcile/test/test_ldap_users.py +0 -125
  70. reconcile/test/test_make.py +0 -28
  71. reconcile/test/test_ocm_additional_routers.py +0 -133
  72. reconcile/test/test_ocm_clusters.py +0 -798
  73. reconcile/test/test_ocm_clusters_manifest_updates.py +0 -87
  74. reconcile/test/test_ocm_machine_pools.py +0 -1103
  75. reconcile/test/test_ocm_update_recommended_version.py +0 -145
  76. reconcile/test/test_ocm_upgrade_scheduler_org_updater.py +0 -125
  77. reconcile/test/test_openshift_base.py +0 -1269
  78. reconcile/test/test_openshift_cluster_bots.py +0 -240
  79. reconcile/test/test_openshift_namespace_labels.py +0 -344
  80. reconcile/test/test_openshift_namespaces.py +0 -256
  81. reconcile/test/test_openshift_resource.py +0 -443
  82. reconcile/test/test_openshift_resources_base.py +0 -478
  83. reconcile/test/test_openshift_saas_deploy.py +0 -188
  84. reconcile/test/test_openshift_saas_deploy_change_tester.py +0 -308
  85. reconcile/test/test_openshift_saas_deploy_trigger_cleaner.py +0 -65
  86. reconcile/test/test_openshift_serviceaccount_tokens.py +0 -282
  87. reconcile/test/test_openshift_tekton_resources.py +0 -265
  88. reconcile/test/test_openshift_upgrade_watcher.py +0 -223
  89. reconcile/test/test_prometheus_rules_tester.py +0 -151
  90. reconcile/test/test_quay_membership.py +0 -86
  91. reconcile/test/test_quay_mirror.py +0 -172
  92. reconcile/test/test_quay_mirror_org.py +0 -82
  93. reconcile/test/test_quay_repos.py +0 -59
  94. reconcile/test/test_queries.py +0 -53
  95. reconcile/test/test_repo_owners.py +0 -47
  96. reconcile/test/test_requests_sender.py +0 -139
  97. reconcile/test/test_saasherder.py +0 -1611
  98. reconcile/test/test_saasherder_allowed_secret_paths.py +0 -125
  99. reconcile/test/test_secret_reader.py +0 -153
  100. reconcile/test/test_slack_base.py +0 -183
  101. reconcile/test/test_slack_usergroups.py +0 -785
  102. reconcile/test/test_sql_query.py +0 -316
  103. reconcile/test/test_status_board.py +0 -258
  104. reconcile/test/test_terraform_aws_route53.py +0 -29
  105. reconcile/test/test_terraform_cloudflare_dns.py +0 -117
  106. reconcile/test/test_terraform_cloudflare_resources.py +0 -408
  107. reconcile/test/test_terraform_cloudflare_users.py +0 -747
  108. reconcile/test/test_terraform_repo.py +0 -440
  109. reconcile/test/test_terraform_resources.py +0 -519
  110. reconcile/test/test_terraform_tgw_attachments.py +0 -1295
  111. reconcile/test/test_terraform_users.py +0 -152
  112. reconcile/test/test_terraform_vpc_peerings.py +0 -576
  113. reconcile/test/test_terraform_vpc_peerings_build_desired_state.py +0 -1434
  114. reconcile/test/test_three_way_diff_strategy.py +0 -131
  115. reconcile/test/test_utils_jinja2.py +0 -130
  116. reconcile/test/test_vault_replication.py +0 -534
  117. reconcile/test/test_vault_utils.py +0 -47
  118. reconcile/test/test_version_bump.py +0 -18
  119. reconcile/test/test_vpc_peerings_validator.py +0 -194
  120. reconcile/test/test_wrong_region.py +0 -78
  121. release/__init__.py +0 -0
  122. release/test_version.py +0 -50
  123. release/version.py +0 -104
  124. tools/cli_commands/test/__init__.py +0 -0
  125. tools/cli_commands/test/conftest.py +0 -332
  126. tools/cli_commands/test/test_aws_cost_report.py +0 -258
  127. tools/cli_commands/test/test_cost_management_api.py +0 -326
  128. tools/cli_commands/test/test_gpg_encrypt.py +0 -235
  129. tools/cli_commands/test/test_openshift_cost_optimization_report.py +0 -255
  130. tools/cli_commands/test/test_openshift_cost_report.py +0 -295
  131. tools/cli_commands/test/test_util.py +0 -70
  132. tools/test/__init__.py +0 -0
  133. tools/test/conftest.py +0 -77
  134. tools/test/test_app_interface_metrics_exporter.py +0 -48
  135. tools/test/test_erv2.py +0 -80
  136. tools/test/test_get_container_images.py +0 -230
  137. tools/test/test_qontract_cli.py +0 -197
  138. tools/test/test_saas_promotion_state.py +0 -187
  139. tools/test/test_sd_app_sre_alert_report.py +0 -74
  140. 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
- ]