qontract-reconcile 0.10.1rc428__py3-none-any.whl → 0.10.1rc429__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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc428
3
+ Version: 0.10.1rc429
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -16,7 +16,7 @@ reconcile/dashdotdb_cso.py,sha256=FoXrWGpOwXG5jf0eklN84tjJVUAYzKat7rtq_28JMlQ,36
16
16
  reconcile/dashdotdb_dora.py,sha256=FINH-8dU3_r8EgUOMiF_fbxD9fKs6LFDxe6rufQ9XcM,17214
17
17
  reconcile/dashdotdb_dvo.py,sha256=YXqpI6fBQAql-ybGI0grj9gWMzmKiAvPE__pNju6obk,8996
18
18
  reconcile/dashdotdb_slo.py,sha256=bf1WSh5JP9obHVQsMy0OO71_VTYZgwAopElFZM6DmRo,6714
19
- reconcile/database_access_manager.py,sha256=in-tEyL7IfS0vLO88kG7Duq9hyFw4u9sdY1ARK-z_dk,21013
19
+ reconcile/database_access_manager.py,sha256=pbf3n-8f2sxitMbj9Y2g0G_U7yfHiJaNX4lverxPnX8,21758
20
20
  reconcile/dynatrace_token_provider.py,sha256=HWItJ_LavPcUJlpYz5fmfnfOpP2-0Qjkar0EAzBJ5Cw,16602
21
21
  reconcile/email_sender.py,sha256=-5L-Ag_jaEYSzYRoMr52KQBRXz1E8yx9GqLbg2X4XFU,3533
22
22
  reconcile/gabi_authorized_users.py,sha256=rCosZv8Iu9jhWG88YiwK-gftX475aJ1R-PYIJYp_svY,4342
@@ -368,7 +368,7 @@ reconcile/test/test_checkpoint.py,sha256=sbDtqTbfw5yMZ_mCltMXxkyyGueVLGUjTDtcWhP
368
368
  reconcile/test/test_cli.py,sha256=qx_iBwh4Z-YkK3sbjK1wEziPTgn060EN-baf9DNvR3k,1096
369
369
  reconcile/test/test_closedbox_endpoint_monitoring.py,sha256=isMHYwRWMFARU2nbJgbl69kD6H0eA86noCM4MPVI1fo,7151
370
370
  reconcile/test/test_dashdotdb_dora.py,sha256=XDMdnLDvup8sSqQEynxdPhXQZAafYbd2IUo9flSRJ8I,7917
371
- reconcile/test/test_database_access_manager.py,sha256=JLoiR_OjRqgi6LdzqDUij_MaXAjGcY9RTFKDLkTJPCU,11987
371
+ reconcile/test/test_database_access_manager.py,sha256=gevaMDgOrpnrzYy7JrcJsYf1ZnyvBA2RB5G96JxJYn4,13278
372
372
  reconcile/test/test_gabi_authorized_users.py,sha256=6XnV5Q9inxP81ktGMVKyWucjBTUj8Imy2L0HG3YHyUE,2496
373
373
  reconcile/test/test_github_org.py,sha256=j3KeB4OnSln1gm2hidce49xdMru-j75NS3cM-AEgzZc,4511
374
374
  reconcile/test/test_github_repo_invites.py,sha256=QJ0VFk5B59rx4XtHoT6XOGWw9xRIZMen_cgtviN_Vi8,3419
@@ -637,8 +637,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=dmEcNwZltP1rd_4DbxIYakO
637
637
  tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
638
638
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
639
639
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
640
- qontract_reconcile-0.10.1rc428.dist-info/METADATA,sha256=TSsRbwFJFumatlbN5nmXMkU7rVyqg5OFbmlTYav_qwc,2347
641
- qontract_reconcile-0.10.1rc428.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
642
- qontract_reconcile-0.10.1rc428.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
643
- qontract_reconcile-0.10.1rc428.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
644
- qontract_reconcile-0.10.1rc428.dist-info/RECORD,,
640
+ qontract_reconcile-0.10.1rc429.dist-info/METADATA,sha256=IlUDMzAOQmNRBNLh7JrCttTiTIiQiuy-cCb8gLp4gNk,2347
641
+ qontract_reconcile-0.10.1rc429.dist-info/WHEEL,sha256=Xo9-1PvkuimrydujYJAjF7pCkriuXBpUPEjma1nZyJ0,92
642
+ qontract_reconcile-0.10.1rc429.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
643
+ qontract_reconcile-0.10.1rc429.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
644
+ qontract_reconcile-0.10.1rc429.dist-info/RECORD,,
@@ -9,6 +9,7 @@ from typing import (
9
9
  Any,
10
10
  Callable,
11
11
  Optional,
12
+ TypedDict,
12
13
  )
