qontract-reconcile 0.9.1rc273__py3-none-any.whl → 0.9.1rc275__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.
@@ -1,87 +1,168 @@
1
+ from collections.abc import (
2
+ Callable,
3
+ Iterable,
4
+ Mapping,
5
+ )
6
+ from typing import Optional
7
+
1
8
  import pytest
9
+ from pytest_mock import MockerFixture
2
10
 
3
11
  import reconcile.terraform_tgw_attachments as integ
4
12
 
5
13
 
6
14
  @pytest.fixture
7
- def account_tgw_connection():
8
- return {
9
- "name": "account_tgw_connection",
10
- "provider": "account-tgw",
11
- "manageRoutes": True,
12
- "account": {
13
- "name": "tgw_account",
14
- "uid": "a-uid",
15
- "terraformUsername": "tf-user",
16
- },
17
- "assumeRole": None,
18
- "cidrBlock": "172.16.0.0/16",
19
- "deleted": False,
20
- }
15
+ def peering_connection_builder() -> Callable[..., dict]:
16
+ def builder(
17
+ name: str,
18
+ provider: str,
19
+ manage_routes: bool = False,
20
+ account_name: Optional[str] = None,
21
+ account_uid: Optional[str] = None,
22
+ terraform_username: Optional[str] = None,
23
+ assume_role: Optional[str] = None,
24
+ cidr_block: Optional[str] = None,
25
+ deleted: Optional[bool] = None,
26
+ ) -> dict:
27
+ return {
28
+ "name": name,
29
+ "provider": provider,
30
+ "manageRoutes": manage_routes,
31
+ "account": {
32
+ "name": account_name,
33
+ "uid": account_uid,
34
+ "terraformUsername": terraform_username,
35
+ },
36
+ "assumeRole": assume_role,
37
+ "cidrBlock": cidr_block,
38
+ "deleted": deleted,
39
+ }
40
+
41
+ return builder
21
42
 
22
43
 
23
44
  @pytest.fixture
24
- def account_vpc_connection():
25
- return {
26
- "name": "account_vpc_connection",
27
- "provider": "account-vpc",
28
- }
45
+ def account_tgw_connection(peering_connection_builder: Callable[..., dict]) -> dict:
46
+ return peering_connection_builder(
47
+ name="account_tgw_connection",
48
+ provider="account-tgw",
49
+ manage_routes=True,
50
+ account_name="tgw_account",
51
+ account_uid="a-uid",
52
+ terraform_username="tf-user",
53
+ assume_role=None,
54
+ cidr_block="172.16.0.0/16",
55
+ deleted=False,
56
+ )
29
57
 
30
58
 
31
59
  @pytest.fixture
