diff --git a/app/entrypoint/operator/cluster_rule_controller.py b/app/entrypoint/operator/cluster_rule_controller.py index 3e94fd3..48ad3f2 100644 --- a/app/entrypoint/operator/cluster_rule_controller.py +++ b/app/entrypoint/operator/cluster_rule_controller.py @@ -7,48 +7,6 @@ from app.entrypoint.operator.base_controller import BaseController from app.domain.entities import ClusterRule, ClusterRuleStatus, Cluster, Enforcement from app.domain.use_case import ApplyRulesUseCase, SyncRulesUseCase, UpdateRulesUseCase, RulesResponse -from app.infra.kubernetes_helper import KubernetesHelper -from app.infra.config import Config - - -@inject -@attr.s(auto_attribs=True) -class StatusManager: - _kubernetes_helper: KubernetesHelper - _config: Config - - def get_status(self, kind: str, name: str) -> ClusterRuleStatus: - status_dto = self._kubernetes_helper.get_custom_resource_status( - self._config.api_group, - self._config.api_version, - kind, - name - ) - - return ClusterRuleStatus(**status_dto) \ - if status_dto and 'clusters' in status_dto else None - - def update_status(self, kind: str, name: str, status_dto: dict): - - self._kubernetes_helper.update_custom_resource_status( - status_dto, - self._config.api_group, - self._config.api_version, - kind, - name, - ) - - @classmethod - def build_status(cls, response: RulesResponse) -> dict: - status = ClusterRuleStatus( - install_errors=[ - enforcement.name for enforcement in response.install_errors - ], - clusters=[ - {"name": cluster.name, "url": cluster.url} for cluster in response.clusters - ] - ) - return status.dict() @inject @@ -57,7 +15,6 @@ class ClusterRuleController(BaseController): _apply_rules_use_case: ApplyRulesUseCase _sync_rules_use_case: SyncRulesUseCase _update_rules_use_case: UpdateRulesUseCase - _status_manager: StatusManager KIND: ClassVar[str] = 'clusterrules' ID: ClassVar[str] = "sync/spec.enforcements" BACKOFF: ClassVar[int] = 10 @@ -73,7 +30,7 @@ def update(self, name, old: List[dict], new: List[dict], status: dict, logger, * old_enforcement_list = ClusterRuleController._make_enforcement_list(old) new_enforcement_list = ClusterRuleController._make_enforcement_list(new) - current_status = self._status_manager.get_status(self.KIND, name) + current_status = ClusterRuleController._restore_status(status) current_clusters = [ Cluster(name=cluster['name'], url=cluster['url'], id='', token='') @@ -92,7 +49,7 @@ def update(self, name, old: List[dict], new: List[dict], status: dict, logger, * response.install_errors = response.install_errors + list( map( - lambda enf_name: Enforcement(name=enf_name, repo=""), + lambda name: Enforcement(name=name, repo=""), filter( lambda enforcement_name: enforcement_name not in enforcements_change, current_status.install_errors, @@ -102,16 +59,12 @@ def update(self, name, old: List[dict], new: List[dict], status: dict, logger, * response.clusters = current_clusters - self._status_manager.update_status( - self.KIND, - name, - self._status_manager.build_status(response) - ) + return ClusterRuleController._make_status(response) def sync(self, name: str, spec: dict, status: dict, logger, **kwargs): logger.debug(f"sync clusters for %s", name) - current_status = self._status_manager.get_status(self.KIND, name) + current_status = ClusterRuleController._restore_status(status) if not current_status: return @@ -121,42 +74,32 @@ def sync(self, name: str, spec: dict, status: dict, logger, **kwargs): for cluster in current_status.clusters ] cluster_rule = ClusterRule(**spec) - response = self._sync_rules_use_case.execute(cluster_rule, current_clusters) response.install_errors = [Enforcement(name=name, repo="") for name in current_status.install_errors] - new_status = self._status_manager.build_status(response) + new_status = ClusterRuleController._make_status(response) - if current_status != new_status: - self._status_manager.update_status( - self.KIND, - name, - new_status - ) + if new_status != current_status.dict(): + return new_status def create(self, name, spec: dict, logger, **kwargs): logger.debug(f"create rules for %s", name) cluster_rule = ClusterRule(**spec) - response = self._apply_rules_use_case.execute(cluster_rule) - self._status_manager.update_status( - self.KIND, - name, - self._status_manager.build_status(response) - ) + return ClusterRuleController._make_status(response) def register(self): - self.register_method(kopf.on.create, self.create, self.KIND, + self.register_method(kopf.on.create, self.create, self.KIND, id=ClusterRuleController.ID, errors=kopf.ErrorsMode.TEMPORARY, backoff=ClusterRuleController.BACKOFF) - self.register_method(kopf.on.field, self.update, self.KIND, + self.register_method(kopf.on.field, self.update, self.KIND, id='sync', field='spec.enforcements', errors=kopf.ErrorsMode.TEMPORARY, backoff=ClusterRuleController.BACKOFF) - self.register_method(kopf.on.timer, self.sync, self.KIND, + self.register_method(kopf.on.timer, self.sync, self.KIND, id=ClusterRuleController.ID, interval=6, initial_delay=20, idle=15, errors=kopf.ErrorsMode.PERMANENT) @classmethod @@ -164,3 +107,22 @@ def _make_enforcement_list(cls, enforcement_map_list) -> List[Enforcement]: if not enforcement_map_list: return [] return [Enforcement(**enforcement_map) for enforcement_map in enforcement_map_list] + + @classmethod + def _make_status(cls, response: RulesResponse) -> dict: + status = ClusterRuleStatus( + install_errors=[ + enforcement.name for enforcement in response.install_errors + ], + clusters=[ + {"name": cluster.name, "url": cluster.url} for cluster in response.clusters + ] + ) + return status.dict() + + @classmethod + def _restore_status(cls, status: dict) -> ClusterRuleStatus: + current_status: dict = status.get(ClusterRuleController.ID) + + return ClusterRuleStatus(**current_status) \ + if current_status and "clusters" in current_status else None diff --git a/app/infra/config.py b/app/infra/config.py index 6658587..5ba4d24 100644 --- a/app/infra/config.py +++ b/app/infra/config.py @@ -27,14 +27,6 @@ def argo_password(self) -> str: def current_namespace(self) -> str: return self._get_config_value('OPERATOR_NAMESPACE', 'operator', 'namespace') - @property - def api_group(self) -> str: - return self._get_config_value('OPERATOR_API_GROUP', 'operator', 'group') - - @property - def api_version(self) -> str: - return self._get_config_value('OPERATOR_API_VERSION', 'operator', 'version') - def _get_config_value( self, environemnt_variable_name: str, config_name: str, config_attribute: str, ) -> str: diff --git a/app/infra/kubernetes_helper.py b/app/infra/kubernetes_helper.py index fbe4ea4..c31c083 100644 --- a/app/infra/kubernetes_helper.py +++ b/app/infra/kubernetes_helper.py @@ -5,7 +5,7 @@ from app.domain.entities import Secret from app.domain.exceptions import SecretNotFound -from kubernetes.client import CoreV1Api, V1Secret, CustomObjectsApi +from kubernetes.client import CoreV1Api, V1Secret @attr.s(auto_attribs=True) @@ -22,38 +22,13 @@ def _get_secret_from_api(self, secret_name: str) -> V1Secret: return secret[0] - @classmethod - def _decode_secret(cls, secret: V1Secret) -> Dict[str, str]: - return {k: base64.b64decode(v).decode() for k, v in secret.data.items()} - def get_secret(self, secret_name: str) -> Secret: secret_encoded = self._get_secret_from_api(secret_name) secret_decoded = self._decode_secret(secret_encoded) return Secret(**secret_decoded) - def get_custom_resource_status(self, group: str, version: str, kind: str, name: str) -> dict: - api_instance = CustomObjectsApi() - response = api_instance.get_namespaced_custom_object_status( - group=group, - version=version, - namespace=self._current_namespace, - plural=kind, - name=name - ) - - return response.get('status') - - def update_custom_resource_status(self, status: dict, group: str, version: str, kind: str, name: str): - api_instance = CustomObjectsApi() - - patch = {"status": status} - - api_instance.patch_namespaced_custom_object_status( - group=group, - version=version, - namespace=self._current_namespace, - plural=kind, - name=name, - body=patch - ) + @classmethod + def _decode_secret(cls, secret: V1Secret) -> Dict[str, str]: + return {k: base64.b64decode(v).decode() for k, v in secret.data.items()} +