qontract-reconcile 0.10.2.dev109__py3-none-any.whl → 0.10.2.dev111__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.dev109.dist-info → qontract_reconcile-0.10.2.dev111.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev109.dist-info → qontract_reconcile-0.10.2.dev111.dist-info}/RECORD +12 -7
- reconcile/automated_actions/__init__.py +0 -0
- reconcile/automated_actions/config/__init__.py +0 -0
- reconcile/automated_actions/config/integration.py +300 -0
- reconcile/cli.py +23 -0
- reconcile/gql_definitions/automated_actions/__init__.py +0 -0
- reconcile/gql_definitions/automated_actions/instance.py +204 -0
- reconcile/gql_definitions/introspection.json +838 -137
- reconcile/utils/promtool.py +1 -1
- {qontract_reconcile-0.10.2.dev109.dist-info → qontract_reconcile-0.10.2.dev111.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev109.dist-info → qontract_reconcile-0.10.2.dev111.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev109.dist-info → qontract_reconcile-0.10.2.dev111.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.dev111
|
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.dev109.dist-info → qontract_reconcile-0.10.2.dev111.dist-info}/RECORD
RENAMED
@@ -10,7 +10,7 @@ reconcile/aws_iam_password_reset.py,sha256=q96mwr2KeEQ5bpNniGlgIMZTxiuLSodcYfX-t
|
|
10
10
|
reconcile/aws_support_cases_sos.py,sha256=hl_9L53yQYRQxKs3IWrd69Cc60XK067g_bJRM9B0udo,2975
|
11
11
|
reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
|
12
12
|
reconcile/checkpoint.py,sha256=_JhMxrye5BgkRMxWYuf7Upli6XayPINKSsuo3ynHTRc,5010
|
13
|
-
reconcile/cli.py,sha256=
|
13
|
+
reconcile/cli.py,sha256=hwqPcZVmazrhzq1esPBk5jNHSzpfr7o9EmuuMqiPxfg,108430
|
14
14
|
reconcile/closedbox_endpoint_monitoring_base.py,sha256=MvGKBqH9PdHWdMjhLuptze-dk0Tifhp3-0SZdI-7Fmo,4862
|
15
15
|
reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
|
16
16
|
reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
|
@@ -139,6 +139,9 @@ reconcile/aus/version_gates/handler.py,sha256=S_isQPYHbG4DERiUEvQBZ6ngiFX3uMmATA
|
|
139
139
|
reconcile/aus/version_gates/ingress_gate_handler.py,sha256=ZCtyggBzzcb0prtdbMpJsVkj5leYN-vS7srM9vbq9xo,1096
|
140
140
|
reconcile/aus/version_gates/ocp_gate_handler.py,sha256=RW1ppDaCZXVegV9AzzqYXxDUu_Z_7d43Z5h2Pk_piKc,716
|
141
141
|
reconcile/aus/version_gates/sts_version_gate_handler.py,sha256=swwwz0YyvrEBf_InqrRRBCt2QzHYNvvq8jz9aYwElh4,3663
|
142
|
+
reconcile/automated_actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
143
|
+
reconcile/automated_actions/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
144
|
+
reconcile/automated_actions/config/integration.py,sha256=cf1VxolcjlGP-xO7IIH5A00Qr4znNJTT7a_sU18ABig,10755
|
142
145
|
reconcile/aws_account_manager/README.md,sha256=_XFM3GZNHUzv--e_navqJuaUWpjC6QrHfulreHynFf0,262
|
143
146
|
reconcile/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
144
147
|
reconcile/aws_account_manager/integration.py,sha256=WOE_mi45pvJW4TNKh4mCGSViJiElH--T4Wg0CLMYxpY,14988
|
@@ -224,7 +227,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=BgMx-NyV9mTuv7Sotb2OioC
|
|
224
227
|
reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
225
228
|
reconcile/glitchtip_project_dsn/integration.py,sha256=2iugub-kHYkHNK33n0v9_TeWonuxCPah_VkoTPvaajE,8077
|
226
229
|
reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
227
|
-
reconcile/gql_definitions/introspection.json,sha256=
|
230
|
+
reconcile/gql_definitions/introspection.json,sha256=Qzf2HgsfNMLNW7EBIpdB4MX-6DjIdXOHtjVzdiBmczQ,2280010
|
228
231
|
reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
229
232
|
reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
|
230
233
|
reconcile/gql_definitions/acs/acs_policies.py,sha256=bN5i4mks10Z23KJSj7jqp966Osq2dps4d-sPH9gjxEA,7008
|
@@ -237,6 +240,8 @@ reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py,sh
|
|
237
240
|
reconcile/gql_definitions/app_sre_tekton_access_revalidation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
238
241
|
reconcile/gql_definitions/app_sre_tekton_access_revalidation/roles.py,sha256=8Y4NsS5T7tumDWxY5MuoV50MK2i-DsLYSpCRjb7KaLE,2353
|
239
242
|
reconcile/gql_definitions/app_sre_tekton_access_revalidation/users.py,sha256=XdVxBxiyTR6Cy939EHNw__0k7iWrZWlhrgS5DakST0I,2504
|
243
|
+
reconcile/gql_definitions/automated_actions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
244
|
+
reconcile/gql_definitions/automated_actions/instance.py,sha256=D50v0exLeK5hvjQKvxQ5V-6DnYZo_L6cX_Gaf3PGREs,5491
|
240
245
|
reconcile/gql_definitions/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
241
246
|
reconcile/gql_definitions/aws_account_manager/aws_accounts.py,sha256=JdqtE3gMpeodymPJST-aFVkYP_MO--_CcwjF070R5Cs,4883
|
242
247
|
reconcile/gql_definitions/aws_ami_cleanup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -630,7 +635,7 @@ reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv
|
|
630
635
|
reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
|
631
636
|
reconcile/utils/prometheus.py,sha256=Ad0rwLbxRuuYjHwkwJloHEdK0bvy42h-p-HIT1DhDhs,3832
|
632
637
|
reconcile/utils/promotion_state.py,sha256=McSgGj3oog83ThJCrMR2v8q6Xb_Pxij-HEe_RbDu8cg,3946
|
633
|
-
reconcile/utils/promtool.py,sha256=
|
638
|
+
reconcile/utils/promtool.py,sha256=xmPBWEApkk0L2qZBAvTxakNXxfTz-tVLPFxGnpsxXnM,2831
|
634
639
|
reconcile/utils/quay_api.py,sha256=uE_jxcdy3ViHtYFAfwDQuFDaO7Pr6AAPoVnmORbyHio,7822
|
635
640
|
reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
|
636
641
|
reconcile/utils/repo_owners.py,sha256=BHrAXxKyvn4qWJwFPWYGTtfgnLmYnWtYFEJGFeD__FE,6573
|
@@ -786,7 +791,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
786
791
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
787
792
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
788
793
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
789
|
-
qontract_reconcile-0.10.2.
|
790
|
-
qontract_reconcile-0.10.2.
|
791
|
-
qontract_reconcile-0.10.2.
|
792
|
-
qontract_reconcile-0.10.2.
|
794
|
+
qontract_reconcile-0.10.2.dev111.dist-info/METADATA,sha256=Md2jyCPz6LwUfZ7bX6OjsZyLcD2I_z7JpH5SAKt_Zl0,24566
|
795
|
+
qontract_reconcile-0.10.2.dev111.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
796
|
+
qontract_reconcile-0.10.2.dev111.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
797
|
+
qontract_reconcile-0.10.2.dev111.dist-info/RECORD,,
|
File without changes
|
File without changes
|
@@ -0,0 +1,300 @@
|
|
1
|
+
import logging
|
2
|
+
from collections.abc import (
|
3
|
+
Callable,
|
4
|
+
Generator,
|
5
|
+
Iterable,
|
6
|
+
)
|
7
|
+
from typing import Any
|
8
|
+
|
9
|
+
import yaml
|
10
|
+
from kubernetes.client import ( # type: ignore[attr-defined]
|
11
|
+
ApiClient,
|
12
|
+
V1ConfigMap,
|
13
|
+
V1ObjectMeta,
|
14
|
+
)
|
15
|
+
from pydantic import BaseModel
|
16
|
+
|
17
|
+
import reconcile.openshift_base as ob
|
18
|
+
from reconcile.gql_definitions.automated_actions.instance import (
|
19
|
+
AutomatedActionArgumentOpenshiftV1,
|
20
|
+
AutomatedActionArgumentV1,
|
21
|
+
AutomatedActionsInstanceV1,
|
22
|
+
PermissionAutomatedActionsV1,
|
23
|
+
)
|
24
|
+
from reconcile.gql_definitions.automated_actions.instance import query as instance_query
|
25
|
+
from reconcile.utils import expiration, gql
|
26
|
+
from reconcile.utils.defer import defer
|
27
|
+
from reconcile.utils.disabled_integrations import integration_is_enabled
|
28
|
+
from reconcile.utils.oc import OCCli
|
29
|
+
from reconcile.utils.oc_map import init_oc_map_from_namespaces
|
30
|
+
from reconcile.utils.openshift_resource import OpenshiftResource, ResourceInventory
|
31
|
+
from reconcile.utils.runtime.integration import (
|
32
|
+
PydanticRunParams,
|
33
|
+
QontractReconcileIntegration,
|
34
|
+
)
|
35
|
+
from reconcile.utils.semver_helper import make_semver
|
36
|
+
|
37
|
+
QONTRACT_INTEGRATION = "automated-actions-config"
|
38
|
+
QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
39
|
+
|
40
|
+
|
41
|
+
class AutomatedActionsConfigIntegrationParams(PydanticRunParams):
|
42
|
+
thread_pool_size: int
|
43
|
+
use_jump_host: bool
|
44
|
+
internal: bool | None = None
|
45
|
+
configmap_name: str = "automated-actions-policy"
|
46
|
+
|
47
|
+
|
48
|
+
class AutomatedActionsUser(BaseModel):
|
49
|
+
username: str
|
50
|
+
roles: set[str]
|
51
|
+
|
52
|
+
|
53
|
+
class AutomatedActionsPolicy(BaseModel):
|
54
|
+
sub: str
|
55
|
+
obj: str
|
56
|
+
params: dict[str, str] = {}
|
57
|
+
|
58
|
+
|
59
|
+
AutomatedActionRoles = dict[str, list[AutomatedActionsPolicy]]
|
60
|
+
|
61
|
+
|
62
|
+
class AutomatedActionsConfigIntegration(
|
63
|
+
QontractReconcileIntegration[AutomatedActionsConfigIntegrationParams]
|
64
|
+
):
|
65
|
+
"""Manage LDAP groups based on App-Interface roles."""
|
66
|
+
|
67
|
+
@property
|
68
|
+
def name(self) -> str:
|
69
|
+
return QONTRACT_INTEGRATION
|
70
|
+
|
71
|
+
def get_early_exit_desired_state(
|
72
|
+
self, query_func: Callable | None = None
|
73
|
+
) -> dict[str, Any]:
|
74
|
+
"""Return the desired state for early exit."""
|
75
|
+
if not query_func:
|
76
|
+
query_func = gql.get_api().query
|
77
|
+
return {
|
78
|
+
"automated_actions_instances": [
|
79
|
+
c.dict() for c in self.get_automated_actions_instances(query_func)
|
80
|
+
]
|
81
|
+
}
|
82
|
+
|
83
|
+
def get_automated_actions_instances(
|
84
|
+
self, query_func: Callable
|
85
|
+
) -> Generator[AutomatedActionsInstanceV1, None, None]:
|
86
|
+
"""Return all automated actions."""
|
87
|
+
data = instance_query(query_func, variables={})
|
88
|
+
for instance in data.automated_actions_instances_v1 or []:
|
89
|
+
if instance.deployment.delete:
|
90
|
+
continue
|
91
|
+
instance.permissions = list(
|
92
|
+
self.filter_permissions(instance.permissions or [])
|
93
|
+
)
|
94
|
+
yield instance
|
95
|
+
|
96
|
+
def is_enabled(self, argument: AutomatedActionArgumentV1) -> bool:
|
97
|
+
"""Check if the integration is enabled for the given argument namespace."""
|
98
|
+
if isinstance(argument, AutomatedActionArgumentOpenshiftV1):
|
99
|
+
return (
|
100
|
+
integration_is_enabled("automated-actions", argument.namespace.cluster)
|
101
|
+
and not argument.namespace.delete
|
102
|
+
)
|
103
|
+
return True
|
104
|
+
|
105
|
+
def filter_permissions(
|
106
|
+
self, permissions: Iterable[PermissionAutomatedActionsV1]
|
107
|
+
) -> Generator[PermissionAutomatedActionsV1, None, None]:
|
108
|
+
"""Filter out expired roles and arguments (cluster.namespace) with disabled integrations."""
|
109
|
+
for permission in permissions:
|
110
|
+
# automated actions disabled for the cluster?
|
111
|
+
permission.arguments = [
|
112
|
+
arg for arg in permission.arguments or [] if self.is_enabled(arg)
|
113
|
+
]
|
114
|
+
# remove expired roles
|
115
|
+
permission.roles = expiration.filter(permission.roles)
|
116
|
+
if not permission.roles:
|
117
|
+
continue
|
118
|
+
yield permission
|
119
|
+
|
120
|
+
def compile_users(
|
121
|
+
self, permissions: Iterable[PermissionAutomatedActionsV1]
|
122
|
+
) -> list[AutomatedActionsUser]:
|
123
|
+
"""Compile all automated actions roles."""
|
124
|
+
users: dict[str, AutomatedActionsUser] = {}
|
125
|
+
for permission in permissions:
|
126
|
+
for role in permission.roles or []:
|
127
|
+
for user in (role.users or []) + (role.bots or []):
|
128
|
+
if not user.org_username:
|
129
|
+
continue
|
130
|
+
aa_user = users.setdefault(
|
131
|
+
user.org_username,
|
132
|
+
AutomatedActionsUser(username=user.org_username, roles=[]),
|
133
|
+
)
|
134
|
+
aa_user.roles.add(role.name)
|
135
|
+
|
136
|
+
return list(users.values())
|
137
|
+
|
138
|
+
def compile_roles(
|
139
|
+
self, permissions: Iterable[PermissionAutomatedActionsV1]
|
140
|
+
) -> AutomatedActionRoles:
|
141
|
+
"""Compile all automated actions policies."""
|
142
|
+
roles: AutomatedActionRoles = {}
|
143
|
+
|
144
|
+
for permission in permissions or []:
|
145
|
+
obj = permission.action.operation_id
|
146
|
+
|
147
|
+
parameters: list[dict[str, str]] = [] if permission.arguments else [{}]
|
148
|
+
for arg in permission.arguments or []:
|
149
|
+
match arg:
|
150
|
+
case AutomatedActionArgumentOpenshiftV1():
|
151
|
+
parameters.append({
|
152
|
+
# all parameter values are regexes in the OPA policy
|
153
|
+
# therefore, cluster and namespace must be fixed to the current strings
|
154
|
+
"cluster": f"^{arg.namespace.cluster.name}$",
|
155
|
+
"namespace": f"^{arg.namespace.name}$",
|
156
|
+
"kind": arg.kind_pattern,
|
157
|
+
"name": arg.name_pattern,
|
158
|
+
})
|
159
|
+
case _:
|
160
|
+
raise NotImplementedError(
|
161
|
+
f"Unsupported argument type: {arg.q_type}"
|
162
|
+
)
|
163
|
+
for role in permission.roles or []:
|
164
|
+
aa_role = roles.setdefault(role.name, [])
|
165
|
+
aa_role.extend(
|
166
|
+
AutomatedActionsPolicy(sub=role.name, obj=obj, params=params)
|
167
|
+
for params in parameters
|
168
|
+
)
|
169
|
+
return roles
|
170
|
+
|
171
|
+
def build_policy_file(
|
172
|
+
self,
|
173
|
+
users: Iterable[AutomatedActionsUser],
|
174
|
+
roles: AutomatedActionRoles,
|
175
|
+
) -> str:
|
176
|
+
"""Build the automated actions casbin policy file."""
|
177
|
+
return yaml.dump(
|
178
|
+
{
|
179
|
+
"users": {user.username: sorted(user.roles) for user in users},
|
180
|
+
"roles": {
|
181
|
+
role: [policy.dict() for policy in policies]
|
182
|
+
for role, policies in roles.items()
|
183
|
+
},
|
184
|
+
},
|
185
|
+
sort_keys=True,
|
186
|
+
)
|
187
|
+
|
188
|
+
def build_desired_configmap(
|
189
|
+
self,
|
190
|
+
ri: ResourceInventory,
|
191
|
+
instance: AutomatedActionsInstanceV1,
|
192
|
+
name: str,
|
193
|
+
data: str,
|
194
|
+
) -> None:
|
195
|
+
"""Build the automated actions configmap."""
|
196
|
+
osr = OpenshiftResource(
|
197
|
+
body=ApiClient().sanitize_for_serialization(
|
198
|
+
V1ConfigMap(
|
199
|
+
api_version="v1",
|
200
|
+
kind="ConfigMap",
|
201
|
+
metadata=V1ObjectMeta(name=name),
|
202
|
+
data={"roles.yml": data},
|
203
|
+
)
|
204
|
+
),
|
205
|
+
integration=QONTRACT_INTEGRATION,
|
206
|
+
integration_version=QONTRACT_INTEGRATION_VERSION,
|
207
|
+
).annotate()
|
208
|
+
ri.initialize_resource_type(
|
209
|
+
cluster=instance.deployment.cluster.name,
|
210
|
+
namespace=instance.deployment.name,
|
211
|
+
resource_type=osr.kind,
|
212
|
+
)
|
213
|
+
ri.add_desired_resource(
|
214
|
+
cluster=instance.deployment.cluster.name,
|
215
|
+
namespace=instance.deployment.name,
|
216
|
+
resource=osr,
|
217
|
+
)
|
218
|
+
|
219
|
+
def fetch_current_configmap(
|
220
|
+
self,
|
221
|
+
ri: ResourceInventory,
|
222
|
+
instance: AutomatedActionsInstanceV1,
|
223
|
+
oc: OCCli,
|
224
|
+
name: str,
|
225
|
+
) -> None:
|
226
|
+
"""Fetch the current automated actions configmap."""
|
227
|
+
item = oc.get(
|
228
|
+
namespace=instance.deployment.name,
|
229
|
+
kind="ConfigMap",
|
230
|
+
name=name,
|
231
|
+
allow_not_found=True,
|
232
|
+
)
|
233
|
+
if not item:
|
234
|
+
return
|
235
|
+
osr = OpenshiftResource(
|
236
|
+
body=item,
|
237
|
+
integration=QONTRACT_INTEGRATION,
|
238
|
+
integration_version=QONTRACT_INTEGRATION_VERSION,
|
239
|
+
)
|
240
|
+
ri.add_current(
|
241
|
+
cluster=instance.deployment.cluster.name,
|
242
|
+
namespace=instance.deployment.name,
|
243
|
+
resource_type=osr.kind,
|
244
|
+
name=osr.name,
|
245
|
+
value=osr,
|
246
|
+
)
|
247
|
+
|
248
|
+
@defer
|
249
|
+
def run(self, dry_run: bool, defer: Callable | None = None) -> None:
|
250
|
+
"""Run the integration."""
|
251
|
+
gql_api = gql.get_api()
|
252
|
+
instances = list(self.get_automated_actions_instances(gql_api.query))
|
253
|
+
if not instances:
|
254
|
+
logging.debug("No instances found.")
|
255
|
+
return
|
256
|
+
|
257
|
+
oc_map = init_oc_map_from_namespaces(
|
258
|
+
namespaces=[instance.deployment for instance in instances],
|
259
|
+
secret_reader=self.secret_reader,
|
260
|
+
integration=QONTRACT_INTEGRATION,
|
261
|
+
use_jump_host=self.params.use_jump_host,
|
262
|
+
thread_pool_size=self.params.thread_pool_size,
|
263
|
+
internal=self.params.internal,
|
264
|
+
)
|
265
|
+
if defer:
|
266
|
+
defer(oc_map.cleanup)
|
267
|
+
ri = ResourceInventory()
|
268
|
+
|
269
|
+
for instance in instances:
|
270
|
+
users = self.compile_users(instance.permissions or [])
|
271
|
+
policies = self.compile_roles(instance.permissions or [])
|
272
|
+
if not users and not policies:
|
273
|
+
logging.info(
|
274
|
+
f"{instance.deployment.cluster.name}/{instance.deployment.name}: No enabled automated actions found. Skipping this instance!"
|
275
|
+
)
|
276
|
+
continue
|
277
|
+
|
278
|
+
self.build_desired_configmap(
|
279
|
+
ri=ri,
|
280
|
+
instance=instance,
|
281
|
+
name=self.params.configmap_name,
|
282
|
+
data=self.build_policy_file(users, policies),
|
283
|
+
)
|
284
|
+
|
285
|
+
oc = oc_map.get_cluster(instance.deployment.cluster.name)
|
286
|
+
if not oc.project_exists(instance.deployment.name):
|
287
|
+
logging.info(
|
288
|
+
f"{instance.deployment.cluster.name}/{instance.deployment.name}: Namespace does not exist (yet). Skipping this instance!"
|
289
|
+
)
|
290
|
+
continue
|
291
|
+
|
292
|
+
self.fetch_current_configmap(
|
293
|
+
ri=ri,
|
294
|
+
instance=instance,
|
295
|
+
oc=oc,
|
296
|
+
name=self.params.configmap_name,
|
297
|
+
)
|
298
|
+
|
299
|
+
ob.publish_metrics(ri, QONTRACT_INTEGRATION)
|
300
|
+
ob.realize_data(dry_run, oc_map, ri, self.params.thread_pool_size)
|
reconcile/cli.py
CHANGED
@@ -3815,6 +3815,29 @@ def external_resources_secrets_sync(
|
|
3815
3815
|
)
|
3816
3816
|
|
3817
3817
|
|
3818
|
+
@integration.command(short_help="Deploy the Automated Actions Config")
|
3819
|
+
@threaded()
|
3820
|
+
@internal()
|
3821
|
+
@use_jump_host()
|
3822
|
+
@click.pass_context
|
3823
|
+
def automated_actions_config(ctx, thread_pool_size, internal, use_jump_host):
|
3824
|
+
from reconcile.automated_actions.config.integration import (
|
3825
|
+
AutomatedActionsConfigIntegration,
|
3826
|
+
AutomatedActionsConfigIntegrationParams,
|
3827
|
+
)
|
3828
|
+
|
3829
|
+
run_class_integration(
|
3830
|
+
integration=AutomatedActionsConfigIntegration(
|
3831
|
+
AutomatedActionsConfigIntegrationParams(
|
3832
|
+
thread_pool_size=thread_pool_size,
|
3833
|
+
use_jump_host=use_jump_host,
|
3834
|
+
internal=internal,
|
3835
|
+
)
|
3836
|
+
),
|
3837
|
+
ctx=ctx.obj,
|
3838
|
+
)
|
3839
|
+
|
3840
|
+
|
3818
3841
|
def get_integration_cli_meta() -> dict[str, IntegrationMeta]:
|
3819
3842
|
"""
|
3820
3843
|
returns all integrations known to cli.py via click introspection
|
File without changes
|
@@ -0,0 +1,204 @@
|
|
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.oc_connection_cluster import OcConnectionCluster
|
21
|
+
|
22
|
+
|
23
|
+
DEFINITION = """
|
24
|
+
fragment CommonJumphostFields on ClusterJumpHost_v1 {
|
25
|
+
hostname
|
26
|
+
knownHosts
|
27
|
+
user
|
28
|
+
port
|
29
|
+
remotePort
|
30
|
+
identity {
|
31
|
+
... VaultSecret
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
fragment OcConnectionCluster on Cluster_v1 {
|
36
|
+
name
|
37
|
+
serverUrl
|
38
|
+
internal
|
39
|
+
insecureSkipTLSVerify
|
40
|
+
jumpHost {
|
41
|
+
...CommonJumphostFields
|
42
|
+
}
|
43
|
+
automationToken {
|
44
|
+
...VaultSecret
|
45
|
+
}
|
46
|
+
clusterAdminAutomationToken {
|
47
|
+
...VaultSecret
|
48
|
+
}
|
49
|
+
disable {
|
50
|
+
integrations
|
51
|
+
}
|
52
|
+
}
|
53
|
+
|
54
|
+
fragment VaultSecret on VaultSecret_v1 {
|
55
|
+
path
|
56
|
+
field
|
57
|
+
version
|
58
|
+
format
|
59
|
+
}
|
60
|
+
|
61
|
+
query AutomatedActionsInstances {
|
62
|
+
automated_actions_instances_v1 {
|
63
|
+
name
|
64
|
+
deployment {
|
65
|
+
name
|
66
|
+
clusterAdmin
|
67
|
+
delete
|
68
|
+
cluster {
|
69
|
+
...OcConnectionCluster
|
70
|
+
}
|
71
|
+
}
|
72
|
+
permissions {
|
73
|
+
roles {
|
74
|
+
name
|
75
|
+
users {
|
76
|
+
org_username
|
77
|
+
}
|
78
|
+
bots {
|
79
|
+
org_username
|
80
|
+
}
|
81
|
+
expirationDate
|
82
|
+
}
|
83
|
+
|
84
|
+
action {
|
85
|
+
operationId
|
86
|
+
retries
|
87
|
+
maxOps
|
88
|
+
}
|
89
|
+
|
90
|
+
arguments {
|
91
|
+
type
|
92
|
+
... on AutomatedActionArgumentOpenshift_v1 {
|
93
|
+
namespace {
|
94
|
+
name
|
95
|
+
delete
|
96
|
+
cluster {
|
97
|
+
name
|
98
|
+
disable {
|
99
|
+
integrations
|
100
|
+
}
|
101
|
+
}
|
102
|
+
}
|
103
|
+
kind_pattern
|
104
|
+
name_pattern
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
"""
|
111
|
+
|
112
|
+
|
113
|
+
class ConfiguredBaseModel(BaseModel):
|
114
|
+
class Config:
|
115
|
+
smart_union=True
|
116
|
+
extra=Extra.forbid
|
117
|
+
|
118
|
+
|
119
|
+
class NamespaceV1(ConfiguredBaseModel):
|
120
|
+
name: str = Field(..., alias="name")
|
121
|
+
cluster_admin: Optional[bool] = Field(..., alias="clusterAdmin")
|
122
|
+
delete: Optional[bool] = Field(..., alias="delete")
|
123
|
+
cluster: OcConnectionCluster = Field(..., alias="cluster")
|
124
|
+
|
125
|
+
|
126
|
+
class UserV1(ConfiguredBaseModel):
|
127
|
+
org_username: str = Field(..., alias="org_username")
|
128
|
+
|
129
|
+
|
130
|
+
class BotV1(ConfiguredBaseModel):
|
131
|
+
org_username: Optional[str] = Field(..., alias="org_username")
|
132
|
+
|
133
|
+
|
134
|
+
class RoleV1(ConfiguredBaseModel):
|
135
|
+
name: str = Field(..., alias="name")
|
136
|
+
users: list[UserV1] = Field(..., alias="users")
|
137
|
+
bots: list[BotV1] = Field(..., alias="bots")
|
138
|
+
expiration_date: Optional[str] = Field(..., alias="expirationDate")
|
139
|
+
|
140
|
+
|
141
|
+
class AutomatedActionV1(ConfiguredBaseModel):
|
142
|
+
operation_id: str = Field(..., alias="operationId")
|
143
|
+
retries: int = Field(..., alias="retries")
|
144
|
+
max_ops: int = Field(..., alias="maxOps")
|
145
|
+
|
146
|
+
|
147
|
+
class AutomatedActionArgumentV1(ConfiguredBaseModel):
|
148
|
+
q_type: str = Field(..., alias="type")
|
149
|
+
|
150
|
+
|
151
|
+
class DisableClusterAutomationsV1(ConfiguredBaseModel):
|
152
|
+
integrations: Optional[list[str]] = Field(..., alias="integrations")
|
153
|
+
|
154
|
+
|
155
|
+
class AutomatedActionArgumentOpenshiftV1_NamespaceV1_ClusterV1(ConfiguredBaseModel):
|
156
|
+
name: str = Field(..., alias="name")
|
157
|
+
disable: Optional[DisableClusterAutomationsV1] = Field(..., alias="disable")
|
158
|
+
|
159
|
+
|
160
|
+
class AutomatedActionArgumentOpenshiftV1_NamespaceV1(ConfiguredBaseModel):
|
161
|
+
name: str = Field(..., alias="name")
|
162
|
+
delete: Optional[bool] = Field(..., alias="delete")
|
163
|
+
cluster: AutomatedActionArgumentOpenshiftV1_NamespaceV1_ClusterV1 = Field(..., alias="cluster")
|
164
|
+
|
165
|
+
|
166
|
+
class AutomatedActionArgumentOpenshiftV1(AutomatedActionArgumentV1):
|
167
|
+
namespace: AutomatedActionArgumentOpenshiftV1_NamespaceV1 = Field(..., alias="namespace")
|
168
|
+
kind_pattern: str = Field(..., alias="kind_pattern")
|
169
|
+
name_pattern: str = Field(..., alias="name_pattern")
|
170
|
+
|
171
|
+
|
172
|
+
class PermissionAutomatedActionsV1(ConfiguredBaseModel):
|
173
|
+
roles: Optional[list[RoleV1]] = Field(..., alias="roles")
|
174
|
+
action: AutomatedActionV1 = Field(..., alias="action")
|
175
|
+
arguments: Optional[list[Union[AutomatedActionArgumentOpenshiftV1, AutomatedActionArgumentV1]]] = Field(..., alias="arguments")
|
176
|
+
|
177
|
+
|
178
|
+
class AutomatedActionsInstanceV1(ConfiguredBaseModel):
|
179
|
+
name: str = Field(..., alias="name")
|
180
|
+
deployment: NamespaceV1 = Field(..., alias="deployment")
|
181
|
+
permissions: Optional[list[PermissionAutomatedActionsV1]] = Field(..., alias="permissions")
|
182
|
+
|
183
|
+
|
184
|
+
class AutomatedActionsInstancesQueryData(ConfiguredBaseModel):
|
185
|
+
automated_actions_instances_v1: Optional[list[AutomatedActionsInstanceV1]] = Field(..., alias="automated_actions_instances_v1")
|
186
|
+
|
187
|
+
|
188
|
+
def query(query_func: Callable, **kwargs: Any) -> AutomatedActionsInstancesQueryData:
|
189
|
+
"""
|
190
|
+
This is a convenience function which queries and parses the data into
|
191
|
+
concrete types. It should be compatible with most GQL clients.
|
192
|
+
You do not have to use it to consume the generated data classes.
|
193
|
+
Alternatively, you can also mime and alternate the behavior
|
194
|
+
of this function in the caller.
|
195
|
+
|
196
|
+
Parameters:
|
197
|
+
query_func (Callable): Function which queries your GQL Server
|
198
|
+
kwargs: optional arguments that will be passed to the query function
|
199
|
+
|
200
|
+
Returns:
|
201
|
+
AutomatedActionsInstancesQueryData: queried data parsed into generated classes
|
202
|
+
"""
|
203
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
204
|
+
return AutomatedActionsInstancesQueryData(**raw_data)
|