qontract-reconcile 0.10.2.dev221__py3-none-any.whl → 0.10.2.dev222__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.dev221.dist-info → qontract_reconcile-0.10.2.dev222.dist-info}/METADATA +1 -3
- {qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.dist-info}/RECORD +5 -17
- reconcile/cli.py +0 -21
- reconcile/cna/__init__.py +0 -0
- reconcile/cna/assets/__init__.py +0 -0
- reconcile/cna/assets/asset.py +0 -43
- reconcile/cna/assets/asset_factory.py +0 -25
- reconcile/cna/assets/null.py +0 -61
- reconcile/cna/client.py +0 -52
- reconcile/cna/integration.py +0 -137
- reconcile/cna/state.py +0 -128
- reconcile/gql_definitions/cna/__init__.py +0 -0
- reconcile/gql_definitions/cna/queries/__init__.py +0 -0
- reconcile/gql_definitions/cna/queries/cna_provisioners.py +0 -106
- reconcile/gql_definitions/cna/queries/cna_resources.py +0 -98
- {qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.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.dev222
|
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
|
@@ -131,8 +131,6 @@ OpenShift templates can be found [here](/openshift/qontract-reconcile.yaml). In
|
|
131
131
|
RHIDP.
|
132
132
|
cluster-deployment-mapper Maps ClusterDeployment resources to Cluster
|
133
133
|
IDs.
|
134
|
-
cna-resources Manage Cloud Resources using Cloud Native
|
135
|
-
Assets (CNA).
|
136
134
|
dashdotdb-cso Collects the ImageManifestVuln CRs from all
|
137
135
|
the clusters and posts them to Dashdotdb.
|
138
136
|
dashdotdb-dora Collects dora metrics.
|
{qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.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=vrUcahFtsq_yGAzaa1QDyZHbMqMsnsNHzrvW4BiV_xA,114159
|
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
|
@@ -178,14 +178,6 @@ reconcile/change_owners/self_service_roles.py,sha256=xSe5AKZxXAIo0vWOMM5hImQ_rd-
|
|
178
178
|
reconcile/change_owners/tester.py,sha256=ijDaSbFYT8fPinhjPrRlw_TCarTTJK5nUgzHSi1oVYE,8875
|
179
179
|
reconcile/cluster_auth_rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
180
180
|
reconcile/cluster_auth_rhidp/integration.py,sha256=KIAiP_XFjsOA2OE8oFJa8lD0T1a7EwOmhct2xbj7tr8,9560
|
181
|
-
reconcile/cna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
182
|
-
reconcile/cna/client.py,sha256=32-G3fDZfVvkCN1vg0EbcsMNYBdJvXoXYI2ShFg6lbc,1586
|
183
|
-
reconcile/cna/integration.py,sha256=dybEWkSCl7V5OC7ZQYIN2j4QV2yWstIUEQy1GpYI2oI,5106
|
184
|
-
reconcile/cna/state.py,sha256=mJghfMGZZtyh8e6GwNNajI2AxEEJQ4e51tAgQ26zEuQ,4501
|
185
|
-
reconcile/cna/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
186
|
-
reconcile/cna/assets/asset.py,sha256=KWgA4fuDAEGsJwmR52WwK_YgSJMW-1cV2la3lmNf4iE,834
|
187
|
-
reconcile/cna/assets/asset_factory.py,sha256=7T7X_J6xIsoGETqBRI45_EyIKEdQcnRPt_GAuVuLQcc,785
|
188
|
-
reconcile/cna/assets/null.py,sha256=85mVh97atCoC0aLuX47poTZiyOthmziJeBsUw0c924w,1658
|
189
181
|
reconcile/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
190
182
|
reconcile/dynatrace_token_provider/dependencies.py,sha256=lvkdwqHMsn_2kgj-tUIJdTUnUNxVoS6z8k4nPkGglnQ,3129
|
191
183
|
reconcile/dynatrace_token_provider/integration.py,sha256=RTGy4A6U4EgE1G4rMdS8gqgw2XIfDcdYd-eF5DL9bo0,27166
|
@@ -262,10 +254,6 @@ reconcile/gql_definitions/change_owners/queries/change_types.py,sha256=SjpKbLWmL
|
|
262
254
|
reconcile/gql_definitions/change_owners/queries/self_service_roles.py,sha256=BcTQvnefPiShG90ajU_l2V6HUYSEXgdAzgiwY89vQew,4790
|
263
255
|
reconcile/gql_definitions/cluster_auth_rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
264
256
|
reconcile/gql_definitions/cluster_auth_rhidp/clusters.py,sha256=Pp9P3Q30Be3szcVqOEOtPfYUNiGTq1xc5Juz-ApMMw0,3283
|
265
|
-
reconcile/gql_definitions/cna/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
266
|
-
reconcile/gql_definitions/cna/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
267
|
-
reconcile/gql_definitions/cna/queries/cna_provisioners.py,sha256=4k527BszdWB84Wkmd8ZE45p1Djr-fig-xZaVF5XRwPI,3009
|
268
|
-
reconcile/gql_definitions/cna/queries/cna_resources.py,sha256=j9sa5iOOBsLT2qwlmEItnafSXK-vdNPXgd9Z4mg9mlc,2710
|
269
257
|
reconcile/gql_definitions/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
270
258
|
reconcile/gql_definitions/common/alerting_services_settings.py,sha256=mT7cobC9mR_bhFSYeQX1apVA5zMqSbu5fYcdd4iZ9mg,1802
|
271
259
|
reconcile/gql_definitions/common/app_code_component_repos.py,sha256=SCpUVfnn1Vl9nN0UYs5zqJfW1-wIOE-W8_1JjhC-hK8,2034
|
@@ -815,7 +803,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
815
803
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
816
804
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
817
805
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
818
|
-
qontract_reconcile-0.10.2.
|
819
|
-
qontract_reconcile-0.10.2.
|
820
|
-
qontract_reconcile-0.10.2.
|
821
|
-
qontract_reconcile-0.10.2.
|
806
|
+
qontract_reconcile-0.10.2.dev222.dist-info/METADATA,sha256=SFFSQXDuQJ82vGauppa3WrCsWzVdw2HsDJOCqUX14Og,24431
|
807
|
+
qontract_reconcile-0.10.2.dev222.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
808
|
+
qontract_reconcile-0.10.2.dev222.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
809
|
+
qontract_reconcile-0.10.2.dev222.dist-info/RECORD,,
|
reconcile/cli.py
CHANGED
@@ -2414,27 +2414,6 @@ def terraform_cloudflare_users(
|
|
2414
2414
|
)
|
2415
2415
|
|
2416
2416
|
|
2417
|
-
@integration.command(
|
2418
|
-
short_help="Manage Cloud Resources using Cloud Native Assets (CNA)."
|
2419
|
-
)
|
2420
|
-
@enable_deletion(default=False)
|
2421
|
-
@threaded()
|
2422
|
-
@click.pass_context
|
2423
|
-
def cna_resources(
|
2424
|
-
ctx: click.Context,
|
2425
|
-
enable_deletion: bool,
|
2426
|
-
thread_pool_size: int,
|
2427
|
-
) -> None:
|
2428
|
-
import reconcile.cna.integration
|
2429
|
-
|
2430
|
-
run_integration(
|
2431
|
-
reconcile.cna.integration,
|
2432
|
-
ctx,
|
2433
|
-
enable_deletion,
|
2434
|
-
thread_pool_size,
|
2435
|
-
)
|
2436
|
-
|
2437
|
-
|
2438
2417
|
@integration.command(short_help="Manage auto-promotions defined in SaaS files")
|
2439
2418
|
@threaded()
|
2440
2419
|
@click.option("--env-name", default=None, help="environment to filter saas files by")
|
reconcile/cna/__init__.py
DELETED
File without changes
|
reconcile/cna/assets/__init__.py
DELETED
File without changes
|
reconcile/cna/assets/asset.py
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from abc import (
|
4
|
-
ABC,
|
5
|
-
abstractmethod,
|
6
|
-
)
|
7
|
-
from dataclasses import (
|
8
|
-
dataclass,
|
9
|
-
field,
|
10
|
-
)
|
11
|
-
from enum import Enum
|
12
|
-
from typing import Any
|
13
|
-
|
14
|
-
|
15
|
-
class AssetError(Exception):
|
16
|
-
pass
|
17
|
-
|
18
|
-
|
19
|
-
class AssetType(Enum):
|
20
|
-
NULL = "null"
|
21
|
-
|
22
|
-
|
23
|
-
class AssetStatus(Enum):
|
24
|
-
TERMINATED = "Terminated"
|
25
|
-
PENDING = "Pending"
|
26
|
-
RUNNING = "Running"
|
27
|
-
|
28
|
-
|
29
|
-
@dataclass(frozen=True)
|
30
|
-
class Asset(ABC):
|
31
|
-
uuid: str | None = field(compare=False, hash=True)
|
32
|
-
href: str | None = field(compare=False, hash=True)
|
33
|
-
status: AssetStatus | None = field(compare=False, hash=True)
|
34
|
-
name: str
|
35
|
-
kind: AssetType
|
36
|
-
|
37
|
-
@abstractmethod
|
38
|
-
def api_payload(self) -> dict[str, Any]:
|
39
|
-
raise NotImplementedError()
|
40
|
-
|
41
|
-
@abstractmethod
|
42
|
-
def update_from(self, asset: Asset) -> Asset:
|
43
|
-
raise NotImplementedError()
|
@@ -1,25 +0,0 @@
|
|
1
|
-
from collections.abc import Mapping
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from reconcile.cna.assets.asset import (
|
5
|
-
Asset,
|
6
|
-
AssetError,
|
7
|
-
)
|
8
|
-
from reconcile.cna.assets.null import NullAsset
|
9
|
-
from reconcile.gql_definitions.cna.queries.cna_resources import (
|
10
|
-
CNANullAssetV1,
|
11
|
-
CNAssetV1,
|
12
|
-
)
|
13
|
-
|
14
|
-
|
15
|
-
def asset_factory_from_schema(schema_asset: CNAssetV1) -> Asset:
|
16
|
-
if isinstance(schema_asset, CNANullAssetV1):
|
17
|
-
return NullAsset.from_query_class(schema_asset)
|
18
|
-
raise AssetError(f"Unknown schema asset type {schema_asset}")
|
19
|
-
|
20
|
-
|
21
|
-
def asset_factory_from_raw_data(data_asset: Mapping[str, Any]) -> Asset:
|
22
|
-
asset_type = data_asset.get("asset_type")
|
23
|
-
if asset_type == "null":
|
24
|
-
return NullAsset.from_api_mapping(data_asset)
|
25
|
-
raise AssetError(f"Unknown data asset type {data_asset}")
|
reconcile/cna/assets/null.py
DELETED
@@ -1,61 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from collections.abc import Mapping
|
4
|
-
from dataclasses import dataclass
|
5
|
-
from typing import Any
|
6
|
-
|
7
|
-
from reconcile.cna.assets.asset import (
|
8
|
-
Asset,
|
9
|
-
AssetError,
|
10
|
-
AssetStatus,
|
11
|
-
AssetType,
|
12
|
-
)
|
13
|
-
from reconcile.gql_definitions.cna.queries.cna_resources import CNANullAssetV1
|
14
|
-
|
15
|
-
|
16
|
-
@dataclass(frozen=True)
|
17
|
-
class NullAsset(Asset):
|
18
|
-
addr_block: str | None
|
19
|
-
|
20
|
-
def api_payload(self) -> dict[str, Any]:
|
21
|
-
return {
|
22
|
-
"asset_type": "null",
|
23
|
-
"name": self.name,
|
24
|
-
"parameters": {
|
25
|
-
"addr_block": self.addr_block,
|
26
|
-
},
|
27
|
-
}
|
28
|
-
|
29
|
-
def update_from(self, asset: Asset) -> Asset:
|
30
|
-
if not isinstance(asset, NullAsset):
|
31
|
-
raise AssetError(f"Cannot create NullAsset from {asset}")
|
32
|
-
return NullAsset(
|
33
|
-
uuid=self.uuid,
|
34
|
-
href=self.href,
|
35
|
-
status=self.status,
|
36
|
-
name=self.name,
|
37
|
-
kind=self.kind,
|
38
|
-
addr_block=asset.addr_block,
|
39
|
-
)
|
40
|
-
|
41
|
-
@staticmethod
|
42
|
-
def from_query_class(asset: CNANullAssetV1) -> NullAsset:
|
43
|
-
return NullAsset(
|
44
|
-
uuid=None,
|
45
|
-
href=None,
|
46
|
-
status=None,
|
47
|
-
kind=AssetType.NULL,
|
48
|
-
name=asset.name,
|
49
|
-
addr_block=asset.addr_block,
|
50
|
-
)
|
51
|
-
|
52
|
-
@staticmethod
|
53
|
-
def from_api_mapping(asset: Mapping[str, Any]) -> NullAsset:
|
54
|
-
return NullAsset(
|
55
|
-
uuid=asset.get("id"),
|
56
|
-
href=asset.get("href"),
|
57
|
-
status=AssetStatus(asset.get("status")),
|
58
|
-
kind=AssetType.NULL,
|
59
|
-
name=asset.get("name", ""),
|
60
|
-
addr_block=asset.get("addr_block"),
|
61
|
-
)
|
reconcile/cna/client.py
DELETED
@@ -1,52 +0,0 @@
|
|
1
|
-
import logging
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from reconcile.cna.assets.asset import Asset
|
5
|
-
from reconcile.utils.ocm_base_client import OCMBaseClient
|
6
|
-
|
7
|
-
|
8
|
-
class CNAClient:
|
9
|
-
"""
|
10
|
-
Client used to interact with CNA. CNA API doc can be found here:
|
11
|
-
https://gitlab.cee.redhat.com/service/cna-management/-/blob/main/openapi/openapi.yaml#/
|
12
|
-
"""
|
13
|
-
|
14
|
-
def __init__(self, ocm_client: OCMBaseClient):
|
15
|
-
self._ocm_client = ocm_client
|
16
|
-
|
17
|
-
def list_assets(self) -> list[dict[str, Any]]:
|
18
|
-
"""
|
19
|
-
We use this to fetch the current real-world state
|
20
|
-
of our assets
|
21
|
-
"""
|
22
|
-
# TODO: properly handle paging
|
23
|
-
cnas = self._ocm_client.get(api_path="/api/cna-management/v1/cnas")
|
24
|
-
return cnas.get("items", [])
|
25
|
-
|
26
|
-
def create(self, asset: Asset, dry_run: bool = False) -> None:
|
27
|
-
if dry_run:
|
28
|
-
logging.info("CREATE %s", asset)
|
29
|
-
return
|
30
|
-
self._ocm_client.post(
|
31
|
-
api_path="/api/cna-management/v1/cnas",
|
32
|
-
data=asset.api_payload(),
|
33
|
-
)
|
34
|
-
|
35
|
-
def delete(self, asset: Asset, dry_run: bool = False) -> None:
|
36
|
-
if dry_run:
|
37
|
-
logging.info("DELETE %s", asset)
|
38
|
-
return
|
39
|
-
if asset.href:
|
40
|
-
self._ocm_client.delete(
|
41
|
-
api_path=asset.href,
|
42
|
-
)
|
43
|
-
|
44
|
-
def update(self, asset: Asset, dry_run: bool = False) -> None:
|
45
|
-
if dry_run:
|
46
|
-
logging.info("UPDATE %s", asset)
|
47
|
-
return
|
48
|
-
if asset.href:
|
49
|
-
self._ocm_client.patch(
|
50
|
-
api_path=asset.href,
|
51
|
-
data=asset.api_payload(),
|
52
|
-
)
|
reconcile/cna/integration.py
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
from collections import defaultdict
|
2
|
-
from collections.abc import (
|
3
|
-
Iterable,
|
4
|
-
Mapping,
|
5
|
-
)
|
6
|
-
|
7
|
-
from reconcile.cna.assets.asset_factory import asset_factory_from_schema
|
8
|
-
from reconcile.cna.client import CNAClient
|
9
|
-
from reconcile.cna.state import State
|
10
|
-
from reconcile.gql_definitions.cna.queries.cna_provisioners import (
|
11
|
-
CNAExperimentalProvisionerV1,
|
12
|
-
)
|
13
|
-
from reconcile.gql_definitions.cna.queries.cna_provisioners import (
|
14
|
-
query as cna_provisioners_query,
|
15
|
-
)
|
16
|
-
from reconcile.gql_definitions.cna.queries.cna_resources import (
|
17
|
-
NamespaceCNAssetV1,
|
18
|
-
NamespaceV1,
|
19
|
-
)
|
20
|
-
from reconcile.gql_definitions.cna.queries.cna_resources import (
|
21
|
-
query as namespaces_query,
|
22
|
-
)
|
23
|
-
from reconcile.typed_queries.app_interface_vault_settings import (
|
24
|
-
get_app_interface_vault_settings,
|
25
|
-
)
|
26
|
-
from reconcile.utils import gql
|
27
|
-
from reconcile.utils.ocm_base_client import OCMBaseClient
|
28
|
-
from reconcile.utils.secret_reader import (
|
29
|
-
SecretReaderBase,
|
30
|
-
create_secret_reader,
|
31
|
-
)
|
32
|
-
from reconcile.utils.semver_helper import make_semver
|
33
|
-
|
34
|
-
QONTRACT_INTEGRATION = "cna_resources"
|
35
|
-
QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
|
36
|
-
|
37
|
-
|
38
|
-
class CNAConfigException(Exception):
|
39
|
-
pass
|
40
|
-
|
41
|
-
|
42
|
-
class CNAIntegration:
|
43
|
-
def __init__(
|
44
|
-
self,
|
45
|
-
cna_clients: Mapping[str, CNAClient],
|
46
|
-
namespaces: Iterable[NamespaceV1],
|
47
|
-
desired_states: Mapping[str, State] | None = None,
|
48
|
-
current_states: Mapping[str, State] | None = None,
|
49
|
-
) -> None:
|
50
|
-
self._cna_clients = cna_clients
|
51
|
-
self._namespaces = namespaces
|
52
|
-
self._desired_states = desired_states or defaultdict(State)
|
53
|
-
self._current_states = current_states or defaultdict(State)
|
54
|
-
|
55
|
-
def assemble_desired_states(self) -> None:
|
56
|
-
self._desired_states = defaultdict(State)
|
57
|
-
for namespace in self._namespaces:
|
58
|
-
for provider in namespace.external_resources or []:
|
59
|
-
# TODO: this should probably be filtered within the query already
|
60
|
-
if not isinstance(provider, NamespaceCNAssetV1):
|
61
|
-
continue
|
62
|
-
for resource in provider.resources or []:
|
63
|
-
self._desired_states[provider.provisioner.name].add_asset(
|
64
|
-
asset_factory_from_schema(resource)
|
65
|
-
)
|
66
|
-
|
67
|
-
def assemble_current_states(self) -> None:
|
68
|
-
self._current_states = defaultdict(State)
|
69
|
-
for name, client in self._cna_clients.items():
|
70
|
-
cnas = client.list_assets()
|
71
|
-
state = State()
|
72
|
-
state.add_raw_data(cnas)
|
73
|
-
self._current_states[name] = state
|
74
|
-
|
75
|
-
def provision(self, dry_run: bool = False) -> None:
|
76
|
-
for provisioner_name, cna_client in self._cna_clients.items():
|
77
|
-
desired_state = self._desired_states[provisioner_name]
|
78
|
-
current_state = self._current_states[provisioner_name]
|
79
|
-
|
80
|
-
additions = desired_state - current_state
|
81
|
-
for asset in additions:
|
82
|
-
cna_client.create(asset=asset, dry_run=dry_run)
|
83
|
-
|
84
|
-
deletions = current_state - desired_state
|
85
|
-
for asset in deletions:
|
86
|
-
cna_client.delete(asset=asset, dry_run=dry_run)
|
87
|
-
|
88
|
-
updates = current_state.required_updates_to_reach(desired_state)
|
89
|
-
for assets in updates:
|
90
|
-
cna_client.update(asset=assets, dry_run=dry_run)
|
91
|
-
|
92
|
-
|
93
|
-
def build_cna_clients(
|
94
|
-
secret_reader: SecretReaderBase,
|
95
|
-
cna_provisioners: list[CNAExperimentalProvisionerV1],
|
96
|
-
) -> dict[str, CNAClient]:
|
97
|
-
clients: dict[str, CNAClient] = {}
|
98
|
-
for provisioner in cna_provisioners:
|
99
|
-
access_token_client_secret = (
|
100
|
-
provisioner.ocm.access_token_client_secret
|
101
|
-
or provisioner.ocm.environment.access_token_client_secret
|
102
|
-
)
|
103
|
-
secret_data = secret_reader.read_all_secret(access_token_client_secret)
|
104
|
-
ocm_client = OCMBaseClient(
|
105
|
-
url=provisioner.ocm.environment.url,
|
106
|
-
access_token_client_secret=secret_data["client_secret"],
|
107
|
-
access_token_url=provisioner.ocm.access_token_url
|
108
|
-
or provisioner.ocm.environment.access_token_url,
|
109
|
-
access_token_client_id=provisioner.ocm.access_token_client_id
|
110
|
-
or provisioner.ocm.environment.access_token_client_id,
|
111
|
-
)
|
112
|
-
clients[provisioner.name] = CNAClient(
|
113
|
-
ocm_client=ocm_client,
|
114
|
-
)
|
115
|
-
return clients
|
116
|
-
|
117
|
-
|
118
|
-
def run(
|
119
|
-
dry_run: bool,
|
120
|
-
# TODO: Threadpool not used yet - will be used once we understand scopes in more detail
|
121
|
-
thread_pool_size: int,
|
122
|
-
) -> None:
|
123
|
-
settings = get_app_interface_vault_settings()
|
124
|
-
secret_reader = create_secret_reader(use_vault=settings.vault)
|
125
|
-
|
126
|
-
query_func = gql.get_api().query
|
127
|
-
cna_provisioners = cna_provisioners_query(query_func).cna_provisioners or []
|
128
|
-
namespaces = namespaces_query(query_func).namespaces or []
|
129
|
-
|
130
|
-
cna_clients = build_cna_clients(
|
131
|
-
secret_reader=secret_reader, cna_provisioners=cna_provisioners
|
132
|
-
)
|
133
|
-
|
134
|
-
integration = CNAIntegration(cna_clients=cna_clients, namespaces=namespaces)
|
135
|
-
integration.assemble_current_states()
|
136
|
-
integration.assemble_desired_states()
|
137
|
-
integration.provision(dry_run=dry_run)
|
reconcile/cna/state.py
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
from collections.abc import (
|
4
|
-
Iterable,
|
5
|
-
Mapping,
|
6
|
-
)
|
7
|
-
from typing import Any
|
8
|
-
|
9
|
-
from reconcile.cna.assets.asset import (
|
10
|
-
Asset,
|
11
|
-
AssetStatus,
|
12
|
-
AssetType,
|
13
|
-
)
|
14
|
-
from reconcile.cna.assets.asset_factory import asset_factory_from_raw_data
|
15
|
-
|
16
|
-
|
17
|
-
class CNAStateError(Exception):
|
18
|
-
pass
|
19
|
-
|
20
|
-
|
21
|
-
class State:
|
22
|
-
"""
|
23
|
-
State object is a collection of assets.
|
24
|
-
It can be used to describe actual or desired state.
|
25
|
-
Main objective is to calculate required additions,
|
26
|
-
deletions and updates to reach another state.
|
27
|
-
"""
|
28
|
-
|
29
|
-
def __init__(self, assets: dict[AssetType, dict[str, Asset]] | None = None):
|
30
|
-
self._assets: dict[AssetType, dict[str, Asset]] = {}
|
31
|
-
for kind in AssetType:
|
32
|
-
self._assets[kind] = {}
|
33
|
-
if assets:
|
34
|
-
self._assets = assets
|
35
|
-
|
36
|
-
def __eq__(self, other: object) -> bool:
|
37
|
-
if not isinstance(other, State):
|
38
|
-
return False
|
39
|
-
if not set(self._assets.keys()) == set(other._assets.keys()):
|
40
|
-
return False
|
41
|
-
for kind in list(self._assets.keys()):
|
42
|
-
if not set(self._assets[kind]) == set(other._assets[kind]):
|
43
|
-
return False
|
44
|
-
for name, asset in self._assets[kind].items():
|
45
|
-
if asset != other._assets[kind][name]:
|
46
|
-
return False
|
47
|
-
return True
|
48
|
-
|
49
|
-
def __repr__(self) -> str:
|
50
|
-
# pytest should show nice diff
|
51
|
-
return str(self._assets)
|
52
|
-
|
53
|
-
def _validate_addition(self, asset: Asset) -> None:
|
54
|
-
if asset.kind not in self._assets:
|
55
|
-
raise CNAStateError(f"State doesn't know asset_kind {asset.kind}")
|
56
|
-
if asset.name in self._assets[asset.kind]:
|
57
|
-
raise CNAStateError(
|
58
|
-
f"Duplicate asset name found in state: kind={asset.kind}, name={asset.name}"
|
59
|
-
)
|
60
|
-
|
61
|
-
def add_asset(self, asset: Asset) -> None:
|
62
|
-
self._validate_addition(asset=asset)
|
63
|
-
self._assets[asset.kind][asset.name] = asset
|
64
|
-
|
65
|
-
def add_raw_data(self, data: Iterable[Mapping[str, Any]]) -> None:
|
66
|
-
for cna in data:
|
67
|
-
asset = asset_factory_from_raw_data(cna)
|
68
|
-
self._validate_addition(asset=asset)
|
69
|
-
self._assets[asset.kind][asset.name] = asset
|
70
|
-
|
71
|
-
def required_updates_to_reach(self, other: State) -> State:
|
72
|
-
"""
|
73
|
-
This operation is NOT commutative, i.e.,:
|
74
|
-
a.required_updates_to_reach(b) != b.required_updates_to_reach(a)
|
75
|
-
|
76
|
-
This is supposed to be called on actual state (self).
|
77
|
-
I.e., actual.required_updates_to_reach(desired)
|
78
|
-
"""
|
79
|
-
ans = State()
|
80
|
-
for kind in AssetType:
|
81
|
-
for asset_name, other_asset in other._assets[kind].items():
|
82
|
-
if asset_name not in self._assets[kind]:
|
83
|
-
continue
|
84
|
-
asset = self._assets[kind][asset_name]
|
85
|
-
if asset.status in {AssetStatus.TERMINATED, AssetStatus.PENDING}:
|
86
|
-
continue
|
87
|
-
if asset == other_asset:
|
88
|
-
# There is no diff - no need to update
|
89
|
-
continue
|
90
|
-
ans.add_asset(asset=asset.update_from(other_asset))
|
91
|
-
return ans
|
92
|
-
|
93
|
-
def __sub__(self, other: State) -> State:
|
94
|
-
"""
|
95
|
-
This is used to determine creations and deletions
|
96
|
-
of assets. We only check the existance of the name
|
97
|
-
in each state. TERMINATED and PENDING assets are
|
98
|
-
omitted.
|
99
|
-
|
100
|
-
additions = self - other
|
101
|
-
deletions = other - self
|
102
|
-
"""
|
103
|
-
ans = State()
|
104
|
-
for kind in AssetType:
|
105
|
-
for asset_name, asset in self._assets[kind].items():
|
106
|
-
if asset.status in {AssetStatus.TERMINATED, AssetStatus.PENDING}:
|
107
|
-
continue
|
108
|
-
if other_asset := other._assets[kind].get(asset_name):
|
109
|
-
if other_asset.status == AssetStatus.TERMINATED:
|
110
|
-
raise CNAStateError(
|
111
|
-
f"Trying to create/update terminated asset {asset}. Currently not possible."
|
112
|
-
)
|
113
|
-
continue
|
114
|
-
ans.add_asset(asset)
|
115
|
-
return ans
|
116
|
-
|
117
|
-
def __iter__(self) -> State:
|
118
|
-
self._i = 0
|
119
|
-
self._assets_list: list[Asset] = []
|
120
|
-
for kind in AssetType:
|
121
|
-
self._assets_list += list(self._assets[kind].values())
|
122
|
-
return self
|
123
|
-
|
124
|
-
def __next__(self) -> Asset:
|
125
|
-
if self._i < len(self._assets_list):
|
126
|
-
self._i += 1
|
127
|
-
return self._assets_list[self._i - 1]
|
128
|
-
raise StopIteration
|
File without changes
|
File without changes
|
@@ -1,106 +0,0 @@
|
|
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.ocm_environment import OCMEnvironment
|
21
|
-
from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
|
22
|
-
|
23
|
-
|
24
|
-
DEFINITION = """
|
25
|
-
fragment OCMEnvironment on OpenShiftClusterManagerEnvironment_v1 {
|
26
|
-
name
|
27
|
-
description
|
28
|
-
labels
|
29
|
-
url
|
30
|
-
accessTokenClientId
|
31
|
-
accessTokenUrl
|
32
|
-
accessTokenClientSecret {
|
33
|
-
... VaultSecret
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
fragment VaultSecret on VaultSecret_v1 {
|
38
|
-
path
|
39
|
-
field
|
40
|
-
version
|
41
|
-
format
|
42
|
-
}
|
43
|
-
|
44
|
-
query CNAProvisioners {
|
45
|
-
cna_provisioners: cna_experimental_provisioners_v1 {
|
46
|
-
name
|
47
|
-
description
|
48
|
-
ocm {
|
49
|
-
name
|
50
|
-
orgId
|
51
|
-
accessTokenUrl
|
52
|
-
accessTokenClientId
|
53
|
-
accessTokenClientSecret {
|
54
|
-
... VaultSecret
|
55
|
-
}
|
56
|
-
environment {
|
57
|
-
... OCMEnvironment
|
58
|
-
}
|
59
|
-
}
|
60
|
-
}
|
61
|
-
}
|
62
|
-
"""
|
63
|
-
|
64
|
-
|
65
|
-
class ConfiguredBaseModel(BaseModel):
|
66
|
-
class Config:
|
67
|
-
smart_union=True
|
68
|
-
extra=Extra.forbid
|
69
|
-
|
70
|
-
|
71
|
-
class OpenShiftClusterManagerV1(ConfiguredBaseModel):
|
72
|
-
name: str = Field(..., alias="name")
|
73
|
-
org_id: str = Field(..., alias="orgId")
|
74
|
-
access_token_url: Optional[str] = Field(..., alias="accessTokenUrl")
|
75
|
-
access_token_client_id: Optional[str] = Field(..., alias="accessTokenClientId")
|
76
|
-
access_token_client_secret: Optional[VaultSecret] = Field(..., alias="accessTokenClientSecret")
|
77
|
-
environment: OCMEnvironment = Field(..., alias="environment")
|
78
|
-
|
79
|
-
|
80
|
-
class CNAExperimentalProvisionerV1(ConfiguredBaseModel):
|
81
|
-
name: str = Field(..., alias="name")
|
82
|
-
description: Optional[str] = Field(..., alias="description")
|
83
|
-
ocm: OpenShiftClusterManagerV1 = Field(..., alias="ocm")
|
84
|
-
|
85
|
-
|
86
|
-
class CNAProvisionersQueryData(ConfiguredBaseModel):
|
87
|
-
cna_provisioners: Optional[list[CNAExperimentalProvisionerV1]] = Field(..., alias="cna_provisioners")
|
88
|
-
|
89
|
-
|
90
|
-
def query(query_func: Callable, **kwargs: Any) -> CNAProvisionersQueryData:
|
91
|
-
"""
|
92
|
-
This is a convenience function which queries and parses the data into
|
93
|
-
concrete types. It should be compatible with most GQL clients.
|
94
|
-
You do not have to use it to consume the generated data classes.
|
95
|
-
Alternatively, you can also mime and alternate the behavior
|
96
|
-
of this function in the caller.
|
97
|
-
|
98
|
-
Parameters:
|
99
|
-
query_func (Callable): Function which queries your GQL Server
|
100
|
-
kwargs: optional arguments that will be passed to the query function
|
101
|
-
|
102
|
-
Returns:
|
103
|
-
CNAProvisionersQueryData: queried data parsed into generated classes
|
104
|
-
"""
|
105
|
-
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
106
|
-
return CNAProvisionersQueryData(**raw_data)
|
@@ -1,98 +0,0 @@
|
|
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 CNAssets {
|
23
|
-
namespaces: namespaces_v1 {
|
24
|
-
name
|
25
|
-
externalResources {
|
26
|
-
provider
|
27
|
-
provisioner {
|
28
|
-
name
|
29
|
-
}
|
30
|
-
... on NamespaceCNAsset_v1 {
|
31
|
-
resources {
|
32
|
-
provider
|
33
|
-
... on CNANullAsset_v1 {
|
34
|
-
name: identifier
|
35
|
-
addr_block
|
36
|
-
}
|
37
|
-
}
|
38
|
-
}
|
39
|
-
}
|
40
|
-
}
|
41
|
-
}
|
42
|
-
"""
|
43
|
-
|
44
|
-
|
45
|
-
class ConfiguredBaseModel(BaseModel):
|
46
|
-
class Config:
|
47
|
-
smart_union=True
|
48
|
-
extra=Extra.forbid
|
49
|
-
|
50
|
-
|
51
|
-
class ExternalResourcesProvisionerV1(ConfiguredBaseModel):
|
52
|
-
name: str = Field(..., alias="name")
|
53
|
-
|
54
|
-
|
55
|
-
class NamespaceExternalResourceV1(ConfiguredBaseModel):
|
56
|
-
provider: str = Field(..., alias="provider")
|
57
|
-
provisioner: ExternalResourcesProvisionerV1 = Field(..., alias="provisioner")
|
58
|
-
|
59
|
-
|
60
|
-
class CNAssetV1(ConfiguredBaseModel):
|
61
|
-
provider: str = Field(..., alias="provider")
|
62
|
-
|
63
|
-
|
64
|
-
class CNANullAssetV1(CNAssetV1):
|
65
|
-
name: str = Field(..., alias="name")
|
66
|
-
addr_block: Optional[str] = Field(..., alias="addr_block")
|
67
|
-
|
68
|
-
|
69
|
-
class NamespaceCNAssetV1(NamespaceExternalResourceV1):
|
70
|
-
resources: list[Union[CNANullAssetV1, CNAssetV1]] = Field(..., alias="resources")
|
71
|
-
|
72
|
-
|
73
|
-
class NamespaceV1(ConfiguredBaseModel):
|
74
|
-
name: str = Field(..., alias="name")
|
75
|
-
external_resources: Optional[list[Union[NamespaceCNAssetV1, NamespaceExternalResourceV1]]] = Field(..., alias="externalResources")
|
76
|
-
|
77
|
-
|
78
|
-
class CNAssetsQueryData(ConfiguredBaseModel):
|
79
|
-
namespaces: Optional[list[NamespaceV1]] = Field(..., alias="namespaces")
|
80
|
-
|
81
|
-
|
82
|
-
def query(query_func: Callable, **kwargs: Any) -> CNAssetsQueryData:
|
83
|
-
"""
|
84
|
-
This is a convenience function which queries and parses the data into
|
85
|
-
concrete types. It should be compatible with most GQL clients.
|
86
|
-
You do not have to use it to consume the generated data classes.
|
87
|
-
Alternatively, you can also mime and alternate the behavior
|
88
|
-
of this function in the caller.
|
89
|
-
|
90
|
-
Parameters:
|
91
|
-
query_func (Callable): Function which queries your GQL Server
|
92
|
-
kwargs: optional arguments that will be passed to the query function
|
93
|
-
|
94
|
-
Returns:
|
95
|
-
CNAssetsQueryData: queried data parsed into generated classes
|
96
|
-
"""
|
97
|
-
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
98
|
-
return CNAssetsQueryData(**raw_data)
|
{qontract_reconcile-0.10.2.dev221.dist-info → qontract_reconcile-0.10.2.dev222.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|