qontract-reconcile 0.10.2.dev481__py3-none-any.whl → 0.10.2.dev484__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/RECORD +14 -8
- reconcile/cli.py +31 -16
- reconcile/gql_definitions/common/app_interface_clusterrole.py +104 -0
- reconcile/openshift_bindings/__init__.py +0 -0
- reconcile/openshift_bindings/constants.py +7 -0
- reconcile/openshift_bindings/models.py +264 -0
- reconcile/openshift_bindings/openshift_clusterrolebindings.py +129 -0
- reconcile/openshift_bindings/openshift_rolebindings.py +135 -0
- reconcile/openshift_bindings/utils.py +14 -0
- reconcile/openshift_users.py +50 -5
- reconcile/typed_queries/app_interface_clusterroles.py +10 -0
- reconcile/openshift_clusterrolebindings.py +0 -192
- reconcile/openshift_rolebindings.py +0 -323
- {qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qontract-reconcile
|
|
3
|
-
Version: 0.10.2.
|
|
3
|
+
Version: 0.10.2.dev484
|
|
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
|
{qontract_reconcile-0.10.2.dev481.dist-info → qontract_reconcile-0.10.2.dev484.dist-info}/RECORD
RENAMED
|
@@ -8,7 +8,7 @@ reconcile/aws_iam_password_reset.py,sha256=5oajSspJVAvpGd445hKsxtEGYb75dM4l1_PJT
|
|
|
8
8
|
reconcile/aws_support_cases_sos.py,sha256=G9g0oM6ohvuJ5N592ePjiPeaDug4_vapAy58RyG-S3Y,3478
|
|
9
9
|
reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
|
|
10
10
|
reconcile/checkpoint.py,sha256=Q5Ru36ZEwWw95ZkUXBf4VfvCmqDS9TcAF7QVroOf9Vk,5375
|
|
11
|
-
reconcile/cli.py,sha256=
|
|
11
|
+
reconcile/cli.py,sha256=ZHOM3LPTUyKJy-N7p9OSyrJsb8pWGCsUFthBvJgjUkE,115887
|
|
12
12
|
reconcile/closedbox_endpoint_monitoring_base.py,sha256=_OKz7K7HHw0-gzxeEma8PcUCtd70pRBy7JMoaAm8IVU,4940
|
|
13
13
|
reconcile/cluster_deployment_mapper.py,sha256=aKroYLY6-nNxWi2jxDu7VRMuNZ3YgSI0J6ek7Fet2AQ,2241
|
|
14
14
|
reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
|
|
@@ -56,7 +56,6 @@ reconcile/ocm_update_recommended_version.py,sha256=Vi3Y2sX-OQxx1mv_xiPQXnmrpsZzG
|
|
|
56
56
|
reconcile/ocm_upgrade_scheduler_org_updater.py,sha256=j4qthqx8qREB6mSbV9NT-Giq1Tu5y2EhPgIObkvmjyU,4371
|
|
57
57
|
reconcile/openshift_base.py,sha256=ERc8BELzWr8PF8SQtBbcYkzvudLe676ecEmS09MYZz0,57929
|
|
58
58
|
reconcile/openshift_cluster_bots.py,sha256=QBflmFjXaIY7bxNlI2qSI5sER3Jks-_FAhrPHschI4g,10939
|
|
59
|
-
reconcile/openshift_clusterrolebindings.py,sha256=jwSaYQvUUY7noQGc148Dkqm6woYxvOEd1sume7k_sUk,6212
|
|
60
59
|
reconcile/openshift_groups.py,sha256=XpIyhgnWY1XUQio1wi6sHoDtoMYdk-lpHp0-1d1RC7o,9471
|
|
61
60
|
reconcile/openshift_limitranges.py,sha256=-MVmUWMMLUbX50GuZ-XClm4RIH8uIKNqAAMYD1IIs0I,4017
|
|
62
61
|
reconcile/openshift_namespace_labels.py,sha256=zjQNini7qgb-x_0QRBfD1zVBeX_5Pgk8ixDkBA4v488,16241
|
|
@@ -67,7 +66,6 @@ reconcile/openshift_resourcequotas.py,sha256=0CSuCre3T2ON42Ku1UDhTRugfmUNBx8PILp
|
|
|
67
66
|
reconcile/openshift_resources.py,sha256=YnhDxCvsp0muxEmULiqWhoar9EzxohTrnbY-U7oS5Hc,1603
|
|
68
67
|
reconcile/openshift_resources_base.py,sha256=wOdQMLqaiQleQTsC8MH9nwpBKFffLcOuZthpOcc1heE,43096
|
|
69
68
|
reconcile/openshift_rhcs_certs.py,sha256=UjBFX344n4eFXZmoEUCVeGECBowWTpbjNyPGrEzAmkA,11544
|
|
70
|
-
reconcile/openshift_rolebindings.py,sha256=G4n7wWTGZ34OsRgen0L3EpdPXuFEdZ_1E-b3ROvSb4I,10824
|
|
71
69
|
reconcile/openshift_routes.py,sha256=xnA34f32xDdkfV2MXIC1QURFJioQUsXT8AZBiY7iSP0,1298
|
|
72
70
|
reconcile/openshift_saas_deploy.py,sha256=YQRIjnb-V6x1a0fUv2w3hqjMj5tyqRirzkG8DzknYdc,13159
|
|
73
71
|
reconcile/openshift_saas_deploy_change_tester.py,sha256=6wU7rFCaTRU1Wj8Izi6ExLvQstwqDbr9n9YfyPcb6zQ,8854
|
|
@@ -80,7 +78,7 @@ reconcile/openshift_saas_deploy_trigger_upstream_jobs.py,sha256=TBMQXyMktbCV1uI5
|
|
|
80
78
|
reconcile/openshift_serviceaccount_tokens.py,sha256=y8pW6j_0ELTDrahZ0Fr-6St5ww1iJWCrPdAW3O-8rRA,9034
|
|
81
79
|
reconcile/openshift_tekton_resources.py,sha256=5mUWEQqU9RpMLQZxHOb6IkbbZzx4iJnaVubGi2xVJLs,16368
|
|
82
80
|
reconcile/openshift_upgrade_watcher.py,sha256=93l8X1-RHNtL89GzPg1XWivWart49l_hpAVFChvT6Wg,6643
|
|
83
|
-
reconcile/openshift_users.py,sha256=
|
|
81
|
+
reconcile/openshift_users.py,sha256=aQfScWJlvMcXc0ApeQMZiDYdmh8zPSAwG6v6XrEDYhw,7015
|
|
84
82
|
reconcile/openshift_vault_secrets.py,sha256=Ax-_EBWWU1VRHYyKaUkGJkIjHGwWM3bZgjXL5CkPW8k,1883
|
|
85
83
|
reconcile/quay_base.py,sha256=4gNca076ICJ2fDoTwCgcA3L-DqmVmBgHWLCubTk78uc,2832
|
|
86
84
|
reconcile/quay_membership.py,sha256=tSS1j34RzoMXZJHmeuW3yVyM2E9onSha9SMJO3RcUMo,7694
|
|
@@ -253,6 +251,7 @@ reconcile/gql_definitions/cluster_auth_rhidp/clusters.py,sha256=f6h8AWhovxTwlqZC
|
|
|
253
251
|
reconcile/gql_definitions/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
254
252
|
reconcile/gql_definitions/common/alerting_services_settings.py,sha256=gjdxA7DoIPvFSCo-4m4xY5msxagNLcjlwNapt0RptSw,1792
|
|
255
253
|
reconcile/gql_definitions/common/app_code_component_repos.py,sha256=EB_Ri23uTZdL47hoBXIr3RNvD1WSLF81EddAwoAAH-w,2029
|
|
254
|
+
reconcile/gql_definitions/common/app_interface_clusterrole.py,sha256=aLTUToIYoGggMT5B2MTH12IG9E3Gs2gI6xg2voMnHK0,2868
|
|
256
255
|
reconcile/gql_definitions/common/app_interface_custom_messages.py,sha256=_EyRjSdfOj76gGyFWATNSrDmLWk1htVqPqtY5EubPaY,1995
|
|
257
256
|
reconcile/gql_definitions/common/app_interface_dms_settings.py,sha256=1XU0IctKM8K5XaGz7XIvAQpfV7eCRmloL6DK7KAYjvw,2563
|
|
258
257
|
reconcile/gql_definitions/common/app_interface_repo_settings.py,sha256=1kDNoj0SuqNxc_Xjr1oFAA5KQe8EuJOCGlu8yLArkdU,1749
|
|
@@ -448,6 +447,12 @@ reconcile/ocm_internal_notifications/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeu
|
|
|
448
447
|
reconcile/ocm_internal_notifications/integration.py,sha256=MX0nXFNMS5mUAPVYAxK8MfXJ-pYanvOgyK3M5iALCZ0,4480
|
|
449
448
|
reconcile/ocm_labels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
450
449
|
reconcile/ocm_labels/integration.py,sha256=3iAfeT_lycgKGZCrycIc6b9aofBRmcVlJivORAzy-mE,15298
|
|
450
|
+
reconcile/openshift_bindings/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
451
|
+
reconcile/openshift_bindings/constants.py,sha256=ox61yleIBG68wx-oqPiS4ZRj3RaoafGpeOh0g5rxtBI,303
|
|
452
|
+
reconcile/openshift_bindings/models.py,sha256=rHnjN5gbmcrL7gqLZpTr1-9un6GaDnvm3IXnInGMRps,8872
|
|
453
|
+
reconcile/openshift_bindings/openshift_clusterrolebindings.py,sha256=QhkU6x05uQEQrn8odW3zn3aY9MDUNzl6UHfJpuRCG3o,4827
|
|
454
|
+
reconcile/openshift_bindings/openshift_rolebindings.py,sha256=7C4bUCPBG3QxQvuL4C33wCfrDjEHLpEQ243QFUeOUw0,5152
|
|
455
|
+
reconcile/openshift_bindings/utils.py,sha256=4o4XwGAOOzBFw7MpowfrqfUKA7H0IU0_D9hQgIMJdg0,542
|
|
451
456
|
reconcile/oum/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
452
457
|
reconcile/oum/base.py,sha256=WCFdHOHXLPrJcvxVqw6HjaJthT7olC5BQqqXlD4DM6c,13552
|
|
453
458
|
reconcile/oum/labelset.py,sha256=oZ_nV2ca8GU8iZptPdIqgCKgzQ2Wq89H8OiTGRIkWhw,2202
|
|
@@ -523,6 +528,7 @@ reconcile/terraform_vpc_resources/merge_request.py,sha256=MFRG7JQojmCTgr-9rLXVot
|
|
|
523
528
|
reconcile/terraform_vpc_resources/merge_request_manager.py,sha256=0_FT_DKE39DU2NJ2dWQVy6mCdIo4T7n2tSdklUyH6S0,4124
|
|
524
529
|
reconcile/typed_queries/__init__.py,sha256=rRk4CyslLsBr4vAh1pIPgt6s3P4R1M9NSEPLnyQgBpk,61
|
|
525
530
|
reconcile/typed_queries/alerting_services_settings.py,sha256=YKQd60O_2C_H103nLrYgcUInndM2vFypqW_NO706L2E,833
|
|
531
|
+
reconcile/typed_queries/app_interface_clusterroles.py,sha256=0pR9uebFTnfEukvumQyz2oomSLP9Z5ILoVVSNTEpULk,266
|
|
526
532
|
reconcile/typed_queries/app_interface_custom_messages.py,sha256=bgSAJEzqee8aiPVCj_bIIqb4VTkrF0-vti1dos7ebEg,684
|
|
527
533
|
reconcile/typed_queries/app_interface_deadmanssnitch_settings.py,sha256=znhfiAawUTYSWabZiKDKeexR_-48z9FZ44sxD-MwA14,638
|
|
528
534
|
reconcile/typed_queries/app_interface_repo_url.py,sha256=9fhgWihjWNYOmK65irBWw9jdm7YPJNUWqZC1Ez__PFc,578
|
|
@@ -803,7 +809,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
|
803
809
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
|
|
804
810
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
|
805
811
|
tools/sre_checkpoints/util.py,sha256=KcYVfa3UmJHVP_ocgrKe8NkrO5IDB9aWEDydSokPcRk,975
|
|
806
|
-
qontract_reconcile-0.10.2.
|
|
807
|
-
qontract_reconcile-0.10.2.
|
|
808
|
-
qontract_reconcile-0.10.2.
|
|
809
|
-
qontract_reconcile-0.10.2.
|
|
812
|
+
qontract_reconcile-0.10.2.dev484.dist-info/METADATA,sha256=BlX3qhbod6w_uXAw5N_cu67mBLoQxpBI9AhsXBvluv4,24901
|
|
813
|
+
qontract_reconcile-0.10.2.dev484.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
814
|
+
qontract_reconcile-0.10.2.dev484.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
|
815
|
+
qontract_reconcile-0.10.2.dev484.dist-info/RECORD,,
|
reconcile/cli.py
CHANGED
|
@@ -776,16 +776,25 @@ def github_validator(ctx: click.Context) -> None:
|
|
|
776
776
|
@use_jump_host()
|
|
777
777
|
@click.pass_context
|
|
778
778
|
def openshift_clusterrolebindings(
|
|
779
|
-
ctx: click.Context,
|
|
779
|
+
ctx: click.Context,
|
|
780
|
+
thread_pool_size: int,
|
|
781
|
+
internal: bool,
|
|
782
|
+
use_jump_host: bool,
|
|
780
783
|
) -> None:
|
|
781
|
-
|
|
784
|
+
from reconcile.openshift_bindings.openshift_clusterrolebindings import (
|
|
785
|
+
OpenShiftClusterRoleBindingsIntegration,
|
|
786
|
+
OpenShiftClusterRoleBindingsIntegrationParams,
|
|
787
|
+
)
|
|
782
788
|
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
+
run_class_integration(
|
|
790
|
+
integration=OpenShiftClusterRoleBindingsIntegration(
|
|
791
|
+
OpenShiftClusterRoleBindingsIntegrationParams(
|
|
792
|
+
thread_pool_size=thread_pool_size,
|
|
793
|
+
internal=internal,
|
|
794
|
+
use_jump_host=use_jump_host,
|
|
795
|
+
)
|
|
796
|
+
),
|
|
797
|
+
ctx=ctx,
|
|
789
798
|
)
|
|
790
799
|
|
|
791
800
|
|
|
@@ -808,15 +817,21 @@ def openshift_rolebindings(
|
|
|
808
817
|
use_jump_host: bool,
|
|
809
818
|
support_role_ref: bool,
|
|
810
819
|
) -> None:
|
|
811
|
-
|
|
820
|
+
from reconcile.openshift_bindings.openshift_rolebindings import (
|
|
821
|
+
OpenShiftRoleBindingsIntegration,
|
|
822
|
+
OpenShiftRoleBindingsIntegrationParams,
|
|
823
|
+
)
|
|
812
824
|
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
825
|
+
run_class_integration(
|
|
826
|
+
integration=OpenShiftRoleBindingsIntegration(
|
|
827
|
+
OpenShiftRoleBindingsIntegrationParams(
|
|
828
|
+
thread_pool_size=thread_pool_size,
|
|
829
|
+
internal=internal,
|
|
830
|
+
use_jump_host=use_jump_host,
|
|
831
|
+
support_role_ref=support_role_ref,
|
|
832
|
+
)
|
|
833
|
+
),
|
|
834
|
+
ctx=ctx,
|
|
820
835
|
)
|
|
821
836
|
|
|
822
837
|
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Generated by qenerate plugin=pydantic_v2. 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
|
+
ConfigDict,
|
|
16
|
+
Field,
|
|
17
|
+
Json,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
DEFINITION = """
|
|
22
|
+
query AppInterfaceClusterRoles {
|
|
23
|
+
clusterRoles: roles_v1 {
|
|
24
|
+
name
|
|
25
|
+
users {
|
|
26
|
+
org_username
|
|
27
|
+
github_username
|
|
28
|
+
}
|
|
29
|
+
bots {
|
|
30
|
+
openshift_serviceaccount
|
|
31
|
+
}
|
|
32
|
+
access {
|
|
33
|
+
cluster {
|
|
34
|
+
name
|
|
35
|
+
auth {
|
|
36
|
+
service
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
clusterRole
|
|
40
|
+
}
|
|
41
|
+
expirationDate
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class ConfiguredBaseModel(BaseModel):
|
|
48
|
+
model_config = ConfigDict(
|
|
49
|
+
extra='forbid'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class UserV1(ConfiguredBaseModel):
|
|
54
|
+
org_username: str = Field(..., alias="org_username")
|
|
55
|
+
github_username: str = Field(..., alias="github_username")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class BotV1(ConfiguredBaseModel):
|
|
59
|
+
openshift_serviceaccount: Optional[str] = Field(..., alias="openshift_serviceaccount")
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class ClusterAuthV1(ConfiguredBaseModel):
|
|
63
|
+
service: str = Field(..., alias="service")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class ClusterV1(ConfiguredBaseModel):
|
|
67
|
+
name: str = Field(..., alias="name")
|
|
68
|
+
auth: list[ClusterAuthV1] = Field(..., alias="auth")
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class AccessV1(ConfiguredBaseModel):
|
|
72
|
+
cluster: Optional[ClusterV1] = Field(..., alias="cluster")
|
|
73
|
+
cluster_role: Optional[str] = Field(..., alias="clusterRole")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RoleV1(ConfiguredBaseModel):
|
|
77
|
+
name: str = Field(..., alias="name")
|
|
78
|
+
users: list[UserV1] = Field(..., alias="users")
|
|
79
|
+
bots: list[BotV1] = Field(..., alias="bots")
|
|
80
|
+
access: Optional[list[AccessV1]] = Field(..., alias="access")
|
|
81
|
+
expiration_date: Optional[str] = Field(..., alias="expirationDate")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
class AppInterfaceClusterRolesQueryData(ConfiguredBaseModel):
|
|
85
|
+
cluster_roles: Optional[list[RoleV1]] = Field(..., alias="clusterRoles")
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def query(query_func: Callable, **kwargs: Any) -> AppInterfaceClusterRolesQueryData:
|
|
89
|
+
"""
|
|
90
|
+
This is a convenience function which queries and parses the data into
|
|
91
|
+
concrete types. It should be compatible with most GQL clients.
|
|
92
|
+
You do not have to use it to consume the generated data classes.
|
|
93
|
+
Alternatively, you can also mime and alternate the behavior
|
|
94
|
+
of this function in the caller.
|
|
95
|
+
|
|
96
|
+
Parameters:
|
|
97
|
+
query_func (Callable): Function which queries your GQL Server
|
|
98
|
+
kwargs: optional arguments that will be passed to the query function
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
AppInterfaceClusterRolesQueryData: queried data parsed into generated classes
|
|
102
|
+
"""
|
|
103
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
|
104
|
+
return AppInterfaceClusterRolesQueryData(**raw_data)
|
|
File without changes
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
OPENSHIFT_ROLEBINDINGS_INTEGRATION_NAME = "openshift-rolebindings"
|
|
2
|
+
OPENSHIFT_CLUSTERROLEBINDINGS_INTEGRATION_NAME = "openshift-clusterrolebindings"
|
|
3
|
+
|
|
4
|
+
ROLE_BINDING_RESOURCE_KIND = "RoleBinding"
|
|
5
|
+
CLUSTER_ROLE_BINDING_RESOURCE_KIND = "ClusterRoleBinding"
|
|
6
|
+
ROLE_KIND = "Role"
|
|
7
|
+
CLUSTER_ROLE_KIND = "ClusterRole"
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""Shared data models for openshift-rolebindings and openshift-clusterrolebindings integrations."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Sequence
|
|
4
|
+
from typing import Any, Self
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel
|
|
7
|
+
|
|
8
|
+
import reconcile.openshift_base as ob
|
|
9
|
+
from reconcile.gql_definitions.common.app_interface_clusterrole import (
|
|
10
|
+
BotV1 as ClusterBotV1,
|
|
11
|
+
)
|
|
12
|
+
from reconcile.gql_definitions.common.app_interface_clusterrole import (
|
|
13
|
+
ClusterV1 as ClusterRoleClusterV1,
|
|
14
|
+
)
|
|
15
|
+
from reconcile.gql_definitions.common.app_interface_clusterrole import (
|
|
16
|
+
RoleV1 as ClusterRoleV1,
|
|
17
|
+
)
|
|
18
|
+
from reconcile.gql_definitions.common.app_interface_clusterrole import (
|
|
19
|
+
UserV1 as ClusterUserV1,
|
|
20
|
+
)
|
|
21
|
+
from reconcile.gql_definitions.common.app_interface_roles import (
|
|
22
|
+
AccessV1,
|
|
23
|
+
NamespaceV1,
|
|
24
|
+
RoleV1,
|
|
25
|
+
UserV1,
|
|
26
|
+
)
|
|
27
|
+
from reconcile.gql_definitions.common.app_interface_roles import (
|
|
28
|
+
BotV1 as RoleBotV1,
|
|
29
|
+
)
|
|
30
|
+
from reconcile.gql_definitions.common.app_interface_roles import (
|
|
31
|
+
ClusterV1 as RoleClusterV1,
|
|
32
|
+
)
|
|
33
|
+
from reconcile.openshift_bindings.constants import (
|
|
34
|
+
CLUSTER_ROLE_BINDING_RESOURCE_KIND,
|
|
35
|
+
CLUSTER_ROLE_KIND,
|
|
36
|
+
ROLE_BINDING_RESOURCE_KIND,
|
|
37
|
+
ROLE_KIND,
|
|
38
|
+
)
|
|
39
|
+
from reconcile.openshift_bindings.utils import is_valid_namespace
|
|
40
|
+
from reconcile.utils.openshift_resource import OpenshiftResource as OR
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def get_usernames_from_users(
|
|
44
|
+
users: Sequence[UserV1 | ClusterUserV1] | None,
|
|
45
|
+
user_keys: list[str] | None = None,
|
|
46
|
+
) -> set[str]:
|
|
47
|
+
return {
|
|
48
|
+
name
|
|
49
|
+
for user in users or []
|
|
50
|
+
for user_key in user_keys or []
|
|
51
|
+
if (name := getattr(user, user_key, None))
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class OCResource(BaseModel, arbitrary_types_allowed=True):
|
|
56
|
+
"""Represents an OpenShift resource with metadata."""
|
|
57
|
+
|
|
58
|
+
resource: OR
|
|
59
|
+
resource_name: str
|
|
60
|
+
privileged: bool = False
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class OCResourceData(BaseModel):
|
|
64
|
+
body: dict[str, Any]
|
|
65
|
+
name: str
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class ServiceAccountSpec(BaseModel):
|
|
69
|
+
"""Service account specification with namespace and name."""
|
|
70
|
+
|
|
71
|
+
sa_namespace_name: str
|
|
72
|
+
sa_name: str
|
|
73
|
+
|
|
74
|
+
@classmethod
|
|
75
|
+
def from_bots(cls, bots: Sequence[RoleBotV1 | ClusterBotV1] | None) -> list[Self]:
|
|
76
|
+
"""Create ServiceAccountSpec list from bot configurations."""
|
|
77
|
+
return [
|
|
78
|
+
cls(
|
|
79
|
+
sa_namespace_name=full_service_account[0],
|
|
80
|
+
sa_name=full_service_account[1],
|
|
81
|
+
)
|
|
82
|
+
for bot in bots or []
|
|
83
|
+
if bot.openshift_serviceaccount
|
|
84
|
+
and (full_service_account := bot.openshift_serviceaccount.split("/"))
|
|
85
|
+
and len(full_service_account) == 2
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class BindingSpec(BaseModel, arbitrary_types_allowed=True):
|
|
90
|
+
"""Base specification for role bindings (cluster or namespace scoped)."""
|
|
91
|
+
|
|
92
|
+
role_name: str
|
|
93
|
+
role_kind: str # "Role" or "ClusterRole"
|
|
94
|
+
cluster: RoleClusterV1 | ClusterRoleClusterV1
|
|
95
|
+
resource_kind: str
|
|
96
|
+
usernames: set[str]
|
|
97
|
+
openshift_service_accounts: list[ServiceAccountSpec]
|
|
98
|
+
|
|
99
|
+
def get_oc_resources(self) -> list[OCResourceData]:
|
|
100
|
+
user_oc_resources = [
|
|
101
|
+
self.construct_user_oc_resource(username) for username in self.usernames
|
|
102
|
+
]
|
|
103
|
+
sa_oc_resources = [
|
|
104
|
+
self.construct_sa_oc_resource(sa.sa_namespace_name, sa.sa_name)
|
|
105
|
+
for sa in self.openshift_service_accounts
|
|
106
|
+
]
|
|
107
|
+
return user_oc_resources + sa_oc_resources
|
|
108
|
+
|
|
109
|
+
def construct_user_oc_resource(self, username: str) -> OCResourceData:
|
|
110
|
+
name = f"{self.role_name}-{username}"
|
|
111
|
+
body: dict[str, Any] = {
|
|
112
|
+
"apiVersion": "rbac.authorization.k8s.io/v1",
|
|
113
|
+
"kind": self.resource_kind,
|
|
114
|
+
"metadata": {"name": name},
|
|
115
|
+
"roleRef": {"kind": self.role_kind, "name": self.role_name},
|
|
116
|
+
"subjects": [{"kind": "User", "name": username}],
|
|
117
|
+
}
|
|
118
|
+
return OCResourceData(body=body, name=name)
|
|
119
|
+
|
|
120
|
+
def construct_sa_oc_resource(
|
|
121
|
+
self, sa_namespace_name: str, sa_name: str
|
|
122
|
+
) -> OCResourceData:
|
|
123
|
+
name = f"{self.role_name}-{sa_namespace_name}-{sa_name}"
|
|
124
|
+
body: dict[str, Any] = {
|
|
125
|
+
"apiVersion": "rbac.authorization.k8s.io/v1",
|
|
126
|
+
"kind": self.resource_kind,
|
|
127
|
+
"metadata": {"name": name},
|
|
128
|
+
"roleRef": {"kind": self.role_kind, "name": self.role_name},
|
|
129
|
+
"subjects": [
|
|
130
|
+
{
|
|
131
|
+
"kind": "ServiceAccount",
|
|
132
|
+
"name": sa_name,
|
|
133
|
+
"namespace": sa_namespace_name,
|
|
134
|
+
}
|
|
135
|
+
],
|
|
136
|
+
}
|
|
137
|
+
return OCResourceData(body=body, name=name)
|
|
138
|
+
|
|
139
|
+
def get_openshift_resources(
|
|
140
|
+
self,
|
|
141
|
+
integration_name: str,
|
|
142
|
+
integration_version: str,
|
|
143
|
+
privileged: bool = False,
|
|
144
|
+
) -> list[OCResource]:
|
|
145
|
+
oc_resources = [
|
|
146
|
+
OCResource(
|
|
147
|
+
resource=OR(
|
|
148
|
+
oc_resource_data.body,
|
|
149
|
+
integration_name,
|
|
150
|
+
integration_version,
|
|
151
|
+
error_details=oc_resource_data.name,
|
|
152
|
+
),
|
|
153
|
+
resource_name=oc_resource_data.name,
|
|
154
|
+
privileged=privileged,
|
|
155
|
+
)
|
|
156
|
+
for oc_resource_data in self.get_oc_resources()
|
|
157
|
+
]
|
|
158
|
+
return oc_resources
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class RoleBindingSpec(BindingSpec):
|
|
162
|
+
"""Namespace-scoped RoleBinding specification."""
|
|
163
|
+
|
|
164
|
+
namespace: NamespaceV1
|
|
165
|
+
privileged: bool = False
|
|
166
|
+
|
|
167
|
+
@classmethod
|
|
168
|
+
def create_role_binding_spec(
|
|
169
|
+
cls,
|
|
170
|
+
access: AccessV1,
|
|
171
|
+
users: list[UserV1] | None = None,
|
|
172
|
+
enforced_user_keys: list[str] | None = None,
|
|
173
|
+
bots: list[RoleBotV1] | None = None,
|
|
174
|
+
support_role_ref: bool = False,
|
|
175
|
+
) -> Self | None:
|
|
176
|
+
"""Create a RoleBindingSpec from access configuration."""
|
|
177
|
+
if not access.namespace:
|
|
178
|
+
return None
|
|
179
|
+
if not (access.role or access.cluster_role):
|
|
180
|
+
return None
|
|
181
|
+
privileged = access.namespace.cluster_admin or False
|
|
182
|
+
auth_dict = [
|
|
183
|
+
auth.model_dump(by_alias=True) for auth in access.namespace.cluster.auth
|
|
184
|
+
]
|
|
185
|
+
usernames = get_usernames_from_users(
|
|
186
|
+
users,
|
|
187
|
+
ob.determine_user_keys_for_access(
|
|
188
|
+
access.namespace.cluster.name,
|
|
189
|
+
auth_dict,
|
|
190
|
+
enforced_user_keys,
|
|
191
|
+
),
|
|
192
|
+
)
|
|
193
|
+
service_accounts = ServiceAccountSpec.from_bots(bots) if bots else []
|
|
194
|
+
role_kind = ROLE_KIND if access.role and support_role_ref else CLUSTER_ROLE_KIND
|
|
195
|
+
return cls(
|
|
196
|
+
role_name=access.role or access.cluster_role,
|
|
197
|
+
role_kind=role_kind,
|
|
198
|
+
namespace=access.namespace,
|
|
199
|
+
cluster=access.namespace.cluster,
|
|
200
|
+
privileged=privileged,
|
|
201
|
+
usernames=usernames,
|
|
202
|
+
openshift_service_accounts=service_accounts,
|
|
203
|
+
resource_kind=ROLE_BINDING_RESOURCE_KIND,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def create_rb_specs_from_role(
|
|
208
|
+
cls,
|
|
209
|
+
role: RoleV1,
|
|
210
|
+
enforced_user_keys: list[str] | None = None,
|
|
211
|
+
support_role_ref: bool = False,
|
|
212
|
+
) -> list[Self]:
|
|
213
|
+
"""Create list of RoleBindingSpec from a role configuration."""
|
|
214
|
+
rolebinding_spec_list = [
|
|
215
|
+
role_binding_spec
|
|
216
|
+
for access in role.access or []
|
|
217
|
+
if (
|
|
218
|
+
access.namespace
|
|
219
|
+
and is_valid_namespace(access.namespace)
|
|
220
|
+
and (
|
|
221
|
+
role_binding_spec := cls.create_role_binding_spec(
|
|
222
|
+
access,
|
|
223
|
+
role.users,
|
|
224
|
+
enforced_user_keys,
|
|
225
|
+
role.bots,
|
|
226
|
+
support_role_ref,
|
|
227
|
+
)
|
|
228
|
+
)
|
|
229
|
+
)
|
|
230
|
+
]
|
|
231
|
+
return rolebinding_spec_list
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ClusterRoleBindingSpec(BindingSpec):
|
|
235
|
+
"""Cluster-scoped ClusterRoleBinding specification."""
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def create_cluster_role_binding_specs(
|
|
239
|
+
cls, cluster_role: ClusterRoleV1
|
|
240
|
+
) -> list[Self]:
|
|
241
|
+
cluster_role_binding_specs = [
|
|
242
|
+
cls(
|
|
243
|
+
cluster=access.cluster,
|
|
244
|
+
usernames=get_usernames_from_users(
|
|
245
|
+
users=cluster_role.users,
|
|
246
|
+
user_keys=cls.get_user_keys(access.cluster),
|
|
247
|
+
),
|
|
248
|
+
openshift_service_accounts=ServiceAccountSpec.from_bots(
|
|
249
|
+
cluster_role.bots
|
|
250
|
+
),
|
|
251
|
+
role_name=access.cluster_role,
|
|
252
|
+
role_kind=CLUSTER_ROLE_KIND,
|
|
253
|
+
resource_kind=CLUSTER_ROLE_BINDING_RESOURCE_KIND,
|
|
254
|
+
)
|
|
255
|
+
for access in cluster_role.access or []
|
|
256
|
+
if access.cluster and access.cluster_role
|
|
257
|
+
]
|
|
258
|
+
return cluster_role_binding_specs
|
|
259
|
+
|
|
260
|
+
@classmethod
|
|
261
|
+
def get_user_keys(cls, cluster: ClusterRoleClusterV1) -> list[str] | None:
|
|
262
|
+
auth_dict = [auth.model_dump(by_alias=True) for auth in cluster.auth]
|
|
263
|
+
user_keys = ob.determine_user_keys_for_access(cluster.name, auth_dict)
|
|
264
|
+
return user_keys
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"""OpenShift ClusterRoleBindings integration.
|
|
2
|
+
|
|
3
|
+
Manages cluster-scoped ClusterRoleBindings across OpenShift clusters.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import TYPE_CHECKING
|
|
9
|
+
|
|
10
|
+
import reconcile.openshift_base as ob
|
|
11
|
+
from reconcile.openshift_bindings.constants import (
|
|
12
|
+
OPENSHIFT_CLUSTERROLEBINDINGS_INTEGRATION_NAME,
|
|
13
|
+
)
|
|
14
|
+
from reconcile.openshift_bindings.models import ClusterRoleBindingSpec
|
|
15
|
+
from reconcile.typed_queries.app_interface_clusterroles import (
|
|
16
|
+
get_app_interface_clusterroles,
|
|
17
|
+
)
|
|
18
|
+
from reconcile.typed_queries.clusters import get_clusters
|
|
19
|
+
from reconcile.utils import expiration
|
|
20
|
+
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
21
|
+
from reconcile.utils.defer import defer
|
|
22
|
+
from reconcile.utils.oc import OC_Map
|
|
23
|
+
from reconcile.utils.openshift_resource import ResourceInventory
|
|
24
|
+
from reconcile.utils.runtime.integration import (
|
|
25
|
+
PydanticRunParams,
|
|
26
|
+
QontractReconcileIntegration,
|
|
27
|
+
)
|
|
28
|
+
from reconcile.utils.semver_helper import make_semver
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from reconcile.gql_definitions.common.app_interface_clusterrole import RoleV1
|
|
32
|
+
|
|
33
|
+
QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
|
34
|
+
QONTRACT_INTEGRATION_MANAGED_TYPE = "ClusterRoleBinding.rbac.authorization.k8s.io"
|
|
35
|
+
NAMESPACE_CLUSTER_SCOPE = "cluster"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class OpenShiftClusterRoleBindingsIntegrationParams(PydanticRunParams):
|
|
39
|
+
thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE
|
|
40
|
+
internal: bool | None = None
|
|
41
|
+
use_jump_host: bool = True
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class OpenShiftClusterRoleBindingsIntegration(
|
|
45
|
+
QontractReconcileIntegration[OpenShiftClusterRoleBindingsIntegrationParams],
|
|
46
|
+
):
|
|
47
|
+
"""Manages ClusterRoleBindings across OpenShift clusters."""
|
|
48
|
+
|
|
49
|
+
@defer
|
|
50
|
+
def run(self, dry_run: bool, defer: Callable | None = None) -> None:
|
|
51
|
+
ri, oc_map = self.fetch_current_state()
|
|
52
|
+
if defer:
|
|
53
|
+
defer(oc_map.cleanup)
|
|
54
|
+
self.fetch_desired_state(
|
|
55
|
+
ri,
|
|
56
|
+
allowed_clusters=set(oc_map.clusters()),
|
|
57
|
+
)
|
|
58
|
+
ob.publish_metrics(ri, self.name)
|
|
59
|
+
ob.realize_data(dry_run, oc_map, ri, self.params.thread_pool_size)
|
|
60
|
+
if ri.has_error_registered():
|
|
61
|
+
sys.exit(1)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def name(self) -> str:
|
|
65
|
+
return OPENSHIFT_CLUSTERROLEBINDINGS_INTEGRATION_NAME
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def integration_version(self) -> str:
|
|
69
|
+
return QONTRACT_INTEGRATION_VERSION
|
|
70
|
+
|
|
71
|
+
def fetch_current_state(self) -> tuple[ResourceInventory, OC_Map]:
|
|
72
|
+
clusters = [
|
|
73
|
+
cluster.model_dump(by_alias=True)
|
|
74
|
+
for cluster in get_clusters()
|
|
75
|
+
if cluster.managed_cluster_roles and cluster.automation_token is not None
|
|
76
|
+
]
|
|
77
|
+
return ob.fetch_current_state(
|
|
78
|
+
clusters=clusters,
|
|
79
|
+
thread_pool_size=self.params.thread_pool_size,
|
|
80
|
+
integration=self.name,
|
|
81
|
+
integration_version=self.integration_version,
|
|
82
|
+
override_managed_types=[QONTRACT_INTEGRATION_MANAGED_TYPE],
|
|
83
|
+
internal=self.params.internal,
|
|
84
|
+
use_jump_host=self.params.use_jump_host,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def fetch_desired_state(
|
|
88
|
+
self,
|
|
89
|
+
ri: ResourceInventory | None,
|
|
90
|
+
allowed_clusters: set[str] | None = None,
|
|
91
|
+
) -> None:
|
|
92
|
+
if allowed_clusters is not None and not allowed_clusters:
|
|
93
|
+
return
|
|
94
|
+
if ri is None:
|
|
95
|
+
return
|
|
96
|
+
cluster_roles: list[RoleV1] = expiration.filter(
|
|
97
|
+
get_app_interface_clusterroles()
|
|
98
|
+
)
|
|
99
|
+
cluster_role_binding_specs = [
|
|
100
|
+
cluster_role_binding_spec
|
|
101
|
+
for cluster_role in cluster_roles
|
|
102
|
+
for cluster_role_binding_spec in ClusterRoleBindingSpec.create_cluster_role_binding_specs(
|
|
103
|
+
cluster_role
|
|
104
|
+
)
|
|
105
|
+
]
|
|
106
|
+
if allowed_clusters:
|
|
107
|
+
cluster_role_binding_specs = [
|
|
108
|
+
cluster_role_binding_spec
|
|
109
|
+
for cluster_role_binding_spec in cluster_role_binding_specs
|
|
110
|
+
if cluster_role_binding_spec.cluster.name in allowed_clusters
|
|
111
|
+
]
|
|
112
|
+
for cluster_role_binding_spec in cluster_role_binding_specs:
|
|
113
|
+
for oc_resource in cluster_role_binding_spec.get_openshift_resources(
|
|
114
|
+
self.name,
|
|
115
|
+
self.integration_version,
|
|
116
|
+
):
|
|
117
|
+
if not ri.get_desired(
|
|
118
|
+
cluster_role_binding_spec.cluster.name,
|
|
119
|
+
NAMESPACE_CLUSTER_SCOPE,
|
|
120
|
+
QONTRACT_INTEGRATION_MANAGED_TYPE,
|
|
121
|
+
oc_resource.resource_name,
|
|
122
|
+
):
|
|
123
|
+
ri.add_desired(
|
|
124
|
+
cluster=cluster_role_binding_spec.cluster.name,
|
|
125
|
+
namespace=NAMESPACE_CLUSTER_SCOPE,
|
|
126
|
+
resource_type=QONTRACT_INTEGRATION_MANAGED_TYPE,
|
|
127
|
+
name=oc_resource.resource_name,
|
|
128
|
+
value=oc_resource.resource,
|
|
129
|
+
)
|