qontract-reconcile 0.10.1rc791__py3-none-any.whl → 0.10.1rc793__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc791
3
+ Version: 0.10.1rc793
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
@@ -10,7 +10,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
10
10
  reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
11
11
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
12
12
  reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
13
- reconcile/cli.py,sha256=uMjslj9XkN1UFJ3k-CuHlTz4MeyXMk6gK1BkWfg6Kt4,99967
13
+ reconcile/cli.py,sha256=IQEXC-Fg-c-9h3j_9JewMYMqgSLkJKT8ChWAHXmtt3k,100436
14
14
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
15
15
  reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
16
16
  reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
@@ -62,7 +62,7 @@ reconcile/ocm_groups.py,sha256=CluPyvmwE5JOZS2HQSReC1sD8L1ChhnJlAg8lcwdtxc,3395
62
62
  reconcile/ocm_machine_pools.py,sha256=oY4oLPm5Y_ajBV8KFg2LuBQvsZl-CxTjSxEyxg4b2OI,16634
63
63
  reconcile/ocm_update_recommended_version.py,sha256=IYkfLXIprOW1jguZeELcGP1iBPuj-b53R-FTqKulMl8,4204
64
64
  reconcile/ocm_upgrade_scheduler_org_updater.py,sha256=49Ss6sp9_n5F9914gXb-uEap4Vm2t-KPTJRFFViJMIo,4184
65
- reconcile/openshift_base.py,sha256=AYTXEfQaU91PRpNJKunf0PILL_zAJMq6jySYfGHzcNg,49702
65
+ reconcile/openshift_base.py,sha256=mE_NuKgEFsZDeA3kUo4iMsvIF1wN5yemUHeuE0M9fbY,49735
66
66
  reconcile/openshift_cluster_bots.py,sha256=eRPYZqWMKFNxLlSN0QG97V5t1iIESQ0BbGaiaQP5VB0,10940
67
67
  reconcile/openshift_clusterrolebindings.py,sha256=QfSy1Ik8eEY5XObc1Q4xyhqyErZenJmbPv_u9wcDNNo,5864
68
68
  reconcile/openshift_groups.py,sha256=fqBQ6ITDOArkC_zOjskmMigdCUDq3wntLZ0NcppXaFc,9414
@@ -86,7 +86,7 @@ reconcile/openshift_saas_deploy_trigger_upstream_jobs.py,sha256=etfBGj7GDXTOhNHK
86
86
  reconcile/openshift_serviceaccount_tokens.py,sha256=kc8o6tYDpzYTuX7BGIuZjGR1rC4tAB1tfN-ijCS0AgA,6005
87
87
  reconcile/openshift_tekton_resources.py,sha256=u17OOKPWp9k8WgPXA9RcrMoANYPocNEf-smD7rKRVgE,16278
88
88
  reconcile/openshift_upgrade_watcher.py,sha256=y1DoU--iX5QqDkYGP8YrHv2nH7rbiT9fRwcJ9i3Zp2o,6609
89
- reconcile/openshift_users.py,sha256=k6Vt-X_1nx0aAodnKvs916g2jLOxA03Mmk7Cf_x6K08,5293
89
+ reconcile/openshift_users.py,sha256=1mhxHvkspSz8dn8zL7PpAG_QZ2rSbYzBuvZ66zWxyL4,5339
90
90
  reconcile/openshift_vault_secrets.py,sha256=9rTqV6wzCQx2Oh712E_Xj8wMG7u8Oh-pY8DWjlv4mZw,1660
91
91
  reconcile/quay_base.py,sha256=WlrTGlpK2Ma9HxXqTRJ_W-7g6e6RM3RlN1vq1ANMP_I,1889
92
92
  reconcile/quay_membership.py,sha256=LKrHwHQDhsBQEP7rUKanks8KTX63qa_93CyHRVwdjfo,6291
@@ -121,7 +121,7 @@ reconcile/vault_replication.py,sha256=79GZ_kCimPoQcxkdhkWTQxPOAa46E0mNhf05s_Mk5s
121
121
  reconcile/vpc_peerings_validator.py,sha256=Kv22HJVlTW9l9GB2eXwjPWqdDbr_VuvQBNPttox6s5o,7177
122
122
  reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
123
123
  reconcile/aus/advanced_upgrade_service.py,sha256=MPqcWpq1QeYzBsB8rtTlo102h_cR_q83oOYtWAncuAk,22391
124
- reconcile/aus/aus_label_source.py,sha256=qoP8Fgxuu1tCuhG6ixCWve7Ll-KD6a79E2uLAmC0ifw,4184
124
+ reconcile/aus/aus_label_source.py,sha256=QG3XuKy5tAyJo9ABPHANrDpECxpkzm2LT5C9Xsu42oI,4183
125
125
  reconcile/aus/base.py,sha256=n53ZtW5MpBitN745mymFxcq7LT5X8si8U0-l8hH6AEc,49878
