qontract-reconcile 0.10.2.dev203__py3-none-any.whl → 0.10.2.dev205__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.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/RECORD +12 -6
- reconcile/cli.py +21 -0
- reconcile/gql_definitions/common/rhcs_provider_settings.py +70 -0
- reconcile/gql_definitions/introspection.json +181 -0
- reconcile/gql_definitions/rhcs/__init__.py +0 -0
- reconcile/gql_definitions/rhcs/certs.py +158 -0
- reconcile/openshift_rhcs_certs.py +283 -0
- reconcile/typed_queries/rhcs_provider_settings.py +21 -0
- reconcile/utils/rhcsv2_certs.py +76 -0
- {qontract_reconcile-0.10.2.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev203.dist-info → qontract_reconcile-0.10.2.dev205.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.dev205
|
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.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/RECORD
RENAMED
@@ -9,7 +9,7 @@ reconcile/aws_iam_password_reset.py,sha256=O0JX2N5kNRKs3u2xzu4NNrI6p0ag5JWy3MTsv
|
|
9
9
|
reconcile/aws_support_cases_sos.py,sha256=PDhilxQ4TBxVnxUPIUdTbKEaNUI0wzPiEsB91oHT2fY,3384
|
10
10
|
reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
|
11
11
|
reconcile/checkpoint.py,sha256=_JhMxrye5BgkRMxWYuf7Upli6XayPINKSsuo3ynHTRc,5010
|
12
|
-
reconcile/cli.py,sha256
|
12
|
+
reconcile/cli.py,sha256=e11ElvZwncpUcx3AwxajGLUfrLuAWm_4fOy_jPMm_oM,108741
|
13
13
|
reconcile/closedbox_endpoint_monitoring_base.py,sha256=al7m8EgnnYx90rY1REryW3byN_ItfJfAzEeLtjbCfi0,4921
|
14
14
|
reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
|
15
15
|
reconcile/dashdotdb_base.py,sha256=83ZWIf5JJk3P_D69y2TmXRcQr6ELJGlv10OM0h7fJVs,4767
|
@@ -72,6 +72,7 @@ reconcile/openshift_prometheus_rules.py,sha256=onowXab248zmHH8SbYDTc1W1bl7JiqRFU
|
|
72
72
|
reconcile/openshift_resourcequotas.py,sha256=yUi56PiOn3inMMfq_x_FEHmaW-reGipzoorjdar372g,2415
|
73
73
|
reconcile/openshift_resources.py,sha256=I2nO_C37mG3rfyGrd4cGwN3mVseVGuTAHAyhFzLyqF4,1518
|
74
74
|
reconcile/openshift_resources_base.py,sha256=3HudPdM7EE0HNWUn1eu0O20Ij25fqGisaDBMVvTk1fk,41768
|
75
|
+
reconcile/openshift_rhcs_certs.py,sha256=lP0GwKMRl8YBzxrwdbBOxrPqIPYNmu2KkZPGzWKyRVU,9859
|
75
76
|
reconcile/openshift_rolebindings.py,sha256=9mlJ2FjWUoH-rsjtasreA_hV-K5Z_YR00qR_RR60OZM,6555
|
76
77
|
reconcile/openshift_routes.py,sha256=fXvuPSjcjVw1X3j2EQvUAdbOepmIFdKk-M3qP8QzPiw,1075
|
77
78
|
reconcile/openshift_saas_deploy.py,sha256=T1dvb9zajisaJNjbnR6-AZHU-itscHtr4oCqLj8KCK0,13037
|
@@ -226,7 +227,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=BgMx-NyV9mTuv7Sotb2OioC
|
|
226
227
|
reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
227
228
|
reconcile/glitchtip_project_dsn/integration.py,sha256=2iugub-kHYkHNK33n0v9_TeWonuxCPah_VkoTPvaajE,8077
|
228
229
|
reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
229
|
-
reconcile/gql_definitions/introspection.json,sha256=
|
230
|
+
reconcile/gql_definitions/introspection.json,sha256=oYlYgYdUjLtUwu9mJA-bh1y6BTTlWgVhL07N-9_9Du0,2335501
|
230
231
|
reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
231
232
|
reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
|
232
233
|
reconcile/gql_definitions/acs/acs_policies.py,sha256=Ygpfl2-VkYLSlJvHgp_dJBfb66K_Rwfdfpsa18w1v1s,4338
|
@@ -295,6 +296,7 @@ reconcile/gql_definitions/common/pipeline_providers.py,sha256=9rpsqPuvj82B4ki56x
|
|
295
296
|
reconcile/gql_definitions/common/quay_instances.py,sha256=toBkdYYVTmEafezAHZKgaW-mQ29xEW6jeronzsAlNyI,1786
|
296
297
|
reconcile/gql_definitions/common/quay_orgs.py,sha256=NhA8kqvVUDbrsryEvEL5mlIv5R3T4XNhSRXtfL_yptY,1788
|
297
298
|
reconcile/gql_definitions/common/reserved_networks.py,sha256=yP9qSQCaSQcva-ZgTnZp09qH27ur5_qK080ToIs04MY,2560
|
299
|
+
reconcile/gql_definitions/common/rhcs_provider_settings.py,sha256=88NwWT1kmbysze_w4YT6kiOYU2StDHHP6QRQXnPlpmQ,2057
|
298
300
|
reconcile/gql_definitions/common/saas_files.py,sha256=d1L_S5LgCMa4QuAqZGQYTWb5L_nPCOxSEjU4O__OeBU,17728
|
299
301
|
reconcile/gql_definitions/common/saas_target_namespaces.py,sha256=4VYP2VbwY8WVwtSFk2-jsUNhSmRD3X4FWKxetOKvmd0,2835
|
300
302
|
reconcile/gql_definitions/common/saasherder_settings.py,sha256=nqQLcMwYxLseqq0BEcVvmrpIj2eQq0h8XDSpLN6GGCw,1793
|
@@ -401,6 +403,8 @@ reconcile/gql_definitions/openshift_serviceaccount_tokens/__init__.py,sha256=47D
|
|
401
403
|
reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py,sha256=FeraeQThHl2UbhTuCPDwm3ltczF_jfZn5E3Squ8-znw,3313
|
402
404
|
reconcile/gql_definitions/quay_membership/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
403
405
|
reconcile/gql_definitions/quay_membership/quay_membership.py,sha256=MKBkrE-1YYelaAAxOdpqUwCo45kOVC8q29vXArqK_zM,3075
|
406
|
+
reconcile/gql_definitions/rhcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
407
|
+
reconcile/gql_definitions/rhcs/certs.py,sha256=8ba9GZVY70ppekuxrMjE4wm6WqcMW2IFawjhWvxHrmI,4677
|
404
408
|
reconcile/gql_definitions/rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
405
409
|
reconcile/gql_definitions/rhidp/organizations.py,sha256=dW9y3ewFu3E-DFrZAi_SEewHYR0MWYeOB52vwnVcq5E,2580
|
406
410
|
reconcile/gql_definitions/service_dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -571,6 +575,7 @@ reconcile/typed_queries/pagerduty_instances.py,sha256=zxCNxMak4iikryePaRi71lTADV
|
|
571
575
|
reconcile/typed_queries/quay.py,sha256=3IMy9jjHF2f9t47EXZOQVA3p0nFkWFhaFhxhvib-71o,644
|
572
576
|
reconcile/typed_queries/repos.py,sha256=8A93dKDt6igT4ClqMjt7YUTsoP4qh1Wnm0W3xsMgj48,824
|
573
577
|
reconcile/typed_queries/reserved_networks.py,sha256=XY9y3amtIQT0n06O0Toubqr_UmylJ2ELAv9-BJCK890,345
|
578
|
+
reconcile/typed_queries/rhcs_provider_settings.py,sha256=fxD84CPCiH25_qc02p48LOIJHv39Wlc1VwOhs9ZVsKk,743
|
574
579
|
reconcile/typed_queries/saas_files.py,sha256=SOE36sWPBcuaRmEaNxXCQZMQdJiUZX8_A92o42XwHQA,14141
|
575
580
|
reconcile/typed_queries/slack.py,sha256=r30lspctHloyygPn8_DVybxPwUWwiBpvBRRXiTVcQYk,251
|
576
581
|
reconcile/typed_queries/slo_documents.py,sha256=YMdox_-lBRqrdxamPhdnUlRTY_Ro35ptsupq7OaynUQ,362
|
@@ -658,6 +663,7 @@ reconcile/utils/quay_mirror.py,sha256=dpWCNv5lITwIk6Q9RkmqaQKHNk_JPy27UQEribJ7E-
|
|
658
663
|
reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
|
659
664
|
reconcile/utils/repo_owners.py,sha256=P0QX6F0oB8wYA08yiyzhYUiBtU57iIK_PsxbzKENbKM,6571
|
660
665
|
reconcile/utils/rest_api_base.py,sha256=MT7tp6CQO2S5aKfVOzw_hipWg7wAGoOqkm4qurI1hEU,4342
|
666
|
+
reconcile/utils/rhcsv2_certs.py,sha256=ZnlUlEI2k6UrljGarkm1ey0znMlQtjeZB7VEfCH1A64,2545
|
661
667
|
reconcile/utils/ruamel.py,sha256=FzL4_L0FnMOUZmgThrZSMJs5MTdXwiy-E9MZWfk8bh8,397
|
662
668
|
reconcile/utils/secret_reader.py,sha256=MaP56KZaAE35EyYbgAitdm6fUSxdzWeGFSOym9qiZkw,10206
|
663
669
|
reconcile/utils/semver_helper.py,sha256=-WfPOMSA2v1h7hT3PwVf-Htg7wOsoKlQC1JdmDX2Ars,1268
|
@@ -809,7 +815,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
809
815
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
810
816
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
811
817
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
812
|
-
qontract_reconcile-0.10.2.
|
813
|
-
qontract_reconcile-0.10.2.
|
814
|
-
qontract_reconcile-0.10.2.
|
815
|
-
qontract_reconcile-0.10.2.
|
818
|
+
qontract_reconcile-0.10.2.dev205.dist-info/METADATA,sha256=Nk9aLZ_YnzuvFX0u_RJVWxJUztk3TT6ko7ANo6xbEgo,24555
|
819
|
+
qontract_reconcile-0.10.2.dev205.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
820
|
+
qontract_reconcile-0.10.2.dev205.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
821
|
+
qontract_reconcile-0.10.2.dev205.dist-info/RECORD,,
|
reconcile/cli.py
CHANGED
@@ -1739,6 +1739,27 @@ def openshift_vault_secrets(
|
|
1739
1739
|
)
|
1740
1740
|
|
1741
1741
|
|
1742
|
+
@integration.command(short_help="Manages OpenShift Secrets for RHCS certificates")
|
1743
|
+
@threaded()
|
1744
|
+
@binary(["oc", "ssh"])
|
1745
|
+
@binary_version("oc", ["version", "--client"], OC_VERSION_REGEX, OC_VERSIONS)
|
1746
|
+
@internal()
|
1747
|
+
@use_jump_host()
|
1748
|
+
@cluster_name
|
1749
|
+
@click.pass_context
|
1750
|
+
def openshift_rhcs_certs(ctx, thread_pool_size, internal, use_jump_host, cluster_name):
|
1751
|
+
import reconcile.openshift_rhcs_certs
|
1752
|
+
|
1753
|
+
run_integration(
|
1754
|
+
reconcile.openshift_rhcs_certs,
|
1755
|
+
ctx.obj,
|
1756
|
+
thread_pool_size,
|
1757
|
+
internal,
|
1758
|
+
use_jump_host,
|
1759
|
+
cluster_name=cluster_name,
|
1760
|
+
)
|
1761
|
+
|
1762
|
+
|
1742
1763
|
@integration.command(short_help="Manages OpenShift Routes.")
|
1743
1764
|
@threaded()
|
1744
1765
|
@binary(["oc", "ssh"])
|
@@ -0,0 +1,70 @@
|
|
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
|
+
|
21
|
+
DEFINITION = """
|
22
|
+
query RhcsProviderSettings {
|
23
|
+
settings: app_interface_settings_v1 {
|
24
|
+
rhcsProvider {
|
25
|
+
issuerUrl
|
26
|
+
vaultBasePath
|
27
|
+
caCertUrl
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
"""
|
32
|
+
|
33
|
+
|
34
|
+
class ConfiguredBaseModel(BaseModel):
|
35
|
+
class Config:
|
36
|
+
smart_union=True
|
37
|
+
extra=Extra.forbid
|
38
|
+
|
39
|
+
|
40
|
+
class RhcsProviderSettingsV1(ConfiguredBaseModel):
|
41
|
+
issuer_url: str = Field(..., alias="issuerUrl")
|
42
|
+
vault_base_path: str = Field(..., alias="vaultBasePath")
|
43
|
+
ca_cert_url: str = Field(..., alias="caCertUrl")
|
44
|
+
|
45
|
+
|
46
|
+
class AppInterfaceSettingsV1(ConfiguredBaseModel):
|
47
|
+
rhcs_provider: Optional[RhcsProviderSettingsV1] = Field(..., alias="rhcsProvider")
|
48
|
+
|
49
|
+
|
50
|
+
class RhcsProviderSettingsQueryData(ConfiguredBaseModel):
|
51
|
+
settings: Optional[list[AppInterfaceSettingsV1]] = Field(..., alias="settings")
|
52
|
+
|
53
|
+
|
54
|
+
def query(query_func: Callable, **kwargs: Any) -> RhcsProviderSettingsQueryData:
|
55
|
+
"""
|
56
|
+
This is a convenience function which queries and parses the data into
|
57
|
+
concrete types. It should be compatible with most GQL clients.
|
58
|
+
You do not have to use it to consume the generated data classes.
|
59
|
+
Alternatively, you can also mime and alternate the behavior
|
60
|
+
of this function in the caller.
|
61
|
+
|
62
|
+
Parameters:
|
63
|
+
query_func (Callable): Function which queries your GQL Server
|
64
|
+
kwargs: optional arguments that will be passed to the query function
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
RhcsProviderSettingsQueryData: queried data parsed into generated classes
|
68
|
+
"""
|
69
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
70
|
+
return RhcsProviderSettingsQueryData(**raw_data)
|
@@ -4362,6 +4362,18 @@
|
|
4362
4362
|
},
|
4363
4363
|
"isDeprecated": false,
|
4364
4364
|
"deprecationReason": null
|
4365
|
+
},
|
4366
|
+
{
|
4367
|
+
"name": "rhcsProvider",
|
4368
|
+
"description": null,
|
4369
|
+
"args": [],
|
4370
|
+
"type": {
|
4371
|
+
"kind": "OBJECT",
|
4372
|
+
"name": "RhcsProviderSettings_v1",
|
4373
|
+
"ofType": null
|
4374
|
+
},
|
4375
|
+
"isDeprecated": false,
|
4376
|
+
"deprecationReason": null
|
4365
4377
|
}
|
4366
4378
|
],
|
4367
4379
|
"inputFields": null,
|
@@ -24806,6 +24818,11 @@
|
|
24806
24818
|
"name": "NamespaceOpenshiftResourceResourceTemplate_v1",
|
24807
24819
|
"ofType": null
|
24808
24820
|
},
|
24821
|
+
{
|
24822
|
+
"kind": "OBJECT",
|
24823
|
+
"name": "NamespaceOpenshiftResourceRhcsCert_v1",
|
24824
|
+
"ofType": null
|
24825
|
+
},
|
24809
24826
|
{
|
24810
24827
|
"kind": "OBJECT",
|
24811
24828
|
"name": "NamespaceOpenshiftResourceRoute_v1",
|
@@ -26558,6 +26575,65 @@
|
|
26558
26575
|
"enumValues": null,
|
26559
26576
|
"possibleTypes": null
|
26560
26577
|
},
|
26578
|
+
{
|
26579
|
+
"kind": "OBJECT",
|
26580
|
+
"name": "RhcsProviderSettings_v1",
|
26581
|
+
"description": null,
|
26582
|
+
"fields": [
|
26583
|
+
{
|
26584
|
+
"name": "issuerUrl",
|
26585
|
+
"description": null,
|
26586
|
+
"args": [],
|
26587
|
+
"type": {
|
26588
|
+
"kind": "NON_NULL",
|
26589
|
+
"name": null,
|
26590
|
+
"ofType": {
|
26591
|
+
"kind": "SCALAR",
|
26592
|
+
"name": "String",
|
26593
|
+
"ofType": null
|
26594
|
+
}
|
26595
|
+
},
|
26596
|
+
"isDeprecated": false,
|
26597
|
+
"deprecationReason": null
|
26598
|
+
},
|
26599
|
+
{
|
26600
|
+
"name": "vaultBasePath",
|
26601
|
+
"description": null,
|
26602
|
+
"args": [],
|
26603
|
+
"type": {
|
26604
|
+
"kind": "NON_NULL",
|
26605
|
+
"name": null,
|
26606
|
+
"ofType": {
|
26607
|
+
"kind": "SCALAR",
|
26608
|
+
"name": "String",
|
26609
|
+
"ofType": null
|
26610
|
+
}
|
26611
|
+
},
|
26612
|
+
"isDeprecated": false,
|
26613
|
+
"deprecationReason": null
|
26614
|
+
},
|
26615
|
+
{
|
26616
|
+
"name": "caCertUrl",
|
26617
|
+
"description": null,
|
26618
|
+
"args": [],
|
26619
|
+
"type": {
|
26620
|
+
"kind": "NON_NULL",
|
26621
|
+
"name": null,
|
26622
|
+
"ofType": {
|
26623
|
+
"kind": "SCALAR",
|
26624
|
+
"name": "String",
|
26625
|
+
"ofType": null
|
26626
|
+
}
|
26627
|
+
},
|
26628
|
+
"isDeprecated": false,
|
26629
|
+
"deprecationReason": null
|
26630
|
+
}
|
26631
|
+
],
|
26632
|
+
"inputFields": null,
|
26633
|
+
"interfaces": [],
|
26634
|
+
"enumValues": null,
|
26635
|
+
"possibleTypes": null
|
26636
|
+
},
|
26561
26637
|
{
|
26562
26638
|
"kind": "OBJECT",
|
26563
26639
|
"name": "AppInterfaceEmail_v1",
|
@@ -40971,6 +41047,111 @@
|
|
40971
41047
|
"enumValues": null,
|
40972
41048
|
"possibleTypes": null
|
40973
41049
|
},
|
41050
|
+
{
|
41051
|
+
"kind": "OBJECT",
|
41052
|
+
"name": "NamespaceOpenshiftResourceRhcsCert_v1",
|
41053
|
+
"description": null,
|
41054
|
+
"fields": [
|
41055
|
+
{
|
41056
|
+
"name": "provider",
|
41057
|
+
"description": null,
|
41058
|
+
"args": [],
|
41059
|
+
"type": {
|
41060
|
+
"kind": "NON_NULL",
|
41061
|
+
"name": null,
|
41062
|
+
"ofType": {
|
41063
|
+
"kind": "SCALAR",
|
41064
|
+
"name": "String",
|
41065
|
+
"ofType": null
|
41066
|
+
}
|
41067
|
+
},
|
41068
|
+
"isDeprecated": false,
|
41069
|
+
"deprecationReason": null
|
41070
|
+
},
|
41071
|
+
{
|
41072
|
+
"name": "secret_name",
|
41073
|
+
"description": null,
|
41074
|
+
"args": [],
|
41075
|
+
"type": {
|
41076
|
+
"kind": "NON_NULL",
|
41077
|
+
"name": null,
|
41078
|
+
"ofType": {
|
41079
|
+
"kind": "SCALAR",
|
41080
|
+
"name": "String",
|
41081
|
+
"ofType": null
|
41082
|
+
}
|
41083
|
+
},
|
41084
|
+
"isDeprecated": false,
|
41085
|
+
"deprecationReason": null
|
41086
|
+
},
|
41087
|
+
{
|
41088
|
+
"name": "service_account_name",
|
41089
|
+
"description": null,
|
41090
|
+
"args": [],
|
41091
|
+
"type": {
|
41092
|
+
"kind": "NON_NULL",
|
41093
|
+
"name": null,
|
41094
|
+
"ofType": {
|
41095
|
+
"kind": "SCALAR",
|
41096
|
+
"name": "String",
|
41097
|
+
"ofType": null
|
41098
|
+
}
|
41099
|
+
},
|
41100
|
+
"isDeprecated": false,
|
41101
|
+
"deprecationReason": null
|
41102
|
+
},
|
41103
|
+
{
|
41104
|
+
"name": "service_account_password",
|
41105
|
+
"description": null,
|
41106
|
+
"args": [],
|
41107
|
+
"type": {
|
41108
|
+
"kind": "NON_NULL",
|
41109
|
+
"name": null,
|
41110
|
+
"ofType": {
|
41111
|
+
"kind": "OBJECT",
|
41112
|
+
"name": "VaultSecret_v1",
|
41113
|
+
"ofType": null
|
41114
|
+
}
|
41115
|
+
},
|
41116
|
+
"isDeprecated": false,
|
41117
|
+
"deprecationReason": null
|
41118
|
+
},
|
41119
|
+
{
|
41120
|
+
"name": "auto_renew_threshold_days",
|
41121
|
+
"description": null,
|
41122
|
+
"args": [],
|
41123
|
+
"type": {
|
41124
|
+
"kind": "SCALAR",
|
41125
|
+
"name": "Int",
|
41126
|
+
"ofType": null
|
41127
|
+
},
|
41128
|
+
"isDeprecated": false,
|
41129
|
+
"deprecationReason": null
|
41130
|
+
},
|
41131
|
+
{
|
41132
|
+
"name": "annotations",
|
41133
|
+
"description": null,
|
41134
|
+
"args": [],
|
41135
|
+
"type": {
|
41136
|
+
"kind": "SCALAR",
|
41137
|
+
"name": "JSON",
|
41138
|
+
"ofType": null
|
41139
|
+
},
|
41140
|
+
"isDeprecated": false,
|
41141
|
+
"deprecationReason": null
|
41142
|
+
}
|
41143
|
+
],
|
41144
|
+
"inputFields": null,
|
41145
|
+
"interfaces": [
|
41146
|
+
{
|
41147
|
+
"kind": "INTERFACE",
|
41148
|
+
"name": "NamespaceOpenshiftResource_v1",
|
41149
|
+
"ofType": null
|
41150
|
+
}
|
41151
|
+
],
|
41152
|
+
"enumValues": null,
|
41153
|
+
"possibleTypes": null
|
41154
|
+
},
|
40974
41155
|
{
|
40975
41156
|
"kind": "OBJECT",
|
40976
41157
|
"name": "NamespaceOpenshiftResourceRoute_v1",
|
File without changes
|
@@ -0,0 +1,158 @@
|
|
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.jumphost_common_fields import CommonJumphostFields
|
21
|
+
from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
|
22
|
+
|
23
|
+
|
24
|
+
DEFINITION = """
|
25
|
+
fragment CommonJumphostFields on ClusterJumpHost_v1 {
|
26
|
+
hostname
|
27
|
+
knownHosts
|
28
|
+
user
|
29
|
+
port
|
30
|
+
remotePort
|
31
|
+
identity {
|
32
|
+
... VaultSecret
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
fragment VaultSecret on VaultSecret_v1 {
|
37
|
+
path
|
38
|
+
field
|
39
|
+
version
|
40
|
+
format
|
41
|
+
}
|
42
|
+
|
43
|
+
query RhcsCerts {
|
44
|
+
namespaces: namespaces_v1 {
|
45
|
+
name
|
46
|
+
delete
|
47
|
+
clusterAdmin
|
48
|
+
openshiftResources {
|
49
|
+
provider
|
50
|
+
... on NamespaceOpenshiftResourceRhcsCert_v1 {
|
51
|
+
secret_name
|
52
|
+
service_account_name
|
53
|
+
service_account_password {
|
54
|
+
... on VaultSecret_v1 {
|
55
|
+
path
|
56
|
+
field
|
57
|
+
version
|
58
|
+
}
|
59
|
+
}
|
60
|
+
auto_renew_threshold_days
|
61
|
+
annotations
|
62
|
+
}
|
63
|
+
}
|
64
|
+
cluster {
|
65
|
+
name
|
66
|
+
serverUrl
|
67
|
+
insecureSkipTLSVerify
|
68
|
+
jumpHost {
|
69
|
+
... CommonJumphostFields
|
70
|
+
}
|
71
|
+
automationToken {
|
72
|
+
... VaultSecret
|
73
|
+
}
|
74
|
+
clusterAdminAutomationToken {
|
75
|
+
... VaultSecret
|
76
|
+
}
|
77
|
+
internal
|
78
|
+
disable {
|
79
|
+
integrations
|
80
|
+
}
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}
|
84
|
+
"""
|
85
|
+
|
86
|
+
|
87
|
+
class ConfiguredBaseModel(BaseModel):
|
88
|
+
class Config:
|
89
|
+
smart_union=True
|
90
|
+
extra=Extra.forbid
|
91
|
+
|
92
|
+
|
93
|
+
class NamespaceOpenshiftResourceV1(ConfiguredBaseModel):
|
94
|
+
provider: str = Field(..., alias="provider")
|
95
|
+
|
96
|
+
|
97
|
+
class VaultSecretV1(ConfiguredBaseModel):
|
98
|
+
...
|
99
|
+
|
100
|
+
|
101
|
+
class VaultSecretV1_VaultSecretV1(VaultSecretV1):
|
102
|
+
path: str = Field(..., alias="path")
|
103
|
+
field: str = Field(..., alias="field")
|
104
|
+
version: Optional[int] = Field(..., alias="version")
|
105
|
+
|
106
|
+
|
107
|
+
class NamespaceOpenshiftResourceRhcsCertV1(NamespaceOpenshiftResourceV1):
|
108
|
+
secret_name: str = Field(..., alias="secret_name")
|
109
|
+
service_account_name: str = Field(..., alias="service_account_name")
|
110
|
+
service_account_password: Union[VaultSecretV1_VaultSecretV1, VaultSecretV1] = Field(..., alias="service_account_password")
|
111
|
+
auto_renew_threshold_days: Optional[int] = Field(..., alias="auto_renew_threshold_days")
|
112
|
+
annotations: Optional[Json] = Field(..., alias="annotations")
|
113
|
+
|
114
|
+
|
115
|
+
class DisableClusterAutomationsV1(ConfiguredBaseModel):
|
116
|
+
integrations: Optional[list[str]] = Field(..., alias="integrations")
|
117
|
+
|
118
|
+
|
119
|
+
class ClusterV1(ConfiguredBaseModel):
|
120
|
+
name: str = Field(..., alias="name")
|
121
|
+
server_url: str = Field(..., alias="serverUrl")
|
122
|
+
insecure_skip_tls_verify: Optional[bool] = Field(..., alias="insecureSkipTLSVerify")
|
123
|
+
jump_host: Optional[CommonJumphostFields] = Field(..., alias="jumpHost")
|
124
|
+
automation_token: Optional[VaultSecret] = Field(..., alias="automationToken")
|
125
|
+
cluster_admin_automation_token: Optional[VaultSecret] = Field(..., alias="clusterAdminAutomationToken")
|
126
|
+
internal: Optional[bool] = Field(..., alias="internal")
|
127
|
+
disable: Optional[DisableClusterAutomationsV1] = Field(..., alias="disable")
|
128
|
+
|
129
|
+
|
130
|
+
class NamespaceV1(ConfiguredBaseModel):
|
131
|
+
name: str = Field(..., alias="name")
|
132
|
+
delete: Optional[bool] = Field(..., alias="delete")
|
133
|
+
cluster_admin: Optional[bool] = Field(..., alias="clusterAdmin")
|
134
|
+
openshift_resources: Optional[list[Union[NamespaceOpenshiftResourceRhcsCertV1, NamespaceOpenshiftResourceV1]]] = Field(..., alias="openshiftResources")
|
135
|
+
cluster: ClusterV1 = Field(..., alias="cluster")
|
136
|
+
|
137
|
+
|
138
|
+
class RhcsCertsQueryData(ConfiguredBaseModel):
|
139
|
+
namespaces: Optional[list[NamespaceV1]] = Field(..., alias="namespaces")
|
140
|
+
|
141
|
+
|
142
|
+
def query(query_func: Callable, **kwargs: Any) -> RhcsCertsQueryData:
|
143
|
+
"""
|
144
|
+
This is a convenience function which queries and parses the data into
|
145
|
+
concrete types. It should be compatible with most GQL clients.
|
146
|
+
You do not have to use it to consume the generated data classes.
|
147
|
+
Alternatively, you can also mime and alternate the behavior
|
148
|
+
of this function in the caller.
|
149
|
+
|
150
|
+
Parameters:
|
151
|
+
query_func (Callable): Function which queries your GQL Server
|
152
|
+
kwargs: optional arguments that will be passed to the query function
|
153
|
+
|
154
|
+
Returns:
|
155
|
+
RhcsCertsQueryData: queried data parsed into generated classes
|
156
|
+
"""
|
157
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
158
|
+
return RhcsCertsQueryData(**raw_data)
|
@@ -0,0 +1,283 @@
|
|
1
|
+
import logging
|
2
|
+
import sys
|
3
|
+
import time
|
4
|
+
from collections.abc import Callable, Iterable, Mapping
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
import reconcile.openshift_base as ob
|
8
|
+
import reconcile.openshift_resources_base as orb
|
9
|
+
from reconcile.gql_definitions.common.rhcs_provider_settings import (
|
10
|
+
RhcsProviderSettingsV1,
|
11
|
+
)
|
12
|
+
from reconcile.gql_definitions.rhcs.certs import (
|
13
|
+
NamespaceOpenshiftResourceRhcsCertV1,
|
14
|
+
NamespaceV1,
|
15
|
+
)
|
16
|
+
from reconcile.gql_definitions.rhcs.certs import (
|
17
|
+
query as rhcs_certs_query,
|
18
|
+
)
|
19
|
+
from reconcile.status import ExitCodes
|
20
|
+
from reconcile.typed_queries.app_interface_vault_settings import (
|
21
|
+
get_app_interface_vault_settings,
|
22
|
+
)
|
23
|
+
from reconcile.typed_queries.rhcs_provider_settings import get_rhcs_provider_settings
|
24
|
+
from reconcile.utils import gql, metrics
|
25
|
+
from reconcile.utils.disabled_integrations import integration_is_enabled
|
26
|
+
from reconcile.utils.metrics import GaugeMetric, normalize_integration_name
|
27
|
+
from reconcile.utils.oc_map import init_oc_map_from_namespaces
|
28
|
+
from reconcile.utils.openshift_resource import OpenshiftResource as OR
|
29
|
+
from reconcile.utils.openshift_resource import (
|
30
|
+
ResourceInventory,
|
31
|
+
base64_encode_secret_field_value,
|
32
|
+
)
|
33
|
+
from reconcile.utils.rhcsv2_certs import RhcsV2Cert, generate_cert
|
34
|
+
from reconcile.utils.runtime.integration import DesiredStateShardConfig
|
35
|
+
from reconcile.utils.secret_reader import create_secret_reader
|
36
|
+
from reconcile.utils.semver_helper import make_semver
|
37
|
+
from reconcile.utils.vault import SecretNotFound, _VaultClient
|
38
|
+
|
39
|
+
QONTRACT_INTEGRATION = "openshift-rhcs-certs"
|
40
|
+
QONTRACT_INTEGRATION_VERSION = make_semver(1, 9, 3)
|
41
|
+
PROVIDERS = ["rhcs-cert"]
|
42
|
+
|
43
|
+
|
44
|
+
def desired_state_shard_config() -> DesiredStateShardConfig:
|
45
|
+
return DesiredStateShardConfig(
|
46
|
+
shard_arg_name="cluster_name",
|
47
|
+
shard_path_selectors={
|
48
|
+
"state.*.shard",
|
49
|
+
},
|
50
|
+
sharded_run_review=lambda proposal: len(proposal.proposed_shards) <= 2,
|
51
|
+
)
|
52
|
+
|
53
|
+
|
54
|
+
class OpenshiftRhcsCertExpiration(GaugeMetric):
|
55
|
+
"""Expiration timestamp of RHCS certificate stored in Vault."""
|
56
|
+
|
57
|
+
integration: str = normalize_integration_name(QONTRACT_INTEGRATION)
|
58
|
+
cert_name: str
|
59
|
+
cluster: str
|
60
|
+
namespace: str
|
61
|
+
|
62
|
+
@classmethod
|
63
|
+
def name(cls) -> str:
|
64
|
+
return "qontract_reconcile_rhcs_cert_expiration_timestamp"
|
65
|
+
|
66
|
+
|
67
|
+
def get_namespaces_with_rhcs_certs(
|
68
|
+
query_func: Callable, cluster_name: Iterable[str] | None = None
|
69
|
+
) -> list[NamespaceV1]:
|
70
|
+
return [
|
71
|
+
ns
|
72
|
+
for ns in rhcs_certs_query(query_func=query_func).namespaces or []
|
73
|
+
if integration_is_enabled(QONTRACT_INTEGRATION, ns.cluster)
|
74
|
+
and not bool(ns.delete)
|
75
|
+
and (not cluster_name or ns.cluster.name in cluster_name)
|
76
|
+
and any(
|
77
|
+
isinstance(r, NamespaceOpenshiftResourceRhcsCertV1)
|
78
|
+
for r in ns.openshift_resources or []
|
79
|
+
)
|
80
|
+
]
|
81
|
+
|
82
|
+
|
83
|
+
def construct_rhcs_cert_oc_secret(
|
84
|
+
secret_name: str, cert: Mapping[str, Any], annotations: Mapping[str, str]
|
85
|
+
) -> OR:
|
86
|
+
body: dict[str, Any] = {
|
87
|
+
"apiVersion": "v1",
|
88
|
+
"kind": "Secret",
|
89
|
+
"type": "kubernetes.io/tls",
|
90
|
+
"metadata": {"name": secret_name, "annotations": annotations},
|
91
|
+
}
|
92
|
+
for k, v in cert.items():
|
93
|
+
v = base64_encode_secret_field_value(v)
|
94
|
+
body.setdefault("data", {})[k] = v
|
95
|
+
return OR(body, QONTRACT_INTEGRATION, QONTRACT_INTEGRATION_VERSION)
|
96
|
+
|
97
|
+
|
98
|
+
def cert_expires_within_threshold(
|
99
|
+
ns: NamespaceV1,
|
100
|
+
cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
|
101
|
+
vault_cert_secret: Mapping[str, Any],
|
102
|
+
) -> bool:
|
103
|
+
auto_renew_threshold_days = cert_resource.auto_renew_threshold_days or 7
|
104
|
+
expires_in = int(vault_cert_secret["expiration_timestamp"]) - time.time()
|
105
|
+
threshold_in_seconds = 60 * 60 * 24 * auto_renew_threshold_days
|
106
|
+
if expires_in < threshold_in_seconds:
|
107
|
+
logging.info(
|
108
|
+
f"Existing cert expires within threshold: cluster='{ns.cluster.name}', namespace='{ns.name}', secret='{cert_resource.secret_name}', threshold='{auto_renew_threshold_days} days'"
|
109
|
+
)
|
110
|
+
return True
|
111
|
+
return False
|
112
|
+
|
113
|
+
|
114
|
+
def get_vault_cert_secret(
|
115
|
+
ns: NamespaceV1,
|
116
|
+
cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
|
117
|
+
vault: _VaultClient,
|
118
|
+
vault_base_path: str,
|
119
|
+
) -> dict | None:
|
120
|
+
vault_cert_secret = None
|
121
|
+
try:
|
122
|
+
vault_cert_secret = vault.read_all({
|
123
|
+
"path": f"{vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}"
|
124
|
+
})
|
125
|
+
except SecretNotFound:
|
126
|
+
logging.info(
|
127
|
+
f"No existing cert found for cluster='{ns.cluster.name}', namespace='{ns.name}', secret='{cert_resource.secret_name}''"
|
128
|
+
)
|
129
|
+
return vault_cert_secret
|
130
|
+
|
131
|
+
|
132
|
+
def generate_vault_cert_secret(
|
133
|
+
dry_run: bool,
|
134
|
+
ns: NamespaceV1,
|
135
|
+
cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
|
136
|
+
vault: _VaultClient,
|
137
|
+
vault_base_path: str,
|
138
|
+
issuer_url: str,
|
139
|
+
ca_cert_url: str,
|
140
|
+
) -> dict:
|
141
|
+
logging.info(
|
142
|
+
f"Creating cert with service account credentials for '{cert_resource.service_account_name}'. cluster='{ns.cluster.name}', namespace='{ns.name}', secret='{cert_resource.secret_name}'"
|
143
|
+
)
|
144
|
+
sa_password = vault.read(cert_resource.service_account_password.dict())
|
145
|
+
if dry_run:
|
146
|
+
rhcs_cert = RhcsV2Cert(
|
147
|
+
certificate="PLACEHOLDER_CERT",
|
148
|
+
private_key="PLACEHOLDER_PRIVATE_KEY",
|
149
|
+
ca_cert="PLACEHOLDER_CA_CERT",
|
150
|
+
expiration_timestamp=int(time.time()),
|
151
|
+
)
|
152
|
+
else:
|
153
|
+
try:
|
154
|
+
rhcs_cert = generate_cert(
|
155
|
+
issuer_url, cert_resource.service_account_name, sa_password, ca_cert_url
|
156
|
+
)
|
157
|
+
except ValueError as e:
|
158
|
+
raise Exception(
|
159
|
+
f"Certificate generation failed using service account '{cert_resource.service_account_name}': {e}"
|
160
|
+
) from None
|
161
|
+
logging.info(
|
162
|
+
f"Writing cert details to Vault at {vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}"
|
163
|
+
)
|
164
|
+
vault.write(
|
165
|
+
secret={
|
166
|
+
"data": rhcs_cert.dict(by_alias=True),
|
167
|
+
"path": f"{vault_base_path}/{ns.cluster.name}/{ns.name}/{cert_resource.secret_name}",
|
168
|
+
},
|
169
|
+
decode_base64=False,
|
170
|
+
)
|
171
|
+
return rhcs_cert.dict(by_alias=True)
|
172
|
+
|
173
|
+
|
174
|
+
def fetch_openshift_resource_for_cert_resource(
|
175
|
+
dry_run: bool,
|
176
|
+
ns: NamespaceV1,
|
177
|
+
cert_resource: NamespaceOpenshiftResourceRhcsCertV1,
|
178
|
+
vault: _VaultClient,
|
179
|
+
rhcs_settings: RhcsProviderSettingsV1,
|
180
|
+
) -> OR:
|
181
|
+
vault_base_path = f"{rhcs_settings.vault_base_path}/{QONTRACT_INTEGRATION}"
|
182
|
+
vault_cert_secret = get_vault_cert_secret(ns, cert_resource, vault, vault_base_path)
|
183
|
+
if vault_cert_secret is None or cert_expires_within_threshold(
|
184
|
+
ns, cert_resource, vault_cert_secret
|
185
|
+
):
|
186
|
+
vault_cert_secret = generate_vault_cert_secret(
|
187
|
+
dry_run,
|
188
|
+
ns,
|
189
|
+
cert_resource,
|
190
|
+
vault,
|
191
|
+
vault_base_path,
|
192
|
+
rhcs_settings.issuer_url,
|
193
|
+
rhcs_settings.ca_cert_url,
|
194
|
+
)
|
195
|
+
|
196
|
+
if not dry_run:
|
197
|
+
metrics.set_gauge(
|
198
|
+
OpenshiftRhcsCertExpiration(
|
199
|
+
cert_name=cert_resource.secret_name,
|
200
|
+
namespace=ns.name,
|
201
|
+
cluster=ns.cluster.name,
|
202
|
+
),
|
203
|
+
int(vault_cert_secret["expiration_timestamp"]),
|
204
|
+
)
|
205
|
+
|
206
|
+
return construct_rhcs_cert_oc_secret(
|
207
|
+
secret_name=cert_resource.secret_name,
|
208
|
+
cert=vault_cert_secret,
|
209
|
+
annotations=cert_resource.annotations or {},
|
210
|
+
)
|
211
|
+
|
212
|
+
|
213
|
+
def fetch_desired_state(
|
214
|
+
dry_run: bool,
|
215
|
+
namespaces: list[NamespaceV1],
|
216
|
+
ri: ResourceInventory,
|
217
|
+
query_func: Callable,
|
218
|
+
) -> None:
|
219
|
+
vault = _VaultClient()
|
220
|
+
cert_provider = get_rhcs_provider_settings(query_func=query_func)
|
221
|
+
|
222
|
+
for ns in namespaces:
|
223
|
+
for cert_resource in ns.openshift_resources or []:
|
224
|
+
if isinstance(cert_resource, NamespaceOpenshiftResourceRhcsCertV1):
|
225
|
+
ri.add_desired_resource(
|
226
|
+
cluster=ns.cluster.name,
|
227
|
+
namespace=ns.name,
|
228
|
+
resource=fetch_openshift_resource_for_cert_resource(
|
229
|
+
dry_run,
|
230
|
+
ns,
|
231
|
+
cert_resource,
|
232
|
+
vault,
|
233
|
+
cert_provider,
|
234
|
+
),
|
235
|
+
)
|
236
|
+
|
237
|
+
|
238
|
+
def run(
|
239
|
+
dry_run: bool,
|
240
|
+
thread_pool_size: int = 10,
|
241
|
+
internal: bool | None = None,
|
242
|
+
use_jump_host: bool = True,
|
243
|
+
cluster_name: Iterable[str] | None = None,
|
244
|
+
) -> None:
|
245
|
+
gql_api = gql.get_api()
|
246
|
+
vault_settings = get_app_interface_vault_settings()
|
247
|
+
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
248
|
+
namespaces = get_namespaces_with_rhcs_certs(gql_api.query, cluster_name)
|
249
|
+
if not namespaces:
|
250
|
+
logging.debug(
|
251
|
+
f"No rhcs-cert definitions found in app-interface for {cluster_name}"
|
252
|
+
)
|
253
|
+
sys.exit(ExitCodes.SUCCESS)
|
254
|
+
oc_map = init_oc_map_from_namespaces(
|
255
|
+
namespaces=namespaces,
|
256
|
+
integration=QONTRACT_INTEGRATION,
|
257
|
+
secret_reader=secret_reader,
|
258
|
+
internal=internal,
|
259
|
+
use_jump_host=use_jump_host,
|
260
|
+
thread_pool_size=thread_pool_size,
|
261
|
+
)
|
262
|
+
ri = ResourceInventory()
|
263
|
+
state_specs = ob.init_specs_to_fetch(
|
264
|
+
ri,
|
265
|
+
oc_map,
|
266
|
+
namespaces=[ns.dict(by_alias=True) for ns in namespaces],
|
267
|
+
override_managed_types=["Secret"],
|
268
|
+
)
|
269
|
+
for spec in state_specs:
|
270
|
+
if isinstance(spec, ob.CurrentStateSpec):
|
271
|
+
orb.fetch_current_state(
|
272
|
+
spec.oc,
|
273
|
+
ri,
|
274
|
+
spec.cluster,
|
275
|
+
spec.namespace,
|
276
|
+
spec.kind,
|
277
|
+
spec.resource_names,
|
278
|
+
)
|
279
|
+
fetch_desired_state(dry_run, namespaces, ri, gql_api.query)
|
280
|
+
ob.realize_data(dry_run, oc_map, ri, thread_pool_size)
|
281
|
+
ob.publish_metrics(ri, QONTRACT_INTEGRATION)
|
282
|
+
if ri.has_error_registered():
|
283
|
+
sys.exit(1)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
from collections.abc import Callable
|
2
|
+
|
3
|
+
from reconcile.gql_definitions.common.rhcs_provider_settings import (
|
4
|
+
RhcsProviderSettingsV1,
|
5
|
+
query,
|
6
|
+
)
|
7
|
+
from reconcile.utils import gql
|
8
|
+
from reconcile.utils.exceptions import AppInterfaceSettingsError
|
9
|
+
|
10
|
+
|
11
|
+
def get_rhcs_provider_settings(
|
12
|
+
query_func: Callable | None = None,
|
13
|
+
) -> RhcsProviderSettingsV1:
|
14
|
+
"""Returns App Interface Settings and raises err if none are found"""
|
15
|
+
if not query_func:
|
16
|
+
query_func = gql.get_api().query
|
17
|
+
data = query(query_func)
|
18
|
+
if data.settings and len(data.settings) == 1:
|
19
|
+
if data.settings[0].rhcs_provider:
|
20
|
+
return data.settings[0].rhcs_provider
|
21
|
+
raise AppInterfaceSettingsError("RHCS provider settings not uniquely defined.")
|
@@ -0,0 +1,76 @@
|
|
1
|
+
import re
|
2
|
+
from datetime import UTC, datetime
|
3
|
+
|
4
|
+
import requests
|
5
|
+
from cryptography import x509
|
6
|
+
from cryptography.hazmat.primitives import hashes, serialization
|
7
|
+
from cryptography.hazmat.primitives.asymmetric import rsa
|
8
|
+
from cryptography.x509.oid import NameOID
|
9
|
+
from pydantic import BaseModel, Field
|
10
|
+
|
11
|
+
|
12
|
+
class RhcsV2Cert(BaseModel):
|
13
|
+
certificate: str = Field(alias="tls.crt")
|
14
|
+
private_key: str = Field(alias="tls.key")
|
15
|
+
ca_cert: str = Field(alias="ca.crt")
|
16
|
+
expiration_timestamp: int
|
17
|
+
|
18
|
+
class Config:
|
19
|
+
allow_population_by_field_name = True
|
20
|
+
|
21
|
+
|
22
|
+
def generate_cert(issuer_url: str, uid: str, pwd: str, ca_url: str) -> RhcsV2Cert:
|
23
|
+
private_key = rsa.generate_private_key(65537, 4096)
|
24
|
+
csr = (
|
25
|
+
x509.CertificateSigningRequestBuilder()
|
26
|
+
.subject_name(
|
27
|
+
x509.Name([
|
28
|
+
x509.NameAttribute(NameOID.USER_ID, uid),
|
29
|
+
])
|
30
|
+
)
|
31
|
+
.sign(private_key, hashes.SHA256())
|
32
|
+
)
|
33
|
+
data = {
|
34
|
+
"uid": uid,
|
35
|
+
"pwd": pwd,
|
36
|
+
"cert_request_type": "pkcs10",
|
37
|
+
"cert_request": csr.public_bytes(serialization.Encoding.PEM).decode(),
|
38
|
+
"profileId": "caDirAppUserCert",
|
39
|
+
"renewal": "false",
|
40
|
+
"xmlOutput": "false",
|
41
|
+
}
|
42
|
+
response = requests.post(issuer_url, data=data)
|
43
|
+
response.raise_for_status()
|
44
|
+
|
45
|
+
cert_pem = re.search(
|
46
|
+
r'outputList\.outputVal="(-----BEGIN CERTIFICATE-----.*?-----END CERTIFICATE-----(?:\\n|\r?\n)?)";',
|
47
|
+
response.text,
|
48
|
+
re.DOTALL,
|
49
|
+
)
|
50
|
+
if not cert_pem:
|
51
|
+
raise ValueError("Could not extract certificate PEM from response")
|
52
|
+
cert_expiry = re.search(r"Not\s+After:\s+(.*?UTC)(?:\\n|\r?\n)?", response.text)
|
53
|
+
if not cert_expiry:
|
54
|
+
raise ValueError("Could not extract expiration date from response")
|
55
|
+
# Weekday, Month Day, Year HH:MM:SS PM/AM Timezone
|
56
|
+
dt_expiry = datetime.strptime(cert_expiry.group(1), "%A, %B %d, %Y %I:%M:%S %p %Z")
|
57
|
+
dt_expiry = dt_expiry.replace(tzinfo=UTC)
|
58
|
+
private_key_pem = private_key.private_bytes(
|
59
|
+
encoding=serialization.Encoding.PEM,
|
60
|
+
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
61
|
+
encryption_algorithm=serialization.NoEncryption(),
|
62
|
+
).decode()
|
63
|
+
|
64
|
+
response = requests.get(ca_url)
|
65
|
+
response.raise_for_status()
|
66
|
+
ca_pem = response.text
|
67
|
+
|
68
|
+
return RhcsV2Cert(
|
69
|
+
private_key=private_key_pem,
|
70
|
+
certificate=cert_pem.group(1)
|
71
|
+
.encode()
|
72
|
+
.decode("unicode_escape")
|
73
|
+
.replace("\\/", "/"),
|
74
|
+
ca_cert=ca_pem,
|
75
|
+
expiration_timestamp=int(dt_expiry.timestamp()),
|
76
|
+
)
|
{qontract_reconcile-0.10.2.dev203.dist-info → qontract_reconcile-0.10.2.dev205.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|