qontract-reconcile 0.10.2.dev180__py3-none-any.whl → 0.10.2.dev182__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.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev180
3
+ Version: 0.10.2.dev182
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -15,7 +15,7 @@ reconcile/closedbox_endpoint_monitoring_base.py,sha256=al7m8EgnnYx90rY1REryW3byN
15
15
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
16
16
  reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
17
17
  reconcile/dashdotdb_cso.py,sha256=QRK0YfIqO4rehs8btD3l_GXIO2ZIycTQEKEthBdB0xA,3639
18
- reconcile/dashdotdb_dora.py,sha256=olQnGp4JYpoh1lQEf9kHc2y3bMaAIUXEB6eFohWH8Io,17859
18
+ reconcile/dashdotdb_dora.py,sha256=hMh0Eq6ze5llWGoDV3l4OGcWY5OeJdS6Xy4b1Y4C_MY,17855
19
19
  reconcile/dashdotdb_dvo.py,sha256=lCkZ0iby6HrNQb-3kYb6xrt8wCjVUZYxKzz9SiStfHU,8946
20
20
  reconcile/dashdotdb_slo.py,sha256=TvKdMOtUZcZP9QydcUJMKh0zURHgOMN_RTpQpCkD1Z8,3960
21
21
  reconcile/database_access_manager.py,sha256=Z3aAmw2LsmMIIor-bOGzziVZdVNC82Gmw8oHBUAFf-8,25577
@@ -26,7 +26,7 @@ reconcile/gcp_image_mirror.py,sha256=1ThuUff_04ZdF6uxcLoDuHhoNA3OIw0V-z0-CwdPE2w
26
26
  reconcile/github_org.py,sha256=Wc5cZamatuWsW2ZJT2ib5ps8l3iY3RXHwNUxVJerqz0,14173
27
27
  reconcile/github_owners.py,sha256=viE1KJ-zaTxuZ5yItg2C263J0brn-Q-3hR_DkYDMbhY,3122
28
28
  reconcile/github_repo_invites.py,sha256=U9UCzNVwrZ7MqODtFah8ogH0NNY-XjBin7G9gqHtCUY,2690
29
- reconcile/github_repo_permissions_validator.py,sha256=DsM5IdEw3HvrpSRR9ZYjOLwE7QqD9cEhmFgM0QhHl5o,1784
29
+ reconcile/github_repo_permissions_validator.py,sha256=ske7cHJ-41jnaywgRlDnR2z9E1yLDSJ0tivq6bhhZdc,1827
30
30
  reconcile/github_users.py,sha256=QdX164LZrm8sqggMj-0beCzWofpS6OEBfzKNrWPrfj0,3934
31
31
  reconcile/github_validator.py,sha256=-j17tn3csFVjPMSPL3te48iWVkPZCncRXdeKeLdGjjQ,931
32
32
  reconcile/gitlab_fork_compliance.py,sha256=RbHckzLnE9zkOFHJANzoejEMMbMAivmqJVs3Suvp9lU,4591
@@ -37,7 +37,7 @@ reconcile/gitlab_mr_sqs_consumer.py,sha256=i_MDVfA3Uk_TJiNkfEJzhO6_rwR7z3I3dH9oE
37
37
  reconcile/gitlab_owners.py,sha256=nIEsf3QWI3yIw_Bxy5oMaCmszTaNZDwQVaaZZxPgh4g,14447
38
38
  reconcile/gitlab_permissions.py,sha256=kZEdWL0rewP7Odz8amRBPToKxkn0IQn81IoroHGdga4,8101
39
39
  reconcile/gitlab_projects.py,sha256=K3tFf_aD1W4Ijp5q-9Qek3kwFGEWPcZ1kd7tzFJ4GyQ,1781
40
- reconcile/integrations_manager.py,sha256=CY7cOj5dzt2se4IOg11VQvGQ-eTvLML5Q42Z9SSgeSk,9463
40
+ reconcile/integrations_manager.py,sha256=2Hz-ecNxoOVCGursvjz7SONp-cMfzcLogklAAtnDln0,9479
41
41
  reconcile/jenkins_base.py,sha256=0Gocu3fU2YTltaxBlbDQOUvP-7CP2OSQV1ZRwtWeVXw,875
42
42
  reconcile/jenkins_job_builder.py,sha256=2aeOSS5pwKJgF4EzoHBWlOYNbzLj3qYzv6u55Qg6MAg,3466
43
43
  reconcile/jenkins_job_builds_cleaner.py,sha256=0iiX0iJiIIter0g9l0l-C6TUvUdVy8O9zFUh7kaYw5w,3865
@@ -102,7 +102,7 @@ reconcile/resource_template_tester.py,sha256=DsKvBuNLPxm4Fa-e1YHHySnhThm5i_j-nF3
102
102
  reconcile/run_integration.py,sha256=8kc-lMKF9n2bvyrItJ-3nOgQMqK7lh1UAubXQrxQDPY,9711
103
103
  reconcile/saas_file_validator.py,sha256=tyvFYU6lnkfDYIkAIr5pWqSvO5Yc6TagZ-quJYD2dtI,2547