126
126
  reconcile/aus/cluster_version_data.py,sha256=j4UyEBi5mQuvPq5Lo7a_L_0blxvH790wJV07uAiikFU,7126
127
127
  reconcile/aus/healthchecks.py,sha256=S8_KPn_zFiOo_wf5XlVmz-I3tUDYAim3EGSqiSPMvLQ,2707
@@ -168,6 +168,8 @@ reconcile/change_owners/diff.py,sha256=puiyo0dyLxBvbSKUJGIxAsgqs6Omrc_75TEUXk40X
168
168
  reconcile/change_owners/implicit_ownership.py,sha256=6BehZvx4IjrphmOt_LLLk9_02Fl5BY5jd00Wuz_PBZk,4234
169
169
  reconcile/change_owners/self_service_roles.py,sha256=4_Dr6ZyRInKLKXCRYaCKLQsoH1USsH-NN72LvnwYsaA,9662
170
170
  reconcile/change_owners/tester.py,sha256=8XGCIa0SIDkhHBDnN8-XCx4OreOmGRU_6N-1MrBuziY,9005
171
+ reconcile/cluster_auth_rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
+ reconcile/cluster_auth_rhidp/integration.py,sha256=KIAiP_XFjsOA2OE8oFJa8lD0T1a7EwOmhct2xbj7tr8,9560
171
173
  reconcile/cna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
172
174
  reconcile/cna/client.py,sha256=t9gJDrKf4ApBlgu8c4QUbmzrYoSo1QPsnAGfucva2_U,1562
173
175
  reconcile/cna/integration.py,sha256=gSxQAsgErvV2wF4Xnr96kgvX0z0lH_Ez5jH2GFNNSqI,5164
@@ -220,6 +222,8 @@ reconcile/gql_definitions/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5J
220
222
  reconcile/gql_definitions/change_owners/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
221
223
  reconcile/gql_definitions/change_owners/queries/change_types.py,sha256=9S2YRNnSAvutjzuubZIQQe35yd8V2rGKvWSUI6yl11Q,5017
222
224
  reconcile/gql_definitions/change_owners/queries/self_service_roles.py,sha256=gU5qQ_zncXqM41WW_c2NYEZBuXlI9Xwm08D9HF6dP7g,4627
225
+ reconcile/gql_definitions/cluster_auth_rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
226
+ reconcile/gql_definitions/cluster_auth_rhidp/clusters.py,sha256=FTSj4nFD4OY6tjqTEuhAYWx3phckAiANbuVTWOHlo4Q,3267
223
227
  reconcile/gql_definitions/cna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
224
228
  reconcile/gql_definitions/cna/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
225
229
  reconcile/gql_definitions/cna/queries/cna_provisioners.py,sha256=YJOO6vo_q2a4z4Dgx6IN3zTmUDFgSZftqPNLf_U5yzU,2993
@@ -234,7 +238,7 @@ reconcile/gql_definitions/common/app_interface_state_settings.py,sha256=VXIK0Hmy
234
238
  reconcile/gql_definitions/common/app_interface_vault_settings.py,sha256=w8quvdG0cSq71ZyJokPPp7MyMpoDb6-HLQ3o9JHVGRQ,1771
235
239
  reconcile/gql_definitions/common/aws_vpcs.py,sha256=Dss9dQ3xagnz3Ltg1e9mtG2PAmQGBbUzKCmmzvuN28s,1892
236
240
  reconcile/gql_definitions/common/clusters.py,sha256=eJIbMQltj-Sc7h_QVIeFkbRy1b_QJIkHATfbagxM-yU,21527
237
- reconcile/gql_definitions/common/clusters_minimal.py,sha256=yZpjS9qWyusCEiWtD8wzf0tak298uQyxiN4lC6KNo4s,4475
241
+ reconcile/gql_definitions/common/clusters_minimal.py,sha256=JYrJV_aStmryiiGKyiXhj47qpF_8KilCqy-d9CofBCo,4635
238
242
  reconcile/gql_definitions/common/clusters_with_dms.py,sha256=GJ53P8tgMLh1NfVkaV9_AmaqF9pNUqJZcDkcKzKzUy0,2242
239
243
  reconcile/gql_definitions/common/clusters_with_peering.py,sha256=6mv1m8lc9UDzMWXatkTbYHwBaXoHIIC62qXULsfQN5Y,11822
240
244
  reconcile/gql_definitions/common/github_orgs.py,sha256=rZ0pDAA2_9hF9N-ykRZIxPtEmczTSjuA_k3nkp0k1W0,2039
