qontract-reconcile 0.10.1rc758__py3-none-any.whl → 0.10.1rc760__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.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/RECORD +18 -15
- reconcile/saas_auto_promotions_manager/integration.py +1 -4
- reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py +28 -0
- reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py +7 -26
- reconcile/saas_auto_promotions_manager/merge_request_manager/reconciler.py +1 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +2 -1
- reconcile/saas_auto_promotions_manager/meta.py +4 -0
- reconcile/test/conftest.py +9 -17
- reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py +60 -0
- reconcile/test/test_deadmanssnitch.py +2 -2
- reconcile/test/test_unleash.py +86 -76
- reconcile/utils/deadmanssnitch_api.py +4 -2
- reconcile/utils/rest_api_base.py +1 -0
- reconcile/utils/slack_api.py +6 -1
- {qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc760
|
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
|
{qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/RECORD
RENAMED
@@ -395,17 +395,19 @@ reconcile/rhidp/sso_client/base.py,sha256=EfQ2ewcOKh5idg46UKAkY6z0m_nGQfvnQKffa2
|
|
395
395
|
reconcile/rhidp/sso_client/integration.py,sha256=kA8g7c38ZBSdrRtyfEqy_WgSreD1PbwY7ZIN-3tZRPc,2221
|
396
396
|
reconcile/rhidp/sso_client/metrics.py,sha256=Tq7tSOsqL3XdcPUdozxqzSPIodUeOV87UCTqpuuqqhw,1013
|
397
397
|
reconcile/saas_auto_promotions_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
398
|
-
reconcile/saas_auto_promotions_manager/integration.py,sha256=
|
398
|
+
reconcile/saas_auto_promotions_manager/integration.py,sha256=shWQ--FWfeh_1rHJUwOWDiZWnvzKxYJYuRUIGQv22RI,6759
|
399
|
+
reconcile/saas_auto_promotions_manager/meta.py,sha256=2b44ik-qpACNtW72QlDa2YOQqxeN8FHZfLPDmKoH3Rg,161
|
399
400
|
reconcile/saas_auto_promotions_manager/publisher.py,sha256=psrthZGgCQDUO3rwQjKSBMlwcTgfij6sxdebGuxkNv4,2739
|
400
401
|
reconcile/saas_auto_promotions_manager/s3_exporter.py,sha256=IKlVWZmiPnvl7sKeF6JgAlhXZe5CovKTxQc0SNkNSx4,2583
|
401
402
|
reconcile/saas_auto_promotions_manager/subscriber.py,sha256=cLhPlkT71J2LIice3SLmH1WpsqzV46gd0peMxrnqyRw,7452
|
402
403
|
reconcile/saas_auto_promotions_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
404
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py,sha256=jgfgKv4UTYFxtDao_JwNEGEKmu4GpeMm5vbaat0289c,1225
|
403
405
|
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request.py,sha256=BeAJWLow7b4HQyZ9zz398sQkPeIz8chpMkCts2NU27c,1282
|
404
|
-
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py,sha256=
|
406
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/merge_request_manager_v2.py,sha256=IRLHdz-XMn-rIb0jFddYkIuN9c7FC6InJZ61c9fVjvI,6193
|
405
407
|
reconcile/saas_auto_promotions_manager/merge_request_manager/metrics.py,sha256=sdHp71Wl87tFM-Z_QvqvdHhyrppFLGi4ekksCi_e_bs,977
|
406
408
|
reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py,sha256=x8Gg-YjEFWEeDPJH3Y8SrfcJbwhLuAqCz4kIhfEyaaA,7060
|
407
|
-
reconcile/saas_auto_promotions_manager/merge_request_manager/reconciler.py,sha256=
|
408
|
-
reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=
|
409
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/reconciler.py,sha256=KZVAkFJR75Qu7-feV4mzg1S8ua-pkbuu1oC7PebSpDs,7801
|
410
|
+
reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=iu0wMyyEvro5r5SBJVN3HGmVSIcxTyLN0Xxx3mhbYXE,7066
|
409
411
|
reconcile/saas_auto_promotions_manager/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
410
412
|
reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=ZNxwqp9kdUSoxb3kTdM4KrtPyd3V5O4jMfjrVT2IJfs,7605
|
411
413
|
reconcile/skupper_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -437,7 +439,7 @@ reconcile/terraform_init/integration.py,sha256=xcFKTc_or3xB3kE_I3OECNkkgbwALIwwd
|
|
437
439
|
reconcile/terraform_init/merge_request.py,sha256=3CYtgSd7Q9zjKg4wsDz437EPCRfGeZZ8fZ0Y-ChKXJY,1475
|
438
440
|
reconcile/terraform_init/merge_request_manager.py,sha256=fMcT6hbdEF3nFATJpvr8BedvQHq_MzFkgVJSloBNwOQ,3101
|
439
441
|
reconcile/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
440
|
-
reconcile/test/conftest.py,sha256=
|
442
|
+
reconcile/test/conftest.py,sha256=0pO4UxFeBALKbL9gwemyap0VkbPR8n5TtZbf5c9pSv0,4303
|
441
443
|
reconcile/test/fixtures.py,sha256=9SDWAUlSd1rCx7z3GhULHcpr-I6FyCsXxaFAZIqYQsQ,591
|
442
444
|
reconcile/test/test_acs_notifiers.py,sha256=xf3WL6q6V7KQdTVSx6YI-pa4yzOX3mkvIJomgPUc3Mw,12746
|
443
445
|
reconcile/test/test_acs_policies.py,sha256=8pwnXpAO-0OI-6oubjf_oPPlpZjVldeZfJJ9uhsNMWM,17579
|
@@ -455,7 +457,7 @@ reconcile/test/test_cli.py,sha256=qx_iBwh4Z-YkK3sbjK1wEziPTgn060EN-baf9DNvR3k,10
|
|
455
457
|
reconcile/test/test_closedbox_endpoint_monitoring.py,sha256=isMHYwRWMFARU2nbJgbl69kD6H0eA86noCM4MPVI1fo,7151
|
456
458
|
reconcile/test/test_dashdotdb_dora.py,sha256=MfHGAsX2eSQSvBVt9_1Sah3aQKNJBXA9Iu86X0NWD6c,7705
|
457
459
|
reconcile/test/test_database_access_manager.py,sha256=-9fYo8wMNhbJUTK_bd7g_fS5zYsAlqQ0rBDDYBMZvZQ,19595
|
458
|
-
reconcile/test/test_deadmanssnitch.py,sha256=
|
460
|
+
reconcile/test/test_deadmanssnitch.py,sha256=YAf8wlZoEC60Ul7UA6Y6XqwnZ1yqf07J15ABqeLpqW4,9835
|
459
461
|
reconcile/test/test_gabi_authorized_users.py,sha256=6XnV5Q9inxP81ktGMVKyWucjBTUj8Imy2L0HG3YHyUE,2496
|
460
462
|
reconcile/test/test_gcr_mirror.py,sha256=A0y8auKZzr62-mGoxSQ__JnN0-ijZUltzjwR5miBgso,490
|
461
463
|
reconcile/test/test_github_org.py,sha256=j3KeB4OnSln1gm2hidce49xdMru-j75NS3cM-AEgzZc,4511
|
@@ -513,7 +515,7 @@ reconcile/test/test_terraform_users.py,sha256=XOAfGvITCJPI1LTlISmHbA4ONMQMkxYUMT
|
|
513
515
|
reconcile/test/test_terraform_vpc_peerings.py,sha256=ubcsKh0TrUIwuI1-W3ETIgzsFvzAyeoFmEJFC-IK6JY,20538
|
514
516
|
reconcile/test/test_terraform_vpc_peerings_build_desired_state.py,sha256=DAfpb12I0PlqnuVUHK2vh4LH4d1OylT3H2GE_3TGZZI,47852
|
515
517
|
reconcile/test/test_three_way_diff_strategy.py,sha256=2fjEqE2w4pIzKq18PRcADTSe01aGwsZfMGloU8xfNaE,3346
|
516
|
-
reconcile/test/test_unleash.py,sha256=
|
518
|
+
reconcile/test/test_unleash.py,sha256=krPgOVmwTE6lb773040Ely9BPbNYOeOIY0_8BK72dgo,6690
|
517
519
|
reconcile/test/test_utils_jinja2.py,sha256=TpzQlpFnLGzNEZp5WOh0o7AuBiGEktqO4MuwiiJW2YY,3895
|
518
520
|
reconcile/test/test_vault_replication.py,sha256=wlc4jm9f8P641UvvxIFFFc5_unJysNkOVrKJscjhQr0,16867
|
519
521
|
reconcile/test/test_vault_utils.py,sha256=vbJnc89XAuE07qbTuWxHM5o9F6R9SO5aHXA38fwxT7A,1122
|
@@ -527,6 +529,7 @@ reconcile/test/saas_auto_promotions_manager/merge_request_manager/__init__.py,sh
|
|
527
529
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
528
530
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/conftest.py,sha256=7O3lbk1EmEtUofqGncfiwMYvDPXrkQNPB59zlQ_zXkM,4588
|
529
531
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/data_keys.py,sha256=Z1IV51OUuzhd-3S8W-k7ixC-fkaglCokn0eakK0Z73s,606
|
532
|
+
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_desired_state.py,sha256=x5DDZog0SA-Z8noxwalZniGtkovaPvR0V9Pqa5QaFFY,2302
|
530
533
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_merge_request_manager.py,sha256=h8lnorFPZIxTtbaaXGLoiEsBbB4Qj-Mg9BKV62ZqEBQ,2389
|
531
534
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_mr_parser.py,sha256=dcGHzxuafKSxmswSO1qF2WlKaqsmEvtERC6Lb8kDAN0,10019
|
532
535
|
reconcile/test/saas_auto_promotions_manager/merge_request_manager/merge_request_manager/test_reconciler.py,sha256=_bzfJzjFJgubu--7wyXIiusUrdbmLtFbHmkbat4SX_M,17828
|
@@ -597,7 +600,7 @@ reconcile/utils/binary.py,sha256=EsOGg82Y2QJh91SGJE0tYpBKqU0iaaagQVoYONBtQn8,235
|
|
597
600
|
reconcile/utils/config.py,sha256=aId5zrPjM_84u_T4yTRE_Psu3zo5-5_JCR6_7Wgv5UQ,990
|
598
601
|
reconcile/utils/constants.py,sha256=pOUd97bqZdsAu5RWJ8NUs9cwCY7K9y0eW9VVeJ4fZIU,138
|
599
602
|
reconcile/utils/data_structures.py,sha256=VyKfnlNJTiRvZKNpfgIrjESQ2YgmEpWuPQXT14WA1vI,311
|
600
|
-
reconcile/utils/deadmanssnitch_api.py,sha256=
|
603
|
+
reconcile/utils/deadmanssnitch_api.py,sha256=OWwkqZxjLRNNfFrfZt-zJ4H1hm4OHg5EZ6lP55APOZc,2493
|
601
604
|
reconcile/utils/defer.py,sha256=SniUsbgOEs9Pa8JkecLu0F94O63yQPByKXaElDYe0FI,377
|
602
605
|
reconcile/utils/differ.py,sha256=kJmUp9ZffFPSUEviaAw3s9c92ErwRJeHaRexGPai7wA,7643
|
603
606
|
reconcile/utils/disabled_integrations.py,sha256=avdDsFyl_LdTsrPVzlcIhWzT_V4C4MXw1ZC__aOtluE,1126
|
@@ -651,12 +654,12 @@ reconcile/utils/promtool.py,sha256=kT2rFZSBaRqW7SSHAuYzGZzQxM5Dzk8KW1NnEUYZU_s,2
|
|
651
654
|
reconcile/utils/quay_api.py,sha256=EuOegpb-7ntEjkKLFwM2Oo4Nw7SyFtmyl3sQ9aXMtrM,8152
|
652
655
|
reconcile/utils/raw_github_api.py,sha256=ZHC-SZuAyRe1zaMoOU7Krt1-zecDxENd9c_NzQYqK9g,2968
|
653
656
|
reconcile/utils/repo_owners.py,sha256=j-pUjc9PuDzq7KpjNLpnhqfU8tUG4nj2WMhFp4ick7g,6629
|
654
|
-
reconcile/utils/rest_api_base.py,sha256=
|
657
|
+
reconcile/utils/rest_api_base.py,sha256=X5o4idyRCDzwnF5xFwmjyoaHmM1tXSZnykTA54Z7D2Q,4006
|
655
658
|
reconcile/utils/ruamel.py,sha256=FzL4_L0FnMOUZmgThrZSMJs5MTdXwiy-E9MZWfk8bh8,397
|
656
659
|
reconcile/utils/secret_reader.py,sha256=2DeYAAQFjUULEKlLw3UDAUoND6gbqvCh9uKPtlc-0us,10403
|
657
660
|
reconcile/utils/semver_helper.py,sha256=-WfPOMSA2v1h7hT3PwVf-Htg7wOsoKlQC1JdmDX2Ars,1268
|
658
661
|
reconcile/utils/sharding.py,sha256=gkYf0lD3IUKQPEmdRJZ70mdDT1c9qWjbdP7evRsUis4,839
|
659
|
-
reconcile/utils/slack_api.py,sha256=
|
662
|
+
reconcile/utils/slack_api.py,sha256=C-VThgYRtrRWraq9ZE6hEf1bXrwzRDCDK0uRw3DP6ew,16425
|
660
663
|
reconcile/utils/smtp_client.py,sha256=gJNbBQJpAt5PX4t_TaeNHsXM8vt50bFgndml6yK2b5o,2800
|
661
664
|
reconcile/utils/sqs_gateway.py,sha256=gFl9DM4DmGnptuxTOe4lS3YTyE80eSAvK42ljS8h4dA,2287
|
662
665
|
reconcile/utils/state.py,sha256=FK8NLT1xyumuXpYRm0Nk6pWpOE_U6-NovGn6zKCw8vw,16298
|
@@ -783,8 +786,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
783
786
|
tools/test/test_qontract_cli.py,sha256=w2l4BHB09k1d-BGJ1jBUNCqDv7zkqYrMHojQXg-21kQ,4155
|
784
787
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
785
788
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
786
|
-
qontract_reconcile-0.10.
|
787
|
-
qontract_reconcile-0.10.
|
788
|
-
qontract_reconcile-0.10.
|
789
|
-
qontract_reconcile-0.10.
|
790
|
-
qontract_reconcile-0.10.
|
789
|
+
qontract_reconcile-0.10.1rc760.dist-info/METADATA,sha256=4WvkJGFrn51U56sSukrjQU-JoWydSXzdq7hg8hzcsoQ,2382
|
790
|
+
qontract_reconcile-0.10.1rc760.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
791
|
+
qontract_reconcile-0.10.1rc760.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
792
|
+
qontract_reconcile-0.10.1rc760.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
793
|
+
qontract_reconcile-0.10.1rc760.dist-info/RECORD,,
|
@@ -18,6 +18,7 @@ from reconcile.saas_auto_promotions_manager.merge_request_manager.reconciler imp
|
|
18
18
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
19
19
|
Renderer,
|
20
20
|
)
|
21
|
+
from reconcile.saas_auto_promotions_manager.meta import QONTRACT_INTEGRATION
|
21
22
|
from reconcile.saas_auto_promotions_manager.publisher import Publisher
|
22
23
|
from reconcile.saas_auto_promotions_manager.s3_exporter import S3Exporter
|
23
24
|
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
@@ -34,14 +35,10 @@ from reconcile.typed_queries.saas_files import get_saas_files
|
|
34
35
|
from reconcile.utils.defer import defer
|
35
36
|
from reconcile.utils.promotion_state import PromotionState
|
36
37
|
from reconcile.utils.secret_reader import create_secret_reader
|
37
|
-
from reconcile.utils.semver_helper import make_semver
|
38
38
|
from reconcile.utils.state import State, init_state
|
39
39
|
from reconcile.utils.unleash import get_feature_toggle_state
|
40
40
|
from reconcile.utils.vcs import VCS
|
41
41
|
|
42
|
-
QONTRACT_INTEGRATION = "saas-auto-promotions-manager"
|
43
|
-
QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
44
|
-
|
45
42
|
|
46
43
|
class SaasAutoPromotionsManager:
|
47
44
|
def __init__(
|
@@ -0,0 +1,28 @@
|
|
1
|
+
from collections import defaultdict
|
2
|
+
from collections.abc import Iterable
|
3
|
+
|
4
|
+
from reconcile.saas_auto_promotions_manager.merge_request_manager.reconciler import (
|
5
|
+
Promotion,
|
6
|
+
)
|
7
|
+
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
8
|
+
|
9
|
+
|
10
|
+
class DesiredState:
|
11
|
+
def __init__(self, subscribers: Iterable[Subscriber]) -> None:
|
12
|
+
self.content_hash_to_subscriber: dict[str, list[Subscriber]] = {}
|
13
|
+
subscribers_per_channel_combo: dict[str, list[Subscriber]] = defaultdict(list)
|
14
|
+
for subscriber in subscribers:
|
15
|
+
channel_combo = ",".join([c.name for c in subscriber.channels])
|
16
|
+
subscribers_per_channel_combo[channel_combo].append(subscriber)
|
17
|
+
|
18
|
+
desired_promotions: list[Promotion] = []
|
19
|
+
for channel_combo, subs in subscribers_per_channel_combo.items():
|
20
|
+
combined_content_hash = Subscriber.combined_content_hash(subscribers=subs)
|
21
|
+
self.content_hash_to_subscriber[combined_content_hash] = subs
|
22
|
+
desired_promotions.append(
|
23
|
+
Promotion(
|
24
|
+
content_hashes={combined_content_hash},
|
25
|
+
channels={channel_combo},
|
26
|
+
)
|
27
|
+
)
|
28
|
+
self.promotions = desired_promotions
|
@@ -1,9 +1,11 @@
|
|
1
1
|
import logging
|
2
|
-
from collections import defaultdict
|
3
2
|
from collections.abc import Iterable
|
4
3
|
|
5
4
|
from gitlab.exceptions import GitlabGetError
|
6
5
|
|
6
|
+
from reconcile.saas_auto_promotions_manager.merge_request_manager.desired_state import (
|
7
|
+
DesiredState,
|
8
|
+
)
|
7
9
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.merge_request import (
|
8
10
|
SAPMMR,
|
9
11
|
)
|
@@ -21,7 +23,6 @@ from reconcile.saas_auto_promotions_manager.merge_request_manager.mr_parser impo
|
|
21
23
|
)
|
22
24
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.reconciler import (
|
23
25
|
Addition,
|
24
|
-
Promotion,
|
25
26
|
Reconciler,
|
26
27
|
)
|
27
28
|
from reconcile.saas_auto_promotions_manager.merge_request_manager.renderer import (
|
@@ -66,33 +67,12 @@ class MergeRequestManagerV2:
|
|
66
67
|
self._mr_parser = mr_parser
|
67
68
|
self._renderer = renderer
|
68
69
|
self._reconciler = reconciler
|
69
|
-
self._content_hash_to_subscriber: dict[str, list[Subscriber]] = {}
|
70
70
|
self._sapm_mrs: list[SAPMMR] = []
|
71
71
|
|
72
|
-
def _aggregate_desired_state(
|
73
|
-
self, subscribers: Iterable[Subscriber]
|
74
|
-
) -> list[Promotion]:
|
75
|
-
subscribers_per_channel_combo: dict[str, list[Subscriber]] = defaultdict(list)
|
76
|
-
for subscriber in subscribers:
|
77
|
-
channel_combo = ",".join([c.name for c in subscriber.channels])
|
78
|
-
subscribers_per_channel_combo[channel_combo].append(subscriber)
|
79
|
-
|
80
|
-
desired_promotions: list[Promotion] = []
|
81
|
-
for channel_combo, subs in subscribers_per_channel_combo.items():
|
82
|
-
combined_content_hash = Subscriber.combined_content_hash(subscribers=subs)
|
83
|
-
self._content_hash_to_subscriber[combined_content_hash] = subs
|
84
|
-
desired_promotions.append(
|
85
|
-
Promotion(
|
86
|
-
content_hashes={combined_content_hash},
|
87
|
-
channels={channel_combo},
|
88
|
-
)
|
89
|
-
)
|
90
|
-
return desired_promotions
|
91
|
-
|
92
72
|
def _render_mr(self, addition: Addition) -> None:
|
93
73
|
subs: list[Subscriber] = []
|
94
74
|
for content_hash in addition.content_hashes:
|
95
|
-
subs.extend(self.
|
75
|
+
subs.extend(self._desired_state.content_hash_to_subscriber[content_hash])
|
96
76
|
content_by_path: dict[str, str] = {}
|
97
77
|
has_error = False
|
98
78
|
for sub in subs:
|
@@ -151,11 +131,12 @@ class MergeRequestManagerV2:
|
|
151
131
|
|
152
132
|
def reconcile(self, subscribers: Iterable[Subscriber]) -> None:
|
153
133
|
current_state = self._mr_parser.retrieve_open_mrs(label=SAPM_LABEL)
|
154
|
-
desired_state =
|
134
|
+
desired_state = DesiredState(subscribers=subscribers)
|
135
|
+
self._desired_state = desired_state
|
155
136
|
|
156
137
|
diff = self._reconciler.reconcile(
|
157
138
|
batch_limit=BATCH_SIZE_LIMIT,
|
158
|
-
desired_promotions=desired_state,
|
139
|
+
desired_promotions=desired_state.promotions,
|
159
140
|
open_mrs=current_state,
|
160
141
|
)
|
161
142
|
parallel_open_mrs = (
|
@@ -13,13 +13,14 @@ from reconcile.gql_definitions.common.saas_files import (
|
|
13
13
|
from reconcile.gql_definitions.fragments.saas_target_namespace import (
|
14
14
|
SaasTargetNamespace,
|
15
15
|
)
|
16
|
+
from reconcile.saas_auto_promotions_manager.meta import QONTRACT_INTEGRATION_VERSION
|
16
17
|
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
17
18
|
from reconcile.utils.ruamel import create_ruamel_instance
|
18
19
|
|
19
20
|
PROMOTION_DATA_SEPARATOR = (
|
20
21
|
"**SAPM Data - DO NOT MANUALLY CHANGE ANYTHING BELOW THIS LINE**"
|
21
22
|
)
|
22
|
-
SAPM_VERSION =
|
23
|
+
SAPM_VERSION = QONTRACT_INTEGRATION_VERSION
|
23
24
|
CONTENT_HASHES = "content_hashes"
|
24
25
|
CHANNELS_REF = "channels"
|
25
26
|
IS_BATCHABLE = "is_batchable"
|
reconcile/test/conftest.py
CHANGED
@@ -12,10 +12,10 @@ from typing import (
|
|
12
12
|
)
|
13
13
|
from unittest.mock import create_autospec
|
14
14
|
|
15
|
-
import httpretty as _httpretty
|
16
15
|
import pytest
|
17
16
|
from pydantic import BaseModel
|
18
17
|
from pydantic.error_wrappers import ValidationError
|
18
|
+
from pytest_httpserver import HTTPServer
|
19
19
|
|
20
20
|
from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
|
21
21
|
from reconcile.test.fixtures import Fixtures
|
@@ -29,13 +29,6 @@ def patch_sleep(mocker):
|
|
29
29
|
yield mocker.patch.object(time, "sleep")
|
30
30
|
|
31
31
|
|
32
|
-
@pytest.fixture()
|
33
|
-
def httpretty():
|
34
|
-
with _httpretty.enabled(allow_net_connect=False):
|
35
|
-
_httpretty.reset()
|
36
|
-
yield _httpretty
|
37
|
-
|
38
|
-
|
39
32
|
@pytest.fixture
|
40
33
|
def secret_reader(mocker) -> None:
|
41
34
|
mock_secretreader = mocker.patch(
|
@@ -153,19 +146,18 @@ def gql_api_builder() -> Callable[[Optional[Mapping]], GqlApi]:
|
|
153
146
|
|
154
147
|
|
155
148
|
@pytest.fixture
|
156
|
-
def
|
157
|
-
"""Create
|
149
|
+
def set_httpserver_responses_based_on_fixture(httpserver: HTTPServer) -> Callable:
|
150
|
+
"""Create httpserver responses based fixture files."""
|
158
151
|
|
159
|
-
def _(
|
152
|
+
def _(fx: Fixtures, paths: Iterable[str]) -> None:
|
160
153
|
for path in paths:
|
161
154
|
for method in ["get", "post", "put", "patch", "delete"]:
|
162
|
-
method_file = Path(fx.path(path)) / f"{method}.json"
|
155
|
+
method_file = Path(fx.path(path.lstrip("/"))) / f"{method}.json"
|
163
156
|
if method_file.exists():
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
content_type="text/json",
|
157
|
+
httpserver.expect_oneshot_request(
|
158
|
+
path, method=method
|
159
|
+
).respond_with_data(
|
160
|
+
method_file.read_text(), content_type="text/json"
|
169
161
|
)
|
170
162
|
|
171
163
|
return _
|
@@ -0,0 +1,60 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
|
3
|
+
from reconcile.saas_auto_promotions_manager.merge_request_manager.desired_state import (
|
4
|
+
DesiredState,
|
5
|
+
)
|
6
|
+
from reconcile.saas_auto_promotions_manager.subscriber import Subscriber
|
7
|
+
|
8
|
+
from .data_keys import (
|
9
|
+
CHANNEL,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
def test_desired_state_empty() -> None:
|
14
|
+
desired_state = DesiredState(subscribers=[])
|
15
|
+
assert desired_state.promotions == []
|
16
|
+
|
17
|
+
|
18
|
+
def test_desired_state_single_subscriber(
|
19
|
+
subscriber_builder: Callable[..., Subscriber],
|
20
|
+
) -> None:
|
21
|
+
subscriber = subscriber_builder({})
|
22
|
+
desired_state = DesiredState(subscribers=[subscriber])
|
23
|
+
assert len(desired_state.promotions) == 1
|
24
|
+
assert desired_state.promotions[0].content_hashes == {
|
25
|
+
Subscriber.combined_content_hash([subscriber])
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
def test_desired_state_multiple_subscribers_same_channel_combo(
|
30
|
+
subscriber_builder: Callable[..., Subscriber],
|
31
|
+
) -> None:
|
32
|
+
subscriber_a = subscriber_builder({CHANNEL: ["channel-a", "channel-b"]})
|
33
|
+
subscriber_a.desired_ref = "ref-a"
|
34
|
+
subscriber_b = subscriber_builder({CHANNEL: ["channel-a", "channel-b"]})
|
35
|
+
subscriber_b.desired_ref = "ref-b"
|
36
|
+
desired_state = DesiredState(subscribers=[subscriber_a, subscriber_b])
|
37
|
+
assert len(desired_state.promotions) == 1
|
38
|
+
assert desired_state.promotions[0].content_hashes == {
|
39
|
+
Subscriber.combined_content_hash([subscriber_a, subscriber_b]),
|
40
|
+
}
|
41
|
+
|
42
|
+
|
43
|
+
def test_desired_state_multiple_subscribers_different_channel_combo(
|
44
|
+
subscriber_builder: Callable[..., Subscriber],
|
45
|
+
) -> None:
|
46
|
+
subscriber_a = subscriber_builder({CHANNEL: ["channel-a", "channel-b"]})
|
47
|
+
subscriber_a.desired_ref = "ref-a"
|
48
|
+
subscriber_b = subscriber_builder({CHANNEL: ["channel-a", "channel-b"]})
|
49
|
+
subscriber_b.desired_ref = "ref-b"
|
50
|
+
subscriber_c = subscriber_builder({CHANNEL: ["channel-b", "channel-c"]})
|
51
|
+
subscriber_c.desired_ref = "ref-c"
|
52
|
+
desired_state = DesiredState(subscribers=[subscriber_a, subscriber_b, subscriber_c])
|
53
|
+
sorted_promotions = sorted(desired_state.promotions)
|
54
|
+
assert len(desired_state.promotions) == 2
|
55
|
+
assert sorted_promotions[0].content_hashes == {
|
56
|
+
Subscriber.combined_content_hash([subscriber_a, subscriber_b]),
|
57
|
+
}
|
58
|
+
assert sorted_promotions[1].content_hashes == {
|
59
|
+
Subscriber.combined_content_hash([subscriber_c]),
|
60
|
+
}
|
@@ -20,7 +20,7 @@ from reconcile.utils.deadmanssnitch_api import (
|
|
20
20
|
|
21
21
|
|
22
22
|
@pytest.fixture
|
23
|
-
def deadmanssnitch_api() ->
|
23
|
+
def deadmanssnitch_api() -> MagicMock:
|
24
24
|
return create_autospec(DeadMansSnitchApi)
|
25
25
|
|
26
26
|
|
@@ -55,7 +55,7 @@ def secret_reader(mocker: MockerFixture) -> MockerFixture:
|
|
55
55
|
|
56
56
|
def test_get_current_state(
|
57
57
|
secret_reader: MagicMock,
|
58
|
-
deadmanssnitch_api:
|
58
|
+
deadmanssnitch_api: MagicMock,
|
59
59
|
mocker: MockerFixture,
|
60
60
|
deadmanssnitch_settings: DeadMansSnitchSettingsV1,
|
61
61
|
) -> None:
|
reconcile/test/test_unleash.py
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
import json
|
2
1
|
import os
|
2
|
+
from collections.abc import Callable
|
3
3
|
|
4
|
-
import httpretty
|
5
4
|
import pytest
|
5
|
+
from pytest_httpserver import HTTPServer
|
6
6
|
from UnleashClient.features import Feature
|
7
7
|
|
8
8
|
import reconcile.utils.unleash
|
@@ -22,6 +22,75 @@ def reset_client():
|
|
22
22
|
reconcile.utils.unleash.client = None
|
23
23
|
|
24
24
|
|
25
|
+
def _setup_unleash_httpserver(features: dict, httpserver: HTTPServer) -> HTTPServer:
|
26
|
+
httpserver.expect_request("/client/features").respond_with_json(features)
|
27
|
+
httpserver.expect_request("/client/register", method="post").respond_with_data(
|
28
|
+
status=202
|
29
|
+
)
|
30
|
+
return httpserver
|
31
|
+
|
32
|
+
|
33
|
+
@pytest.fixture
|
34
|
+
def setup_unleash_disable_cluster_strategy(httpserver: HTTPServer):
|
35
|
+
def _(enabled: bool) -> HTTPServer:
|
36
|
+
features = {
|
37
|
+
"version": 2,
|
38
|
+
"features": [
|
39
|
+
{
|
40
|
+
"strategies": [
|
41
|
+
{
|
42
|
+
"name": "disableCluster",
|
43
|
+
"constraints": [],
|
44
|
+
"parameters": {"cluster_name": "foo"},
|
45
|
+
},
|
46
|
+
],
|
47
|
+
"impressionData": False,
|
48
|
+
"enabled": enabled,
|
49
|
+
"name": "test-strategies",
|
50
|
+
"description": "",
|
51
|
+
"project": "default",
|
52
|
+
"stale": False,
|
53
|
+
"type": "release",
|
54
|
+
"variants": [],
|
55
|
+
}
|
56
|
+
],
|
57
|
+
}
|
58
|
+
return _setup_unleash_httpserver(features, httpserver)
|
59
|
+
|
60
|
+
return _
|
61
|
+
|
62
|
+
|
63
|
+
@pytest.fixture
|
64
|
+
def setup_unleash_enable_cluster_strategy(httpserver: HTTPServer):
|
65
|
+
def _(enabled: bool) -> HTTPServer:
|
66
|
+
features = {
|
67
|
+
"version": 2,
|
68
|
+
"features": [
|
69
|
+
{
|
70
|
+
"strategies": [
|
71
|
+
{
|
72
|
+
"name": "enableCluster",
|
73
|
+
"constraints": [],
|
74
|
+
"parameters": {"cluster_name": "enabled-cluster"},
|
75
|
+
},
|
76
|
+
],
|
77
|
+
"impressionData": False,
|
78
|
+
"enabled": enabled,
|
79
|
+
"name": "test-strategies",
|
80
|
+
"description": "",
|
81
|
+
"project": "default",
|
82
|
+
"stale": False,
|
83
|
+
"type": "release",
|
84
|
+
"variants": [],
|
85
|
+
}
|
86
|
+
],
|
87
|
+
}
|
88
|
+
|
89
|
+
return _setup_unleash_httpserver(features, httpserver)
|
90
|
+
|
91
|
+
return _
|
92
|
+
|
93
|
+
|
25
94
|
def test__get_unleash_api_client(mocker):
|
26
95
|
mocked_unleash_client = mocker.patch(
|
27
96
|
"reconcile.utils.unleash.UnleashClient",
|
@@ -108,42 +177,12 @@ def test_get_feature_toggles(mocker, monkeypatch):
|
|
108
177
|
assert toggles["bar"] == "enabled"
|
109
178
|
|
110
179
|
|
111
|
-
def
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
"strategies": [
|
117
|
-
{
|
118
|
-
"name": "disableCluster",
|
119
|
-
"constraints": [],
|
120
|
-
"parameters": {"cluster_name": "foo"},
|
121
|
-
},
|
122
|
-
],
|
123
|
-
"impressionData": False,
|
124
|
-
"enabled": enabled,
|
125
|
-
"name": "test-strategies",
|
126
|
-
"description": "",
|
127
|
-
"project": "default",
|
128
|
-
"stale": False,
|
129
|
-
"type": "release",
|
130
|
-
"variants": [],
|
131
|
-
}
|
132
|
-
],
|
133
|
-
}
|
134
|
-
|
135
|
-
feature_param = (httpretty.GET, "http://unleash/api/client/features")
|
136
|
-
httpretty.register_uri(*feature_param, body=json.dumps(features), status=200)
|
137
|
-
|
138
|
-
register_param = (httpretty.POST, "http://unleash/api/client/register")
|
139
|
-
httpretty.register_uri(*register_param, status=202)
|
140
|
-
|
141
|
-
|
142
|
-
@httpretty.activate(allow_net_connect=False)
|
143
|
-
def test_get_feature_toggle_state_with_strategy(reset_client):
|
144
|
-
os.environ["UNLEASH_API_URL"] = "http://unleash/api"
|
180
|
+
def test_get_feature_toggle_state_with_strategy(
|
181
|
+
reset_client: None, setup_unleash_disable_cluster_strategy: Callable
|
182
|
+
):
|
183
|
+
httpserver = setup_unleash_disable_cluster_strategy(True)
|
184
|
+
os.environ["UNLEASH_API_URL"] = httpserver.url_for("/")
|
145
185
|
os.environ["UNLEASH_CLIENT_ACCESS_TOKEN"] = "bar"
|
146
|
-
setup_unleash_disable_cluster_strategy_httpretty(True)
|
147
186
|
assert not get_feature_toggle_state(
|
148
187
|
"test-strategies", context={"cluster_name": "foo"}
|
149
188
|
)
|
@@ -151,53 +190,24 @@ def test_get_feature_toggle_state_with_strategy(reset_client):
|
|
151
190
|
_shutdown_client()
|
152
191
|
|
153
192
|
|
154
|
-
|
155
|
-
|
156
|
-
|
193
|
+
def test_get_feature_toggle_state_disabled_with_strategy(
|
194
|
+
reset_client: None, setup_unleash_disable_cluster_strategy: Callable
|
195
|
+
):
|
196
|
+
httpserver = setup_unleash_disable_cluster_strategy(False)
|
197
|
+
os.environ["UNLEASH_API_URL"] = httpserver.url_for("/")
|
157
198
|
os.environ["UNLEASH_CLIENT_ACCESS_TOKEN"] = "bar"
|
158
|
-
setup_unleash_disable_cluster_strategy_httpretty(False)
|
159
199
|
assert not get_feature_toggle_state(
|
160
200
|
"test-strategies", context={"cluster_name": "bar"}
|
161
201
|
)
|
162
202
|
_shutdown_client()
|
163
203
|
|
164
204
|
|
165
|
-
def
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
"strategies": [
|
171
|
-
{
|
172
|
-
"name": "enableCluster",
|
173
|
-
"constraints": [],
|
174
|
-
"parameters": {"cluster_name": "enabled-cluster"},
|
175
|
-
},
|
176
|
-
],
|
177
|
-
"impressionData": False,
|
178
|
-
"enabled": enabled,
|
179
|
-
"name": "test-strategies",
|
180
|
-
"description": "",
|
181
|
-
"project": "default",
|
182
|
-
"stale": False,
|
183
|
-
"type": "release",
|
184
|
-
"variants": [],
|
185
|
-
}
|
186
|
-
],
|
187
|
-
}
|
188
|
-
|
189
|
-
feature_param = (httpretty.GET, "http://unleash/api/client/features")
|
190
|
-
httpretty.register_uri(*feature_param, body=json.dumps(features), status=200)
|
191
|
-
|
192
|
-
register_param = (httpretty.POST, "http://unleash/api/client/register")
|
193
|
-
httpretty.register_uri(*register_param, status=202)
|
194
|
-
|
195
|
-
|
196
|
-
@httpretty.activate(allow_net_connect=False)
|
197
|
-
def test_get_feature_toggle_state_with_enable_cluster_strategy(reset_client):
|
198
|
-
os.environ["UNLEASH_API_URL"] = "http://unleash/api"
|
205
|
+
def test_get_feature_toggle_state_with_enable_cluster_strategy(
|
206
|
+
reset_client: None, setup_unleash_enable_cluster_strategy: Callable
|
207
|
+
):
|
208
|
+
httpserver = setup_unleash_enable_cluster_strategy(True)
|
209
|
+
os.environ["UNLEASH_API_URL"] = httpserver.url_for("/")
|
199
210
|
os.environ["UNLEASH_CLIENT_ACCESS_TOKEN"] = "bar"
|
200
|
-
setup_unleash_enable_cluster_strategy_httpretty(True)
|
201
211
|
assert get_feature_toggle_state(
|
202
212
|
"test-strategies", context={"cluster_name": "enabled-cluster"}
|
203
213
|
)
|
@@ -49,10 +49,12 @@ class DeadMansSnitchApi:
|
|
49
49
|
self.session.close()
|
50
50
|
|
51
51
|
def get_snitches(self, tags: list[str]) -> list[Snitch]:
|
52
|
-
full_url = f"{self.url}?tags={','.join(tags)}"
|
53
52
|
logging.debug("Getting snitches for tags:%s", tags)
|
54
53
|
response = self.session.get(
|
55
|
-
url=
|
54
|
+
url=self.url,
|
55
|
+
params={"tags": ",".join(tags)},
|
56
|
+
auth=(self.token, ""),
|
57
|
+
timeout=self.timeout,
|
56
58
|
)
|
57
59
|
response.raise_for_status()
|
58
60
|
snitches = [Snitch(**item) for item in response.json()]
|
reconcile/utils/rest_api_base.py
CHANGED
reconcile/utils/slack_api.py
CHANGED
@@ -166,6 +166,7 @@ class SlackApi:
|
|
166
166
|
api_config: Optional[SlackApiConfig] = None,
|
167
167
|
init_usergroups: bool = True,
|
168
168
|
channel: Optional[str] = None,
|
169
|
+
slack_url: Optional[str] = None,
|
169
170
|
**chat_kwargs: Any,
|
170
171
|
) -> None:
|
171
172
|
"""
|
@@ -187,7 +188,11 @@ class SlackApi:
|
|
187
188
|
else:
|
188
189
|
self.config = SlackApiConfig()
|
189
190
|
|
190
|
-
self._sc = WebClient(
|
191
|
+
self._sc = WebClient(
|
192
|
+
token=token,
|
193
|
+
timeout=self.config.timeout,
|
194
|
+
base_url=slack_url or WebClient.BASE_URL,
|
195
|
+
)
|
191
196
|
self._configure_client_retry()
|
192
197
|
|
193
198
|
self._results: dict[str, Any] = {}
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc758.dist-info → qontract_reconcile-0.10.1rc760.dist-info}/top_level.txt
RENAMED
File without changes
|