qontract-reconcile 0.10.2.dev185__py3-none-any.whl → 0.10.2.dev187__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.dev185
3
+ Version: 0.10.2.dev187
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
@@ -99,7 +99,6 @@ OpenShift templates can be found [here](/openshift/qontract-reconcile.yaml). In
99
99
  `qontract-reconcile` includes the following integrations:
100
100
 
101
101
  ```text
102
- acs-notifiers Manages RHACS notifier configurations
103
102
  acs-policies Manages RHACS security policy configurations
104
103
  acs-rbac Manages RHACS rbac configuration
105
104
  advanced-upgrade-scheduler Manage Cluster Upgrade Policy schedules in
@@ -1,6 +1,5 @@
1
1
  reconcile/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- reconcile/acs_notifiers.py,sha256=YIV9TrgWjBD8nFbrBvvU8-9s-AdezY4X5t90bFROCtM,4451
3
- reconcile/acs_policies.py,sha256=xNbhIlwE1u2URbEQcX-3C-pTu--XjrKAqGj0-Wd85dY,9152
2
+ reconcile/acs_policies.py,sha256=pwFKP3afmRbpRq-7FRAosI-A60yfufE2vvXBjOMgsCU,8653
4
3
  reconcile/acs_rbac.py,sha256=15vNfNzdG_DeXaJ-f5m8DSaJh__LUK766_xAECqyTsg,22657
5
4
  reconcile/aws_ami_share.py,sha256=M_gT7y3cSAyT_Pm90PBCNDSmbZtqREqe2jNETh0i9Qs,3808
6
5
  reconcile/aws_ecr_image_pull_secrets.py,sha256=F58PtX1GlB9XHqj8hGy9ItiTznXLAAKTNlWD9iT2MWI,2593
@@ -10,7 +9,7 @@ reconcile/aws_iam_password_reset.py,sha256=O0JX2N5kNRKs3u2xzu4NNrI6p0ag5JWy3MTsv
10
9
  reconcile/aws_support_cases_sos.py,sha256=PDhilxQ4TBxVnxUPIUdTbKEaNUI0wzPiEsB91oHT2fY,3384
11
10
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
12
11
  reconcile/checkpoint.py,sha256=_JhMxrye5BgkRMxWYuf7Upli6XayPINKSsuo3ynHTRc,5010
13
- reconcile/cli.py,sha256=xyVnxNyq3IPISWwFlB9j4HAFjowXYv3EdsEGIMFhTy0,108438
12
+ reconcile/cli.py,sha256=-WTtuEtxDuHdZ-GIGtklu4p6r8YdR7QfRqdK6rHl2gY,108163
14
13
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=al7m8EgnnYx90rY1REryW3byN_ItfJfAzEeLtjbCfi0,4921
15
14
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
16
15
  reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
@@ -27,7 +26,7 @@ reconcile/github_org.py,sha256=Wc5cZamatuWsW2ZJT2ib5ps8l3iY3RXHwNUxVJerqz0,14173
27
26
  reconcile/github_owners.py,sha256=viE1KJ-zaTxuZ5yItg2C263J0brn-Q-3hR_DkYDMbhY,3122
28
27
  reconcile/github_repo_invites.py,sha256=U9UCzNVwrZ7MqODtFah8ogH0NNY-XjBin7G9gqHtCUY,2690
29
28
  reconcile/github_repo_permissions_validator.py,sha256=PNqL4dqa2OaNBy-NmLVN-t1HZa6eS6HgSYmfOunYqtA,1798
30
- reconcile/github_users.py,sha256=QdX164LZrm8sqggMj-0beCzWofpS6OEBfzKNrWPrfj0,3934
29
+ reconcile/github_users.py,sha256=ZeYNMyvZPVMx6mh5TiKEUQy4W1uw3VmUZOHiXus5I0Y,5073
31
30
  reconcile/github_validator.py,sha256=-j17tn3csFVjPMSPL3te48iWVkPZCncRXdeKeLdGjjQ,931
32
31
  reconcile/gitlab_fork_compliance.py,sha256=RbHckzLnE9zkOFHJANzoejEMMbMAivmqJVs3Suvp9lU,4591
33
32
  reconcile/gitlab_housekeeping.py,sha256=c31Jtw5t8bnOzUO9jMWF_0DHitPzol93AA7YWBxM5L0,25416
@@ -48,7 +47,7 @@ reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffF
48
47
  reconcile/jenkins_worker_fleets.py,sha256=L2wEXpd4xuEHrXGss4iH788nG8UlLSYduZe1EY2IVw4,5377
49
48
  reconcile/jira_permissions_validator.py,sha256=5rc4Q2mXGL3HCZmYpZaJkjzBrpCRnlLeCY0Yl2fDOs4,14672
50
49
  reconcile/jira_watcher.py,sha256=L_UL2MKm2SoIGNsCLThm28pnqCkoFc154JWsD6bURug,3593
51
- reconcile/ldap_users.py,sha256=7hdO5CAPl-VNBvDRmKHg13LoblHXXPt7YEKNGomAoGg,3158
50
+ reconcile/ldap_users.py,sha256=oP1CAxmgSi3zDJ3vKTPySjap6WmEX1U469FmFrov5l4,4599
52
51
  reconcile/mr_client_gateway.py,sha256=WhjMd-sIXDFCV8-rt8CEjurJ5OYB1pOD0K3o0tZRXQg,1885
53
52
  reconcile/ocm_additional_routers.py,sha256=KfcFDVbNoc6n5dHWjYdAf1_DiVqVG6Tw23WLKoV8cdg,3306
54
53
  reconcile/ocm_addons.py,sha256=qqAyqRBRbdZQvAcjb-QlSVyRAyQBZk6iVlgnI4jyi7s,3353
@@ -230,7 +229,7 @@ reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
230
229
  reconcile/gql_definitions/introspection.json,sha256=MT1HCR90ZahZxjrgaBEp34JNhjaEiDRblQK-qfSrfF0,2316277
231
230
  reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
232
231
  reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
233
- reconcile/gql_definitions/acs/acs_policies.py,sha256=9IFpGAcrZ8nTFC05q3-12nwkNOEkquHB2-XbVq5_jy4,7219
232
+ reconcile/gql_definitions/acs/acs_policies.py,sha256=Ygpfl2-VkYLSlJvHgp_dJBfb66K_Rwfdfpsa18w1v1s,4338
234
233
  reconcile/gql_definitions/acs/acs_rbac.py,sha256=cZsIlCWliPQdQHgmBsIMx54fJNOtkdRXLzmOKZmJNHk,3009
235
234
  reconcile/gql_definitions/advanced_upgrade_service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
235
  reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py,sha256=230uwBoBaaVz686d_WNZT9eAw6BX2VpoQX0mKhf5UcM,4390
@@ -285,6 +284,7 @@ reconcile/gql_definitions/common/clusters_with_peering.py,sha256=B1Hi3u6rZZsl4bD
285
284
  reconcile/gql_definitions/common/github_orgs.py,sha256=rZ0pDAA2_9hF9N-ykRZIxPtEmczTSjuA_k3nkp0k1W0,2039
286
285
  reconcile/gql_definitions/common/jira_settings.py,sha256=Fmjxhlhr69kc4jkG_0k17fuYlQVucbNex0jXYu83wbY,1990
287
286
  reconcile/gql_definitions/common/jiralert_settings.py,sha256=H96nMg_r2YcOvioj3aIkwqtFrALGSLt7uhbx9jGSUTo,1984
287
+ reconcile/gql_definitions/common/ldap_settings.py,sha256=qkKm3BusR4FreHcI9VSwPg-hrhBKtiQIjGsTAGvHDG4,1885
288
288
  reconcile/gql_definitions/common/namespaces.py,sha256=FUgyoDAKWSetfDumfqHRVT6lOGp9hoj1-8rC0khVJI4,11071
289
289
  reconcile/gql_definitions/common/namespaces_minimal.py,sha256=XVt8LFe-bGYbjN3ysX3b9sFGmLX4snQ_A9ZouQGaaAI,3429
290
290
  reconcile/gql_definitions/common/ocm_env_telemeter.py,sha256=jW0Q9WazDQVOxh4u0LMFG69rupBioJ8HGGjvR9bVK9Y,2424
@@ -302,6 +302,7 @@ reconcile/gql_definitions/common/slack_workspaces.py,sha256=2o0kgi4QiaRuNmZJnc_B
302
302
  reconcile/gql_definitions/common/smtp_client_settings.py,sha256=JU6t6D-Qj-z1gLlgUiHKe0W7AxWQdty9jlv-ig_43tM,2248
303
303
  reconcile/gql_definitions/common/state_aws_account.py,sha256=LAdpCG2-ykVpWBPO0Zu1WvG-hwKXyDC0fJQxJRpbqCk,2198
304
304
  reconcile/gql_definitions/common/users.py,sha256=ahY3d185LbTekCGYBLJwZJljn54RJI_P5CVefdqyoZA,1705
305
+ reconcile/gql_definitions/common/users_with_paths.py,sha256=kOYzIXKG11JI0afgRMA5WLlEwJlvElAKytE8RAeuSco,2726
305
306
  reconcile/gql_definitions/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
306
307
  reconcile/gql_definitions/cost_report/app_names.py,sha256=fzqYXyiTSll359J1F1o7qapco0MSxgs3sr_Ssb2Kbns,1786
307
308
  reconcile/gql_definitions/cost_report/cost_namespaces.py,sha256=URRozAgSa9OnkqOCZf3MGH21_wcnsqYl0n-olXdjQH0,2286
@@ -466,7 +467,7 @@ reconcile/oum/base.py,sha256=WCFdHOHXLPrJcvxVqw6HjaJthT7olC5BQqqXlD4DM6c,13552
466
467
  reconcile/oum/labelset.py,sha256=f5kDndbaIT4iNYxTRPSELTUgj_aMlzEJDPzooAkG2mE,2154
467
468
  reconcile/oum/metrics.py,sha256=S_0C-hIW4jHVl9Lltgis9q-p33fdBjADWBouQ9Emeao,1575
468
469
  reconcile/oum/models.py,sha256=teH0bJTCMTzbdbYD9CU4yXDuMr34ceLcM0KuoIPU8gI,1712
469
- reconcile/oum/providers.py,sha256=3kEjXvsTPzXc7gzrdO7hWqgzcMmMZMpk2S0X7wQUTWU,1767
470
+ reconcile/oum/providers.py,sha256=lfG6d7YV-A4Lte45EMv1Gx4A346piJ_jAkrU5AHJZ_g,1834
470
471
  reconcile/oum/standalone.py,sha256=EN5y1S-3DwUZYzSRqRMtf63mI2slvBHKiU9zOTjYvWM,7334
471
472
  reconcile/prometheus_rules_tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
472
473
  reconcile/prometheus_rules_tester/integration.py,sha256=8lXZSaNqZAemulVNDxanrxwl7ZGfUxtwfptJlMPX8ac,9308
@@ -562,6 +563,7 @@ reconcile/typed_queries/jenkins.py,sha256=Pus8Rhsb04F92Iqh31xe-rW1TLiEziIWd0UAth
562
563
  reconcile/typed_queries/jira.py,sha256=jq6-ERCr_Fh96_3i9A9UKfpAAstoc4Iz6Irl-_0IUkw,235
563
564
  reconcile/typed_queries/jira_settings.py,sha256=i0ddx5xxHrM1v-9mtL_6OB-jBFLw7-HS6xenpIDjrkw,570
564
565
  reconcile/typed_queries/jiralert_settings.py,sha256=y59S5xvYmuaGxszzfKhVLjbCyDwKiaSIlajocbK5MDE,793
566
+ reconcile/typed_queries/ldap_settings.py,sha256=k8LWM11wPHn6_x2TMP7xkiwQTOPBgzuYdjzygG1WVT4,664
565
567
  reconcile/typed_queries/namespaces.py,sha256=vItPrn7sfcHOix-VvkzQkf54_ljzI_ymyxh5esdBJ5Y,262
566
568
  reconcile/typed_queries/namespaces_minimal.py,sha256=rUtqNQ0ORXXUTQfnpsMURymAJ4gYtE77V-Lb3LiJFEY,278
567
569
  reconcile/typed_queries/ocm.py,sha256=aTXW9NaMpMq-90sBUAUQmGPtk6Hnsk2rzSbXv3pD8dY,312
@@ -578,6 +580,7 @@ reconcile/typed_queries/tekton_pipeline_providers.py,sha256=LtoSnSRkuckYsXIU64L1
578
580
  reconcile/typed_queries/terraform_namespaces.py,sha256=4H9WE90jN_BVYBAt1DxJITS4vkL-vykbXZIS1H4EKNM,413
579
581
  reconcile/typed_queries/unleash.py,sha256=7HDc4owF044xM9Thx4WsXV7DZgETxJjy4lbpwmqz1vU,282
580
582
  reconcile/typed_queries/users.py,sha256=UXlaxeZAoNIugMEndfcjbkHYowUURE72aWcdmxfb3yk,377
583
+ reconcile/typed_queries/users_with_paths.py,sha256=lvW0QzTJtKkLS2O_Jm9_0mFXJFKvGlZE809LI-5ddvc,342
581
584
  reconcile/typed_queries/vault.py,sha256=lkRsmobykorof3fcrIPLz-NgvAiSOWSOZc_jXBlnl1w,274
582
585
  reconcile/typed_queries/app_interface_metrics_exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
583
586
  reconcile/typed_queries/app_interface_metrics_exporter/onboarding_status.py,sha256=X-N1WJGOL6OR9940P0_K4-YJzkL5Vg4favhYrBxXD9A,327
@@ -630,7 +633,7 @@ reconcile/utils/jjb_client.py,sha256=e5cDeNAeJMGz3sZMJ1KUIMFyLdRet0YnC0Qgj1vTPHc
630
633
  reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
631
634
  reconcile/utils/jump_host.py,sha256=gi8vGUDgdTVwJvROvRVauFxtL0YAramhbWvG70L7AY8,5137
632
635
  reconcile/utils/keycloak.py,sha256=YWSEUGrOVqFaJUk055dKUWpLDPdDRvhcmvR-lfbmxdE,3388
633
- reconcile/utils/ldap_client.py,sha256=sxvvSEwuswDexrTlLSz9GKQ4ym9OJr-yk2dnCM5cmxs,2463
636
+ reconcile/utils/ldap_client.py,sha256=HxOxpqyLRbXxOARZyBNbccceWIk6gF8PQjNQmBvjF2o,2148
634
637
  reconcile/utils/lean_terraform_client.py,sha256=X9358loxzkhwRExTeDv_NC8Q6HNr2tJK6Lx897YtJUc,4004
635
638
  reconcile/utils/make.py,sha256=QaEwucrzbl8-VHS66Wfdjfo0ubmAcvt_hZGpiGsKU50,231
636
639
  reconcile/utils/metrics.py,sha256=kiOoWO0b0mO-MDZWxyClYz9SeohQ0QU-xji0p-cSiLo,18462
@@ -675,7 +678,6 @@ reconcile/utils/vaultsecretref.py,sha256=0KUSzuvTRxPyKY919TO3-B_eYg4_76fzKvMF8j5
675
678
  reconcile/utils/vcs.py,sha256=AK35vIjx9bXYclKmvNekpaG_OETt-ZybibwV-m123xc,10186
676
679
  reconcile/utils/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
677
680
  reconcile/utils/acs/base.py,sha256=4UsDrCpAOuddL3PKNuIQYoJP1BtZQNNB8_KEX0lXneg,2532
678
- reconcile/utils/acs/notifiers.py,sha256=DlzTDM9arWQlBSiDy70y5Mf38OKVs9V0FzFe2LfOKXA,5046
679
681
  reconcile/utils/acs/policies.py,sha256=jpbi3qpGkBD_X6MfzsX12dPajUbmACmhIOz_0rDvYzs,5489
680
682
  reconcile/utils/acs/rbac.py,sha256=ugsLM9Pb7FbUbdq85E3VzXGMaB9ZovXob7tdWCxwqZ8,8808
681
683
  reconcile/utils/aws_api_typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -729,7 +731,7 @@ reconcile/utils/mr/ocm_update_recommended_version.py,sha256=p_aVP0TGrlKk9WBwgQnY
729
731
  reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=5EncHGr4QRnZgHedRfCwMYZ9CaijYzHGj7-M6lhtQRo,3004
730
732
  reconcile/utils/mr/promote_qontract.py,sha256=wgvX2CBlcZaihKJSXJ0zcEK8NGaEP2_DUQDz0STzGes,7158
731
733
  reconcile/utils/mr/update_access_report_base.py,sha256=4Grohtp44v1sSHZyIAYOwClxH8SLj_nnOrCcHPKp9p0,4361
732
- reconcile/utils/mr/user_maintenance.py,sha256=ZlR1Id_r2BUXsoerJW-0Ioh5bcbwlnQxBBhSs-ri9Dk,5099
734
+ reconcile/utils/mr/user_maintenance.py,sha256=KSfl4i0k1CqCa9mj93bvFxHeBHPRMn3sBWdGS1nNzYY,5371
733
735
  reconcile/utils/ocm/__init__.py,sha256=Y-bp8GomMpyCo0tFW6kJ78-ZG1UIupYRtBzbMWU0kwM,798
734
736
  reconcile/utils/ocm/addons.py,sha256=_LDdJ-gapM3s5exKlIUt-MlXZTAUoHezbYBU0QmvfWQ,7335
735
737
  reconcile/utils/ocm/base.py,sha256=8rZ8WilNeAfq7HRNI8kNOLB4VcYzQpqQ5gvWbS54MuM,14576
@@ -807,7 +809,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
807
809
  tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
808
810
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
809
811
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
810
- qontract_reconcile-0.10.2.dev185.dist-info/METADATA,sha256=-GBtujthD8qi9Bdj1oIRUOc5iAeTf-Xa5Lp5jpTyTPE,24627
811
- qontract_reconcile-0.10.2.dev185.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
812
- qontract_reconcile-0.10.2.dev185.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
813
- qontract_reconcile-0.10.2.dev185.dist-info/RECORD,,
812
+ qontract_reconcile-0.10.2.dev187.dist-info/METADATA,sha256=-iInkgy_ICSk6OCktOC27DWeGBEvZpYlsXpPFJ640IU,24555
813
+ qontract_reconcile-0.10.2.dev187.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
814
+ qontract_reconcile-0.10.2.dev187.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
815
+ qontract_reconcile-0.10.2.dev187.dist-info/RECORD,,
reconcile/acs_policies.py CHANGED
@@ -8,7 +8,6 @@ from reconcile.gql_definitions.acs.acs_policies import (
8
8
  AcsPolicyV1,
9
9
  )
10
10
  from reconcile.utils import gql
11
- from reconcile.utils.acs.notifiers import JiraNotifier
12
11
  from reconcile.utils.acs.policies import AcsPolicyApi, Policy, PolicyCondition, Scope
13
12
  from reconcile.utils.differ import diff_iterables
14
13
  from reconcile.utils.runtime.integration import (
@@ -63,21 +62,10 @@ class AcsPoliciesIntegration(QontractReconcileIntegration[NoParams]):
63
62
  conditions = [
64
63
  pc for c in gql_policy.conditions if (pc := self._build_policy_condition(c))
65
64
  ]
66
- jira_notifier = (
67
- notifier_name_to_id.get(
68
- JiraNotifier.from_escalation_policy(
69
- gql_policy.integrations.notifiers.jira.escalation_policy
70
- ).name
71
- )
72
- if gql_policy.integrations
73
- and gql_policy.integrations.notifiers
74
- and gql_policy.integrations.notifiers.jira
75
- else None
76
- )
77
65
  return Policy(
78
66
  name=gql_policy.name,
79
67
  description=gql_policy.description,
80
- notifiers=[jira_notifier] if jira_notifier else [],
68
+ notifiers=[],
81
69
  severity=f"{gql_policy.severity.upper()}_SEVERITY", # align with acs api severity value format
82
70
  scope=sorted(
83
71
  [
reconcile/cli.py CHANGED
@@ -3719,17 +3719,6 @@ def acs_policies(ctx):
3719
3719
  )
3720
3720
 
3721
3721
 
3722
- @integration.command(short_help="Manages RHACS notifier configurations")
3723
- @click.pass_context
3724
- def acs_notifiers(ctx):
3725
- from reconcile import acs_notifiers
3726
-
3727
- run_class_integration(
3728
- integration=acs_notifiers.AcsNotifiersIntegration(),
3729
- ctx=ctx.obj,
3730
- )
3731
-
3732
-
3733
3722
  @integration.command(short_help="Manage Unleash feature toggles.")
3734
3723
  @click.option("--instance", help="Reconcile just this Unlash instance.", default=None)
3735
3724
  @click.pass_context
reconcile/github_users.py CHANGED
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
  import os
3
3
  import re
4
+ from collections import defaultdict
4
5
  from collections.abc import Callable
5
6
 
6
7
  from github import Github
@@ -17,9 +18,9 @@ from reconcile import (
17
18
  typed_queries,
18
19
  )
19
20
  from reconcile.github_org import get_default_config
20
- from reconcile.ldap_users import init_users as init_users_and_paths
21
21
  from reconcile.utils.defer import defer
22
22
  from reconcile.utils.mr import CreateDeleteUserAppInterface
23
+ from reconcile.utils.mr.user_maintenance import PathSpec, PathTypes
23
24
  from reconcile.utils.secret_reader import SecretReader
24
25
  from reconcile.utils.smtp_client import (
25
26
  DEFAULT_SMTP_TIMEOUT,
@@ -39,6 +40,33 @@ def init_github() -> Github:
39
40
  return Github(token, base_url=GH_BASE_URL)
40
41
 
41
42
 
43
+ def init_users_and_paths() -> list[dict[str, list]]:
44
+ app_int_users = queries.get_users(refs=True)
45
+
46
+ users = defaultdict(list)
47
+ for user in app_int_users:
48
+ u = user["org_username"]
49
+ item = PathSpec(type=PathTypes.USER, path=user["path"])
50
+ users[u].append(item)
51
+ for r in user.get("requests"):
52
+ item = PathSpec(type=PathTypes.REQUEST, path=r["path"])
53
+ users[u].append(item)
54
+ for q in user.get("queries"):
55
+ item = PathSpec(type=PathTypes.QUERY, path=q["path"])
56
+ users[u].append(item)
57
+ for g in user.get("gabi_instances"):
58
+ item = PathSpec(type=PathTypes.GABI, path=g["path"])
59
+ users[u].append(item)
60
+ for a in user.get("aws_accounts", []):
61
+ item = PathSpec(type=PathTypes.AWS_ACCOUNTS, path=a["path"])
62
+ users[u].append(item)
63
+ for s in user.get("schedules"):
64
+ item = PathSpec(type=PathTypes.SCHEDULE, path=s["path"])
65
+ users[u].append(item)
66
+
67
+ return [{"username": username, "paths": paths} for username, paths in users.items()]
68
+
69
+
42
70
  @retry(exceptions=(GithubException, ReadTimeout))
43
71
  def get_user_company(user: dict, github: Github) -> UserAndCompany:
44
72
  gh_user = github.get_user(login=user["github_username"])
@@ -24,40 +24,6 @@ query AcsPolicy {
24
24
  name
25
25
  description
26
26
  severity
27
- integrations {
28
- notifiers {
29
- jira {
30
- escalationPolicy {
31
- name
32
- channels {
33
- jiraBoard {
34
- name
35
- server {
36
- serverUrl
37
- }
38
- severityPriorityMappings {
39
- name
40
- mappings {
41
- severity
42
- priority
43
- }
44
- }
45
- issueType
46
- issueFields {
47
- name
48
- value
49
- }
50
- disable {
51
- integrations
52
- }
53
- }
54
- jiraComponent
55
- jiraLabels
56
- }
57
- }
58
- }
59
- }
60
- }
61
27
  categories
62
28
  scope {
63
29
  level
@@ -107,61 +73,6 @@ class ConfiguredBaseModel(BaseModel):
107
73
  extra=Extra.forbid
108
74
 
109
75
 
110
- class JiraServerV1(ConfiguredBaseModel):
111
- server_url: str = Field(..., alias="serverUrl")
112
-
113
-
114
- class SeverityPriorityMappingV1(ConfiguredBaseModel):
115
- severity: str = Field(..., alias="severity")
116
- priority: str = Field(..., alias="priority")
117
-
118
-
119
- class JiraSeverityPriorityMappingsV1(ConfiguredBaseModel):
120
- name: str = Field(..., alias="name")
121
- mappings: list[SeverityPriorityMappingV1] = Field(..., alias="mappings")
122
-
123
-
124
- class JiraBoardIssueFieldV1(ConfiguredBaseModel):
125
- name: str = Field(..., alias="name")
126
- value: str = Field(..., alias="value")
127
-
128
-
129
- class DisableJiraBoardAutomationsV1(ConfiguredBaseModel):
130
- integrations: Optional[list[str]] = Field(..., alias="integrations")
131
-
132
-
133
- class JiraBoardV1(ConfiguredBaseModel):
134
- name: str = Field(..., alias="name")
135
- server: JiraServerV1 = Field(..., alias="server")
136
- severity_priority_mappings: JiraSeverityPriorityMappingsV1 = Field(..., alias="severityPriorityMappings")
137
- issue_type: Optional[str] = Field(..., alias="issueType")
138
- issue_fields: Optional[list[JiraBoardIssueFieldV1]] = Field(..., alias="issueFields")
139
- disable: Optional[DisableJiraBoardAutomationsV1] = Field(..., alias="disable")
140
-
141
-
142
- class AppEscalationPolicyChannelsV1(ConfiguredBaseModel):
143
- jira_board: list[JiraBoardV1] = Field(..., alias="jiraBoard")
144
- jira_component: Optional[str] = Field(..., alias="jiraComponent")
145
- jira_labels: Optional[list[str]] = Field(..., alias="jiraLabels")
146
-
147
-
148
- class AppEscalationPolicyV1(ConfiguredBaseModel):
149
- name: str = Field(..., alias="name")
150
- channels: AppEscalationPolicyChannelsV1 = Field(..., alias="channels")
151
-
152
-
153
- class AcsPolicyIntegrationNotifierJiraV1(ConfiguredBaseModel):
154
- escalation_policy: AppEscalationPolicyV1 = Field(..., alias="escalationPolicy")
155
-
156
-
157
- class AcsPolicyIntegrationNotifiersV1(ConfiguredBaseModel):
158
- jira: Optional[AcsPolicyIntegrationNotifierJiraV1] = Field(..., alias="jira")
159
-
160
-
161
- class AcsPolicyIntegrationsV1(ConfiguredBaseModel):
162
- notifiers: Optional[AcsPolicyIntegrationNotifiersV1] = Field(..., alias="notifiers")
163
-
164
-
165
76
  class AcsPolicyScopeV1(ConfiguredBaseModel):
166
77
  level: str = Field(..., alias="level")
167
78
 
@@ -218,7 +129,6 @@ class AcsPolicyV1(ConfiguredBaseModel):
218
129
  name: str = Field(..., alias="name")
219
130
  description: Optional[str] = Field(..., alias="description")
220
131
  severity: str = Field(..., alias="severity")
221
- integrations: Optional[AcsPolicyIntegrationsV1] = Field(..., alias="integrations")
222
132
  categories: list[str] = Field(..., alias="categories")
223
133
  scope: Union[AcsPolicyScopeClusterV1, AcsPolicyScopeNamespaceV1, AcsPolicyScopeV1] = Field(..., alias="scope")
224
134
  conditions: list[Union[AcsPolicyConditionsCvssV1, AcsPolicyConditionsSeverityV1, AcsPolicyConditionsImageTagV1, AcsPolicyConditionsCveV1, AcsPolicyConditionsImageAgeV1, AcsPolicyConditionsV1]] = Field(..., alias="conditions")
@@ -0,0 +1,68 @@
1
+ """
2
+ Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
3
+ """
4
+ from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
5
+ from datetime import datetime # noqa: F401 # pylint: disable=W0611
6
+ from enum import Enum # noqa: F401 # pylint: disable=W0611
7
+ from typing import ( # noqa: F401 # pylint: disable=W0611
8
+ Any,
9
+ Optional,
10
+ Union,
11
+ )
12
+
13
+ from pydantic import ( # noqa: F401 # pylint: disable=W0611
14
+ BaseModel,
15
+ Extra,
16
+ Field,
17
+ Json,
18
+ )
19
+
20
+
21
+ DEFINITION = """
22
+ query LdapSettings {
23
+ settings: app_interface_settings_v1 {
24
+ ldap {
25
+ serverUrl
26
+ baseDn
27
+ }
28
+ }
29
+ }
30
+ """
31
+
32
+
33
+ class ConfiguredBaseModel(BaseModel):
34
+ class Config:
35
+ smart_union=True
36
+ extra=Extra.forbid
37
+
38
+
39
+ class LdapSettingsV1(ConfiguredBaseModel):
40
+ server_url: str = Field(..., alias="serverUrl")
41
+ base_dn: str = Field(..., alias="baseDn")
42
+
43
+
44
+ class AppInterfaceSettingsV1(ConfiguredBaseModel):
45
+ ldap: Optional[LdapSettingsV1] = Field(..., alias="ldap")
46
+
47
+
48
+ class LdapSettingsQueryData(ConfiguredBaseModel):
49
+ settings: Optional[list[AppInterfaceSettingsV1]] = Field(..., alias="settings")
50
+
51
+
52
+ def query(query_func: Callable, **kwargs: Any) -> LdapSettingsQueryData:
53
+ """
54
+ This is a convenience function which queries and parses the data into
55
+ concrete types. It should be compatible with most GQL clients.
56
+ You do not have to use it to consume the generated data classes.
57
+ Alternatively, you can also mime and alternate the behavior
58
+ of this function in the caller.
59
+
60
+ Parameters:
61
+ query_func (Callable): Function which queries your GQL Server
62
+ kwargs: optional arguments that will be passed to the query function
63
+
64
+ Returns:
65
+ LdapSettingsQueryData: queried data parsed into generated classes
66
+ """
67
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
68
+ return LdapSettingsQueryData(**raw_data)
@@ -0,0 +1,102 @@
1
+ """
2
+ Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
3
+ """
4
+ from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
5
+ from datetime import datetime # noqa: F401 # pylint: disable=W0611
6
+ from enum import Enum # noqa: F401 # pylint: disable=W0611
7
+ from typing import ( # noqa: F401 # pylint: disable=W0611
8
+ Any,
9
+ Optional,
10
+ Union,
11
+ )
12
+
13
+ from pydantic import ( # noqa: F401 # pylint: disable=W0611
14
+ BaseModel,
15
+ Extra,
16
+ Field,
17
+ Json,
18
+ )
19
+
20
+
21
+ DEFINITION = """
22
+ query UsersWithPaths {
23
+ users: users_v1 {
24
+ path
25
+ org_username
26
+ aws_accounts {
27
+ path
28
+ }
29
+ requests {
30
+ path
31
+ }
32
+ queries {
33
+ path
34
+ }
35
+ gabi_instances {
36
+ path
37
+ }
38
+ schedules {
39
+ path
40
+ }
41
+ }
42
+ }
43
+ """
44
+
45
+
46
+ class ConfiguredBaseModel(BaseModel):
47
+ class Config:
48
+ smart_union=True
49
+ extra=Extra.forbid
50
+
51
+
52
+ class AWSAccountV1(ConfiguredBaseModel):
53
+ path: str = Field(..., alias="path")
54
+
55
+
56
+ class CredentialsRequestV1(ConfiguredBaseModel):
57
+ path: str = Field(..., alias="path")
58
+
59
+
60
+ class AppInterfaceSqlQueryV1(ConfiguredBaseModel):
61
+ path: str = Field(..., alias="path")
62
+
63
+
64
+ class GabiInstanceV1(ConfiguredBaseModel):
65
+ path: str = Field(..., alias="path")
66
+
67
+
68
+ class ScheduleV1(ConfiguredBaseModel):
69
+ path: str = Field(..., alias="path")
70
+
71
+
72
+ class UserV1(ConfiguredBaseModel):
73
+ path: str = Field(..., alias="path")
74
+ org_username: str = Field(..., alias="org_username")
75
+ aws_accounts: Optional[list[AWSAccountV1]] = Field(..., alias="aws_accounts")
76
+ requests: Optional[list[CredentialsRequestV1]] = Field(..., alias="requests")
77
+ queries: Optional[list[AppInterfaceSqlQueryV1]] = Field(..., alias="queries")
78
+ gabi_instances: Optional[list[GabiInstanceV1]] = Field(..., alias="gabi_instances")
79
+ schedules: Optional[list[ScheduleV1]] = Field(..., alias="schedules")
80
+
81
+
82
+ class UsersWithPathsQueryData(ConfiguredBaseModel):
83
+ users: Optional[list[UserV1]] = Field(..., alias="users")
84
+
85
+
86
+ def query(query_func: Callable, **kwargs: Any) -> UsersWithPathsQueryData:
87
+ """
88
+ This is a convenience function which queries and parses the data into
89
+ concrete types. It should be compatible with most GQL clients.
90
+ You do not have to use it to consume the generated data classes.
91
+ Alternatively, you can also mime and alternate the behavior
92
+ of this function in the caller.
93
+
94
+ Parameters:
95
+ query_func (Callable): Function which queries your GQL Server
96
+ kwargs: optional arguments that will be passed to the query function
97
+
98
+ Returns:
99
+ UsersWithPathsQueryData: queried data parsed into generated classes
100
+ """
101
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
102
+ return UsersWithPathsQueryData(**raw_data)
reconcile/ldap_users.py CHANGED
@@ -1,99 +1,126 @@
1
1
  import logging
2
- from collections import defaultdict
2
+ from collections.abc import Callable
3
+
4
+ from pydantic import BaseModel, Field
3
5
 
4
6
  from reconcile import (
5
7
  mr_client_gateway,
6
- queries,
7
8
  )
8
- from reconcile.utils import gql
9
+ from reconcile.gql_definitions.common.ldap_settings import LdapSettingsV1
10
+ from reconcile.gql_definitions.common.users_with_paths import UserV1
11
+ from reconcile.typed_queries.ldap_settings import get_ldap_settings
12
+ from reconcile.typed_queries.users_with_paths import get_users_with_paths
9
13
  from reconcile.utils.defer import defer
10
14
  from reconcile.utils.ldap_client import LdapClient
11
15
  from reconcile.utils.mr import (
12
16
  CreateDeleteUserAppInterface,
13
17
  CreateDeleteUserInfra,
14
18
  )
15
- from reconcile.utils.mr.user_maintenance import PathTypes
19
+ from reconcile.utils.mr.user_maintenance import PathSpec, PathTypes
16
20
 
17
21
  QONTRACT_INTEGRATION = "ldap-users"
18
22
 
19
23
 
20
- def init_users() -> list[dict[str, list]]:
21
- app_int_users = queries.get_users(refs=True)
22
-
23
- users = defaultdict(list)
24
- for user in app_int_users:
25
- u = user["org_username"]
26
- item = {"type": PathTypes.USER, "path": "data" + user["path"]}
27
- users[u].append(item)
28
- for r in user.get("requests"):
29
- item = {"type": PathTypes.REQUEST, "path": "data" + r["path"]}
30
- users[u].append(item)
31
- for q in user.get("queries"):
32
- item = {"type": PathTypes.QUERY, "path": "data" + q["path"]}
33
- users[u].append(item)
34
- for g in user.get("gabi_instances"):
35
- item = {"type": PathTypes.GABI, "path": "data" + g["path"]}
36
- users[u].append(item)
37
- for a in user.get("aws_accounts", []):
38
- item = {"type": PathTypes.AWS_ACCOUNTS, "path": "data" + a["path"]}
39
- users[u].append(item)
40
- for s in user.get("schedules"):
41
- item = {"type": PathTypes.SCHEDULE, "path": "data" + s["path"]}
42
- users[u].append(item)
43
-
44
- return [{"username": username, "paths": paths} for username, paths in users.items()]
45
-
46
-
47
- LDAP_SETTINGS_QUERY = """
48
- {
49
- settings: app_interface_settings_v1 {
50
- ldap {
51
- serverUrl
52
- baseDn
53
- }
54
- }
55
- }
56
- """
57
-
58
-
59
- def get_ldap_settings() -> dict:
60
- """Returns LDAP settings"""
61
- gqlapi = gql.get_api()
62
- settings = gqlapi.query(LDAP_SETTINGS_QUERY)["settings"]
63
- if settings:
64
- # assuming a single settings file for now
65
- return settings[0]
66
- raise ValueError("no app-interface-settings settings found")
24
+ class UserPaths(BaseModel):
25
+ username: str
26
+ paths: list[PathSpec] = Field(default_factory=list)
67
27
 
68
28
 
69
- @defer
70
- def run(dry_run, app_interface_project_id, infra_project_id, defer=None):
71
- users = init_users()
72
- with LdapClient.from_settings(get_ldap_settings()) as ldap_client:
73
- ldap_users = ldap_client.get_users([u["username"] for u in users])
29
+ def transform_users_with_paths(users_with_paths: list[UserV1]) -> list[UserPaths]:
30
+ """Converts UserV1 objects into UserPaths, consolidating all associated resource paths by type."""
31
+
32
+ users_paths = []
33
+ for user in users_with_paths:
34
+ up = UserPaths(username=user.org_username)
35
+ up.paths.append(PathSpec(type=PathTypes.USER, path=user.path))
36
+ for r in user.requests or []:
37
+ up.paths.append(PathSpec(type=PathTypes.REQUEST, path=r.path))
38
+ for q in user.queries or []:
39
+ up.paths.append(PathSpec(type=PathTypes.QUERY, path=q.path))
40
+ for g in user.gabi_instances or []:
41
+ up.paths.append(PathSpec(type=PathTypes.GABI, path=g.path))
42
+ for a in user.aws_accounts or []:
43
+ up.paths.append(PathSpec(type=PathTypes.AWS_ACCOUNTS, path=a.path))
44
+ for s in user.schedules or []:
45
+ up.paths.append(PathSpec(type=PathTypes.SCHEDULE, path=s.path))
46
+
47
+ users_paths.append(up)
48
+
49
+ return users_paths
50
+
51
+
52
+ def get_usernames(users_paths: list[UserPaths]) -> list[str]:
53
+ """Extracts a list of usernames from a list of UserPaths objects."""
54
+ return [u.username for u in users_paths]
55
+
56
+
57
+ def filter_users_not_exists(
58
+ users_paths: list[UserPaths], ldap_users: set[str]
59
+ ) -> list[UserPaths]:
60
+ """Filters out UserPaths objects whose usernames are not present in the given set of LDAP users."""
61
+
62
+ return [u for u in users_paths if u.username not in ldap_users]
63
+
74
64
 
75
- users_to_delete = [u for u in users if u["username"] not in ldap_users]
65
+ def get_ldap_users(ldap_settings: LdapSettingsV1, usernames: list[str]) -> set[str]:
66
+ """Retrieves existing usernames from LDAP based on the provided list and connection settings."""
67
+ with LdapClient.from_params(
68
+ server_url=ldap_settings.server_url,
69
+ user=None,
70
+ password=None,
71
+ base_dn=ldap_settings.base_dn,
72
+ ) as ldap_client:
73
+ return ldap_client.get_users(usernames)
76
74
 
75
+
76
+ @defer
77
+ def delete_user_from_app_interface(
78
+ dry_run: bool,
79
+ app_interface_project_id: str | int | None,
80
+ users: list[UserPaths],
81
+ defer: Callable | None = None,
82
+ ) -> None:
83
+ """Delete user data stored in the repository app-interface related with the given users."""
77
84
  if not dry_run:
78
85
  mr_cli_app_interface = mr_client_gateway.init(
79
86
  gitlab_project_id=app_interface_project_id, sqs_or_gitlab="gitlab"
80
87
  )
81
- defer(mr_cli_app_interface.cleanup)
82
- mr_cli_infra = mr_client_gateway.init(
83
- gitlab_project_id=infra_project_id, sqs_or_gitlab="gitlab"
84
- )
85
- defer(mr_cli_infra.cleanup)
88
+ if defer:
89
+ defer(mr_cli_app_interface.cleanup)
86
90
 
87
- for u in users_to_delete:
88
- username = u["username"]
89
- paths = u["paths"]
90
- logging.info(["delete_user", username])
91
+ for user in users:
92
+ logging.info(["delete_user", user.username])
91
93
 
92
94
  if not dry_run:
93
- mr = CreateDeleteUserAppInterface(username, paths)
95
+ mr = CreateDeleteUserAppInterface(user.username, user.paths)
94
96
  mr.submit(cli=mr_cli_app_interface)
95
97
 
98
+
99
+ def delete_user_from_infra(
100
+ dry_run: bool,
101
+ infra_project_id: str | int | None,
102
+ usernames: list[str],
103
+ ) -> None:
104
+ """Delete user data stored in the repository infra related with the given usernames."""
96
105
  if not dry_run:
97
- usernames = [u["username"] for u in users_to_delete]
98
- mr_infra = CreateDeleteUserInfra(usernames)
99
- mr_infra.submit(cli=mr_cli_infra)
106
+ with mr_client_gateway.init(
107
+ gitlab_project_id=infra_project_id, sqs_or_gitlab="gitlab"
108
+ ) as mr_cli_infra:
109
+ mr_infra = CreateDeleteUserInfra(usernames)
110
+ mr_infra.submit(cli=mr_cli_infra)
111
+
112
+
113
+ def run(dry_run: bool, app_interface_project_id: str, infra_project_id: str) -> None:
114
+ """
115
+ Synchronizes user data stored in the repositories app_interface and infra
116
+ associated with users that are no longer found in the LDAP.
117
+ """
118
+ users_paths = transform_users_with_paths(get_users_with_paths())
119
+
120
+ ldap_users = get_ldap_users(get_ldap_settings(), get_usernames(users_paths))
121
+
122
+ users_to_delete = filter_users_not_exists(users_paths, ldap_users)
123
+ usernames_to_delete = get_usernames(users_to_delete)
124
+
125
+ delete_user_from_app_interface(dry_run, app_interface_project_id, users_to_delete)
126
+ delete_user_from_infra(dry_run, infra_project_id, usernames_to_delete)
@@ -3,7 +3,7 @@ from abc import (
3
3
  abstractmethod,
4
4
  )
5
5
 
6
- from reconcile.ldap_users import get_ldap_settings
6
+ from reconcile.typed_queries.ldap_settings import get_ldap_settings
7
7
  from reconcile.utils.ldap_client import LdapClient
8
8
 
9
9
 
@@ -53,7 +53,10 @@ def init_ldap_group_member_provider(group_base_dn: str) -> LdapGroupMemberProvid
53
53
  settings = get_ldap_settings()
54
54
  return LdapGroupMemberProvider(
55
55
  LdapClient.from_params(
56
- settings["ldap"]["serverUrl"], None, None, settings["ldap"]["baseDn"]
56
+ server_url=settings.server_url,
57
+ user=None,
58
+ password=None,
59
+ base_dn=settings.base_dn,
57
60
  ),
58
61
  group_base_dn,
59
62
  )
@@ -0,0 +1,20 @@
1
+ from collections.abc import Callable
2
+
3
+ from reconcile.gql_definitions.common.ldap_settings import (
4
+ LdapSettingsV1,
5
+ query,
6
+ )
7
+ from reconcile.utils import gql
8
+ from reconcile.utils.exceptions import AppInterfaceSettingsError
9
+
10
+
11
+ def get_ldap_settings(
12
+ query_func: Callable | None = None,
13
+ ) -> LdapSettingsV1:
14
+ """Returns App Interface Settings and raises err if none are found"""
15
+ if not query_func:
16
+ query_func = gql.get_api().query
17
+ data = query(query_func)
18
+ if data.settings and len(data.settings) == 1 and data.settings[0].ldap:
19
+ return data.settings[0].ldap
20
+ raise AppInterfaceSettingsError("Ldap settings is not defined.")
@@ -0,0 +1,10 @@
1
+ from collections.abc import Callable
2
+
3
+ from reconcile.gql_definitions.common.users_with_paths import UserV1, query
4
+ from reconcile.utils import gql
5
+
6
+
7
+ def get_users_with_paths(query_func: Callable | None = None) -> list[UserV1]:
8
+ if not query_func:
9
+ query_func = gql.get_api().query
10
+ return query(query_func=query_func).users or []
@@ -56,13 +56,6 @@ class LdapClient:
56
56
 
57
57
  return dict(groups_and_members)
58
58
 
59
- @classmethod
60
- def from_settings(cls, settings: dict) -> "LdapClient":
61
- """Requires a nested dictionary with key 'ldap' in addition sub keys 'serverUrl' and 'baseDn'."""
62
- return LdapClient.from_params(
63
- settings["ldap"]["serverUrl"], None, None, settings["ldap"]["baseDn"]
64
- )
65
-
66
59
  @classmethod
67
60
  def from_params(
68
61
  cls, server_url: str, user: str | None, password: str | None, base_dn: str
@@ -1,5 +1,8 @@
1
+ from collections.abc import Iterable
2
+ from enum import Enum
1
3
  from pathlib import Path
2
4
 
5
+ from pydantic import BaseModel, validator
3
6
  from ruamel import yaml
4
7
 
5
8
  from reconcile.utils.gitlab_api import GitLabApi
@@ -7,7 +10,7 @@ from reconcile.utils.mr.base import MergeRequestBase
7
10
  from reconcile.utils.mr.labels import AUTO_MERGE
8
11
 
9
12
 
10
- class PathTypes:
13
+ class PathTypes(Enum):
11
14
  USER = 0
12
15
  REQUEST = 1
13
16
  QUERY = 2
@@ -16,10 +19,19 @@ class PathTypes:
16
19
  SCHEDULE = 5
17
20
 
18
21
 
22
+ class PathSpec(BaseModel):
23
+ type: PathTypes
24
+ path: str
25
+
26
+ @validator("path")
27
+ def prepend_data_to_path(cls, v):
28
+ return "data" + v
29
+
30
+
19
31
  class CreateDeleteUserAppInterface(MergeRequestBase):
20
32
  name = "create_delete_user_mr"
21
33
 
22
- def __init__(self, username, paths):
34
+ def __init__(self, username, paths: Iterable[PathSpec]):
23
35
  self.username = username
24
36
  self.paths = paths
25
37
 
@@ -37,8 +49,8 @@ class CreateDeleteUserAppInterface(MergeRequestBase):
37
49
 
38
50
  def process(self, gitlab_cli: GitLabApi) -> None:
39
51
  for path_spec in self.paths:
40
- path_type = path_spec["type"]
41
- path = path_spec["path"]
52
+ path_type = path_spec.type
53
+ path = path_spec.path
42
54
  if path_type in {PathTypes.USER, PathTypes.REQUEST, PathTypes.QUERY}:
43
55
  gitlab_cli.delete_file(
44
56
  branch_name=self.branch, file_path=path, commit_message=self.title
@@ -1,126 +0,0 @@
1
- import logging
2
-
3
- import reconcile.gql_definitions.acs.acs_policies as gql_acs_policies
4
- from reconcile.gql_definitions.jira.jira_servers import (
5
- JiraServerV1,
6
- )
7
- from reconcile.gql_definitions.jira.jira_servers import (
8
- query as query_jira_servers,
9
- )
10
- from reconcile.utils import gql
11
- from reconcile.utils.acs.notifiers import (
12
- AcsNotifiersApi,
13
- JiraCredentials,
14
- JiraNotifier,
15
- )
16
- from reconcile.utils.differ import diff_iterables
17
- from reconcile.utils.disabled_integrations import integration_is_enabled
18
- from reconcile.utils.runtime.integration import (
19
- NoParams,
20
- QontractReconcileIntegration,
21
- )
22
- from reconcile.utils.semver_helper import make_semver
23
-
24
-
25
- class AcsNotifiersIntegration(QontractReconcileIntegration[NoParams]):
26
- def __init__(self) -> None:
27
- super().__init__(NoParams())
28
- self.qontract_integration = "acs-notifiers"
29
- self.qontract_integration_version = make_semver(0, 1, 0)
30
-
31
- @property
32
- def name(self) -> str:
33
- return self.qontract_integration
34
-
35
- def _get_escalation_policies(
36
- self, acs_policies: list[gql_acs_policies.AcsPolicyV1]
37
- ) -> list[gql_acs_policies.AppEscalationPolicyV1]:
38
- return list(
39
- {
40
- p.integrations.notifiers.jira.escalation_policy.name: p.integrations.notifiers.jira.escalation_policy
41
- for p in acs_policies
42
- if p.integrations
43
- and p.integrations.notifiers
44
- and p.integrations.notifiers.jira
45
- and integration_is_enabled(
46
- self.qontract_integration,
47
- p.integrations.notifiers.jira.escalation_policy.channels.jira_board[
48
- 0
49
- ],
50
- )
51
- }.values()
52
- )
53
-
54
- def get_desired_state(
55
- self, acs_policies: list[gql_acs_policies.AcsPolicyV1]
56
- ) -> list[JiraNotifier]:
57
- return [
58
- JiraNotifier.from_escalation_policy(ep)
59
- for ep in self._get_escalation_policies(acs_policies)
60
- ]
61
-
62
- def get_jira_credentials(
63
- self, jira_servers: list[JiraServerV1]
64
- ) -> dict[str, JiraCredentials]:
65
- return {
66
- server.server_url: JiraCredentials(
67
- url=server.server_url,
68
- username=server.username,
69
- token=self.secret_reader.read_secret(server.token),
70
- )
71
- for server in jira_servers
72
- }
73
-
74
- def reconcile(
75
- self,
76
- current_state: list[JiraNotifier],
77
- desired_state: list[JiraNotifier],
78
- acs_api: AcsNotifiersApi,
79
- jira_credentials: dict[str, JiraCredentials],
80
- dry_run: bool,
81
- ) -> None:
82
- diff = diff_iterables(
83
- current=current_state, desired=desired_state, key=lambda x: x.name
84
- )
85
- for a in diff.add.values():
86
- logging.info(f"Create Jira notifier: {a.name}")
87
- if not dry_run:
88
- acs_api.create_jira_notifier(
89
- a,
90
- jira_credentials=jira_credentials[a.url],
91
- )
92
- for c in diff.change.values():
93
- logging.info(f"Update Jira notifier: {c.desired.name}")
94
- if not dry_run:
95
- acs_api.update_jira_notifier(
96
- c.desired,
97
- jira_credentials=jira_credentials[c.desired.url],
98
- )
99
- for d in diff.delete.values():
100
- logging.info(f"Delete Jira notifier: {d.name}")
101
- if not dry_run:
102
- acs_api.delete_jira_notifier(d)
103
-
104
- def run(
105
- self,
106
- dry_run: bool,
107
- ) -> None:
108
- gql_api_query = gql.get_api().query
109
- jira_credentials = self.get_jira_credentials(
110
- query_jira_servers(query_func=gql_api_query).jira_servers or []
111
- )
112
- desired_state = self.get_desired_state(
113
- gql_acs_policies.query(query_func=gql_api_query).acs_policies or []
114
- )
115
- instance = AcsNotifiersApi.get_acs_instance(query_func=gql_api_query)
116
- with AcsNotifiersApi(
117
- url=instance.url, token=self.secret_reader.read_secret(instance.credentials)
118
- ) as acs_api:
119
- current_state = acs_api.get_jira_notifiers()
120
- self.reconcile(
121
- current_state=current_state,
122
- desired_state=desired_state,
123
- acs_api=acs_api,
124
- jira_credentials=jira_credentials,
125
- dry_run=dry_run,
126
- )
@@ -1,145 +0,0 @@
1
- import json
2
- from typing import Any
3
-
4
- from pydantic import BaseModel
5
-
6
- from reconcile.gql_definitions.acs.acs_policies import AppEscalationPolicyV1
7
- from reconcile.utils.acs.base import AcsBaseApi
8
-
9
-
10
- class JiraCredentials(BaseModel):
11
- url: str
12
- username: str
13
- token: str
14
-
15
-
16
- class SeverityPriorityMapping(BaseModel):
17
- severity: str
18
- priority: str
19
-
20
- @staticmethod
21
- def from_api(mapping: dict[str, str]) -> "SeverityPriorityMapping":
22
- return SeverityPriorityMapping(
23
- severity=mapping["severity"].replace("_SEVERITY", "").lower(),
24
- priority=mapping["priorityName"],
25
- )
26
-
27
- def to_api(self) -> Any:
28
- return {
29
- "severity": f"{self.severity.upper()}_SEVERITY",
30
- "priorityName": self.priority,
31
- }
32
-
33
-
34
- class JiraNotifier(BaseModel):
35
- name: str
36
- board: str
37
- url: str
38
- issue_type: str | None
39
- severity_priority_mappings: list[SeverityPriorityMapping]
40
- custom_fields: dict[str, Any] | None
41
-
42
- @staticmethod
43
- def from_api(notifier: dict[str, Any]) -> "JiraNotifier":
44
- notifier_jira = notifier["jira"]
45
- return JiraNotifier(
46
- name=notifier["name"],
47
- board=notifier["labelDefault"],
48
- url=notifier_jira["url"],
49
- issue_type=notifier_jira["issueType"],
50
- severity_priority_mappings=sorted(
51
- [
52
- SeverityPriorityMapping.from_api(mapping)
53
- for mapping in notifier_jira["priorityMappings"]
54
- ],
55
- key=lambda m: m.severity,
56
- ),
57
- custom_fields=json.loads(notifier_jira.get("defaultFieldsJson") or "{}"),
58
- )
59
-
60
- def to_api(self, ui_endpoint: str, jira_credentials: JiraCredentials) -> Any:
61
- return {
62
- "name": self.name,
63
- "type": "jira",
64
- "uiEndpoint": ui_endpoint,
65
- "labelDefault": self.board,
66
- "jira": {
67
- "url": jira_credentials.url,
68
- "username": jira_credentials.username,
69
- "password": jira_credentials.token,
70
- "issueType": self.issue_type or "Task",
71
- "priorityMappings": [
72
- mapping.to_api() for mapping in self.severity_priority_mappings
73
- ],
74
- "defaultFieldsJson": json.dumps(self.custom_fields or {}),
75
- },
76
- }
77
-
78
- @staticmethod
79
- def from_escalation_policy(
80
- escalation_policy: AppEscalationPolicyV1,
81
- ) -> "JiraNotifier":
82
- jira_board = escalation_policy.channels.jira_board[0]
83
-
84
- custom_fields: dict[str, Any] = {}
85
- for field in jira_board.issue_fields or []:
86
- if field.name == "Security Level":
87
- custom_fields["security"] = {"name": field.value}
88
-
89
- if escalation_policy.channels.jira_component:
90
- custom_fields["components"] = [
91
- {"name": escalation_policy.channels.jira_component}
92
- ]
93
- if escalation_policy.channels.jira_labels:
94
- custom_fields["labels"] = escalation_policy.channels.jira_labels
95
-
96
- return JiraNotifier(
97
- name=f"jira-{escalation_policy.name}",
98
- board=jira_board.name,
99
- url=jira_board.server.server_url,
100
- issue_type=jira_board.issue_type or "Task",
101
- severity_priority_mappings=sorted(
102
- [
103
- SeverityPriorityMapping(**vars(sp))
104
- for sp in jira_board.severity_priority_mappings.mappings
105
- ],
106
- key=lambda m: m.severity,
107
- ),
108
- custom_fields=custom_fields,
109
- )
110
-
111
-
112
- class AcsNotifiersApi(AcsBaseApi):
113
- """
114
- Implements methods to support reconcile operations against the ACS NotifiersService api
115
- """
116
-
117
- def get_notifiers(self) -> list[Any]:
118
- return self.generic_request_json("/v1/notifiers", "GET")["notifiers"]
119
-
120
- def get_jira_notifiers(self) -> list[JiraNotifier]:
121
- return [
122
- JiraNotifier.from_api(notifier)
123
- for notifier in self.get_notifiers()
124
- if notifier["type"] == "jira"
125
- ]
126
-
127
- def get_notifier_id_by_name(self, name: str) -> str:
128
- return next(n["id"] for n in self.get_notifiers() if n["name"] == name)
129
-
130
- def update_jira_notifier(
131
- self, jira_notifier: JiraNotifier, jira_credentials: JiraCredentials
132
- ) -> None:
133
- notifier_id = self.get_notifier_id_by_name(jira_notifier.name)
134
- body = jira_notifier.to_api(self.url, jira_credentials)
135
- self.generic_request(f"/v1/notifiers/{notifier_id}", "PUT", body)
136
-
137
- def create_jira_notifier(
138
- self, jira_notifier: JiraNotifier, jira_credentials: JiraCredentials
139
- ) -> None:
140
- body = jira_notifier.to_api(self.url, jira_credentials)
141
- self.generic_request("/v1/notifiers", "POST", body)
142
-
143
- def delete_jira_notifier(self, jira_notifier: JiraNotifier) -> None:
144
- notifier_id = self.get_notifier_id_by_name(jira_notifier.name)
145
- self.generic_request(f"/v1/notifiers/{notifier_id}", "DELETE")