@@ -317,7 +321,6 @@ reconcile/gql_definitions/ocm_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
317
321
  reconcile/gql_definitions/ocm_labels/clusters.py,sha256=MIVR5c8JxEOjsdGNpB737QyHGkjmHCycY6MAYNclPck,2916
318
322
  reconcile/gql_definitions/ocm_labels/organizations.py,sha256=mmYB5C5Fp_nPzwBDKdKG4qWiLre2VkZ26U_2O-jRKC4,2001
319
323
  reconcile/gql_definitions/ocm_oidc_idp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
320
- reconcile/gql_definitions/ocm_subscription_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
321
324
  reconcile/gql_definitions/openshift_cluster_bots/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
322
325
  reconcile/gql_definitions/openshift_cluster_bots/clusters.py,sha256=68meUg2Wgh91gplbPHAIlRWjnGg2RDrwtWd93QK6qRE,3672
323
326
  reconcile/gql_definitions/openshift_groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -384,8 +387,7 @@ reconcile/ocm/types.py,sha256=ibJYvzfAZyyMFkcF1bP8u3rkXciYJRplt_7Z1pKHFh0,2484
384
387
  reconcile/ocm_internal_notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
385
388
  reconcile/ocm_internal_notifications/integration.py,sha256=Gw2oB1Oe1Vvbj-fN_undhkQ2y5tCVhUfW5DenKu9ybM,4395
386
389
  reconcile/ocm_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
387
- reconcile/ocm_labels/integration.py,sha256=SqIgCYhlHEeoH2YXJ8kWIxCo9S3fzzAlZGxt9KCnJ1s,14792
388
- reconcile/ocm_labels/label_sources.py,sha256=z5Rg5uMTfRkTi3QbEQK1P82LLciWrSnp65XBEpZf2-o,1877
390
+ reconcile/ocm_labels/integration.py,sha256=JmqjY_8QI8lgnCgXroiEfj58wkd98fOe5mTiKvkLeOQ,14946
389
391
  reconcile/oum/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
390
392
  reconcile/oum/base.py,sha256=gw5qDx3VKtIk-VW_g0j5iPdM4qAweqy0YROHJhKHt0M,13596
391
393
  reconcile/oum/labelset.py,sha256=MTOMEyBMbge3JOq0mOKWja-kV3cs0ZId9Ozxv7FJ9Kw,2185
@@ -664,7 +666,7 @@ reconcile/utils/state.py,sha256=FK8NLT1xyumuXpYRm0Nk6pWpOE_U6-NovGn6zKCw8vw,1629
664
666
  reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
665
667
  reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
666
668
  reconcile/utils/terraform_client.py,sha256=mZEKpu6nbfiQd60wRkc8-5sljBTUTOgaAKnF89itMzU,32085
667
- reconcile/utils/terrascript_aws_client.py,sha256=4FR4JUSniDOFMtc_r1t2z0x_drrBfcw-J33AdWeJEyw,272662
669
+ reconcile/utils/terrascript_aws_client.py,sha256=VlvIHgrZRiMFVgx6a8ZHxoiJoDwqbtOmsZFnwwNrdL0,273199
668
670
  reconcile/utils/three_way_diff_strategy.py,sha256=nyqeQsLCoPI6e16k2CF3b9KNgQLU-rPf5RtfdUfVMwE,4468
669
671
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
670
672
  reconcile/utils/unleash.py,sha256=1D56CsZfE3ShDtN3IErE1T2eeIwNmxhK-yYbCotJ99E,3601
@@ -727,6 +729,7 @@ reconcile/utils/ocm/base.py,sha256=KPS1CgiALlq7INdgTDpN7MmtZ6uQMSWcUDjeGHmp5Z4,1
727
729
  reconcile/utils/ocm/cluster_groups.py,sha256=F8oqVqN_4QUnGL0K61zZhoYIzJeP57EcmZpwmoV0mr4,1751
728
730
  reconcile/utils/ocm/clusters.py,sha256=Nw9m-jgN3GHHCh6w9UOBbMV4rtS24_-Ep09jAWQ-_fE,7653
729
731
  reconcile/utils/ocm/identity_providers.py,sha256=dKed09N8iWmn39tI_MpwgVe47x23eLsknGbjMUxtwr4,2175
732
+ reconcile/utils/ocm/label_sources.py,sha256=z5Rg5uMTfRkTi3QbEQK1P82LLciWrSnp65XBEpZf2-o,1877
730
733
  reconcile/utils/ocm/labels.py,sha256=pRzTE506hKAgVbBNO51IBlGOuCbJmTYDMsL0pWBEwp8,5945
