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.
- qontract_reconcile-0.10.2.dev1.dist-info/METADATA +500 -0
- {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/RECORD +14 -132
- {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/WHEEL +1 -2
- {qontract_reconcile-0.10.1rc1201.dist-info → qontract_reconcile-0.10.2.dev1.dist-info}/entry_points.txt +1 -0
- reconcile/aws_account_manager/README.md +5 -0
- reconcile/change_owners/README.md +34 -0
- reconcile/external_resources/manager.py +12 -1
- reconcile/external_resources/model.py +11 -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.1rc1201.dist-info/METADATA +0 -64
- qontract_reconcile-0.10.1rc1201.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,86 +0,0 @@
|
|
1
|
-
from unittest.mock import patch
|
2
|
-
|
3
|
-
from reconcile import quay_membership
|
4
|
-
from reconcile.utils import (
|
5
|
-
config,
|
6
|
-
gql,
|
7
|
-
)
|
8
|
-
from reconcile.utils.aggregated_list import AggregatedList
|
9
|
-
|
10
|
-
from .fixtures import Fixtures
|
11
|
-
|
12
|
-
fxt = Fixtures("quay_membership")
|
13
|
-
|
14
|
-
|
15
|
-
def get_items_by_params(state, params):
|
16
|
-
h = AggregatedList.hash_params(params)
|
17
|
-
for group in state:
|
18
|
-
this_h = AggregatedList.hash_params(group["params"])
|
19
|
-
|
20
|
-
if h == this_h:
|
21
|
-
return sorted(group["items"])
|
22
|
-
return False
|
23
|
-
|
24
|
-
|
25
|
-
class QuayApiMock:
|
26
|
-
def __init__(self, list_team_members_response):
|
27
|
-
self.list_team_members_response = list_team_members_response
|
28
|
-
|
29
|
-
def list_team_members(self, team):
|
30
|
-
return self.list_team_members_response[team]
|
31
|
-
|
32
|
-
|
33
|
-
class TestQuayMembership:
|
34
|
-
@staticmethod
|
35
|
-
def setup_method(method):
|
36
|
-
config.init_from_toml(fxt.path("config.toml"))
|
37
|
-
gql.init_from_config(autodetect_sha=False)
|
38
|
-
|
39
|
-
@staticmethod
|
40
|
-
def do_current_state_test(path):
|
41
|
-
fixture = fxt.get_anymarkup(path)
|
42
|
-
|
43
|
-
quay_org_catalog = fixture["quay_org_catalog"]
|
44
|
-
quay_org_teams = fixture["quay_org_teams"]
|
45
|
-
|
46
|
-
store = {}
|
47
|
-
for org_data in quay_org_catalog:
|
48
|
-
name = org_data["name"]
|
49
|
-
store[name] = {
|
50
|
-
"api": QuayApiMock(quay_org_teams[name]),
|
51
|
-
"teams": org_data["managedTeams"],
|
52
|
-
}
|
53
|
-
|
54
|
-
current_state = quay_membership.fetch_current_state(store)
|
55
|
-
current_state = current_state.dump()
|
56
|
-
|
57
|
-
expected_current_state = fixture["state"]
|
58
|
-
|
59
|
-
assert len(current_state) == len(expected_current_state)
|
60
|
-
for group in current_state:
|
61
|
-
params = group["params"]
|
62
|
-
items = sorted(group["items"])
|
63
|
-
assert items == get_items_by_params(expected_current_state, params)
|
64
|
-
|
65
|
-
@staticmethod
|
66
|
-
def do_desired_state_test(path):
|
67
|
-
fixture = fxt.get_anymarkup(path)
|
68
|
-
|
69
|
-
with patch("reconcile.utils.gql.GqlApi.query") as m_gql:
|
70
|
-
m_gql.return_value = fixture["gql_response"]
|
71
|
-
|
72
|
-
desired_state = quay_membership.fetch_desired_state().dump()
|
73
|
-
|
74
|
-
expected_desired_state = fixture["state"]
|
75
|
-
|
76
|
-
assert len(desired_state) == len(expected_desired_state)
|
77
|
-
for group in desired_state:
|
78
|
-
params = group["params"]
|
79
|
-
items = sorted(group["items"])
|
80
|
-
assert items == get_items_by_params(expected_desired_state, params)
|
81
|
-
|
82
|
-
def test_current_state_simple(self):
|
83
|
-
self.do_current_state_test("current_state_simple.yml")
|
84
|
-
|
85
|
-
def test_desired_state_simple(self):
|
86
|
-
self.do_desired_state_test("desired_state_simple.yml")
|
@@ -1,172 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import tempfile
|
3
|
-
from unittest.mock import create_autospec, patch
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
import requests
|
7
|
-
|
8
|
-
from reconcile.quay_mirror import (
|
9
|
-
CONTROL_FILE_NAME,
|
10
|
-
OrgKey,
|
11
|
-
QuayMirror,
|
12
|
-
queries,
|
13
|
-
)
|
14
|
-
|
15
|
-
from .fixtures import Fixtures
|
16
|
-
|
17
|
-
NOW = 1662124612.995397
|
18
|
-
|
19
|
-
fxt = Fixtures("quay_mirror")
|
20
|
-
|
21
|
-
|
22
|
-
@patch("reconcile.utils.gql.get_api", autospec=True)
|
23
|
-
@patch("reconcile.queries.get_app_interface_settings", return_value={})
|
24
|
-
class TestControlFile:
|
25
|
-
def test_check_compare_tags_no_control_file(self, mock_gql, mock_settings):
|
26
|
-
assert QuayMirror.check_compare_tags_elapsed_time("/no-such-file", 100)
|
27
|
-
|
28
|
-
@patch("time.time", return_value=NOW)
|
29
|
-
def test_check_compare_tags_with_file(self, mock_gql, mock_settings, mock_time):
|
30
|
-
with tempfile.NamedTemporaryFile() as fp:
|
31
|
-
fp.write(str(NOW - 100.0).encode())
|
32
|
-
fp.seek(0)
|
33
|
-
|
34
|
-
assert QuayMirror.check_compare_tags_elapsed_time(fp.name, 10)
|
35
|
-
assert not QuayMirror.check_compare_tags_elapsed_time(fp.name, 1000)
|
36
|
-
|
37
|
-
def test_control_file_dir_does_not_exist(self, mock_gql, mock_settings):
|
38
|
-
with pytest.raises(FileNotFoundError):
|
39
|
-
QuayMirror(control_file_dir="/no-such-dir")
|
40
|
-
|
41
|
-
def test_control_file_path_from_given_dir(self, mock_gql, mock_settings):
|
42
|
-
with tempfile.TemporaryDirectory() as tmp_dir_name:
|
43
|
-
qm = QuayMirror(control_file_dir=tmp_dir_name)
|
44
|
-
assert qm.control_file_path == os.path.join(tmp_dir_name, CONTROL_FILE_NAME)
|
45
|
-
|
46
|
-
|
47
|
-
@patch("reconcile.utils.gql.get_api", autospec=True)
|
48
|
-
@patch("reconcile.queries.get_app_interface_settings", return_value={})
|
49
|
-
@patch("time.time", return_value=NOW)
|
50
|
-
class TestIsCompareTags:
|
51
|
-
def setup_method(self):
|
52
|
-
self.tmp_dir = (
|
53
|
-
tempfile.TemporaryDirectory() # pylint: disable=consider-using-with
|
54
|
-
)
|
55
|
-
with open(
|
56
|
-
os.path.join(self.tmp_dir.name, CONTROL_FILE_NAME), "w", encoding="locale"
|
57
|
-
) as fh:
|
58
|
-
fh.write(str(NOW - 100.0))
|
59
|
-
|
60
|
-
def teardown_method(self):
|
61
|
-
self.tmp_dir.cleanup()
|
62
|
-
|
63
|
-
# Last run was in NOW - 100s, we run compare tags every 10s.
|
64
|
-
def test_is_compare_tags_elapsed(self, mock_gql, mock_settings, mock_time):
|
65
|
-
qm = QuayMirror(control_file_dir=self.tmp_dir.name, compare_tags_interval=10)
|
66
|
-
assert qm.is_compare_tags
|
67
|
-
|
68
|
-
# Same as before, but now we force no compare with the option.
|
69
|
-
def test_is_compare_tags_force_no_compare(self, mock_gql, mock_settings, mock_time):
|
70
|
-
qm = QuayMirror(
|
71
|
-
control_file_dir=self.tmp_dir.name,
|
72
|
-
compare_tags_interval=10,
|
73
|
-
compare_tags=False,
|
74
|
-
)
|
75
|
-
assert not qm.is_compare_tags
|
76
|
-
|
77
|
-
# Last run was in NOW - 100s, we run compare tags every 1000s.
|
78
|
-
def test_is_compare_tags_not_elapsed(self, mock_gql, mock_settings, mock_time):
|
79
|
-
qm = QuayMirror(control_file_dir=self.tmp_dir.name, compare_tags_interval=1000)
|
80
|
-
assert not qm.is_compare_tags
|
81
|
-
|
82
|
-
# Same as before, but now we force compare with the option.
|
83
|
-
def test_is_compare_tags_force_compare(self, mock_gql, mock_settings, mock_time):
|
84
|
-
qm = QuayMirror(
|
85
|
-
control_file_dir=self.tmp_dir.name,
|
86
|
-
compare_tags_interval=1000,
|
87
|
-
compare_tags=True,
|
88
|
-
)
|
89
|
-
assert qm.is_compare_tags
|
90
|
-
|
91
|
-
|
92
|
-
@pytest.mark.parametrize(
|
93
|
-
"tags, tags_exclude, candidate, result",
|
94
|
-
[
|
95
|
-
# Tags include tests.
|
96
|
-
(["^sha256-.+sig$", "^main-.+"], None, "main-755781cc", True),
|
97
|
-
(["^sha256-.+sig$", "^main-.+"], None, "sha256-8b5.sig", True),
|
98
|
-
(["^sha256-.+sig$", "^main-.+"], None, "1.2.3", False),
|
99
|
-
# # Tags exclude tests.
|
100
|
-
(None, ["^sha256-.+sig$", "^main-.+"], "main-755781cc", False),
|
101
|
-
(None, ["^sha256-.+sig$", "^main-.+"], "sha256-8b5.sig", False),
|
102
|
-
(None, ["^sha256-.+sig$", "^main-.+"], "1.2.3", True),
|
103
|
-
# When both includes and excludes are explicitly given, exclude take precedence.
|
104
|
-
(["^sha256-.+sig$", "^main-.+"], ["main-755781cc"], "main-755781cc", False),
|
105
|
-
(["^sha256-.+sig$", "^main-.+"], ["main-755781cc"], "sha256-8b5.sig", True),
|
106
|
-
# both include and exclude are not set
|
107
|
-
(None, None, "main-755781cc", True),
|
108
|
-
],
|
109
|
-
)
|
110
|
-
def test_sync_tag(tags, tags_exclude, candidate, result):
|
111
|
-
assert QuayMirror.sync_tag(tags, tags_exclude, candidate) == result
|
112
|
-
|
113
|
-
|
114
|
-
def test_process_repos_query_ok(mocker):
|
115
|
-
repos_query = mocker.patch.object(queries, "get_quay_repos")
|
116
|
-
repos_query.return_value = fxt.get_anymarkup("get_quay_repos_ok.yaml")
|
117
|
-
|
118
|
-
cloudservices = OrgKey(instance="quay.io", org_name="cloudservices")
|
119
|
-
app_sre = OrgKey(instance="quay.io", org_name="app-sre")
|
120
|
-
session = create_autospec(requests.Session)
|
121
|
-
timeout = 30
|
122
|
-
|
123
|
-
summary = QuayMirror.process_repos_query()
|
124
|
-
assert len(summary) == 2
|
125
|
-
assert len(summary[cloudservices]) == 2
|
126
|
-
assert len(summary[app_sre]) == 1
|
127
|
-
|
128
|
-
mosquitto = "docker.io/library/eclipse-mosquitto"
|
129
|
-
redis = "docker.io/redis"
|
130
|
-
|
131
|
-
summary = QuayMirror.process_repos_query(
|
132
|
-
repository_urls=[mosquitto, redis],
|
133
|
-
session=session,
|
134
|
-
timeout=timeout,
|
135
|
-
)
|
136
|
-
cl = summary[cloudservices]
|
137
|
-
ap = summary[app_sre]
|
138
|
-
assert len(cl) == 2
|
139
|
-
assert len(ap) == 0
|
140
|
-
assert cl[0]["mirror"]["url"] == mosquitto
|
141
|
-
assert cl[1]["mirror"]["url"] == redis
|
142
|
-
|
143
|
-
summary = QuayMirror.process_repos_query(
|
144
|
-
exclude_repository_urls=[mosquitto, redis],
|
145
|
-
session=session,
|
146
|
-
timeout=timeout,
|
147
|
-
)
|
148
|
-
cl = summary[cloudservices]
|
149
|
-
ap = summary[app_sre]
|
150
|
-
assert len(cl) == 0
|
151
|
-
assert len(summary[app_sre]) == 1
|
152
|
-
|
153
|
-
|
154
|
-
def test_process_repos_query_public_dockerhub(mocker, caplog):
|
155
|
-
repos_query = mocker.patch.object(queries, "get_quay_repos")
|
156
|
-
repos_query.return_value = fxt.get_anymarkup("get_quay_repos_public_dockerhub.yaml")
|
157
|
-
|
158
|
-
with pytest.raises(SystemExit):
|
159
|
-
QuayMirror.process_repos_query()
|
160
|
-
|
161
|
-
assert "can't be mirrored to a public quay repository" in caplog.text
|
162
|
-
|
163
|
-
|
164
|
-
def test_quay_mirror_session(mocker):
|
165
|
-
mocker.patch("reconcile.quay_mirror.gql")
|
166
|
-
mocker.patch("reconcile.quay_mirror.queries")
|
167
|
-
mocked_request = mocker.patch("reconcile.quay_mirror.requests")
|
168
|
-
|
169
|
-
with QuayMirror() as quay_mirror:
|
170
|
-
assert quay_mirror.session == mocked_request.Session.return_value
|
171
|
-
|
172
|
-
mocked_request.Session.return_value.close.assert_called_once_with()
|
@@ -1,82 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import tempfile
|
3
|
-
from unittest.mock import patch
|
4
|
-
|
5
|
-
import pytest
|
6
|
-
|
7
|
-
from reconcile.quay_mirror_org import (
|
8
|
-
CONTROL_FILE_NAME,
|
9
|
-
QuayMirrorOrg,
|
10
|
-
)
|
11
|
-
|
12
|
-
NOW = 1662124612.995397
|
13
|
-
|
14
|
-
|
15
|
-
@patch("reconcile.utils.gql.get_api", autospec=True)
|
16
|
-
@patch("reconcile.queries.get_app_interface_settings", return_value={})
|
17
|
-
class TestControlFile:
|
18
|
-
def test_control_file_dir_does_not_exist(self, mock_gql, mock_settings):
|
19
|
-
with pytest.raises(FileNotFoundError):
|
20
|
-
QuayMirrorOrg(control_file_dir="/no-such-dir")
|
21
|
-
|
22
|
-
def test_control_file_path_from_given_dir(self, mock_gql, mock_settings):
|
23
|
-
with tempfile.TemporaryDirectory() as tmp_dir_name:
|
24
|
-
qm = QuayMirrorOrg(control_file_dir=tmp_dir_name)
|
25
|
-
assert qm.control_file_path == os.path.join(tmp_dir_name, CONTROL_FILE_NAME)
|
26
|
-
|
27
|
-
|
28
|
-
@patch("reconcile.utils.gql.get_api", autospec=True)
|
29
|
-
@patch("reconcile.queries.get_app_interface_settings", return_value={})
|
30
|
-
@patch("time.time", return_value=NOW)
|
31
|
-
class TestIsCompareTags:
|
32
|
-
def setup_method(self):
|
33
|
-
self.tmp_dir = (
|
34
|
-
tempfile.TemporaryDirectory() # pylint: disable=consider-using-with
|
35
|
-
)
|
36
|
-
with open(
|
37
|
-
os.path.join(self.tmp_dir.name, CONTROL_FILE_NAME), "w", encoding="locale"
|
38
|
-
) as fh:
|
39
|
-
fh.write(str(NOW - 100.0))
|
40
|
-
|
41
|
-
def teardown_method(self):
|
42
|
-
self.tmp_dir.cleanup()
|
43
|
-
|
44
|
-
# Last run was in NOW - 100s, we run compare tags every 10s.
|
45
|
-
def test_is_compare_tags_elapsed(self, mock_gql, mock_settings, mock_time):
|
46
|
-
qm = QuayMirrorOrg(control_file_dir=self.tmp_dir.name, compare_tags_interval=10)
|
47
|
-
assert qm.is_compare_tags
|
48
|
-
|
49
|
-
# Same as before, but now we force no compare with the option.
|
50
|
-
def test_is_compare_tags_force_no_compare(self, mock_gql, mock_settings, mock_time):
|
51
|
-
qm = QuayMirrorOrg(
|
52
|
-
control_file_dir=self.tmp_dir.name,
|
53
|
-
compare_tags_interval=10,
|
54
|
-
compare_tags=False,
|
55
|
-
)
|
56
|
-
assert not qm.is_compare_tags
|
57
|
-
|
58
|
-
# Last run was in NOW - 100s, we run compare tags every 1000s.
|
59
|
-
def test_is_compare_tags_not_elapsed(self, mock_gql, mock_settings, mock_time):
|
60
|
-
qm = QuayMirrorOrg(
|
61
|
-
control_file_dir=self.tmp_dir.name, compare_tags_interval=1000
|
62
|
-
)
|
63
|
-
assert not qm.is_compare_tags
|
64
|
-
|
65
|
-
# Same as before, but now we force compare with the option.
|
66
|
-
def test_is_compare_tags_force_compare(self, mock_gql, mock_settings, mock_time):
|
67
|
-
qm = QuayMirrorOrg(
|
68
|
-
control_file_dir=self.tmp_dir.name,
|
69
|
-
compare_tags_interval=1000,
|
70
|
-
compare_tags=True,
|
71
|
-
)
|
72
|
-
assert qm.is_compare_tags
|
73
|
-
|
74
|
-
|
75
|
-
def test_quay_mirror_org_session(mocker):
|
76
|
-
mocker.patch("reconcile.quay_mirror_org.get_quay_api_store")
|
77
|
-
mocked_request = mocker.patch("reconcile.quay_mirror_org.requests")
|
78
|
-
|
79
|
-
with QuayMirrorOrg() as quay_mirror_org:
|
80
|
-
assert quay_mirror_org.session == mocked_request.Session.return_value
|
81
|
-
|
82
|
-
mocked_request.Session.return_value.close.assert_called_once_with()
|
@@ -1,59 +0,0 @@
|
|
1
|
-
from unittest.mock import patch
|
2
|
-
|
3
|
-
from reconcile.quay_base import OrgKey
|
4
|
-
from reconcile.quay_repos import (
|
5
|
-
RepoInfo,
|
6
|
-
act,
|
7
|
-
)
|
8
|
-
|
9
|
-
from .fixtures import Fixtures
|
10
|
-
|
11
|
-
fxt = Fixtures("quay_repos")
|
12
|
-
|
13
|
-
|
14
|
-
def build_state(fixture_state):
|
15
|
-
return [
|
16
|
-
RepoInfo(
|
17
|
-
org_key=OrgKey("instance", "org"),
|
18
|
-
name=item[0],
|
19
|
-
public=item[1],
|
20
|
-
description=item[2],
|
21
|
-
)
|
22
|
-
for item in fixture_state
|
23
|
-
]
|
24
|
-
|
25
|
-
|
26
|
-
def get_test_repo_from_state(state, name):
|
27
|
-
for item in state:
|
28
|
-
if item.name == name:
|
29
|
-
return item
|
30
|
-
return None
|
31
|
-
|
32
|
-
|
33
|
-
class TestQuayRepos:
|
34
|
-
@staticmethod
|
35
|
-
@patch("reconcile.quay_repos.act_public")
|
36
|
-
@patch("reconcile.quay_repos.act_description")
|
37
|
-
@patch("reconcile.quay_repos.act_delete")
|
38
|
-
@patch("reconcile.quay_repos.act_create")
|
39
|
-
def test_act(act_create, act_delete, act_description, act_public):
|
40
|
-
fixture = fxt.get_anymarkup("state.yml")
|
41
|
-
|
42
|
-
current_state = build_state(fixture["current_state"])
|
43
|
-
desired_state = build_state(fixture["desired_state"])
|
44
|
-
|
45
|
-
quay_api_store = {}
|
46
|
-
dry_run = True
|
47
|
-
act(dry_run, quay_api_store, current_state, desired_state)
|
48
|
-
|
49
|
-
repo_delete = get_test_repo_from_state(current_state, "repo_delete")
|
50
|
-
act_delete.assert_called_once_with(dry_run, quay_api_store, repo_delete)
|
51
|
-
|
52
|
-
repo_create = get_test_repo_from_state(desired_state, "repo_create")
|
53
|
-
act_create.assert_called_once_with(dry_run, quay_api_store, repo_create)
|
54
|
-
|
55
|
-
repo_desc = get_test_repo_from_state(desired_state, "repo_desc")
|
56
|
-
act_description.assert_called_once_with(dry_run, quay_api_store, repo_desc)
|
57
|
-
|
58
|
-
repo_public = get_test_repo_from_state(desired_state, "repo_public")
|
59
|
-
act_public.assert_called_once_with(dry_run, quay_api_store, repo_public)
|
reconcile/test/test_queries.py
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
from copy import deepcopy
|
2
|
-
from typing import Any
|
3
|
-
from unittest.mock import (
|
4
|
-
create_autospec,
|
5
|
-
patch,
|
6
|
-
)
|
7
|
-
|
8
|
-
from reconcile import queries
|
9
|
-
from reconcile.utils import gql
|
10
|
-
|
11
|
-
from .fixtures import Fixtures
|
12
|
-
|
13
|
-
|
14
|
-
class TestQueries:
|
15
|
-
def setup_method(self) -> None:
|
16
|
-
"""This starts a patch on gql.query method which will answer with the
|
17
|
-
contents of self.fixture_data."""
|
18
|
-
|
19
|
-
# Resetting this to make sure it is set from every test
|
20
|
-
self.fixture_data: dict[str, Any] = {}
|
21
|
-
|
22
|
-
self.gql_patcher = patch.object(gql, "get_api", autospec=True)
|
23
|
-
self.gql = self.gql_patcher.start()
|
24
|
-
gqlapi_mock = create_autospec(gql.GqlApi)
|
25
|
-
self.gql.return_value = gqlapi_mock
|
26
|
-
gqlapi_mock.query.side_effect = self.mock_gql_query
|
27
|
-
|
28
|
-
def teardown_method(self) -> None:
|
29
|
-
"""Cleanup patches created in self.setup_method"""
|
30
|
-
self.gql_patcher.stop()
|
31
|
-
|
32
|
-
def mock_gql_query(self, query: str) -> dict[str, Any]:
|
33
|
-
return self.fixture_data
|
34
|
-
|
35
|
-
def test_get_pipelines_providers_all_defaults(self) -> None:
|
36
|
-
data = Fixtures("queries").get_json("pipelines_providers_all_defaults.json")
|
37
|
-
self.fixture_data = deepcopy(data)
|
38
|
-
pps = queries.get_pipelines_providers()
|
39
|
-
|
40
|
-
for k in ["retention", "taskTemplates", "pipelineTemplates", "deployResources"]:
|
41
|
-
assert data["pipelines_providers"][0]["defaults"][k] == pps[0][k]
|
42
|
-
|
43
|
-
def test_get_pipelines_providers_mixed(self) -> None:
|
44
|
-
data = Fixtures("queries").get_json("pipelines_providers_mixed.json")
|
45
|
-
self.fixture_data = deepcopy(data)
|
46
|
-
pps = queries.get_pipelines_providers()
|
47
|
-
|
48
|
-
# the fixture has some keys overriden from the defaults
|
49
|
-
for k in ["taskTemplates", "pipelineTemplates"]:
|
50
|
-
assert data["pipelines_providers"][0]["defaults"][k] == pps[0][k]
|
51
|
-
|
52
|
-
for k in ["retention", "deployResources"]:
|
53
|
-
assert data["pipelines_providers"][0][k] == pps[0][k]
|
@@ -1,47 +0,0 @@
|
|
1
|
-
from reconcile.utils import repo_owners
|
2
|
-
|
3
|
-
|
4
|
-
def test_repo_owners_subpath() -> None:
|
5
|
-
def _mock_get_owners_map():
|
6
|
-
return {
|
7
|
-
"/foo": {
|
8
|
-
"approvers": ["foo_approver"],
|
9
|
-
"reviewers": ["foo_reviewer"],
|
10
|
-
},
|
11
|
-
"/foobar": {
|
12
|
-
"approvers": ["foobar_approver"],
|
13
|
-
"reviewers": ["foobar_reviewer"],
|
14
|
-
},
|
15
|
-
"/bar": {
|
16
|
-
"approvers": ["bar_approver"],
|
17
|
-
"reviewers": ["bar_reviewer"],
|
18
|
-
},
|
19
|
-
}
|
20
|
-
|
21
|
-
owners = repo_owners.RepoOwners(None)
|
22
|
-
owners._get_owners_map = _mock_get_owners_map # type: ignore
|
23
|
-
assert owners.get_path_owners("/foobar/baz") == {
|
24
|
-
"approvers": ["foobar_approver"],
|
25
|
-
"reviewers": ["foobar_reviewer"],
|
26
|
-
}
|
27
|
-
|
28
|
-
|
29
|
-
def test_repo_owners_subpath_closest() -> None:
|
30
|
-
def _mock_get_owners_map():
|
31
|
-
return {
|
32
|
-
"/": {
|
33
|
-
"approvers": ["root_approver"],
|
34
|
-
"reviewers": ["root_reviewer"],
|
35
|
-
},
|
36
|
-
"/foo": {
|
37
|
-
"approvers": ["foo_approver"],
|
38
|
-
"reviewers": ["foo_reviewer"],
|
39
|
-
},
|
40
|
-
}
|
41
|
-
|
42
|
-
owners = repo_owners.RepoOwners(None)
|
43
|
-
owners._get_owners_map = _mock_get_owners_map # type: ignore
|
44
|
-
assert owners.get_path_closest_owners("/foobar/baz") == {
|
45
|
-
"approvers": ["root_approver"],
|
46
|
-
"reviewers": ["root_reviewer"],
|
47
|
-
}
|
@@ -1,139 +0,0 @@
|
|
1
|
-
import os
|
2
|
-
import sys
|
3
|
-
from subprocess import CalledProcessError
|
4
|
-
from unittest import TestCase
|
5
|
-
from unittest.mock import patch
|
6
|
-
|
7
|
-
import reconcile.requests_sender as integ
|
8
|
-
from reconcile import (
|
9
|
-
queries,
|
10
|
-
typed_queries,
|
11
|
-
)
|
12
|
-
|
13
|
-
|
14
|
-
class TestRunInteg(TestCase):
|
15
|
-
def setUp(self):
|
16
|
-
os.environ["APP_INTERFACE_STATE_BUCKET_ACCOUNT"] = "anaccount"
|
17
|
-
self.user = {
|
18
|
-
"org_username": "myorg",
|
19
|
-
"public_gpg_key": "mykey",
|
20
|
-
}
|
21
|
-
|
22
|
-
self.requests = [
|
23
|
-
{
|
24
|
-
"user": self.user,
|
25
|
-
"credentials": "credentials_name",
|
26
|
-
"name": "aname",
|
27
|
-
}
|
28
|
-
]
|
29
|
-
|
30
|
-
self.exit_patcher = patch.object(sys, "exit", autospec=True)
|
31
|
-
self.get_encrypted_creds_patcher = patch.object(
|
32
|
-
integ, "get_encrypted_credentials"
|
33
|
-
)
|
34
|
-
self.smtpclient_patcher = patch.object(integ, "SmtpClient", autospec=True)
|
35
|
-
self.get_settings_patcher = patch.object(
|
36
|
-
queries, "get_app_interface_settings", autospec=True
|
37
|
-
)
|
38
|
-
self.get_smtp_credentials_patcher = patch.object(
|
39
|
-
integ, "get_smtp_server_connection", autospec=True
|
40
|
-
)
|
41
|
-
self.smtp_settings_patcher = patch.object(
|
42
|
-
typed_queries.smtp, "settings", autospec=True
|
43
|
-
)
|
44
|
-
self.get_aws_accounts_patcher = patch.object(
|
45
|
-
queries, "get_aws_accounts", autospec=True
|
46
|
-
)
|
47
|
-
self.get_credentials_requests_patcher = patch.object(
|
48
|
-
queries, "get_credentials_requests", autospec=True
|
49
|
-
)
|
50
|
-
self.state_patcher = patch.object(integ, "init_state", autospec=True)
|
51
|
-
self.vault_settings_patcher = patch.object(
|
52
|
-
integ,
|
53
|
-
"get_app_interface_vault_settings",
|
54
|
-
autospec=True,
|
55
|
-
)
|
56
|
-
|
57
|
-
self.do_exit = self.exit_patcher.start()
|
58
|
-
self.get_encrypted_credentials = self.get_encrypted_creds_patcher.start()
|
59
|
-
self.smtpclient = self.smtpclient_patcher.start()
|
60
|
-
self.get_app_interface_settings = self.get_settings_patcher.start()
|
61
|
-
self.get_smtp_credentials = self.get_smtp_credentials_patcher.start()
|
62
|
-
self.smtp_settings = self.smtp_settings_patcher.start()
|
63
|
-
self.get_aws_accounts = self.get_aws_accounts_patcher.start()
|
64
|
-
self.get_credentials_requests = self.get_credentials_requests_patcher.start()
|
65
|
-
self.get_credentials_requests.return_value = self.requests
|
66
|
-
self.get_encrypted_credentials.return_value = "anencryptedcred"
|
67
|
-
self.state = self.state_patcher.start()
|
68
|
-
self.vault_settings_patcher.start()
|
69
|
-
self.settings = {
|
70
|
-
"smtp": {
|
71
|
-
"secret_path": "asecretpath",
|
72
|
-
"server": "aserver",
|
73
|
-
"password": "apassword",
|
74
|
-
"port": 993,
|
75
|
-
"require_tls": True,
|
76
|
-
"username": "asmtpuser",
|
77
|
-
}
|
78
|
-
}
|
79
|
-
self.get_app_interface_settings.return_value = self.settings
|
80
|
-
self.get_aws_accounts.return_value = ["anaccount"]
|
81
|
-
|
82
|
-
def tearDown(self):
|
83
|
-
for p in (
|
84
|
-
self.exit_patcher,
|
85
|
-
self.get_encrypted_creds_patcher,
|
86
|
-
self.smtpclient_patcher,
|
87
|
-
self.get_settings_patcher,
|
88
|
-
self.get_smtp_credentials_patcher,
|
89
|
-
self.smtp_settings_patcher,
|
90
|
-
self.get_credentials_requests_patcher,
|
91
|
-
self.get_aws_accounts_patcher,
|
92
|
-
self.state_patcher,
|
93
|
-
self.vault_settings_patcher,
|
94
|
-
):
|
95
|
-
p.stop()
|
96
|
-
|
97
|
-
def test_valid_credentials(self):
|
98
|
-
# Yeah, yeah, whatever
|
99
|
-
self.state.return_value.exists.return_value = False
|
100
|
-
integ.run(False)
|
101
|
-
# This has succeeded
|
102
|
-
self.do_exit.assert_not_called()
|
103
|
-
self.get_encrypted_credentials.assert_called_once_with(
|
104
|
-
"credentials_name", self.user, self.settings
|
105
|
-
)
|
106
|
-
calls = self.smtpclient.return_value.send_mail.call_args_list
|
107
|
-
self.assertEqual(len(calls), 1)
|
108
|
-
# I don't care too much about the body of the email, TBH
|
109
|
-
self.assertEqual(calls[0][0][:-1], (["myorg"], "aname"))
|
110
|
-
# Just check that we're still sending the encrypted credential
|
111
|
-
# in the body. This assertion is backwards with regards to all
|
112
|
-
# other assertions :(
|
113
|
-
self.assertIn("anencryptedcred", calls[0][0][-1])
|
114
|
-
|
115
|
-
def test_existing_credentials(self):
|
116
|
-
self.state.return_value.exists.return_value = True
|
117
|
-
integ.run(False)
|
118
|
-
self.do_exit.assert_not_called()
|
119
|
-
self.get_encrypted_credentials.assert_not_called()
|
120
|
-
self.smtpclient.return_value.send_mail.assert_not_called()
|
121
|
-
|
122
|
-
def test_invalid_credentials(self):
|
123
|
-
self.get_encrypted_credentials.side_effect = CalledProcessError(
|
124
|
-
stderr="iadaiada", returncode=1, cmd="a command"
|
125
|
-
)
|
126
|
-
self.state.return_value.exists.return_value = False
|
127
|
-
integ.run(False)
|
128
|
-
self.do_exit.assert_called_once()
|
129
|
-
self.get_encrypted_credentials.assert_called_once()
|
130
|
-
self.smtpclient.return_value.send_mail.assert_not_called()
|
131
|
-
|
132
|
-
def test_dry_run_honored(self):
|
133
|
-
self.state.return_value.exists.return_value = False
|
134
|
-
integ.run(True)
|
135
|
-
|
136
|
-
self.get_encrypted_credentials.assert_called_once()
|
137
|
-
self.do_exit.assert_not_called()
|
138
|
-
self.smtpclient.return_value.send_mail.assert_not_called()
|
139
|
-
self.state.return_value.add.assert_not_called()
|