104
104
  reconcile/sendgrid_teammates.py,sha256=oO8QbLb4s1o8A6CGiCagN9CmS05BSS_WLztuY0Ym9D8,4773
105
- reconcile/service_dependencies.py,sha256=SOSJvSo6GchQpLsTbkGFnf1yHtlSFu2VnirAfi6-XGA,4418
105
+ reconcile/service_dependencies.py,sha256=S8DOputtxAX-MXvWuTLEOsgLlVkXJHALODPSnWOpvGk,4500
106
106
  reconcile/signalfx_endpoint_monitoring.py,sha256=Nqgsg1cflSd2nNnm89y_e8c--7xLUqTrKOHkDs-qADE,2868
107
107
  reconcile/slack_base.py,sha256=I-msunWxfgu5bSwXYulGbtLjxUB_tRmTCAUCU-3nabI,3484
108
108
  reconcile/slack_usergroups.py,sha256=vMifpbnrQDLeckGtUmpIg7sVvlhpaJz8HZH_loA7fpY,30221
@@ -482,11 +482,11 @@ reconcile/rhidp/sso_client/base.py,sha256=EfQ2ewcOKh5idg46UKAkY6z0m_nGQfvnQKffa2
482
482
  reconcile/rhidp/sso_client/integration.py,sha256=kA8g7c38ZBSdrRtyfEqy_WgSreD1PbwY7ZIN-3tZRPc,2221
483
483
  reconcile/rhidp/sso_client/metrics.py,sha256=Tq7tSOsqL3XdcPUdozxqzSPIodUeOV87UCTqpuuqqhw,1013
484
484
  reconcile/saas_auto_promotions_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
485
- reconcile/saas_auto_promotions_manager/integration.py,sha256=haXTpwi0rEQtN9-MadBzFvSNtbxw5QQuFpMQLg3bZlQ,6707
485
+ reconcile/saas_auto_promotions_manager/integration.py,sha256=PhngUNVQMdZ_7KlIOCokdYqQ2k-XdsvvfCb8BAdSiAA,6889
486
486
  reconcile/saas_auto_promotions_manager/meta.py,sha256=76Jp50r6Y_KyJoXFfSjrt5YrCtXyg_A4FXXxHYiS3TE,161
487
487
  reconcile/saas_auto_promotions_manager/publisher.py,sha256=5gphMxr2NUvyB7WDK4eAbgZeyeF30cZ3a2ZGrbFQgZk,2976
488
488
  reconcile/saas_auto_promotions_manager/s3_exporter.py,sha256=Y-r5R6viiAzglUHbYKItYSjT_axmLlPEJVmu_H6N170,2682
489
- reconcile/saas_auto_promotions_manager/subscriber.py,sha256=tZLcjPt64cIxiswlSGpEcAg3VTp0PHvnDqgFQ2pajVM,10044
489
+ reconcile/saas_auto_promotions_manager/subscriber.py,sha256=V8e2tz0s3A-pcUNb2UoREQm6-6ZzS4F3gJdGUBoa9WY,11281
490
490
  reconcile/saas_auto_promotions_manager/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
491
491
  reconcile/saas_auto_promotions_manager/merge_request_manager/batcher.py,sha256=R2CRtjdOggY5lSqt-A5qz2ymqomx4opVeVV_oqBAW0A,7804
492
492
  reconcile/saas_auto_promotions_manager/merge_request_manager/desired_state.py,sha256=isY8frVsL3PlcdZmdZ4O0qyp76oczl4DUMX9uMArs5Y,1222
@@ -497,7 +497,7 @@ reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py,sha256
497
497
  reconcile/saas_auto_promotions_manager/merge_request_manager/open_merge_requests.py,sha256=-qGQOh6Jdp4lomNDij3zWVC0pl6uPHFWS5Woqcp5HQk,410
498
498
  reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py,sha256=EgImn9SpeThTNN5-P5lvz__bMKZNU4m2SfKpocRCy5w,7278
499
499
  reconcile/saas_auto_promotions_manager/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
500
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=LhK3OtHf-l_wm1SZCb3vZUrPpNZJRpM65WKY2HATXRY,8606
500
+ reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py,sha256=BPzSsuXy5ULKSo36v1bItC3rU-AEFfVZeWXZw1tXqLg,9674
501
501
  reconcile/skupper_network/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
502
502
  reconcile/skupper_network/integration.py,sha256=oZIVDBRcQPC-lWxNFiJhGbtCM7Yj7fjwAzYZ8JvVe3I,10789
503
503
  reconcile/skupper_network/models.py,sha256=HEwlVKsbmMaKaaBGvITIiSYNEVdjwXVhLaOJgLSZ2xQ,6604
@@ -569,7 +569,7 @@ reconcile/typed_queries/pagerduty_instances.py,sha256=zxCNxMak4iikryePaRi71lTADV
569
569
  reconcile/typed_queries/quay.py,sha256=3IMy9jjHF2f9t47EXZOQVA3p0nFkWFhaFhxhvib-71o,644
570
570
  reconcile/typed_queries/repos.py,sha256=8A93dKDt6igT4ClqMjt7YUTsoP4qh1Wnm0W3xsMgj48,824