731
734
  reconcile/utils/ocm/ocm.py,sha256=axXuTUpJR-fqhDo6OiDzEygOsR9OCX8ZV1I_Q2vWJQc,36712
732
735
  reconcile/utils/ocm/products.py,sha256=835Wr354wrP4hDhBi6dVMy1ru_G3-C-wCJyjki43KuA,25961
@@ -785,8 +788,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
785
788
  tools/test/test_qontract_cli.py,sha256=w2l4BHB09k1d-BGJ1jBUNCqDv7zkqYrMHojQXg-21kQ,4155
786
789
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
787
790
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
788
- qontract_reconcile-0.10.1rc791.dist-info/METADATA,sha256=fZl69FcdS3wj97ZwyE1VwTV42kLiyLmoNcZ0yrB7T9Q,2364
789
- qontract_reconcile-0.10.1rc791.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
790
- qontract_reconcile-0.10.1rc791.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
791
- qontract_reconcile-0.10.1rc791.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
792
- qontract_reconcile-0.10.1rc791.dist-info/RECORD,,
791
+ qontract_reconcile-0.10.1rc793.dist-info/METADATA,sha256=pa6ro62kS2lKnya9PY8ITjbQy-s8wAgrS-N65BECpL0,2364
792
+ qontract_reconcile-0.10.1rc793.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
793
+ qontract_reconcile-0.10.1rc793.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
794
+ qontract_reconcile-0.10.1rc793.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
795
+ qontract_reconcile-0.10.1rc793.dist-info/RECORD,,
@@ -16,7 +16,7 @@ from reconcile.gql_definitions.advanced_upgrade_service.aus_organization import
16
16
  query as aus_organizations_query,
17
17
  )
18
18
  from reconcile.gql_definitions.fragments.aus_organization import AUSOCMOrganization
