qontract-reconcile 0.10.2.dev298__py3-none-any.whl → 0.10.2.dev300__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.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/RECORD +12 -12
- reconcile/gql_definitions/introspection.json +6 -14
- reconcile/gql_definitions/status_board/status_board.py +20 -0
- reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py +2 -2
- reconcile/status_board.py +133 -9
- reconcile/typed_queries/status_board.py +43 -8
- reconcile/utils/ocm/status_board.py +13 -0
- reconcile/utils/terrascript_aws_client.py +21 -16
- tools/qontract_cli.py +1 -1
- {qontract_reconcile-0.10.2.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev298.dist-info → qontract_reconcile-0.10.2.dev300.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.dev300
|
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.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/RECORD
RENAMED
@@ -102,7 +102,7 @@ reconcile/slack_base.py,sha256=I-msunWxfgu5bSwXYulGbtLjxUB_tRmTCAUCU-3nabI,3484
|
|
102
102
|
reconcile/slack_usergroups.py,sha256=3uQVZK0WeZfvE1g7xQwciKCcC3LifDa3NuE1ygQ0cRk,30174
|
103
103
|
reconcile/sql_query.py,sha256=FVwANLPWjkUHqN2OXJ-vnX5hqqcO6rTdyLEO4HkmAgM,26397
|
104
104
|
reconcile/status.py,sha256=cY4IJFXemhxptRJqR4qaaOWqei9e4jgLXuVSGajMsjg,544
|
105
|
-
reconcile/status_board.py,sha256=
|
105
|
+
reconcile/status_board.py,sha256=Wt8aqCmhTbKxRa9GUj5U_aoqif18z9XL5hRK3zy8too,20091
|
106
106
|
reconcile/terraform_aws_route53.py,sha256=CWp5bE3ddUrJGNNvG8YmkSPyNHCWtOc1GEDVLnbOY9Q,10043
|
107
107
|
reconcile/terraform_cloudflare_dns.py,sha256=0Eu46o_BBEEq-B-CCvKop9VTbwrvliCKGSS9gLBSJE4,13456
|
108
108
|
reconcile/terraform_cloudflare_resources.py,sha256=tK-BxQeNdZjf59deKd51Roz868e7UXe52XvcHsffJK0,14982
|
@@ -213,7 +213,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=d3PMy-mQSbSZdIGAVaZCA2U
|
|
213
213
|
reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
214
214
|
reconcile/glitchtip_project_dsn/integration.py,sha256=3GgcqUM6hWhLpo9Yx5Xr9vrdexF-WNevVCNL9bJ0Upc,8162
|
215
215
|
reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
216
|
-
reconcile/gql_definitions/introspection.json,sha256=
|
216
|
+
reconcile/gql_definitions/introspection.json,sha256=jJN4kUVnrBkb39pHZ0lwt_0ZuIyrhjyzsL17pQuGwXo,2358826
|
217
217
|
reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
218
218
|
reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
|
219
219
|
reconcile/gql_definitions/acs/acs_policies.py,sha256=Ygpfl2-VkYLSlJvHgp_dJBfb66K_Rwfdfpsa18w1v1s,4338
|
@@ -405,7 +405,7 @@ reconcile/gql_definitions/slack_usergroups/users.py,sha256=0KFLYHYXL_bGIKf5LAJSY
|
|
405
405
|
reconcile/gql_definitions/slo_documents/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
406
406
|
reconcile/gql_definitions/slo_documents/slo_documents.py,sha256=pOrm9NXAonlo6Lxq6NkD3mHkZ53ZeBnZOZMkDvOEwds,3746
|
407
407
|
reconcile/gql_definitions/status_board/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
408
|
-
reconcile/gql_definitions/status_board/status_board.py,sha256=
|
408
|
+
reconcile/gql_definitions/status_board/status_board.py,sha256=qm7TkvvBvkMvf_9SQ50wz8ke8LEvwZYQjMkUDrN-BV0,5370
|
409
409
|
reconcile/gql_definitions/statuspage/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
410
410
|
reconcile/gql_definitions/statuspage/statuspages.py,sha256=CTRzjiR9k41LqlkgyoNHwC2JERsoD_Run_aK7jw_Ono,5299
|
411
411
|
reconcile/gql_definitions/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -426,7 +426,7 @@ reconcile/gql_definitions/terraform_repo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5
|
|
426
426
|
reconcile/gql_definitions/terraform_repo/terraform_repo.py,sha256=9cDKdP9ziBh9J_mw2Gi6GUOP4mFxMABY_D62qSeMtJI,3881
|
427
427
|
reconcile/gql_definitions/terraform_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
428
428
|
reconcile/gql_definitions/terraform_resources/database_access_manager.py,sha256=yv0_YC-LmhaKD_gyGG3le1w5BtypBjlsO894-Zgdg4U,4813
|
429
|
-
reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py,sha256=
|
429
|
+
reconcile/gql_definitions/terraform_resources/terraform_resources_namespaces.py,sha256=j1xemQQIjR4O80Ni4RbJhDOWzk9iYcGinO79jZ3kZow,44688
|
430
430
|
reconcile/gql_definitions/terraform_tgw_attachments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
431
431
|
reconcile/gql_definitions/terraform_tgw_attachments/aws_accounts.py,sha256=r6RacQX243Rrtm_6wobSLJZlObehqzkV-seyCVCqiv8,2596
|
432
432
|
reconcile/gql_definitions/unleash_feature_toggles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -563,7 +563,7 @@ reconcile/typed_queries/saas_files.py,sha256=SOE36sWPBcuaRmEaNxXCQZMQdJiUZX8_A92
|
|
563
563
|
reconcile/typed_queries/slack.py,sha256=r30lspctHloyygPn8_DVybxPwUWwiBpvBRRXiTVcQYk,251
|
564
564
|
reconcile/typed_queries/slo_documents.py,sha256=YMdox_-lBRqrdxamPhdnUlRTY_Ro35ptsupq7OaynUQ,362
|
565
565
|
reconcile/typed_queries/smtp.py,sha256=aSLglYa5bHKmlGwKkxq2RZqyMWuAf0a4S_mOuhDa084,542
|
566
|
-
reconcile/typed_queries/status_board.py,sha256=
|
566
|
+
reconcile/typed_queries/status_board.py,sha256=ghu3jRJVzEPm5qHQUJWSCGLUdQzBcrtxU4mEeCCdXjo,3091
|
567
567
|
reconcile/typed_queries/tekton_pipeline_providers.py,sha256=LtoSnSRkuckYsXIU64L1Mf-o3iuUjaN-5O-ARzIROZA,515
|
568
568
|
reconcile/typed_queries/terraform_namespaces.py,sha256=4H9WE90jN_BVYBAt1DxJITS4vkL-vykbXZIS1H4EKNM,413
|
569
569
|
reconcile/typed_queries/unleash.py,sha256=7HDc4owF044xM9Thx4WsXV7DZgETxJjy4lbpwmqz1vU,282
|
@@ -658,7 +658,7 @@ reconcile/utils/sqs_gateway.py,sha256=XNIf3PY4UCPNufP2Ul0UJj3fKlt5larBba-VTT-41F
|
|
658
658
|
reconcile/utils/state.py,sha256=vCHYIfrWLfPyIWEHSaADWlc4OqhwcOiqM3Egqvw-lfo,16372
|
659
659
|
reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
|
660
660
|
reconcile/utils/terraform_client.py,sha256=GoLbfs4d4YItNCeV3NZnrth4sD8ziNYgY2IszruRDpg,37303
|
661
|
-
reconcile/utils/terrascript_aws_client.py,sha256=
|
661
|
+
reconcile/utils/terrascript_aws_client.py,sha256=o5-K61gEbQN48IRfdHVDfgt0sW-sYN9WYho4ZZ7j7io,295917
|
662
662
|
reconcile/utils/three_way_diff_strategy.py,sha256=oQcHXd9LVhirJfoaOBoHUYuZVGfyL2voKr6KVI34zZE,4833
|
663
663
|
reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
|
664
664
|
reconcile/utils/vault.py,sha256=6V15LByFghp-U3k0N4lum6V7qt2EAlRfcAxjy5e-FAU,15146
|
@@ -733,7 +733,7 @@ reconcile/utils/ocm/products.py,sha256=UtWpkAvSMCxPOulEB7aV5ZY8ej_rmErlE_HVdm9Gn
|
|
733
733
|
reconcile/utils/ocm/search_filters.py,sha256=09p4Wq1d1HGrDiinf1dmLJ46VtFhkkRCOL4V-N-zwjY,14808
|
734
734
|
reconcile/utils/ocm/service_log.py,sha256=RG1f0MMn6joKaRCAm2xveSJCavdOPP1BVo9FXecDxaI,2018
|
735
735
|
reconcile/utils/ocm/sre_capability_labels.py,sha256=nqh0imrYczNeeeC7ZNX3pEwuAIVkKLTKZf0YHSPZYpE,1537
|
736
|
-
reconcile/utils/ocm/status_board.py,sha256=
|
736
|
+
reconcile/utils/ocm/status_board.py,sha256=1T9iD3DxWiVlY_8sK94twH01j_WrUbh-A2FC9dpXYPE,4548
|
737
737
|
reconcile/utils/ocm/subscriptions.py,sha256=hehKXsDXIhnhqvWOuiYvx6y2FGq3zt0APGYj7WiBIdI,2765
|
738
738
|
reconcile/utils/ocm/syncsets.py,sha256=9IQm1l5BodOVZa2OFbQmow3afmh4nXe5pn-CCJ5LxTI,1169
|
739
739
|
reconcile/utils/ocm/upgrades.py,sha256=W8-sLgETI_418ftY9vBlXswyjx_KdhJTJO9cwBL3hfY,4162
|
@@ -770,7 +770,7 @@ tools/app_sre_tekton_access_reporter.py,sha256=5qmkevJdlb2j_lpGC5Pu1Pmo0eomX5Zxz
|
|
770
770
|
tools/app_sre_tekton_access_revalidation.py,sha256=vwL1o_j7oSTOhrHNH1znpgjA2LHGzb8yc5iG3aaY4m0,2684
|
771
771
|
tools/glitchtip_access_reporter.py,sha256=wnaiDGW4MkYONV_erltnJ6nGkEj0kQrAiv04NNnOS0k,2859
|
772
772
|
tools/glitchtip_access_revalidation.py,sha256=jjeLO53LTbz_LfQw3G2Cs8lVLO_6xqU39BYyTH3cEPE,2764
|
773
|
-
tools/qontract_cli.py,sha256=
|
773
|
+
tools/qontract_cli.py,sha256=oOyvoY5-9UzLZbl7iUhENVs8DSH4bORbG90Dxww8GrQ,159925
|
774
774
|
tools/template_validation.py,sha256=Xn9X4sGFznx-rvBDnq9Kq16rfET8V3bqH1EwavsGBac,3335
|
775
775
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
776
776
|
tools/cli_commands/container_images_report.py,sha256=8mAjCS6XR0yD7k0mfiVBlt6xbYU47q_ftdYNi5o5VKE,5566
|
@@ -796,7 +796,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
796
796
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=uQv2QJAmUXP1g2GPIH30WTlvL9soY6m9lefpZEVDM5w,3965
|
797
797
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
798
798
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
799
|
-
qontract_reconcile-0.10.2.
|
800
|
-
qontract_reconcile-0.10.2.
|
801
|
-
qontract_reconcile-0.10.2.
|
802
|
-
qontract_reconcile-0.10.2.
|
799
|
+
qontract_reconcile-0.10.2.dev300.dist-info/METADATA,sha256=ZDSvK_d6EHWuZ5pW0oPw4ETFGG4X5Sexs4LYDjlII-U,24916
|
800
|
+
qontract_reconcile-0.10.2.dev300.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
801
|
+
qontract_reconcile-0.10.2.dev300.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
802
|
+
qontract_reconcile-0.10.2.dev300.dist-info/RECORD,,
|
@@ -47157,13 +47157,9 @@
|
|
47157
47157
|
"description": null,
|
47158
47158
|
"args": [],
|
47159
47159
|
"type": {
|
47160
|
-
"kind": "
|
47161
|
-
"name":
|
47162
|
-
"ofType":
|
47163
|
-
"kind": "SCALAR",
|
47164
|
-
"name": "String",
|
47165
|
-
"ofType": null
|
47166
|
-
}
|
47160
|
+
"kind": "SCALAR",
|
47161
|
+
"name": "String",
|
47162
|
+
"ofType": null
|
47167
47163
|
},
|
47168
47164
|
"isDeprecated": false,
|
47169
47165
|
"deprecationReason": null
|
@@ -47173,13 +47169,9 @@
|
|
47173
47169
|
"description": null,
|
47174
47170
|
"args": [],
|
47175
47171
|
"type": {
|
47176
|
-
"kind": "
|
47177
|
-
"name":
|
47178
|
-
"ofType":
|
47179
|
-
"kind": "SCALAR",
|
47180
|
-
"name": "String",
|
47181
|
-
"ofType": null
|
47182
|
-
}
|
47172
|
+
"kind": "SCALAR",
|
47173
|
+
"name": "String",
|
47174
|
+
"ofType": null
|
47183
47175
|
},
|
47184
47176
|
"isDeprecated": false,
|
47185
47177
|
"deprecationReason": null
|
@@ -51,11 +51,19 @@ query StatusBoard {
|
|
51
51
|
childrenApps {
|
52
52
|
name
|
53
53
|
onboardingStatus
|
54
|
+
saasFiles {
|
55
|
+
name
|
56
|
+
managedResourceTypes
|
57
|
+
}
|
54
58
|
}
|
55
59
|
parentApp {
|
56
60
|
name
|
57
61
|
onboardingStatus
|
58
62
|
}
|
63
|
+
saasFiles {
|
64
|
+
name
|
65
|
+
managedResourceTypes
|
66
|
+
}
|
59
67
|
}
|
60
68
|
}
|
61
69
|
}
|
@@ -96,9 +104,15 @@ class ProductV1(ConfiguredBaseModel):
|
|
96
104
|
name: str = Field(..., alias="name")
|
97
105
|
|
98
106
|
|
107
|
+
class SaasFileV2(ConfiguredBaseModel):
|
108
|
+
name: str = Field(..., alias="name")
|
109
|
+
managed_resource_types: list[str] = Field(..., alias="managedResourceTypes")
|
110
|
+
|
111
|
+
|
99
112
|
class AppV1_AppV1(ConfiguredBaseModel):
|
100
113
|
name: str = Field(..., alias="name")
|
101
114
|
onboarding_status: str = Field(..., alias="onboardingStatus")
|
115
|
+
saas_files: Optional[list[SaasFileV2]] = Field(..., alias="saasFiles")
|
102
116
|
|
103
117
|
|
104
118
|
class NamespaceV1_AppV1_AppV1(ConfiguredBaseModel):
|
@@ -106,11 +120,17 @@ class NamespaceV1_AppV1_AppV1(ConfiguredBaseModel):
|
|
106
120
|
onboarding_status: str = Field(..., alias="onboardingStatus")
|
107
121
|
|
108
122
|
|
123
|
+
class AppV1_SaasFileV2(ConfiguredBaseModel):
|
124
|
+
name: str = Field(..., alias="name")
|
125
|
+
managed_resource_types: list[str] = Field(..., alias="managedResourceTypes")
|
126
|
+
|
127
|
+
|
109
128
|
class AppV1(ConfiguredBaseModel):
|
110
129
|
name: str = Field(..., alias="name")
|
111
130
|
onboarding_status: str = Field(..., alias="onboardingStatus")
|
112
131
|
children_apps: Optional[list[AppV1_AppV1]] = Field(..., alias="childrenApps")
|
113
132
|
parent_app: Optional[NamespaceV1_AppV1_AppV1] = Field(..., alias="parentApp")
|
133
|
+
saas_files: Optional[list[AppV1_SaasFileV2]] = Field(..., alias="saasFiles")
|
114
134
|
|
115
135
|
|
116
136
|
class NamespaceV1(ConfiguredBaseModel):
|
@@ -817,8 +817,8 @@ class NamespaceTerraformResourceS3CloudFrontPublicKeyV1(NamespaceTerraformResour
|
|
817
817
|
|
818
818
|
class NamespaceTerraformResourceALBMutualAuthenticationV1(ConfiguredBaseModel):
|
819
819
|
mode: str = Field(..., alias="mode")
|
820
|
-
ca_cert_bundle_s3_bucket_name: str = Field(..., alias="ca_cert_bundle_s3_bucket_name")
|
821
|
-
ca_cert_bundle_s3_bucket_key: str = Field(..., alias="ca_cert_bundle_s3_bucket_key")
|
820
|
+
ca_cert_bundle_s3_bucket_name: Optional[str] = Field(..., alias="ca_cert_bundle_s3_bucket_name")
|
821
|
+
ca_cert_bundle_s3_bucket_key: Optional[str] = Field(..., alias="ca_cert_bundle_s3_bucket_key")
|
822
822
|
|
823
823
|
|
824
824
|
class NamespaceTerraformResourceALBTargetHealthcheckV1(ConfiguredBaseModel):
|
reconcile/status_board.py
CHANGED
@@ -6,6 +6,9 @@ from abc import (
|
|
6
6
|
from collections.abc import Iterable, Mapping
|
7
7
|
from enum import Enum
|
8
8
|
from itertools import chain
|
9
|
+
from typing import (
|
10
|
+
Any,
|
11
|
+
)
|
9
12
|
|
10
13
|
from pydantic import BaseModel
|
11
14
|
|
@@ -13,7 +16,7 @@ from reconcile.gql_definitions.slo_documents.slo_documents import SLODocumentV1
|
|
13
16
|
from reconcile.gql_definitions.status_board.status_board import StatusBoardV1
|
14
17
|
from reconcile.typed_queries.slo_documents import get_slo_documents
|
15
18
|
from reconcile.typed_queries.status_board import (
|
16
|
-
|
19
|
+
get_selected_app_data,
|
17
20
|
get_status_board,
|
18
21
|
)
|
19
22
|
from reconcile.utils.differ import diff_mappings
|
@@ -31,6 +34,7 @@ from reconcile.utils.ocm.status_board import (
|
|
31
34
|
get_application_services,
|
32
35
|
get_managed_products,
|
33
36
|
get_product_applications,
|
37
|
+
update_application,
|
34
38
|
update_service,
|
35
39
|
)
|
36
40
|
from reconcile.utils.ocm_base_client import (
|
@@ -55,6 +59,7 @@ class AbstractStatusBoard(ABC, BaseModel):
|
|
55
59
|
id: str | None
|
56
60
|
name: str
|
57
61
|
fullname: str
|
62
|
+
metadata: dict[str, Any] | ServiceMetadataSpec | None
|
58
63
|
|
59
64
|
@abstractmethod
|
60
65
|
def create(self, ocm: OCMBaseClient) -> None:
|
@@ -122,6 +127,7 @@ class Product(AbstractStatusBoard):
|
|
122
127
|
class Application(AbstractStatusBoard):
|
123
128
|
product: Product
|
124
129
|
services: list["Service"] | None
|
130
|
+
metadata: dict[str, Any]
|
125
131
|
|
126
132
|
def create(self, ocm: OCMBaseClient) -> None:
|
127
133
|
if self.product.id:
|
@@ -131,9 +137,11 @@ class Application(AbstractStatusBoard):
|
|
131
137
|
logging.warning("Missing product id for application")
|
132
138
|
|
133
139
|
def update(self, ocm: OCMBaseClient) -> None:
|
134
|
-
|
135
|
-
|
136
|
-
|
140
|
+
if not self.id:
|
141
|
+
logging.error(f'Trying to update Application "{self.name}" without id')
|
142
|
+
return
|
143
|
+
spec = self.to_ocm_spec()
|
144
|
+
update_application(ocm, self.id, spec)
|
137
145
|
|
138
146
|
def delete(self, ocm: OCMBaseClient) -> None:
|
139
147
|
if not self.id:
|
@@ -149,6 +157,7 @@ class Application(AbstractStatusBoard):
|
|
149
157
|
return {
|
150
158
|
"name": self.name,
|
151
159
|
"fullname": self.fullname,
|
160
|
+
"metadata": self.metadata,
|
152
161
|
"product": {"id": product_id},
|
153
162
|
}
|
154
163
|
|
@@ -248,12 +257,14 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
248
257
|
return QONTRACT_INTEGRATION
|
249
258
|
|
250
259
|
@staticmethod
|
251
|
-
def get_product_apps(
|
260
|
+
def get_product_apps(
|
261
|
+
sb: StatusBoardV1,
|
262
|
+
) -> dict[str, dict[str, dict[str, dict[str, set[str]]]]]:
|
252
263
|
global_selectors = (
|
253
264
|
sb.global_app_selectors.exclude or [] if sb.global_app_selectors else []
|
254
265
|
)
|
255
266
|
return {
|
256
|
-
p.product_environment.product.name:
|
267
|
+
p.product_environment.product.name: get_selected_app_data(
|
257
268
|
global_selectors, p
|
258
269
|
)
|
259
270
|
for p in sb.products
|
@@ -287,7 +298,7 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
287
298
|
|
288
299
|
@staticmethod
|
289
300
|
def desired_abstract_status_board_map(
|
290
|
-
desired_product_apps: Mapping[str, set[str]],
|
301
|
+
desired_product_apps: Mapping[str, dict[str, dict[str, dict[str, set[str]]]]],
|
291
302
|
slodocs: list[SLODocumentV1],
|
292
303
|
) -> dict[str, AbstractStatusBoard]:
|
293
304
|
"""
|
@@ -299,7 +310,11 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
299
310
|
desired_abstract_status_board_map: dict[str, AbstractStatusBoard] = {}
|
300
311
|
for product_name, apps in desired_product_apps.items():
|
301
312
|
product = Product(
|
302
|
-
id=None,
|
313
|
+
id=None,
|
314
|
+
name=product_name,
|
315
|
+
fullname=product_name,
|
316
|
+
applications=[],
|
317
|
+
metadata={},
|
303
318
|
)
|
304
319
|
desired_abstract_status_board_map[product_name] = product
|
305
320
|
for a in apps:
|
@@ -310,6 +325,7 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
310
325
|
fullname=key,
|
311
326
|
services=[],
|
312
327
|
product=product,
|
328
|
+
metadata=apps[a]["metadata"],
|
313
329
|
)
|
314
330
|
for slodoc in slodocs:
|
315
331
|
products = [
|
@@ -373,6 +389,99 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
373
389
|
|
374
390
|
return return_value
|
375
391
|
|
392
|
+
@staticmethod
|
393
|
+
def _compare_metadata(
|
394
|
+
current_metadata: dict[str, Any] | ServiceMetadataSpec,
|
395
|
+
desired_metadata: dict[str, Any] | ServiceMetadataSpec,
|
396
|
+
) -> bool:
|
397
|
+
"""
|
398
|
+
Compare metadata dictionaries with deep equality checking for nested structures.
|
399
|
+
|
400
|
+
:param current_metadata: The current metadata dictionary
|
401
|
+
:param desired_metadata: The desired metadata dictionary
|
402
|
+
:return: True if metadata are equal, False otherwise
|
403
|
+
"""
|
404
|
+
# Convert TypedDict to regular dict to allow variable key access
|
405
|
+
current_dict = dict(current_metadata)
|
406
|
+
desired_dict = dict(desired_metadata)
|
407
|
+
|
408
|
+
if current_dict.keys() != desired_dict.keys():
|
409
|
+
return False
|
410
|
+
|
411
|
+
for key, current_value in current_dict.items():
|
412
|
+
desired_value = desired_dict[key]
|
413
|
+
|
414
|
+
# Handle None values
|
415
|
+
if current_value is None or desired_value is None:
|
416
|
+
if current_value is not desired_value:
|
417
|
+
return False
|
418
|
+
continue
|
419
|
+
|
420
|
+
# Handle sets
|
421
|
+
if isinstance(current_value, set) and isinstance(desired_value, set):
|
422
|
+
if current_value != desired_value:
|
423
|
+
return False
|
424
|
+
# Handle lists and tuples
|
425
|
+
elif isinstance(current_value, (list, tuple)) and isinstance(
|
426
|
+
desired_value, (list, tuple)
|
427
|
+
):
|
428
|
+
if len(current_value) != len(desired_value):
|
429
|
+
return False
|
430
|
+
|
431
|
+
try:
|
432
|
+
sorted_current = sorted(current_value, key=repr)
|
433
|
+
sorted_desired = sorted(desired_value, key=repr)
|
434
|
+
except Exception:
|
435
|
+
# Fallback: compare without sorting
|
436
|
+
sorted_current = list(current_value)
|
437
|
+
sorted_desired = list(desired_value)
|
438
|
+
|
439
|
+
for c, d in zip(sorted_current, sorted_desired, strict=True):
|
440
|
+
if isinstance(c, dict) and isinstance(d, dict):
|
441
|
+
if not StatusBoardExporterIntegration._compare_metadata(c, d):
|
442
|
+
return False
|
443
|
+
elif isinstance(c, (list, tuple)) and isinstance(d, (list, tuple)):
|
444
|
+
if not StatusBoardExporterIntegration._compare_metadata(
|
445
|
+
{"x": c}, {"x": d}
|
446
|
+
):
|
447
|
+
return False
|
448
|
+
elif c != d:
|
449
|
+
return False
|
450
|
+
# Handle nested dictionaries
|
451
|
+
elif isinstance(current_value, dict) and isinstance(desired_value, dict):
|
452
|
+
if not StatusBoardExporterIntegration._compare_metadata(
|
453
|
+
current_value, desired_value
|
454
|
+
):
|
455
|
+
return False
|
456
|
+
# Handle primitive types
|
457
|
+
elif current_value != desired_value:
|
458
|
+
return False
|
459
|
+
|
460
|
+
return True
|
461
|
+
|
462
|
+
@staticmethod
|
463
|
+
def _status_board_objects_equal(
|
464
|
+
current: AbstractStatusBoard, desired: AbstractStatusBoard
|
465
|
+
) -> bool:
|
466
|
+
"""
|
467
|
+
Check if two AbstractStatusBoard objects are equal, including metadata comparison.
|
468
|
+
|
469
|
+
:param current: The current AbstractStatusBoard object
|
470
|
+
:param desired: The desired AbstractStatusBoard object
|
471
|
+
:return: True if objects are equal, False otherwise
|
472
|
+
"""
|
473
|
+
# Check basic equality first (name, fullname)
|
474
|
+
if current.name != desired.name or current.fullname != desired.fullname:
|
475
|
+
return False
|
476
|
+
|
477
|
+
# Compare metadata with deep equality
|
478
|
+
if current.metadata and desired.metadata:
|
479
|
+
return StatusBoardExporterIntegration._compare_metadata(
|
480
|
+
current.metadata, desired.metadata
|
481
|
+
)
|
482
|
+
else:
|
483
|
+
return True
|
484
|
+
|
376
485
|
@staticmethod
|
377
486
|
def get_diff(
|
378
487
|
desired_abstract_status_board_map: Mapping[str, AbstractStatusBoard],
|
@@ -383,6 +492,7 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
383
492
|
diff_result = diff_mappings(
|
384
493
|
current_abstract_status_board_map,
|
385
494
|
desired_abstract_status_board_map,
|
495
|
+
equal=StatusBoardExporterIntegration._status_board_objects_equal,
|
386
496
|
)
|
387
497
|
|
388
498
|
for pair in chain(diff_result.identical.values(), diff_result.change.values()):
|
@@ -398,6 +508,18 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
398
508
|
for o in diff_result.delete.values()
|
399
509
|
)
|
400
510
|
|
511
|
+
# Handle Applications that need updates (metadata changes)
|
512
|
+
applications_to_update = [
|
513
|
+
a.desired
|
514
|
+
for _, a in diff_result.change.items()
|
515
|
+
if isinstance(a.desired, Application)
|
516
|
+
]
|
517
|
+
|
518
|
+
return_list.extend(
|
519
|
+
StatusBoardHandler(action=Action.update, status_board_object=a)
|
520
|
+
for a in applications_to_update
|
521
|
+
)
|
522
|
+
|
401
523
|
services_to_update = [
|
402
524
|
s.desired
|
403
525
|
for _, s in diff_result.change.items()
|
@@ -445,7 +567,9 @@ class StatusBoardExporterIntegration(QontractReconcileIntegration):
|
|
445
567
|
ocm_api = init_ocm_base_client(sb.ocm, self.secret_reader)
|
446
568
|
|
447
569
|
# Desired state
|
448
|
-
desired_product_apps: dict[
|
570
|
+
desired_product_apps: dict[
|
571
|
+
str, dict[str, dict[str, dict[str, set[str]]]]
|
572
|
+
] = self.get_product_apps(sb)
|
449
573
|
desired_abstract_status_board_map = self.desired_abstract_status_board_map(
|
450
574
|
desired_product_apps, slodocs
|
451
575
|
)
|
@@ -9,6 +9,10 @@ from reconcile.gql_definitions.status_board.status_board import (
|
|
9
9
|
query,
|
10
10
|
)
|
11
11
|
from reconcile.utils import gql
|
12
|
+
from reconcile.utils.ocm.status_board import (
|
13
|
+
METADATA_MANAGED_BY_KEY,
|
14
|
+
METADATA_MANAGED_BY_VALUE,
|
15
|
+
)
|
12
16
|
|
13
17
|
|
14
18
|
def get_status_board(
|
@@ -19,11 +23,11 @@ def get_status_board(
|
|
19
23
|
return query(query_func).status_board_v1 or []
|
20
24
|
|
21
25
|
|
22
|
-
def
|
26
|
+
def get_selected_app_data(
|
23
27
|
global_selectors: Iterable[str],
|
24
28
|
product: StatusBoardProductV1,
|
25
|
-
) -> set[str]:
|
26
|
-
|
29
|
+
) -> dict[str, dict[str, dict[str, set[str]]]]:
|
30
|
+
selected_app_data: dict[str, dict[str, dict[str, Any]]] = {}
|
27
31
|
|
28
32
|
apps: dict[str, Any] = {"apps": []}
|
29
33
|
for namespace in product.product_environment.namespaces or []:
|
@@ -31,15 +35,45 @@ def get_selected_app_names(
|
|
31
35
|
if namespace.app.parent_app:
|
32
36
|
prefix = f"{namespace.app.parent_app.name}-"
|
33
37
|
name = f"{prefix}{namespace.app.name}"
|
34
|
-
|
38
|
+
|
39
|
+
deployment_saas_files = set()
|
40
|
+
if namespace.app.saas_files:
|
41
|
+
deployment_saas_files = {
|
42
|
+
saas_file.name
|
43
|
+
for saas_file in namespace.app.saas_files
|
44
|
+
if "Deployment" in saas_file.managed_resource_types
|
45
|
+
or "ClowdApp" in saas_file.managed_resource_types
|
46
|
+
}
|
47
|
+
|
48
|
+
selected_app_data[name] = {
|
49
|
+
"metadata": {
|
50
|
+
METADATA_MANAGED_BY_KEY: METADATA_MANAGED_BY_VALUE,
|
51
|
+
"deploymentSaasFiles": set(deployment_saas_files),
|
52
|
+
},
|
53
|
+
}
|
54
|
+
|
35
55
|
app = namespace.app.dict(by_alias=True)
|
36
56
|
app["name"] = name
|
37
57
|
apps["apps"].append(app)
|
38
58
|
|
39
59
|
for child in namespace.app.children_apps or []:
|
40
60
|
name = f"{namespace.app.name}-{child.name}"
|
41
|
-
if name not in
|
42
|
-
|
61
|
+
if name not in selected_app_data:
|
62
|
+
deployment_saas_files = set()
|
63
|
+
if child.saas_files:
|
64
|
+
deployment_saas_files = {
|
65
|
+
saas_file.name
|
66
|
+
for saas_file in child.saas_files
|
67
|
+
if "Deployment" in saas_file.managed_resource_types
|
68
|
+
}
|
69
|
+
|
70
|
+
selected_app_data[name] = {
|
71
|
+
"metadata": {
|
72
|
+
METADATA_MANAGED_BY_KEY: METADATA_MANAGED_BY_VALUE,
|
73
|
+
"deploymentSaasFiles": set(deployment_saas_files),
|
74
|
+
},
|
75
|
+
}
|
76
|
+
|
43
77
|
child_dict = child.dict(by_alias=True)
|
44
78
|
child_dict["name"] = name
|
45
79
|
apps["apps"].append(child_dict)
|
@@ -52,6 +86,7 @@ def get_selected_app_names(
|
|
52
86
|
apps_to_remove: set[str] = set()
|
53
87
|
results = parser.parse(selector).find(apps)
|
54
88
|
apps_to_remove.update(match.value["name"] for match in results)
|
55
|
-
|
89
|
+
for app_name in apps_to_remove:
|
90
|
+
selected_app_data.pop(app_name, None)
|
56
91
|
|
57
|
-
return
|
92
|
+
return selected_app_data
|
@@ -21,6 +21,7 @@ class IDSpec(TypedDict):
|
|
21
21
|
|
22
22
|
|
23
23
|
class ApplicationOCMSpec(BaseOCMSpec):
|
24
|
+
metadata: dict[str, Any]
|
24
25
|
product: IDSpec
|
25
26
|
|
26
27
|
|
@@ -117,6 +118,18 @@ def create_service(ocm_api: OCMBaseClient, spec: ServiceOCMSpec) -> str:
|
|
117
118
|
return resp["id"]
|
118
119
|
|
119
120
|
|
121
|
+
def update_application(
|
122
|
+
ocm_api: OCMBaseClient,
|
123
|
+
application_id: str,
|
124
|
+
spec: ApplicationOCMSpec,
|
125
|
+
) -> None:
|
126
|
+
data = spec | {
|
127
|
+
"metadata": spec.get("metadata", {})
|
128
|
+
| {METADATA_MANAGED_BY_KEY: METADATA_MANAGED_BY_VALUE}
|
129
|
+
}
|
130
|
+
ocm_api.patch(f"/api/status-board/v1/applications/{application_id}", data=data)
|
131
|
+
|
132
|
+
|
120
133
|
def update_service(
|
121
134
|
ocm_api: OCMBaseClient,
|
122
135
|
service_id: str,
|
@@ -5563,22 +5563,27 @@ class TerrascriptClient:
|
|
5563
5563
|
|
5564
5564
|
# mutual authentication section
|
5565
5565
|
if mutual_authentication := resource.get("mutual_authentication"):
|
5566
|
-
|
5567
|
-
"
|
5568
|
-
"
|
5569
|
-
|
5570
|
-
|
5571
|
-
|
5572
|
-
|
5573
|
-
|
5574
|
-
|
5575
|
-
|
5576
|
-
|
5577
|
-
|
5578
|
-
|
5579
|
-
|
5580
|
-
|
5581
|
-
|
5566
|
+
if mutual_authentication["mode"] in {"off", "passthrough"}:
|
5567
|
+
values["mutual_authentication"] = {
|
5568
|
+
"mode": mutual_authentication["mode"],
|
5569
|
+
}
|
5570
|
+
else:
|
5571
|
+
trust_store_values = {
|
5572
|
+
"ca_certificates_bundle_s3_bucket": mutual_authentication[
|
5573
|
+
"ca_cert_bundle_s3_bucket_name"
|
5574
|
+
],
|
5575
|
+
"ca_certificates_bundle_s3_key": mutual_authentication[
|
5576
|
+
"ca_cert_bundle_s3_bucket_key"
|
5577
|
+
],
|
5578
|
+
}
|
5579
|
+
trust_store = aws_lb_trust_store(
|
5580
|
+
f"{identifier}-trust-store", **trust_store_values
|
5581
|
+
)
|
5582
|
+
tf_resources.append(trust_store)
|
5583
|
+
values["mutual_authentication"] = {
|
5584
|
+
"mode": mutual_authentication["mode"],
|
5585
|
+
"trust_store_arn": f"${{{trust_store.arn}}}",
|
5586
|
+
}
|
5582
5587
|
|
5583
5588
|
forward_identifier = f"{identifier}-forward"
|
5584
5589
|
forward_lbl_tf_resource = aws_lb_listener(forward_identifier, **values)
|
tools/qontract_cli.py
CHANGED
@@ -2772,7 +2772,7 @@ def slo_document_services(ctx: click.Context, status_board_instance: str) -> Non
|
|
2772
2772
|
print(f"Status-board instance '{status_board_instance}' not found.")
|
2773
2773
|
sys.exit(1)
|
2774
2774
|
|
2775
|
-
desired_product_apps: dict[str, set[str]] = (
|
2775
|
+
desired_product_apps: dict[str, dict[str, dict[str, dict[str, set[str]]]]] = (
|
2776
2776
|
StatusBoardExporterIntegration.get_product_apps(sb)
|
2777
2777
|
)
|
2778
2778
|
|
{qontract_reconcile-0.10.2.dev298.dist-info → qontract_reconcile-0.10.2.dev300.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|