13
14
 
14
15
  from pydantic import BaseModel
@@ -67,6 +68,7 @@ class DatabaseConnectionParameters(BaseModel):
67
68
  class PSQLScriptGenerator(BaseModel):
68
69
  db_access: DatabaseAccessV1
69
70
  connection_parameter: DatabaseConnectionParameters
71
+ admin_connection_parameter: DatabaseConnectionParameters
70
72
  engine: str
71
73
 
72
74
  def _get_db(self) -> str:
@@ -92,7 +94,7 @@ select 'CREATE ROLE "{self._get_user()}" WITH LOGIN PASSWORD ''{self.connection
92
94
  WHERE NOT EXISTS (SELECT FROM pg_catalog.pg_roles WHERE rolname = '{self._get_db()}');\\gexec
93
95
 
94
96
  -- rds specific, grant role to admin or create schema fails
95
- grant "{self._get_user()}" to postgres;
97
+ GRANT "{self._get_user()}" to "{self.admin_connection_parameter.user}";
96
98
  CREATE SCHEMA IF NOT EXISTS "{self._get_user()}" AUTHORIZATION "{self._get_user()}";"""
97
99
 
98
100
  def _generate_db_access(self) -> str:
@@ -357,7 +359,8 @@ def _populate_resources(
357
359
  admin_secret_name: str,
358
360
  resource_prefix: str,
359
361
  settings: dict[Any, Any],
360
- database_connection: DatabaseConnectionParameters,
362
+ user_connection: DatabaseConnectionParameters,
363
+ admin_connection: DatabaseConnectionParameters,
361
364
  ) -> list[DBAMResource]:
362
365
  managed_resources: list[DBAMResource] = []
363
366
  # create service account
@@ -371,7 +374,8 @@ def _populate_resources(
371
374
  # create script secret
372
375
  generator = PSQLScriptGenerator(
373
376
  db_access=db_access,
374
- connection_parameter=database_connection,
377
+ connection_parameter=user_connection,
378
+ admin_connection_parameter=admin_connection,
375
379
  engine=engine,
376
380
  )
377
381
  script_secret_name = f"{resource_prefix}-script"
@@ -387,7 +391,7 @@ def _populate_resources(
387
391
  # create user secret
388
392
  managed_resources.append(
389
393
  DBAMResource(
390
- resource=generate_user_secret_spec(resource_prefix, database_connection),
394
+ resource=generate_user_secret_spec(resource_prefix, user_connection),
391
395
  clean_up=False,
392
396
  )
393
397
  )
@@ -433,13 +437,18 @@ def _generate_password() -> str:
433
437
  return "".join(choices(ascii_letters + digits, k=32))
434
438
 
435
439
 
440
+ class _DBDonnections(TypedDict):
441
+ user: DatabaseConnectionParameters
442
+ admin: DatabaseConnectionParameters
443
+
444
+
436
445
  def _create_database_connection_parameter(
437
446
  db_access: DatabaseAccessV1,
438
447
  namespace_name: str,
439
448
  oc: OCClient,
440
449
  admin_secret_name: str,
441
450
  user_secret_name: str,
442
- ) -> DatabaseConnectionParameters:
451
+ ) -> _DBDonnections:
443
452
  def _decode_secret_value(value: str) -> str:
444
453
  return base64.b64decode(value).decode("utf-8")
445
454
 
@@ -449,6 +458,13 @@ def _create_database_connection_parameter(
449
458
  user_secret_name,
450
459
  allow_not_found=True,
451
460
  )
461
+ admin_secret = oc.get(
462
+ namespace_name,
463
+ "Secret",
464
+ admin_secret_name,
465
+ allow_not_found=False,
466
+ )
467
+
452
468
  if user_secret:
453
469
  password = _decode_secret_value(user_secret["data"]["db.password"])
454
470
  host = _decode_secret_value(user_secret["data"]["db.host"])
@@ -456,25 +472,27 @@ def _create_database_connection_parameter(
456
472
  port = _decode_secret_value(user_secret["data"]["db.port"])
457
473
  database = _decode_secret_value(user_secret["data"]["db.name"])
458
474
  else:
459
- admin_secret = oc.get(
460
- namespace_name,
461
- "Secret",
462
- admin_secret_name,
463
- allow_not_found=False,
464
- )
465
475
  host = _decode_secret_value(admin_secret["data"]["db.host"])
466
476
  port = _decode_secret_value(admin_secret["data"]["db.port"])
467
477
  user = db_access.username
468
478
  password = _generate_password()
469
479
  database = db_access.database
470
- database_connection = DatabaseConnectionParameters(
471
- host=host,
472
- port=port,
473
- user=user,
474
- password=password,
475
- database=database,
480
+ return _DBDonnections(
481
+ user=DatabaseConnectionParameters(
482
+ host=host,
483
+ port=port,
484
+ user=user,
485
+ password=password,
486
+ database=database,
487
+ ),
488
+ admin=DatabaseConnectionParameters(
489
+ host=_decode_secret_value(admin_secret["data"]["db.host"]),
490
+ port=_decode_secret_value(admin_secret["data"]["db.port"]),
491
+ user=_decode_secret_value(admin_secret["data"]["db.user"]),
492
+ password=_decode_secret_value(admin_secret["data"]["db.password"]),
493
+ database=_decode_secret_value(admin_secret["data"]["db.name"]),
494
+ ),
476
495
  )
477
- return database_connection
478
496
 
479
497
 
480
498
  class JobFailedError(Exception):
@@ -506,7 +524,7 @@ def _process_db_access(
506
524
  ) as oc_map:
507
525
  oc = oc_map.get_cluster(cluster_name, False)
508
526
 
509
- database_connection = _create_database_connection_parameter(
527
+ connections = _create_database_connection_parameter(
510
528
  db_access,
511
529
  namespace_name,
512
530
  oc,
@@ -526,7 +544,8 @@ def _process_db_access(
526
544
  admin_secret_name,
527
545
  resource_prefix,
528
546
  settings,
529
- database_connection,
547
+ connections["user"],
548
+ connections["admin"],
530
549
  )
531
550
 
532
551
  # create job, delete old, failed job first
@@ -16,6 +16,7 @@ from reconcile.database_access_manager import (
16
16
  JobStatusCondition,
17
17
  PSQLScriptGenerator,
18
18
  _create_database_connection_parameter,
19
+ _DBDonnections,
19
20
  _generate_password,
20
21
  _populate_resources,
21
22
  _process_db_access,
@@ -87,6 +88,17 @@ def db_connection_parameter():
87
88
  )
88
89
 
89
90
 
91
+ @pytest.fixture
92
+ def db_admin_connection_parameter():
93
+ return DatabaseConnectionParameters(
94
+ host="localhost",
95
+ port="5432",
96
+ user="admin",
97
+ password="adminpw", # notsecret
98
+ database="test",
99
+ )
100
+
101
+
90
102
  @pytest.fixture
91
103
  def db_secret_dict() -> dict[str, dict[str, str]]:
92
104
  return {
@@ -118,6 +130,7 @@ def _assert_create_script(script: str) -> None:
118
130
  assert "REVOKE ALL ON DATABASE" in script
119
131
  assert 'CREATE ROLE "test" WITH LOGIN PASSWORD' in script
120
132
  assert "CREATE SCHEMA IF NOT EXISTS" in script
133
+ assert 'GRANT "test" to "admin";' in script
121
134
 
122
135
 
123
136
  def _assert_grant_access(script: str) -> None:
@@ -125,11 +138,14 @@ def _assert_grant_access(script: str) -> None:
125
138
 
126
139
 
127
140
  def test_generate_create_user(
128
- db_access: DatabaseAccessV1, db_connection_parameter: DatabaseConnectionParameters
141
+ db_access: DatabaseAccessV1,
142
+ db_connection_parameter: DatabaseConnectionParameters,
143
+ db_admin_connection_parameter: DatabaseConnectionParameters,
129
144
  ) -> None:
130
145
  s = PSQLScriptGenerator(
131
146
  db_access=db_access,
132
147
  connection_parameter=db_connection_parameter,
148
+ admin_connection_parameter=db_admin_connection_parameter,
133
149
  engine="postgres",
134
150
  )
135
151
  script = s._generate_create_user()
@@ -140,12 +156,14 @@ def test_generate_access(
140
156
  db_access: DatabaseAccessV1,
141
157
  db_access_access: DatabaseAccessAccessV1,
142
158
  db_connection_parameter: DatabaseConnectionParameters,
159
+ db_admin_connection_parameter: DatabaseConnectionParameters,
143
160
  ):
144
161
  db_access.access = [db_access_access]
145
162
 
146
163
  s = PSQLScriptGenerator(
147
164
  db_access=db_access,
148
165
  connection_parameter=db_connection_parameter,
166
+ admin_connection_parameter=db_connection_parameter,
149
167
  engine="postgres",
150
168
  )
151
169
  script = s._generate_db_access()
@@ -155,10 +173,12 @@ def test_generate_access(
155
173
  def test_generate_complete(
156
174
  db_access_complete: DatabaseAccessV1,
157
175
  db_connection_parameter: DatabaseConnectionParameters,
176
+ db_admin_connection_parameter: DatabaseConnectionParameters,
158
177
  ):
159
178
  s = PSQLScriptGenerator(
160
179
  db_access=db_access_complete,
161
180
  connection_parameter=db_connection_parameter,
181
+ admin_connection_parameter=db_admin_connection_parameter,
162
182
  engine="postgres",
163
183
  )
164
184
  script = s.generate_script()
@@ -185,6 +205,7 @@ def test_populate_resources(
185
205
  mocker: MockerFixture,
186
206
  db_access: DatabaseAccessV1,
187
207
  db_connection_parameter: DatabaseConnectionParameters,
208
+ db_admin_connection_parameter: DatabaseConnectionParameters,
188
209
  openshift_resource_secet: OpenshiftResource,
189
210
  ):
190
211
  mocker.patch(
@@ -205,7 +226,8 @@ def test_populate_resources(
205
226
  admin_secret_name="db-secret",
206
227
  resource_prefix="dbam-foo",
207
228
  settings={"foo": "bar"},
208
- database_connection=db_connection_parameter,
229
+ user_connection=db_connection_parameter,
230
+ admin_connection=db_admin_connection_parameter,
209
231
  )
210
232
 
211
233
  r_kinds = [r.resource.kind for r in reources]
@@ -226,7 +248,7 @@ def test__create_database_connection_parameter_user_exists(
226
248
  admin_secret_name="db-secret",
227
249
  user_secret_name="db-user-secret",
228
250
  )
229
- assert p == DatabaseConnectionParameters(
251
+ conn = DatabaseConnectionParameters(
230
252
  host="localhost",
231
253
  port="5432",
232
254
  user="test",
@@ -234,6 +256,10 @@ def test__create_database_connection_parameter_user_exists(
234
256
  database="test",
235
257
  )
236
258
 
259
+ assert p["user"] == conn
260
+ assert p["admin"] == conn
261
+ assert oc.get.call_count == 2
262
+
237
263
 
238
264
  def test__create_database_connection_parameter_user_missing(
239
265
  db_access: DatabaseAccessV1,
@@ -254,7 +280,7 @@ def test__create_database_connection_parameter_user_missing(
254
280
  admin_secret_name="db-secret",
255
281
  user_secret_name="db-user-secret",
256
282
  )
257
- assert p == DatabaseConnectionParameters(
283
+ conn = DatabaseConnectionParameters(
258
284
  host="localhost",
259
285
  port="5432",
260
286
  user="test",
@@ -262,6 +288,13 @@ def test__create_database_connection_parameter_user_missing(
262
288
  database="test",
263
289
  )
264
290
 
291
+ admin_conn = conn.copy()
292
+ admin_conn.password = "hduhsdfuhsdf"
293
+
294
+ assert p["user"] == conn
295
+ assert p["admin"] == admin_conn
296
+ assert oc.get.call_count == 2
297
+
265
298
 
266
299
  def test_generate_password():
267
300
  assert len(_generate_password()) == 32
@@ -275,12 +308,18 @@ def dbam_state(mocker: MockerFixture) -> MockerFixture:
275
308
 
276
309
  @pytest.fixture
277
310
  def dbam_process_mocks(
278
- openshift_resource_secet: OpenshiftResource, mocker: MockerFixture
311
+ openshift_resource_secet: OpenshiftResource,
312
+ mocker: MockerFixture,
313
+ db_connection_parameter: DatabaseConnectionParameters,
314
+ db_admin_connection_parameter: DatabaseConnectionParameters,
279
315
  ) -> DBAMResource:
280
316
  expected_resource = DBAMResource(resource=openshift_resource_secet, clean_up=True)
281
317
  mocker.patch(
282
318
  "reconcile.database_access_manager._create_database_connection_parameter",
283
- return_value=db_connection_parameter,
319
+ return_value=_DBDonnections(
320
+ user=db_connection_parameter,
321
+ admin=db_admin_connection_parameter,
322
+ ),
284
323
  )
285
324
  mocker.patch(
286
325
  "reconcile.database_access_manager._populate_resources",