19
- from reconcile.ocm_labels.label_sources import (
19
+ from reconcile.utils.ocm.label_sources import (
20
20
  ClusterRef,
21
21
  LabelSource,
22
22
  LabelState,
reconcile/cli.py CHANGED
@@ -2918,6 +2918,22 @@ def rhidp_sso_client(
2918
2918
  )
2919
2919
 
2920
2920
 
2921
+ @integration.command(
2922
+ short_help="Manages the OCM subscription labels for clusters with RHIDP authentication. Part of RHIDP."
2923
+ )
2924
+ @click.pass_context
2925
+ def cluster_auth_rhidp(ctx):
2926
+ from reconcile.cluster_auth_rhidp.integration import (
2927
+ ClusterAuthRhidpIntegration,
2928
+ ClusterAuthRhidpIntegrationParams,
2929
+ )
2930
+
2931
+ run_class_integration(
2932
+ integration=ClusterAuthRhidpIntegration(ClusterAuthRhidpIntegrationParams()),
2933
+ ctx=ctx.obj,
2934
+ )
2935
+
2936
+
2921
2937
  @integration.command(
2922
2938
  short_help="Automatically provide dedicated Dynatrace tokens to management clusters"
2923
2939
  )
@@ -0,0 +1,257 @@
1
+ import logging
2
+ from collections.abc import Callable, Iterable
3
+ from typing import Any
4
+
5
+ from deepdiff import DeepHash
6
+
7
+ from reconcile.gql_definitions.cluster_auth_rhidp.clusters import (
8
+ ClusterAuthRHIDPV1,
9
+ ClusterV1,
10
+ )
11
+ from reconcile.gql_definitions.cluster_auth_rhidp.clusters import query as cluster_query
12
+ from reconcile.gql_definitions.common.ocm_environments import (
13
+ query as ocm_environment_query,
14
+ )
15
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
16
+ from reconcile.rhidp.common import (
17
+ AUTH_NAME_LABEL_KEY,
18
+ ISSUER_LABEL_KEY,
19
+ RHIDP_NAMESPACE_LABEL_KEY,
20
+ STATUS_LABEL_KEY,
21
+ StatusValue,
22
+ )
23
+ from reconcile.utils import gql
24
+ from reconcile.utils.defer import defer
25
+ from reconcile.utils.differ import diff_mappings
26
+ from reconcile.utils.disabled_integrations import integration_is_enabled
27
+ from reconcile.utils.ocm.clusters import discover_clusters_for_organizations
28
+ from reconcile.utils.ocm.label_sources import ClusterRef, LabelState
29
+ from reconcile.utils.ocm.labels import add_label, delete_label, update_label
30
+ from reconcile.utils.ocm_base_client import (
31
+ OCMAPIClientConfigurationProtocol,
32
+ OCMBaseClient,
33
+ init_ocm_base_client,
34
+ )
35
+ from reconcile.utils.runtime.integration import (
36
+ PydanticRunParams,
37
+ QontractReconcileIntegration,
38
+ )
39
+ from reconcile.utils.secret_reader import SecretReaderBase
40
+ from reconcile.utils.semver_helper import make_semver
41
+
42
+ QONTRACT_INTEGRATION = "cluster-auth-rhidp"
43
+ QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
44
+
45
+
46
+ class ClusterAuthRhidpIntegrationParams(PydanticRunParams):
47
+ pass
48
+
49
+
50
+ class ManagedLabelConflictError(Exception):
51
+ pass
52
+
53
+
54
+ OcmApis = dict[str, OCMBaseClient]
55
+
56
+
57
+ class ClusterAuthRhidpIntegration(
58
+ QontractReconcileIntegration[ClusterAuthRhidpIntegrationParams]
59
+ ):
60
+ """Manages the OCM subscription labels for clusters with RHIDP authentication."""
61
+
62
+ @property
63
+ def name(self) -> str:
64
+ return QONTRACT_INTEGRATION
65
+
66
+ def get_early_exit_desired_state(
67
+ self, query_func: Callable | None = None
68
+ ) -> dict[str, Any]:
69
+ """Return the desired state for early exit."""
70
+ if not query_func:
71
+ query_func = gql.get_api().query
72
+
73
+ desired = {
74
+ "subs_labels": self.fetch_desired_state(self.get_clusters(query_func)),
75
+ }
76
+ # to figure out wheter to run a PR check of to exit early, a hash value
77
+ # of the desired state is sufficient
78
+ return {"hash": DeepHash(desired).get(desired)}
79
+
80
+ def get_clusters(self, query_func: Callable) -> list[ClusterV1]:
81
+ data = cluster_query(query_func)
82
+ return [
83
+ c
84
+ for c in data.clusters or []
85
+ if integration_is_enabled(self.name, c)
86
+ # ocm is mandatory
87
+ and c.ocm
88
+ and c.spec
89
+ and c.spec.q_id
90
+ ]
91
+
92
+ def get_environments(self, query_func: Callable) -> list[OCMEnvironment]:
93
+ return ocm_environment_query(query_func).environments
94
+
95
+ def fetch_desired_state(self, clusters: Iterable[ClusterV1]) -> LabelState:
96
+ """Fetches the desired state of subscription labels for the given clusters."""
97
+ states: LabelState = {}
98
+
99
+ for cluster in clusters:
100
+ assert cluster.ocm and cluster.spec and cluster.spec.q_id # make mypy happy
101
+ label = {}
102
+ if auth := next(
103
+ (
104
+ a
105
+ for a in cluster.auth
106
+ if isinstance(a, ClusterAuthRHIDPV1) and a.service == "rhidp"
107
+ ),
108
+ None,
109
+ ):
110
+ label = {
111
+ STATUS_LABEL_KEY: auth.status or StatusValue.ENABLED.value,
112
+ AUTH_NAME_LABEL_KEY: auth.name,
113
+ }
114
+ if auth.issuer:
115
+ label[ISSUER_LABEL_KEY] = auth.issuer
116
+ cluster_ref = ClusterRef(
117
+ cluster_id=cluster.spec.q_id,
118
+ org_id=cluster.ocm.org_id,
119
+ ocm_env=cluster.ocm.environment.name,
120
+ name=cluster.name,
121
+ label_container_href=None,
122
+ )
123
+ states[cluster_ref] = label
124
+
125
+ return states
126
+
127
+ def fetch_current_state(
128
+ self,
129
+ ocm_apis: OcmApis,
130
+ clusters: Iterable[ClusterV1],
131
+ managed_label_prefixes: Iterable[str],
132
+ ) -> LabelState:
133
+ """Fetches the current state of subscription labels for the given clusters.
134
+
135
+ If a cluster can't be found in OCM, the resulting dict will not contain a
136
+ state for it, not even an empty one.
137
+ """
138
+ cluster_ids = {c.spec.q_id for c in clusters if c.spec and c.spec.q_id}
139
+ states: LabelState = {}
140
+ for env_name, ocm_api in ocm_apis.items():
141
+ for cluster_details in discover_clusters_for_organizations(
142
+ ocm_api=ocm_api,
143
+ organization_ids=list({
144
+ c.ocm.org_id
145
+ for c in clusters
146
+ if c.ocm and c.ocm.environment.name == env_name
147
+ }),
148
+ ):
149
+ if cluster_details.ocm_cluster.id not in cluster_ids:
150
+ # there might be more clusters in an organization than we care about
151
+ continue
152
+
153
+ filtered_labels = {
154
+ label: value
155
+ for label, value in cluster_details.subscription_labels.get_values_dict().items()
156
+ if label.startswith(tuple(managed_label_prefixes))
157
+ }
158
+ states[
159
+ ClusterRef(
160
+ cluster_id=cluster_details.ocm_cluster.id,
161
+ org_id=cluster_details.organization_id,
162
+ ocm_env=env_name,
163
+ name=cluster_details.ocm_cluster.name,
164
+ label_container_href=f"{cluster_details.ocm_cluster.subscription.href}/labels",
165
+ )
166
+ ] = filtered_labels
167
+ return states
168
+
169
+ def init_ocm_apis(
170
+ self,
171
+ environments: Iterable[OCMEnvironment],
172
+ init_ocm_base_client: Callable[
173
+ [OCMAPIClientConfigurationProtocol, SecretReaderBase], OCMBaseClient
174
+ ] = init_ocm_base_client,
175
+ ) -> OcmApis:
176
+ """Initialize OCM clients for each OCM environment."""
177
+ return {
178
+ env.name: init_ocm_base_client(env, self.secret_reader)
179
+ for env in environments
180
+ }
181
+
182
+ def reconcile(
183
+ self,
184
+ dry_run: bool,
185
+ ocm_apis: OcmApis,
186
+ current_state: LabelState,
187
+ desired_state: LabelState,
188
+ ) -> None:
189
+ # we iterate via the current state because it refers to the clusters we can act on
190
+ for label_owner_ref, current_labels in current_state.items():
191
+ ocm_api = ocm_apis[label_owner_ref.ocm_env]
192
+ desired_labels = desired_state.get(label_owner_ref, {})
193
+ if current_labels == desired_labels:
194
+ continue
195
+
196
+ diff_result = diff_mappings(current_labels, desired_labels)
197
+
198
+ for label_to_add, value in diff_result.add.items():
199
+ logging.info([
200
+ "create_label",
201
+ *label_owner_ref.identity_labels(),
202
+ f"{label_to_add}={value}",
203
+ ])
204
+ if not dry_run:
205
+ add_label(
206
+ ocm_api=ocm_api,
207
+ label_container_href=label_owner_ref.required_label_container_href(),
208
+ label=label_to_add,
209
+ value=value,
210
+ )
211
+ for label_to_rm, value in diff_result.delete.items():
212
+ logging.info([
213
+ "delete_label",
214
+ *label_owner_ref.identity_labels(),
215
+ f"{label_to_rm}={value}",
216
+ ])
217
+ if not dry_run:
218
+ delete_label(
219
+ ocm_api=ocm_api,
220
+ label_container_href=label_owner_ref.required_label_container_href(),
221
+ label=label_to_rm,
222
+ )
223
+ for label_to_update, diff_pair in diff_result.change.items():
224
+ value = diff_pair.desired
225
+ logging.info([
226
+ "update_label",
227
+ *label_owner_ref.identity_labels(),
228
+ f"{label_to_update}={value}",
229
+ ])
230
+ if not dry_run:
231
+ update_label(
232
+ ocm_api=ocm_api,
233
+ label_container_href=label_owner_ref.required_label_container_href(),
234
+ label=label_to_update,
235
+ value=value,
236
+ )
237
+
238
+ @defer
239
+ def run(self, dry_run: bool, defer: Callable | None = None) -> None:
240
+ """Run the integration."""
241
+ gql_api = gql.get_api()
242
+ clusters = self.get_clusters(gql_api.query)
243
+ environments = self.get_environments(gql_api.query)
244
+ ocm_apis = self.init_ocm_apis(environments, init_ocm_base_client)
245
+ if defer:
246
+ defer(lambda: [ocm_api.close() for ocm_api in ocm_apis.values()]) # type: ignore
247
+
248
+ current_state = self.fetch_current_state(
249
+ ocm_apis, clusters, managed_label_prefixes=[RHIDP_NAMESPACE_LABEL_KEY]
250
+ )
251
+ desired_state = self.fetch_desired_state(clusters)
252
+ self.reconcile(
253
+ dry_run=dry_run,
254
+ ocm_apis=ocm_apis,
255
+ current_state=current_state,
256
+ desired_state=desired_state,
257
+ )
@@ -0,0 +1,127 @@
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
+ from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
21
+
22
+
23
+ DEFINITION = """
24
+ fragment OCMEnvironment on OpenShiftClusterManagerEnvironment_v1 {
25
+ name
26
+ labels
27
+ url
28
+ accessTokenClientId
29
+ accessTokenUrl
30
+ accessTokenClientSecret {
31
+ ... VaultSecret
32
+ }
33
+ }
34
+
35
+ fragment VaultSecret on VaultSecret_v1 {
36
+ path
37
+ field
38
+ version
39
+ format
40
+ }
41
+
42
+ query ClusterAuthRhidp {
43
+ clusters: clusters_v1 {
44
+ name
45
+ spec {
46
+ id
47
+ }
48
+ ocm {
49
+ environment {
50
+ ...OCMEnvironment
51
+ }
52
+ orgId
53
+ }
54
+ disable {
55
+ integrations
56
+ }
57
+ auth {
58
+ service
59
+ ... on ClusterAuthRHIDP_v1 {
60
+ name
61
+ status
62
+ issuer
63
+ }
64
+ }
65
+ }
66
+ }
67
+ """
68
+
69
+
70
+ class ConfiguredBaseModel(BaseModel):
71
+ class Config:
72
+ smart_union=True
73
+ extra=Extra.forbid
74
+
75
+
76
+ class ClusterSpecV1(ConfiguredBaseModel):
77
+ q_id: Optional[str] = Field(..., alias="id")
78
+
79
+
80
+ class OpenShiftClusterManagerV1(ConfiguredBaseModel):
81
+ environment: OCMEnvironment = Field(..., alias="environment")
82
+ org_id: str = Field(..., alias="orgId")
83
+
84
+
85
+ class DisableClusterAutomationsV1(ConfiguredBaseModel):
86
+ integrations: Optional[list[str]] = Field(..., alias="integrations")
87
+
88
+
89
+ class ClusterAuthV1(ConfiguredBaseModel):
90
+ service: str = Field(..., alias="service")
91
+
92
+
93
+ class ClusterAuthRHIDPV1(ClusterAuthV1):
94
+ name: str = Field(..., alias="name")
95
+ status: Optional[str] = Field(..., alias="status")
96
+ issuer: Optional[str] = Field(..., alias="issuer")
97
+
98
+
99
+ class ClusterV1(ConfiguredBaseModel):
100
+ name: str = Field(..., alias="name")
101
+ spec: Optional[ClusterSpecV1] = Field(..., alias="spec")
102
+ ocm: Optional[OpenShiftClusterManagerV1] = Field(..., alias="ocm")
103
+ disable: Optional[DisableClusterAutomationsV1] = Field(..., alias="disable")
104
+ auth: list[Union[ClusterAuthRHIDPV1, ClusterAuthV1]] = Field(..., alias="auth")
105
+
106
+
107
+ class ClusterAuthRhidpQueryData(ConfiguredBaseModel):
108
+ clusters: Optional[list[ClusterV1]] = Field(..., alias="clusters")
109
+
110
+
111
+ def query(query_func: Callable, **kwargs: Any) -> ClusterAuthRhidpQueryData:
112
+ """
113
+ This is a convenience function which queries and parses the data into
114
+ concrete types. It should be compatible with most GQL clients.
115
+ You do not have to use it to consume the generated data classes.
116
+ Alternatively, you can also mime and alternate the behavior
117
+ of this function in the caller.
118
+
119
+ Parameters:
120
+ query_func (Callable): Function which queries your GQL Server
121
+ kwargs: optional arguments that will be passed to the query function
122
+
123
+ Returns:
124
+ ClusterAuthRhidpQueryData: queried data parsed into generated classes
125
+ """
126
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
127
+ return ClusterAuthRhidpQueryData(**raw_data)
@@ -80,6 +80,9 @@ query ClustersMinimal($name: String) {
80
80
  ... on ClusterAuthOIDC_v1 {
81
81
  name
82
82
  }
83
+ ... on ClusterAuthRHIDP_v1 {
84
+ name
85
+ }
83
86
  }
84
87
  }
85
88
  }
