qontract-reconcile 0.10.2.dev395__py3-none-any.whl → 0.10.2.dev408__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.dev395.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/METADATA +2 -2
- {qontract_reconcile-0.10.2.dev395.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/RECORD +23 -23
- reconcile/automated_actions/config/integration.py +12 -0
- reconcile/aws_ami_share.py +69 -62
- reconcile/change_owners/change_owners.py +1 -1
- reconcile/endpoints_discovery/integration.py +4 -1
- reconcile/external_resources/integration.py +1 -1
- reconcile/external_resources/reconciler.py +5 -2
- reconcile/gql_definitions/automated_actions/instance.py +41 -2
- reconcile/gql_definitions/introspection.json +219 -0
- reconcile/ocm/types.py +6 -1
- reconcile/openshift_base.py +47 -1
- reconcile/openshift_resources_base.py +6 -2
- reconcile/queries.py +6 -0
- reconcile/utils/aws_api.py +51 -20
- reconcile/utils/jobcontroller/controller.py +1 -1
- reconcile/utils/jobcontroller/models.py +17 -1
- reconcile/utils/oc.py +107 -90
- reconcile/utils/ocm/products.py +5 -5
- reconcile/utils/openshift_resource.py +5 -0
- reconcile/utils/terrascript_aws_client.py +41 -40
- {qontract_reconcile-0.10.2.dev395.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev395.dist-info → qontract_reconcile-0.10.2.dev408.dist-info}/entry_points.txt +0 -0
|
@@ -4937,6 +4937,11 @@
|
|
|
4937
4937
|
"name": "AutomatedActionNoOp_v1",
|
|
4938
4938
|
"ofType": null
|
|
4939
4939
|
},
|
|
4940
|
+
{
|
|
4941
|
+
"kind": "OBJECT",
|
|
4942
|
+
"name": "AutomatedActionOpenshiftTriggerCronjob_v1",
|
|
4943
|
+
"ofType": null
|
|
4944
|
+
},
|
|
4940
4945
|
{
|
|
4941
4946
|
"kind": "OBJECT",
|
|
4942
4947
|
"name": "AutomatedActionOpenshiftWorkloadDelete_v1",
|
|
@@ -27712,6 +27717,11 @@
|
|
|
27712
27717
|
"name": "AutomatedActionNoOp_v1",
|
|
27713
27718
|
"ofType": null
|
|
27714
27719
|
},
|
|
27720
|
+
{
|
|
27721
|
+
"kind": "OBJECT",
|
|
27722
|
+
"name": "AutomatedActionOpenshiftTriggerCronjob_v1",
|
|
27723
|
+
"ofType": null
|
|
27724
|
+
},
|
|
27715
27725
|
{
|
|
27716
27726
|
"kind": "OBJECT",
|
|
27717
27727
|
"name": "AutomatedActionOpenshiftWorkloadDelete_v1",
|
|
@@ -55332,6 +55342,215 @@
|
|
|
55332
55342
|
"enumValues": null,
|
|
55333
55343
|
"possibleTypes": null
|
|
55334
55344
|
},
|
|
55345
|
+
{
|
|
55346
|
+
"kind": "OBJECT",
|
|
55347
|
+
"name": "AutomatedActionOpenshiftTriggerCronjob_v1",
|
|
55348
|
+
"description": null,
|
|
55349
|
+
"fields": [
|
|
55350
|
+
{
|
|
55351
|
+
"name": "schema",
|
|
55352
|
+
"description": null,
|
|
55353
|
+
"args": [],
|
|
55354
|
+
"type": {
|
|
55355
|
+
"kind": "NON_NULL",
|
|
55356
|
+
"name": null,
|
|
55357
|
+
"ofType": {
|
|
55358
|
+
"kind": "SCALAR",
|
|
55359
|
+
"name": "String",
|
|
55360
|
+
"ofType": null
|
|
55361
|
+
}
|
|
55362
|
+
},
|
|
55363
|
+
"isDeprecated": false,
|
|
55364
|
+
"deprecationReason": null
|
|
55365
|
+
},
|
|
55366
|
+
{
|
|
55367
|
+
"name": "path",
|
|
55368
|
+
"description": null,
|
|
55369
|
+
"args": [],
|
|
55370
|
+
"type": {
|
|
55371
|
+
"kind": "NON_NULL",
|
|
55372
|
+
"name": null,
|
|
55373
|
+
"ofType": {
|
|
55374
|
+
"kind": "SCALAR",
|
|
55375
|
+
"name": "String",
|
|
55376
|
+
"ofType": null
|
|
55377
|
+
}
|
|
55378
|
+
},
|
|
55379
|
+
"isDeprecated": false,
|
|
55380
|
+
"deprecationReason": null
|
|
55381
|
+
},
|
|
55382
|
+
{
|
|
55383
|
+
"name": "type",
|
|
55384
|
+
"description": null,
|
|
55385
|
+
"args": [],
|
|
55386
|
+
"type": {
|
|
55387
|
+
"kind": "NON_NULL",
|
|
55388
|
+
"name": null,
|
|
55389
|
+
"ofType": {
|
|
55390
|
+
"kind": "SCALAR",
|
|
55391
|
+
"name": "String",
|
|
55392
|
+
"ofType": null
|
|
55393
|
+
}
|
|
55394
|
+
},
|
|
55395
|
+
"isDeprecated": false,
|
|
55396
|
+
"deprecationReason": null
|
|
55397
|
+
},
|
|
55398
|
+
{
|
|
55399
|
+
"name": "description",
|
|
55400
|
+
"description": null,
|
|
55401
|
+
"args": [],
|
|
55402
|
+
"type": {
|
|
55403
|
+
"kind": "SCALAR",
|
|
55404
|
+
"name": "String",
|
|
55405
|
+
"ofType": null
|
|
55406
|
+
},
|
|
55407
|
+
"isDeprecated": false,
|
|
55408
|
+
"deprecationReason": null
|
|
55409
|
+
},
|
|
55410
|
+
{
|
|
55411
|
+
"name": "maxOps",
|
|
55412
|
+
"description": null,
|
|
55413
|
+
"args": [],
|
|
55414
|
+
"type": {
|
|
55415
|
+
"kind": "NON_NULL",
|
|
55416
|
+
"name": null,
|
|
55417
|
+
"ofType": {
|
|
55418
|
+
"kind": "SCALAR",
|
|
55419
|
+
"name": "Int",
|
|
55420
|
+
"ofType": null
|
|
55421
|
+
}
|
|
55422
|
+
},
|
|
55423
|
+
"isDeprecated": false,
|
|
55424
|
+
"deprecationReason": null
|
|
55425
|
+
},
|
|
55426
|
+
{
|
|
55427
|
+
"name": "instances",
|
|
55428
|
+
"description": null,
|
|
55429
|
+
"args": [],
|
|
55430
|
+
"type": {
|
|
55431
|
+
"kind": "NON_NULL",
|
|
55432
|
+
"name": null,
|
|
55433
|
+
"ofType": {
|
|
55434
|
+
"kind": "LIST",
|
|
55435
|
+
"name": null,
|
|
55436
|
+
"ofType": {
|
|
55437
|
+
"kind": "NON_NULL",
|
|
55438
|
+
"name": null,
|
|
55439
|
+
"ofType": {
|
|
55440
|
+
"kind": "OBJECT",
|
|
55441
|
+
"name": "AutomatedActionsInstance_v1",
|
|
55442
|
+
"ofType": null
|
|
55443
|
+
}
|
|
55444
|
+
}
|
|
55445
|
+
}
|
|
55446
|
+
},
|
|
55447
|
+
"isDeprecated": false,
|
|
55448
|
+
"deprecationReason": null
|
|
55449
|
+
},
|
|
55450
|
+
{
|
|
55451
|
+
"name": "permissions",
|
|
55452
|
+
"description": null,
|
|
55453
|
+
"args": [],
|
|
55454
|
+
"type": {
|
|
55455
|
+
"kind": "LIST",
|
|
55456
|
+
"name": null,
|
|
55457
|
+
"ofType": {
|
|
55458
|
+
"kind": "NON_NULL",
|
|
55459
|
+
"name": null,
|
|
55460
|
+
"ofType": {
|
|
55461
|
+
"kind": "OBJECT",
|
|
55462
|
+
"name": "PermissionAutomatedActions_v1",
|
|
55463
|
+
"ofType": null
|
|
55464
|
+
}
|
|
55465
|
+
}
|
|
55466
|
+
},
|
|
55467
|
+
"isDeprecated": false,
|
|
55468
|
+
"deprecationReason": null
|
|
55469
|
+
},
|
|
55470
|
+
{
|
|
55471
|
+
"name": "arguments",
|
|
55472
|
+
"description": null,
|
|
55473
|
+
"args": [],
|
|
55474
|
+
"type": {
|
|
55475
|
+
"kind": "NON_NULL",
|
|
55476
|
+
"name": null,
|
|
55477
|
+
"ofType": {
|
|
55478
|
+
"kind": "LIST",
|
|
55479
|
+
"name": null,
|
|
55480
|
+
"ofType": {
|
|
55481
|
+
"kind": "NON_NULL",
|
|
55482
|
+
"name": null,
|
|
55483
|
+
"ofType": {
|
|
55484
|
+
"kind": "OBJECT",
|
|
55485
|
+
"name": "AutomatedActionOpenshiftTriggerCronjobArgument_v1",
|
|
55486
|
+
"ofType": null
|
|
55487
|
+
}
|
|
55488
|
+
}
|
|
55489
|
+
}
|
|
55490
|
+
},
|
|
55491
|
+
"isDeprecated": false,
|
|
55492
|
+
"deprecationReason": null
|
|
55493
|
+
}
|
|
55494
|
+
],
|
|
55495
|
+
"inputFields": null,
|
|
55496
|
+
"interfaces": [
|
|
55497
|
+
{
|
|
55498
|
+
"kind": "INTERFACE",
|
|
55499
|
+
"name": "AutomatedAction_v1",
|
|
55500
|
+
"ofType": null
|
|
55501
|
+
},
|
|
55502
|
+
{
|
|
55503
|
+
"kind": "INTERFACE",
|
|
55504
|
+
"name": "DatafileObject_v1",
|
|
55505
|
+
"ofType": null
|
|
55506
|
+
}
|
|
55507
|
+
],
|
|
55508
|
+
"enumValues": null,
|
|
55509
|
+
"possibleTypes": null
|
|
55510
|
+
},
|
|
55511
|
+
{
|
|
55512
|
+
"kind": "OBJECT",
|
|
55513
|
+
"name": "AutomatedActionOpenshiftTriggerCronjobArgument_v1",
|
|
55514
|
+
"description": null,
|
|
55515
|
+
"fields": [
|
|
55516
|
+
{
|
|
55517
|
+
"name": "namespace",
|
|
55518
|
+
"description": null,
|
|
55519
|
+
"args": [],
|
|
55520
|
+
"type": {
|
|
55521
|
+
"kind": "NON_NULL",
|
|
55522
|
+
"name": null,
|
|
55523
|
+
"ofType": {
|
|
55524
|
+
"kind": "OBJECT",
|
|
55525
|
+
"name": "Namespace_v1",
|
|
55526
|
+
"ofType": null
|
|
55527
|
+
}
|
|
55528
|
+
},
|
|
55529
|
+
"isDeprecated": false,
|
|
55530
|
+
"deprecationReason": null
|
|
55531
|
+
},
|
|
55532
|
+
{
|
|
55533
|
+
"name": "cronjob",
|
|
55534
|
+
"description": null,
|
|
55535
|
+
"args": [],
|
|
55536
|
+
"type": {
|
|
55537
|
+
"kind": "NON_NULL",
|
|
55538
|
+
"name": null,
|
|
55539
|
+
"ofType": {
|
|
55540
|
+
"kind": "SCALAR",
|
|
55541
|
+
"name": "String",
|
|
55542
|
+
"ofType": null
|
|
55543
|
+
}
|
|
55544
|
+
},
|
|
55545
|
+
"isDeprecated": false,
|
|
55546
|
+
"deprecationReason": null
|
|
55547
|
+
}
|
|
55548
|
+
],
|
|
55549
|
+
"inputFields": null,
|
|
55550
|
+
"interfaces": [],
|
|
55551
|
+
"enumValues": null,
|
|
55552
|
+
"possibleTypes": null
|
|
55553
|
+
},
|
|
55335
55554
|
{
|
|
55336
55555
|
"kind": "OBJECT",
|
|
55337
55556
|
"name": "AutomatedActionOpenshiftWorkloadDelete_v1",
|
reconcile/ocm/types.py
CHANGED
|
@@ -4,6 +4,7 @@ from pydantic import (
|
|
|
4
4
|
BaseModel,
|
|
5
5
|
Extra,
|
|
6
6
|
Field,
|
|
7
|
+
validator,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
|
|
@@ -36,7 +37,11 @@ class OCMClusterSpec(BaseModel):
|
|
|
36
37
|
initial_version: str | None
|
|
37
38
|
version: str
|
|
38
39
|
hypershift: bool | None
|
|
39
|
-
fips: bool
|
|
40
|
+
fips: bool = False
|
|
41
|
+
|
|
42
|
+
@validator("fips", pre=True)
|
|
43
|
+
def set_fips_default(cls, v: bool | None) -> bool:
|
|
44
|
+
return v or False
|
|
40
45
|
|
|
41
46
|
class Config:
|
|
42
47
|
extra = Extra.forbid
|
reconcile/openshift_base.py
CHANGED
|
@@ -30,9 +30,11 @@ from reconcile.utils import (
|
|
|
30
30
|
)
|
|
31
31
|
from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
|
|
32
32
|
from reconcile.utils.oc import (
|
|
33
|
+
AmbiguousResourceTypeError,
|
|
33
34
|
DeploymentFieldIsImmutableError,
|
|
34
35
|
FieldIsImmutableError,
|
|
35
36
|
InvalidValueApplyError,
|
|
37
|
+
KindNotFoundError,
|
|
36
38
|
MayNotChangeOnceSetError,
|
|
37
39
|
MetaDataAnnotationsTooLongApplyError,
|
|
38
40
|
OC_Map,
|
|
@@ -128,6 +130,29 @@ class ClusterMap(Protocol):
|
|
|
128
130
|
) -> list[str]: ...
|
|
129
131
|
|
|
130
132
|
|
|
133
|
+
def validate_managed_resource_types(
|
|
134
|
+
oc: OCCli,
|
|
135
|
+
managed_resource_types: Iterable[str],
|
|
136
|
+
managed_resource_names: Iterable[Mapping[str, Any]],
|
|
137
|
+
cluster_scope_resource_validation: bool,
|
|
138
|
+
) -> None:
|
|
139
|
+
"""Validate the managed resource types."""
|
|
140
|
+
managed_resources = [
|
|
141
|
+
managed_resource_name["resource"]
|
|
142
|
+
for managed_resource_name in managed_resource_names
|
|
143
|
+
]
|
|
144
|
+
for managed_resource_type in managed_resource_types:
|
|
145
|
+
# The k8s kind must be supported by the cluster
|
|
146
|
+
resource = oc.get_api_resource(managed_resource_type)
|
|
147
|
+
|
|
148
|
+
if cluster_scope_resource_validation and not resource.namespaced:
|
|
149
|
+
# cluster-scoped resources must be use managedResourceNames!
|
|
150
|
+
if managed_resource_type not in managed_resources:
|
|
151
|
+
raise ValidationError(
|
|
152
|
+
f"Cluster-scoped resource {managed_resource_type} must be managed by name only. Please use 'managedResourceNames' field to specify the names of the resources to manage."
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
|
|
131
156
|
def init_specs_to_fetch(
|
|
132
157
|
ri: ResourceInventory,
|
|
133
158
|
oc_map: ClusterMap,
|
|
@@ -136,6 +161,7 @@ def init_specs_to_fetch(
|
|
|
136
161
|
override_managed_types: Iterable[str] | None = None,
|
|
137
162
|
managed_types_key: str = "managedResourceTypes",
|
|
138
163
|
cluster_admin: bool = False,
|
|
164
|
+
cluster_scope_resource_validation: bool = False,
|
|
139
165
|
) -> list[StateSpec]:
|
|
140
166
|
state_specs: list[StateSpec] = []
|
|
141
167
|
|
|
@@ -163,9 +189,27 @@ def init_specs_to_fetch(
|
|
|
163
189
|
logging.log(level=ex.log_level, msg=ex.message)
|
|
164
190
|
continue
|
|
165
191
|
|
|
192
|
+
managed_resource_names = namespace_info.get("managedResourceNames") or []
|
|
193
|
+
try:
|
|
194
|
+
validate_managed_resource_types(
|
|
195
|
+
oc,
|
|
196
|
+
managed_types,
|
|
197
|
+
managed_resource_names,
|
|
198
|
+
cluster_scope_resource_validation=cluster_scope_resource_validation,
|
|
199
|
+
)
|
|
200
|
+
except KindNotFoundError:
|
|
201
|
+
# We must allow kinds that are not supported by the cluster because:
|
|
202
|
+
# 1. We install CRD with an operator in the same MR
|
|
203
|
+
# 2. SAAS files initialize the namespace objects with managedResourceTypes from the SAAS file
|
|
204
|
+
# and we can't expect that all of those are valid for all clusters
|
|
205
|
+
pass
|
|
206
|
+
except (AmbiguousResourceTypeError, ValidationError) as e:
|
|
207
|
+
ri.register_error()
|
|
208
|
+
logging.error(f"[{cluster}/{namespace_info['name']}] {e}")
|
|
209
|
+
continue
|
|
210
|
+
|
|
166
211
|
namespace = namespace_info["name"]
|
|
167
212
|
# These may exit but have a value of None
|
|
168
|
-
managed_resource_names = namespace_info.get("managedResourceNames") or []
|
|
169
213
|
managed_resource_type_overrides = (
|
|
170
214
|
namespace_info.get("managedResourceTypeOverrides") or []
|
|
171
215
|
)
|
|
@@ -340,6 +384,7 @@ def fetch_current_state(
|
|
|
340
384
|
cluster_admin: bool = False,
|
|
341
385
|
caller: str | None = None,
|
|
342
386
|
init_projects: bool = False,
|
|
387
|
+
cluster_scope_resource_validation: bool = False,
|
|
343
388
|
) -> tuple[ResourceInventory, OC_Map]:
|
|
344
389
|
ri = ResourceInventory()
|
|
345
390
|
settings = queries.get_app_interface_settings()
|
|
@@ -362,6 +407,7 @@ def fetch_current_state(
|
|
|
362
407
|
clusters=clusters,
|
|
363
408
|
override_managed_types=override_managed_types,
|
|
364
409
|
cluster_admin=cluster_admin,
|
|
410
|
+
cluster_scope_resource_validation=cluster_scope_resource_validation,
|
|
365
411
|
)
|
|
366
412
|
threaded.run(
|
|
367
413
|
populate_current_state,
|
|
@@ -806,7 +806,11 @@ def fetch_data(
|
|
|
806
806
|
init_api_resources=init_api_resources,
|
|
807
807
|
)
|
|
808
808
|
state_specs = ob.init_specs_to_fetch(
|
|
809
|
-
ri,
|
|
809
|
+
ri,
|
|
810
|
+
oc_map,
|
|
811
|
+
namespaces=namespaces,
|
|
812
|
+
override_managed_types=overrides,
|
|
813
|
+
cluster_scope_resource_validation=True,
|
|
810
814
|
)
|
|
811
815
|
threaded.run(fetch_states, state_specs, thread_pool_size, ri=ri, settings=settings)
|
|
812
816
|
|
|
@@ -861,7 +865,7 @@ def canonicalize_namespaces(
|
|
|
861
865
|
elif providers[0] == "route":
|
|
862
866
|
override = ["Route"]
|
|
863
867
|
elif providers[0] == "prometheus-rule":
|
|
864
|
-
override = ["PrometheusRule"]
|
|
868
|
+
override = ["PrometheusRule.monitoring.coreos.com"]
|
|
865
869
|
|
|
866
870
|
namespace_info["openshiftResources"] = ors
|
|
867
871
|
canonicalized_namespaces.append(namespace_info)
|
reconcile/queries.py
CHANGED
reconcile/utils/aws_api.py
CHANGED
|
@@ -3,7 +3,6 @@ from __future__ import annotations
|
|
|
3
3
|
import logging
|
|
4
4
|
import operator
|
|
5
5
|
import os
|
|
6
|
-
import re
|
|
7
6
|
from functools import lru_cache
|
|
8
7
|
from threading import Lock
|
|
9
8
|
from typing import (
|
|
@@ -25,6 +24,7 @@ import reconcile.utils.lean_terraform_client as terraform
|
|
|
25
24
|
from reconcile.utils.secret_reader import SecretReader, SecretReaderBase
|
|
26
25
|
|
|
27
26
|
if TYPE_CHECKING:
|
|
27
|
+
import re
|
|
28
28
|
from collections.abc import (
|
|
29
29
|
Iterable,
|
|
30
30
|
Iterator,
|
|
@@ -1074,28 +1074,40 @@ class AWSApi:
|
|
|
1074
1074
|
return [rt["RouteTableId"] for rt in vpc_route_tables]
|
|
1075
1075
|
|
|
1076
1076
|
@staticmethod
|
|
1077
|
-
def
|
|
1078
|
-
|
|
1079
|
-
) -> list[dict[str, Any]]:
|
|
1080
|
-
results = []
|
|
1081
|
-
pattern = re.compile(regex)
|
|
1082
|
-
for i in images:
|
|
1083
|
-
if not re.search(pattern, i["Name"]):
|
|
1084
|
-
continue
|
|
1085
|
-
if i["State"] != "available":
|
|
1086
|
-
continue
|
|
1087
|
-
item = {"image_id": i["ImageId"], "tags": i.get("Tags", [])}
|
|
1088
|
-
results.append(item)
|
|
1077
|
+
def normalize_tags(tags: Iterable[TagTypeDef]) -> dict[str, str]:
|
|
1078
|
+
return {tag["Key"]: tag["Value"] for tag in tags}
|
|
1089
1079
|
|
|
1090
|
-
|
|
1080
|
+
@staticmethod
|
|
1081
|
+
def _filter_amis(
|
|
1082
|
+
images: Iterable[ImageTypeDef],
|
|
1083
|
+
regex: re.Pattern,
|
|
1084
|
+
) -> dict[str, dict[str, str]]:
|
|
1085
|
+
return {
|
|
1086
|
+
image["ImageId"]: AWSApi.normalize_tags(image.get("Tags", []))
|
|
1087
|
+
for image in images
|
|
1088
|
+
if regex.search(image["Name"]) and image["State"] == "available"
|
|
1089
|
+
}
|
|
1091
1090
|
|
|
1092
1091
|
def get_amis_details(
|
|
1093
1092
|
self,
|
|
1094
1093
|
account: Mapping[str, Any],
|
|
1095
1094
|
owner_account: Mapping[str, Any],
|
|
1096
|
-
regex:
|
|
1095
|
+
regex: re.Pattern,
|
|
1097
1096
|
region: str | None = None,
|
|
1098
|
-
) ->
|
|
1097
|
+
) -> dict[str, dict[str, str]]:
|
|
1098
|
+
"""
|
|
1099
|
+
Get AMI details for an account, find AMI name matches regex and state is available.
|
|
1100
|
+
Return ImageId and normalized tags.
|
|
1101
|
+
|
|
1102
|
+
Args:
|
|
1103
|
+
account: AWS account
|
|
1104
|
+
owner_account: AMI owner AWS account uid
|
|
1105
|
+
regex: regex to filter AMI name
|
|
1106
|
+
region: AWS account region
|
|
1107
|
+
|
|
1108
|
+
Returns:
|
|
1109
|
+
dict[str, dict[str, str]]: Key is AMI ImageId, value is AMI normalized tags.
|
|
1110
|
+
"""
|
|
1099
1111
|
ec2 = self._account_ec2_client(account["name"], region_name=region)
|
|
1100
1112
|
images = self.get_account_amis(ec2, owner=owner_account["uid"])
|
|
1101
1113
|
return self._filter_amis(images, regex)
|
|
@@ -1175,12 +1187,31 @@ class AWSApi:
|
|
|
1175
1187
|
client = self._account_cloudwatch_client(account_name, region_name=region_name)
|
|
1176
1188
|
client.delete_log_group(logGroupName=group_name)
|
|
1177
1189
|
|
|
1178
|
-
def
|
|
1179
|
-
self,
|
|
1190
|
+
def create_tags(
|
|
1191
|
+
self,
|
|
1192
|
+
account: Mapping[str, Any],
|
|
1193
|
+
resource_id: str,
|
|
1194
|
+
tags: Mapping[str, str],
|
|
1180
1195
|
) -> None:
|
|
1196
|
+
"""
|
|
1197
|
+
Create tags on EC2 resources (AMI)
|
|
1198
|
+
|
|
1199
|
+
Args:
|
|
1200
|
+
account: AWS account
|
|
1201
|
+
resource_id: AWS resource id
|
|
1202
|
+
tags: tags to update
|
|
1203
|
+
|
|
1204
|
+
Returns:
|
|
1205
|
+
None
|
|
1206
|
+
"""
|
|
1181
1207
|
ec2 = self._account_ec2_client(account["name"])
|
|
1182
|
-
|
|
1183
|
-
|
|
1208
|
+
formatted_tags: list[TagTypeDef] = [
|
|
1209
|
+
{"Key": k, "Value": v} for k, v in tags.items()
|
|
1210
|
+
]
|
|
1211
|
+
ec2.create_tags(
|
|
1212
|
+
Resources=[resource_id],
|
|
1213
|
+
Tags=formatted_tags,
|
|
1214
|
+
)
|
|
1184
1215
|
|
|
1185
1216
|
def get_alb_network_interface_ips(
|
|
1186
1217
|
self, account: awsh.Account, service_name: str
|
|
@@ -38,6 +38,8 @@ class JobValidationError(Exception):
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
JOB_GENERATION_ANNOTATION = "qontract-reconcile/job.generation"
|
|
41
|
+
MAX_JOB_NAME_LENGTH = 63
|
|
42
|
+
UNIT_OF_WORK_DIGEST_LENGTH = 10
|
|
41
43
|
|
|
42
44
|
|
|
43
45
|
class K8sJob(ABC):
|
|
@@ -72,7 +74,21 @@ class K8sJob(ABC):
|
|
|
72
74
|
"""
|
|
73
75
|
|
|
74
76
|
def name(self) -> str:
|
|
75
|
-
|
|
77
|
+
"""
|
|
78
|
+
Generate the full job name by combining the name prefix with a digest.
|
|
79
|
+
|
|
80
|
+
The name is constructed from the name_prefix (truncated to ensure total
|
|
81
|
+
length compliance) and the unit_of_work_digest. The total length is
|
|
82
|
+
limited to MAX_JOB_NAME_LENGTH (63 characters) to comply with Kubernetes
|
|
83
|
+
naming constraints.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
A unique job name in the format: {name_prefix}-{digest}
|
|
87
|
+
"""
|
|
88
|
+
prefix = self.name_prefix()[
|
|
89
|
+
: MAX_JOB_NAME_LENGTH - UNIT_OF_WORK_DIGEST_LENGTH - 1
|
|
90
|
+
]
|
|
91
|
+
return f"{prefix}-{self.unit_of_work_digest(UNIT_OF_WORK_DIGEST_LENGTH)}"
|
|
76
92
|
|
|
77
93
|
@abstractmethod
|
|
78
94
|
def name_prefix(self) -> str:
|