571
571
  reconcile/typed_queries/reserved_networks.py,sha256=XY9y3amtIQT0n06O0Toubqr_UmylJ2ELAv9-BJCK890,345
572
- reconcile/typed_queries/saas_files.py,sha256=O2kd0nSFfMgnbXvSv9oMIdlBGZg7XlOU3y2CWg1W2DQ,14001
572
+ reconcile/typed_queries/saas_files.py,sha256=SOE36sWPBcuaRmEaNxXCQZMQdJiUZX8_A92o42XwHQA,14141
573
573
  reconcile/typed_queries/slack.py,sha256=r30lspctHloyygPn8_DVybxPwUWwiBpvBRRXiTVcQYk,251
574
574
  reconcile/typed_queries/slo_documents.py,sha256=YMdox_-lBRqrdxamPhdnUlRTY_Ro35ptsupq7OaynUQ,362
575
575
  reconcile/typed_queries/smtp.py,sha256=aSLglYa5bHKmlGwKkxq2RZqyMWuAf0a4S_mOuhDa084,542
@@ -616,7 +616,7 @@ reconcile/utils/external_resources.py,sha256=YzTb0xAcNdmKO326mGQy7BmST56CZcdru4l
616
616
  reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,402
617
617
  reconcile/utils/git.py,sha256=o4p9m8jlzCJDcutl2HErvGLhL6sZ1NB4Aw3zGcQIzso,2427
618
618
  reconcile/utils/github_api.py,sha256=o4J0ZU1ZSr9808uoorKHv19iae-eLo85yrCZX67p2kw,2822
619
- reconcile/utils/gitlab_api.py,sha256=PzeELVuJMnXowMPl6eXVTapbQ6U5p2QltBfeeAMXQBA,27402
619
+ reconcile/utils/gitlab_api.py,sha256=qQMN9JwaUvbirbLBy1BImkeJ4iFR-OCfWExphrfkmXs,28610
620
620
  reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
621
621
  reconcile/utils/gql.py,sha256=C0thIm_k9MBldfqwHzyqtYZk9sIvMdm9IbbnXLGwjD8,14158
622
622
  reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
@@ -626,7 +626,7 @@ reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSb
626
626
  reconcile/utils/instrumented_wrappers.py,sha256=VqT4s0Bdicv224-uSeSaugtHXm-xJ3oSeBiqj0QQRiU,1942
627
627
  reconcile/utils/jenkins_api.py,sha256=RaKuZmO7_lbI-hE6c_Pq2a6CQdmBVj7BcP2jR68cIbI,7081
628
628
  reconcile/utils/jira_client.py,sha256=xhklHRsMnQqZ8WCxO1efjRKvx6diLcGQ6DLi5ZKl_HM,10544
629
- reconcile/utils/jjb_client.py,sha256=4YqeXEkO4p6QtJE_fkaD1XuLKbe9l3g0W7AVpcjJ3yg,15187
629
+ reconcile/utils/jjb_client.py,sha256=e5cDeNAeJMGz3sZMJ1KUIMFyLdRet0YnC0Qgj1vTPHc,15239
630
630
  reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
631
631
  reconcile/utils/jump_host.py,sha256=gi8vGUDgdTVwJvROvRVauFxtL0YAramhbWvG70L7AY8,5137
632
632
  reconcile/utils/keycloak.py,sha256=YWSEUGrOVqFaJUk055dKUWpLDPdDRvhcmvR-lfbmxdE,3388
@@ -667,12 +667,12 @@ reconcile/utils/state.py,sha256=az4tBmZ0EdbFcAGiBVUxs3cr2-BVWsuDQiNTvjjQq8s,1637
667
667
  reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
668
668
  reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
669
669
  reconcile/utils/terraform_client.py,sha256=IDlrNvGEc2i6ElZIL_fzaJEad1nRC3DkP9_VXhJXmU0,37329
670
- reconcile/utils/terrascript_aws_client.py,sha256=qQAwDAUK131tm2oBWBJzlKFYtpv7mPkqW1u19-A3wds,292248
670
+ reconcile/utils/terrascript_aws_client.py,sha256=-FNQXltzl4OYGO7eMyvH7-wYUMHNFEVhiNwCvhN6WrI,292307
671
671
  reconcile/utils/three_way_diff_strategy.py,sha256=oQcHXd9LVhirJfoaOBoHUYuZVGfyL2voKr6KVI34zZE,4833
672
672
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
673
673
  reconcile/utils/vault.py,sha256=aSA8l9cJlPUHpChFGl27nSY-Mpq9FMjBo7Dcgb1BVfM,15036
674
674
  reconcile/utils/vaultsecretref.py,sha256=0KUSzuvTRxPyKY919TO3-B_eYg4_76fzKvMF8j5s1G0,911
675
- reconcile/utils/vcs.py,sha256=YtjR3E_VaS7lA4ROzSr7hte-8G-SZ_UmJP5RJ2DCyJk,8807
675
+ reconcile/utils/vcs.py,sha256=_S_QZ5UYXOnhNDOBQ-jNVOB6VpYh7Y96YLsM5T3ivug,8836
676
676
  reconcile/utils/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