@@ -121,6 +124,10 @@ class ClusterAuthOIDCV1(ClusterAuthV1):
121
124
  name: str = Field(..., alias="name")
122
125
 
123
126
 
127
+ class ClusterAuthRHIDPV1(ClusterAuthV1):
128
+ name: str = Field(..., alias="name")
129
+
130
+
124
131
  class ClusterV1(ConfiguredBaseModel):
125
132
  name: str = Field(..., alias="name")
126
133
  server_url: str = Field(..., alias="serverUrl")
@@ -136,7 +143,7 @@ class ClusterV1(ConfiguredBaseModel):
136
143
  cluster_admin_automation_token: Optional[VaultSecret] = Field(..., alias="clusterAdminAutomationToken")
137
144
  internal: Optional[bool] = Field(..., alias="internal")
138
145
  disable: Optional[DisableClusterAutomationsV1] = Field(..., alias="disable")
139
- auth: list[Union[ClusterAuthGithubOrgTeamV1, ClusterAuthGithubOrgV1, ClusterAuthOIDCV1, ClusterAuthV1]] = Field(..., alias="auth")
146
+ auth: list[Union[ClusterAuthGithubOrgTeamV1, ClusterAuthGithubOrgV1, ClusterAuthOIDCV1, ClusterAuthRHIDPV1, ClusterAuthV1]] = Field(..., alias="auth")
140
147
 
