Skip to content

Commit

Permalink
Remove clusterrule status manager and return original use kopf
Browse files Browse the repository at this point in the history
  • Loading branch information
rodrigo.santos committed Mar 29, 2021
1 parent a0424f5 commit e8ad098
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 106 deletions.
98 changes: 30 additions & 68 deletions app/entrypoint/operator/cluster_rule_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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='')
Expand All @@ -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,
Expand All @@ -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
Expand All @@ -121,46 +74,55 @@ 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
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
8 changes: 0 additions & 8 deletions app/infra/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
35 changes: 5 additions & 30 deletions app/infra/kubernetes_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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()}


0 comments on commit e8ad098

Please sign in to comment.