677
677
  reconcile/utils/acs/base.py,sha256=4UsDrCpAOuddL3PKNuIQYoJP1BtZQNNB8_KEX0lXneg,2532
678
678
  reconcile/utils/acs/notifiers.py,sha256=DlzTDM9arWQlBSiDy70y5Mf38OKVs9V0FzFe2LfOKXA,5046
@@ -761,7 +761,7 @@ reconcile/utils/runtime/sharding.py,sha256=r0ieUtNed7NvknSw6qQrCkKpVXE1shuHGnfFc
761
761
  reconcile/utils/saasherder/__init__.py,sha256=3U8plqMAPRE1kjwZ5YnIsYsggTf4_gS7flRUEuXVBAs,343
762
762
  reconcile/utils/saasherder/interfaces.py,sha256=NEYQspYfyWQhBeJyNCqSFbixi1A4wRVGB7FeNM5BDCk,9141
763
763
  reconcile/utils/saasherder/models.py,sha256=JaOz_DEtudJZhiDe90kaBlJkppFufn81V92oK9PHYx0,10208
764
- reconcile/utils/saasherder/saasherder.py,sha256=crF_hQBeL9TBv_R6SafAze6xutXFSNEX77KaT4XqjF8,87135
764
+ reconcile/utils/saasherder/saasherder.py,sha256=PjwJ36Eki5X0Wqu7-sE1slApMh6th3NojAWfZpOz_OQ,87111
765
765
  reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
766
766
  reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
767
767
  reconcile/utils/terraform/config_client.py,sha256=gRL1rQ0AqvShei_rcGqC3HDYGskOFKE1nPrJyJE9yno,4676
@@ -807,7 +807,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
807
807
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
808
808
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
809
809
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
810
- qontract_reconcile-0.10.2.dev180.dist-info/METADATA,sha256=3ABcj9gi3YuKUyzROkTB3i4Mo6F6eXVi4BpgPXIwLg4,24627
811
- qontract_reconcile-0.10.2.dev180.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
- qontract_reconcile-0.10.2.dev180.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
- qontract_reconcile-0.10.2.dev180.dist-info/RECORD,,
810
+ qontract_reconcile-0.10.2.dev182.dist-info/METADATA,sha256=ykY9LybQBKkA6Trn_h7QDGXtheaf8sfn8XwOcno_Ev4,24627
811
+ qontract_reconcile-0.10.2.dev182.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
+ qontract_reconcile-0.10.2.dev182.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
+ qontract_reconcile-0.10.2.dev182.dist-info/RECORD,,
@@ -34,6 +34,7 @@ from reconcile.typed_queries.saas_files import get_saas_files
34
34
  from reconcile.utils.github_api import GithubRepositoryApi
35
35
  from reconcile.utils.gitlab_api import GitLabApi
36
36
  from reconcile.utils.secret_reader import create_secret_reader
37
+ from reconcile.utils.vcs import GITHUB_BASE_URL
37
38
 
38
39
  QONTRACT_INTEGRATION = "dashdotdb-dora"
39
40
 
@@ -421,7 +422,7 @@ class DashdotdbDORA(DashdotdbBase):
421
422
  return rc, []
422
423
 
423
424
  LOG.info("Fetching commits %s", rc)
424
- if rc.repo_url.startswith("https://github.com"):
425
+ if rc.repo_url.startswith(GITHUB_BASE_URL):
425
426
  try:
426
427
  commits = self._github_compare_commits(rc)
427
428
  except GithubException as e:
@@ -476,8 +477,7 @@ class DashdotdbDORA(DashdotdbBase):
476
477
  if not rc.repo_url:
477
478
  return []
478
479
 
479
- prefix = "https://github.com/"
480
- repo = rc.repo_url[rc.repo_url.startswith(prefix) and len(prefix) :]
480
+ repo = rc.repo_url.removeprefix(GITHUB_BASE_URL).rstrip("/")
481
481
 