141
148
 
142
149
  class ClustersMinimalQueryData(ConfiguredBaseModel):
@@ -27,17 +27,18 @@ from reconcile.gql_definitions.ocm_labels.organizations import OpenShiftClusterM
27
27
  from reconcile.gql_definitions.ocm_labels.organizations import (
28
28
  query as organization_query,
29
29
  )
30
- from reconcile.ocm_labels.label_sources import (
31
- ClusterRef,
32
- LabelSource,
33
- LabelState,
34
- OrgRef,
35
- )
36
30
  from reconcile.utils import gql
31
+ from reconcile.utils.defer import defer
37
32
  from reconcile.utils.differ import diff_mappings
38
33
  from reconcile.utils.disabled_integrations import integration_is_enabled
39
34
  from reconcile.utils.helpers import flatten
40
35
  from reconcile.utils.ocm.clusters import discover_clusters_for_organizations
36
+ from reconcile.utils.ocm.label_sources import (
37
+ ClusterRef,
38
+ LabelSource,
39
+ LabelState,
40
+ OrgRef,
41
+ )
41
42
  from reconcile.utils.ocm.labels import (
42
43
  add_label,
43
44
  build_organization_labels_href,
@@ -138,14 +139,16 @@ class OcmLabelsIntegration(QontractReconcileIntegration[OcmLabelsIntegrationPara
138
139
  # of the desired state is sufficient
139
140
  return {"hash": DeepHash(desired).get(desired)}
140
141
 
141
- def run(self, dry_run: bool) -> None:
142
+ @defer
143
+ def run(self, dry_run: bool, defer: Callable | None = None) -> None:
142
144
  gqlapi = gql.get_api()
143
- self.get_early_exit_desired_state()
144
145
  clusters = self.get_clusters(gqlapi.query)
145
146
  organizations = self.get_organizations(gqlapi.query)
146
147
  environments = self.get_environments(gqlapi.query)
147
148
 
148
149
  self.ocm_apis = self.init_ocm_apis(environments, init_ocm_base_client)
150
+ if defer:
151
+ defer(lambda: [ocm_api.close() for ocm_api in self.ocm_apis.values()]) # type: ignore
149
152
 
150
153
  # organization labels
151
154
  orgs_current_state, orgs_desired_state = self.fetch_organization_label_states(
@@ -1313,6 +1313,7 @@ def determine_user_keys_for_access(
1313
1313
  "github-org": "github_username",
1314
1314
  "github-org-team": "github_username",
1315
1315
  "oidc": "org_username",
1316
+ "rhidp": "org_username",
1316
1317
  }
1317
1318
  user_keys: list[str] = []
1318
1319
 
@@ -18,6 +18,7 @@ from reconcile import (
18
18
  )
19
19
  from reconcile.gql_definitions.common.clusters_minimal import (
20
20
  ClusterAuthOIDCV1,
21
+ ClusterAuthRHIDPV1,
21
22
  ClusterV1,
22
23
  )
23
24
  from reconcile.typed_queries.app_interface_vault_settings import (
@@ -51,7 +52,7 @@ def get_cluster_users(
51
52
  identity_prefixes = ["github"]
52
53
 
53
54
  for auth in cluster_info.auth:
54
- if isinstance(auth, ClusterAuthOIDCV1):
55
+ if isinstance(auth, (ClusterAuthOIDCV1, ClusterAuthRHIDPV1)):
55
56
  identity_prefixes.append(auth.name)
56
57
 
57
58
  for u in oc.get_users():
@@ -6464,11 +6464,20 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
6464
6464
  )
6465
6465
 
6466
6466
  # resource - msk config
6467
+ # unique msk config resource name enables "create_before_destroy" lifecycle
6468
+ # which is required when changing version which requires a resource replacement
6469
+ msk_version_str = values["kafka_version"].replace(".", "-")
6470
+ msk_config_name = f"{resource_id}-{msk_version_str}"
6467
6471
  msk_config = aws_msk_configuration(
6468
6472
  resource_id,
6469
- name=resource_id,
6473
+ name=msk_config_name,
6470
6474
  kafka_versions=[values["kafka_version"]],
6471
6475
  server_properties=values["server_properties"],
6476
+ # lifecycle create_before_destroy is required to ensure that the config is created
6477
+ # before it is assigned to the cluster
6478
+ lifecycle={
6479
+ "create_before_destroy": True,
6480
+ },
6472
6481
  )
6473
6482
  tf_resources.append(msk_config)
6474
6483
  values.pop("server_properties", None)
File without changes