32
- def cluster_with_tgw_connection(account_tgw_connection):
33
- return {
34
- "name": "cluster_with_tgw_connection",
35
- "ocm": {"name": "cluster_with_tgw_connection-ocm"},
36
- "spec": {
37
- "region": "us-east-1",
38
- },
39
- "network": {"vpc": "10.0.0.0/16"},
40
- "peering": {
60
+ def account_vpc_connection(peering_connection_builder: Callable[..., dict]) -> dict:
61
+ return peering_connection_builder(
62
+ name="account_vpc_connection",
63
+ provider="account-vpc",
64
+ )
65
+
66
+
67
+ @pytest.fixture
68
+ def cluster_builder() -> Callable[..., dict]:
69
+ def builder(
70
+ name: str,
71
+ ocm: dict,
72
+ region: str,
73
+ vpc_cidr: str,
74
+ peering: dict,
75
+ ) -> dict:
76
+ return {
77
+ "name": name,
78
+ "ocm": ocm,
79
+ "spec": {
80
+ "region": region,
81
+ },
82
+ "network": {"vpc": vpc_cidr},
83
+ "peering": peering,
84
+ }
85
+
86
+ return builder
87
+
88
+
89
+ @pytest.fixture
90
+ def cluster_with_tgw_connection(
91
+ cluster_builder: Callable[..., dict],
92
+ account_tgw_connection: dict,
93
+ ) -> dict:
94
+ return cluster_builder(
95
+ name="cluster_with_tgw_connection",
96
+ ocm={"name": "cluster_with_tgw_connection-ocm"},
97
+ region="us-east-1",
98
+ vpc_cidr="10.0.0.0/16",
99
+ peering={
41
100
  "connections": [
42
101
  account_tgw_connection,
43
102
  ]
44
103
  },
45
- }
104
+ )
46
105
 
47
106
 
48
107
  @pytest.fixture
49
- def cluster_with_vpc_connection(account_vpc_connection):
50
- return {
51
- "name": "cluster_with_vpc_connection",
52
- "ocm": {"name": "cluster_with_vpc_connection-ocm"},
53
- "spec": {
54
- "region": "us-east-1",
108
+ def cluster_with_duplicate_tgw_connections(
109
+ cluster_builder: Callable[..., dict],
110
+ account_tgw_connection: dict,
111
+ ) -> dict:
112
+ return cluster_builder(
113
+ name="cluster_with_duplicate_tgw_connections",
114
+ ocm={"name": "cluster_with_duplicate_tgw_connections-ocm"},
115
+ region="us-east-1",
116
+ vpc_cidr="10.0.0.0/16",
117
+ peering={
118
+ "connections": [
119
+ account_tgw_connection,
120
+ account_tgw_connection,
121
+ ]
55
122
  },
56
- "network": {"vpc": "10.0.0.1/16"},
57
- "peering": {
123
+ )
124
+
125
+
126
+ @pytest.fixture
127
+ def cluster_with_vpc_connection(
128
+ cluster_builder: Callable[..., dict],
129
+ account_vpc_connection: Mapping,
130
+ ) -> dict:
131
+ return cluster_builder(
132
+ name="cluster_with_vpc_connection",
133
+ ocm={"name": "cluster_with_vpc_connection-ocm"},
134
+ region="us-east-1",
135
+ vpc_cidr="10.0.0.1/16",
136
+ peering={
58
137
  "connections": [
59
138
  account_vpc_connection,
60
139
  ]
61
140
  },
62
- }
141
+ )
63
142
 
64
143
 
65
144
  @pytest.fixture
66
- def cluster_with_mixed_connections(account_tgw_connection, account_vpc_connection):
67
- return {
68
- "name": "cluster_with_mixed_connections",
69
- "ocm": {"name": "cluster_with_mixed_connections-ocm"},
70
- "spec": {
71
- "region": "us-east-1",
72
- },
73
- "network": {"vpc": "10.0.0.2/16"},
74
- "peering": {
145
+ def cluster_with_mixed_connections(
146
+ cluster_builder: Callable[..., dict],
147
+ account_tgw_connection: Mapping,
148
+ account_vpc_connection: Mapping,
149
+ ) -> dict:
150
+ return cluster_builder(
151
+ name="cluster_with_mixed_connections",
152
+ ocm={"name": "cluster_with_mixed_connections-ocm"},
153
+ region="us-east-1",
154
+ vpc_cidr="10.0.0.2/16",
155
+ peering={
75
156
  "connections": [
76
157
  account_tgw_connection,
77
158
  account_vpc_connection,
78
159
  ]
79
160
  },
80
- }
161
+ )
81
162
 
82
163
 
83
164
  @pytest.fixture
84
- def tgw():
165
+ def tgw() -> dict:
85
166
  return {
86
167
  "tgw_id": "tgw-1",
87
168
  "tgw_arn": "tgw-arn-1",
@@ -93,7 +174,7 @@ def tgw():
93
174
 
94
175
 
95
176
  @pytest.fixture
96
- def vpc_details():
177
+ def vpc_details() -> dict:
97
178
  return {
98
179
  "vpc_id": "vpc-id-1",
99
180
  "route_table_ids": ["rtb-1"],
@@ -102,24 +183,36 @@ def vpc_details():
102
183
 
103
184
 
104
185
  @pytest.fixture
105
- def assume_role():
186
+ def assume_role() -> str:
106
187
  return "some-role"
107
188
 
108
189
 
109
190
  def _setup_mocks(
110
- mocker, clusters=None, accounts=None, vpc=None, tgws=None, assume_role=None
111
- ):
112
- mocker.patch("reconcile.queries.get_secret_reader_settings", return_value={})
113
- mocker.patch(
114
- "reconcile.queries.get_clusters_with_peering_settings",
115
- return_value=clusters or [],
116
- )
117
- mocker.patch("reconcile.queries.get_aws_accounts", return_value=accounts or [])
191
+ mocker: MockerFixture,
192
+ clusters: Optional[Iterable] = None,
193
+ accounts: Optional[Iterable] = None,
194
+ vpc_details: Optional[Mapping] = None,
195
+ tgws: Optional[Iterable] = None,
196
+ assume_role: Optional[str] = None,
197
+ ) -> dict:
198
+ mocked_queries = mocker.patch("reconcile.terraform_tgw_attachments.queries")
199
+ mocked_queries.get_secret_reader_settings.return_value = {}
200
+ mocked_queries.get_clusters_with_peering_settings.return_value = clusters or []
201
+ mocked_queries.get_aws_accounts.return_value = accounts or []
118
202
  mocked_aws_api = mocker.patch(
119
203
  "reconcile.terraform_tgw_attachments.AWSApi", autospec=True
120
204
  ).return_value
121
205
  with mocked_aws_api as aws_api:
122
- aws_api.get_cluster_vpc_details.return_value = vpc or (None, None, None)
206
+ vpc = (
207
+ (
208
+ vpc_details["vpc_id"],
209
+ vpc_details["route_table_ids"],
210
+ vpc_details["subnets_id_az"],
211
+ )
212
+ if vpc_details is not None
213
+ else (None, None, None)
214
+ )
215
+ aws_api.get_cluster_vpc_details.return_value = vpc
123
216
  aws_api.get_tgws_details.return_value = tgws or []
124
217
  mocked_ocm = mocker.patch(
125
218
  "reconcile.terraform_tgw_attachments.OCMMap", autospec=True
@@ -140,43 +233,50 @@ def _setup_mocks(
140
233
  return {
141
234
  "tf": mocked_tf,
142
235
  "ts": mocked_ts,
236
+ "queries": mocked_queries,
143
237
  }
144
238
 
145
239
 
146
- def test_dry_run(mocker):
240
+ def test_dry_run(mocker: MockerFixture) -> None:
147
241
  mocks = _setup_mocks(mocker)
148
242
 
149
243
  integ.run(True, enable_deletion=False)
150
244
 
245
+ mocks["queries"].get_secret_reader_settings.assert_called_once_with()
246
+ mocks["queries"].get_clusters_with_peering_settings.assert_called_once_with()
247
+ mocks["queries"].get_aws_accounts.assert_called_once_with(
248
+ terraform_state=True, ecrs=False
249
+ )
151
250
  mocks["tf"].plan.assert_called_once_with(False)
152
251
  mocks["tf"].apply.assert_not_called()
153
252
 
154
253
 
155
- def test_non_dry_run(mocker):
254
+ def test_non_dry_run(mocker: MockerFixture) -> None:
156
255
  mocks = _setup_mocks(mocker)
157
256
 
158
257
  integ.run(False, enable_deletion=False)
159
258
 
259
+ mocks["queries"].get_secret_reader_settings.assert_called_once_with()
260
+ mocks["queries"].get_clusters_with_peering_settings.assert_called_once_with()
261
+ mocks["queries"].get_aws_accounts.assert_called_once_with(
262
+ terraform_state=True, ecrs=False
263
+ )
160
264
  mocks["tf"].plan.assert_called_once_with(False)
161
265
  mocks["tf"].apply.assert_called_once()
162
266
 
163
267
 
164
268
  def test_run_when_cluster_with_tgw_connection(
165
- mocker,
166
- cluster_with_tgw_connection,
167
- account_tgw_connection,
168
- tgw,
169
- vpc_details,
170
- assume_role,
171
- ):
269
+ mocker: MockerFixture,
270
+ cluster_with_tgw_connection: Mapping,
271
+ account_tgw_connection: Mapping,
272
+ tgw: Mapping,
273
+ vpc_details: Mapping,
274
+ assume_role: str,
275
+ ) -> None:
172
276
  mocks = _setup_mocks(
173
277
  mocker,
174
278
  clusters=[cluster_with_tgw_connection],
175
- vpc=(
176
- vpc_details["vpc_id"],
177
- vpc_details["route_table_ids"],
178
- vpc_details["subnets_id_az"],
179
- ),
279
+ vpc_details=vpc_details,
180
280
  tgws=[tgw],
181
281
  assume_role=assume_role,
182
282
  )
@@ -193,7 +293,7 @@ def test_run_when_cluster_with_tgw_connection(
193
293
  }
194
294
 
195
295
  mocks["ts"].populate_additional_providers.assert_called_once_with(
196
- [expected_tgw_account, expected_tgw_account]
296
+ [expected_tgw_account]
197
297
  )
198
298
  mocks["ts"].populate_tgw_attachments.assert_called_once_with(
199
299
  [
@@ -225,21 +325,17 @@ def test_run_when_cluster_with_tgw_connection(
225
325
 
226
326
 
227
327
  def test_run_when_cluster_with_mixed_connections(
228
- mocker,
229
- cluster_with_mixed_connections,
230
- account_tgw_connection,
231
- tgw,
232
- vpc_details,
233
- assume_role,
234
- ):
328
+ mocker: MockerFixture,
329
+ cluster_with_mixed_connections: Mapping,
330
+ account_tgw_connection: Mapping,
331
+ tgw: Mapping,
332
+ vpc_details: Mapping,
333
+ assume_role: str,
334
+ ) -> None:
235
335
  mocks = _setup_mocks(
236
336
  mocker,
237
337
  clusters=[cluster_with_mixed_connections],
238
- vpc=(
239
- vpc_details["vpc_id"],
240
- vpc_details["route_table_ids"],
241
- vpc_details["subnets_id_az"],
242
- ),
338
+ vpc_details=vpc_details,
243
339
  tgws=[tgw],
244
340
  assume_role=assume_role,
245
341
  )
@@ -256,7 +352,7 @@ def test_run_when_cluster_with_mixed_connections(
256
352
  }
257
353
 
258
354
  mocks["ts"].populate_additional_providers.assert_called_once_with(
259
- [expected_tgw_account, expected_tgw_account]
355
+ [expected_tgw_account]
260
356
  )
261
357
  mocks["ts"].populate_tgw_attachments.assert_called_once_with(
262
358
  [
@@ -288,9 +384,9 @@ def test_run_when_cluster_with_mixed_connections(
288
384
 
289
385
 
290
386
  def test_run_when_cluster_with_vpc_connection_only(
291
- mocker,
292
- cluster_with_vpc_connection,
293
- ):
387
+ mocker: MockerFixture,
388
+ cluster_with_vpc_connection: Mapping,
389
+ ) -> None:
294
390
  mocks = _setup_mocks(
295
391
  mocker,
296
392
  clusters=[cluster_with_vpc_connection],
@@ -300,3 +396,114 @@ def test_run_when_cluster_with_vpc_connection_only(
300
396
 
301
397
  mocks["ts"].populate_additional_providers.assert_called_once_with([])
302
398
  mocks["ts"].populate_tgw_attachments.assert_called_once_with([])
399
+
400
+
401
+ def test_duplicate_tgw_connection_names(
402
+ mocker: MockerFixture,
403
+ cluster_with_duplicate_tgw_connections: Mapping,
404
+ tgw: Mapping,
405
+ vpc_details: Mapping,
406
+ assume_role: str,
407
+ ) -> None:
408
+ _setup_mocks(
409
+ mocker,
410
+ clusters=[cluster_with_duplicate_tgw_connections],
411
+ vpc_details=vpc_details,
412
+ tgws=[tgw],
413
+ assume_role=assume_role,
414
+ )
415
+
416
+ with pytest.raises(integ.ValidationError) as e:
417
+ integ.run(True)
418
+
419
+ assert "duplicate tgw connection names found" == str(e.value)
420
+
421
+
422
+ def test_missing_vpc_id(
423
+ mocker: MockerFixture,
424
+ cluster_with_tgw_connection: Mapping,
425
+ tgw: Mapping,
426
+ vpc_details: Mapping,
427
+ assume_role: str,
428
+ ) -> None:
429
+ _setup_mocks(
430
+ mocker,
431
+ clusters=[cluster_with_tgw_connection],
432
+ vpc_details=None,
433
+ tgws=[tgw],
434
+ assume_role=assume_role,
435
+ )
436
+
437
+ with pytest.raises(RuntimeError) as e:
438
+ integ.run(True)
439
+
440
+ assert "Could not find VPC ID for cluster" == str(e.value)
441
+
442
+
443
+ def test_error_in_tf_plan(
444
+ mocker: MockerFixture,
445
+ cluster_with_tgw_connection: Mapping,
446
+ account_tgw_connection: Mapping,
447
+ tgw: Mapping,
448
+ vpc_details: Mapping,
449
+ assume_role: str,
450
+ ) -> None:
451
+ mocks = _setup_mocks(
452
+ mocker,
453
+ clusters=[cluster_with_tgw_connection],
454
+ vpc_details=vpc_details,
455
+ tgws=[tgw],
456
+ assume_role=assume_role,
457
+ )
458
+ mocks["tf"].plan.return_value = (False, True)
459
+
460
+ with pytest.raises(RuntimeError) as e:
461
+ integ.run(True)
462
+
463
+ assert "Error running terraform plan" == str(e.value)
464
+
465
+
466
+ def test_disabled_deletions_detected_in_tf_plan(
467
+ mocker: MockerFixture,
468
+ cluster_with_tgw_connection: Mapping,
469
+ account_tgw_connection: Mapping,
470
+ tgw: Mapping,
471
+ vpc_details: Mapping,
472
+ assume_role: str,
473
+ ) -> None:
474
+ mocks = _setup_mocks(
475
+ mocker,
476
+ clusters=[cluster_with_tgw_connection],
477
+ vpc_details=vpc_details,
478
+ tgws=[tgw],
479
+ assume_role=assume_role,
480
+ )
481
+ mocks["tf"].plan.return_value = (True, False)
482
+
483
+ with pytest.raises(RuntimeError) as e:
484
+ integ.run(True)
485
+
486
+ assert "Disabled deletions detected running terraform plan" == str(e.value)
487
+
488
+
489
+ def test_error_in_terraform_apply(
490
+ mocker: MockerFixture,
491
+ cluster_with_tgw_connection: Mapping,
492
+ account_tgw_connection: Mapping,
493
+ tgw: Mapping,
494
+ vpc_details: Mapping,
495
+ assume_role: str,
496
+ ) -> None:
497
+ mocks = _setup_mocks(
498
+ mocker,
499
+ clusters=[cluster_with_tgw_connection],
500
+ vpc_details=vpc_details,
501
+ tgws=[tgw],
502
+ assume_role=assume_role,
503
+ )
504
+ mocks["tf"].apply.return_value = True
505
+
506
+ with pytest.raises(RuntimeError) as e:
507
+ integ.run(False)
508
+
509
+ assert "Error running terraform apply" == str(e.value)
@@ -328,7 +328,7 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
328
328
  integration: str,
329
329
  integration_prefix: str,
330
330
  thread_pool_size: int,
331
- accounts: list[dict[str, Any]],
331
+ accounts: Iterable[dict[str, Any]],
332
332
  settings: Optional[Mapping[str, Any]] = None,
333
333
  prefetch_resources_by_schemas: Optional[list[str]] = None,
334
334
  ) -> None:
@@ -837,7 +837,7 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
837
837
  alias = self.get_alias_name_from_assume_role(assume_role)
838
838
  ts = self.tss[account_name]
839
839
  config = self.configs[account_name]
840
- existing_provider_aliases = [p.get("alias") for p in ts["provider"]["aws"]]
840
+ existing_provider_aliases = {p.get("alias") for p in ts["provider"]["aws"]}
841
841
  if alias not in existing_provider_aliases:
842
842
  ts += provider.aws(
843
843
  access_key=config["aws_access_key_id"],