482
482
  return [
483
483
  Commit(rc.repo_url, commit.sha, commit.commit.committer.date)
@@ -11,6 +11,7 @@ from reconcile.jenkins_job_builder import init_jjb
11
11
  from reconcile.utils.jjb_client import JJB
12
12
  from reconcile.utils.secret_reader import SecretReader
13
13
  from reconcile.utils.semver_helper import make_semver
14
+ from reconcile.utils.vcs import GITHUB_BASE_URL
14
15
 
15
16
  QONTRACT_INTEGRATION = "github-repo-permissions-validator"
16
17
  QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
@@ -45,7 +46,7 @@ def run(dry_run: bool, instance_name: str) -> None:
45
46
  error = False
46
47
  for job in pr_check_jobs:
47
48
  repo_url = jjb.get_repo_url(job)
48
- repo_name = repo_url.rstrip("/").replace("https://github.com/", "")
49
+ repo_name = repo_url.removeprefix(GITHUB_BASE_URL).rstrip("/")
49
50
  repo = gh.get_repo(repo_name)
50
51
  permissions = repo.permissions
51
52
  if not permissions.push and repo_url not in invitations:
@@ -47,6 +47,7 @@ from reconcile.utils.runtime.sharding import (
47
47
  StaticShardingStrategy,
48
48
  )
49
49
  from reconcile.utils.semver_helper import make_semver
50
+ from reconcile.utils.vcs import GITHUB_BASE_URL
50
51
 
51
52
  QONTRACT_INTEGRATION = "integrations-manager"
52
53
  QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
@@ -58,8 +59,7 @@ INTEGRATION_UPSTREAM_REPOS_PARAM = "INTEGRATION_UPSTREAM_REPOS"
58
59
 
59
60
 
60
61
  def get_image_tag_from_ref(ref: str, upstream: str) -> str:
61
- gh_prefix = "https://github.com/"
62
- upstream = upstream.removeprefix(gh_prefix)
62
+ upstream = upstream.removeprefix(GITHUB_BASE_URL)
63
63
  settings = queries.get_app_interface_settings()
64
64
  gh_token = get_default_config()["token"]
65
65
  github = Github(gh_token, base_url=GH_BASE_URL)
@@ -94,6 +94,7 @@ class SaasAutoPromotionsManager:
94
94
 
95
95
  def init_external_dependencies(
96
96
  dry_run: bool,
97
+ thread_pool_size: int,
97
98
  env_name: str | None = None,
98
99
  app_name: str | None = None,
99
100
  ) -> tuple[
@@ -139,7 +140,11 @@ def init_external_dependencies(
139
140
  renderer=Renderer(),
140
141
  )
141
142
  saas_files = get_saas_files(env_name=env_name, app_name=app_name)
142
- saas_inventory = SaasFilesInventory(saas_files=saas_files)
143
+ saas_inventory = SaasFilesInventory(
144
+ saas_files=saas_files,
145
+ secret_reader=secret_reader,
146
+ thread_pool_size=thread_pool_size,
147
+ )
143
148
  saas_deploy_state = init_state(
144
149
  integration=OPENSHIFT_SAAS_DEPLOY, secret_reader=secret_reader
145
150
  )
@@ -181,7 +186,10 @@ def run(
181
186
  saas_deploy_state,
182
187
  sapm_state,
183
188
  ) = init_external_dependencies(
184
- dry_run=dry_run, env_name=env_name, app_name=app_name
189
+ dry_run=dry_run,
190
+ env_name=env_name,
191
+ app_name=app_name,
192
+ thread_pool_size=thread_pool_size,
185
193
  )
186
194
  if defer:
187
195
  defer(vcs.cleanup)
@@ -13,6 +13,7 @@ from reconcile.saas_auto_promotions_manager.publisher import (
13
13
  DeploymentInfo,
14
14
  Publisher,
15
15
  )
16
+ from reconcile.utils.slo_document_manager import SLODocumentManager
16
17
 
17
18
  CONTENT_HASH_LENGTH = 32
18
19
 
@@ -47,7 +48,9 @@ class Subscriber:
47
48
  uid: str,
48
49
  soak_days: int,
49
50
  blocked_versions: set[str],
51
+ hotfix_versions: set[str],
50
52
  schedule: str,
53
+ slo_document_manager: SLODocumentManager | None = None,
51
54
  ):
52
55
  self.saas_name = saas_name
53
56
  self.template_name = template_name
@@ -64,6 +67,8 @@ class Subscriber:
64
67
  self._content_hash = ""
65
68
  self._use_target_config_hash = use_target_config_hash
66
69
  self._blocked_versions = blocked_versions
70
+ self._hotfix_versions = hotfix_versions
71
+ self.slo_document_manager = slo_document_manager
67
72
 
68
73
  def has_diff(self) -> bool:
69
74
  current_hashes = {
@@ -192,6 +197,10 @@ class Subscriber:
192
197
  return
193
198
 
194
199
  desired_ref = next(iter(publisher_refs))
200
+ # validate slo gatekeeping
201
+ if self._has_breached_slos(desired_ref=desired_ref):
202
+ return
203
+
195
204
  if desired_ref in self._blocked_versions:
196
205
  logging.info(
197
206
  "Subscriber at path %s promotion stopped because of blocked ref: %s",
@@ -203,6 +212,25 @@ class Subscriber:
203
212
  # Passed all gates -> lets promote desired ref
204
213
  self.desired_ref = desired_ref
205
214
 
215
+ def _has_breached_slos(self, desired_ref: str) -> bool:
216
+ if self.slo_document_manager and desired_ref not in self._hotfix_versions:
217
+ breached_slos = self.slo_document_manager.get_breached_slos()
218
+ if breached_slos:
219
+ logging.info(
220
+ "Subscriber at path %s promotion stopped because following breached SLOs",
221
+ self.target_file_path,
222
+ )
223
+ for slo in breached_slos:
224
+ logging.info(
225
+ "SLO:%s of document %s is breached. Current value:%f Expected:%f ",
226
+ slo.slo.name,
227
+ slo.slo_document_name,
228
+ slo.current_slo_value,
229
+ slo.slo.slo_target,
230
+ )
231
+ return True
232
+ return False
233
+
206
234
  def _compute_desired_config_hashes(self) -> None:
207
235
  """
208
236
  Compute the desired config hashes for this subscriber.
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from collections import defaultdict
2
3
  from collections.abc import Iterable
3
4
 
4
5
  from reconcile.gql_definitions.common.saas_files import ParentSaasPromotionV1
@@ -8,7 +9,9 @@ from reconcile.saas_auto_promotions_manager.subscriber import (
8
9
  ConfigHash,
9
10
  Subscriber,
10
11
  )
11
- from reconcile.typed_queries.saas_files import SaasFile
12
+ from reconcile.typed_queries.saas_files import SaasFile, SaasResourceTemplateTarget
13
+ from reconcile.utils.secret_reader import SecretReaderBase
14
+ from reconcile.utils.slo_document_manager import SLODocumentManager
12
15
 
13
16
 
14
17
  class SaasFileInventoryError(Exception):
@@ -25,8 +28,15 @@ class SaasFilesInventory:
25
28
  This basically spans a directed graph, with subscribers as the root.
26
29
  """
27
30
 
28
- def __init__(self, saas_files: Iterable[SaasFile]):
31
+ def __init__(
32
+ self,
33
+ saas_files: Iterable[SaasFile],
34
+ secret_reader: SecretReaderBase,
35
+ thread_pool_size: int,
36
+ ):
29
37
  self._saas_files = saas_files
38
+ self.secret_reader = secret_reader
39
+ self.thread_pool_size = thread_pool_size
30
40
  self._channels_by_name: dict[str, Channel] = {}
31
41
  self.subscribers: list[Subscriber] = []
32
42
  self.publishers: list[Publisher] = []
@@ -86,10 +96,13 @@ class SaasFilesInventory:
86
96
 
87
97
  def _assemble_subscribers_with_auto_promotions(self) -> None:
88
98
  for saas_file in self._saas_files:
89
- blocked_versions: dict[str, set[str]] = {}
99
+ blocked_versions: dict[str, set[str]] = defaultdict(set[str])
100
+ hotfix_versions: dict[str, set[str]] = defaultdict(set[str])
90
101
  for code_component in saas_file.app.code_components or []:
91
102
  for version in code_component.blocked_versions or []:
92
- blocked_versions.setdefault(code_component.url, set()).add(version)
103
+ blocked_versions[code_component.url].add(version)
104
+ for hf_version in code_component.hotfix_versions or []:
105
+ hotfix_versions[code_component.url].add(hf_version)
93
106
  for resource_template in saas_file.resource_templates:
94
107
  for target in resource_template.targets:
95
108
  file_path = target.path or saas_file.path
@@ -101,6 +114,7 @@ class SaasFilesInventory:
101
114
  continue
102
115
  soak_days = target.promotion.soak_days or 0
103
116
  schedule = target.promotion.schedule or "* * * * *"
117
+
104
118
  subscriber = Subscriber(
105
119
  uid=target.uid(
106
120
  parent_saas_file_name=saas_file.name,
@@ -112,10 +126,10 @@ class SaasFilesInventory:
112
126
  ref=target.ref,
113
127
  target_namespace=target.namespace,
114
128
  soak_days=soak_days,
129
+ slo_document_manager=self._build_slo_document_manager(target),
115
130
  schedule=schedule,
116
- blocked_versions=blocked_versions.get(
117
- resource_template.url, set()
118
- ),
131
+ hotfix_versions=hotfix_versions[resource_template.url],
132
+ blocked_versions=blocked_versions[resource_template.url],
119
133
  use_target_config_hash=bool(
120
134
  target.promotion.redeploy_on_publisher_config_change
121
135
  ),
@@ -158,6 +172,17 @@ class SaasFilesInventory:
158
172
  self._channels_by_name[subscribe_channel]
159
173
  )
160
174
 
175
+ def _build_slo_document_manager(
176
+ self, target: SaasResourceTemplateTarget
177
+ ) -> SLODocumentManager | None:
178
+ if target.slos:
179
+ return SLODocumentManager(
180
+ slo_documents=target.slos,
181
+ secret_reader=self.secret_reader,
182
+ thread_pool_size=self.thread_pool_size,
183
+ )
184
+ return None
185
+
161
186
  def _remove_unsupported(self) -> None:
162
187
  """
163
188
  Lets remove subscribers from which we know we do not support them and log an error.
@@ -16,6 +16,7 @@ from reconcile.gql_definitions.service_dependencies.service_dependencies import
16
16
  SaasResourceTemplateV2,
17
17
  )
18
18
  from reconcile.utils import gql
19
+ from reconcile.utils.vcs import GITHUB_BASE_URL
19
20
 
20
21
  QONTRACT_INTEGRATION = "service-dependencies"
21
22
 
@@ -35,7 +36,9 @@ def get_desired_dependency_names(
35
36
  gitlab_urls = [cc for cc in code_components if "gitlab" in cc.url]
36
37
  if gitlab_urls:
37
38
  required_dep_names.update(get_dependency_names(dependency_map, "gitlab"))
38
- github_urls = [cc for cc in code_components if "github.com" in cc.url]
39
+ github_urls = [
40
+ cc for cc in code_components if cc.url.startswith(GITHUB_BASE_URL)
41
+ ]
39
42
  if github_urls:
40
43
  required_dep_names.update(get_dependency_names(dependency_map, "github"))
41
44
 
@@ -39,6 +39,7 @@ from reconcile.gql_definitions.common.saasherder_settings import AppInterfaceSet
39
39
  from reconcile.gql_definitions.common.saasherder_settings import (
40
40
  query as saasherder_settings_query,
41
41
  )
42
+ from reconcile.gql_definitions.fragments.saas_slo_document import SLODocument
42
43
  from reconcile.gql_definitions.fragments.saas_target_namespace import (
43
44
  SaasTargetNamespace,
44
45
  )
@@ -63,6 +64,7 @@ class SaasResourceTemplateTarget(ConfiguredBaseModel):
63
64
  secret_parameters: (
64
65
  list[SaasResourceTemplateTargetV2_SaasSecretParametersV1] | None
65
66
  ) = Field(..., alias="secretParameters")
67
+ slos: list[SLODocument] | None = Field(..., alias="slos")
66
68
  upstream: SaasResourceTemplateTargetUpstreamV1 | None = Field(..., alias="upstream")
67
69
  images: list[SaasResourceTemplateTargetImageV1] | None = Field(..., alias="images")
68
70
  disable: bool | None = Field(..., alias="disable")
@@ -1,6 +1,8 @@
1
+ import io
1
2
  import logging
2
3
  import os
3
4
  import re
5
+ import tarfile
4
6
  from collections.abc import (
5
7
  Iterable,
6
8
  Mapping,
@@ -828,3 +830,29 @@ class GitLabApi:
828
830
  list[PersonalAccessToken],
829
831
  self.gl.personal_access_tokens.list(get_all=True),
830
832
  )
833
+
834
+ @staticmethod
835
+ def get_directory_contents(
836
+ project: Project,
837
+ ref: str | None = None,
838
+ path: str | None = None,
839
+ ) -> dict[str, bytes]:
840
+ """
841
+ Get the contents of a directory in a project.
842
+
843
+ :param project: The project to get the contents from.
844
+ :param ref: The commit SHA to download. A tag, branch reference, or SHA can be used. If not specified, defaults to the tip of the default branch.
845
+ :param path: The subpath of the repository to download. If an empty string, defaults to the whole repository.
846
+ :return: A dictionary with the file path as keys and the file content bytes as values.
847
+ """
848
+ archive = project.repository_archive(format="tar.gz", sha=ref, path=path)
849
+ tar_bytes = io.BytesIO(archive)
850
+ with tarfile.open(fileobj=tar_bytes, mode="r:gz") as tar:
851
+ return {
852
+ file_path: file.read()
853
+ for member in tar.getmembers()
854
+ if member.isfile()
855
+ # skip leading prefix xxx/
856
+ and (file_path := member.name.split("/", 1)[-1])
857
+ and (file := tar.extractfile(member))
858
+ }
@@ -25,6 +25,7 @@ from sretoolbox.utils import retry
25
25
 
26
26
  from reconcile.utils import throughput
27
27
  from reconcile.utils.helpers import toggle_logger
28
+ from reconcile.utils.vcs import GITHUB_BASE_URL
28
29
 
29
30
  JJB_INI = "[jenkins]\nurl = https://JENKINS_URL"
30
31
 
@@ -290,7 +291,7 @@ class JJB: # pylint: disable=too-many-public-methods
290
291
  for job in jobs:
291
292
  try:
292
293
  project_url_raw = job["properties"][0]["github"]["url"]
293
- if "https://github.com" in project_url_raw:
294
+ if project_url_raw.startswith(GITHUB_BASE_URL):
294
295
  continue
295
296
  if str(job.get("disabled")).lower() == "true":
296
297
  continue
@@ -83,6 +83,7 @@ from reconcile.utils.saasherder.models import (
83
83
  )
84
84
  from reconcile.utils.secret_reader import SecretReaderBase
85
85
  from reconcile.utils.state import State
86
+ from reconcile.utils.vcs import GITHUB_BASE_URL
86
87
 
87
88
  TARGET_CONFIG_HASH = "target_config_hash"
88
89
 
@@ -742,7 +743,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
742
743
  [url, sha] = trigger_reason.split(" ")[0].split("/commit/")
743
744
  repo_name = urlparse(url).path.strip("/")
744
745
  file_name = f"{repo_name.replace('/', '-')}-{sha}.tar.gz"
745
- if "github" in url:
746
+ if url.startswith(GITHUB_BASE_URL):
746
747
  github = self._initiate_github(saas_file, base_url="https://api.github.com")
747
748
  repo = github.get_repo(repo_name)
748
749
  # get_archive_link get redirect url form header, it does not work with github-mirror
@@ -760,8 +761,8 @@ class SaasHerder: # pylint: disable=too-many-public-methods
760
761
  ) -> tuple[Any, str]:
761
762
  commit_sha = self._get_commit_sha(url, ref, github)
762
763
 
763
- if "github" in url:
764
- repo_name = url.rstrip("/").replace("https://github.com/", "")
764
+ if url.startswith(GITHUB_BASE_URL):
765
+ repo_name = url.removeprefix(GITHUB_BASE_URL).rstrip("/")
765
766
  repo = github.get_repo(repo_name)
766
767
  content = self._get_file_contents_github(repo, path, commit_sha)
767
768
  elif "gitlab" in url:
@@ -781,8 +782,8 @@ class SaasHerder: # pylint: disable=too-many-public-methods
781
782
  ) -> tuple[list[Any], str]:
782
783
  commit_sha = self._get_commit_sha(url, ref, github)
783
784
  resources: list[Any] = []
784
- if "github" in url:
785
- repo_name = url.rstrip("/").replace("https://github.com/", "")
785
+ if url.startswith(GITHUB_BASE_URL):
786
+ repo_name = url.removeprefix(GITHUB_BASE_URL).rstrip("/")
786
787
  repo = github.get_repo(repo_name)
787
788
  directory = repo.get_contents(path, commit_sha)
788
789
  if isinstance(directory, ContentFile):
@@ -798,17 +799,14 @@ class SaasHerder: # pylint: disable=too-many-public-methods
798
799
  if not self.gitlab:
799
800
  raise Exception("gitlab is not initialized")
800
801
  project = self.gitlab.get_project(url)
801
- for item in self.gitlab.get_repository_tree(
802
- project=project,
803
- path=path.lstrip("/"),
802
+ dir_contents = self.gitlab.get_directory_contents(
803
+ project,
804
804
  ref=commit_sha,
805
- recursive=False,
806
- ):
807
- file_contents = project.files.get(
808
- file_path=item["path"], ref=commit_sha
809
- )
810
- resource = yaml.safe_load(file_contents.decode())
811
- resources.append(resource)
805
+ path=path,
806
+ )
807
+ for content in dir_contents.values():
808
+ result_resources = yaml.safe_load_all(content)
809
+ resources.extend(result_resources)
812
810
  else:
813
811
  raise Exception(f"Only GitHub and GitLab are supported: {url}")
814
812
 
@@ -817,8 +815,8 @@ class SaasHerder: # pylint: disable=too-many-public-methods
817
815
  @retry()
818
816
  def _get_commit_sha(self, url: str, ref: str, github: Github) -> str:
819
817
  commit_sha = ""
820
- if "github" in url:
821
- repo_name = url.rstrip("/").replace("https://github.com/", "")
818
+ if url.startswith(GITHUB_BASE_URL):
819
+ repo_name = url.removeprefix(GITHUB_BASE_URL).rstrip("/")
822
820
  repo = github.get_repo(repo_name)
823
821
  commit = repo.get_commit(sha=ref)
824
822
  commit_sha = commit.sha
@@ -186,6 +186,7 @@ from reconcile.utils.password_validator import (
186
186
  )
187
187
  from reconcile.utils.secret_reader import SecretReader, SecretReaderBase
188
188
  from reconcile.utils.terraform import safe_resource_id
189
+ from reconcile.utils.vcs import GITHUB_BASE_URL
189
190
 
190
191
  GH_BASE_URL = os.environ.get("GITHUB_API", "https://api.github.com")
191
192
  LOGTOES_RELEASE = "repos/app-sre/logs-to-elasticsearch-lambda/releases/latest"
@@ -5685,9 +5686,9 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
5685
5686
  return ref
5686
5687
 
5687
5688
  # get commit_sha from branch
5688
- if "github" in url:
5689
+ if url.startswith(GITHUB_BASE_URL):
5689
5690
  github = self.init_github()
5690
- repo_name = url.rstrip("/").replace("https://github.com/", "")
5691
+ repo_name = url.removeprefix(GITHUB_BASE_URL).rstrip("/")
5691
5692
  repo = github.get_repo(repo_name)
5692
5693
  commit = repo.get_commit(sha=ref)
5693
5694
  return commit.sha
reconcile/utils/vcs.py CHANGED
@@ -22,6 +22,8 @@ from reconcile.utils.secret_reader import (
22
22
  SecretReaderBase,
23
23
  )
24
24
 
25
+ GITHUB_BASE_URL = "https://github.com/"
26
+
25
27
 
26
28
  class MRCheckStatus(Enum):
27
29
  NONE = 0
@@ -158,7 +160,7 @@ class VCS:
158
160
  ) -> str:
159
161
  if bool(self._is_commit_sha_regex.search(ref)):
160
162
  return ref
161
- if repo_url.startswith("https://github.com/"):
163
+ if repo_url.startswith(GITHUB_BASE_URL):
162
164
  github = self._init_github(repo_url=repo_url, auth_code=auth_code)
163
165
  return github.get_commit_sha(ref=ref)
164
166
  # assume gitlab by default
@@ -175,7 +177,7 @@ class VCS:
175
177
  Return a list of commits between two commits.
176
178
  Note, that the commit_to is included in the result list, whereas commit_from is not included.
177
179
  """
178
- if repo_url.startswith("https://github.com/"):
180
+ if repo_url.startswith(GITHUB_BASE_URL):
179
181
  github = self._init_github(repo_url=repo_url, auth_code=auth_code)
180
182
  data = github.compare(commit_from=commit_from, commit_to=commit_to)
181
183
  return [