import abc import datetime import functools from dateutil import parser from kubernetes import client from src import util, consts, suspicions from src.context import get_infra_api_cli CR_GROUP = "cloud.pingcap.com" CR_VERSION = "v1" class BaseInspector(abc.ABC): @abc.abstractmethod def inspect(self) -> list[str] | None: pass class NetworkInspector(BaseInspector): # on prod, we wait 24 hours before hibernating a idle network. # So that 3 days is enough to wait IDLE_THRESHOLD = datetime.timedelta(days=3) # in general, the creation take up to 1 hour CREATING_THRESHOLD = datetime.timedelta(days=1) # in general, the creation take up to 1 hour DELETING_THRESHOLD = datetime.timedelta(days=1) # in general, the creation take up to 1 hour WAKING_THRESHOLD = datetime.timedelta(days=1) def __init__(self, namespace: str, name: str): self.ns = namespace self.name = name self.infra_api_k8s_cli = get_infra_api_cli() self.cr_api = client.CustomObjectsApi(api_client=self.infra_api_k8s_cli) self.network = util.get_namespaced_cr(self.cr_api, CR_GROUP, CR_VERSION, self.ns, "networks", self.name) label_selector = { consts.LabelProviderType: self.network.metadata.labels[consts.LabelProviderType], consts.LabelRegion: self.network.metadata.labels[consts.LabelRegion], } self.clusters = util.list_namespaced_cr(self.cr_api, CR_GROUP, CR_VERSION, self.ns, "clusters", label_selector=util.build_label_selector(label_selector)) def inspect(self) -> list[suspicions.SuspicionABC] | None: phase2fn = { 'Normal': self._inspect_on_normal, 'Deleting': self._inspect_on_deleting, 'WakingUp': self._inspect_on_waking_up, 'Creating': self._inspect_on_creating, } fn = phase2fn.get(self.network.status.phase) if not fn: return suspicion_cls_list = fn() if suspicion_cls_list: info = { "project_id": self.network.metadata.labels[consts.LabelProjectId], "tenant_id": self.network.metadata.labels[consts.LabelTenantId], "network_name": self.network.metadata.name, "namespace": self.network.metadata.namespace, "provider": self.network.spec.provider, "region": self.network.spec.region, } if self.network.spec.provider in ['gke']: info["account_id"] = self.network.spc.forProvider.projectId info["vpc"] = self.network.spc.forProvider.network elif self.network.spec.provider in ['eks']: info["account_id"] = self.network.status.iam.accountID info["vpc"] = self.network.spec.forProvider.vpcId suspicion_cls_list.insert(0, suspicions.FixedSolution(info=info)) return suspicion_cls_list def _inspect_on_normal(self) -> list[suspicions.SuspicionABC] | None: pass def _inspect_on_deleting(self) -> list[suspicions.SuspicionABC] | None: if not self.network.metadata.deletionTimestamp: return now = datetime.datetime.now(tz=datetime.timezone.utc) deleted_at = parser.parse(self.network.metadata.deletionTimestamp) if (now - deleted_at) < self.DELETING_THRESHOLD: return return [ functools.partial(suspicions.ReconcileFail, obj_name=self.network.metadata.name, obj_ns=self.network.metadata.namespace, obj_kind="Network"), suspicions.NetworkUsedCrossplaneCRsRemain, ] def _inspect_on_waking_up(self) -> list[suspicions.SuspicionABC] | None: pass def _inspect_on_creating(self) -> list[suspicions.SuspicionABC] | None: pass